docs: auto-generated translations (#14434)
* docs: auto-generated translations * download docs from new location
This commit is contained in:
parent
275f8d4ffd
commit
780ae101c0
2283 changed files with 125 additions and 296865 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -118,3 +118,4 @@ e2e/screenshot.png
|
|||
website/community/events
|
||||
website/events.json
|
||||
e2e/wdio/headless/*.png
|
||||
website/i18n
|
||||
109
scripts/docs-generation/downloadDocsTranslations.ts
Normal file
109
scripts/docs-generation/downloadDocsTranslations.ts
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
import fs from 'node:fs/promises'
|
||||
import url from 'node:url'
|
||||
import path from 'node:path'
|
||||
import unzipper from 'unzipper'
|
||||
import { Readable } from 'node:stream'
|
||||
|
||||
import { Octokit } from '@octokit/rest'
|
||||
|
||||
// GitHub repository information
|
||||
const REPO_OWNER = 'webdriverio'
|
||||
const REPO_NAME = 'i18n'
|
||||
|
||||
const GITHUB_TOKEN = process.env.GITHUB_AUTH
|
||||
if (!GITHUB_TOKEN) {
|
||||
throw new Error('GITHUB_AUTH is not set')
|
||||
}
|
||||
|
||||
const IGNORE_FILES = ['src', 'package.json', 'package-lock.json', 'README.md', 'LICENSE', 'tsconfig.json']
|
||||
|
||||
const __dirname = path.dirname(url.fileURLToPath(import.meta.url))
|
||||
const octokit = new Octokit({ auth: GITHUB_TOKEN })
|
||||
|
||||
export function downloadDocsTranslations() {
|
||||
return downloadAndExtractRepo(REPO_OWNER, REPO_NAME)
|
||||
}
|
||||
|
||||
async function downloadAndExtractRepo(owner: string, repo: string, branch?: string) {
|
||||
// Step 1: Determine default branch if not specified
|
||||
if (!branch) {
|
||||
const { data } = await octokit.repos.get({ owner, repo })
|
||||
branch = data.default_branch
|
||||
}
|
||||
|
||||
// Step 2: Download the zipball
|
||||
const url = `https://api.github.com/repos/${owner}/${repo}/zipball/${branch}`
|
||||
console.log(`Downloading ${url}...`)
|
||||
|
||||
const response = await fetch(url, {
|
||||
headers: {
|
||||
Authorization: `token ${GITHUB_TOKEN}`,
|
||||
Accept: 'application/vnd.github.v3+json',
|
||||
},
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to download repo zip: ${response.status} ${response.statusText}`)
|
||||
}
|
||||
|
||||
// Create a temp directory for initial extraction
|
||||
const tempExtractPath = path.resolve(__dirname, '..', '..', 'temp_extract')
|
||||
const finalExtractPath = path.resolve(__dirname, '..', '..', 'website', 'i18n')
|
||||
|
||||
// Ensure both directories exist
|
||||
await fs.mkdir(tempExtractPath, { recursive: true })
|
||||
await fs.mkdir(finalExtractPath, { recursive: true })
|
||||
|
||||
try {
|
||||
// Step 3: Extract to temp directory first
|
||||
await new Promise((resolve, reject) => {
|
||||
if (!response.body) {
|
||||
return reject(new Error('Response body is null'))
|
||||
}
|
||||
|
||||
// Convert ReadableStream to Node.js stream
|
||||
// @ts-ignore - Type compatibility issue between web streams and node streams
|
||||
const nodeStream = Readable.fromWeb(response.body)
|
||||
|
||||
nodeStream
|
||||
.pipe(unzipper.Extract({ path: tempExtractPath }))
|
||||
.on('close', resolve)
|
||||
.on('error', reject)
|
||||
})
|
||||
|
||||
// Step 4: Move contents from temp directory to final location
|
||||
const extractedDirs = await fs.readdir(tempExtractPath)
|
||||
const repoDir = extractedDirs[0] // This should be the GitHub repository directory (e.g., webdriverio-i18n-7b50bc6)
|
||||
const repoDirPath = path.join(tempExtractPath, repoDir)
|
||||
|
||||
// Copy all contents from the repo directory to the final location
|
||||
const files = await fs.readdir(repoDirPath)
|
||||
for (const file of files) {
|
||||
const baseName = path.basename(file)
|
||||
if (baseName.startsWith('.') || IGNORE_FILES.includes(baseName)) {
|
||||
continue
|
||||
}
|
||||
|
||||
const srcPath = path.join(repoDirPath, file)
|
||||
const destPath = path.join(finalExtractPath, file)
|
||||
|
||||
// Handle the case if the file already exists in the destination
|
||||
try {
|
||||
await fs.rm(destPath, { recursive: true, force: true })
|
||||
} catch {
|
||||
// Ignore if file doesn't exist
|
||||
}
|
||||
|
||||
await fs.cp(srcPath, destPath, { recursive: true })
|
||||
}
|
||||
|
||||
console.log(`Repository extracted to ${finalExtractPath}`)
|
||||
} finally {
|
||||
// Clean up temp directory
|
||||
try {
|
||||
await fs.rm(tempExtractPath, { recursive: true, force: true })
|
||||
} catch (err) {
|
||||
console.warn('Failed to clean up temporary directory:', err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3,7 +3,7 @@ import fs from 'node:fs'
|
|||
import url from 'node:url'
|
||||
import path from 'node:path'
|
||||
|
||||
import sidebars from '../../website/_sidebars.json' assert { type: 'json' }
|
||||
import sidebars from '../../website/_sidebars.json' with { type: 'json' }
|
||||
import { generateProtocolDocs } from './protocolDocs.js'
|
||||
import { generateWdioDocs } from './wdioDocs.js'
|
||||
import { generateReportersAndServicesDocs } from './packagesDocs.js'
|
||||
|
|
@ -12,6 +12,7 @@ import { generateElectronDocs } from './electronDocs.js'
|
|||
import { generateEventDocs } from './eventDocs.js'
|
||||
import { copyContributingDocs } from './copyContributingDocs.js'
|
||||
import { downloadAwesomeResources } from './downloadAwesomeResources.js'
|
||||
import { downloadDocsTranslations } from './downloadDocsTranslations.js'
|
||||
|
||||
const __dirname = path.dirname(url.fileURLToPath(import.meta.url))
|
||||
|
||||
|
|
@ -49,6 +50,8 @@ try {
|
|||
await copyContributingDocs()
|
||||
print('Copy over Awesome Resources')
|
||||
await downloadAwesomeResources()
|
||||
print('Download docs translations')
|
||||
await downloadDocsTranslations()
|
||||
|
||||
writeSidebars(sidebars)
|
||||
|
||||
|
|
|
|||
|
|
@ -36,14 +36,21 @@ const config: Config = {
|
|||
defaultLocale: 'en',
|
||||
locales: [
|
||||
'en',
|
||||
'ar',
|
||||
'de',
|
||||
'es',
|
||||
'hi',
|
||||
'fr',
|
||||
'uk',
|
||||
'fa',
|
||||
'fr',
|
||||
'hi',
|
||||
'it',
|
||||
'ja',
|
||||
'pl',
|
||||
'pt',
|
||||
'ru',
|
||||
'sv',
|
||||
'ta',
|
||||
'pt'
|
||||
'uk',
|
||||
'zh',
|
||||
],
|
||||
},
|
||||
themeConfig: {
|
||||
|
|
|
|||
|
|
@ -1,580 +0,0 @@
|
|||
{
|
||||
"theme.ErrorPageContent.title": {
|
||||
"message": "هذه الصفحة تعطلت.",
|
||||
"description": "The title of the fallback page when the page crashed"
|
||||
},
|
||||
"theme.ErrorPageContent.tryAgain": {
|
||||
"message": "أعد المحاولة",
|
||||
"description": "The label of the button to try again when the page crashed"
|
||||
},
|
||||
"theme.NotFound.title": {
|
||||
"message": "لم يتم العثور على الصفحة",
|
||||
"description": "The title of the 404 page"
|
||||
},
|
||||
"theme.NotFound.p1": {
|
||||
"message": "لم تتمكن من العثور على ما تبحث عنه.",
|
||||
"description": "The first paragraph of the 404 page"
|
||||
},
|
||||
"theme.NotFound.p2": {
|
||||
"message": "يرجى الاتصال بمالك الموقع الذي ربطك بعنوان URL الأصلي وإخباره بأن الرابط الخاص به معطل.",
|
||||
"description": "The 2nd paragraph of the 404 page"
|
||||
},
|
||||
"theme.admonition.note": {
|
||||
"message": "ملاحظة",
|
||||
"description": "The default label used for the Note admonition (:::note)"
|
||||
},
|
||||
"theme.admonition.tip": {
|
||||
"message": "نصيحة",
|
||||
"description": "The default label used for the Tip admonition (:::tip)"
|
||||
},
|
||||
"theme.admonition.danger": {
|
||||
"message": "خطر",
|
||||
"description": "The default label used for the Danger admonition (:::danger)"
|
||||
},
|
||||
"theme.admonition.info": {
|
||||
"message": "معلومات",
|
||||
"description": "The default label used for the Info admonition (:::info)"
|
||||
},
|
||||
"theme.admonition.caution": {
|
||||
"message": "تحذير!",
|
||||
"description": "The default label used for the Caution admonition (:::caution)"
|
||||
},
|
||||
"theme.BackToTopButton.buttonAriaLabel": {
|
||||
"message": "انتقل إلى الأعلى",
|
||||
"description": "The ARIA label for the back to top button"
|
||||
},
|
||||
"theme.blog.archive.title": {
|
||||
"message": "الأرشيف",
|
||||
"description": "The page & hero title of the blog archive page"
|
||||
},
|
||||
"theme.blog.archive.description": {
|
||||
"message": "الأرشيف",
|
||||
"description": "The page & hero description of the blog archive page"
|
||||
},
|
||||
"theme.blog.paginator.navAriaLabel": {
|
||||
"message": "التنقل في صفحة قائمة المدونة",
|
||||
"description": "The ARIA label for the blog pagination"
|
||||
},
|
||||
"theme.blog.paginator.newerEntries": {
|
||||
"message": "إدخالات أحدث",
|
||||
"description": "The label used to navigate to the newer blog posts page (previous page)"
|
||||
},
|
||||
"theme.blog.paginator.olderEntries": {
|
||||
"message": "إدخالات أقدم",
|
||||
"description": "The label used to navigate to the older blog posts page (next page)"
|
||||
},
|
||||
"theme.blog.post.paginator.navAriaLabel": {
|
||||
"message": "تصفح صفحة نشر المدونة",
|
||||
"description": "The ARIA label for the blog posts pagination"
|
||||
},
|
||||
"theme.blog.post.paginator.newerPost": {
|
||||
"message": "منشور أحدث",
|
||||
"description": "The blog post button label to navigate to the newer/previous post"
|
||||
},
|
||||
"theme.blog.post.paginator.olderPost": {
|
||||
"message": "منشور أقدم",
|
||||
"description": "The blog post button label to navigate to the older/next post"
|
||||
},
|
||||
"theme.blog.post.plurals": {
|
||||
"message": "مشاركة واحدة |{count} مشاركة",
|
||||
"description": "Pluralized label for \"{count} posts\". Use as much plural forms (separated by \"|\") as your language support (see https://www.unicode.org/cldr/cldr-aux/charts/34/supplemental/language_plural_rules.html)"
|
||||
},
|
||||
"theme.blog.tagTitle": {
|
||||
"message": "تم وضع علامات {nPosts} مع \"{tagName}\"",
|
||||
"description": "The title of the page for a blog tag"
|
||||
},
|
||||
"theme.tags.tagsPageLink": {
|
||||
"message": "عرض كل الوسوم",
|
||||
"description": "The label of the link targeting the tag list page"
|
||||
},
|
||||
"theme.docs.breadcrumbs.home": {
|
||||
"message": "الصفحة الرئيسية",
|
||||
"description": "The ARIA label for the home page in the breadcrumbs"
|
||||
},
|
||||
"theme.docs.breadcrumbs.navAriaLabel": {
|
||||
"message": "البحث فى شركة معينة",
|
||||
"description": "The ARIA label for the breadcrumbs"
|
||||
},
|
||||
"theme.docs.DocCard.categoryDescription": {
|
||||
"message": "{count} عناصر",
|
||||
"description": "The default description for a category card in the generated index about how many items this category includes"
|
||||
},
|
||||
"theme.colorToggle.ariaLabel": {
|
||||
"message": "التبديل بين الوضع الداكن والفاتح (حاليًا {mode})",
|
||||
"description": "The ARIA label for the navbar color mode toggle"
|
||||
},
|
||||
"theme.colorToggle.ariaLabel.mode.dark": {
|
||||
"message": "الوضع الداكن",
|
||||
"description": "The name for the dark color mode"
|
||||
},
|
||||
"theme.colorToggle.ariaLabel.mode.light": {
|
||||
"message": "وضع الإضاءة",
|
||||
"description": "The name for the light color mode"
|
||||
},
|
||||
"theme.docs.paginator.navAriaLabel": {
|
||||
"message": "تصفح صفحات المستندات",
|
||||
"description": "The ARIA label for the docs pagination"
|
||||
},
|
||||
"theme.docs.paginator.previous": {
|
||||
"message": "السابق",
|
||||
"description": "The label used to navigate to the previous doc"
|
||||
},
|
||||
"theme.docs.paginator.next": {
|
||||
"message": "التالي",
|
||||
"description": "The label used to navigate to the next doc"
|
||||
},
|
||||
"theme.docs.tagDocListPageTitle.nDocsTagged": {
|
||||
"message": "One doc tagged|{count} docs tagged",
|
||||
"description": "Pluralized label for \"{count} docs tagged\". Use as much plural forms (separated by \"|\") as your language support (see https://www.unicode.org/cldr/cldr-aux/charts/34/supplemental/language_plural_rules.html)"
|
||||
},
|
||||
"theme.docs.tagDocListPageTitle": {
|
||||
"message": "{nDocsTagged} with \"{tagName}\"",
|
||||
"description": "The title of the page for a docs tag"
|
||||
},
|
||||
"theme.docs.versionBadge.label": {
|
||||
"message": "Version: {versionLabel}"
|
||||
},
|
||||
"theme.docs.versions.unreleasedVersionLabel": {
|
||||
"message": "This is unreleased documentation for {siteTitle} {versionLabel} version.",
|
||||
"description": "The label used to tell the user that he's browsing an unreleased doc version"
|
||||
},
|
||||
"theme.docs.versions.unmaintainedVersionLabel": {
|
||||
"message": "This is documentation for {siteTitle} {versionLabel}, which is no longer actively maintained.",
|
||||
"description": "The label used to tell the user that he's browsing an unmaintained doc version"
|
||||
},
|
||||
"theme.docs.versions.latestVersionSuggestionLabel": {
|
||||
"message": "For up-to-date documentation, see the {latestVersionLink} ({versionLabel}).",
|
||||
"description": "The label used to tell the user to check the latest version"
|
||||
},
|
||||
"theme.docs.versions.latestVersionLinkLabel": {
|
||||
"message": "latest version",
|
||||
"description": "The label used for the latest version suggestion link label"
|
||||
},
|
||||
"theme.common.editThisPage": {
|
||||
"message": "Edit this page",
|
||||
"description": "The link label to edit the current page"
|
||||
},
|
||||
"theme.common.headingLinkTitle": {
|
||||
"message": "Direct link to heading",
|
||||
"description": "Title for link to heading"
|
||||
},
|
||||
"theme.lastUpdated.atDate": {
|
||||
"message": " on {date}",
|
||||
"description": "The words used to describe on which date a page has been last updated"
|
||||
},
|
||||
"theme.lastUpdated.byUser": {
|
||||
"message": " by {user}",
|
||||
"description": "The words used to describe by who the page has been last updated"
|
||||
},
|
||||
"theme.lastUpdated.lastUpdatedAtBy": {
|
||||
"message": "Last updated{atDate}{byUser}",
|
||||
"description": "The sentence used to display when a page has been last updated, and by who"
|
||||
},
|
||||
"theme.navbar.mobileVersionsDropdown.label": {
|
||||
"message": "Versions",
|
||||
"description": "The label for the navbar versions dropdown on mobile view"
|
||||
},
|
||||
"theme.AnnouncementBar.closeButtonAriaLabel": {
|
||||
"message": "Close",
|
||||
"description": "The ARIA label for close button of announcement bar"
|
||||
},
|
||||
"theme.tags.tagsListLabel": {
|
||||
"message": "Tags:",
|
||||
"description": "The label alongside a tag list"
|
||||
},
|
||||
"theme.blog.sidebar.navAriaLabel": {
|
||||
"message": "Blog recent posts navigation",
|
||||
"description": "The ARIA label for recent posts in the blog sidebar"
|
||||
},
|
||||
"theme.CodeBlock.copied": {
|
||||
"message": "Copied",
|
||||
"description": "The copied button label on code blocks"
|
||||
},
|
||||
"theme.CodeBlock.copyButtonAriaLabel": {
|
||||
"message": "Copy code to clipboard",
|
||||
"description": "The ARIA label for copy code blocks button"
|
||||
},
|
||||
"theme.CodeBlock.copy": {
|
||||
"message": "Copy",
|
||||
"description": "The copy button label on code blocks"
|
||||
},
|
||||
"theme.CodeBlock.wordWrapToggle": {
|
||||
"message": "Toggle word wrap",
|
||||
"description": "The title attribute for toggle word wrapping button of code block lines"
|
||||
},
|
||||
"theme.DocSidebarItem.toggleCollapsedCategoryAriaLabel": {
|
||||
"message": "Toggle the collapsible sidebar category '{label}'",
|
||||
"description": "The ARIA label to toggle the collapsible sidebar category"
|
||||
},
|
||||
"theme.TOCCollapsible.toggleButtonLabel": {
|
||||
"message": "On this page",
|
||||
"description": "The label used by the button on the collapsible TOC component"
|
||||
},
|
||||
"theme.blog.post.readMore": {
|
||||
"message": "Read More",
|
||||
"description": "The label used in blog post item excerpts to link to full blog posts"
|
||||
},
|
||||
"theme.blog.post.readMoreLabel": {
|
||||
"message": "Read more about {title}",
|
||||
"description": "The ARIA label for the link to full blog posts from excerpts"
|
||||
},
|
||||
"theme.blog.post.readingTime.plurals": {
|
||||
"message": "One min read|{readingTime} min read",
|
||||
"description": "Pluralized label for \"{readingTime} min read\". Use as much plural forms (separated by \"|\") as your language support (see https://www.unicode.org/cldr/cldr-aux/charts/34/supplemental/language_plural_rules.html)"
|
||||
},
|
||||
"theme.navbar.mobileLanguageDropdown.label": {
|
||||
"message": "Languages",
|
||||
"description": "The label for the mobile language switcher dropdown"
|
||||
},
|
||||
"theme.docs.sidebar.collapseButtonTitle": {
|
||||
"message": "Collapse sidebar",
|
||||
"description": "The title attribute for collapse button of doc sidebar"
|
||||
},
|
||||
"theme.docs.sidebar.collapseButtonAriaLabel": {
|
||||
"message": "Collapse sidebar",
|
||||
"description": "The title attribute for collapse button of doc sidebar"
|
||||
},
|
||||
"theme.docs.sidebar.closeSidebarButtonAriaLabel": {
|
||||
"message": "Close navigation bar",
|
||||
"description": "The ARIA label for close button of mobile sidebar"
|
||||
},
|
||||
"theme.navbar.mobileSidebarSecondaryMenu.backButtonLabel": {
|
||||
"message": "← Back to main menu",
|
||||
"description": "The label of the back button to return to main menu, inside the mobile navbar sidebar secondary menu (notably used to display the docs sidebar)"
|
||||
},
|
||||
"theme.docs.sidebar.toggleSidebarButtonAriaLabel": {
|
||||
"message": "Toggle navigation bar",
|
||||
"description": "The ARIA label for hamburger menu button of mobile navigation"
|
||||
},
|
||||
"theme.docs.sidebar.expandButtonTitle": {
|
||||
"message": "Expand sidebar",
|
||||
"description": "The ARIA label and title attribute for expand button of doc sidebar"
|
||||
},
|
||||
"theme.docs.sidebar.expandButtonAriaLabel": {
|
||||
"message": "Expand sidebar",
|
||||
"description": "The ARIA label and title attribute for expand button of doc sidebar"
|
||||
},
|
||||
"theme.SearchBar.seeAll": {
|
||||
"message": "See all {count} results"
|
||||
},
|
||||
"theme.SearchPage.documentsFound.plurals": {
|
||||
"message": "One document found|{count} documents found",
|
||||
"description": "Pluralized label for \"{count} documents found\". Use as much plural forms (separated by \"|\") as your language support (see https://www.unicode.org/cldr/cldr-aux/charts/34/supplemental/language_plural_rules.html)"
|
||||
},
|
||||
"theme.SearchPage.existingResultsTitle": {
|
||||
"message": "Search results for \"{query}\"",
|
||||
"description": "The search page title for non-empty query"
|
||||
},
|
||||
"theme.SearchPage.emptyResultsTitle": {
|
||||
"message": "Search the documentation",
|
||||
"description": "The search page title for empty query"
|
||||
},
|
||||
"theme.SearchPage.inputPlaceholder": {
|
||||
"message": "Type your search here",
|
||||
"description": "The placeholder for search page input"
|
||||
},
|
||||
"theme.SearchPage.inputLabel": {
|
||||
"message": "Search",
|
||||
"description": "The ARIA label for search page input"
|
||||
},
|
||||
"theme.SearchPage.algoliaLabel": {
|
||||
"message": "Search by Algolia",
|
||||
"description": "The ARIA label for Algolia mention"
|
||||
},
|
||||
"theme.SearchPage.noResultsText": {
|
||||
"message": "No results were found",
|
||||
"description": "The paragraph for empty search result"
|
||||
},
|
||||
"theme.SearchPage.fetchingNewResults": {
|
||||
"message": "Fetching new results...",
|
||||
"description": "The paragraph for fetching new search results"
|
||||
},
|
||||
"theme.SearchBar.label": {
|
||||
"message": "Search",
|
||||
"description": "The ARIA label and placeholder for search button"
|
||||
},
|
||||
"theme.SearchModal.searchBox.resetButtonTitle": {
|
||||
"message": "Clear the query",
|
||||
"description": "The label and ARIA label for search box reset button"
|
||||
},
|
||||
"theme.SearchModal.searchBox.cancelButtonText": {
|
||||
"message": "Cancel",
|
||||
"description": "The label and ARIA label for search box cancel button"
|
||||
},
|
||||
"theme.SearchModal.startScreen.recentSearchesTitle": {
|
||||
"message": "Recent",
|
||||
"description": "The title for recent searches"
|
||||
},
|
||||
"theme.SearchModal.startScreen.noRecentSearchesText": {
|
||||
"message": "No recent searches",
|
||||
"description": "The text when no recent searches"
|
||||
},
|
||||
"theme.SearchModal.startScreen.saveRecentSearchButtonTitle": {
|
||||
"message": "Save this search",
|
||||
"description": "The label for save recent search button"
|
||||
},
|
||||
"theme.SearchModal.startScreen.removeRecentSearchButtonTitle": {
|
||||
"message": "Remove this search from history",
|
||||
"description": "The label for remove recent search button"
|
||||
},
|
||||
"theme.SearchModal.startScreen.favoriteSearchesTitle": {
|
||||
"message": "Favorite",
|
||||
"description": "The title for favorite searches"
|
||||
},
|
||||
"theme.SearchModal.startScreen.removeFavoriteSearchButtonTitle": {
|
||||
"message": "Remove this search from favorites",
|
||||
"description": "The label for remove favorite search button"
|
||||
},
|
||||
"theme.SearchModal.errorScreen.titleText": {
|
||||
"message": "Unable to fetch results",
|
||||
"description": "The title for error screen of search modal"
|
||||
},
|
||||
"theme.SearchModal.errorScreen.helpText": {
|
||||
"message": "You might want to check your network connection.",
|
||||
"description": "The help text for error screen of search modal"
|
||||
},
|
||||
"theme.SearchModal.footer.selectText": {
|
||||
"message": "to select",
|
||||
"description": "The explanatory text of the action for the enter key"
|
||||
},
|
||||
"theme.SearchModal.footer.selectKeyAriaLabel": {
|
||||
"message": "Enter key",
|
||||
"description": "The ARIA label for the Enter key button that makes the selection"
|
||||
},
|
||||
"theme.SearchModal.footer.navigateText": {
|
||||
"message": "to navigate",
|
||||
"description": "The explanatory text of the action for the Arrow up and Arrow down key"
|
||||
},
|
||||
"theme.SearchModal.footer.navigateUpKeyAriaLabel": {
|
||||
"message": "Arrow up",
|
||||
"description": "The ARIA label for the Arrow up key button that makes the navigation"
|
||||
},
|
||||
"theme.SearchModal.footer.navigateDownKeyAriaLabel": {
|
||||
"message": "Arrow down",
|
||||
"description": "The ARIA label for the Arrow down key button that makes the navigation"
|
||||
},
|
||||
"theme.SearchModal.footer.closeText": {
|
||||
"message": "to close",
|
||||
"description": "The explanatory text of the action for Escape key"
|
||||
},
|
||||
"theme.SearchModal.footer.closeKeyAriaLabel": {
|
||||
"message": "Escape key",
|
||||
"description": "The ARIA label for the Escape key button that close the modal"
|
||||
},
|
||||
"theme.SearchModal.footer.searchByText": {
|
||||
"message": "Search by",
|
||||
"description": "The text explain that the search is making by Algolia"
|
||||
},
|
||||
"theme.SearchModal.noResultsScreen.noResultsText": {
|
||||
"message": "No results for",
|
||||
"description": "The text explains that there are no results for the following search"
|
||||
},
|
||||
"theme.SearchModal.noResultsScreen.suggestedQueryText": {
|
||||
"message": "Try searching for",
|
||||
"description": "The text for the suggested query when no results are found for the following search"
|
||||
},
|
||||
"theme.SearchModal.noResultsScreen.reportMissingResultsText": {
|
||||
"message": "Believe this query should return results?",
|
||||
"description": "The text for the question where the user thinks there are missing results"
|
||||
},
|
||||
"theme.SearchModal.noResultsScreen.reportMissingResultsLinkText": {
|
||||
"message": "Let us know.",
|
||||
"description": "The text for the link to report missing results"
|
||||
},
|
||||
"theme.SearchModal.placeholder": {
|
||||
"message": "Search docs",
|
||||
"description": "The placeholder of the input of the DocSearch pop-up modal"
|
||||
},
|
||||
"theme.IdealImageMessage.loading": {
|
||||
"message": "Loading...",
|
||||
"description": "When the full-scale image is loading"
|
||||
},
|
||||
"theme.IdealImageMessage.load": {
|
||||
"message": "Click to load{sizeMessage}",
|
||||
"description": "To prompt users to load the full image. sizeMessage is a parenthesized size figure."
|
||||
},
|
||||
"theme.IdealImageMessage.offline": {
|
||||
"message": "Your browser is offline. Image not loaded",
|
||||
"description": "When the user is viewing an offline document"
|
||||
},
|
||||
"theme.IdealImageMessage.404error": {
|
||||
"message": "404. Image not found",
|
||||
"description": "When the image is not found"
|
||||
},
|
||||
"theme.IdealImageMessage.error": {
|
||||
"message": "Error. Click to reload",
|
||||
"description": "When the image fails to load for unknown error"
|
||||
},
|
||||
"theme.PwaReloadPopup.info": {
|
||||
"message": "New version available",
|
||||
"description": "The text for PWA reload popup"
|
||||
},
|
||||
"theme.PwaReloadPopup.refreshButtonText": {
|
||||
"message": "Refresh",
|
||||
"description": "The text for PWA reload button"
|
||||
},
|
||||
"theme.PwaReloadPopup.closeButtonAriaLabel": {
|
||||
"message": "Close",
|
||||
"description": "The ARIA label for close button of PWA reload popup"
|
||||
},
|
||||
"theme.common.skipToMainContent": {
|
||||
"message": "Skip to main content",
|
||||
"description": "The skip to content label used for accessibility, allowing to rapidly navigate to main content with keyboard tab/enter navigation"
|
||||
},
|
||||
"theme.tags.tagsPageTitle": {
|
||||
"message": "Tags",
|
||||
"description": "The title of the tag list page"
|
||||
},
|
||||
"WebdriverIO allows you to test in actual browser or mobile devices used by your users.": {
|
||||
"message": "WebdriverIO allows you to test in actual browser or mobile devices used by your users."
|
||||
},
|
||||
"Use WebdriverIO for full e2e or unit and component testing in the browser.": {
|
||||
"message": "Use WebdriverIO for full e2e or unit and component testing in the browser."
|
||||
},
|
||||
"WebdriverIO automatically waits for elements to appear before interacting with them.": {
|
||||
"message": "WebdriverIO automatically waits for elements to appear before interacting with them."
|
||||
},
|
||||
"Get Started": {
|
||||
"message": "Get Started"
|
||||
},
|
||||
"Why WebdriverIO?": {
|
||||
"message": "Why WebdriverIO?"
|
||||
},
|
||||
"View on GitHub": {
|
||||
"message": "View on GitHub"
|
||||
},
|
||||
"Watch on YouTube": {
|
||||
"message": "Watch on YouTube"
|
||||
},
|
||||
"Easy setup for web component testing with:": {
|
||||
"message": "Easy setup for web component testing with:"
|
||||
},
|
||||
"homepage.hightlight.createProject": {
|
||||
"message": "Get Started With WebdriverIO within Seconds"
|
||||
},
|
||||
"The WebdriverIO testrunner comes with a command line interface that provides a powerful configuration utility and helps you to create your test setup in less than a minute. It let's you pick from available test framework integrations and easily allows to add all supported reporter and service plugins!": {
|
||||
"message": "The WebdriverIO testrunner comes with a command line interface that provides a powerful configuration utility and helps you to create your test setup in less than a minute. It let's you pick from available test framework integrations and easily allows to add all supported reporter and service plugins!"
|
||||
},
|
||||
"With just one simple command you can set up a complete test suite:": {
|
||||
"message": "With just one simple command you can set up a complete test suite:"
|
||||
},
|
||||
"homepage.hightlight.watch": {
|
||||
"message": "Watch Talks about WebdriverIO"
|
||||
},
|
||||
"homepage.hightlight.lighthouse": {
|
||||
"message": "Google Lighthouse Integration"
|
||||
},
|
||||
"Integration to developer tools such as:": {
|
||||
"message": "Integration to developer tools such as:"
|
||||
},
|
||||
"until October 2023": {
|
||||
"message": "until October 2023"
|
||||
},
|
||||
"since February 2022": {
|
||||
"message": "since February 2022"
|
||||
},
|
||||
"since January 2021": {
|
||||
"message": "since January 2021"
|
||||
},
|
||||
"since December 2019": {
|
||||
"message": "since December 2019"
|
||||
},
|
||||
"versions.description": {
|
||||
"message": "Docusaurus 2 Versions page listing all documented site versions"
|
||||
},
|
||||
"WebdriverIO documentation versions": {
|
||||
"message": "WebdriverIO documentation versions"
|
||||
},
|
||||
"The project team releases new major versions roughly on a yearly cadence. LTS release status is \"long-term support\", which typically guarantees that critical bugs will be fixed for a total of 12 months until a new major release is made.": {
|
||||
"message": "The project team releases new major versions roughly on a yearly cadence. LTS release status is \"long-term support\", which typically guarantees that critical bugs will be fixed for a total of 12 months until a new major release is made."
|
||||
},
|
||||
"Current version (Stable)": {
|
||||
"message": "Current version (Stable)"
|
||||
},
|
||||
"Here you can find the documentation for current released version.": {
|
||||
"message": "Here you can find the documentation for current released version."
|
||||
},
|
||||
"Documentation": {
|
||||
"message": "Documentation"
|
||||
},
|
||||
"Release Notes": {
|
||||
"message": "Release Notes"
|
||||
},
|
||||
"Past versions": {
|
||||
"message": "Past versions"
|
||||
},
|
||||
"Here you can find documentation for previous versions of Docusaurus.": {
|
||||
"message": "Here you can find documentation for previous versions of Docusaurus."
|
||||
},
|
||||
"homepage.createBrowserAnimation": {
|
||||
"message": "Create WebdriverIO Demo"
|
||||
},
|
||||
"homepage.highlight.getStartedOnYouTube": {
|
||||
"message": "Start learning more about WebdriverIO and how to get started {youtubeLink}."
|
||||
},
|
||||
"homepage.highlight.getStartedOnYouTube.label;": {
|
||||
"message": "on YouTube",
|
||||
"description": "The label for the link to my YouTube"
|
||||
},
|
||||
"homepage.highlight.conferenceTalk": {
|
||||
"message": "The community around WebdriverIO is actively speaking on various user groups or conferences about specific topics around automated testing with WebdriverIO. Check out this talk on {youtubeLink} by {videoAuthor} at {conferenceLink}."
|
||||
},
|
||||
"homepage.highlight.conferenceTalk.label": {
|
||||
"message": "My favourite features of WebdriverIO",
|
||||
"description": "The conference talk title"
|
||||
},
|
||||
"homepage.highlight.youtubeChannels": {
|
||||
"message": "There is also many YouTube Channels with useful tutorials by community members such as {channelOne}, {channelTwo} or {channelThree}."
|
||||
},
|
||||
"homepage.highlight.devtools": {
|
||||
"message": "WebdriverIO not only runs automation based on the WebDriver protocol, it also leverages native browser APIs to enable integrations to popular developer tools such as {devtoolsLink} or {lighthouseLink}. With the {devtoolsServiceLink} plugin you have access to commands for validating if you app is a valid PWA application as well as to commands for capturing frontend performance metrics such as `speedIndex` and others."
|
||||
},
|
||||
"homepage.features.crossBrowser": {
|
||||
"message": "Cross browser support via automation through {webdriverLink} and {bidiLink}."
|
||||
},
|
||||
"homepage.features.mobile": {
|
||||
"message": "Run WebdriverIO on real mobile devices, smart TVs or other IoT devices through {appiumLink}."
|
||||
},
|
||||
"homepage.features.support": {
|
||||
"message": "Running a {supportChannelLink} with over 8k members and a rich ecosystem of community maintained plugins."
|
||||
},
|
||||
"homepage.tagline": {
|
||||
"message": "Next-gen browser and mobile automation test framework for Node.js"
|
||||
},
|
||||
"homepage.componentTesting.para1": {
|
||||
"message": "WebdriverIO is an {allInOne} framework for your web app development. It enables you to run small and lightweight component tests as well as running e2e test scenarios in the browser or on a mobile device. This guarantees that you to do the testing in an environment {usedBy}."
|
||||
},
|
||||
"features.realEnvironments": {
|
||||
"message": "Test in Real Environments"
|
||||
},
|
||||
"features.versatile": {
|
||||
"message": "Versatile and Feature Rich"
|
||||
},
|
||||
"features.autoWait": {
|
||||
"message": "Auto Wait"
|
||||
},
|
||||
"features.standards": {
|
||||
"message": "Based on Web Standards"
|
||||
},
|
||||
"features.mobileSupport": {
|
||||
"message": "Native Mobile Support"
|
||||
},
|
||||
"features.community": {
|
||||
"message": "Committed Community"
|
||||
},
|
||||
"homepage.componentTesting.heading": {
|
||||
"message": "E2E and Unit / Component Testing in real Browser!"
|
||||
},
|
||||
"homepage.componentTesting.para2": {
|
||||
"message": "It comes with smart selector strategies that simplify interacting e.g. with {reactComponents} or running deep selector queries with nested shadow DOM trees. As interactions happen through a standardized automation protocol it is guaranteed they behave natively and aren't just JavaScript emulated."
|
||||
},
|
||||
"Who is using WebdriverIO?": {
|
||||
"message": "Who is using WebdriverIO?"
|
||||
},
|
||||
"all in one": {
|
||||
"message": "all in one"
|
||||
},
|
||||
"used by your users": {
|
||||
"message": "used by your users"
|
||||
}
|
||||
}
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
{
|
||||
"title": {
|
||||
"message": "المدونة",
|
||||
"description": "The title for the blog used in SEO"
|
||||
},
|
||||
"description": {
|
||||
"message": "المدونة",
|
||||
"description": "The description for the blog used in SEO"
|
||||
},
|
||||
"sidebar.title": {
|
||||
"message": "المشاركات الاخيرة",
|
||||
"description": "The label for the left sidebar"
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
{
|
||||
"version.label": {
|
||||
"message": "التالي",
|
||||
"description": "The label for version current"
|
||||
}
|
||||
}
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
---
|
||||
id: donate
|
||||
title: تبرع لنا
|
||||
---
|
||||
|
||||
إذا كانت شركتك تستخدم WebdriverIO وتستفيد منها. من فضلك اطلب من مديرك أو فريق التسويق الخاص بك دعم المشروع عن طريق التبرع للمجموعة. سيسمح الدعم للمشرفين بتخصيص مزيد من الوقت للصيانة والميزات الجديدة للجميع.
|
||||
|
||||
الرجاء مراجعة [صفحة OpenCollective](https://opencollective.com/webdriverio) للحصول على مزيد من المعلومات حول كيفية التبرع للمشروع.
|
||||
|
||||
شكراً لكل من تبرع أو سيتبرع بالمال للجماعة وبالتالي دعم المشروع وكل من ساهم فيه. هذا حقا يعني الكثير ❤️
|
||||
|
||||
## سياسات المصروفات
|
||||
|
||||
نحن نريد أن نكون شفافين في الطريقة التي نقبل بها النفقات للجماعة. وينبغي أن يكون لكل شخص الحق في المشاركة وأن يرسل النفقات اللازمة للتنمية في بعض المميزات. ونريد أيضا أن نعود إلى المجتمع بالسماح لنا بنفقات الفعاليات. قد تكون أنواع المصروفات التالية مؤهلة للاسترداد من المجموعة:
|
||||
|
||||
### مصاريف الفعالية
|
||||
|
||||
إذا قمت باستضافة حدث أو فعالية يتحدث فيه متحدث عن استخدام WebdriverIO ومميزاته يمكنك حساب نفقة ما يصل إلى __100 $__. تشمل متطلبات السداد لنفقات الحدث ما يلي:
|
||||
|
||||
- يجب أن تشارك أنت أو حساب الحدث المشروع على وسائل التواصل الاجتماعي (Twitter أو Facebook أو LinkedIn) 3 مرات على الأقل
|
||||
- يجب أن تحتوي صفحة الحدث على شعار WebdriverIO وربط لصفحة المشروع في وصف الاجتماع الخاص بك
|
||||
- يجب عليك استخدام الأموال لنفقات مناسبة مثل المأكولات أو المشروبات أو تأجير المكان أو تأجير المعدات.
|
||||
- يجب عليك تقديم إيصالات مع طلب الاسترداد الخاص بك.
|
||||
|
||||
### مصاريف التطوير
|
||||
|
||||
إذا كنت قد أنجزت أعمال التطوير في أي من المستودعات داخل مؤسسة GitHub WebdriverIO ، فيمكنك سداد ما يصل إلى __$ 1000__ إذا تم استيفاء المتطلبات التالية:
|
||||
|
||||
- You must have submitted qualifying pull requests that have closed at least 10 issues that were labeled with [`Expensable 💸`](https://github.com/webdriverio/webdriverio/labels/Expensable%20%F0%9F%92%B8)
|
||||
- Every additional issue closed with that label can be expensed with $100
|
||||
- You must submit links to all issues you’ve closed due to your pull requests
|
||||
- In order to close the ticket automatically, you must have one commit message with the Fix keyword. For example, Fix #1234 to close ticket #1234.
|
||||
- Pull Requests must be merged by someone from the [core team](https://github.com/webdriverio/webdriverio/blob/main/AUTHORS.md#tsc-technical-steering-committee). If there are several Pull Requests, the core team member either selects the most recent one or the best one - that’s up to them to decide what is best for the project.
|
||||
- You must claim an [`Expensable 💸`](https://github.com/webdriverio/webdriverio/labels/Expensable%20%F0%9F%92%B8) issue by commenting to the issue thread to ensure that no one else is working on the same issue.
|
||||
- Anyone contributing to WebdriverIO is eligible to expense their work if the implemented features or bug fixes are not objectives of a commercial job.
|
||||
|
||||
### Travel Expenses
|
||||
|
||||
If you are a member of the [Technical Steering Committee team](https://github.com/webdriverio/webdriverio/blob/main/AUTHORS.md#tsc-technical-steering-committee) you are eligible to expense flights and hotel accommodations for travel to conferences or meetups as part of a speaking engagement on WebdriverIO, not paid by the event itself or a company. You may expense up to $500. Reimbursement requirements for travel expenses include:
|
||||
|
||||
- You must send out a post from your main social media account (e.g. Twitter, LinkedIn or personal blog) thanking all contributors of the collective after the event took place.
|
||||
- You must use the funds for qualified travel expenses such as ground or air transportation to the event and hotel accomodations.
|
||||
- You must submit receipts with your reimbursement request.
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
---
|
||||
id: أحداث
|
||||
title: أحداث
|
||||
---
|
||||
|
||||
# WebdriverIO Events
|
||||
|
||||
The WebdriverIO community organizes events around the world. The project is completely community driven and we encourage and support everyone who likes to host and run WebdriverIO meetups and events.
|
||||
|
||||
<EventList></EventList>
|
||||
|
||||
# Host your own WebdriverIO Event?
|
||||
|
||||
You would like to host a WebdriverIO meetup? Awesome ❤️! We would love to support you and provide Pizza and drinks. Let's get in touch: please [raise an issue](https://github.com/webdriverio/webdriverio/issues/new?assignees=\&labels=Event+%F0%9F%93%85%2CNeeds+Triaging+%E2%8F%B3\&projects=\&template=event-proposal.yml\&title=%5B%F0%9F%93%85+Event+Suggestion%5D%3A+%3Ctitle%3E) in the WebdriverIO repository and we will take it from there.
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
---
|
||||
id: materials
|
||||
title: Materials
|
||||
---
|
||||
|
||||
If you want to use WebdriverIO materials for your own workshops or events feel free to grab them directly from here.
|
||||
|
||||
## Workshops
|
||||
|
||||
The WebdriverIO community gives workshops around the world. Feel free to use these materials to host your own.
|
||||
|
||||
- [webdriverio/workshop](https://github.com/webdriverio/workshop)
|
||||
|
||||
## Assets
|
||||
|
||||
If you create your own WebdriverIO content for education purposes, feel free to grab these assets to include in your materials.
|
||||
|
||||
### Roboter Logo
|
||||
|
||||
 
|
||||
|
||||
### Project Logo
|
||||
|
||||

|
||||
|
|
@ -1,25 +0,0 @@
|
|||
---
|
||||
id: openofficehours
|
||||
title: Office Hours
|
||||
---
|
||||
|
||||
Contributing to a big Open Source project can be hard. Especially if the codebase is large and requires a lot of context to understand what certain changes do. Then there is the possibility that tools or infrastructure is being used that are unknown. Nevertheless the project depends on your contributions and help to identify bugs and fix them. How can we overcome this hurdle?
|
||||
|
||||
The WebdriverIO projects has introduced Open Office Hours that allow everyone to schedule individual 1:1 pairing sessions with the WebdriverIO contributors. This allows you to get started contributing to WebdriverIO easily with the help of people that know the code base well. We only ask you to pick [a task](https://github.com/webdriverio/webdriverio/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3Afirst-timers-only) you would like to work on up-front so that the pairing partner can be prepared for the session to provide better guidance. This is a free opportunity for you to not only give back to the WebdriverIO community but also get to know the team behind the project.
|
||||
|
||||
Currently we offer 4 slots a week from: every Wednesday 10am - 12am (CEST / GMT+2) for our European friends as well as: 11am - 13am (PDT / GMT -7) for people living on the west side of this hemisphere.
|
||||
|
||||
Before you book an appointment, make sure you can meet the following basic requirements needed to contribute to WebdriverIO:
|
||||
|
||||
- you've found an issue you like to contribute to in the [issue tracker](https://github.com/webdriverio/webdriverio/issues) (without an issue, your appointment will be cancelled immediatelly)
|
||||
- you have been working on a Node.js project before (these sessions can't be used to learn Node.js fundamentals)
|
||||
- you've read the [Contribution Guidelines](https://github.com/webdriverio/webdriverio/blob/main/CONTRIBUTING.md#set-up-project) and have the project set-up locally or in an ephemeral workspace
|
||||
- you can run the projects unit tests, and they are passing
|
||||
|
||||
After that, you can book an appointment at any time on:
|
||||
|
||||
[calendly.com/webdriverio/open-office-hours](https://calendly.com/webdriverio/open-office-hours)
|
||||
|
||||
All sessions will be held over [Zoom](https://zoom.us/) or [Google Hangouts](https://hangouts.google.com/) but alternative platforms are possible, please let us know in the comment section. Also please note that this is not a way to get individual consulting for your own projects. The time is to be used to work on WebdriverIO related bugs or features. For support questions we still ask you to use our [Discord Support Server](https://discord.webdriver.io).
|
||||
|
||||
We are looking forward to meet you all online and hope to find more collaborators helping us to push the project forward!
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
---
|
||||
id: support
|
||||
title: Need Help?
|
||||
---
|
||||
|
||||
This project is maintained by a dedicated group of people.
|
||||
|
||||
If you're interested in making a contribution to the WebdriverIO project, check out [the free "Office Hours" program](/blog/2020/07/01/office-hours) to help you with your efforts.
|
||||
|
||||
If you're seeking immediate help with your own project, reach out to a trusted WebdriverIO expert:
|
||||
|
||||
- [Kevin Lamping](https://www.codementor.io/@kevinlamping)
|
||||
- [Add your name to this list](https://github.com/webdriverio/webdriverio/edit/master/website/docs/Support.md)
|
||||
|
||||
## Stack Overflow
|
||||
|
||||
Stack Overflow is a popular forum to ask code-level questions or if you’re stuck with a specific error. Read through the [existing questions](https://stackoverflow.com/questions/tagged/webdriver-io) tagged with webdriver-io or [ask your own](https://stackoverflow.com/questions/ask?tags=webdriver-io)!
|
||||
|
||||
## Discussion Forums
|
||||
|
||||
There are many online forums which are a great place for discussion about WebdriverIO features, problems and bugs, e.g.:
|
||||
|
||||
- [GitHub Discussions](https://github.com/webdriverio/webdriverio/discussions)
|
||||
- [Discord Community Server](https://discord.webdriver.io)
|
||||
- [#webdriverio Channel on SeleniumHQ Slack](https://join.slack.com/t/seleniumhq/shared_invite/zt-vv33sc0w-VKKQop3WDV_lfrLXGGHvDw)
|
||||
|
||||
## News
|
||||
|
||||
For the latest news about WebdriverIO, [follow @webdriverio on Twitter](https://twitter.com/webdriverio) and the official [WebdriverIO blog](/blog) on this website.
|
||||
|
|
@ -1,87 +0,0 @@
|
|||
---
|
||||
id: team
|
||||
title: Team
|
||||
---
|
||||
|
||||
The WebdriverIO development is led by a dedicated team of developers and SDETs from various companies as well contributions from people around the world.
|
||||
|
||||
## Technical Steering Committee
|
||||
|
||||
<Card
|
||||
name="Adam Bjerstedt"
|
||||
avatar="https://avatars2.githubusercontent.com/u/4043890?s=460"
|
||||
github="abjerstedt"
|
||||
>
|
||||
Senior Software Engineer - Android at <a href="https://www.target.com/">Target</a>.
|
||||
</Card>
|
||||
|
||||
<Card
|
||||
name="Christian Bromann"
|
||||
avatar="https://avatars2.githubusercontent.com/u/731337?s=460"
|
||||
github="christian-bromann"
|
||||
twitter="bromann"
|
||||
>
|
||||
Staff Software Engineer at the Open Source Program Office <a href="https://saucelabs.com/">@saucelabs</a>.
|
||||
</Card>
|
||||
|
||||
<Card
|
||||
name="Erwin Heitzman"
|
||||
avatar="https://avatars.githubusercontent.com/u/15839059?s=460"
|
||||
github="erwinheitzman"
|
||||
>
|
||||
Senior Test Automation Engineer at <a href="https://detesters.nl/">deTesters</a>.
|
||||
</Card>
|
||||
|
||||
<Card
|
||||
name="Kevin Lamping"
|
||||
avatar="https://avatars0.githubusercontent.com/u/706039?s=460"
|
||||
github="klamping"
|
||||
twitter="klamping"
|
||||
>
|
||||
Senior Front End Engineer.
|
||||
</Card>
|
||||
|
||||
<Card
|
||||
name="Mykola Grybyk"
|
||||
avatar="https://avatars0.githubusercontent.com/u/25589559?s=460"
|
||||
github="mgrybyk"
|
||||
>
|
||||
Senior Software Development Engineer in Test.
|
||||
</Card>
|
||||
|
||||
<Card
|
||||
name="Will Brock"
|
||||
avatar="https://avatars3.githubusercontent.com/u/1300981?s=460"
|
||||
github="WillBrock"
|
||||
>
|
||||
Automation Engineering Manager at <a href="https://focusschoolsoftware.com/">Focus School Software</a>.
|
||||
</Card>
|
||||
|
||||
<Card
|
||||
name="Wim Selles"
|
||||
avatar="https://avatars2.githubusercontent.com/u/11979740?s=460"
|
||||
github="wswebcreation"
|
||||
twitter="wswebcreation"
|
||||
>
|
||||
Senior Solutions Architect <a href="https://saucelabs.com/">@saucelabs</a>.
|
||||
</Card>
|
||||
|
||||
## Project Collaborators
|
||||
|
||||
<Card
|
||||
name="Baruch Velez"
|
||||
avatar="https://avatars0.githubusercontent.com/u/14321495?s=460"
|
||||
github="baruchvlz"
|
||||
twitter="baruchvelez"
|
||||
>
|
||||
</Card>
|
||||
|
||||
<Card
|
||||
name="Boris Osipov"
|
||||
avatar="https://avatars.githubusercontent.com/u/6514276?v=4"
|
||||
github="BorisOsipov"
|
||||
twitter="bo_osipov"
|
||||
>
|
||||
</Card>
|
||||
|
||||
and [many many more](https://github.com/orgs/webdriverio/people).
|
||||
|
|
@ -1,74 +0,0 @@
|
|||
{
|
||||
"version.label": {
|
||||
"message": "التالي",
|
||||
"description": "The label for version current"
|
||||
},
|
||||
"sidebar.docs.category.Getting Started": {
|
||||
"message": "ابدأ الأن",
|
||||
"description": "The label for category Getting Started in sidebar docs"
|
||||
},
|
||||
"sidebar.docs.category.Core Concepts": {
|
||||
"message": "المفاهيم الأساسية",
|
||||
"description": "The label for category Core Concepts in sidebar docs"
|
||||
},
|
||||
"sidebar.docs.category.Configuration": {
|
||||
"message": "الضبط",
|
||||
"description": "The label for category Configuration in sidebar docs"
|
||||
},
|
||||
"sidebar.docs.category.Guides": {
|
||||
"message": "التعليمات",
|
||||
"description": "The label for category Guides in sidebar docs"
|
||||
},
|
||||
"sidebar.docs.category.Testrunner": {
|
||||
"message": "Testrunner",
|
||||
"description": "The label for category Testrunner in sidebar docs"
|
||||
},
|
||||
"sidebar.docs.category.Component Testing": {
|
||||
"message": "اختبار المكونات",
|
||||
"description": "The label for category Component Testing in sidebar docs"
|
||||
},
|
||||
"sidebar.docs.category.Framework Setup": {
|
||||
"message": "إعداد الإطار",
|
||||
"description": "The label for category Framework Setup in sidebar docs"
|
||||
},
|
||||
"sidebar.docs.category.Migrate": {
|
||||
"message": "الترحيل",
|
||||
"description": "The label for category Migrate in sidebar docs"
|
||||
},
|
||||
"sidebar.docs.category.Integration": {
|
||||
"message": "الدمج",
|
||||
"description": "The label for category Integration in sidebar docs"
|
||||
},
|
||||
"sidebar.docs.category.Reporter": {
|
||||
"message": "المراسل",
|
||||
"description": "The label for category Reporter in sidebar docs"
|
||||
},
|
||||
"sidebar.docs.category.Services": {
|
||||
"message": "خدمات",
|
||||
"description": "The label for category Services in sidebar docs"
|
||||
},
|
||||
"sidebar.api.category.Protocols": {
|
||||
"message": "البروتوكولات",
|
||||
"description": "The label for category Protocols in sidebar api"
|
||||
},
|
||||
"sidebar.api.category.browser": {
|
||||
"message": "browser",
|
||||
"description": "The label for category browser in sidebar api"
|
||||
},
|
||||
"sidebar.api.category.element": {
|
||||
"message": "element",
|
||||
"description": "The label for category element in sidebar api"
|
||||
},
|
||||
"sidebar.api.category.mock": {
|
||||
"message": "mock",
|
||||
"description": "The label for category mock in sidebar api"
|
||||
},
|
||||
"sidebar.flowcharts.category.Flowcharts": {
|
||||
"message": "المخططات الانسيابية",
|
||||
"description": "The label for category Flowcharts in sidebar flowcharts"
|
||||
},
|
||||
"sidebar.docs.category.Web Extension Testing": {
|
||||
"message": "اختبار ملحق الويب",
|
||||
"description": "The label for category Web Extension Testing in sidebar docs"
|
||||
}
|
||||
}
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
---
|
||||
id: api
|
||||
title: Introduction
|
||||
---
|
||||
|
||||
Welcome to the WebdriverIO API docs. These pages contain reference materials for all implemented protocol bindings and convenience commands. Protocol commands, including [WebDriver](/docs/api/webdriver), [WebDriver Bidi](/docs/api/webdriverBidi) or mobile commands like [Appium](http://appium.io) are commands that are directly send to the underlying driver backend. Convenience commands provided by the [`browser`](/docs/api/browser), [`element`](/docs/api/element) or [`mock`](/docs/api/mock) object offer higher level interactivity.
|
||||
|
||||
:::info
|
||||
These are the docs for the latest version (__>=8.x__) of WebdriverIO. If you are still using an older version, please visit the [old documentation websites](/versions)!
|
||||
:::
|
||||
|
||||
## Contribute
|
||||
|
||||
If you feel like you have a good example for a command, don't hesitate to open a PR and submit it. Just click on the orange link on the bottom left with the label _“Edit this page”_. Make sure you understand the way we write these docs by checking the [Contributing](https://github.com/webdriverio/webdriverio/blob/main/CONTRIBUTING.md) section.
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
---
|
||||
id: accessibility
|
||||
title: Accessibility Testing
|
||||
---
|
||||
|
||||
You can include accessibility tests within your WebdriverIO test suite using the open source accessibility tools [from Deque called Axe](https://www.deque.com/axe/). The set-up is very easy, all you need to do is to install the WebdriverIO Axe adapter via:
|
||||
|
||||
```bash npm2yarn
|
||||
npm install -g @axe-core/webdriverio
|
||||
```
|
||||
|
||||
The Axe adapter can be used either in [standalone or testrunner](/docs/setuptypes) mode by simply importing and initializing it with the [browser object](/docs/api/browser), e.g.:
|
||||
|
||||
```ts
|
||||
import { browser } from '@wdio/globals'
|
||||
import AxeBuilder from '@axe-core/webdriverio'
|
||||
|
||||
describe('Accessibility Test', () => {
|
||||
it('should get the accessibility results from a page', async () => {
|
||||
const builder = new AxeBuilder({ client: browser })
|
||||
|
||||
await browser.url('https://testingbot.com')
|
||||
const result = await builder.analyze()
|
||||
console.log('Acessibility Results:', result)
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
You can find more documentation on the Axe WebdriverIO adapter [on GitHub](https://github.com/dequelabs/axe-core-npm/tree/develop/packages/webdriverio#usage).
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
---
|
||||
id: appium
|
||||
title: Appium Setup
|
||||
---
|
||||
|
||||
With WebdriverIO you can test not only web application in the browser but also other platforms such as:
|
||||
|
||||
- 📱 mobile applications on iOS, Android or Tizen
|
||||
- 🖥️ desktop applications on macOS or Windows
|
||||
- 📺 as well as TV apps for Roku, tvOS, Android TV and Samsung
|
||||
|
||||
We recommend to use [Appium](https://appium.io/) to help you facilitate these types of tests. You can get an overview on Appium on their [official documentation page](https://appium.io/docs/en/2.0/intro/).
|
||||
|
||||
Setting up the right environment is not straight forward. Luckily the Appium ecosystem has great tooling around this to help you. To set-up one of the above environments, just run:
|
||||
|
||||
```sh
|
||||
$ npx appium-installer
|
||||
```
|
||||
|
||||
This will initiate the [appium-installer](https://github.com/AppiumTestDistribution/appium-installer) toolkit that guides you through the setup process.
|
||||
|
|
@ -1,111 +0,0 @@
|
|||
---
|
||||
id: assertion
|
||||
title: Assertion
|
||||
---
|
||||
|
||||
The [WDIO testrunner](https://webdriver.io/docs/clioptions) comes with a built in assertion library that allows you to make powerful assertions on various aspects of the browser or elements within your (web) application. It extends [Jests Matchers](https://jestjs.io/docs/en/using-matchers) functionality with additional, for e2e testing optimized, matchers, e.g.:
|
||||
|
||||
```js
|
||||
const $button = await $('button')
|
||||
await expect($button).toBeDisplayed()
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```js
|
||||
const selectOptions = await $$('form select>option')
|
||||
|
||||
// make sure there is at least one option in select
|
||||
await expect(selectOptions).toHaveChildren({ gte: 1 })
|
||||
```
|
||||
|
||||
For the full list, see the [expect API doc](/docs/api/expect-webdriverio).
|
||||
|
||||
## Migrating from Chai
|
||||
|
||||
[Chai](https://www.chaijs.com/) and [expect-webdriverio](https://github.com/webdriverio/expect-webdriverio#readme) can coexist, and with some minor adjustments a smooth transition to expect-webdriverio can be achieved. If you've upgraded to WebdriverIO v6 then by default you will have access to all the assertions from `expect-webdriverio` out of the box. This means that globally wherever you use `expect` you would call an `expect-webdriverio` assertion. That is, unless you you set [`injectGlobals`](/docs/configuration#injectglobals) to `false` or have explicitly overridden the global `expect` to use Chai. In this case you would not have access to any of the expect-webdriverio assertions without explicitly importing the expect-webdriverio package where you need it.
|
||||
|
||||
This guide will show examples of how to migrate from Chai if it has been overridden locally and how to migrate from Chai if it has been overridden globally.
|
||||
|
||||
### Local
|
||||
|
||||
Assume Chai was imported explicitly in a file, e.g.:
|
||||
|
||||
```js
|
||||
// myfile.js - original code
|
||||
import { expect as expectChai } from 'chai'
|
||||
|
||||
describe('Homepage', () => {
|
||||
it('should assert', async () => {
|
||||
await browser.url('./')
|
||||
expectChai(await browser.getUrl()).to.include('/login')
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
To migrate this code remove the Chai import and use the new expect-webdriverio assertion method `toHaveUrl` instead:
|
||||
|
||||
```js
|
||||
// myfile.js - migrated code
|
||||
describe('Homepage', () => {
|
||||
it('should assert', async () => {
|
||||
await browser.url('./')
|
||||
await expect(browser).toHaveUrl('/login') // new expect-webdriverio API method https://webdriver.io/docs/api/expect-webdriverio.html#tohaveurl
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
If you wanted to use both Chai and expect-webdriverio in the same file you would keep the Chai import and `expect` would default to the expect-webdriverio assertion, e.g.:
|
||||
|
||||
```js
|
||||
// myfile.js
|
||||
import { expect as expectChai } from 'chai'
|
||||
import { expect as expectWDIO } from '@wdio/globals'
|
||||
|
||||
describe('Element', () => {
|
||||
it('should be displayed', async () => {
|
||||
const isDisplayed = await $("#element").isDisplayed()
|
||||
expectChai(isDisplayed).to.equal(true); // Chai assertion
|
||||
})
|
||||
});
|
||||
|
||||
describe('Other element', () => {
|
||||
it('should not be displayed', async () => {
|
||||
await expectWDIO($("#element")).not.toBeDisplayed(); // expect-webdriverio assertion
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
### Global
|
||||
|
||||
Assume `expect` was globally overridden to use Chai. In order to use expect-webdriverio assertions we need to globally set a variable in the "before" hook, e.g.:
|
||||
|
||||
```js
|
||||
// wdio.conf.js
|
||||
before: async () => {
|
||||
await import('expect-webdriverio');
|
||||
global.wdioExpect = global.expect;
|
||||
const chai = await import('chai');
|
||||
global.expect = chai.expect;
|
||||
}
|
||||
```
|
||||
|
||||
Now Chai and expect-webdriverio can be used alongside each other. In your code you would use Chai and expect-webdriverio assertions as follows, e.g.:
|
||||
|
||||
```js
|
||||
// myfile.js
|
||||
describe('Element', () => {
|
||||
it('should be displayed', async () => {
|
||||
const isDisplayed = await $("#element").isDisplayed()
|
||||
expect(isDisplayed).to.equal(true); // Chai assertion
|
||||
});
|
||||
});
|
||||
|
||||
describe('Other element', () => {
|
||||
it('should not be displayed', async () => {
|
||||
await expectWdio($("#element")).not.toBeDisplayed(); // expect-webdriverio assertion
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
To migrate you would slowly move each Chai assertion over to expect-webdriverio. Once all Chai assertions have been replaced throughout the code base the "before" hook can be deleted. A global find and replace to replace all instances of `wdioExpect` to `expect` will then finish off the migration.
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
---
|
||||
id: autowait
|
||||
title: Auto-waiting
|
||||
---
|
||||
|
||||
When using a command that directly interacts with an element WebdriverIO will automatically wait for the element to be visible and interactable, no manual waits are needed when using the commands (think of click, setValue etc). An element is considered interactable when the conditions for [isClickable](https://webdriver.io/docs/api/element/isClickable) are met.
|
||||
|
||||
While WebdriverIO automatically waits for elements to become interactable, there are rare cases for which you might need to manually wait. For these rare cases we offer commands such as [`waitForDisplayed`](/docs/api/element/waitForDisplayed).
|
||||
|
||||
|
||||
## Implicit timeouts (not recommended)
|
||||
|
||||
While we do not recommend using this but the WebDriver protocol offers [implicit timeouts](https://w3c.github.io/webdriver/#timeouts) that allow specify how long the driver is suppose to wait for an element to show up. By default this timeout is set to `0` and therefore makes the driver return with an `no such element` error immediately if an element could not be found on the page. Increasing this timeout using the [`setTimeout`](/docs/api/browser/setTimeout) would make the driver wait and increases the chances that the element shows up eventually.
|
||||
|
||||
:::note
|
||||
|
||||
Read more about WebDriver and framework related timeouts in the [timeouts guide](/docs/timeouts)
|
||||
|
||||
:::
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
---
|
||||
id: autocompletion
|
||||
title: Autocompletion
|
||||
---
|
||||
|
||||
## IntelliJ
|
||||
|
||||
Autocompletion works out of the box in IDEA and WebStorm.
|
||||
|
||||
If you have been writing program code for a while, you probably like autocompletion. Autocomplete is available out of the box in many code editors.
|
||||
|
||||

|
||||
|
||||
Type definitions based on [JSDoc](http://usejsdoc.org/) is used for documenting code. It helps to see more additional details about parameters and their types.
|
||||
|
||||

|
||||
|
||||
Use standard shortcuts <kbd>⇧ + ⌥ + SPACE</kbd> on IntelliJ Platform to see available documentation:
|
||||
|
||||

|
||||
|
||||
## Visual Studio Code (VSCode)
|
||||
|
||||
Visual Studio Code usually has type support automatically integrated and there is no action needed.
|
||||
|
||||

|
||||
|
||||
If you use vanilla JavaScript and want to have proper type support you have to create a `jsconfig.json` in your project root and refer to used wdio packages, e.g.:
|
||||
|
||||
```json title="jsconfig.json"
|
||||
{
|
||||
"compilerOptions": {
|
||||
"types": [
|
||||
"node",
|
||||
"@wdio/globals/types",
|
||||
"@wdio/mocha-framework"
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
---
|
||||
id: automationProtocols
|
||||
title: Automation Protocols
|
||||
---
|
||||
|
||||
With WebdriverIO, you can choose between multiple automation technologies when running your E2E tests locally or in the cloud. By default, WebdriverIO will attempt to start a local automation session using the [WebDriver Bidi](https://w3c.github.io/webdriver-bidi/) protocol.
|
||||
|
||||
## WebDriver Bidi Protocol
|
||||
|
||||
The [WebDriver Bidi](https://w3c.github.io/webdriver-bidi/) is an automation protocol to automate browsers using bi-directional communication. It's the successor of the [WebDriver](https://w3c.github.io/webdriver/) protocol and enables a lot more introspection capabilities for various testing use cases.
|
||||
|
||||
This protocol is currently under development and new primitives might be added in the future. All browser vendors have committed to implementing this web standard and a lot of [primitives](https://wpt.fyi/results/webdriver/tests/bidi?label=experimental&label=master&aligned) have already been landed in browsers.
|
||||
|
||||
## WebDriver Protocol
|
||||
|
||||
> [WebDriver](https://w3c.github.io/webdriver/) is a remote control interface that enables introspection and control of user agents. It provides a platform- and language-neutral wire protocol as a way for out-of-process programs to remotely instruct the behavior of web browsers.
|
||||
|
||||
The WebDriver protocol was designed to automate a browser from the user perspective, meaning that everything a user is able to do, you can do with the browser. It provides a set of commands that abstract away common interactions with an application (e.g., navigating, clicking, or reading the state of an element). Since it is a web standard, it is well supported across all major browser vendors and also is being used as an underlying protocol for mobile automation using [Appium](http://appium.io).
|
||||
|
||||
To use this automation protocol, you need a proxy server that translates all commands and executes them in the target environment (i.e. the browser or the mobile app).
|
||||
|
||||
For browser automation, the proxy server is usually the browser driver. There are drivers available for all browsers:
|
||||
|
||||
- Chrome – [ChromeDriver](http://chromedriver.chromium.org/downloads)
|
||||
- Firefox – [Geckodriver](https://github.com/mozilla/geckodriver/releases)
|
||||
- Microsoft Edge – [Edge Driver](https://developer.microsoft.com/en-us/microsoft-edge/tools/webdriver/)
|
||||
- Internet Explorer – [InternetExplorerDriver](https://github.com/SeleniumHQ/selenium/wiki/InternetExplorerDriver)
|
||||
- Safari – [SafariDriver](https://developer.apple.com/documentation/webkit/testing_with_webdriver_in_safari)
|
||||
|
||||
For any kind of mobile automation, you’ll need to install and setup [Appium](http://appium.io). It will allow you to automate mobile (iOS/Android) or even desktop (macOS/Windows) applications using the same WebdriverIO setup.
|
||||
|
||||
There are also plenty of services that allow you to run your automation test in the cloud at high scale. Instead of having to setup all these drivers locally, you can just talk to these services (e.g. [Sauce Labs](https://saucelabs.com)) in the cloud and inspect the results on their platform. The communication between test script and automation environment will look as follows:
|
||||
|
||||

|
||||
|
||||
### Advantages
|
||||
|
||||
- Official W3C web standard, supported by all major browsers
|
||||
- Simplified protocol that covers common user interactions
|
||||
- Support for mobile automation (and even native desktop apps)
|
||||
- Can be used locally as well as in the cloud through services like [Sauce Labs](https://saucelabs.com)
|
||||
|
||||
### Disadvantages
|
||||
|
||||
- Not designed for in-depth browser analysis (e.g., tracing or intercepting network events)
|
||||
- Limited set of automation capabilities (e.g., no support to throttle CPU or network)
|
||||
- Additional effort to set up browser driver with selenium-standalone/chromedriver/etc
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
---
|
||||
id: babel
|
||||
title: Babel Setup
|
||||
---
|
||||
|
||||
To write tests using next-generation JavaScript features, you can use [Babel](https://babeljs.io) to compile your test files.
|
||||
|
||||
To do so, first install the necessary Babel dependencies:
|
||||
|
||||
```bash npm2yarn
|
||||
npm install --save-dev @babel/core @babel/cli @babel/preset-env @babel/register
|
||||
```
|
||||
|
||||
Make sure your [`babel.config.js`](https://babeljs.io/docs/en/config-files) is configured properly.
|
||||
|
||||
The simplest setup you can use is:
|
||||
|
||||
```js title="babel.config.js"
|
||||
module.exports = {
|
||||
presets: [
|
||||
['@babel/preset-env', {
|
||||
targets: {
|
||||
node: '20' // update with the target you are aiming for
|
||||
}
|
||||
}]
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
When using Babel in a monorepo things can get complicated if you do not follow the documentation steps, so make sure you read the [Babel documentation](https://babeljs.io/docs/config-files#monorepos) thoroughly.
|
||||
|
||||
To give you some guidance, here's some things to keep in mind:
|
||||
- You have to create a [root babel.config.json](https://babeljs.io/docs/config-files#root-babelconfigjson-file).
|
||||
- After you have done so and the project is correctly configured according to the documentation, you will have to make Babel look for the config by updating your wdio config files by adding the example found below.
|
||||
|
||||
```js
|
||||
require("@babel/register")({
|
||||
rootMode: "upward",
|
||||
});
|
||||
```
|
||||
|
||||
This will make Babel look for the closest `babel.config.json` that it can find upwards.
|
||||
|
||||
Once this is set up WebdriverIO will take care of the rest.
|
||||
|
||||
Alternatively you can configure how @babel/register is run through the environment variables for [@babel/register](https://babeljs.io/docs/babel-register#environment-variables).
|
||||
|
|
@ -1,84 +0,0 @@
|
|||
---
|
||||
id: bamboo
|
||||
title: Bamboo
|
||||
---
|
||||
|
||||
WebdriverIO offers a tight integration to CI systems like [Bamboo](https://www.atlassian.com/software/bamboo). With the [JUnit](https://webdriver.io/docs/junit-reporter.html) or [Allure](https://webdriver.io/docs/allure-reporter.html) reporter, you can easily debug your tests as well as keep track of your test results. The integration is pretty easy.
|
||||
|
||||
1. Install the JUnit test reporter: `$ npm install @wdio/junit-reporter --save-dev`)
|
||||
1. Update your config to save your JUnit results where Bamboo can find them, (and specify the `junit` reporter):
|
||||
|
||||
```js
|
||||
// wdio.conf.js
|
||||
module.exports = {
|
||||
// ...
|
||||
reporters: [
|
||||
'dot',
|
||||
['junit', {
|
||||
outputDir: './testresults/'
|
||||
}]
|
||||
],
|
||||
// ...
|
||||
}
|
||||
```
|
||||
Note: *It's always a good standard to keep the test results in separate folder than in the root folder.*
|
||||
|
||||
```js
|
||||
// wdio.conf.js - For tests running in parallel
|
||||
module.exports = {
|
||||
// ...
|
||||
reporters: [
|
||||
'dot',
|
||||
['junit', {
|
||||
outputDir: './testresults/',
|
||||
outputFileFormat: function (options) {
|
||||
return `results-${options.cid}.xml`;
|
||||
}
|
||||
}]
|
||||
],
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
The reports will be similar for all the frameworks and you can use anyone: Mocha, Jasmine or Cucumber.
|
||||
|
||||
By this time, we believe you have the tests written up and results are generated in `./testresults/` folder, and your Bamboo is up and running.
|
||||
|
||||
## Integrate your tests in Bamboo
|
||||
|
||||
1. Open your Bamboo project
|
||||
|
||||
> Create a new plan, link your repository (make sure it always points to newest version of your repository) and create your stages
|
||||
|
||||

|
||||
|
||||
I will go with the default stage and job. In your case, you can create your own stages and jobs
|
||||
|
||||

|
||||
2. Open your testing job and create tasks to run your tests in Bamboo
|
||||
> **Task 1:** Source Code Checkout
|
||||
> **Task 2:** Run your tests `npm i && npm run test`. You can use *Script* task and *Shell Interpreter* to run the above commands (This will generate the test results and save them in `./testresults/` folder)
|
||||
|
||||

|
||||
> **Task: 3** Add *jUnit Parser* task to parse your saved test results. Please specify the test results directory here (you can use Ant style patterns as well)
|
||||
|
||||

|
||||
|
||||
Note: *Make sure you are keeping the results parser task in *Final* section, so that it always get executed even if your test task is failed*
|
||||
> **Task: 4** (optional) In order to make sure that your test results are not messed up with old files, you can create a task to remove the `./testresults/` folder after a successful parse to Bamboo. You can add a shell script like `rm -f ./testresults/*.xml` to remove the results or `rm -r testresults` to remove the complete folder
|
||||
|
||||
Once the above *rocket science* is done, please enable the plan and run it. Your final output will be like:
|
||||
|
||||
## Successful Test
|
||||
|
||||

|
||||
|
||||
## Failed Test
|
||||
|
||||

|
||||
|
||||
## Failed and Fixed
|
||||
|
||||

|
||||
|
||||
Yay!! That's all. You have successfully integrated your WebdriverIO tests in Bamboo.
|
||||
|
|
@ -1,312 +0,0 @@
|
|||
---
|
||||
id: bestpractices
|
||||
title: Best Practices
|
||||
---
|
||||
|
||||
# Best Practices
|
||||
|
||||
This guide aims to share our best practices that help you write performant and resilient tests.
|
||||
|
||||
## Use resilient selectors
|
||||
|
||||
Using selectors that are resilient to changes in the DOM, you'll have a less or even no tests failing when the for example a class is removed from an element.
|
||||
|
||||
Classes can be applied to multiple elements and should be avoided if possible unless you deliberately want to fetch all elements with that class.
|
||||
|
||||
```js
|
||||
// 👎
|
||||
await $('.button')
|
||||
```
|
||||
|
||||
All these selectors should return a single element.
|
||||
|
||||
```js
|
||||
// 👍
|
||||
await $('aria/Submit')
|
||||
await $('[test-id="submit-button"]')
|
||||
await $('#submit-button')
|
||||
```
|
||||
|
||||
__Note:__ To find out all the possible selectors WebdriverIO supports, checkout our [Selectors](./Selectors.md) page.
|
||||
|
||||
## Limit the amount of element queries
|
||||
|
||||
Every time you use the [`$`](https://webdriver.io/docs/api/browser/$) or [`$$`](https://webdriver.io/docs/api/browser/$$) command (this includes chaining them), WebdriverIO tries to locate the element in the DOM. These queries are expensive so you should try to limit them as much as possible.
|
||||
|
||||
Queries three elements.
|
||||
|
||||
```js
|
||||
// 👎
|
||||
await $('table').$('tr').$('td')
|
||||
```
|
||||
|
||||
Queries only one element.
|
||||
|
||||
``` js
|
||||
// 👍
|
||||
await $('table tr td')
|
||||
```
|
||||
|
||||
The only time you should use chaining is when you want to combine different [selector strategies](https://webdriver.io/docs/selectors/#custom-selector-strategies). In the example we use the [Deep Selectors](https://webdriver.io/docs/selectors#deep-selectors), which is a strategy to go inside the shadow DOM of an element.
|
||||
|
||||
``` js
|
||||
// 👍
|
||||
await $('custom-datepicker').$('#calendar').$('aria/Select')
|
||||
```
|
||||
|
||||
### Prefer locating a single element instead of taking one from a list
|
||||
|
||||
It isn't always possible to do this but using CSS pseudo-classes like [:nth-child](https://developer.mozilla.org/en-US/docs/Web/CSS/:nth-child) you can match elements based on the indexes of the elements in the child list of their parents.
|
||||
|
||||
Queries all table rows.
|
||||
|
||||
```js
|
||||
// 👎
|
||||
await $$('table tr')[15]
|
||||
```
|
||||
|
||||
Queries a single table row.
|
||||
|
||||
```js
|
||||
// 👍
|
||||
await $('table tr:nth-child(15)')
|
||||
```
|
||||
|
||||
## Use the built-in assertions
|
||||
|
||||
Don't use manual assertions that do not automatically wait for the results to match as this will cause for flaky tests.
|
||||
|
||||
```js
|
||||
// 👎
|
||||
expect(await button.isDisplayed()).toBe(true)
|
||||
```
|
||||
|
||||
By using the built-in assertions WebdriverIO will automatically wait for the actual result to match the expected result, resulting in resilient tests. It achieves this by automatically retrying the assertion until it passes or times out.
|
||||
|
||||
```js
|
||||
// 👍
|
||||
await expect(button).toBeDisplayed()
|
||||
```
|
||||
|
||||
## Lazy loading and promise chaining
|
||||
|
||||
WebdriverIO has some tricks up it's sleeve when it comes to writing clean code as it can lazy load the element which allows you to chain your promises and reduces the amount of `await`. This also allows you to pass the element as a ChainablePromiseElement instead of an Element and for easier use with page objects.
|
||||
|
||||
So when do you have to use `await`? You should always use `await` with the exception of the `$` and `$$` command.
|
||||
|
||||
```js
|
||||
// 👎
|
||||
const div = await $('div')
|
||||
const button = await div.$('button')
|
||||
await button.click()
|
||||
// or
|
||||
await (await (await $('div')).$('button')).click()
|
||||
```
|
||||
|
||||
```js
|
||||
// 👍
|
||||
const button = $('div').$('button')
|
||||
await button.click()
|
||||
// or
|
||||
await $('div').$('button').click()
|
||||
```
|
||||
|
||||
## Don't overuse commands and assertions
|
||||
|
||||
When using expect.toBeDisplayed you implicitly also wait for the element to exist. There isn't a need to use the waitForXXX commands when you already have an assertion doing the same thing.
|
||||
|
||||
```js
|
||||
// 👎
|
||||
await button.waitForExist()
|
||||
await expect(button).toBeDisplayed()
|
||||
|
||||
// 👎
|
||||
await button.waitForDisplayed()
|
||||
await expect(button).toBeDisplayed()
|
||||
|
||||
// 👍
|
||||
await expect(button).toBeDisplayed()
|
||||
```
|
||||
|
||||
No need to wait for an element to exist or be displayed when interacting or when asserting something like it's text unless the element can explicitly be invisible (opacity: 0 for example) or can explicitly be disabled (disabled attribute for example) in which case waiting for the element to be displayed makes sense.
|
||||
|
||||
```js
|
||||
// 👎
|
||||
await expect(button).toBeExisting()
|
||||
await expect(button).toHaveText('Submit')
|
||||
|
||||
// 👎
|
||||
await expect(button).toBeDisplayed()
|
||||
await expect(button).toHaveText('Submit')
|
||||
|
||||
// 👎
|
||||
await expect(button).toBeDisplayed()
|
||||
await button.click()
|
||||
```
|
||||
|
||||
```js
|
||||
// 👍
|
||||
await button.click()
|
||||
|
||||
// 👍
|
||||
await expect(button).toHaveText('Submit')
|
||||
```
|
||||
|
||||
## Dynamic Tests
|
||||
|
||||
Use environment variables to store dynamic test data e.g. secret credentials, within your environment rather than hard code them into the test. Head over to the [Parameterize Tests](parameterize-tests) page for more information on this topic.
|
||||
|
||||
## Lint your code
|
||||
|
||||
Using eslint to lint your code you can potentionally catch errors early, use our [linting rules](https://www.npmjs.com/package/eslint-plugin-wdio) to make sure that some of the best practices are always applied.
|
||||
|
||||
## Don't pause
|
||||
|
||||
It can be tempting to use the pause command but using this is a bad idea as it isn't resilient and will only cause for flaky tests in the long run.
|
||||
|
||||
```js
|
||||
// 👎
|
||||
await nameInput.setValue('Bob')
|
||||
await browser.pause(200) // wait for submit button to enable
|
||||
await submitFormButton.click()
|
||||
|
||||
// 👍
|
||||
await nameInput.setValue('Bob')
|
||||
await submitFormButton.waitForEnabled()
|
||||
await submitFormButton.click()
|
||||
```
|
||||
|
||||
## Async loops
|
||||
|
||||
When you have some asynchronous code that you want to repeat, it is important to know that not all loops can do this. For example, the Array's forEach function does not allow for asynchronous callbacks as can be read over on [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach).
|
||||
|
||||
__Note:__ You can still use these when you do not need the operation to be synchronous like in shown in this example `console.log(await $$('h1').map((h1) => h1.getText()))`.
|
||||
|
||||
Below are some examples of what this means.
|
||||
|
||||
The following will not work as asynchronous callback are not supported.
|
||||
|
||||
```js
|
||||
// 👎
|
||||
const characters = 'this is some example text that should be put in order'
|
||||
characters.forEach(async (character) => {
|
||||
await browser.keys(character)
|
||||
})
|
||||
```
|
||||
|
||||
The following will work.
|
||||
|
||||
```js
|
||||
// 👍
|
||||
const characters = 'this is some example text that should be put in order'
|
||||
for (const character of characters) {
|
||||
await browser.keys(character)
|
||||
}
|
||||
```
|
||||
|
||||
## Keep it simple
|
||||
|
||||
Sometimes we see our users map data like text or values. This often isn't needed and is often a code smell, check the examples below why this is the case.
|
||||
|
||||
```js
|
||||
// 👎 too complex, synchronous assertion, use the built-in assertions to prevent flaky tests
|
||||
const headerText = ['Products', 'Prices']
|
||||
const texts = await $$('th').map(e => e.getText());
|
||||
expect(texts).toBe(headerText)
|
||||
|
||||
// 👎 too complex
|
||||
const headerText = ['Products', 'Prices']
|
||||
const columns = await $$('th');
|
||||
await expect(columns).toBeElementsArrayOfSize(2);
|
||||
for (let i = 0; i < columns.length; i++) {
|
||||
await expect(columns[i]).toHaveText(headerText[i]);
|
||||
}
|
||||
|
||||
// 👎 finds elements by their text but does not take into account the position of the elements
|
||||
await expect($('th=Products')).toExist();
|
||||
await expect($('th=Prices')).toExist();
|
||||
```
|
||||
|
||||
```js
|
||||
// 👍 use unique identifiers (often used for custom elements)
|
||||
await expect($('[data-testid="Products"]')).toHaveText('Products');
|
||||
// 👍 accessibility names (often used for native html elements)
|
||||
await expect($('aria/Product Prices')).toHaveText('Prices');
|
||||
```
|
||||
|
||||
Another thing we sometimes see is that simple things have an overcomplicated solution.
|
||||
|
||||
```js
|
||||
// 👎
|
||||
class BadExample {
|
||||
public async selectOptionByValue(value: string) {
|
||||
await $('select').click();
|
||||
await $$('option')
|
||||
.map(async function (element) {
|
||||
const hasValue = (await element.getValue()) === value;
|
||||
if (hasValue) {
|
||||
await $(element).click();
|
||||
}
|
||||
return hasValue;
|
||||
});
|
||||
}
|
||||
|
||||
public async selectOptionByText(text: string) {
|
||||
await $('select').click();
|
||||
await $$('option')
|
||||
.map(async function (element) {
|
||||
const hasText = (await element.getText()) === text;
|
||||
if (hasText) {
|
||||
await $(element).click();
|
||||
}
|
||||
return hasText;
|
||||
});
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
// 👍
|
||||
class BetterExample {
|
||||
public async selectOptionByValue(value: string) {
|
||||
await $('select').click();
|
||||
await $(`option[value=${value}]`).click();
|
||||
}
|
||||
|
||||
public async selectOptionByText(text: string) {
|
||||
await $('select').click();
|
||||
await $(`option=${text}]`).click();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Executing code in parallel
|
||||
|
||||
If you do not care about the order in which some code is ran you can utilise [`Promise.all`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all) to speed up the execution.
|
||||
|
||||
__Note:__ Since this makes the code harder to read you could abstract this away using a page object or a function, although you should also question if the benefit in performance is worth the cost of readability.
|
||||
|
||||
```js
|
||||
// 👎
|
||||
await name.setValue('Bob')
|
||||
await email.setValue('bob@webdriver.io')
|
||||
await age.setValue('50')
|
||||
await submitFormButton.waitForEnabled()
|
||||
await submitFormButton.click()
|
||||
|
||||
// 👍
|
||||
await Promise.all([
|
||||
name.setValue('Bob'),
|
||||
email.setValue('bob@webdriver.io'),
|
||||
age.setValue('50'),
|
||||
])
|
||||
await submitFormButton.waitForEnabled()
|
||||
await submitFormButton.click()
|
||||
```
|
||||
|
||||
If abstracted away it could look something like below where the logic is put in a method called submitWithDataOf and the data is retrieved by the Person class.
|
||||
|
||||
```js
|
||||
// 👍
|
||||
await form.submitData(new Person('bob@webdriver.io'))
|
||||
```
|
||||
|
|
@ -1,363 +0,0 @@
|
|||
---
|
||||
id: boilerplates
|
||||
title: Boilerplate Projects
|
||||
---
|
||||
|
||||
Over time, our community has developed several projects that you can use as inspiration to set up your own test suite.
|
||||
|
||||
# v8 Boilerplate Projects
|
||||
|
||||
## [webdriverio/cucumber-boilerplate](https://github.com/webdriverio/cucumber-boilerplate)
|
||||
|
||||
Our very own boilerplate for Cucumber test suites. We created over 150 predefined step definitions for you, so you can start writing feature files in your project right away.
|
||||
|
||||
- Framework:
|
||||
- Cucumber
|
||||
- WebdriverIO
|
||||
- Features:
|
||||
- Over 150 predefined steps that cover almost everything you need
|
||||
- Integrates WebdriverIO's Multiremote functionality
|
||||
- Own demo app
|
||||
|
||||
## [webdriverio/jasmine-boilerplate](https://github.com/webdriverio/jasmine-boilerplate)
|
||||
Boilerplate project to run WebdriverIO tests with Jasmine using Babel features and the page objects pattern.
|
||||
|
||||
- Frameworks
|
||||
- WebdriverIO
|
||||
- Jasmine
|
||||
- Features
|
||||
- Page Object Pattern
|
||||
- Sauce Labs integration
|
||||
|
||||
## [webdriverio/electron-boilerplate](https://github.com/webdriverio/electron-boilerplate)
|
||||
Boilerplate project to run WebdriverIO tests on a minimal Electron application.
|
||||
|
||||
- Frameworks
|
||||
- WebdriverIO
|
||||
- Mocha
|
||||
- Features
|
||||
- Electron API mocking
|
||||
|
||||
## [syamphaneendra/webdriverio-web-mobile-boilerplate](https://github.com/syamphaneendra/webdriverio-web-mobile-boilerplate)
|
||||
|
||||
This boilerplate project has WebdriverIO 8 tests with cucumber and typescript, followed by the page objects pattern.
|
||||
|
||||
- Frameworks:
|
||||
- WebdriverIO v8
|
||||
- Cucumber v8
|
||||
|
||||
- Features:
|
||||
- Typescript v5
|
||||
- Page Object Pattern
|
||||
- Prettier
|
||||
- Multi browser support
|
||||
- Chrome
|
||||
- Firefox
|
||||
- Edge
|
||||
- Safari
|
||||
- Standalone
|
||||
- Crossbrowser parallel execution
|
||||
- Appium
|
||||
- Cloud testing Integration with BrowserStack & Sauce Labs
|
||||
- Docker service
|
||||
- Share data service
|
||||
- Separate config files for each service
|
||||
- Testdata management & read by user type
|
||||
- Reporting
|
||||
- Dot
|
||||
- Spec
|
||||
- Multiple cucumber html report with failure screenshots
|
||||
- Gitlab pipelines for Gitlab repository
|
||||
- Github actions for Github repository
|
||||
- Docker compose for setting up the docker hub
|
||||
- Accessibility testing using AXE
|
||||
- Visual testing using Applitools
|
||||
- Log mechansim
|
||||
|
||||
## [amiya-pattnaik/webdriverIO-with-cucumberBDD](https://github.com/amiya-pattnaik/webdriverIO-with-cucumberBDD)
|
||||
|
||||
- Framework: WDIO-V8 with Cucumber (V8x).
|
||||
- Features:
|
||||
- Page Objects Model uses with ES6 /ES7 style class base approach and TypeScript support
|
||||
- Examples of multi selector option to query element with more than one selector at a time
|
||||
- Examples of multi browser and headless browser execution using - Chrome and Firefox
|
||||
- Cloud testing Integration with BrowserStack, Sauce Labs, LambdaTest
|
||||
- Examples of read/write data from MS-Excel for easy test data management from external data sources with examples
|
||||
- Database support to any RDBMS (Oracle, MySql, TeraData, Vertica etc.), executing any queries / fetching result set etc. with examples for E2E testing
|
||||
- Multiple reporting (Spec, Xunit/Junit, Allure, JSON) and Hosting Allure and Xunit/Junit reporting on WebServer.
|
||||
- Examples with demo app https://search.yahoo.com/ and http://the-internet.herokuapp.com.
|
||||
- BrowserStack, Sauce Labs, LambdaTest and Appium specific `.config` file (for playback on mobile device). For one click Appium setup on local machine for iOS and Android refer to [appium-setup-made-easy-OSX](https://github.com/amiya-pattnaik/appium-setup-made-easy-OSX).
|
||||
|
||||
## [amiya-pattnaik/webdriverIO-with-mochaBDD](https://github.com/amiya-pattnaik/webdriverIO-with-mochaBDD)
|
||||
|
||||
- Framework: WDIO-V8 with Mocha (V10x).
|
||||
- Features:
|
||||
- Page Objects Model uses with ES6 /ES7 style class base approach and TypeScript support
|
||||
- Examples with demo app https://search.yahoo.com and http://the-internet.herokuapp.com
|
||||
- Examples of multi browser and headless browser execution using - Chrome and Firefox
|
||||
- Cloud testing Integration with BrowserStack, Sauce Labs, LambdaTest
|
||||
- Multiple reporting (Spec, Xunit/Junit, Allure, JSON) and Hosting Allure and Xunit/Junit reporting on WebServer.
|
||||
- Examples of read/write data from MS-Excel for easy test data management from external data sources with examples
|
||||
- Examples of DB connect to any RDBMS (Oracle, MySql, TeraData, Vertica etc.), any query execution / fetching result set etc. with examples for E2E testing
|
||||
- BrowserStack, Sauce Labs, LambdaTest and Appium specific `.config` file (for playback on mobile device). For one click Appium setup on local machine for iOS and Android refer to [appium-setup-made-easy-OSX](https://github.com/amiya-pattnaik/appium-setup-made-easy-OSX).
|
||||
|
||||
## [amiya-pattnaik/webdriverIO-with-jasmineBDD](https://github.com/amiya-pattnaik/webdriverIO-with-jasmineBDD)
|
||||
|
||||
- Framework: WDIO-V8 with Jasmine (V4x).
|
||||
- Features:
|
||||
- Page Objects Model uses with ES6 /ES7 style class base approach and TypeScript support
|
||||
- Examples with demo app https://search.yahoo.com and http://the-internet.herokuapp.com
|
||||
- Examples of multi browser and headless browser execution using - Chrome and Firefox
|
||||
- Cloud testing Integration with BrowserStack, Sauce Labs, LambdaTest
|
||||
- Multiple reporting (Spec, Xunit/Junit, Allure, JSON) and Hosting Allure and Xunit/Junit reporting on WebServer.
|
||||
- Examples of read/write data from MS-Excel for easy test data management from external data sources with examples
|
||||
- Examples of DB connect to any RDBMS (Oracle, MySql, TeraData, Vertica etc.), any query execution / fetching result set etc. with examples for E2E testing
|
||||
- BrowserStack, Sauce Labs, LambdaTest and Appium specific `.config` file ( for playback on mobile device). For one click Appium setup on local machine for iOS and Android refer to [appium-setup-made-easy-OSX](https://github.com/amiya-pattnaik/appium-setup-made-easy-OSX).
|
||||
|
||||
## [klassijs/klassi-js (cucumber-template)](https://github.com/klassijs/klassi-example-test-suite.git)
|
||||
|
||||
- Frameworks
|
||||
- WebdriverIO (v8)
|
||||
- Cucumber (v8)
|
||||
|
||||
- Features
|
||||
- Contain sample test scenario in cucumber
|
||||
- Integrated cucumber html reports with Embedded videos on failures
|
||||
- Integrated Lambdatest and CircleCI services
|
||||
- Integrated Visual, Accessibility and API testing
|
||||
- Integrated Email functionality
|
||||
- Integrated s3 bucket for test reports storage and retrieval
|
||||
|
||||
## [serenity-js/serenity-js-mocha-webdriverio-template/](https://github.com/serenity-js/serenity-js-mocha-webdriverio-template/)
|
||||
|
||||
[Serenity/JS](https://serenity-js.org?pk_campaign=wdio8&pk_source=webdriver.io) template project to help you get started with acceptance testing your web applications using the latest WebdriverIO, Mocha, and Serenity/JS.
|
||||
|
||||
- Frameworks
|
||||
- WebdriverIO (v8)
|
||||
- Mocha (v10)
|
||||
- Serenity/JS (v3)
|
||||
- Serenity BDD reporting
|
||||
|
||||
- Features
|
||||
- [Screenplay Pattern](https://serenity-js.org/handbook/design/screenplay-pattern/?pk_campaign=wdio8&pk_source=webdriver.io)
|
||||
- Automatic screenshots on test failure, embedded in reports
|
||||
- Continuous Integration (CI) setup using [GitHub Actions](https://github.com/serenity-js/serenity-js-mocha-webdriverio-template/blob/main/.github/workflows/main.yml)
|
||||
- [Demo Serenity BDD reports](https://serenity-js.github.io/serenity-js-mocha-webdriverio-template/) published to GitHub Pages
|
||||
- TypeScript
|
||||
- ESLint
|
||||
|
||||
## [serenity-js/serenity-js-cucumber-webdriverio-template/](https://github.com/serenity-js/serenity-js-cucumber-webdriverio-template/)
|
||||
|
||||
[Serenity/JS](https://serenity-js.org?pk_campaign=wdio8&pk_source=webdriver.io) template project to help you get started with acceptance testing your web applications using the latest WebdriverIO, Cucumber, and Serenity/JS.
|
||||
|
||||
- Frameworks
|
||||
- WebdriverIO (v8)
|
||||
- Cucumber (v9)
|
||||
- Serenity/JS (v3)
|
||||
- Serenity BDD reporting
|
||||
|
||||
- Features
|
||||
- [Screenplay Pattern](https://serenity-js.org/handbook/design/screenplay-pattern/?pk_campaign=wdio8&pk_source=webdriver.io)
|
||||
- Automatic screenshots on test failure, embedded in reports
|
||||
- Continuous Integration (CI) setup using [GitHub Actions](https://github.com/serenity-js/serenity-js-cucumber-webdriverio-template/blob/main/.github/workflows/main.yml)
|
||||
- [Demo Serenity BDD reports](https://serenity-js.github.io/serenity-js-mocha-webdriverio-template/) published to GitHub Pages
|
||||
- TypeScript
|
||||
- ESLint
|
||||
|
||||
## [Muralijc/wdio-headspin-boilerplate](https://github.com/Muralijc/Wdio-Headspin-boilerplate/)
|
||||
Boilerplate project to run WebdriverIO tests in Headspin Cloud (https://www.headspin.io/) using Cucumber features, and the page objects pattern.
|
||||
- Frameworks
|
||||
- WebdriverIO (v8)
|
||||
- Cucumber (v8)
|
||||
|
||||
- Features
|
||||
- Cloud integration with [Headspin](https://www.headspin.io/)
|
||||
- Supports Page Object Model
|
||||
- Contains sample Scenarios written in Declarative style of BDD
|
||||
- Integrated cucumber html reports
|
||||
|
||||
# v7 Boilerplate Projects
|
||||
|
||||
## [webdriverio/appium-boilerplate](https://github.com/webdriverio/appium-boilerplate/)
|
||||
|
||||
Boilerplate project to run Appium tests with WebdriverIO for:
|
||||
|
||||
- iOS/Android Native Apps
|
||||
- iOS/Android Hybrid Apps
|
||||
- Android Chrome and iOS Safari browser
|
||||
|
||||
This boilerplate includes the following:
|
||||
|
||||
- Framework: Mocha
|
||||
- Features:
|
||||
- Configs for:
|
||||
- iOS and Android app
|
||||
- iOS and Android browsers
|
||||
- Helpers for:
|
||||
- WebView
|
||||
- Gestures
|
||||
- Native alerts
|
||||
- Pickers
|
||||
- Tests examples for:
|
||||
- WebView
|
||||
- Login
|
||||
- Forms
|
||||
- Swipe
|
||||
- Browsers
|
||||
|
||||
## [serhatbolsu/webdriverio-mocha-uiautomation-boiler](https://github.com/serhatbolsu/webdriverio-mocha-uiautomation-boiler)
|
||||
ATDD WEB tests with Mocha, WebdriverIO v6 with PageObject
|
||||
|
||||
- Frameworks
|
||||
- WebdriverIO (v7)
|
||||
- Mocha
|
||||
- Features
|
||||
- [Page Object](pageobjects) Model
|
||||
- Sauce Labs integration with [Sauce Service](https://github.com/webdriverio/webdriverio/blob/main/packages/wdio-sauce-service/README.md)
|
||||
- Allure Report
|
||||
- Automatic screenshots capture for failing tests
|
||||
- CircleCI example
|
||||
- ESLint
|
||||
|
||||
## [WarleyGabriel/demo-webdriverio-mocha](https://github.com/WarleyGabriel/demo-webdriverio-mocha)
|
||||
|
||||
Boilerplate project to run E2E tests with Mocha.
|
||||
|
||||
- Frameworks:
|
||||
- WebdriverIO (v7)
|
||||
- Mocha
|
||||
- Features:
|
||||
- TypeScript
|
||||
- [Expect-webdriverio](https://github.com/webdriverio/expect-webdriverio)
|
||||
- [Visual regression tests](https://github.com/wswebcreation/wdio-image-comparison-service)
|
||||
- Page Object Pattern
|
||||
- [Commit lint](https://github.com/conventional-changelog/commitlint) and [Commitizen](https://github.com/commitizen/cz-cli#making-your-repo-commitizen-friendly)
|
||||
- ESlint
|
||||
- Prettier
|
||||
- Husky
|
||||
- Github Actions example
|
||||
- Allure report (screenshots on failure)
|
||||
|
||||
## [17thSep/WebdriverIO_Master](https://github.com/17thSep/WebdriverIO_Master)
|
||||
|
||||
Boilerplate project to run **WebdriverIO v7** tests for the following:
|
||||
|
||||
[WDIO 7 scripts with TypeScript in Cucumber Framework](https://github.com/17thSep/WebdriverIO_Master/tree/master/TypeScript/Cucumber) [WDIO 7 scripts with TypeScript in Mocha Framework](https://github.com/17thSep/WebdriverIO_Master/tree/master/TypeScript/Mocha) [Run WDIO 7 script in Docker](https://github.com/17thSep/WebdriverIO_Master/tree/master/TypeScript/Docker) [Network logs](https://github.com/17thSep/MonitorNetworkLogs/)
|
||||
|
||||
Boiler plate project for:
|
||||
|
||||
- Capture Network Logs
|
||||
- Capture all GET/POST calls or a specific REST API
|
||||
- Assert Request parameters
|
||||
- Assert Response parameters
|
||||
- Store all the response in a separate file
|
||||
|
||||
## [Arjun-Ar91/Wdio7-appium-cucumber](https://github.com/Arjun-Ar91/Wdio7-appium-cucumber.git)
|
||||
|
||||
Boilerplate project to run appium tests for native and mobile browser using cucumber v7 and wdio v7 with page object pattern.
|
||||
|
||||
- Frameworks
|
||||
- WebdriverIO v7
|
||||
- Cucumber v7
|
||||
- Appium
|
||||
|
||||
- Features
|
||||
- Native Android and iOS apps
|
||||
- Android Chrome browser
|
||||
- iOS Safari browser
|
||||
- Page Object Model
|
||||
- Contains sample test scenarios in cucumber
|
||||
- Integrated with multiple cucumber html reports
|
||||
|
||||
## [praveendvd/webdriverIODockerBoilerplate/](https://github.com/praveendvd/webdriverIODockerBoilerplate)
|
||||
|
||||
This a template project to help you show how you can run webdriverio test from Web applications using the latest WebdriverIO, and Cucumber framework. This project intends to act as a baseline image that you can use to understand how to run WebdriverIO tests in docker
|
||||
|
||||
This project includes:
|
||||
|
||||
- DockerFile
|
||||
- cucumber Project
|
||||
|
||||
Read more at: [Medium Blog](https://praveendavidmathew.medium.com/running-webdriverio-in-wsl2-windows-91d3a0dc7746)
|
||||
|
||||
## [praveendvd/WebdriverIO_electronAppAutomation_boilerplate/](https://github.com/praveendvd/WebdriverIO_electronAppAutomation_boilerplate)
|
||||
|
||||
This a template project to help you show how you can run electronJS tests using WebdriverIO. This project intends to act as a baseline image that you can use to understand how to run WebdriverIO electronJS tests.
|
||||
|
||||
This project include:
|
||||
|
||||
- Sample electronjs app
|
||||
- Sample cucumber test scripts
|
||||
|
||||
Read more at: [Medium Blog](https://praveendavidmathew.medium.com/first-step-into-automation-of-electronjs-applications-ef89b7423ddd)
|
||||
|
||||
## [praveendvd/webdriverIO_winappdriver_boilerplate/](https://github.com/praveendvd/webdriverIO_winappdriver_boilerplate)
|
||||
|
||||
This a template project to help you show how you can automate windows application using winappdriver and WebdriverIO . This project intends to act as a baseline image that you can use to understand how to run windappdriver and WebdriverIO tests.
|
||||
|
||||
Read more at: [Medium Blog](https://praveendavidmathew.medium.com/winappdriver-first-step-into-windows-app-test-automation-using-webdriverio-and-winappdriver-46320d89570b)
|
||||
|
||||
## [praveendvd/appium-chromedriver-multiremote-wdio-boilerplate/](https://github.com/praveendvd/appium-chromedriver-multiremote-wdio-boilerplate)
|
||||
|
||||
|
||||
This a template project to help you show how you can run webdriverio multiremote capability with latest WebdriverIO, and Jasmine framework. This project intends to act as a baseline image that you can use to understand how to run WebdriverIO tests in docker
|
||||
|
||||
This project uses:
|
||||
|
||||
- chromedriver
|
||||
- jasmine
|
||||
- appium
|
||||
|
||||
## [webdriverio-roku-appium-boilerplate](https://github.com/AntonKostenko/webdriverIO-roku-appium)
|
||||
|
||||
Template project to run appium tests on real Roku devices using mocha with page object pattern.
|
||||
|
||||
- Frameworks
|
||||
- WebdriverIO Async v7
|
||||
- Appium 2.0
|
||||
- Mocha v7
|
||||
- Allure Reporting
|
||||
|
||||
- Features
|
||||
- Page Object Model
|
||||
- Typescript
|
||||
- Screenshot on failure
|
||||
- Example tests using a sample Roku channel
|
||||
|
||||
## [krishnapollu/wdio-cucumber-poc](https://github.com/krishnapollu/wdio-cucumber-poc)
|
||||
|
||||
PoC project for E2E Multiremote Cucumber tests as well as Data driven Mocha tests
|
||||
|
||||
- Framework:
|
||||
- Cucumber (v8)
|
||||
- WebdriverIO (v8)
|
||||
- Mocha (v8)
|
||||
|
||||
- Features:
|
||||
- Cucumber based E2E Tests
|
||||
- Mocha based Data Driven Tests
|
||||
- Web only Tests - in Local as well as cloud platforms
|
||||
- Mobile Only tests - local as well as remote cloud emulators (or devices)
|
||||
- Web + Mobile Tests - Multiremote - local as well as cloud platforms
|
||||
- Multiple Reports integrated including Allure
|
||||
- Test Data ( JSON / XLSX ) handled globally so as to write the data (created on the fly) to a file post test execution
|
||||
- Github workflow to run the test and upload the allure report
|
||||
|
||||
## [Rondleysg/wdio-multiremote-appium-chromedriver-boilerplate](https://github.com/Rondleysg/wdio-multiremote-appium-chromedriver-boilerplate)
|
||||
|
||||
This is a boilerplate project to help show how to run webdriverio multi-remote using appium and chromedriver service with the latest WebdriverIO.
|
||||
|
||||
- Frameworks
|
||||
- WebdriverIO (v9)
|
||||
- Appium (v2)
|
||||
- Mocha
|
||||
|
||||
- Features
|
||||
- [Page Object](pageobjects) Model
|
||||
- Typescript
|
||||
- Web + Mobile Tests - Multiremote
|
||||
- Native Android and iOS apps
|
||||
- Appium
|
||||
- Chromedriver
|
||||
- ESLint
|
||||
- Tests examples for Login in http://the-internet.herokuapp.com and [WebdriverIO native demo app](https://github.com/webdriverio/native-demo-app)
|
||||
|
|
@ -1,438 +0,0 @@
|
|||
---
|
||||
id: capabilities
|
||||
title: Capabilities
|
||||
---
|
||||
|
||||
A capability is a definition for a remote interface. It helps WebdriverIO to understand in which browser or mobile environment you like to run your tests on. Capabilities are less crucial when developing tests locally as you run it on one remote interface most of the time but becomes more important when running a large set of integration tests in CI/CD.
|
||||
|
||||
:::info
|
||||
|
||||
The format of a capability object is well defined by the [WebDriver specification](https://w3c.github.io/webdriver/#capabilities). The WebdriverIO testrunner will fail early if user defined capabilities do not adhere to that specification.
|
||||
|
||||
:::
|
||||
|
||||
## Custom Capabilities
|
||||
|
||||
While the amount of fixed defined capabilities is very low, everyone can provide and accept custom capabilities that are specific to the automation driver or remote interface:
|
||||
|
||||
### Browser Specific Capability Extensions
|
||||
|
||||
- `goog:chromeOptions`: [Chromedriver](https://chromedriver.chromium.org/capabilities) extensions, only applicable for testing in Chrome
|
||||
- `moz:firefoxOptions`: [Geckodriver](https://firefox-source-docs.mozilla.org/testing/geckodriver/Capabilities.html) extensions, only applicable for testing in Firefox
|
||||
- `ms:edgeOptions`: [EdgeOptions](https://learn.microsoft.com/en-us/microsoft-edge/webdriver-chromium/capabilities-edge-options) for specifying the environment when using EdgeDriver for testing Chromium Edge
|
||||
|
||||
### Cloud Vendor Capability Extensions
|
||||
|
||||
- `sauce:options`: [Sauce Labs](https://docs.saucelabs.com/dev/test-configuration-options/#w3c-webdriver-browser-capabilities--optional)
|
||||
- `bstack:options`: [BrowserStack](https://www.browserstack.com/docs/automate/selenium/organize-tests)
|
||||
- `tb:options`: [TestingBot](https://testingbot.com/support/other/test-options)
|
||||
- and many more...
|
||||
|
||||
### Automation Engine Capability Extensions
|
||||
|
||||
- `appium:xxx`: [Appium](https://appium.github.io/appium.io/docs/en/writing-running-appium/caps/)
|
||||
- `selenoid:xxx`: [Selenoid](https://github.com/aerokube/selenoid/blob/master/docs/special-capabilities.adoc)
|
||||
- and many more...
|
||||
|
||||
### WebdriverIO Capabilities to manage browser driver options
|
||||
|
||||
WebdriverIO manages installing and running browser driver for you. WebdriverIO uses a custom capability that allows you to pass in parameters to the driver.
|
||||
|
||||
#### `wdio:chromedriverOptions`
|
||||
|
||||
Specific options passed into Chromedriver when starting it.
|
||||
|
||||
#### `wdio:geckodriverOptions`
|
||||
|
||||
Specific options passed into Geckodriver when starting it.
|
||||
|
||||
#### `wdio:edgedriverOptions`
|
||||
|
||||
Specific options passed into Edgedriver when starting it.
|
||||
|
||||
#### `wdio:safaridriverOptions`
|
||||
|
||||
Specific options passed into Safari when starting it.
|
||||
|
||||
#### `wdio:maxInstances`
|
||||
|
||||
Maximum number of total parallel running workers for the specific browser/capability. Takes precedence over [maxInstances](#configuration#maxInstances) and [maxInstancesPerCapability](configuration/#maxinstancespercapability).
|
||||
|
||||
Type: `number`
|
||||
|
||||
#### `wdio:specs`
|
||||
|
||||
Define specs for test execution for that browser/capability. Same as the [regular `specs` configuration option](configuration#specs), but specific to the browser/capability. Takes precedence over `specs`.
|
||||
|
||||
Type: `(String | String[])[]`
|
||||
|
||||
#### `wdio:exclude`
|
||||
|
||||
Exclude specs from test execution for that browser/capability. Same as the [regular `exclude` configuration option](configuration#exclude), but specific to the browser/capability. Takes precedence over `exclude`.
|
||||
|
||||
Type: `String[]`
|
||||
|
||||
#### `wdio:enforceWebDriverClassic`
|
||||
|
||||
By default, WebdriverIO attempts to establish a WebDriver Bidi session. If you don't prefer that, you can set this flag to disable this behavior.
|
||||
|
||||
Type: `boolean`
|
||||
|
||||
#### Common Driver Options
|
||||
|
||||
While all driver offer different parameters for configuration, there are some common ones that WebdriverIO understand and uses for setting up your driver or browser:
|
||||
|
||||
##### `cacheDir`
|
||||
|
||||
The path to the root of the cache directory. This directory is used to store all drivers that are downloaded when attempting to start a session.
|
||||
|
||||
Type: `string`<br /> Default: `process.env.WEBDRIVER_CACHE_DIR || os.tmpdir()`
|
||||
|
||||
##### `binary`
|
||||
|
||||
Path to a custom driver binary. If set WebdriverIO won't attempt to download a driver but will use the one provided by this path. Make sure the driver is compatible with the browser you are using.
|
||||
|
||||
You can provide this path via `CHROMEDRIVER_PATH`, `GECKODRIVER_PATH` or `EDGEDRIVER_PATH` environment variables.
|
||||
|
||||
Type: `string`
|
||||
|
||||
:::caution
|
||||
|
||||
If the driver `binary` is set, WebdriverIO won't attempt to download a driver but will use the one provided by this path. Make sure the driver is compatible with the browser you are using.
|
||||
|
||||
:::
|
||||
|
||||
#### Browser Specific Driver Options
|
||||
|
||||
In order to propagate options to the driver you can use the following custom capabilities:
|
||||
|
||||
- Chrome or Chromium: `wdio:chromedriverOptions`
|
||||
- Firefox: `wdio:geckodriverOptions`
|
||||
- Microsoft Egde: `wdio:edgedriverOptions`
|
||||
- Safari: `wdio:safaridriverOptions`
|
||||
|
||||
<Tabs
|
||||
defaultValue="chrome"
|
||||
values={[
|
||||
{label: 'wdio:chromedriverOptions', value: 'chrome'},
|
||||
{label: 'wdio:geckodriverOptions', value: 'firefox'},
|
||||
{label: 'wdio:edgedriverOptions', value: 'msedge'},
|
||||
{label: 'wdio:safaridriverOptions', value: 'safari'},
|
||||
]
|
||||
}>
|
||||
<TabItem value="chrome">
|
||||
|
||||
##### adbPort
|
||||
The port on which the ADB driver should run.
|
||||
|
||||
Example: `9515`
|
||||
|
||||
Type: `number`
|
||||
|
||||
##### urlBase
|
||||
Base URL path prefix for commands, e.g. `wd/url`.
|
||||
|
||||
Example: `/`
|
||||
|
||||
Type: `string`
|
||||
|
||||
##### logPath
|
||||
Write server log to file instead of stderr, increases log level to `INFO`
|
||||
|
||||
Type: `string`
|
||||
|
||||
##### logLevel
|
||||
Set log level. Possible options `ALL`, `DEBUG`, `INFO`, `WARNING`, `SEVERE`, `OFF`.
|
||||
|
||||
Type: `string`
|
||||
|
||||
##### verbose
|
||||
Log verbosely (equivalent to `--log-level=ALL`)
|
||||
|
||||
Type: `boolean`
|
||||
|
||||
##### silent
|
||||
Log nothing (equivalent to `--log-level=OFF`)
|
||||
|
||||
Type: `boolean`
|
||||
|
||||
##### appendLog
|
||||
Append log file instead of rewriting.
|
||||
|
||||
Type: `boolean`
|
||||
|
||||
##### replayable
|
||||
Log verbosely and don't truncate long strings so that the log can be replayed (experimental).
|
||||
|
||||
Type: `boolean`
|
||||
|
||||
##### readableTimestamp
|
||||
Add readable timestamps to log.
|
||||
|
||||
Type: `boolean`
|
||||
|
||||
##### enableChromeLogs
|
||||
Show logs from the browser (overrides other logging options).
|
||||
|
||||
Type: `boolean`
|
||||
|
||||
##### bidiMapperPath
|
||||
Custom bidi mapper path.
|
||||
|
||||
Type: `string`
|
||||
|
||||
##### allowedIps
|
||||
Comma-separated allowlist of remote IP addresses which are allowed to connect to EdgeDriver.
|
||||
|
||||
Type: `string[]`<br />
|
||||
Default: `['']`
|
||||
|
||||
##### allowedOrigins
|
||||
Comma-separated allowlist of request origins which are allowed to connect to EdgeDriver. Using `*` to allow any host origin is dangerous!
|
||||
|
||||
Type: `string[]`<br />
|
||||
Default: `['*']`
|
||||
|
||||
##### spawnOpts
|
||||
Options to be passed into the driver process.
|
||||
|
||||
Type: `SpawnOptionsWithoutStdio | SpawnOptionsWithStdioTuple<StdioOption, StdioOption, StdioOption>`<br />
|
||||
Default: `undefined`
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="firefox">
|
||||
|
||||
See all Geckodriver options in the official [driver package](https://github.com/webdriverio-community/node-geckodriver#options).
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="msedge">
|
||||
|
||||
See all Edgedriver options in the official [driver package](https://github.com/webdriverio-community/node-edgedriver#options).
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="safari">
|
||||
|
||||
See all Safaridriver options in the official [driver package](https://github.com/webdriverio-community/node-safaridriver#options).
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## Special Capabilities for Specific Use Cases
|
||||
|
||||
This is a list of examples showing which capabilities need to be applied to achieve a certain use case.
|
||||
|
||||
### Run Browser Headless
|
||||
|
||||
Running a headless browser means to run a browser instance without window or UI. This is mostly used within CI/CD environments where no display is used. To run a browser in headless mode, apply the following capabilities:
|
||||
|
||||
<Tabs
|
||||
defaultValue="chrome"
|
||||
values={[
|
||||
{label: 'Chrome', value: 'chrome'},
|
||||
{label: 'Firefox', value: 'firefox'},
|
||||
{label: 'Microsoft Edge', value: 'msedge'},
|
||||
{label: 'Safari', value: 'safari'},
|
||||
]
|
||||
}>
|
||||
<TabItem value="chrome">
|
||||
|
||||
```ts
|
||||
{
|
||||
browserName: 'chrome', // or 'chromium'
|
||||
'goog:chromeOptions': {
|
||||
args: ['headless', 'disable-gpu']
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="firefox">
|
||||
|
||||
```ts
|
||||
browserName: 'firefox',
|
||||
'moz:firefoxOptions': {
|
||||
args: ['-headless']
|
||||
}
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="msedge">
|
||||
|
||||
```ts
|
||||
browserName: 'msedge',
|
||||
'ms:edgeOptions': {
|
||||
args: ['--headless']
|
||||
}
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="safari">
|
||||
|
||||
It seems that Safari [doesn't support](https://discussions.apple.com/thread/251837694) running in headless mode.
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
### Automate Different Browser Channels
|
||||
|
||||
If you like to test a browser version that is not yet released as stable, e.g. Chrome Canary, you can do so by setting capabilities and pointing to the browser you like to start, e.g.:
|
||||
|
||||
<Tabs
|
||||
defaultValue="chrome"
|
||||
values={[
|
||||
{label: 'Chrome', value: 'chrome'},
|
||||
{label: 'Firefox', value: 'firefox'},
|
||||
{label: 'Microsoft Edge', value: 'msedge'},
|
||||
{label: 'Safari', value: 'safari'},
|
||||
]
|
||||
}>
|
||||
<TabItem value="chrome">
|
||||
|
||||
When testing on Chrome, WebdriverIO will automatically download the desired browser version and driver for you based on the defined `browserVersion`, e.g.:
|
||||
|
||||
```ts
|
||||
{
|
||||
browserName: 'chrome', // or 'chromium'
|
||||
browserVersion: '116' // or '116.0.5845.96', 'stable', 'dev', 'canary', 'beta' or 'latest' (same as 'canary')
|
||||
}
|
||||
```
|
||||
|
||||
If you like to test a manually downloaded browser, you can provide a binary path to the browser via:
|
||||
|
||||
```ts
|
||||
{
|
||||
browserName: 'chrome', // or 'chromium'
|
||||
'goog:chromeOptions': {
|
||||
binary: '/Applications/Google\ Chrome\ Canary.app/Contents/MacOS/Google\ Chrome\ Canary'
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Additionally, if you like to use a manually downloaded driver, you can provide a binary path to the driver via:
|
||||
|
||||
```ts
|
||||
{
|
||||
browserName: 'chrome', // or 'chromium'
|
||||
'wdio:chromedriverOptions': {
|
||||
binary: '/path/to/chromdriver'
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="firefox">
|
||||
|
||||
When testing on Firefox, WebdriverIO will automatically download the desired browser version and driver for you based on the defined `browserVersion`, e.g.:
|
||||
|
||||
```ts
|
||||
{
|
||||
browserName: 'firefox',
|
||||
browserVersion: '119.0a1' // or 'latest'
|
||||
}
|
||||
```
|
||||
|
||||
If you like to test a manually downloaded version you can provide a binary path to the browser via:
|
||||
|
||||
```ts
|
||||
{
|
||||
browserName: 'firefox',
|
||||
'moz:firefoxOptions': {
|
||||
binary: '/Applications/Firefox\ Nightly.app/Contents/MacOS/firefox'
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Additionally, if you like to use a manually downloaded driver, you can provide a binary path to the driver via:
|
||||
|
||||
```ts
|
||||
{
|
||||
browserName: 'firefox',
|
||||
'wdio:geckodriverOptions': {
|
||||
binary: '/path/to/geckodriver'
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="msedge">
|
||||
|
||||
When testing on Microsoft Edge, make sure you have the desired browser version installed on your machine. You can point WebdriverIO to the browser to execute via:
|
||||
|
||||
```ts
|
||||
{
|
||||
browserName: 'msedge',
|
||||
'ms:edgeOptions': {
|
||||
binary: '/Applications/Microsoft\ Edge\ Canary.app/Contents/MacOS/Microsoft\ Edge\ Canary'
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
WebdriverIO will automatically download the desired driver version for you based on the defined `browserVersion`, e.g.:
|
||||
|
||||
```ts
|
||||
{
|
||||
browserName: 'msedge',
|
||||
browserVersion: '109' // or '109.0.1467.0', 'stable', 'dev', 'canary', 'beta'
|
||||
}
|
||||
```
|
||||
|
||||
Additionally, if you like to use a manually downloaded driver, you can provide a binary path to the driver via:
|
||||
|
||||
```ts
|
||||
{
|
||||
browserName: 'msedge',
|
||||
'wdio:edgedriverOptions': {
|
||||
binary: '/path/to/msedgedriver'
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="safari">
|
||||
|
||||
When testing on Safari, make sure you have the [Safari Technology Preview](https://developer.apple.com/safari/technology-preview/) installed on your machine. You can point WebdriverIO to that version via:
|
||||
|
||||
```ts
|
||||
{
|
||||
browserName: 'safari technology preview'
|
||||
}
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## Extend Custom Capabilities
|
||||
|
||||
If you like to define your own set of capabilities in order to e.g. store arbitrary data to be used within the tests for that specific capability, you can do so by e.g. setting:
|
||||
|
||||
```js title=wdio.conf.ts
|
||||
export const config = {
|
||||
// ...
|
||||
capabilities: [{
|
||||
browserName: 'chrome',
|
||||
'custom:caps': {
|
||||
// custom configurations
|
||||
}
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
It is advised to follow the [W3C protocol](https://w3c.github.io/webdriver/#dfn-extension-capability) when it comes to capability naming which requires a `:` (colon) character, denoting an implementation specific namespace. Within your tests you can access your custom capability through, e.g.:
|
||||
|
||||
```ts
|
||||
browser.capabilities['custom:caps']
|
||||
```
|
||||
|
||||
In order to ensure type safety you can extend WebdriverIOs capability interface via:
|
||||
|
||||
```ts
|
||||
declare global {
|
||||
namespace WebdriverIO {
|
||||
interface Capabilities {
|
||||
'custom:caps': {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
|
@ -1,183 +0,0 @@
|
|||
---
|
||||
id: cloudservices
|
||||
title: Using Cloud Services
|
||||
---
|
||||
|
||||
Using on-demand services like Sauce Labs, Browserstack, TestingBot, LambdaTest or Perfecto with WebdriverIO is pretty simple. All you need to do is to set your service's `user` and `key` in your options.
|
||||
|
||||
Optionally, you can also parametrize your test by setting cloud-specific capabilities like `build`. If you only want to run cloud services in Travis, you can use the `CI` environment variable to check if you are in Travis and modify the config accordingly.
|
||||
|
||||
```js
|
||||
// wdio.conf.js
|
||||
export let config = {...}
|
||||
if (process.env.CI) {
|
||||
config.user = process.env.SAUCE_USERNAME
|
||||
config.key = process.env.SAUCE_ACCESS_KEY
|
||||
}
|
||||
```
|
||||
|
||||
## Sauce Labs
|
||||
|
||||
You can set up your tests to run remotely in [Sauce Labs](https://saucelabs.com).
|
||||
|
||||
The only requirement is to set the `user` and `key` in your config (either exported by `wdio.conf.js` or passed into `webdriverio.remote(...)`) to your Sauce Labs username and access key.
|
||||
|
||||
You can also pass in any optional [test configuration option](https://docs.saucelabs.com/dev/test-configuration-options/) as a key/value in the capabilities for any browser.
|
||||
|
||||
### Sauce Connect
|
||||
|
||||
If you want to run tests against a server that is not accessible to the Internet (like on `localhost`), then you need to use [Sauce Connect](https://docs.saucelabs.com/secure-connections/#sauce-connect-proxy).
|
||||
|
||||
It is out of the scope of WebdriverIO to support this, so you'll have to start it by yourself.
|
||||
|
||||
If you are using the WDIO testrunner download and configure the [`@wdio/sauce-service`](https://github.com/webdriverio/webdriverio/tree/main/packages/wdio-sauce-service) in your `wdio.conf.js`. It helps getting Sauce Connect running and comes with additional features that better integrate your tests into the Sauce service.
|
||||
|
||||
### With Travis CI
|
||||
|
||||
Travis CI, however, does [have support](http://docs.travis-ci.com/user/sauce-connect/#Setting-up-Sauce-Connect) for starting Sauce Connect before each test, so following their directions for that is an option.
|
||||
|
||||
If you do so, you must set the `tunnel-identifier` test configuration option in each browser's `capabilities`. Travis sets this to the `TRAVIS_JOB_NUMBER` environmental variable by default.
|
||||
|
||||
Also, if you want to have Sauce Labs group your tests by build number, you can set the `build` to `TRAVIS_BUILD_NUMBER`.
|
||||
|
||||
Lastly, if you set `name`, this changes the name of this test in Sauce Labs for this build. If you are using the WDIO testrunner combined with the [`@wdio/sauce-service`](https://github.com/webdriverio/webdriverio/tree/main/packages/wdio-sauce-service), WebdriverIO automatically sets a proper name for the test.
|
||||
|
||||
Example `capabilities`:
|
||||
|
||||
```javascript
|
||||
browserName: 'chrome',
|
||||
version: '27.0',
|
||||
platform: 'XP',
|
||||
'tunnel-identifier': process.env.TRAVIS_JOB_NUMBER,
|
||||
name: 'integration',
|
||||
build: process.env.TRAVIS_BUILD_NUMBER
|
||||
```
|
||||
|
||||
### Timeouts
|
||||
|
||||
Since you are running your tests remotely, it might be necessary to increase some timeouts.
|
||||
|
||||
You can change the [idle timeout](https://docs.saucelabs.com/dev/test-configuration-options/#idletimeout) by passing `idle-timeout` as a test configuration option. This controls how long Sauce will wait between commands before closing the connection.
|
||||
|
||||
## BrowserStack
|
||||
|
||||
WebdriverIO also has a [Browserstack](https://www.browserstack.com) integration built-in.
|
||||
|
||||
The only requirement is to set the `user` and `key` in your config (either exported by `wdio.conf.js` or passed into `webdriverio.remote(...)`) to your Browserstack automate username and access key.
|
||||
|
||||
You can also pass in any optional [supported capabilities](https://www.browserstack.com/automate/capabilities) as a key/value in the capabilities for any browser. If you set `browserstack.debug` to `true` it will record a screencast of the session, which might be helpful.
|
||||
|
||||
### Local Testing
|
||||
|
||||
If you want to run tests against a server that is not accessible to the Internet (like on `localhost`), then you need to use [Local Testing](https://www.browserstack.com/local-testing#command-line).
|
||||
|
||||
It is out of the scope of WebdriverIO to support this, so you must start it by yourself.
|
||||
|
||||
If you do use local, you should set `browserstack.local` to `true` in your capabilities.
|
||||
|
||||
If you are using the WDIO testrunner, download and configure the [`@wdio/browserstack-service`](https://github.com/webdriverio/webdriverio/tree/master/packages/wdio-browserstack-service) in your `wdio.conf.js`. It helps get BrowserStack running, and comes with additional features that better integrate your tests into the BrowserStack service.
|
||||
|
||||
### With Travis CI
|
||||
|
||||
If you want to add Local Testing in Travis, you have to start it by yourself.
|
||||
|
||||
The following script will download and start it in the background. You should run this in Travis before starting the tests.
|
||||
|
||||
```sh
|
||||
wget https://www.browserstack.com/browserstack-local/BrowserStackLocal-linux-x64.zip
|
||||
unzip BrowserStackLocal-linux-x64.zip
|
||||
./BrowserStackLocal -v -onlyAutomate -forcelocal $BROWSERSTACK_ACCESS_KEY &
|
||||
sleep 3
|
||||
```
|
||||
|
||||
Also, you might wish set the `build` to the Travis build number.
|
||||
|
||||
Example `capabilities`:
|
||||
|
||||
```javascript
|
||||
browserName: 'chrome',
|
||||
project: 'myApp',
|
||||
version: '44.0',
|
||||
build: `myApp #${process.env.TRAVIS_BUILD_NUMBER}.${process.env.TRAVIS_JOB_NUMBER}`,
|
||||
'browserstack.local': 'true',
|
||||
'browserstack.debug': 'true'
|
||||
```
|
||||
|
||||
## TestingBot
|
||||
|
||||
The only requirement is to set the `user` and `key` in your config (either exported by `wdio.conf.js` or passed into `webdriverio.remote(...)`) to your [TestingBot](https://testingbot.com) username and secret key.
|
||||
|
||||
You can also pass in any optional [supported capabilities](https://testingbot.com/support/other/test-options) as a key/value in the capabilities for any browser.
|
||||
|
||||
### Local Testing
|
||||
|
||||
If you want to run tests against a server that is not accessible to the Internet (like on `localhost`), then you need to use [Local Testing](https://testingbot.com/support/other/tunnel). TestingBot provides a Java-based tunnel to allow you to test websites not accessible from the internet.
|
||||
|
||||
Their tunnel support page contains the information necessary to get this up and running.
|
||||
|
||||
If you are using the WDIO testrunner, download and configure the [`@wdio/testingbot-service`](https://github.com/webdriverio/webdriverio/tree/main/packages/wdio-testingbot-service) in your `wdio.conf.js`. It helps get TestingBot running, and comes with additional features that better integrate your tests into the TestingBot service.
|
||||
|
||||
## LambdaTest
|
||||
|
||||
[LambdaTest](https://www.lambdatest.com) integration is also built-in.
|
||||
|
||||
The only requirement is to set the `user` and `key` in your config (either exported by `wdio.conf.js` or passed into `webdriverio.remote(...)`) to your LambdaTest account username and access key.
|
||||
|
||||
You can also pass in any optional [supported capabilities](https://www.lambdatest.com/capabilities-generator/) as a key/value in the capabilities for any browser. If you set `visual` to `true` it will record a screencast of the session, which might be helpful.
|
||||
|
||||
### Tunnel for local testing
|
||||
|
||||
If you want to run tests against a server that is not accessible to the Internet (like on `localhost`), then you need to use [Local Testing](https://www.lambdatest.com/support/docs/testing-locally-hosted-pages/).
|
||||
|
||||
It is out of the scope of WebdriverIO to support this, so you must start it by yourself.
|
||||
|
||||
If you do use local, you should set `tunnel` to `true` in your capabilities.
|
||||
|
||||
If you are using the WDIO testrunner, download and configure the [`wdio-lambdatest-service`](https://github.com/LambdaTest/wdio-lambdatest-service) in your `wdio.conf.js`. It helps get LambdaTest running, and comes with additional features that better integrate your tests into the LambdaTest service.
|
||||
|
||||
### With Travis CI
|
||||
|
||||
If you want to add Local Testing in Travis, you have to start it by yourself.
|
||||
|
||||
The following script will download and start it in the background. You should run this in Travis before starting the tests.
|
||||
|
||||
```sh
|
||||
wget http://downloads.lambdatest.com/tunnel/linux/64bit/LT_Linux.zip
|
||||
unzip LT_Linux.zip
|
||||
./LT -user $LT_USERNAME -key $LT_ACCESS_KEY -cui &
|
||||
sleep 3
|
||||
```
|
||||
|
||||
Also, you might wish set the `build` to the Travis build number.
|
||||
|
||||
Example `capabilities`:
|
||||
|
||||
```javascript
|
||||
platform: 'Windows 10',
|
||||
browserName: 'chrome',
|
||||
version: '79.0',
|
||||
build: `myApp #${process.env.TRAVIS_BUILD_NUMBER}.${process.env.TRAVIS_JOB_NUMBER}`,
|
||||
'tunnel': 'true',
|
||||
'visual': 'true'
|
||||
```
|
||||
|
||||
## Perfecto
|
||||
|
||||
When using wdio with [`Perfecto`](https://www.perfecto.io), you need to create a security token for each user and add this in the capabilities structure (in addition to other capabilities), as follows:
|
||||
|
||||
```js
|
||||
export const config = {
|
||||
capabilities: [{
|
||||
// ...
|
||||
securityToken: "your security token"
|
||||
}],
|
||||
```
|
||||
|
||||
In addition, you need to add cloud configuration, as follows:
|
||||
|
||||
```js
|
||||
hostname: "your_cloud_name.perfectomobile.com",
|
||||
path: "/nexperience/perfectomobile/wd/hub",
|
||||
port: 443,
|
||||
protocol: "https",
|
||||
```
|
||||
|
|
@ -1,141 +0,0 @@
|
|||
---
|
||||
id: component-testing
|
||||
title: Component Testing
|
||||
---
|
||||
|
||||
With WebdriverIOs [Browser Runner](/docs/runner#browser-runner) you can run tests within an actual desktop or mobile browser while using WebdriverIO and the WebDriver protocol to automate and interact what gets rendered on the page. This approach has [many advantages](/docs/runner#browser-runner) compared to other test frameworks that only allow testing against [JSDOM](https://www.npmjs.com/package/jsdom).
|
||||
|
||||
## How does it Work?
|
||||
|
||||
The Browser Runner uses [Vite](https://vitejs.dev/) to render a test page and initialize a test framework to run your tests in the browser. Currently it only supports Mocha but Jasmine and Cucumber are [on the roadmap](https://github.com/orgs/webdriverio/projects/1). This allows to test any kind of components even for projects that don't use Vite.
|
||||
|
||||
The Vite server is started by the WebdriverIO testrunner and configured so that you can use all reporter and services as you used to for normal e2e tests. Furthermore it initializes a [`browser`](/docs/api/browser) instance that allows you to access a subset of the [WebdriverIO API](/docs/api) to interact with the any elements on the page. Similar as e2e tests you can access that instance through the `browser` variable attached to the global scope or by importing it from `@wdio/globals` depending on how [`injectGlobals`](/docs/api/globals) is set.
|
||||
|
||||
WebdriverIO has built-in support for the following frameworks:
|
||||
|
||||
- [__Nuxt__](https://nuxt.com/): WebdriverIO's testrunner detects a Nuxt application and automatically sets up your project composables and helps mock out the Nuxt backend, read more in the [Nuxt docs](/docs/component-testing/vue#testing-vue-components-in-nuxt)
|
||||
- [__TailwindCSS__](https://tailwindcss.com/): WebdriverIO's testrunner detects if you are using TailwindCSS and loads the environment properly into the test page
|
||||
|
||||
## Setup
|
||||
|
||||
To set-up WebdriverIO for unit or component testing in the browser, initiate a new WebdriverIO project via:
|
||||
|
||||
```bash
|
||||
npm init wdio@latest ./
|
||||
# or
|
||||
yarn create wdio ./
|
||||
```
|
||||
|
||||
Once the configuration wizard starts, pick `browser` for running unit and component testing and choose one of the presets if desired otherwise go with _"Other"_ if you only want to run basic unit tests. You can also configure a custom Vite configuration if you use Vite already in your project. For more information check out all [runner options](/docs/runner#runner-options).
|
||||
|
||||
:::info
|
||||
|
||||
__Note:__ WebdriverIO by default will run browser tests in CI headlessly, e.g. a `CI` environment variable is set to `'1'` or `'true'`. You can manually configure this behavior using the [`headless`](/docs/runner#headless) option for the runner.
|
||||
|
||||
:::
|
||||
|
||||
At the end of this process you should find a `wdio.conf.js` that contains various WebdriverIO configurations, including a `runner` property, e.g.:
|
||||
|
||||
```ts reference useHTTPS runmeRepository="git@github.com:webdriverio/example-recipes.git" runmeFileToOpen="component-testing%2FREADME.md"
|
||||
https://github.com/webdriverio/example-recipes/blob/fd54f94306ed8e7b40f967739164dfe4d6d76b41/wdio.comp.conf.js
|
||||
```
|
||||
|
||||
By defining different [capabilities](/docs/configuration#capabilities) you can run your tests in different browser, in parallel if desired.
|
||||
|
||||
If you are still unsure how everything works, watch the following tutorial on how to get started with Component Testing in WebdriverIO:
|
||||
|
||||
<LiteYouTubeEmbed id="5vp_3tGtnMc" title="Getting Started with Component Testing in WebdriverIO" />
|
||||
|
||||
## Test Harness
|
||||
|
||||
It is totally up to you what you want to run in your tests and how you like to render the components. However we recommend to use the [Testing Library](https://testing-library.com/) as utility framework as it provides plugins for various of component frameworks, such as React, Preact, Svelte and Vue. It is very useful for rendering components into the test page and it automatically cleans up these components after every test.
|
||||
|
||||
You can mix Testing Library primitives with WebdriverIO commands as you wish, e.g.:
|
||||
|
||||
```js reference useHTTPS
|
||||
https://github.com/webdriverio/example-recipes/blob/fd54f94306ed8e7b40f967739164dfe4d6d76b41/component-testing/svelte-example.js
|
||||
```
|
||||
|
||||
__Note:__ using render methods from Testing Library helps remove created components between the tests. If you don't use Testing Library ensure to attach your test components to a container that gets cleaned up between tests.
|
||||
|
||||
## Setup Scripts
|
||||
|
||||
You can set up your tests by running arbitrary scripts in Node.js or in the browser, e.g. injecting styles, mocking browser APIs or connecting to a 3rd party service. The WebdriverIO [hooks](/docs/configuration#hooks) can be used to run code in Node.js while the [`mochaOpts.require`](/docs/frameworks#require) allows you to import scripts into the browser before tests are loaded, e.g.:
|
||||
|
||||
```js wdio.conf.js
|
||||
export const config = {
|
||||
// ...
|
||||
mochaOpts: {
|
||||
ui: 'tdd',
|
||||
// provide a setup script to run in the browser
|
||||
require: './__fixtures__/setup.js'
|
||||
},
|
||||
before: () => {
|
||||
// set up test environment in Node.js
|
||||
}
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
For example, if you like to mock all [`fetch()`](https://developer.mozilla.org/en-US/docs/Web/API/fetch) calls in your test with the following set-up script:
|
||||
|
||||
```js ./fixtures/setup.js
|
||||
import { fn } from '@wdio/browser-runner'
|
||||
|
||||
// run code before all tests are loaded
|
||||
window.fetch = fn()
|
||||
|
||||
export const mochaGlobalSetup = () => {
|
||||
// run code after test file is loaded
|
||||
}
|
||||
|
||||
export const mochaGlobalTeardown = () => {
|
||||
// run code after spec file was executed
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
Now in your tests you can provide custom response values for all browser requests. Read more on global fixtures in the [Mocha docs](https://mochajs.org/#global-fixtures).
|
||||
|
||||
## Watch Test and Application Files
|
||||
|
||||
There are multiple ways how you can debug your browser tests. The easiest is to start the WebdriverIO testrunner with the `--watch` flag, e.g.:
|
||||
|
||||
```sh
|
||||
$ npx wdio run ./wdio.conf.js --watch
|
||||
```
|
||||
|
||||
This will run through all tests initially and halt once all are run. You can then make changes to individual files which then will be rerun individually. If you set a [`filesToWatch`](/docs/configuration#filestowatch) pointing to your application files, it will re-run all tests when changes to your app are being made.
|
||||
|
||||
## Debugging
|
||||
|
||||
While it is not (yet) possible to set breakpoints in your IDE and have them being recognized by the remote browser, you can use the [`debug`](/docs/api/browser/debug) command to stop the test at any point. This allows you to open DevTools to then debug the test by setting breakpoints in the [sources tab](https://buddy.works/tutorials/debugging-javascript-efficiently-with-chrome-devtools).
|
||||
|
||||
When the `debug` command is called, you will also get a Node.js repl interface in your terminal, saying:
|
||||
|
||||
```
|
||||
The execution has stopped!
|
||||
You can now go into the browser or use the command line as REPL
|
||||
(To exit, press ^C again or type .exit)
|
||||
```
|
||||
|
||||
Press `Ctrl` or `Command` + `c` or enter `.exit` to continue with the test.
|
||||
|
||||
## Run using a Selenium Grid
|
||||
|
||||
If you have a [Selenium Grid](https://www.selenium.dev/documentation/grid/) set up and run your browser through that grid, you have to set the `host` browser runner option to allow the browser, to access the right host where the test files are being served, e.g.:
|
||||
|
||||
```ts title=wdio.conf.ts
|
||||
export const config: WebdriverIO.Config = {
|
||||
runner: ['browser', {
|
||||
// network IP of the machine that runs the WebdriverIO process
|
||||
host: 'http://172.168.0.2'
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
This will ensure the browser correctly opens the right server instance hosted on the instance that runs the WebdriverIO tests.
|
||||
|
||||
## Examples
|
||||
|
||||
You can find various examples for testing components using popular component frameworks in our [example repository](https://github.com/webdriverio/component-testing-examples).
|
||||
|
|
@ -1,740 +0,0 @@
|
|||
---
|
||||
id: configuration
|
||||
title: Configuration
|
||||
---
|
||||
|
||||
Based on the [setup type](/docs/setuptypes) (e.g. using the raw protocol bindings, WebdriverIO as standalone package or the WDIO testrunner) there is a different set of options available to control the environment.
|
||||
|
||||
## WebDriver Options
|
||||
|
||||
The following options are defined when using the [`webdriver`](https://www.npmjs.com/package/webdriver) protocol package:
|
||||
|
||||
### protocol
|
||||
|
||||
Protocol to use when communicating with the driver server.
|
||||
|
||||
Type: `String`<br /> Default: `http`
|
||||
|
||||
### hostname
|
||||
|
||||
Host of your driver server.
|
||||
|
||||
Type: `String`<br /> Default: `0.0.0.0`
|
||||
|
||||
### port
|
||||
|
||||
Port your driver server is on.
|
||||
|
||||
Type: `Number`<br /> Default: `undefined`
|
||||
|
||||
### path
|
||||
|
||||
Path to driver server endpoint.
|
||||
|
||||
Type: `String`<br /> Default: `/`
|
||||
|
||||
### queryParams
|
||||
|
||||
Query parameters that are propagated to the driver server.
|
||||
|
||||
Type: `Object`<br /> Default: `undefined`
|
||||
|
||||
### user
|
||||
|
||||
Your cloud service username (only works for [Sauce Labs](https://saucelabs.com), [Browserstack](https://www.browserstack.com), [TestingBot](https://testingbot.com) or [LambdaTest](https://www.lambdatest.com) accounts). If set, WebdriverIO will automatically set connection options for you. If you don't use a cloud provider this can be used to authenticate any other WebDriver backend.
|
||||
|
||||
Type: `String`<br /> Default: `undefined`
|
||||
|
||||
### key
|
||||
|
||||
Your cloud service access key or secret key (only works for [Sauce Labs](https://saucelabs.com), [Browserstack](https://www.browserstack.com), [TestingBot](https://testingbot.com) or [LambdaTest](https://www.lambdatest.com) accounts). If set, WebdriverIO will automatically set connection options for you. If you don't use a cloud provider this can be used to authenticate any other WebDriver backend.
|
||||
|
||||
Type: `String`<br /> Default: `undefined`
|
||||
|
||||
### capabilities
|
||||
|
||||
Defines the capabilities you want to run in your WebDriver session. Check out the [WebDriver Protocol](https://w3c.github.io/webdriver/#capabilities) for more details. If you run an older driver that doesn't support the WebDriver protocol, you’ll need to use the [JSONWireProtocol capabilities](https://github.com/SeleniumHQ/selenium/wiki/DesiredCapabilities) to successfully run a session.
|
||||
|
||||
Next to the WebDriver based capabilities you can apply browser and vendor specific options that allow deeper configuration to the remote browser or device. These are documented in the corresponding vendor docs, e.g.:
|
||||
|
||||
- `goog:chromeOptions`: for [Google Chrome](https://chromedriver.chromium.org/capabilities#h.p_ID_106)
|
||||
- `moz:firefoxOptions`: for [Mozilla Firefox](https://firefox-source-docs.mozilla.org/testing/geckodriver/Capabilities.html)
|
||||
- `ms:edgeOptions`: for [Microsoft Edge](https://docs.microsoft.com/en-us/microsoft-edge/webdriver-chromium/capabilities-edge-options#using-the-edgeoptions-class)
|
||||
- `sauce:options`: for [Sauce Labs](https://docs.saucelabs.com/dev/test-configuration-options/#desktop-and-mobile-capabilities-sauce-specific--optional)
|
||||
- `bstack:options`: for [BrowserStack](https://www.browserstack.com/automate/capabilities?tag=selenium-4#)
|
||||
- `selenoid:options`: for [Selenoid](https://github.com/aerokube/selenoid/blob/master/docs/special-capabilities.adoc)
|
||||
|
||||
Additionally, a useful utility is the Sauce Labs [Automated Test Configurator](https://docs.saucelabs.com/basics/platform-configurator/), which helps you create this object by clicking together your desired capabilities.
|
||||
|
||||
Type: `Object`<br /> Default: `null`
|
||||
|
||||
**Example:**
|
||||
|
||||
```js
|
||||
{
|
||||
browserName: 'chrome', // options: `chrome`, `edge`, `firefox`, `safari`
|
||||
browserVersion: '27.0', // browser version
|
||||
platformName: 'Windows 10' // OS platform
|
||||
}
|
||||
```
|
||||
|
||||
If you’re running web or native tests on mobile devices, `capabilities` differs from the WebDriver protocol. See the [Appium Docs](https://appium.github.io/appium.io/docs/en/writing-running-appium/caps/) for more details.
|
||||
|
||||
### logLevel
|
||||
|
||||
Level of logging verbosity.
|
||||
|
||||
Type: `String`<br /> Default: `info`<br /> Options: `trace` | `debug` | `info` | `warn` | `error` | `silent`
|
||||
|
||||
### outputDir
|
||||
|
||||
Directory to store all testrunner log files (including reporter logs and `wdio` logs). If not set, all logs are streamed to `stdout`. Since most reporters are made to log to `stdout`, it is recommended to only use this option for specific reporters where it makes more sense to push report into a file (like the `junit` reporter, for example).
|
||||
|
||||
When running in standalone mode, the only log generated by WebdriverIO will be the `wdio` log.
|
||||
|
||||
Type: `String`<br /> Default: `null`
|
||||
|
||||
### connectionRetryTimeout
|
||||
|
||||
Timeout for any WebDriver request to a driver or grid.
|
||||
|
||||
Type: `Number`<br /> Default: `120000`
|
||||
|
||||
### connectionRetryCount
|
||||
|
||||
Maximum count of request retries to the Selenium server.
|
||||
|
||||
Type: `Number`<br /> Default: `3`
|
||||
|
||||
### agent
|
||||
|
||||
Allows you to use a custom`http`/`https`/`http2` [agent](https://www.npmjs.com/package/got#agent) to make requests.
|
||||
|
||||
Type: `Object`<br /> Default:
|
||||
|
||||
```js
|
||||
{
|
||||
http: new http.Agent({ keepAlive: true }),
|
||||
https: new https.Agent({ keepAlive: true })
|
||||
}
|
||||
```
|
||||
|
||||
### headers
|
||||
|
||||
Specify custom `headers` to pass into every WebDriver request. If your Selenium Grid requires Basic Authentification we recommend to pass in an `Authorization` header through this option to authenticate your WebDriver requests, e.g.:
|
||||
|
||||
```ts wdio.conf.ts
|
||||
import { Buffer } from 'buffer';
|
||||
// Read the username and password from environment variables
|
||||
const username = process.env.SELENIUM_GRID_USERNAME;
|
||||
const password = process.env.SELENIUM_GRID_PASSWORD;
|
||||
|
||||
// Combine the username and password with a colon separator
|
||||
const credentials = `${username}:${password}`;
|
||||
// Encode the credentials using Base64
|
||||
const encodedCredentials = Buffer.from(credentials).toString('base64');
|
||||
|
||||
export const config: WebdriverIO.Config = {
|
||||
// ...
|
||||
headers: {
|
||||
Authorization: `Basic ${encodedCredentials}`
|
||||
}
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
Type: `Object`<br /> Default: `{}`
|
||||
|
||||
### transformRequest
|
||||
|
||||
Function intercepting [HTTP request options](https://github.com/sindresorhus/got#options) before a WebDriver request is made
|
||||
|
||||
Type: `(RequestOptions) => RequestOptions`<br /> Default: *none*
|
||||
|
||||
### transformResponse
|
||||
|
||||
Function intercepting HTTP response objects after a WebDriver response has arrived. The function is passed the original response object as the first and the corresponding `RequestOptions` as the second argument.
|
||||
|
||||
Type: `(Response, RequestOptions) => Response`<br /> Default: *none*
|
||||
|
||||
### strictSSL
|
||||
|
||||
Whether it does not require SSL certificate to be valid. It can be set via an environment variables as `STRICT_SSL` or `strict_ssl`.
|
||||
|
||||
Type: `Boolean`<br /> Default: `true`
|
||||
|
||||
### enableDirectConnect
|
||||
|
||||
Whether enable [Appium direct connection feature](https://appiumpro.com/editions/86-connecting-directly-to-appium-hosts-in-distributed-environments). It does nothing if the response did not have proper keys while the flag is enabled.
|
||||
|
||||
Type: `Boolean`<br /> Default: `true`
|
||||
|
||||
### cacheDir
|
||||
|
||||
The path to the root of the cache directory. This directory is used to store all drivers that are downloaded when attempting to start a session.
|
||||
|
||||
Type: `String`<br /> Default: `process.env.WEBDRIVER_CACHE_DIR || os.tmpdir()`
|
||||
|
||||
---
|
||||
|
||||
## WebdriverIO
|
||||
|
||||
The following options (including the ones listed above) can be used with WebdriverIO in standalone:
|
||||
|
||||
### automationProtocol
|
||||
|
||||
Define the protocol you want to use for your browser automation. Currently only [`webdriver`](https://www.npmjs.com/package/webdriver) is supported, as it is the main browser automation technology WebdriverIO uses.
|
||||
|
||||
If you want to automate the browser using a different automation technology, make you set this property to a path that resolves to a module that adheres to the following interface:
|
||||
|
||||
```ts
|
||||
import type { Capabilities } from '@wdio/types';
|
||||
import type { Client, AttachOptions } from 'webdriver';
|
||||
|
||||
export default class YourAutomationLibrary {
|
||||
/**
|
||||
* Start a automation session and return a WebdriverIO [monad](https://github.com/webdriverio/webdriverio/blob/940cd30939864bdbdacb2e94ee6e8ada9b1cc74c/packages/wdio-utils/src/monad.ts)
|
||||
* with respective automation commands. See the [webdriver](https://www.npmjs.com/package/webdriver) package
|
||||
* as a reference implementation
|
||||
*
|
||||
* @param {Capabilities.RemoteConfig} options WebdriverIO options
|
||||
* @param {Function} hook that allows to modify the client before it gets released from the function
|
||||
* @param {PropertyDescriptorMap} userPrototype allows user to add custom protocol commands
|
||||
* @param {Function} customCommandWrapper allows to modify the command execution
|
||||
* @returns a WebdriverIO compatible client instance
|
||||
*/
|
||||
static newSession(
|
||||
options: Capabilities.RemoteConfig,
|
||||
modifier?: (...args: any[]) => any,
|
||||
userPrototype?: PropertyDescriptorMap,
|
||||
customCommandWrapper?: (...args: any[]) => any
|
||||
): Promise<Client>;
|
||||
|
||||
/**
|
||||
* allows user to attach to existing sessions
|
||||
* @optional
|
||||
*/
|
||||
static attachToSession(
|
||||
options?: AttachOptions,
|
||||
modifier?: (...args: any[]) => any, userPrototype?: {},
|
||||
commandWrapper?: (...args: any[]) => any
|
||||
): Client;
|
||||
|
||||
/**
|
||||
* Changes The instance session id and browser capabilities for the new session
|
||||
* directly into the passed in browser object
|
||||
*
|
||||
* @optional
|
||||
* @param {object} instance the object we get from a new browser session.
|
||||
* @returns {string} the new session id of the browser
|
||||
*/
|
||||
static reloadSession(
|
||||
instance: Client,
|
||||
newCapabilities?: WebdriverIO.Capabilitie
|
||||
): Promise<string>;
|
||||
}
|
||||
```
|
||||
|
||||
Type: `String`<br />
|
||||
Default: `webdriver`
|
||||
|
||||
### baseUrl
|
||||
|
||||
Shorten `url` command calls by setting a base URL.
|
||||
- If your `url` parameter starts with `/`, then `baseUrl` is prepended (except the `baseUrl` path, if it has one).
|
||||
- If your `url` parameter starts without a scheme or `/` (like `some/path`), then the full `baseUrl` is prepended directly.
|
||||
|
||||
Type: `String`<br /> Default: `null`
|
||||
|
||||
### waitforTimeout
|
||||
|
||||
Default timeout for all `waitFor*` commands. (Note the lowercase `f` in the option name.) This timeout __only__ affects commands starting with `waitFor*` and their default wait time.
|
||||
|
||||
To increase the timeout for a _test_, please see the framework docs.
|
||||
|
||||
Type: `Number`<br /> Default: `5000`
|
||||
|
||||
### waitforInterval
|
||||
|
||||
Default interval for all `waitFor*` commands to check if an expected state (e.g., visibility) has been changed.
|
||||
|
||||
Type: `Number`<br /> Default: `100`
|
||||
|
||||
### region
|
||||
|
||||
If running on Sauce Labs, you can choose to run tests between different data centers: US or EU. To change your region to EU, add `region: 'eu'` to your config.
|
||||
|
||||
__Note:__ This only has an effect if you provide `user` and `key` options that are connected to your Sauce Labs account.
|
||||
|
||||
Type: `String`<br /> Default: `us`
|
||||
|
||||
*(only for vm and or em/simulators)*
|
||||
|
||||
---
|
||||
|
||||
## Testrunner Options
|
||||
|
||||
The following options (including the ones listed above) are defined only for running WebdriverIO with the WDIO testrunner:
|
||||
|
||||
### specs
|
||||
|
||||
Define specs for test execution. You can either specify a glob pattern to match multiple files at once or wrap a glob or set of paths into an array to run them within a single worker process. All paths are seen as relative from the config file path.
|
||||
|
||||
Type: `(String | String[])[]`<br /> Default: `[]`
|
||||
|
||||
### exclude
|
||||
|
||||
Exclude specs from test execution. All paths are seen as relative from the config file path.
|
||||
|
||||
Type: `String[]`<br /> Default: `[]`
|
||||
|
||||
### suites
|
||||
|
||||
An object describing various of suites, which you can then specify with the `--suite` option on the `wdio` CLI.
|
||||
|
||||
Type: `Object`<br /> Default: `{}`
|
||||
|
||||
### capabilities
|
||||
|
||||
The same as the `capabilities` section described above, except with the option to specify either a [`multiremote`](/docs/multiremote) object, or multiple WebDriver sessions in an array for parallel execution.
|
||||
|
||||
You can apply the same vendor and browser specific capabilities as defined [above](/docs/configuration#capabilities).
|
||||
|
||||
Type: `Object`|`Object[]`<br /> Default: `[{ 'wdio:maxInstances': 5, browserName: 'firefox' }]`
|
||||
|
||||
### maxInstances
|
||||
|
||||
Maximum number of total parallel running workers.
|
||||
|
||||
__Note:__ that it may be a number as high as `100`, when the tests are being performed on some external vendors such as Sauce Labs's machines. There, the tests are not tested on a single machine, but rather, on multiple VMs. If the tests are to be run on a local development machine, use a number that is more reasonable, such as `3`, `4`, or `5`. Essentially, this is the number of browsers that will be concurrently started and running your tests at the same time, so it depends on how much RAM there is on your machine, and how many other apps are running on your machine.
|
||||
|
||||
You can also apply `maxInstances` within your capability objects using the `wdio:maxInstances` capability. This will limit the amount of parallel sessions for that particular capability.
|
||||
|
||||
Type: `Number`<br /> Default: `100`
|
||||
|
||||
### maxInstancesPerCapability
|
||||
|
||||
Maximum number of total parallel running workers per capability.
|
||||
|
||||
Type: `Number`<br /> Default: `100`
|
||||
|
||||
### injectGlobals
|
||||
|
||||
Inserts WebdriverIO's globals (e.g. `browser`, `$` and `$$`) into the global environment. If you set to `false`, you should import from `@wdio/globals`, e.g.:
|
||||
|
||||
```ts
|
||||
import { browser, $, $$, expect } from '@wdio/globals'
|
||||
```
|
||||
|
||||
Note: WebdriverIO doesn't handle injection of test framework specific globals.
|
||||
|
||||
Type: `Boolean`<br /> Default: `true`
|
||||
|
||||
### bail
|
||||
|
||||
If you want your test run to stop after a specific number of test failures, use `bail`. (It defaults to `0`, which runs all tests no matter what.) **Note:** A test in this context are all tests within a single spec file (when using Mocha or Jasmine) or all steps within a feature file (when using Cucumber). If you want to control the bail behavior within tests of a single test file, take a look at the available [framework](frameworks) options.
|
||||
|
||||
Type: `Number`<br /> Default: `0` (don't bail; run all tests)
|
||||
|
||||
### specFileRetries
|
||||
|
||||
The number of times to retry an entire specfile when it fails as a whole.
|
||||
|
||||
Type: `Number`<br /> Default: `0`
|
||||
|
||||
### specFileRetriesDelay
|
||||
|
||||
Delay in seconds between the spec file retry attempts
|
||||
|
||||
Type: `Number`<br /> Default: `0`
|
||||
|
||||
### specFileRetriesDeferred
|
||||
|
||||
Whether or not retried spec files should be retried immediately or deferred to the end of the queue.
|
||||
|
||||
Type: `Boolean`<br />
|
||||
Default: `true`
|
||||
|
||||
### groupLogsByTestSpec
|
||||
|
||||
Choose the log output view.
|
||||
|
||||
If set to `false` logs from different test files will be printed in real-time. Please note that this may result in the mixing of log outputs from different files when running in parallel.
|
||||
|
||||
If set to `true` log outputs will be grouped by Test Spec and printed only when the Test Spec is completed.
|
||||
|
||||
By default, it is set to `false` so logs are printed in real-time.
|
||||
|
||||
Type: `Boolean`<br />
|
||||
Default: `false`
|
||||
|
||||
### groupLogsByTestSpec
|
||||
|
||||
Choose the log output view.
|
||||
|
||||
If set to `false` logs from different test files will be printed in real-time. Please note that this may result in the mixing of log outputs from different files when running in parallel.
|
||||
|
||||
If set to `true` log outputs will be grouped by Test Spec and printed only when the Test Spec is completed.
|
||||
|
||||
By default, it is set to `false` so logs are printed in real-time.
|
||||
|
||||
Type: `Boolean`<br /> Default: `false`
|
||||
|
||||
### services
|
||||
|
||||
Services take over a specific job you don't want to take care of. They enhance your test setup with almost no effort.
|
||||
|
||||
Type: `String[]|Object[]`<br /> Default: `[]`
|
||||
|
||||
### framework
|
||||
|
||||
Defines the test framework to be used by the WDIO testrunner.
|
||||
|
||||
Type: `String`<br /> Default: `mocha`<br /> Options: `mocha` | `jasmine`
|
||||
|
||||
### mochaOpts, jasmineOpts and cucumberOpts
|
||||
|
||||
Specific framework-related options. See the framework adapter documentation on which options are available. Read more on this in [Frameworks](frameworks).
|
||||
|
||||
Type: `Object`<br /> Default: `{ timeout: 10000 }`
|
||||
|
||||
### cucumberFeaturesWithLineNumbers
|
||||
|
||||
List of cucumber features with line numbers (when [using cucumber framework](./Frameworks.md#using-cucumber)).
|
||||
|
||||
Type: `String[]` Default: `[]`
|
||||
|
||||
### reporters
|
||||
|
||||
List of reporters to use. A reporter can be either a string, or an array of `['reporterName', { /* reporter options */}]` where the first element is a string with the reporter name and the second element an object with reporter options.
|
||||
|
||||
Type: `String[]|Object[]`<br /> Default: `[]`
|
||||
|
||||
Example:
|
||||
|
||||
```js
|
||||
reporters: [
|
||||
'dot',
|
||||
'spec'
|
||||
['junit', {
|
||||
outputDir: `${__dirname}/reports`,
|
||||
otherOption: 'foobar'
|
||||
}]
|
||||
]
|
||||
```
|
||||
|
||||
### reporterSyncInterval
|
||||
|
||||
Determines in which interval the reporter should check if they are synchronized if they report their logs asynchronously (e.g. if logs are streamed to a 3rd party vendor).
|
||||
|
||||
Type: `Number`<br /> Default: `100` (ms)
|
||||
|
||||
### reporterSyncTimeout
|
||||
|
||||
Determines the maximum time reporters have to finish uploading all their logs until an error is being thrown by the testrunner.
|
||||
|
||||
Type: `Number`<br /> Default: `5000` (ms)
|
||||
|
||||
### execArgv
|
||||
|
||||
Node arguments to specify when launching child processes.
|
||||
|
||||
Type: `String[]`<br /> Default: `null`
|
||||
|
||||
### filesToWatch
|
||||
|
||||
A list of glob supporting string patterns that tell the testrunner to have it additionally watch other files, e.g. application files, when running it with the `--watch` flag. By default the testrunner already watches all spec files.
|
||||
|
||||
Type: `String[]`<br /> Default: `[]`
|
||||
|
||||
### updateSnapshots
|
||||
|
||||
Set to true if you want to update your snapshots. Ideally used as part of a CLI parameter, e.g. `wdio run wdio.conf.js --s`.
|
||||
|
||||
Type: `'new' | 'all' | 'none'`<br /> Default: `none` if not provided and tests run in CI, `new` if not provided, otherwise what's been provided
|
||||
|
||||
### resolveSnapshotPath
|
||||
|
||||
Overrides default snapshot path. For example, to store snapshots next to test files.
|
||||
|
||||
```ts title="wdio.conf.ts"
|
||||
export const config: WebdriverIO.Config = {
|
||||
resolveSnapshotPath: (testPath, snapExtension) => testPath + snapExtension,
|
||||
}
|
||||
```
|
||||
|
||||
Type: `(testPath: string, snapExtension: string) => string`<br /> Default: stores snapshot files in `__snapshots__` directory next to test file
|
||||
|
||||
### tsConfigPath
|
||||
|
||||
WDIO uses `tsx` to compile TypeScript files. Your TSConfig is automatically detected from the current working directory but you can specify a custom path here or by setting the TSX_TSCONFIG_PATH environment variable.
|
||||
|
||||
See the `tsx` docs: https://tsx.is/dev-api/node-cli#custom-tsconfig-json-path
|
||||
|
||||
Type: `String`<br /> Default: `null`<br />
|
||||
|
||||
## Hooks
|
||||
|
||||
The WDIO testrunner allows you to set hooks to be triggered at specific times of the test lifecycle. This allows custom actions (e.g. take screenshot if a test fails).
|
||||
|
||||
Every hook has as parameter specific information about the lifecycle (e.g. information about the test suite or test). Read more about all hook properties in [our example config](https://github.com/webdriverio/webdriverio/blob/master/examples/wdio.conf.js#L183-L326).
|
||||
|
||||
**Note:** Some hooks (`onPrepare`, `onWorkerStart`, `onWorkerEnd` and `onComplete`) are executed in a different process and therefore can not share any global data with the other hooks that live in the worker process.
|
||||
|
||||
### onPrepare
|
||||
|
||||
Gets executed once before all workers get launched.
|
||||
|
||||
Parameters:
|
||||
|
||||
- `config` (`object`): WebdriverIO configuration object
|
||||
- `param` (`object[]`): list of capabilities details
|
||||
|
||||
### onWorkerStart
|
||||
|
||||
Gets executed before a worker process is spawned and can be used to initialize specific service for that worker as well as modify runtime environments in an async fashion.
|
||||
|
||||
Parameters:
|
||||
|
||||
- `cid` (`string`): capability id (e.g 0-0)
|
||||
- `caps` (`object`): containing capabilities for session that will be spawn in the worker
|
||||
- `specs` (`string[]`): specs to be run in the worker process
|
||||
- `args` (`object`): object that will be merged with the main configuration once worker is initialized
|
||||
- `execArgv` (`string[]`): list of string arguments passed to the worker process
|
||||
|
||||
### onWorkerEnd
|
||||
|
||||
Gets executed just after a worker process has exited.
|
||||
|
||||
Parameters:
|
||||
|
||||
- `cid` (`string`): capability id (e.g 0-0)
|
||||
- `exitCode` (`number`): 0 - success, 1 - fail
|
||||
- `specs` (`string[]`): specs to be run in the worker process
|
||||
- `retries` (`number`): number of spec level retries used as defined in [_"Add retries on a per-specfile basis"_](./Retry.md#add-retries-on-a-per-specfile-basis)
|
||||
|
||||
### beforeSession
|
||||
|
||||
Gets executed just before initializing the webdriver session and test framework. It allows you to manipulate configurations depending on the capability or spec.
|
||||
|
||||
Parameters:
|
||||
|
||||
- `config` (`object`): WebdriverIO configuration object
|
||||
- `caps` (`object`): containing capabilities for session that will be spawn in the worker
|
||||
- `specs` (`string[]`): specs to be run in the worker process
|
||||
|
||||
### before
|
||||
|
||||
Gets executed before test execution begins. At this point you can access to all global variables like `browser`. It is the perfect place to define custom commands.
|
||||
|
||||
Parameters:
|
||||
|
||||
- `caps` (`object`): containing capabilities for session that will be spawn in the worker
|
||||
- `specs` (`string[]`): specs to be run in the worker process
|
||||
- `browser` (`object`): instance of created browser/device session
|
||||
|
||||
### beforeSuite
|
||||
|
||||
Hook that gets executed before the suite starts (in Mocha/Jasmine only)
|
||||
|
||||
Parameters:
|
||||
|
||||
- `suite` (`object`): suite details
|
||||
|
||||
### beforeHook
|
||||
|
||||
Hook that gets executed *before* a hook within the suite starts (e.g. runs before calling beforeEach in Mocha)
|
||||
|
||||
Parameters:
|
||||
|
||||
- `test` (`object`): test details
|
||||
- `context` (`object`): test context (represents World object in Cucumber)
|
||||
|
||||
### afterHook
|
||||
|
||||
Hook that gets executed *after* a hook within the suite ends (e.g. runs after calling afterEach in Mocha)
|
||||
|
||||
Parameters:
|
||||
|
||||
- `test` (`object`): test details
|
||||
- `context` (`object`): test context (represents World object in Cucumber)
|
||||
- `result` (`object`): hook result (contains `error`, `result`, `duration`, `passed`, `retries` properties)
|
||||
|
||||
### beforeTest
|
||||
|
||||
Function to be executed before a test (in Mocha/Jasmine only).
|
||||
|
||||
Parameters:
|
||||
|
||||
- `test` (`object`): test details
|
||||
- `context` (`object`): scope object the test was executed with
|
||||
|
||||
### beforeCommand
|
||||
|
||||
Runs before a WebdriverIO command gets executed.
|
||||
|
||||
Parameters:
|
||||
|
||||
- `commandName` (`string`): command name
|
||||
- `args` (`*`): arguments that command would receive
|
||||
|
||||
### afterCommand
|
||||
|
||||
Runs after a WebdriverIO command gets executed.
|
||||
|
||||
Parameters:
|
||||
|
||||
- `commandName` (`string`): command name
|
||||
- `args` (`*`): arguments that command would receive
|
||||
- `result` (`number`): 0 - command success, 1 - command error
|
||||
- `error` (`Error`): error object if any
|
||||
|
||||
### afterTest
|
||||
|
||||
Function to be executed after a test (in Mocha/Jasmine) ends.
|
||||
|
||||
Parameters:
|
||||
|
||||
- `test` (`object`): test details
|
||||
- `context` (`object`): scope object the test was executed with
|
||||
- `result.error` (`Error`): error object in case the test fails, otherwise `undefined`
|
||||
- `result.result` (`Any`): return object of test function
|
||||
- `result.duration` (`Number`): duration of test
|
||||
- `result.passed` (`Boolean`): true if test has passed, otherwise false
|
||||
- `result.retries` (`Object`): information about single test related retries as defined for [Mocha and Jasmine](./Retry.md#rerun-single-tests-in-jasmine-or-mocha) as well as [Cucumber](./Retry.md#rerunning-in-cucumber), e.g. `{ attempts: 0, limit: 0 }`, see
|
||||
- `result` (`object`): hook result (contains `error`, `result`, `duration`, `passed`, `retries` properties)
|
||||
|
||||
### afterSuite
|
||||
|
||||
Hook that gets executed after the suite has ended (in Mocha/Jasmine only)
|
||||
|
||||
Parameters:
|
||||
|
||||
- `suite` (`object`): suite details
|
||||
|
||||
### after
|
||||
|
||||
Gets executed after all tests are done. You still have access to all global variables from the test.
|
||||
|
||||
Parameters:
|
||||
|
||||
- `result` (`number`): 0 - test pass, 1 - test fail
|
||||
- `caps` (`object`): containing capabilities for session that will be spawn in the worker
|
||||
- `specs` (`string[]`): specs to be run in the worker process
|
||||
|
||||
### afterSession
|
||||
|
||||
Gets executed right after terminating the webdriver session.
|
||||
|
||||
Parameters:
|
||||
|
||||
- `config` (`object`): WebdriverIO configuration object
|
||||
- `caps` (`object`): containing capabilities for session that will be spawn in the worker
|
||||
- `specs` (`string[]`): specs to be run in the worker process
|
||||
|
||||
### onComplete
|
||||
|
||||
Gets executed after all workers got shut down and the process is about to exit. An error thrown in the onComplete hook will result in the test run failing.
|
||||
|
||||
Parameters:
|
||||
|
||||
- `exitCode` (`number`): 0 - success, 1 - fail
|
||||
- `config` (`object`): WebdriverIO configuration object
|
||||
- `caps` (`object`): containing capabilities for session that will be spawn in the worker
|
||||
- `result` (`object`): results object containing test results
|
||||
|
||||
### onReload
|
||||
|
||||
Gets executed when a refresh happens.
|
||||
|
||||
Parameters:
|
||||
|
||||
- `oldSessionId` (`string`): session ID of the old session
|
||||
- `newSessionId` (`string`): session ID of the new session
|
||||
|
||||
### beforeFeature
|
||||
|
||||
Runs before a Cucumber Feature.
|
||||
|
||||
Parameters:
|
||||
|
||||
- `uri` (`string`): path to feature file
|
||||
- `feature` ([`GherkinDocument.IFeature`](https://github.com/cucumber/common/blob/b94ce625967581de78d0fc32d84c35b46aa5a075/json-to-messages/javascript/src/cucumber-generic/JSONSchema.ts#L8-L17)): Cucumber feature object
|
||||
|
||||
### afterFeature
|
||||
|
||||
Runs after a Cucumber Feature.
|
||||
|
||||
Parameters:
|
||||
|
||||
- `uri` (`string`): path to feature file
|
||||
- `feature` ([`GherkinDocument.IFeature`](https://github.com/cucumber/common/blob/b94ce625967581de78d0fc32d84c35b46aa5a075/json-to-messages/javascript/src/cucumber-generic/JSONSchema.ts#L8-L17)): Cucumber feature object
|
||||
|
||||
### beforeScenario
|
||||
|
||||
Runs before a Cucumber Scenario.
|
||||
|
||||
Parameters:
|
||||
|
||||
- `world` ([`ITestCaseHookParameter`](https://github.com/cucumber/cucumber-js/blob/ac124f7b2be5fa54d904c7feac077a2657b19440/src/support_code_library_builder/types.ts#L10-L15)): world object containing information on pickle and test step
|
||||
- `context` (`object`): Cucumber World object
|
||||
|
||||
### afterScenario
|
||||
|
||||
Runs after a Cucumber Scenario.
|
||||
|
||||
Parameters:
|
||||
|
||||
- `world` ([`ITestCaseHookParameter`](https://github.com/cucumber/cucumber-js/blob/ac124f7b2be5fa54d904c7feac077a2657b19440/src/support_code_library_builder/types.ts#L10-L15)): world object containing information on pickle and test step
|
||||
- `result` (`object`): results object containing scenario results
|
||||
- `result.passed` (`boolean`): true if scenario has passed
|
||||
- `result.error` (`string`): error stack if scenario failed
|
||||
- `result.duration` (`number`): duration of scenario in milliseconds
|
||||
- `context` (`object`): Cucumber World object
|
||||
|
||||
### beforeStep
|
||||
|
||||
Runs before a Cucumber Step.
|
||||
|
||||
Parameters:
|
||||
|
||||
- `step` ([`Pickle.IPickleStep`](https://github.com/cucumber/common/blob/b94ce625967581de78d0fc32d84c35b46aa5a075/messages/jsonschema/Pickle.json#L20-L49)): Cucumber step object
|
||||
- `scenario` ([`IPickle`](https://github.com/cucumber/common/blob/b94ce625967581de78d0fc32d84c35b46aa5a075/messages/jsonschema/Pickle.json#L137-L175)): Cucumber scenario object
|
||||
- `context` (`object`): Cucumber World object
|
||||
|
||||
### afterStep
|
||||
|
||||
Runs after a Cucumber Step.
|
||||
|
||||
Parameters:
|
||||
|
||||
- `step` ([`Pickle.IPickleStep`](https://github.com/cucumber/common/blob/b94ce625967581de78d0fc32d84c35b46aa5a075/messages/jsonschema/Pickle.json#L20-L49)): Cucumber step object
|
||||
- `scenario` ([`IPickle`](https://github.com/cucumber/common/blob/b94ce625967581de78d0fc32d84c35b46aa5a075/messages/jsonschema/Pickle.json#L137-L175)): Cucumber scenario object
|
||||
- `result`: (`object`): results object containing step results
|
||||
- `result.passed` (`boolean`): true if scenario has passed
|
||||
- `result.error` (`string`): error stack if scenario failed
|
||||
- `result.duration` (`number`): duration of scenario in milliseconds
|
||||
- `context` (`object`): Cucumber World object
|
||||
|
||||
### beforeAssertion
|
||||
|
||||
Hook that gets executed before a WebdriverIO assertion happens.
|
||||
|
||||
Parameters:
|
||||
|
||||
- `params`: assertion information
|
||||
- `params.matcherName` (`string`): name of the matcher (e.g. `toHaveTitle`)
|
||||
- `params.expectedValue`: value that is passed into the matcher
|
||||
- `params.options`: assertion options
|
||||
|
||||
### afterAssertion
|
||||
|
||||
Hook that gets executed after a WebdriverIO assertion happened.
|
||||
|
||||
Parameters:
|
||||
|
||||
- `params`: assertion information
|
||||
- `params.matcherName` (`string`): name of the matcher (e.g. `toHaveTitle`)
|
||||
- `params.expectedValue`: value that is passed into the matcher
|
||||
- `params.options`: assertion options
|
||||
- `params.result`: assertion results
|
||||
|
|
@ -1,490 +0,0 @@
|
|||
---
|
||||
id: configurationfile
|
||||
title: Configuration File
|
||||
---
|
||||
|
||||
The configuration file contains all necessary information to run your test suite. It’s a NodeJS module that exports a JSON.
|
||||
|
||||
Here is an example configuration with all supported properties and additional information:
|
||||
|
||||
```js
|
||||
export const config = {
|
||||
|
||||
// ==================================
|
||||
// Where should your test be launched
|
||||
// ==================================
|
||||
//
|
||||
runner: 'local',
|
||||
//
|
||||
// =====================
|
||||
// Server Configurations
|
||||
// =====================
|
||||
// Host address of the running Selenium server. This information is usually obsolete, as
|
||||
// WebdriverIO automatically connects to localhost. Also if you are using one of the
|
||||
// supported cloud services like Sauce Labs, Browserstack, Testing Bot or LambdaTest, you also don't
|
||||
// need to define host and port information (because WebdriverIO can figure that out
|
||||
// from your user and key information). However, if you are using a private Selenium
|
||||
// backend, you should define the `hostname`, `port`, and `path` here.
|
||||
//
|
||||
hostname: 'localhost',
|
||||
port: 4444,
|
||||
path: '/',
|
||||
// Protocol: http | https
|
||||
// protocol: 'http',
|
||||
//
|
||||
// =================
|
||||
// Service Providers
|
||||
// =================
|
||||
// WebdriverIO supports Sauce Labs, Browserstack, Testing Bot and LambdaTest. (Other cloud providers
|
||||
// should work, too.) These services define specific `user` and `key` (or access key)
|
||||
// values you must put here, in order to connect to these services.
|
||||
//
|
||||
user: 'webdriverio',
|
||||
key: 'xxxxxxxxxxxxxxxx-xxxxxx-xxxxx-xxxxxxxxx',
|
||||
|
||||
// If you run your tests on Sauce Labs you can specify the region you want to run your tests
|
||||
// in via the `region` property. Available short handles for regions are `us` (default) and `eu`.
|
||||
// These regions are used for the Sauce Labs VM cloud and the Sauce Labs Real Device Cloud.
|
||||
// If you don't provide the region, it defaults to `us`.
|
||||
region: 'us',
|
||||
//
|
||||
// Sauce Labs provides a [headless offering](https://saucelabs.com/products/web-testing/sauce-headless-testing)
|
||||
// that allows you to run Chrome and Firefox tests headless.
|
||||
//
|
||||
headless: false,
|
||||
//
|
||||
// ==================
|
||||
// Specify Test Files
|
||||
// ==================
|
||||
// Define which test specs should run. The pattern is relative to the directory
|
||||
// of the configuration file being run.
|
||||
//
|
||||
// The specs are defined as an array of spec files (optionally using wildcards
|
||||
// that will be expanded). The test for each spec file will be run in a separate
|
||||
// worker process. In order to have a group of spec files run in the same worker
|
||||
// process enclose them in an array within the specs array.
|
||||
//
|
||||
// The path of the spec files will be resolved relative from the directory of
|
||||
// of the config file unless it's absolute.
|
||||
//
|
||||
specs: [
|
||||
'test/spec/**',
|
||||
['group/spec/**']
|
||||
],
|
||||
// Patterns to exclude.
|
||||
exclude: [
|
||||
'test/spec/multibrowser/**',
|
||||
'test/spec/mobile/**'
|
||||
],
|
||||
//
|
||||
// ============
|
||||
// Capabilities
|
||||
// ============
|
||||
// Define your capabilities here. WebdriverIO can run multiple capabilities at the same
|
||||
// time. Depending on the number of capabilities, WebdriverIO launches several test
|
||||
// sessions. Within your `capabilities`, you can overwrite the `spec` and `exclude`
|
||||
// options in order to group specific specs to a specific capability.
|
||||
//
|
||||
// First, you can define how many instances should be started at the same time. Let's
|
||||
// say you have 3 different capabilities (Chrome, Firefox, and Safari) and you have
|
||||
// set `maxInstances` to 1. wdio will spawn 3 processes.
|
||||
//
|
||||
// Therefore, if you have 10 spec files and you set `maxInstances` to 10, all spec files
|
||||
// will be tested at the same time and 30 processes will be spawned.
|
||||
//
|
||||
// The property handles how many capabilities from the same test should run tests.
|
||||
//
|
||||
maxInstances: 10,
|
||||
//
|
||||
// Or set a limit to run tests with a specific capability.
|
||||
maxInstancesPerCapability: 10,
|
||||
//
|
||||
// Inserts WebdriverIO's globals (e.g. `browser`, `$` and `$$`) into the global environment.
|
||||
// If you set to `false`, you should import from `@wdio/globals`. Note: WebdriverIO doesn't
|
||||
// handle injection of test framework specific globals.
|
||||
//
|
||||
injectGlobals: true,
|
||||
//
|
||||
// If you have trouble getting all important capabilities together, check out the
|
||||
// Sauce Labs platform configurator - a great tool to configure your capabilities:
|
||||
// https://docs.saucelabs.com/basics/platform-configurator
|
||||
//
|
||||
capabilities: [{
|
||||
browserName: 'chrome',
|
||||
'goog:chromeOptions': {
|
||||
// to run chrome headless the following flags are required
|
||||
// (see https://developers.google.com/web/updates/2017/04/headless-chrome)
|
||||
// args: ['--headless', '--disable-gpu'],
|
||||
}
|
||||
//
|
||||
// Parameter to ignore some or all default flags
|
||||
// - if value is true: ignore all DevTools 'default flags' and Puppeteer 'default arguments'
|
||||
// - if value is an array: DevTools filters given default arguments
|
||||
// 'wdio:devtoolsOptions': {
|
||||
// ignoreDefaultArgs: true,
|
||||
// ignoreDefaultArgs: ['--disable-sync', '--disable-extensions'],
|
||||
// }
|
||||
}, {
|
||||
// maxInstances can get overwritten per capability. So if you have an in house Selenium
|
||||
// grid with only 5 firefox instance available you can make sure that not more than
|
||||
// 5 instance gets started at a time.
|
||||
'wdio:maxInstances': 5,
|
||||
browserName: 'firefox',
|
||||
'wdio:specs': [
|
||||
'test/ffOnly/*'
|
||||
],
|
||||
'moz:firefoxOptions': {
|
||||
// flag to activate Firefox headless mode (see https://github.com/mozilla/geckodriver/blob/master/README.md#firefox-capabilities for more details about moz:firefoxOptions)
|
||||
// args: ['-headless']
|
||||
},
|
||||
// If outputDir is provided WebdriverIO can capture driver session logs
|
||||
// it is possible to configure which logTypes to exclude.
|
||||
// excludeDriverLogs: ['*'], // pass '*' to exclude all driver session logs
|
||||
excludeDriverLogs: ['bugreport', 'server'],
|
||||
//
|
||||
// Parameter to ignore some or all Puppeteer default arguments
|
||||
// ignoreDefaultArgs: ['-foreground'], // set value to true to ignore all default arguments
|
||||
}],
|
||||
//
|
||||
// Additional list of node arguments to use when starting child processes
|
||||
execArgv: [],
|
||||
//
|
||||
// ===================
|
||||
// Test Configurations
|
||||
// ===================
|
||||
// Define all options that are relevant for the WebdriverIO instance here
|
||||
//
|
||||
// Level of logging verbosity: trace | debug | info | warn | error | silent
|
||||
logLevel: 'info',
|
||||
//
|
||||
// Set specific log levels per logger
|
||||
// use 'silent' level to disable logger
|
||||
logLevels: {
|
||||
webdriver: 'info',
|
||||
'@wdio/appium-service': 'info'
|
||||
},
|
||||
//
|
||||
// Set directory to store all logs into
|
||||
outputDir: __dirname,
|
||||
//
|
||||
// If you only want to run your tests until a specific amount of tests have failed use
|
||||
// bail (default is 0 - don't bail, run all tests).
|
||||
bail: 0,
|
||||
//
|
||||
// Set a base URL in order to shorten `url()` command calls. If your `url` parameter starts
|
||||
// with `/`, the `baseUrl` is prepended, not including the path portion of `baseUrl`.
|
||||
//
|
||||
// If your `url` parameter starts without a scheme or `/` (like `some/path`), the `baseUrl`
|
||||
// gets prepended directly.
|
||||
baseUrl: 'http://localhost:8080',
|
||||
//
|
||||
// Default timeout for all waitForXXX commands.
|
||||
waitforTimeout: 1000,
|
||||
//
|
||||
// Add files to watch (e.g. application code or page objects) when running `wdio` command
|
||||
// with `--watch` flag. Globbing is supported.
|
||||
filesToWatch: [
|
||||
// e.g. rerun tests if I change my application code
|
||||
// './app/**/*.js'
|
||||
],
|
||||
//
|
||||
// Framework you want to run your specs with.
|
||||
// The following are supported: 'mocha', 'jasmine', and 'cucumber'
|
||||
// See also: https://webdriver.io/docs/frameworks.html
|
||||
//
|
||||
// Make sure you have the wdio adapter package for the specific framework installed before running any tests.
|
||||
framework: 'mocha',
|
||||
//
|
||||
// The number of times to retry the entire specfile when it fails as a whole
|
||||
specFileRetries: 1,
|
||||
// Delay in seconds between the spec file retry attempts
|
||||
specFileRetriesDelay: 0,
|
||||
// Whether or not retried spec files should be retried immediately or deferred to the end of the queue
|
||||
specFileRetriesDeferred: false,
|
||||
//
|
||||
// Test reporter for stdout.
|
||||
// The only one supported by default is 'dot'
|
||||
// See also: https://webdriver.io/docs/dot-reporter.html , and click on "Reporters" in left column
|
||||
reporters: [
|
||||
'dot',
|
||||
['allure', {
|
||||
//
|
||||
// If you are using the "allure" reporter you should define the directory where
|
||||
// WebdriverIO should save all allure reports.
|
||||
outputDir: './'
|
||||
}]
|
||||
],
|
||||
//
|
||||
// Options to be passed to Mocha.
|
||||
// See the full list at: http://mochajs.org
|
||||
mochaOpts: {
|
||||
ui: 'bdd'
|
||||
},
|
||||
//
|
||||
// Options to be passed to Jasmine.
|
||||
// See also: https://github.com/webdriverio/webdriverio/tree/main/packages/wdio-jasmine-framework#jasmineopts-options
|
||||
jasmineOpts: {
|
||||
//
|
||||
// Jasmine default timeout
|
||||
defaultTimeoutInterval: 5000,
|
||||
//
|
||||
// The Jasmine framework allows it to intercept each assertion in order to log the state of the application
|
||||
// or website depending on the result. For example, it is pretty handy to take a screenshot every time
|
||||
// an assertion fails.
|
||||
expectationResultHandler: function(passed, assertion) {
|
||||
// do something
|
||||
},
|
||||
//
|
||||
// Make use of Jasmine-specific grep functionality
|
||||
grep: null,
|
||||
invertGrep: null
|
||||
},
|
||||
//
|
||||
// If you are using Cucumber you need to specify where your step definitions are located.
|
||||
// See also: https://github.com/webdriverio/webdriverio/tree/main/packages/wdio-cucumber-framework#cucumberopts-options
|
||||
cucumberOpts: {
|
||||
require: [], // <string[]> (file/dir) require files before executing features
|
||||
backtrace: false, // <boolean> show full backtrace for errors
|
||||
compiler: [], // <string[]> ("extension:module") require files with the given EXTENSION after requiring MODULE (repeatable)
|
||||
dryRun: false, // <boolean> invoke formatters without executing steps
|
||||
failFast: false, // <boolean> abort the run on first failure
|
||||
snippets: true, // <boolean> hide step definition snippets for pending steps
|
||||
source: true, // <boolean> hide source URIs
|
||||
strict: false, // <boolean> fail if there are any undefined or pending steps
|
||||
tagExpression: '', // <string> (expression) only execute the features or scenarios with tags matching the expression
|
||||
timeout: 20000, // <number> timeout for step definitions
|
||||
ignoreUndefinedDefinitions: false, // <boolean> Enable this config to treat undefined definitions as warnings.
|
||||
scenarioLevelReporter: false // Enable this to make webdriver.io behave as if scenarios and not steps were the tests.
|
||||
},
|
||||
// Specify a custom tsconfig path - WDIO uses `tsx` to compile TypeScript files
|
||||
// Your TSConfig is automatically detected from the current working directory
|
||||
// but you can specify a custom path here or by setting the TSX_TSCONFIG_PATH env var
|
||||
// See the `tsx` docs: https://tsx.is/dev-api/node-cli#custom-tsconfig-json-path
|
||||
tsConfigPath: 'path/to/tsconfig.json',
|
||||
//
|
||||
// =====
|
||||
// Hooks
|
||||
// =====
|
||||
// WebdriverIO provides a several hooks you can use to interfere the test process in order to enhance
|
||||
// it and build services around it. You can either apply a single function to it or an array of
|
||||
// methods. If one of them returns with a promise, WebdriverIO will wait until that promise is
|
||||
// resolved to continue.
|
||||
//
|
||||
/**
|
||||
* Gets executed once before all workers get launched.
|
||||
* @param {object} config wdio configuration object
|
||||
* @param {Array.<Object>} capabilities list of capabilities details
|
||||
*/
|
||||
onPrepare: function (config, capabilities) {
|
||||
},
|
||||
/**
|
||||
* Gets executed before a worker process is spawned and can be used to initialize specific service
|
||||
* for that worker as well as modify runtime environments in an async fashion.
|
||||
* @param {string} cid capability id (e.g 0-0)
|
||||
* @param {object} caps object containing capabilities for session that will be spawn in the worker
|
||||
* @param {object} specs specs to be run in the worker process
|
||||
* @param {object} args object that will be merged with the main configuration once worker is initialized
|
||||
* @param {object} execArgv list of string arguments passed to the worker process
|
||||
*/
|
||||
onWorkerStart: function (cid, caps, specs, args, execArgv) {
|
||||
},
|
||||
/**
|
||||
* Gets executed after a worker process has exited.
|
||||
* @param {string} cid capability id (e.g 0-0)
|
||||
* @param {number} exitCode 0 - success, 1 - fail
|
||||
* @param {object} specs specs to be run in the worker process
|
||||
* @param {number} retries number of retries used
|
||||
*/
|
||||
onWorkerEnd: function (cid, exitCode, specs, retries) {
|
||||
},
|
||||
/**
|
||||
* Gets executed before initializing the webdriver session and test framework. It allows you
|
||||
* to manipulate configurations depending on the capability or spec.
|
||||
* @param {object} config wdio configuration object
|
||||
* @param {Array.<Object>} capabilities list of capabilities details
|
||||
* @param {Array.<String>} specs List of spec file paths that are to be run
|
||||
*/
|
||||
beforeSession: function (config, capabilities, specs) {
|
||||
},
|
||||
/**
|
||||
* Gets executed before test execution begins. At this point you can access to all global
|
||||
* variables like `browser`. It is the perfect place to define custom commands.
|
||||
* @param {Array.<Object>} capabilities list of capabilities details
|
||||
* @param {Array.<String>} specs List of spec file paths that are to be run
|
||||
* @param {object} browser instance of created browser/device session
|
||||
*/
|
||||
before: function (capabilities, specs, browser) {
|
||||
},
|
||||
/**
|
||||
* Gets executed before the suite starts (in Mocha/Jasmine only).
|
||||
* @param {object} suite suite details
|
||||
*/
|
||||
beforeSuite: function (suite) {
|
||||
},
|
||||
/**
|
||||
* This hook gets executed _before_ every hook within the suite starts.
|
||||
* (For example, this runs before calling `before`, `beforeEach`, `after`, `afterEach` in Mocha.). In Cucumber `context` is the World object.
|
||||
*
|
||||
*/
|
||||
beforeHook: function (test, context, hookName) {
|
||||
},
|
||||
/**
|
||||
* Hook that gets executed _after_ every hook within the suite ends.
|
||||
* (For example, this runs after calling `before`, `beforeEach`, `after`, `afterEach` in Mocha.). In Cucumber `context` is the World object.
|
||||
*/
|
||||
afterHook: function (test, context, { error, result, duration, passed, retries }, hookName) {
|
||||
},
|
||||
/**
|
||||
* Function to be executed before a test (in Mocha/Jasmine only)
|
||||
* @param {object} test test object
|
||||
* @param {object} context scope object the test was executed with
|
||||
*/
|
||||
beforeTest: function (test, context) {
|
||||
},
|
||||
/**
|
||||
* Runs before a WebdriverIO command is executed.
|
||||
* @param {string} commandName hook command name
|
||||
* @param {Array} args arguments that the command would receive
|
||||
*/
|
||||
beforeCommand: function (commandName, args) {
|
||||
},
|
||||
/**
|
||||
* Runs after a WebdriverIO command gets executed
|
||||
* @param {string} commandName hook command name
|
||||
* @param {Array} args arguments that command would receive
|
||||
* @param {number} result 0 - command success, 1 - command error
|
||||
* @param {object} error error object, if any
|
||||
*/
|
||||
afterCommand: function (commandName, args, result, error) {
|
||||
},
|
||||
/**
|
||||
* Function to be executed after a test (in Mocha/Jasmine only)
|
||||
* @param {object} test test object
|
||||
* @param {object} context scope object the test was executed with
|
||||
* @param {Error} result.error error object in case the test fails, otherwise `undefined`
|
||||
* @param {*} result.result return object of test function
|
||||
* @param {number} result.duration duration of test
|
||||
* @param {boolean} result.passed true if test has passed, otherwise false
|
||||
* @param {object} result.retries information about spec related retries, e.g. `{ attempts: 0, limit: 0 }`
|
||||
*/
|
||||
afterTest: function (test, context, { error, result, duration, passed, retries }) {
|
||||
},
|
||||
/**
|
||||
* Hook that gets executed after the suite has ended (in Mocha/Jasmine only).
|
||||
* @param {object} suite suite details
|
||||
*/
|
||||
afterSuite: function (suite) {
|
||||
},
|
||||
/**
|
||||
* Gets executed after all tests are done. You still have access to all global variables from
|
||||
* the test.
|
||||
* @param {number} result 0 - test pass, 1 - test fail
|
||||
* @param {Array.<Object>} capabilities list of capabilities details
|
||||
* @param {Array.<String>} specs List of spec file paths that ran
|
||||
*/
|
||||
after: function (result, capabilities, specs) {
|
||||
},
|
||||
/**
|
||||
* Gets executed right after terminating the webdriver session.
|
||||
* @param {object} config wdio configuration object
|
||||
* @param {Array.<Object>} capabilities list of capabilities details
|
||||
* @param {Array.<String>} specs List of spec file paths that ran
|
||||
*/
|
||||
afterSession: function (config, capabilities, specs) {
|
||||
},
|
||||
/**
|
||||
* Gets executed after all workers have shut down and the process is about to exit.
|
||||
* An error thrown in the `onComplete` hook will result in the test run failing.
|
||||
* @param {object} exitCode 0 - success, 1 - fail
|
||||
* @param {object} config wdio configuration object
|
||||
* @param {Array.<Object>} capabilities list of capabilities details
|
||||
* @param {<Object>} results object containing test results
|
||||
*/
|
||||
onComplete: function (exitCode, config, capabilities, results) {
|
||||
},
|
||||
/**
|
||||
* Gets executed when a refresh happens.
|
||||
* @param {string} oldSessionId session ID of the old session
|
||||
* @param {string} newSessionId session ID of the new session
|
||||
*/
|
||||
onReload: function(oldSessionId, newSessionId) {
|
||||
},
|
||||
/**
|
||||
* Cucumber Hooks
|
||||
*
|
||||
* Runs before a Cucumber Feature.
|
||||
* @param {string} uri path to feature file
|
||||
* @param {GherkinDocument.IFeature} feature Cucumber feature object
|
||||
*/
|
||||
beforeFeature: function (uri, feature) {
|
||||
},
|
||||
/**
|
||||
*
|
||||
* Runs before a Cucumber Scenario.
|
||||
* @param {ITestCaseHookParameter} world world object containing information on pickle and test step
|
||||
* @param {object} context Cucumber World object
|
||||
*/
|
||||
beforeScenario: function (world, context) {
|
||||
},
|
||||
/**
|
||||
*
|
||||
* Runs before a Cucumber Step.
|
||||
* @param {Pickle.IPickleStep} step step data
|
||||
* @param {IPickle} scenario scenario pickle
|
||||
* @param {object} context Cucumber World object
|
||||
*/
|
||||
beforeStep: function (step, scenario, context) {
|
||||
},
|
||||
/**
|
||||
*
|
||||
* Runs after a Cucumber Step.
|
||||
* @param {Pickle.IPickleStep} step step data
|
||||
* @param {IPickle} scenario scenario pickle
|
||||
* @param {object} result results object containing scenario results
|
||||
* @param {boolean} result.passed true if scenario has passed
|
||||
* @param {string} result.error error stack if scenario failed
|
||||
* @param {number} result.duration duration of scenario in milliseconds
|
||||
* @param {object} context Cucumber World object
|
||||
*/
|
||||
afterStep: function (step, scenario, result, context) {
|
||||
},
|
||||
/**
|
||||
*
|
||||
* Runs after a Cucumber Scenario.
|
||||
* @param {ITestCaseHookParameter} world world object containing information on pickle and test step
|
||||
* @param {object} result results object containing scenario results `{passed: boolean, error: string, duration: number}`
|
||||
* @param {boolean} result.passed true if scenario has passed
|
||||
* @param {string} result.error error stack if scenario failed
|
||||
* @param {number} result.duration duration of scenario in milliseconds
|
||||
* @param {object} context Cucumber World object
|
||||
*/
|
||||
afterScenario: function (world, result, context) {
|
||||
},
|
||||
/**
|
||||
*
|
||||
* Runs after a Cucumber Feature.
|
||||
* @param {string} uri path to feature file
|
||||
* @param {GherkinDocument.IFeature} feature Cucumber feature object
|
||||
*/
|
||||
afterFeature: function (uri, feature) {
|
||||
},
|
||||
/**
|
||||
* Runs before a WebdriverIO assertion library makes an assertion.
|
||||
* @param commandName command name
|
||||
* @param args arguments that command would receive
|
||||
*/
|
||||
beforeAssertion: function (params) {
|
||||
},
|
||||
/**
|
||||
* Runs after a WebdriverIO command gets executed
|
||||
* @param commandName command name
|
||||
* @param args arguments that command would receive
|
||||
* @param result result of the command
|
||||
* @param error error in case something went wrong
|
||||
*/
|
||||
afterAssertion: function (params) {
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
You can also find a file with all possible options and variations in the [example folder](https://github.com/webdriverio/webdriverio/blob/main/examples/wdio.conf.js).
|
||||
|
|
@ -1,379 +0,0 @@
|
|||
---
|
||||
id: customcommands
|
||||
title: Custom Commands
|
||||
---
|
||||
|
||||
If you want to extend the `browser` instance with your own set of commands, the browser method `addCommand` is here for you. You can write your command in a asynchronous way, just as in your specs.
|
||||
|
||||
## Parameters
|
||||
|
||||
### Command Name
|
||||
|
||||
A name that defines the command and will be attached to the browser or element scope.
|
||||
|
||||
Type: `String`
|
||||
|
||||
### Custom Function
|
||||
|
||||
A function that is being executed when the command is called. The `this` scope is either [`WebdriverIO.Browser`](/docs/api/browser) or [`WebdriverIO.Element`](/docs/api/element) depending whether the command gets attached to the browser or element scope.
|
||||
|
||||
Type: `Function`
|
||||
|
||||
### Target Scope
|
||||
|
||||
Flag to decide whether to attach the command to the browser or element scope. If set to `true` the command will be an element command.
|
||||
|
||||
Type: `Boolean`<br /> Default: `false`
|
||||
|
||||
## Examples
|
||||
|
||||
This example shows how to add a new command that returns the current URL and title as one result. The scope (`this`) is a [`WebdriverIO.Browser`](/docs/api/browser) object.
|
||||
|
||||
```js
|
||||
browser.addCommand('getUrlAndTitle', async function (customVar) {
|
||||
// `this` refers to the `browser` scope
|
||||
return {
|
||||
url: await this.getUrl(),
|
||||
title: await this.getTitle(),
|
||||
customVar: customVar
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
Additionally, you can extend the element instance with your own set of commands, by passing `true` as the final argument. The scope (`this`) in this case is a [`WebdriverIO.Element`](/docs/api/element) object.
|
||||
|
||||
```js
|
||||
browser.addCommand("waitAndClick", async function () {
|
||||
// `this` is return value of $(selector)
|
||||
await this.waitForDisplayed()
|
||||
await this.click()
|
||||
}, true)
|
||||
```
|
||||
|
||||
Custom commands give you the opportunity to bundle a specific sequence of commands you use frequently as a single call. You can define custom commands at any point in your test suite; just make sure that the command is defined *before* its first use. (The `before` hook in your `wdio.conf.js` is one good place to create them.)
|
||||
|
||||
Once defined, you can use them as follows:
|
||||
|
||||
```js
|
||||
it('should use my custom command', async () => {
|
||||
await browser.url('http://www.github.com')
|
||||
const result = await browser.getUrlAndTitle('foobar')
|
||||
|
||||
assert.strictEqual(result.url, 'https://github.com/')
|
||||
assert.strictEqual(result.title, 'GitHub · Where software is built')
|
||||
assert.strictEqual(result.customVar, 'foobar')
|
||||
})
|
||||
```
|
||||
|
||||
__Note:__ If you register a custom command to the `browser` scope, the command won't be accessible for elements. Likewise, if you register a command to the element scope, it won't be accessible in the `browser` scope:
|
||||
|
||||
```js
|
||||
browser.addCommand("myCustomBrowserCommand", () => { return 1 })
|
||||
const elem = await $('body')
|
||||
console.log(typeof browser.myCustomBrowserCommand) // outputs "function"
|
||||
console.log(typeof elem.myCustomBrowserCommand()) // outputs "undefined"
|
||||
|
||||
browser.addCommand("myCustomElementCommand", () => { return 1 }, true)
|
||||
const elem2 = await $('body')
|
||||
console.log(typeof browser.myCustomElementCommand) // outputs "undefined"
|
||||
console.log(await elem2.myCustomElementCommand('foobar')) // outputs "1"
|
||||
|
||||
const elem3 = await $('body')
|
||||
elem3.addCommand("myCustomElementCommand2", () => { return 2 })
|
||||
console.log(typeof browser.myCustomElementCommand2) // outputs "undefined"
|
||||
console.log(await elem3.myCustomElementCommand2('foobar')) // outputs "2"
|
||||
```
|
||||
|
||||
__Note:__ If you need to chain a custom command, the command should end with `$`,
|
||||
|
||||
```js
|
||||
browser.addCommand("user$", (locator) => { return ele })
|
||||
browser.addCommand("user$", (locator) => { return ele }, true)
|
||||
await browser.user$('foo').user$('bar').click()
|
||||
```
|
||||
|
||||
Be careful to not overload the `browser` scope with too many custom commands.
|
||||
|
||||
We recommend defining custom logic in [page objects](pageobjects), so they are bound to a specific page.
|
||||
|
||||
### Multiremote
|
||||
|
||||
`addCommand` works in a similar way for multiremote, except the new command will propagate down to the children instances. You have to be mindful when using `this` object since the multiremote `browser` and its children instances have different `this`.
|
||||
|
||||
This example shows how to add a new command for multiremote.
|
||||
|
||||
```js
|
||||
import { multiremotebrowser } from '@wdio/globals'
|
||||
|
||||
multiremotebrowser.addCommand('getUrlAndTitle', async function (this: WebdriverIO.MultiRemoteBrowser, customVar: any) {
|
||||
// `this` refers to:
|
||||
// - MultiRemoteBrowser scope for browser
|
||||
// - Browser scope for instances
|
||||
return {
|
||||
url: await this.getUrl(),
|
||||
title: await this.getTitle(),
|
||||
customVar: customVar
|
||||
}
|
||||
})
|
||||
|
||||
multiremotebrowser.getUrlAndTitle()
|
||||
/*
|
||||
{
|
||||
url: [ 'https://webdriver.io/', 'https://webdriver.io/' ],
|
||||
title: [
|
||||
'WebdriverIO · Next-gen browser and mobile automation test framework for Node.js | WebdriverIO',
|
||||
'WebdriverIO · Next-gen browser and mobile automation test framework for Node.js | WebdriverIO'
|
||||
],
|
||||
customVar: undefined
|
||||
}
|
||||
*/
|
||||
|
||||
multiremotebrowser.getInstance('browserA').getUrlAndTitle()
|
||||
/*
|
||||
{
|
||||
url: 'https://webdriver.io/',
|
||||
title: 'WebdriverIO · Next-gen browser and mobile automation test framework for Node.js | WebdriverIO',
|
||||
customVar: undefined
|
||||
}
|
||||
*/
|
||||
```
|
||||
|
||||
## Extend Type Definitions
|
||||
|
||||
With TypeScript, it's easy to extend WebdriverIO interfaces. Add types to your custom commands like this:
|
||||
|
||||
1. Create a type definition file (e.g., `./src/types/wdio.d.ts`)
|
||||
2. a. If using a module-style type definition file (using import/export and `declare global WebdriverIO` in the type definition file), make sure to include the file path in the `tsconfig.json` `include` property.
|
||||
|
||||
b. If using ambient-style type definition files (no import/export in type definition files and `declare namespace WebdriverIO` for custom commands), make sure the `tsconfig.json` does *not* contain any `include` section, since this will cause all type definition files not listed in the `include` section to not be recognized by typescript.
|
||||
|
||||
<Tabs
|
||||
defaultValue="modules"
|
||||
values={[
|
||||
{label: 'Modules (using import/export)', value: 'modules'},
|
||||
{label: 'Ambient Type Definitions (no tsconfig include)', value: 'ambient'},
|
||||
]
|
||||
}>
|
||||
<TabItem value="modules">
|
||||
|
||||
```json title="tsconfig.json"
|
||||
{
|
||||
"compilerOptions": { ... },
|
||||
"include": [
|
||||
"./test/**/*.ts",
|
||||
"./src/types/**/*.ts"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="ambient">
|
||||
|
||||
```json title="tsconfig.json"
|
||||
{
|
||||
"compilerOptions": { ... }
|
||||
}
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
3. Add definitions for your commands according to your execution mode.
|
||||
|
||||
<Tabs
|
||||
defaultValue="modules"
|
||||
values={[
|
||||
{label: 'Modules (using import/export)', value: 'modules'},
|
||||
{label: 'Ambient Type Definitions', value: 'ambient'},
|
||||
]
|
||||
}>
|
||||
<TabItem value="modules">
|
||||
|
||||
```typescript
|
||||
declare global {
|
||||
namespace WebdriverIO {
|
||||
interface Browser {
|
||||
browserCustomCommand: (arg: any) => Promise<void>
|
||||
}
|
||||
|
||||
interface MultiRemoteBrowser {
|
||||
browserCustomCommand: (arg: any) => Promise<void>
|
||||
}
|
||||
|
||||
interface Element {
|
||||
elementCustomCommand: (arg: any) => Promise<number>
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="ambient">
|
||||
|
||||
```typescript
|
||||
declare namespace WebdriverIO {
|
||||
interface Browser {
|
||||
browserCustomCommand: (arg: any) => Promise<void>
|
||||
}
|
||||
|
||||
interface MultiRemoteBrowser {
|
||||
browserCustomCommand: (arg: any) => Promise<void>
|
||||
}
|
||||
|
||||
interface Element {
|
||||
elementCustomCommand: (arg: any) => Promise<number>
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## Integrate 3rd Party Libraries
|
||||
|
||||
If you use external libraries (e.g., to do database calls) that support promises, a nice approach to integrate them is to wrap certain API methods with a custom command.
|
||||
|
||||
When returning the promise, WebdriverIO ensures that it doesn't continue with the next command until the promise is resolved. If the promise gets rejected, the command will throw an error.
|
||||
|
||||
```js
|
||||
browser.addCommand('makeRequest', async (url) => {
|
||||
const response = await fetch(url)
|
||||
return await response.json()
|
||||
})
|
||||
```
|
||||
|
||||
Then, just use it in your WDIO test specs:
|
||||
|
||||
```js
|
||||
it('execute external library in a sync way', async () => {
|
||||
await browser.url('...')
|
||||
const body = await browser.makeRequest('http://...')
|
||||
console.log(body) // returns response body
|
||||
})
|
||||
```
|
||||
|
||||
**Note:** The result of your custom command is the result of the promise you return.
|
||||
|
||||
## Overwriting Commands
|
||||
|
||||
You can also overwrite native commands with `overwriteCommand`.
|
||||
|
||||
It is not recommended to do this, because it may lead to unpredictable behavior of the framework!
|
||||
|
||||
The overall approach is similar to `addCommand`, the only difference is that the first argument in the command function is the original function that you are about to overwrite. Please see some examples below.
|
||||
|
||||
### Overwriting Browser Commands
|
||||
|
||||
```js
|
||||
/**
|
||||
* print milliseconds before pause and return its value.
|
||||
*/
|
||||
// 'pause' - name of command to be overwritten
|
||||
// origPauseFunction - original pause function
|
||||
browser.overwriteCommand('pause', async (origPauseFunction, ms) => {
|
||||
console.log(`sleeping for ${ms}`)
|
||||
await origPauseFunction(ms)
|
||||
return ms
|
||||
})
|
||||
|
||||
// then use it as before
|
||||
console.log(`was sleeping for ${await browser.pause(1000)}`)
|
||||
```
|
||||
|
||||
### Overwriting Element Commands
|
||||
|
||||
Overwriting commands on element level is almost the same. Simply pass `true` as the third argument to `overwriteCommand`:
|
||||
|
||||
```js
|
||||
/**
|
||||
* Attempt to scroll to element if it is not clickable.
|
||||
* Pass { force: true } to click with JS even if element is not visible or clickable.
|
||||
*/
|
||||
// 'click' - name of command to be overwritten
|
||||
// origClickFunction - original click function
|
||||
browser.overwriteCommand('click', async function (origClickFunction, { force = false } = {}) {
|
||||
if (!force) {
|
||||
try {
|
||||
// attempt to click
|
||||
await origClickFunction()
|
||||
return null
|
||||
} catch (err) {
|
||||
if (err.message.includes('not clickable at point')) {
|
||||
console.warn('WARN: Element', this.selector, 'is not clickable.',
|
||||
'Scrolling to it before clicking again.')
|
||||
|
||||
// scroll to element and click again
|
||||
await this.scrollIntoView()
|
||||
return origClickFunction()
|
||||
}
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
// clicking with js
|
||||
console.warn('WARN: Using force click for', this.selector)
|
||||
await browser.execute((el) => {
|
||||
el.click()
|
||||
}, this)
|
||||
}, true) // don't forget to pass `true` as 3rd argument
|
||||
|
||||
// then use it as before
|
||||
const elem = await $('body')
|
||||
await elem.click()
|
||||
|
||||
// or pass params
|
||||
await elem.click({ force: true })
|
||||
```
|
||||
|
||||
## Add More WebDriver Commands
|
||||
|
||||
If you are using the WebDriver protocol and run tests on a platform that supports additional commands not defined by any of the protocol definitions in [`@wdio/protocols`](https://github.com/webdriverio/webdriverio/tree/main/packages/wdio-protocols/src/protocols) you can manually add them through the `addCommand` interface. The `webdriver` package offers a command wrapper that allows to register these new endpoints in the same way as other commands, providing the same parameter checks and error handling. To register this new endpoint import the command wrapper and register a new command with it as follows:
|
||||
|
||||
```js
|
||||
import { command } from 'webdriver'
|
||||
|
||||
browser.addCommand('myNewCommand', command('POST', '/session/:sessionId/foobar/:someId', {
|
||||
command: 'myNewCommand',
|
||||
description: 'a new WebDriver command',
|
||||
ref: 'https://vendor.com/commands/#myNewCommand',
|
||||
variables: [{
|
||||
name: 'someId',
|
||||
description: 'some id to something'
|
||||
}],
|
||||
parameters: [{
|
||||
name: 'foo',
|
||||
type: 'string',
|
||||
description: 'a valid parameter',
|
||||
required: true
|
||||
}]
|
||||
}))
|
||||
```
|
||||
|
||||
Calling this command with invalid parameters results in the same error handling as predefined protocol commands, e.g.:
|
||||
|
||||
```js
|
||||
// call command without required url parameter and payload
|
||||
await browser.myNewCommand()
|
||||
|
||||
/**
|
||||
* results in the following error:
|
||||
* Error: Wrong parameters applied for myNewCommand
|
||||
* Usage: myNewCommand(someId, foo)
|
||||
*
|
||||
* Property Description:
|
||||
* "someId" (string): some id to something
|
||||
* "foo" (string): a valid parameter
|
||||
*
|
||||
* For more info see https://my-api.com
|
||||
* at Browser.protocolCommand (...)
|
||||
* ...
|
||||
*/
|
||||
```
|
||||
|
||||
Calling the command correctly, e.g. `browser.myNewCommand('foo', 'bar')`, correctly makes a WebDriver request to e.g. `http://localhost:4444/session/7bae3c4c55c3bf82f54894ddc83c5f31/foobar/foo` with a payload like `{ foo: 'bar' }`.
|
||||
|
||||
:::note
|
||||
The `:sessionId` url parameter will be automatically substituted with the session id of the WebDriver session. Other url parameter can be applied but need to be defined within `variables`.
|
||||
:::
|
||||
|
||||
See examples of how protocol commands can be defined in the [`@wdio/protocols`](https://github.com/webdriverio/webdriverio/tree/main/packages/wdio-protocols/src/protocols) package.
|
||||
|
|
@ -1,60 +0,0 @@
|
|||
---
|
||||
id: custommatchers
|
||||
title: Custom Matchers
|
||||
---
|
||||
|
||||
WebdriverIO uses a Jest style [`expect`](https://webdriver.io/docs/api/expect-webdriverio) assertion library that comes with special features and custom matchers specific for running web and mobile tests. While the library of matchers is big, it certainly doesn't fit all possible situations. Therefore it is possible to extend the existing matchers with custom ones defined by you.
|
||||
|
||||
:::warning
|
||||
|
||||
While there is currently no difference in how matchers are defined that are specific to the [`browser`](/docs/api/browser) object or an [element](/docs/api/element) instance, this certainly might change in the future. Keep an eye on [`webdriverio/expect-webdriverio#1408`](https://github.com/webdriverio/expect-webdriverio/issues/1408) for further information on this development.
|
||||
|
||||
:::
|
||||
|
||||
## Custom Browser Matchers
|
||||
|
||||
To register a custom browser matcher, call `extend` on the `expect` object either in your spec file directly or as part of the e.g. `before` hook in your `wdio.conf.js`:
|
||||
|
||||
```js reference useHTTPS
|
||||
https://github.com/webdriverio/example-recipes/blob/e719632df8f241f923c8d9301aab6bccee5cb109/customMatchers/example.ts#L3-L18
|
||||
```
|
||||
|
||||
As shown in the example the matcher function takes the expected object, e.g. the browser or element object, as the first parameter and the expected value as the second. You can then use the matcher as follows:
|
||||
|
||||
```js reference useHTTPS
|
||||
https://github.com/webdriverio/example-recipes/blob/e719632df8f241f923c8d9301aab6bccee5cb109/customMatchers/example.ts#L50-L52
|
||||
```
|
||||
|
||||
## Custom Element Matchers
|
||||
|
||||
Similar to custom browser matchers, element matchers don't differ. Here is an example of how to create a custom matcher to assert the aria-label of an element:
|
||||
|
||||
```js reference useHTTPS
|
||||
https://github.com/webdriverio/example-recipes/blob/e719632df8f241f923c8d9301aab6bccee5cb109/customMatchers/example.ts#L20-L38
|
||||
```
|
||||
|
||||
This allows you to call the assertion as follows:
|
||||
|
||||
```js reference useHTTPS
|
||||
https://github.com/webdriverio/example-recipes/blob/e719632df8f241f923c8d9301aab6bccee5cb109/customMatchers/example.ts#L54-L57
|
||||
```
|
||||
|
||||
## TypeScript Support
|
||||
|
||||
If you are using TypeScript, one more step is required to ensure the type safety of your custom matchers. By extending the `Matcher` interface with your custom matchers, all type issues vanish:
|
||||
|
||||
```js reference useHTTPS
|
||||
https://github.com/webdriverio/example-recipes/blob/e719632df8f241f923c8d9301aab6bccee5cb109/customMatchers/example.ts#L40-L47
|
||||
```
|
||||
|
||||
If you created a custom [asymmetric matcher](https://jestjs.io/docs/expect#expectextendmatchers), you can similarly extend the `expect` types as follows:
|
||||
|
||||
```ts
|
||||
declare global {
|
||||
namespace ExpectWebdriverIO {
|
||||
interface AsymmetricMatchers {
|
||||
myCustomMatcher(value: string): ExpectWebdriverIO.PartialMatcher;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
|
@ -1,193 +0,0 @@
|
|||
---
|
||||
id: customreporter
|
||||
title: Custom Reporter
|
||||
---
|
||||
|
||||
You can write your own custom reporter for the WDIO test runner that is tailored to your needs. And it’s easy!
|
||||
|
||||
All you need to do is to create a node module that inherits from the `@wdio/reporter` package, so it can receive messages from the test.
|
||||
|
||||
The basic setup should look like:
|
||||
|
||||
```js
|
||||
import WDIOReporter from '@wdio/reporter'
|
||||
|
||||
export default class CustomReporter extends WDIOReporter {
|
||||
constructor(options) {
|
||||
/*
|
||||
* make reporter to write to the output stream by default
|
||||
*/
|
||||
options = Object.assign(options, { stdout: true })
|
||||
super(options)
|
||||
}
|
||||
|
||||
onTestPass(test) {
|
||||
this.write(`Congratulations! Your test "${test.title}" passed 👏`)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
To use this reporter, all you need to do is assign it to the `reporter` property in your configuration.
|
||||
|
||||
|
||||
Your `wdio.conf.js` file should look like this:
|
||||
|
||||
```js
|
||||
import CustomReporter from './reporter/my.custom.reporter'
|
||||
|
||||
export const config = {
|
||||
// ...
|
||||
reporters: [
|
||||
/**
|
||||
* use imported reporter class
|
||||
*/
|
||||
[CustomReporter, {
|
||||
someOption: 'foobar'
|
||||
}],
|
||||
/**
|
||||
* use absolute path to reporter
|
||||
*/
|
||||
['/path/to/reporter.js', {
|
||||
someOption: 'foobar'
|
||||
}]
|
||||
],
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
You can also publish the reporter to NPM so everyone can use it. Name the package like other reporters `wdio-<reportername>-reporter`, and tag it with keywords like `wdio` or `wdio-reporter`.
|
||||
|
||||
## Event Handler
|
||||
|
||||
You can register an event handler for several events which are triggered during testing. All of the following handlers will receive payloads with useful information about the current state and progress.
|
||||
|
||||
The structure of these payload objects depend on the event, and are unified across the frameworks (Mocha, Jasmine, and Cucumber). Once you implement a custom reporter, it should work for all frameworks.
|
||||
|
||||
The following list contains all possible methods you can add to your reporter class:
|
||||
|
||||
```js
|
||||
import WDIOReporter from '@wdio/reporter'
|
||||
|
||||
export default class CustomReporter extends WDIOReporter {
|
||||
onRunnerStart() {}
|
||||
onBeforeCommand() {}
|
||||
onAfterCommand() {}
|
||||
onSuiteStart() {}
|
||||
onHookStart() {}
|
||||
onHookEnd() {}
|
||||
onTestStart() {}
|
||||
onTestPass() {}
|
||||
onTestFail() {}
|
||||
onTestSkip() {}
|
||||
onTestEnd() {}
|
||||
onSuiteEnd() {}
|
||||
onRunnerEnd() {}
|
||||
}
|
||||
```
|
||||
|
||||
The method names are pretty self explanatory.
|
||||
|
||||
To print something on a certain event, use the `this.write(...)` method, which is provided by the parent `WDIOReporter` class. It either streams the content to `stdout`, or to a log file (depending on the options of the reporter).
|
||||
|
||||
```js
|
||||
import WDIOReporter from '@wdio/reporter'
|
||||
|
||||
export default class CustomReporter extends WDIOReporter {
|
||||
onTestPass(test) {
|
||||
this.write(`Congratulations! Your test "${test.title}" passed 👏`)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Note that you cannot defer the test execution in any way.
|
||||
|
||||
All event handlers should execute synchronous routines (or you'll run into race conditions).
|
||||
|
||||
Be sure to check out the [example section](https://github.com/webdriverio/webdriverio/tree/main/examples/wdio) where you can find an example custom reporter that prints the event name for each event.
|
||||
|
||||
If you have implemented a custom reporter that could be useful for the community, don't hesitate to make a Pull Request so we can make the reporter available for the public!
|
||||
|
||||
Also, if you run the WDIO testrunner via the `Launcher` interface, you can't apply a custom reporter as function as follows:
|
||||
|
||||
```js
|
||||
import Launcher from '@wdio/cli'
|
||||
|
||||
import CustomReporter from './reporter/my.custom.reporter'
|
||||
|
||||
const launcher = new Launcher('/path/to/config.file.js', {
|
||||
// this will NOT work, because CustomReporter is not serializable
|
||||
reporters: ['dot', CustomReporter]
|
||||
})
|
||||
```
|
||||
|
||||
## Wait Until `isSynchronised`
|
||||
|
||||
If your reporter has to execute async operations to report the data (e.g. upload of log files or other assets) you can overwrite the `isSynchronised` method in your custom reporter to let the WebdriverIO runner wait until you have computed everything. An example of this can be seen in the [`@wdio/sumologic-reporter`](https://github.com/webdriverio/webdriverio/blob/main/packages/wdio-sumologic-reporter/src/index.ts):
|
||||
|
||||
```js
|
||||
export default class SumoLogicReporter extends WDIOReporter {
|
||||
constructor (options) {
|
||||
// ...
|
||||
this.unsynced = []
|
||||
this.interval = setInterval(::this.sync, this.options.syncInterval)
|
||||
// ...
|
||||
}
|
||||
|
||||
/**
|
||||
* overwrite isSynchronised method
|
||||
*/
|
||||
get isSynchronised () {
|
||||
return this.unsynced.length === 0
|
||||
}
|
||||
|
||||
/**
|
||||
* sync log files
|
||||
*/
|
||||
sync () {
|
||||
// ...
|
||||
request({
|
||||
method: 'POST',
|
||||
uri: this.options.sourceAddress,
|
||||
body: logLines
|
||||
}, (err, resp) => {
|
||||
// ...
|
||||
/**
|
||||
* remove transferred logs from log bucket
|
||||
*/
|
||||
this.unsynced.splice(0, MAX_LINES)
|
||||
// ...
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This way the runner will wait until all log information are uploaded.
|
||||
|
||||
## Publish Reporter on NPM
|
||||
|
||||
To make reporter easier to consume and discover by the WebdriverIO community, please follow these recommendations:
|
||||
|
||||
* Services should use this naming convention: `wdio-*-reporter`
|
||||
* Use NPM keywords: `wdio-plugin`, `wdio-reporter`
|
||||
* The `main` entry should `export` an instance of the reporter
|
||||
* Example reporter: [`@wdio/dot-service`](https://github.com/webdriverio/webdriverio/tree/main/packages/wdio-dot-reporter)
|
||||
|
||||
Following the recommended naming pattern allows services to be added by name:
|
||||
|
||||
```js
|
||||
// Add wdio-custom-reporter
|
||||
export const config = {
|
||||
// ...
|
||||
reporter: ['custom'],
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
### Add Published Service to WDIO CLI and Docs
|
||||
|
||||
We really appreciate every new plugin that could help other people run better tests! If you have created such a plugin, please consider adding it to our CLI and docs to make it easier to be found.
|
||||
|
||||
Please raise a pull request with the following changes:
|
||||
|
||||
- add your service to the list of [supported reporters](https://github.com/webdriverio/webdriverio/blob/main/packages/wdio-cli/src/constants.ts#L74-L91)) in the CLI module
|
||||
- enhance the [reporter list](https://github.com/webdriverio/webdriverio/blob/main/scripts/docs-generation/3rd-party/reporters.json) for adding your docs to the official Webdriver.io page
|
||||
|
|
@ -1,175 +0,0 @@
|
|||
---
|
||||
id: customservices
|
||||
title: Custom Services
|
||||
---
|
||||
|
||||
You can write your own custom service for the WDIO test runner to custom-fit your needs.
|
||||
|
||||
Services are add-ons that are created for reusable logic to simplify tests, manage your test suite and integrate results. Services have access to all the same [hooks](/docs/configurationfile) available in the `wdio.conf.js`.
|
||||
|
||||
There are two types of services that can be defined: a launcher service that only has access to the `onPrepare`, `onWorkerStart`, `onWorkerEnd` and `onComplete` hook which are only executed once per test run, and a worker service that has access to all other hooks and is being executed for each worker. Note that you can not share (global) variables between both types of services as worker services run in a different (worker) process.
|
||||
|
||||
A launcher service can be defined as follows:
|
||||
|
||||
```js
|
||||
export default class CustomLauncherService {
|
||||
// If a hook returns a promise, WebdriverIO will wait until that promise is resolved to continue.
|
||||
async onPrepare(config, capabilities) {
|
||||
// TODO: something before all workers launch
|
||||
}
|
||||
|
||||
onComplete(exitCode, config, capabilities) {
|
||||
// TODO: something after the workers shutdown
|
||||
}
|
||||
|
||||
// custom service methods ...
|
||||
}
|
||||
```
|
||||
|
||||
Whereas a worker service should look like this:
|
||||
|
||||
```js
|
||||
export default class CustomWorkerService {
|
||||
/**
|
||||
* `serviceOptions` contains all options specific to the service
|
||||
* e.g. if defined as follows:
|
||||
*
|
||||
* ```
|
||||
* services: [['custom', { foo: 'bar' }]]
|
||||
* ```
|
||||
*
|
||||
* the `serviceOptions` parameter will be: `{ foo: 'bar' }`
|
||||
*/
|
||||
constructor (serviceOptions, capabilities, config) {
|
||||
this.options = serviceOptions
|
||||
}
|
||||
|
||||
/**
|
||||
* this browser object is passed in here for the first time
|
||||
*/
|
||||
async before(config, capabilities, browser) {
|
||||
this.browser = browser
|
||||
|
||||
// TODO: something before all tests are run, e.g.:
|
||||
await this.browser.setWindowSize(1024, 768)
|
||||
}
|
||||
|
||||
after(exitCode, config, capabilities) {
|
||||
// TODO: something after all tests are run
|
||||
}
|
||||
|
||||
beforeTest(test, context) {
|
||||
// TODO: something before each Mocha/Jasmine test run
|
||||
}
|
||||
|
||||
beforeScenario(test, context) {
|
||||
// TODO: something before each Cucumber scenario run
|
||||
}
|
||||
|
||||
// other hooks or custom service methods ...
|
||||
}
|
||||
```
|
||||
|
||||
It is recommended to store the browser object through the passed in parameter in the constructor. Lastly expose both types of workers as following:
|
||||
|
||||
```js
|
||||
import CustomLauncherService from './launcher'
|
||||
import CustomWorkerService from './service'
|
||||
|
||||
export default CustomWorkerService
|
||||
export const launcher = CustomLauncherService
|
||||
```
|
||||
|
||||
If you are using TypeScript and want to make sure that hook methods parameter are type safe, you can define your service class as follows:
|
||||
|
||||
```ts
|
||||
import type { Capabilities, Options, Services } from '@wdio/types'
|
||||
|
||||
export default class CustomWorkerService implements Services.ServiceInstance {
|
||||
constructor (
|
||||
private _options: MyServiceOptions,
|
||||
private _capabilities: Capabilities.RemoteCapability,
|
||||
private _config: WebdriverIO.Config,
|
||||
) {
|
||||
// ...
|
||||
}
|
||||
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
## Service Error Handling
|
||||
|
||||
An Error thrown during a service hook will be logged while the runner continues. If a hook in your service is critical to the setup or teardown of the test runner, the `SevereServiceError` exposed from the `webdriverio` package can be used to stop the runner.
|
||||
|
||||
```js
|
||||
import { SevereServiceError } from 'webdriverio'
|
||||
|
||||
export default class CustomServiceLauncher {
|
||||
async onPrepare(config, capabilities) {
|
||||
// TODO: something critical for setup before all workers launch
|
||||
|
||||
throw new SevereServiceError('Something went wrong.')
|
||||
}
|
||||
|
||||
// custom service methods ...
|
||||
}
|
||||
```
|
||||
|
||||
## Import Service from Module
|
||||
|
||||
The only thing to do now in order to use this service is to assign it to the `services` property.
|
||||
|
||||
Modify your `wdio.conf.js` file to look like this:
|
||||
|
||||
```js
|
||||
import CustomService from './service/my.custom.service'
|
||||
|
||||
export const config = {
|
||||
// ...
|
||||
services: [
|
||||
/**
|
||||
* use imported service class
|
||||
*/
|
||||
[CustomService, {
|
||||
someOption: true
|
||||
}],
|
||||
/**
|
||||
* use absolute path to service
|
||||
*/
|
||||
['/path/to/service.js', {
|
||||
someOption: true
|
||||
}]
|
||||
],
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
## Publish Service on NPM
|
||||
|
||||
To make services easier to consume and discover by the WebdriverIO community, please follow these recommendations:
|
||||
|
||||
* Services should use this naming convention: `wdio-*-service`
|
||||
* Use NPM keywords: `wdio-plugin`, `wdio-service`
|
||||
* The `main` entry should `export` an instance of the service
|
||||
* Example services: [`@wdio/sauce-service`](https://github.com/webdriverio/webdriverio/tree/main/packages/wdio-sauce-service)
|
||||
|
||||
Following the recommended naming pattern allows services to be added by name:
|
||||
|
||||
```js
|
||||
// Add wdio-custom-service
|
||||
export const config = {
|
||||
// ...
|
||||
services: ['custom'],
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
### Add Published Service to WDIO CLI and Docs
|
||||
|
||||
We really appreciate every new plugin that could help other people run better tests! If you have created such a plugin, please consider adding it to our CLI and docs to make it easier to be found.
|
||||
|
||||
Please raise a pull request with the following changes:
|
||||
|
||||
- add your service to the list of [supported services](https://github.com/webdriverio/webdriverio/blob/main/packages/wdio-cli/src/constants.ts#L92-L128)) in the CLI module
|
||||
- enhance the [service list](https://github.com/webdriverio/webdriverio/blob/main/scripts/docs-generation/3rd-party/services.json) for adding your docs to the official Webdriver.io page
|
||||
|
|
@ -1,159 +0,0 @@
|
|||
---
|
||||
id: debugging
|
||||
title: Debugging
|
||||
---
|
||||
|
||||
Debugging is significantly more difficult when several processes spawn dozens of tests in multiple browsers.
|
||||
|
||||
<iframe width="560" height="315" src="https://www.youtube.com/embed/_bw_VWn5IzU" frameborder="0" allowFullScreen></iframe>
|
||||
|
||||
For starters, it is extremely helpful to limit parallelism by setting `maxInstances` to `1`, and targeting only those specs and browsers that need to be debugged.
|
||||
|
||||
In `wdio.conf`:
|
||||
|
||||
```js
|
||||
export const config = {
|
||||
// ...
|
||||
maxInstances: 1,
|
||||
specs: [
|
||||
'**/myspec.spec.js'
|
||||
],
|
||||
capabilities: [{
|
||||
browserName: 'firefox'
|
||||
}],
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
## The Debug Command
|
||||
|
||||
In many cases, you can use [`browser.debug()`](/docs/api/browser/debug) to pause your test and inspect the browser.
|
||||
|
||||
Your command line interface will also switch into REPL mode. This mode allows you to fiddle around with commands and elements on the page. In REPL mode, you can access the `browser` object—or `$` and `$$` functions—like you can in your tests.
|
||||
|
||||
When using `browser.debug()`, you will likely need to increase the timeout of the test runner to prevent the test runner from failing the test for taking to long. For example:
|
||||
|
||||
In `wdio.conf`:
|
||||
|
||||
```js
|
||||
jasmineOpts: {
|
||||
defaultTimeoutInterval: (24 * 60 * 60 * 1000)
|
||||
}
|
||||
```
|
||||
|
||||
See [timeouts](timeouts) for more information on how to do that using other frameworks.
|
||||
|
||||
To proceed with the tests after debugging, in the shell use `^C` shortcut or the `.exit` command.
|
||||
## Dynamic configuration
|
||||
|
||||
Note that `wdio.conf.js` can contain Javascript. Since you probably do not want to permanently change your timeout value to 1 day, it can be often helpful to change these settings from the command line using an environment variable.
|
||||
|
||||
Using this technique, you can dynamically change the configuration:
|
||||
|
||||
```js
|
||||
const debug = process.env.DEBUG
|
||||
const defaultCapabilities = ...
|
||||
const defaultTimeoutInterval = ...
|
||||
const defaultSpecs = ...
|
||||
|
||||
export const config = {
|
||||
// ...
|
||||
maxInstances: debug ? 1 : 100,
|
||||
capabilities: debug ? [{ browserName: 'chrome' }] : defaultCapabilities,
|
||||
execArgv: debug ? ['--inspect'] : [],
|
||||
jasmineOpts: {
|
||||
defaultTimeoutInterval: debug ? (24 * 60 * 60 * 1000) : defaultTimeoutInterval
|
||||
}
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
You can then prefix the `wdio` command with the `debug` flag:
|
||||
|
||||
```
|
||||
$ DEBUG=true npx wdio wdio.conf.js --spec ./tests/e2e/myspec.test.js
|
||||
```
|
||||
|
||||
...and debug your spec file with the DevTools!
|
||||
|
||||
## Debugging with Visual Studio Code (VSCode)
|
||||
|
||||
If you want to debug your tests with breakpoints in latest VSCode, you have two options for starting the debugger of which option 1 is the easiest method:
|
||||
1. automatically attaching the debugger
|
||||
2. attaching the debugger using a configuration file
|
||||
|
||||
### VSCode Toggle Auto Attach
|
||||
|
||||
You can automatically attach the debugger by following these steps in VSCode:
|
||||
- Press CMD + Shift + P (Linux and Macos) or CTRL + Shift + P (Windows)
|
||||
- Type "attach" into the input field
|
||||
- Select "Debug: Toggle Auto Attach"
|
||||
- Select "Only With Flag"
|
||||
|
||||
That's it! Now when you run your tests (remember you will need the --inspect flag set in your config as shown earlier) it will automatically start the debugger and stop on the first breakpoint that it reaches.
|
||||
|
||||
### VSCode Configuration file
|
||||
|
||||
It's possible to run all or selected spec file(s). Debug configuration(s) have to be added to `.vscode/launch.json`, to debug selected spec add the following config:
|
||||
```
|
||||
{
|
||||
"name": "run select spec",
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"args": ["wdio.conf.js", "--spec", "${file}"],
|
||||
"cwd": "${workspaceFolder}",
|
||||
"autoAttachChildProcesses": true,
|
||||
"program": "${workspaceRoot}/node_modules/@wdio/cli/bin/wdio.js",
|
||||
"console": "integratedTerminal",
|
||||
"skipFiles": [
|
||||
"${workspaceFolder}/node_modules/**/*.js",
|
||||
"${workspaceFolder}/lib/**/*.js",
|
||||
"<node_internals>/**/*.js"
|
||||
]
|
||||
},
|
||||
```
|
||||
|
||||
To run all spec files remove `"--spec", "${file}"` from `"args"`
|
||||
|
||||
Example: [.vscode/launch.json](https://github.com/mgrybyk/webdriverio-devtools/blob/master/.vscode/launch.json)
|
||||
|
||||
Additional info: https://code.visualstudio.com/docs/nodejs/nodejs-debugging
|
||||
|
||||
## Dynamic Repl with Atom
|
||||
|
||||
If you are an [Atom](https://atom.io/) hacker you can try [`wdio-repl`](https://github.com/kurtharriger/wdio-repl) by [@kurtharriger](https://github.com/kurtharriger) which is a dynamic repl that allows you to execute single code lines in Atom. Watch [this](https://www.youtube.com/watch?v=kdM05ChhLQE) YouTube video to see a demo.
|
||||
|
||||
## Debugging with WebStorm / Intellij
|
||||
You can create a node.js debug configuration like this:  Watch this [YouTube Video](https://www.youtube.com/watch?v=Qcqnmle6Wu8) for more information about how to make a configuration.
|
||||
|
||||
## Debugging flaky tests
|
||||
|
||||
Flaky tests can be really hard to debug so here are some tips how you can try and get that flaky result you got in your CI, reproduced locally.
|
||||
|
||||
### Network
|
||||
To debug network related flakiness use the [throttleNetwork](https://webdriver.io/docs/api/browser/throttleNetwork) command.
|
||||
```js
|
||||
await browser.throttleNetwork('Regular3G')
|
||||
```
|
||||
|
||||
### Rendering speed
|
||||
To debug device speed related flakiness use the [throttleCPU](https://webdriver.io/docs/api/browser/throttleCPU) command. This will cause your pages to render slower which can be caused by many things like running multiple processes in your CI which could be slowing down your tests.
|
||||
```js
|
||||
await browser.throttleCPU(4)
|
||||
```
|
||||
|
||||
### Test execution speed
|
||||
|
||||
If your tests do not seem to be affected it is possible that WebdriverIO is faster than the update from the frontend framework / browser. This happens when using synchronous assertions since WebdriverIO has no chance to retry these assertions anymore. Some examples of code that can break because of this:
|
||||
```js
|
||||
expect(elementList.length).toEqual(7) // list might not be populated at the time of the assertion
|
||||
expect(await elem.getText()).toEqual('this button was clicked 3 times') // text might not be updated yet at the time of assertion resulting in an error ("this button was clicked 2 times" does not match the expected "this button was clicked 3 times")
|
||||
expect(await elem.isDisplayed()).toBe(true) // might not be displayed yet
|
||||
```
|
||||
To resolve this problem, asynchronous assertions should be used instead. The above examples would looks like this:
|
||||
```js
|
||||
await expect(elementList).toBeElementsArrayOfSize(7)
|
||||
await expect(elem).toHaveText('this button was clicked 3 times')
|
||||
await expect(elem).toBeDisplayed()
|
||||
```
|
||||
Using these assertions, WebdriverIO will automatically wait until the condition matches. When asserting text this means that the element needs to exist and the text needs to be equal to the expected value. We talk more about this in our [Best Practices Guide](https://webdriver.io/docs/bestpractices#use-the-built-in-assertions).
|
||||
|
|
@ -1,64 +0,0 @@
|
|||
---
|
||||
id: docker
|
||||
title: Docker
|
||||
---
|
||||
|
||||
Docker is a powerful containerization technology that allows to encapsulate your test suite into a container that behaves the same on every system. This can avoid flakiness due to different browser or platform versions. In order to run your tests within a container, create a `Dockerfile` in your project directory, e.g.:
|
||||
|
||||
```Dockerfile
|
||||
FROM selenium/standalone-chrome:134.0-20250323 # Change the browser and version according to your needs
|
||||
WORKDIR /app
|
||||
ADD . /app
|
||||
|
||||
RUN npm install
|
||||
|
||||
CMD npx wdio
|
||||
```
|
||||
|
||||
Make sure you don't include your `node_modules` in your Docker image and have these installed when building the image. For that add a `.dockerignore` file with the following content:
|
||||
|
||||
```
|
||||
node_modules
|
||||
```
|
||||
|
||||
:::info
|
||||
We are using a Docker image here that comes with Selenium and Google Chrome pre-installed. There are various of images available with different browser setups and browser versions. Check out the images maintained by the Selenium project [on Docker Hub](https://hub.docker.com/u/selenium).
|
||||
:::
|
||||
|
||||
As we can only run Google Chrome in headless mode in our Docker container we have to modify our `wdio.conf.js` to ensure we do that:
|
||||
|
||||
```js title="wdio.conf.js"
|
||||
export const config = {
|
||||
// ...
|
||||
capabilities: [{
|
||||
maxInstances: 1,
|
||||
browserName: 'chrome',
|
||||
'goog:chromeOptions': {
|
||||
args: [
|
||||
'--no-sandbox',
|
||||
'--disable-infobars',
|
||||
'--headless',
|
||||
'--disable-gpu',
|
||||
'--window-size=1440,735'
|
||||
],
|
||||
}
|
||||
}],
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
As mentioned in [Automation Protocols](/docs/automationProtocols) you can run WebdriverIO using the WebDriver protocol or WebDriver BiDi protocol. Make sure that the Chrome version installed on your image matches the [Chromedriver](https://www.npmjs.com/package/chromedriver) version you have defined in your `package.json`.
|
||||
|
||||
To build the Docker container you can run:
|
||||
|
||||
```sh
|
||||
docker build -t mytest -f Dockerfile .
|
||||
```
|
||||
|
||||
Then to run the tests, execute:
|
||||
|
||||
```sh
|
||||
docker run -it mytest
|
||||
```
|
||||
|
||||
For more information on how to configure the Docker image, check out the [Docker docs](https://docs.docker.com/).
|
||||
|
|
@ -1,199 +0,0 @@
|
|||
---
|
||||
id: driverbinaries
|
||||
title: Driver Binaries
|
||||
---
|
||||
|
||||
To run automation based on the WebDriver protocol you need to have browser drivers set up that translate the automation commands and are able to execute them in the browser.
|
||||
|
||||
## Automated setup
|
||||
|
||||
With WebdriverIO `v8.14` and above there is no need to manually download and setup any browser drivers anymore as this is handled by WebdriverIO. All you have to do is specify the browser you want to test and WebdriverIO will do the rest.
|
||||
|
||||
### Customizing the level of automation
|
||||
|
||||
WebdriverIO's have three levels of automation:
|
||||
|
||||
**1. Download and install the browser using [@puppeteer/browsers](https://www.npmjs.com/package/@puppeteer/browsers).**
|
||||
|
||||
If you specify a `browserName`/`browserVersion` combination in the [capabilities](configuration#capabilities-1) configuration, WebdriverIO will download and install the requested combination, regardless of whether there's an existing installation on the machine. If you omit `browserVersion`, WebdriverIO will first try to locate and use an existing installation with [locate-app](https://www.npmjs.com/package/locate-app), otherwise it will download and install the current stable browser release. For more details on `browserVersion`, see [here](capabilities#automate-different-browser-channels).
|
||||
|
||||
:::caution
|
||||
|
||||
Automated browser setup does not support Microsoft Edge. Currently, only Chrome, Chromium and Firefox are supported.
|
||||
|
||||
:::
|
||||
|
||||
If you have a browser installation on a location that cannot be auto-detected by WebdriverIO, you can specify the browser binary which will disable the automated download and installation.
|
||||
|
||||
```ts
|
||||
{
|
||||
capabilities: [
|
||||
{
|
||||
browserName: 'chrome', // or 'firefox' or 'chromium'
|
||||
'goog:chromeOptions': { // or 'moz:firefoxOptions' or 'wdio:chromedriverOptions'
|
||||
binary: '/path/to/chrome'
|
||||
},
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**2. Download and install the driver using [Chromedriver](https://www.npmjs.com/package/chromedriver), [Edgedriver](https://www.npmjs.com/package/edgedriver) or [Geckodriver](https://www.npmjs.com/package/geckodriver).**
|
||||
|
||||
WebdriverIO will always do this, unless driver [binary](capabilities#binary) is specified in the configuration:
|
||||
|
||||
```ts
|
||||
{
|
||||
capabilities: [
|
||||
{
|
||||
browserName: 'chrome', // or 'firefox', 'msedge', 'safari', 'chromium'
|
||||
'wdio:chromedriverOptions': { // or 'wdio:geckodriverOptions', 'wdio:edgedriverOptions'
|
||||
binary: '/path/to/chromedriver' // or 'geckodriver', 'msedgedriver'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
:::info
|
||||
|
||||
WebdriverIO won't automatically download Safari driver as it is already installed on macOS.
|
||||
|
||||
:::
|
||||
|
||||
:::caution
|
||||
|
||||
Avoid specifying a `binary` for the browser and omitting the corresponding driver `binary` or vice-versa. If only one of the `binary` values is specified, WebdriverIO will try to use or download a browser/driver compatible with it. However, in some scenarios it may result in an incompatible combination. Therefore, it's recommended that you always specify both to avoid any problems caused by version incompatibilities.
|
||||
|
||||
:::
|
||||
|
||||
**3. Start/stop the driver.**
|
||||
|
||||
By default, WebdriverIO will automatically start and stop the driver using an arbitrary unused port. Specifying any of the following configuration will disable this feature which means you'll need to manually start and stop the driver:
|
||||
|
||||
- Any value for [port](configuration#port).
|
||||
- Any value different from the default for [protocol](configuration#protocol), [hostname](configuration#hostname), [path](configuration#path).
|
||||
- Any value for both [user](configuration#user) and [key](configuration#key).
|
||||
|
||||
## Manual setup
|
||||
|
||||
The following describes how you can still set up each driver individually. You can find a list with all drivers in the [`awesome-selenium`](https://github.com/christian-bromann/awesome-selenium#driver) README.
|
||||
|
||||
:::tip
|
||||
|
||||
If you are looking to set up mobile and other UI platforms, have a look into our [Appium Setup](appium) guide.
|
||||
|
||||
:::
|
||||
|
||||
### Chromedriver
|
||||
|
||||
To automate Chrome you can download Chromedriver directly on the [project website](http://chromedriver.chromium.org/downloads) or through the NPM package:
|
||||
|
||||
```bash npm2yarn
|
||||
npm install -g chromedriver
|
||||
```
|
||||
|
||||
You can then start it via:
|
||||
|
||||
```sh
|
||||
chromedriver --port=4444 --verbose
|
||||
```
|
||||
|
||||
### Geckodriver
|
||||
|
||||
To automate Firefox download the latest version of `geckodriver` for your environment and unpack it in your project directory:
|
||||
|
||||
<Tabs
|
||||
defaultValue="npm"
|
||||
values={[
|
||||
{label: 'NPM', value: 'npm'},
|
||||
{label: 'Curl', value: 'curl'},
|
||||
{label: 'Brew', value: 'brew'},
|
||||
{label: 'Windows (64 bit / Chocolatey)', value: 'chocolatey'},
|
||||
{label: 'Windows (64 bit / Powershell) DevTools', value: 'powershell'},
|
||||
]
|
||||
}>
|
||||
<TabItem value="npm">
|
||||
|
||||
```bash npm2yarn
|
||||
npm install geckodriver
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="curl">
|
||||
|
||||
Linux:
|
||||
|
||||
```sh
|
||||
curl -L https://github.com/mozilla/geckodriver/releases/download/v0.24.0/geckodriver-v0.24.0-linux64.tar.gz | tar xz
|
||||
```
|
||||
|
||||
MacOS (64 bit):
|
||||
|
||||
```sh
|
||||
curl -L https://github.com/mozilla/geckodriver/releases/download/v0.24.0/geckodriver-v0.24.0-macos.tar.gz | tar xz
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="brew">
|
||||
|
||||
```sh
|
||||
brew install geckodriver
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="chocolatey">
|
||||
|
||||
```sh
|
||||
choco install selenium-gecko-driver
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="powershell">
|
||||
|
||||
```sh
|
||||
# Run as privileged session. Right-click and set 'Run as Administrator'
|
||||
# Use geckodriver-v0.24.0-win32.zip for 32 bit Windows
|
||||
$url = "https://github.com/mozilla/geckodriver/releases/download/v0.24.0/geckodriver-v0.24.0-win64.zip"
|
||||
$output = "geckodriver.zip" # will drop into current directory unless defined otherwise
|
||||
$unzipped_file = "geckodriver" # will unzip to this folder name
|
||||
|
||||
# By default, Powershell uses TLS 1.0 the site security requires TLS 1.2
|
||||
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
|
||||
|
||||
# Downloads Geckodriver
|
||||
Invoke-WebRequest -Uri $url -OutFile $output
|
||||
|
||||
# Unzip Geckodriver
|
||||
Expand-Archive $output -DestinationPath $unzipped_file
|
||||
cd $unzipped_file
|
||||
|
||||
# Globally Set Geckodriver to PATH
|
||||
[System.Environment]::SetEnvironmentVariable("PATH", "$Env:Path;$pwd\geckodriver.exe", [System.EnvironmentVariableTarget]::Machine)
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
**Note:** Other `geckodriver` releases are available [here](https://github.com/mozilla/geckodriver/releases). After download you can start the driver via:
|
||||
|
||||
```sh
|
||||
/path/to/binary/geckodriver --port 4444
|
||||
```
|
||||
|
||||
### Edgedriver
|
||||
|
||||
You can download the driver for Microsoft Edge on the [project website](https://developer.microsoft.com/en-us/microsoft-edge/tools/webdriver/) or as NPM package via:
|
||||
|
||||
```sh
|
||||
npm install -g edgedriver
|
||||
edgedriver --version # prints: Microsoft Edge WebDriver 115.0.1901.203 (a5a2b1779bcfe71f081bc9104cca968d420a89ac)
|
||||
```
|
||||
|
||||
### Safaridriver
|
||||
|
||||
Safaridriver comes pre-installed on your MacOS and can be started directly via:
|
||||
|
||||
```sh
|
||||
safaridriver -p 4444
|
||||
```
|
||||
|
|
@ -1,165 +0,0 @@
|
|||
---
|
||||
id: emulation
|
||||
title: Emulation
|
||||
---
|
||||
|
||||
With WebdriverIO you can emulate Web APIs using the [`emulate`](/docs/api/browser/emulate) command to return custom values that help you emulate certain browser behaviors. Note that this requires your application to explicitly use these APIs.
|
||||
|
||||
<LiteYouTubeEmbed
|
||||
id="2bQXzIB_97M"
|
||||
title="WebdriverIO Tutorials: The Emulate Command - Emulate Web APIs at Runtime with WebdriverIO"
|
||||
/>
|
||||
|
||||
:::info
|
||||
|
||||
This feature requires WebDriver Bidi support for the browser. While recent versions of Chrome, Edge and Firefox have such support, Safari **does not**. For updates follow [wpt.fyi](https://wpt.fyi/results/webdriver/tests/bidi/script/add_preload_script/add_preload_script.py?label=experimental\&label=master\&aligned). Furthermore if you use a cloud vendor for spawning browsers, make sure your vendor also supports WebDriver Bidi.
|
||||
|
||||
To enable WebDriver Bidi for your test, make sure to have `webSocketUrl: true` set in your capabilities.
|
||||
|
||||
:::
|
||||
|
||||
## Geolocation
|
||||
|
||||
Change the browser geolocation to a specific area, e.g.:
|
||||
|
||||
```ts
|
||||
await browser.emulate('geolocation', {
|
||||
latitude: 52.52,
|
||||
longitude: 13.39,
|
||||
accuracy: 100
|
||||
})
|
||||
await browser.url('https://www.google.com/maps')
|
||||
await browser.$('aria/Show Your Location').click()
|
||||
await browser.pause(5000)
|
||||
console.log(await browser.getUrl()) // outputs: "https://www.google.com/maps/@52.52,13.39,16z?entry=ttu"
|
||||
```
|
||||
|
||||
This will monkey patch how [`navigator.geolocation.getCurrentPosition`](https://developer.mozilla.org/en-US/docs/Web/API/Geolocation/getCurrentPosition) works and returns the location provided by you.
|
||||
|
||||
## Color Scheme
|
||||
|
||||
Change the default color scheme setup of the browser via:
|
||||
|
||||
```ts
|
||||
await browser.emulate('colorScheme', 'light')
|
||||
await browser.url('https://webdriver.io')
|
||||
const backgroundColor = await browser.$('nav').getCSSProperty('background-color')
|
||||
console.log(backgroundColor.parsed.hex) // outputs: "#efefef"
|
||||
|
||||
await browser.emulate('colorScheme', 'dark')
|
||||
await browser.url('https://webdriver.io')
|
||||
const backgroundColor = await browser.$('nav').getCSSProperty('background-color')
|
||||
console.log(backgroundColor.parsed.hex) // outputs: "#000000"
|
||||
```
|
||||
|
||||
This will monkey patch how [`window.matchMedia`](https://developer.mozilla.org/en-US/docs/Web/API/Window/matchMedia) behaves when you query the color scheme via `(prefers-color-scheme: dark)`.
|
||||
|
||||
## User Agent
|
||||
|
||||
Change the user agent of the browser to a different string via:
|
||||
|
||||
```ts
|
||||
await browser.emulate('userAgent', 'Chrome/1.2.3.4 Safari/537.36')
|
||||
```
|
||||
|
||||
This will change the value of [`navigator.userAgent`](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/userAgent). Note that browser vendors progressively deprecating the User Agent.
|
||||
|
||||
## onLine Property
|
||||
|
||||
Change the online status of the browser via:
|
||||
|
||||
```ts
|
||||
await browser.emulate('onLine', false)
|
||||
```
|
||||
|
||||
This will **not** turn off network traffic between the browser and the internet and only changes the return value of [`navigator.onLine`](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/onLine). If you are interested modifying network capabilities of the browser, look into the [`throttleNetwork`](/docs/api/browser/throttleNetwork) command.
|
||||
|
||||
## Clock
|
||||
|
||||
You can modify the browser system clock using the [`emulate`](/docs/emulation) command. It overrides native global functions related to time allowing them to be controlled synchronously via `clock.tick()` or the yielded clock object. This includes controlling:
|
||||
|
||||
- `setTimeout`
|
||||
- `clearTimeout`
|
||||
- `setInterval`
|
||||
- `clearInterval`
|
||||
- `Date Objects`
|
||||
|
||||
The clock starts at the unix epoch (timestamp of 0). This means that when you instantiate new Date in your application, it will have a time of January 1st, 1970 if you don't pass any other options to the `emulate` command.
|
||||
|
||||
##### Example
|
||||
|
||||
When calling `browser.emulate('clock', { ... })` it will immediately overwrite the global functions for the current page as well as all following pages, e.g.:
|
||||
|
||||
```ts
|
||||
const clock = await browser.emulate('clock', { now: new Date(1989, 7, 4) })
|
||||
|
||||
console.log(await browser.execute(() => (new Date()).toString()))
|
||||
// returns "Fri Aug 04 1989 00:00:00 GMT-0700 (Pacific Daylight Time)"
|
||||
|
||||
await browser.url('https://webdriverio')
|
||||
console.log(await browser.execute(() => (new Date()).toString()))
|
||||
// returns "Fri Aug 04 1989 00:00:00 GMT-0700 (Pacific Daylight Time)"
|
||||
|
||||
await clock.restore()
|
||||
|
||||
console.log(await browser.execute(() => (new Date()).toString()))
|
||||
// returns "Thu Aug 01 2024 17:59:59 GMT-0700 (Pacific Daylight Time)"
|
||||
|
||||
await browser.url('https://guinea-pig.webdriver.io/pointer.html')
|
||||
console.log(await browser.execute(() => (new Date()).toString()))
|
||||
// returns "Thu Aug 01 2024 17:59:59 GMT-0700 (Pacific Daylight Time)"
|
||||
```
|
||||
|
||||
You can modify the system time by calling [`setSystemTime`](/docs/api/clock/setSystemTime) or [`tick`](/docs/api/clock/tick).
|
||||
|
||||
The `FakeTimerInstallOpts` object can have the following properties:
|
||||
|
||||
```ts
|
||||
interface FakeTimerInstallOpts {
|
||||
// Installs fake timers with the specified unix epoch
|
||||
// @default: 0
|
||||
now?: number | Date | undefined;
|
||||
|
||||
// An array with names of global methods and APIs to fake. By default, WebdriverIO
|
||||
// does not replace `nextTick()` and `queueMicrotask()`. For instance,
|
||||
// `browser.emulate('clock', { toFake: ['setTimeout', 'nextTick'] })` will fake only
|
||||
// `setTimeout()` and `nextTick()`
|
||||
toFake?: FakeMethod[] | undefined;
|
||||
|
||||
// The maximum number of timers that will be run when calling runAll() (default: 1000)
|
||||
loopLimit?: number | undefined;
|
||||
|
||||
// Tells WebdriverIO to increment mocked time automatically based on the real system
|
||||
// time shift (e.g. the mocked time will be incremented by 20ms for every 20ms change
|
||||
// in the real system time)
|
||||
// @default false
|
||||
shouldAdvanceTime?: boolean | undefined;
|
||||
|
||||
// Relevant only when using with shouldAdvanceTime: true. increment mocked time by
|
||||
// advanceTimeDelta ms every advanceTimeDelta ms change in the real system time
|
||||
// @default: 20
|
||||
advanceTimeDelta?: number | undefined;
|
||||
|
||||
// Tells FakeTimers to clear 'native' (i.e. not fake) timers by delegating to their
|
||||
// respective handlers. These are not cleared by default, leading to potentially
|
||||
// unexpected behavior if timers existed prior to installing FakeTimers.
|
||||
// @default: false
|
||||
shouldClearNativeTimers?: boolean | undefined;
|
||||
}
|
||||
```
|
||||
|
||||
## Device
|
||||
|
||||
The `emulate` command also supports emulating a certain mobile or desktop device by changing the viewport, device scale factor and the user agent. This should, by no means, be used for mobile testing as desktop browser engines differ from mobile ones. This should only be used if your application offers a specific behavior for smaller viewport sizes.
|
||||
|
||||
For example, to switch the user agent and viewport to an iPhone 15, just run:
|
||||
|
||||
```ts
|
||||
const restore = await browser.emulate('device', 'iPhone 15')
|
||||
// test your application ...
|
||||
|
||||
// reset to original viewport and user agent
|
||||
await restore()
|
||||
```
|
||||
|
||||
WebdriverIO maintains a fixed list of [all defined devices](https://github.com/webdriverio/webdriverio/blob/main/packages/webdriverio/src/deviceDescriptorsSource.ts).
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
---
|
||||
id: enterprise
|
||||
title: WebdriverIO for Enterprise
|
||||
---
|
||||
|
||||
## Available as part of the Tidelift Subscription
|
||||
|
||||
Tidelift is working with the maintainers of WebdriverIO and thousands of other open source projects to deliver commercial support and maintenance for the open source dependencies you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact dependencies you use.
|
||||
|
||||
#### Enterprise-ready open source software—managed for you
|
||||
The Tidelift Subscription is a managed open source subscription for application dependencies covering millions of open source projects across JavaScript, Python, Java, PHP, Ruby, .NET, and more.
|
||||
|
||||
#### Your subscription includes:
|
||||
|
||||
##### Security updates
|
||||
Tidelift’s security response team coordinates patches for new breaking security vulnerabilities and alerts immediately through a private channel, so your software supply chain is always secure.
|
||||
|
||||
##### Licensing verification and indemnification
|
||||
Tidelift verifies license information to enable easy policy enforcement and adds intellectual property indemnification to cover creators and users in case something goes wrong. You always have a 100% up-to-date bill of materials for your dependencies to share with your legal team, customers, or partners.
|
||||
|
||||
##### Maintenance and code improvement
|
||||
Tidelift ensures the software you rely on keeps working as long as you need it to work. Your managed dependencies are actively maintained and we recruit additional maintainers where required.
|
||||
|
||||
##### Package selection and version guidance
|
||||
We help you choose the best open source packages from the start—and then guide you through updates to stay on the best releases as new issues arise.
|
||||
|
||||
##### Roadmap input
|
||||
Take a seat at the table with the creators behind the software you use. Tidelift’s participating maintainers earn more income as their software is used by more subscribers, so they’re interested in knowing what you need.
|
||||
|
||||
##### Tooling and cloud integration
|
||||
Tidelift works with GitHub, GitLab, BitBucket, and more. We support every cloud platform (and other deployment targets, too).
|
||||
|
||||
The end result? All of the capabilities you expect from commercial-grade software, for the full breadth of open source you use. That means less time grappling with esoteric open source trivia, and more time building your own applications—and your business.
|
||||
|
||||
<div class="learnmore">
|
||||
<a class="button" href="https://tidelift.com/subscription/pkg/npm-webdriverio?utm_source=npm-webdriverio&utm_medium=referral&utm_campaign=enterprise" target="_self">Learn More</a>
|
||||
<a class="button" href="https://tidelift.com/subscription/request-a-demo?utm_source=npm-webdriverio&utm_medium=referral&utm_campaign=enterprise" target="_self">Request a demo</a>
|
||||
</div>
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
---
|
||||
id: flowchartsoverview
|
||||
title: Flowcharts
|
||||
---
|
||||
|
||||
The flowcharts provide a high level overview of the WebdriverIO ecosystem and how the different packages interact with each other.
|
||||
|
||||
[WDIO Commands](flowcharts/WDIOCommands.md) - Explains the wdio config, install and repl command workflows.
|
||||
|
||||
[Create local worker process](flowcharts/CreateLocalWorkerProcess.md) - Explains the interaction between the @wdio/cli, @wdio/local-runner and @wdio/runner packages and how a worker process is created.
|
||||
|
||||
[Test execution](flowcharts/TestExecution.md) - Overview of how the tests are run in the local runner worker process.
|
||||
|
||||
[High level overview](flowcharts/HighLevelOverview.md) - Flow chart provides a high level overview of how the WebdriverIO ecosystem interacts with the core packages.
|
||||
|
|
@ -1,679 +0,0 @@
|
|||
---
|
||||
id: frameworks
|
||||
title: Frameworks
|
||||
---
|
||||
|
||||
WebdriverIO Runner has built-in support for [Mocha](http://mochajs.org/), [Jasmine](http://jasmine.github.io/), and [Cucumber.js](https://cucumber.io/). You can also integrate it with 3rd-party open-source frameworks, such as [Serenity/JS](#using-serenityjs).
|
||||
|
||||
:::tip Integrating WebdriverIO with test frameworks
|
||||
To integrate WebdriverIO with a test framework, you need an adapter package available on NPM. Note that the adapter package must be installed in the same location where WebdriverIO is installed. So, if you installed WebdriverIO globally, be sure to install the adapter package globally, too.
|
||||
:::
|
||||
|
||||
Integrating WebdriverIO with a test framework lets you access the WebDriver instance using the global `browser` variable in your spec files or step definitions. Note that WebdriverIO will also take care of instantiating and ending the Selenium session, so you don't have to do it yourself.
|
||||
|
||||
## Using Mocha
|
||||
|
||||
First, install the adapter package from NPM:
|
||||
|
||||
```bash npm2yarn
|
||||
npm install @wdio/mocha-framework --save-dev
|
||||
```
|
||||
|
||||
By default WebdriverIO provides an [assertion library](assertion) that is built-in which you can start right away:
|
||||
|
||||
```js
|
||||
describe('my awesome website', () => {
|
||||
it('should do some assertions', async () => {
|
||||
await browser.url('https://webdriver.io')
|
||||
await expect(browser).toHaveTitle('WebdriverIO · Next-gen browser and mobile automation test framework for Node.js | WebdriverIO')
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
WebdriverIO supports Mocha's `BDD` (default), `TDD`, and `QUnit` [interfaces](https://mochajs.org/#interfaces).
|
||||
|
||||
If you like to write your specs in TDD style, set the `ui` property in your `mochaOpts` config to `tdd`. Now your test files should be written like this:
|
||||
|
||||
```js
|
||||
suite('my awesome website', () => {
|
||||
test('should do some assertions', async () => {
|
||||
await browser.url('https://webdriver.io')
|
||||
await expect(browser).toHaveTitle('WebdriverIO · Next-gen browser and mobile automation test framework for Node.js | WebdriverIO')
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
If you want to define other Mocha-specific settings, you can do it with the `mochaOpts` key in your configuration file. A list of all options can be found on the [Mocha project website](https://mochajs.org/api/mocha).
|
||||
|
||||
__Note:__ WebdriverIO does not support the deprecated usage of `done` callbacks in Mocha:
|
||||
|
||||
```js
|
||||
it('should test something', (done) => {
|
||||
done() // throws "done is not a function"
|
||||
})
|
||||
```
|
||||
|
||||
### Mocha Options
|
||||
|
||||
The following options can be applied in your `wdio.conf.js` to configure your Mocha environment. __Note:__ not all options are supported, e.g. applying the `parallel` option will cause an error as the WDIO testrunner has its own way to run tests in parallel. You can pass these framework options as arguments, e.g.:
|
||||
|
||||
```sh
|
||||
wdio run wdio.conf.ts --mochaOpts.grep "my test" --mochaOpts.bail --no-mochaOpts.checkLeaks
|
||||
```
|
||||
|
||||
This will pass along the following Mocha options:
|
||||
|
||||
```ts
|
||||
{
|
||||
grep: ['my-test'],
|
||||
bail: true
|
||||
checkLeacks: false
|
||||
}
|
||||
```
|
||||
|
||||
The following Mocha options are supported:
|
||||
|
||||
#### require
|
||||
The `require` option is useful when you want to add or extend some basic functionality (WebdriverIO framework option).
|
||||
|
||||
Type: `string|string[]`<br /> Default: `[]`
|
||||
|
||||
#### compilers
|
||||
Use the given module(s) to compile files. Compilers will be included before requires (WebdriverIO framework option).
|
||||
|
||||
Type: `string[]`<br /> Default: `[]`
|
||||
|
||||
#### allowUncaught
|
||||
Propagate uncaught errors.
|
||||
|
||||
Type: `boolean`<br /> Default: `false`
|
||||
|
||||
#### bail
|
||||
Bail after first test failure.
|
||||
|
||||
Type: `boolean`<br /> Default: `false`
|
||||
|
||||
#### checkLeaks
|
||||
Check for global variable leaks.
|
||||
|
||||
Type: `boolean`<br /> Default: `false`
|
||||
|
||||
#### delay
|
||||
Delay root suite execution.
|
||||
|
||||
Type: `boolean`<br /> Default: `false`
|
||||
|
||||
#### fgrep
|
||||
Test filter given string.
|
||||
|
||||
Type: `string`<br /> Default: `null`
|
||||
|
||||
#### forbidOnly
|
||||
Tests marked `only` fail the suite.
|
||||
|
||||
Type: `boolean`<br /> Default: `false`
|
||||
|
||||
#### forbidPending
|
||||
Pending tests fail the suite.
|
||||
|
||||
Type: `boolean`<br /> Default: `false`
|
||||
|
||||
#### fullTrace
|
||||
Full stacktrace upon failure.
|
||||
|
||||
Type: `boolean`<br /> Default: `false`
|
||||
|
||||
#### global
|
||||
Variables expected in global scope.
|
||||
|
||||
Type: `string[]`<br /> Default: `[]`
|
||||
|
||||
#### grep
|
||||
Test filter given regular expression.
|
||||
|
||||
Type: `RegExp|string`<br /> Default: `null`
|
||||
|
||||
#### invert
|
||||
Invert test filter matches.
|
||||
|
||||
Type: `boolean`<br /> Default: `false`
|
||||
|
||||
#### retries
|
||||
Number of times to retry failed tests.
|
||||
|
||||
Type: `number`<br /> Default: `0`
|
||||
|
||||
#### timeout
|
||||
Timeout threshold value (in ms).
|
||||
|
||||
Type: `number`<br /> Default: `30000`
|
||||
|
||||
## Using Jasmine
|
||||
|
||||
First, install the adapter package from NPM:
|
||||
|
||||
```bash npm2yarn
|
||||
npm install @wdio/jasmine-framework --save-dev
|
||||
```
|
||||
|
||||
You can then configure your Jasmine environment by setting a `jasmineOpts` property in your config. A list of all options can be found on the [Jasmine project website](https://jasmine.github.io/api/3.5/Configuration.html).
|
||||
|
||||
### Jasmine Options
|
||||
|
||||
The following options can be applied in your `wdio.conf.js` to configure your Jasmine environment using the `jasmineOpts` property. For more information on these configuration options, check out the [Jasmine docs](https://jasmine.github.io/api/edge/Configuration). You can pass these framework options as arguments, e.g.:
|
||||
|
||||
```sh
|
||||
wdio run wdio.conf.ts --jasmineOpts.grep "my test" --jasmineOpts.failSpecWithNoExpectations --no-jasmineOpts.random
|
||||
```
|
||||
|
||||
This will pass along the following Mocha options:
|
||||
|
||||
```ts
|
||||
{
|
||||
grep: ['my-test'],
|
||||
bail: true
|
||||
checkLeacks: false
|
||||
}
|
||||
```
|
||||
|
||||
The following Jasmine options are supported:
|
||||
|
||||
#### defaultTimeoutInterval
|
||||
Default Timeout Interval for Jasmine operations.
|
||||
|
||||
Type: `number`<br /> Default: `60000`
|
||||
|
||||
#### helpers
|
||||
Array of filepaths (and globs) relative to spec_dir to include before jasmine specs.
|
||||
|
||||
Type: `string[]`<br /> Default: `[]`
|
||||
|
||||
#### requires
|
||||
The `requires` option is useful when you want to add or extend some basic functionality.
|
||||
|
||||
Type: `string[]`<br /> Default: `[]`
|
||||
|
||||
#### random
|
||||
Whether to randomize spec execution order.
|
||||
|
||||
Type: `boolean`<br /> Default: `true`
|
||||
|
||||
#### seed
|
||||
Seed to use as the basis of randomization. Null causes the seed to be determined randomly at the start of execution.
|
||||
|
||||
Type: `Function`<br /> Default: `null`
|
||||
|
||||
#### failSpecWithNoExpectations
|
||||
Whether to fail the spec if it ran no expectations. By default a spec that ran no expectations is reported as passed. Setting this to true will report such spec as a failure.
|
||||
|
||||
Type: `boolean`<br /> Default: `false`
|
||||
|
||||
#### oneFailurePerSpec
|
||||
Whether to cause specs to only have one expectation failure.
|
||||
|
||||
Type: `boolean`<br /> Default: `false`
|
||||
|
||||
#### specFilter
|
||||
Function to use to filter specs.
|
||||
|
||||
Type: `Function`<br /> Default: `(spec) => true`
|
||||
|
||||
#### grep
|
||||
Only run tests matching this string or regexp. (Only applicable if no custom `specFilter` function is set)
|
||||
|
||||
Type: `string|Regexp`<br /> Default: `null`
|
||||
|
||||
#### invertGrep
|
||||
If true it inverts the matching tests and only runs tests that don't match with the expression used in `grep`. (Only applicable if no custom `specFilter` function is set)
|
||||
|
||||
Type: `boolean`<br /> Default: `false`
|
||||
|
||||
## Using Cucumber
|
||||
|
||||
First, install the adapter package from NPM:
|
||||
|
||||
```bash npm2yarn
|
||||
npm install @wdio/cucumber-framework --save-dev
|
||||
```
|
||||
|
||||
If you want to use Cucumber, set the `framework` property to `cucumber` by adding `framework: 'cucumber'` to the [config file](configurationfile) .
|
||||
|
||||
Options for Cucumber can be given in the config file with `cucumberOpts`. Check out the whole list of options [here](https://github.com/webdriverio/webdriverio/tree/main/packages/wdio-cucumber-framework#cucumberopts-options).
|
||||
|
||||
To get up and running quickly with Cucumber, have a look on our [`cucumber-boilerplate`](https://github.com/webdriverio/cucumber-boilerplate) project that comes with all the step definitions you need to get stared, and you'll be writing feature files right away.
|
||||
|
||||
### Cucumber Options
|
||||
|
||||
The following options can be applied in your `wdio.conf.js` to configure your Cucumber environment using the `cucumberOpts` property:
|
||||
|
||||
:::tip Adjusting options through the command line
|
||||
The `cucumberOpts`, such as custom `tags` for filtering tests, can be specified through the command line. This is accomplished by using the `cucumberOpts.{optionName}="value"` format.
|
||||
|
||||
For example, if you want to run only the tests that are tagged with `@smoke`, you can use the following command:
|
||||
|
||||
```sh
|
||||
# When you only want to run tests that hold the tag "@smoke"
|
||||
npx wdio run ./wdio.conf.js --cucumberOpts.tags="@smoke"
|
||||
npx wdio run ./wdio.conf.js --cucumberOpts.name="some scenario name" --cucumberOpts.failFast
|
||||
```
|
||||
|
||||
This command sets the `tags` option in `cucumberOpts` to `@smoke`, ensuring that only tests with this tag are executed.
|
||||
|
||||
:::
|
||||
|
||||
#### backtrace
|
||||
Show full backtrace for errors.
|
||||
|
||||
Type: `Boolean`<br /> Default: `true`
|
||||
|
||||
#### requireModule
|
||||
Require modules prior to requiring any support files.
|
||||
|
||||
Type: `string[]`<br /> Default: `[]`<br /> Example:
|
||||
|
||||
```js
|
||||
cucumberOpts: {
|
||||
requireModule: ['@babel/register']
|
||||
// or
|
||||
requireModule: [
|
||||
[
|
||||
'@babel/register',
|
||||
{
|
||||
rootMode: 'upward',
|
||||
ignore: ['node_modules']
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
#### failFast
|
||||
Abort the run on first failure.
|
||||
|
||||
Type: `boolean`<br /> Default: `false`
|
||||
|
||||
#### name
|
||||
Only execute the scenarios with name matching the expression (repeatable).
|
||||
|
||||
Type: `RegExp[]`<br /> Default: `[]`
|
||||
|
||||
#### require
|
||||
Require files containing your step definitions before executing features. You can also specify a glob to your step definitions.
|
||||
|
||||
Type: `string[]`<br /> Default: `[]` Example:
|
||||
|
||||
```js
|
||||
cucumberOpts: {
|
||||
require: [path.join(__dirname, 'step-definitions', 'my-steps.js')]
|
||||
}
|
||||
```
|
||||
|
||||
#### import
|
||||
Paths to where your support code is, for ESM.
|
||||
|
||||
Type: `String[]`<br /> Default: `[]` Example:
|
||||
|
||||
```js
|
||||
cucumberOpts: {
|
||||
import: [path.join(__dirname, 'step-definitions', 'my-steps.js')]
|
||||
}
|
||||
```
|
||||
|
||||
#### strict
|
||||
Fail if there are any undefined or pending steps.
|
||||
|
||||
Type: `boolean`<br /> Default: `false`
|
||||
|
||||
#### tags
|
||||
Only execute the features or scenarios with tags matching the expression. Please see the [Cucumber documentation](https://docs.cucumber.io/cucumber/api/#tag-expressions) for more details.
|
||||
|
||||
Type: `String`<br /> Default: ``
|
||||
|
||||
#### timeout
|
||||
Timeout in milliseconds for step definitions.
|
||||
|
||||
Type: `Number`<br /> Default: `30000`
|
||||
|
||||
#### retry
|
||||
Specify the number of times to retry failing test cases.
|
||||
|
||||
Type: `Number`<br /> Default: `0`
|
||||
|
||||
#### retryTagFilter
|
||||
Only retries the features or scenarios with tags matching the expression (repeatable). This option requires '--retry' to be specified.
|
||||
|
||||
Type: `RegExp`
|
||||
|
||||
#### language
|
||||
Default language for your feature files
|
||||
|
||||
Type: `String`<br /> Default: `en`
|
||||
|
||||
#### order
|
||||
Run tests in defined / random order
|
||||
|
||||
Type: `String`<br /> Default: `defined`
|
||||
|
||||
#### format
|
||||
Name and output file path of formatter to use. WebdriverIO primarily supports only the [Formatters](https://github.com/cucumber/cucumber-js/blob/main/docs/formatters.md) that writes output to a file.
|
||||
|
||||
Type: `string[]`<br />
|
||||
|
||||
#### formatOptions
|
||||
Options to be provided to formatters
|
||||
|
||||
Type: `object`<br />
|
||||
|
||||
#### tagsInTitle
|
||||
Add cucumber tags to feature or scenario name
|
||||
|
||||
Type: `Boolean`<br /> Default: `false`
|
||||
|
||||
***Please note that this is a @wdio/cucumber-framework specific option and not recognized by cucumber-js itself***<br/>
|
||||
|
||||
#### ignoreUndefinedDefinitions
|
||||
Treat undefined definitions as warnings.
|
||||
|
||||
Type: `Boolean`<br /> Default: `false`
|
||||
|
||||
***Please note that this is a @wdio/cucumber-framework specific option and not recognized by cucumber-js itself***<br/>
|
||||
|
||||
#### failAmbiguousDefinitions
|
||||
Treat ambiguous definitions as errors.
|
||||
|
||||
Type: `Boolean`<br /> Default: `false`
|
||||
|
||||
***Please note that this is a @wdio/cucumber-framework specific option and not recognized by cucumber-js itself***<br/>
|
||||
|
||||
#### tagExpression
|
||||
Only execute the features or scenarios with tags matching the expression. Please see the [Cucumber documentation](https://docs.cucumber.io/cucumber/api/#tag-expressions) for more details.
|
||||
|
||||
Type: `String`<br /> Default: ``
|
||||
|
||||
***Please note that this option would be deprecated in future. Use [`tags`](#tags) config property instead***
|
||||
|
||||
#### profile
|
||||
Specify the profile to use.
|
||||
|
||||
Type: `string[]`<br /> Default: `[]`
|
||||
|
||||
***Kindly take note that only specific values (worldParameters, name, retryTagFilter) are supported within profiles, as `cucumberOpts` takes precedence. Additionally, when using a profile, make sure that the mentioned values are not declared within `cucumberOpts`.***
|
||||
|
||||
### Skipping tests in cucumber
|
||||
|
||||
Note that if you want to skip a test using regular cucumber test filtering capabilities available in `cucumberOpts`, you will do it for all the browsers and devices configured in the capabilities. In order to be able to skip scenarios only for specific capabilities combinations without having a session started if not necessary, webdriverio provides the following specific tag syntax for cucumber:
|
||||
|
||||
`@skip([condition])`
|
||||
|
||||
were condition is an optional combination of capabilities properties with their values that when **all** matched with cause the tagged scenario or feature to be skipped. Of course you can add several tags to scenarios and features to skip a tests under several different conditions.
|
||||
|
||||
You can also use the '@skip' annotation to skip tests without changing `tagExpression'. In this case the skipped tests will be displayed in the test report.
|
||||
|
||||
Here you have some examples of this syntax:
|
||||
- `@skip` or `@skip()`: will always skip the tagged item
|
||||
- `@skip(browserName="chrome")`: the test will not be executed against chrome browsers.
|
||||
- `@skip(browserName="firefox";platformName="linux")`: will skip the test in firefox over linux executions.
|
||||
- `@skip(browserName=["chrome","firefox"])`: tagged items will be skipped for both chrome and firefox browsers.
|
||||
- `@skip(browserName=/i.*explorer/)`: capabilities with browsers matching the regexp will be skipped (like `iexplorer`, `internet explorer`, `internet-explorer`, ...).
|
||||
|
||||
### Import Step Definition Helper
|
||||
|
||||
In order to use step definition helper like `Given`, `When` or `Then` or hooks, you are suppose to import then from `@cucumber/cucumber`, e.g. like this:
|
||||
|
||||
```js
|
||||
import { Given, When, Then } from '@cucumber/cucumber'
|
||||
```
|
||||
|
||||
Now, if you use Cucumber already for other types of tests unrelated to WebdriverIO for which you use a specific version you need to import these helpers in your e2e tests from the WebdriverIO Cucumber package, e.g.:
|
||||
|
||||
```js
|
||||
import { Given, When, Then, world, context } from '@wdio/cucumber-framework'
|
||||
```
|
||||
|
||||
This ensures that you use the right helpers within the WebdriverIO framework and allows you to use an independent Cucumber version for other types of testing.
|
||||
|
||||
### Publishing Report
|
||||
|
||||
Cucumber provides a feature to publish your test run reports to `https://reports.cucumber.io/`, which can be controlled either by setting the `publish` flag in `cucumberOpts` or by configuring the `CUCUMBER_PUBLISH_TOKEN` environment variable. However, when you use `WebdriverIO` for test execution, there's a limitation with this approach. It updates the reports separately for each feature file, making it difficult to view a consolidated report.
|
||||
|
||||
To overcome this limitation, we've introduced a promise-based method called `publishCucumberReport` within `@wdio/cucumber-framework`. This method should be called in the `onComplete` hook, which is the optimal place to invoke it. `publishCucumberReport` requires the input of the report directory where cucumber message reports are stored.
|
||||
|
||||
You can generate `cucumber message` reports by configuring the `format` option in your `cucumberOpts`. It's highly recommended to provide a dynamic file name within the `cucumber message` format option to prevent overwriting reports and ensure that each test run is accurately recorded.
|
||||
|
||||
Before using this function, make sure to set the following environment variables:
|
||||
- CUCUMBER_PUBLISH_REPORT_URL: The URL where you want to publish the Cucumber report. If not provided, the default URL 'https://messages.cucumber.io/api/reports' will be used.
|
||||
- CUCUMBER_PUBLISH_REPORT_TOKEN: The authorization token required to publish the report. If this token is not set, the function will exit without publishing the report.
|
||||
|
||||
Here's an example of the necessary configurations and code samples for implementation:
|
||||
|
||||
```javascript
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import { publishCucumberReport } from '@wdio/cucumber-framework';
|
||||
|
||||
export const config = {
|
||||
// ... Other Configuration Options
|
||||
cucumberOpts: {
|
||||
// ... Cucumber Options Configuration
|
||||
format: [
|
||||
['message', `./reports/${uuidv4()}.ndjson`],
|
||||
['json', './reports/test-report.json']
|
||||
]
|
||||
},
|
||||
async onComplete() {
|
||||
await publishCucumberReport('./reports');
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Please note that `./reports/` is the directory where `cucumber message` reports will be stored.
|
||||
|
||||
## Using Serenity/JS
|
||||
|
||||
[Serenity/JS](https://serenity-js.org?pk_campaign=wdio8&pk_source=webdriver.io) is an open-source framework designed to make acceptance and regression testing of complex software systems faster, more collaborative, and easier to scale.
|
||||
|
||||
For WebdriverIO test suites, Serenity/JS offers:
|
||||
- [Enhanced Reporting](https://serenity-js.org/handbook/reporting/?pk_campaign=wdio8&pk_source=webdriver.io) - You can use Serenity/JS as a drop-in replacement of any built-in WebdriverIO framework to produce in-depth test execution reports and living documentation of your project.
|
||||
- [Screenplay Pattern APIs](https://serenity-js.org/handbook/design/screenplay-pattern/?pk_campaign=wdio8&pk_source=webdriver.io) - To make your test code portable and reusable across projects and teams, Serenity/JS gives you an optional [abstraction layer](https://serenity-js.org/api/webdriverio?pk_campaign=wdio8&pk_source=webdriver.io) on top of native WebdriverIO APIs.
|
||||
- [Integration Libraries](https://serenity-js.org/api/core/?pk_campaign=wdio8&pk_source=webdriver.io) - For test suites that follow the Screenplay Pattern, Serenity/JS also provides optional integration libraries to help you write [API tests](https://serenity-js.org/api/rest/?pk_campaign=wdio8&pk_source=webdriver.io), [manage local servers](https://serenity-js.org/api/local-server/?pk_campaign=wdio8&pk_source=webdriver.io), [perform assertions](https://serenity-js.org/api/assertions/?pk_campaign=wdio8&pk_source=webdriver.io), and more!
|
||||
|
||||

|
||||
|
||||
### Installing Serenity/JS
|
||||
|
||||
To add Serenity/JS to an [existing WebdriverIO project](https://webdriver.io/docs/gettingstarted), install the following Serenity/JS modules from NPM:
|
||||
|
||||
```sh npm2yarn
|
||||
npm install @serenity-js/{core,web,webdriverio,assertions,console-reporter,serenity-bdd} --save-dev
|
||||
```
|
||||
|
||||
Learn more about Serenity/JS modules:
|
||||
- [`@serenity-js/core`](https://serenity-js.org/api/core/?pk_campaign=wdio8&pk_source=webdriver.io)
|
||||
- [`@serenity-js/web`](https://serenity-js.org/api/web/?pk_campaign=wdio8&pk_source=webdriver.io)
|
||||
- [`@serenity-js/webdriverio`](https://serenity-js.org/api/webdriverio/?pk_campaign=wdio8&pk_source=webdriver.io)
|
||||
- [`@serenity-js/assertions`](https://serenity-js.org/api/assertions/?pk_campaign=wdio8&pk_source=webdriver.io)
|
||||
- [`@serenity-js/console-reporter`](https://serenity-js.org/api/console-reporter/?pk_campaign=wdio8&pk_source=webdriver.io)
|
||||
- [`@serenity-js/serenity-bdd`](https://serenity-js.org/api/serenity-bdd/?pk_campaign=wdio8&pk_source=webdriver.io)
|
||||
|
||||
### Configuring Serenity/JS
|
||||
|
||||
To enable integration with Serenity/JS, configure WebdriverIO as follows:
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="wdio-conf-typescript" label="TypeScript" default>
|
||||
|
||||
```typescript title="wdio.conf.ts"
|
||||
import { WebdriverIOConfig } from '@serenity-js/webdriverio';
|
||||
|
||||
export const config: WebdriverIOConfig = {
|
||||
|
||||
// Tell WebdriverIO to use Serenity/JS framework
|
||||
framework: '@serenity-js/webdriverio',
|
||||
|
||||
// Serenity/JS configuration
|
||||
serenity: {
|
||||
// Configure Serenity/JS to use the appropriate adapter for your test runner
|
||||
runner: 'cucumber',
|
||||
// runner: 'mocha',
|
||||
// runner: 'jasmine',
|
||||
|
||||
// Register Serenity/JS reporting services, a.k.a. the "stage crew"
|
||||
crew: [
|
||||
// Optional, print test execution results to standard output
|
||||
'@serenity-js/console-reporter',
|
||||
|
||||
// Optional, produce Serenity BDD reports and living documentation (HTML)
|
||||
'@serenity-js/serenity-bdd',
|
||||
[ '@serenity-js/core:ArtifactArchiver', { outputDirectory: 'target/site/serenity' } ],
|
||||
|
||||
// Optional, automatically capture screenshots upon interaction failure
|
||||
[ '@serenity-js/web:Photographer', { strategy: 'TakePhotosOfFailures' } ],
|
||||
]
|
||||
},
|
||||
|
||||
// Configure your Cucumber runner
|
||||
cucumberOpts: {
|
||||
// see Cucumber configuration options below
|
||||
},
|
||||
|
||||
|
||||
// ... or Jasmine runner
|
||||
jasmineOpts: {
|
||||
// see Jasmine configuration options below
|
||||
},
|
||||
|
||||
// ... or Mocha runner
|
||||
mochaOpts: {
|
||||
// see Mocha configuration options below
|
||||
},
|
||||
|
||||
runner: 'local',
|
||||
|
||||
// Any other WebdriverIO configuration
|
||||
};
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="wdio-conf-javascript" label="JavaScript">
|
||||
|
||||
```typescript title="wdio.conf.js"
|
||||
export const config = {
|
||||
|
||||
// Tell WebdriverIO to use Serenity/JS framework
|
||||
framework: '@serenity-js/webdriverio',
|
||||
|
||||
// Serenity/JS configuration
|
||||
serenity: {
|
||||
// Configure Serenity/JS to use the appropriate adapter for your test runner
|
||||
runner: 'cucumber',
|
||||
// runner: 'mocha',
|
||||
// runner: 'jasmine',
|
||||
|
||||
// Register Serenity/JS reporting services, a.k.a. the "stage crew"
|
||||
crew: [
|
||||
'@serenity-js/console-reporter',
|
||||
'@serenity-js/serenity-bdd',
|
||||
[ '@serenity-js/core:ArtifactArchiver', { outputDirectory: 'target/site/serenity' } ],
|
||||
[ '@serenity-js/web:Photographer', { strategy: 'TakePhotosOfFailures' } ],
|
||||
]
|
||||
},
|
||||
|
||||
// Configure your Cucumber runner
|
||||
cucumberOpts: {
|
||||
// see Cucumber configuration options below
|
||||
},
|
||||
|
||||
|
||||
// ... or Jasmine runner
|
||||
jasmineOpts: {
|
||||
// see Jasmine configuration options below
|
||||
},
|
||||
|
||||
// ... or Mocha runner
|
||||
mochaOpts: {
|
||||
// see Mocha configuration options below
|
||||
},
|
||||
|
||||
runner: 'local',
|
||||
|
||||
// Any other WebdriverIO configuration
|
||||
};
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
Learn more about:
|
||||
- [Serenity/JS Cucumber configuration options](https://serenity-js.org/api/cucumber-adapter/interface/CucumberConfig/?pk_campaign=wdio8&pk_source=webdriver.io)
|
||||
- [Serenity/JS Jasmine configuration options](https://serenity-js.org/api/jasmine-adapter/interface/JasmineConfig/?pk_campaign=wdio8&pk_source=webdriver.io)
|
||||
- [Serenity/JS Mocha configuration options](https://serenity-js.org/api/mocha-adapter/interface/MochaConfig/?pk_campaign=wdio8&pk_source=webdriver.io)
|
||||
- [WebdriverIO configuration file](configurationfile)
|
||||
|
||||
### Producing Serenity BDD reports and living documentation
|
||||
|
||||
[Serenity BDD reports and living documentation](https://serenity-bdd.github.io/docs/reporting/the_serenity_reports) are generated by [Serenity BDD CLI](https://github.com/serenity-bdd/serenity-core/tree/main/serenity-cli), a Java program downloaded and managed by the [`@serenity-js/serenity-bdd`](https://serenity-js.org/api/serenity-bdd/?pk_campaign=wdio8&pk_source=webdriver.io) module.
|
||||
|
||||
To produce Serenity BDD reports, your test suite must:
|
||||
- download the Serenity BDD CLI, by calling `serenity-bdd update` which caches the CLI `jar` locally
|
||||
- produce intermediate Serenity BDD `.json` reports, by registering [`SerenityBDDReporter`](https://serenity-js.org/api/serenity-bdd/class/SerenityBDDReporter/?pk_campaign=wdio8&pk_source=webdriver.io) as per the [configuration instructions](#configuring-serenityjs)
|
||||
- invoke the Serenity BDD CLI when you want to produce the report, by calling `serenity-bdd run`
|
||||
|
||||
The pattern used by all the [Serenity/JS Project Templates](https://serenity-js.org/handbook/project-templates/?pk_campaign=wdio8&pk_source=webdriver.io#webdriverio) relies on using:
|
||||
- a [`postinstall`](https://docs.npmjs.com/cli/v9/using-npm/scripts#life-cycle-operation-order) NPM script to download the Serenity BDD CLI
|
||||
- [`npm-failsafe`](https://www.npmjs.com/package/npm-failsafe) to run the reporting process even if the test suite itself has failed (which is precisely when you need test reports the most...).
|
||||
- [`rimraf`](https://www.npmjs.com/package/rimraf) as a convenience method to remove any test reports left over from the previous run
|
||||
|
||||
```json title="package.json"
|
||||
{
|
||||
"scripts": {
|
||||
"postinstall": "serenity-bdd update",
|
||||
"clean": "rimraf target",
|
||||
"test": "failsafe clean test:execute test:report",
|
||||
"test:execute": "wdio wdio.conf.ts",
|
||||
"test:report": "serenity-bdd run"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
To learn more about the `SerenityBDDReporter`, please consult:
|
||||
- installation instructions in [`@serenity-js/serenity-bdd` documentation](https://serenity-js.org/api/serenity-bdd/?pk_campaign=wdio8&pk_source=webdriver.io),
|
||||
- configuration examples in [`SerenityBDDReporter` API docs](https://serenity-js.org/api/serenity-bdd/class/SerenityBDDReporter/?pk_campaign=wdio8&pk_source=webdriver.io),
|
||||
- [Serenity/JS examples on GitHub](https://github.com/serenity-js/serenity-js/tree/main/examples).
|
||||
|
||||
### Using Serenity/JS Screenplay Pattern APIs
|
||||
|
||||
The [Screenplay Pattern](https://serenity-js.org/handbook/design/screenplay-pattern/?pk_campaign=wdio8&pk_source=webdriver.io) is an innovative, user-centred approach to writing high-quality automated acceptance tests. It steers you towards an effective use of layers of abstraction, helps your test scenarios capture the business vernacular of your domain, and encourages good testing and software engineering habits on your team.
|
||||
|
||||
By default, when you register `@serenity-js/webdriverio` as your WebdriverIO `framework`, Serenity/JS configures a default [cast](https://serenity-js.org/api/core/class/Cast/?pk_campaign=wdio8&pk_source=webdriver.io) of [actors](https://serenity-js.org/api/core/class/Actor/?pk_campaign=wdio8&pk_source=webdriver.io), where every actor can:
|
||||
- [`BrowseTheWebWithWebdriverIO`](https://serenity-js.org/api/webdriverio/class/BrowseTheWebWithWebdriverIO/?pk_campaign=wdio8&pk_source=webdriver.io)
|
||||
- [`TakeNotes.usingAnEmptyNotepad()`](https://serenity-js.org/api/core/class/TakeNotes/?pk_campaign=wdio8&pk_source=webdriver.io)
|
||||
|
||||
This should be enough to help you get started with introducing test scenarios that follow the Screenplay Pattern even to an existing test suite, for example:
|
||||
|
||||
```typescript title="specs/example.spec.ts"
|
||||
import { actorCalled } from '@serenity-js/core'
|
||||
import { Navigate, Page } from '@serenity-js/web'
|
||||
import { Ensure, equals } from '@serenity-js/assertions'
|
||||
|
||||
describe('My awesome website', () => {
|
||||
it('can have test scenarios that follow the Screenplay Pattern', async () => {
|
||||
await actorCalled('Alice').attemptsTo(
|
||||
Navigate.to(`https://webdriver.io`),
|
||||
Ensure.that(
|
||||
Page.current().title(),
|
||||
equals(`WebdriverIO · Next-gen browser and mobile automation test framework for Node.js | WebdriverIO`)
|
||||
),
|
||||
)
|
||||
})
|
||||
|
||||
it('can have non-Screenplay scenarios too', async () => {
|
||||
await browser.url('https://webdriver.io')
|
||||
await expect(browser)
|
||||
.toHaveTitle('WebdriverIO · Next-gen browser and mobile automation test framework for Node.js | WebdriverIO')
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
To learn more about the Screenplay Pattern, check out:
|
||||
- [The Screenplay Pattern](https://serenity-js.org/handbook/design/screenplay-pattern/?pk_campaign=wdio8&pk_source=webdriver.io)
|
||||
- [Web testing with Serenity/JS](https://serenity-js.org/handbook/web-testing/?pk_campaign=wdio8&pk_source=webdriver.io)
|
||||
- ["BDD in Action, Second Edition"](https://www.manning.com/books/bdd-in-action-second-edition)
|
||||
|
|
@ -1,187 +0,0 @@
|
|||
---
|
||||
id: gettingstarted
|
||||
title: Getting Started
|
||||
---
|
||||
|
||||
Welcome to the WebdriverIO documentation. It will help you to get started fast. If you run into problems, you can find help and answers on our [Discord Support Server](https://discord.webdriver.io) or you can hit me on [Twitter](https://twitter.com/webdriverio).
|
||||
|
||||
:::info
|
||||
These are the docs for the latest version (__>=9.x__) of WebdriverIO. If you are still using an older version, please visit the [old documentation websites](/versions)!
|
||||
:::
|
||||
|
||||
<LiteYouTubeEmbed id="rA4IFNyW54c" title="Getting Started with WebdriverIO" />
|
||||
|
||||
:::tip Official YouTube Channel 🎥
|
||||
|
||||
You can find more videos around WebdriverIO on the [official YouTube channel](https://youtube.com/@webdriverio). Make sure you subscribe!
|
||||
|
||||
:::
|
||||
|
||||
## Initiate a WebdriverIO Setup
|
||||
|
||||
To add a full WebdriverIO setup to an existing or new project using the [WebdriverIO Starter Toolkit](https://www.npmjs.com/package/create-wdio), run:
|
||||
|
||||
If you're in the root directory of an existing project, run:
|
||||
|
||||
<Tabs
|
||||
defaultValue="npm"
|
||||
values={[
|
||||
{label: 'NPM', value: 'npm'},
|
||||
{label: 'Yarn', value: 'yarn'},
|
||||
{label: 'pnpm', value: 'pnpm'},
|
||||
{label: 'bun', value: 'bun'},
|
||||
]
|
||||
}>
|
||||
<TabItem value="npm">
|
||||
|
||||
```sh
|
||||
npm init wdio@latest .
|
||||
```
|
||||
|
||||
or if you want to create a new project:
|
||||
|
||||
```sh
|
||||
npm init wdio@latest ./path/to/new/project
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="yarn">
|
||||
|
||||
```sh
|
||||
yarn create wdio .
|
||||
```
|
||||
|
||||
or if you want to create a new project:
|
||||
|
||||
```sh
|
||||
yarn create wdio ./path/to/new/project
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="pnpm">
|
||||
|
||||
```sh
|
||||
pnpm create wdio@latest .
|
||||
```
|
||||
|
||||
or if you want to create a new project:
|
||||
|
||||
```sh
|
||||
pnpm create wdio@latest ./path/to/new/project
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="bun">
|
||||
|
||||
```sh
|
||||
bun create wdio@latest .
|
||||
```
|
||||
|
||||
or if you want to create a new project:
|
||||
|
||||
```sh
|
||||
bun create wdio@latest ./path/to/new/project
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
This single command downloads the WebdriverIO CLI tool and runs a configuration wizard that helps you to configure your test suite.
|
||||
|
||||
<CreateProjectAnimation />
|
||||
|
||||
The wizard will prompt a set questions that guides you through the setup. You can pass a `--yes` parameter to pick a default set up which will use Mocha with Chrome using the [Page Object](https://martinfowler.com/bliki/PageObject.html) pattern.
|
||||
|
||||
<Tabs
|
||||
defaultValue="npm"
|
||||
values={[
|
||||
{label: 'NPM', value: 'npm'},
|
||||
{label: 'Yarn', value: 'yarn'},
|
||||
{label: 'pnpm', value: 'pnpm'},
|
||||
{label: 'bun', value: 'bun'},
|
||||
]
|
||||
}>
|
||||
<TabItem value="npm">
|
||||
|
||||
```sh
|
||||
npm init wdio@latest . -- --yes
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="yarn">
|
||||
|
||||
```sh
|
||||
yarn create wdio . --yes
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="pnpm">
|
||||
|
||||
```sh
|
||||
pnpm create wdio@latest . --yes
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="bun">
|
||||
|
||||
```sh
|
||||
bun create wdio@latest . --yes
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## Install CLI Manually
|
||||
|
||||
You can also add the CLI package to your project manually via:
|
||||
|
||||
```sh
|
||||
npm i --save-dev @wdio/cli
|
||||
npx wdio --version # prints e.g. `8.13.10`
|
||||
|
||||
# run configuration wizard
|
||||
npx wdio config
|
||||
```
|
||||
|
||||
## Run Test
|
||||
|
||||
You can start your test suite by using the `run` command and pointing to the WebdriverIO config that you just created:
|
||||
|
||||
```sh
|
||||
npx wdio run ./wdio.conf.js
|
||||
```
|
||||
|
||||
If you like to run specific test files you can add a `--spec` parameter:
|
||||
|
||||
```sh
|
||||
npx wdio run ./wdio.conf.js --spec example.e2e.js
|
||||
```
|
||||
|
||||
or define suites in your config file and run just the test files defined by in a suite:
|
||||
|
||||
```sh
|
||||
npx wdio run ./wdio.conf.js --suite exampleSuiteName
|
||||
```
|
||||
|
||||
## Run in a script
|
||||
|
||||
If you would like to use WebdriverIO as an automation engine in [Standalone Mode](/docs/setuptypes#standalone-mode) within a Node.JS script you can also directly install WebdriverIO and use it as a package, e.g. to generate a screenshot of a website:
|
||||
|
||||
```js reference useHTTPS
|
||||
https://github.com/webdriverio/example-recipes/blob/fc362f2f8dd823d294b9bb5f92bd5991339d4591/getting-started/run-in-script.js#L2-L19
|
||||
```
|
||||
|
||||
__Note:__ all WebdriverIO commands are asynchronous and need to be properly handled using [`async/await`](https://javascript.info/async-await).
|
||||
|
||||
## Record tests
|
||||
|
||||
WebdriverIO provides tools to help you get started by recording your test actions on screen and generate WebdriverIO test scripts automatically. See [Recorder tests with Chrome DevTools Recorder](/docs/record) for more information.
|
||||
|
||||
## System Requirements
|
||||
|
||||
You’ll need [Node.js](http://nodejs.org) installed.
|
||||
|
||||
- Install at least v18.20.0 or higher as this is the oldest active LTS version
|
||||
- Only releases that are or will become an LTS release are officially supported
|
||||
|
||||
If Node is not currently installed on your system, we suggest utilizing a tool such as [NVM](https://github.com/creationix/nvm) or [Volta](https://volta.sh/) to assist in managing multiple active Node.js versions. NVM is a popular choice, while Volta is also a good alternative.
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
---
|
||||
id: githubactions
|
||||
title: Github Actions
|
||||
---
|
||||
|
||||
If your repository is hosted on Github, you can use [Github Actions](https://docs.github.com/en/actions) to run your tests on Github's infrastructure.
|
||||
|
||||
1. every time you push changes
|
||||
2. on every pull request creation
|
||||
3. on scheduled time
|
||||
4. by manual trigger
|
||||
|
||||
In the root of your repository, create a `.github/workflows` directory. Add a Yaml file, for example `.github/workflows/ci.yaml`. In there you will configure how to run your tests.
|
||||
|
||||
See [jasmine-boilerplate](https://github.com/webdriverio/jasmine-boilerplate/blob/master/.github/workflows/ci.yaml) for reference implementation, and [sample test runs](https://github.com/webdriverio/jasmine-boilerplate/actions?query=workflow%3ACI).
|
||||
|
||||
```yaml reference
|
||||
https://github.com/webdriverio/jasmine-boilerplate/blob/master/.github/workflows/ci.yaml
|
||||
```
|
||||
|
||||
Find out in the [Github Docs](https://docs.github.com/en/actions/managing-workflow-runs-and-deployments/managing-workflow-runs/manually-running-a-workflow?tool=cli) on more information about creating workflow files.
|
||||
|
|
@ -1,51 +0,0 @@
|
|||
---
|
||||
id: jenkins
|
||||
title: Jenkins
|
||||
---
|
||||
|
||||
WebdriverIO offers a tight integration to CI systems like [Jenkins](https://jenkins-ci.org). With the `junit` reporter, you can easily debug your tests as well as keep track of your test results. The integration is pretty easy.
|
||||
|
||||
1. Install the `junit` test reporter: `$ npm install @wdio/junit-reporter --save-dev`)
|
||||
1. Update your config to save your XUnit results where Jenkins can find them, (and specify the `junit` reporter):
|
||||
|
||||
```js
|
||||
// wdio.conf.js
|
||||
module.exports = {
|
||||
// ...
|
||||
reporters: [
|
||||
'dot',
|
||||
['junit', {
|
||||
outputDir: './'
|
||||
}]
|
||||
],
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
It is up to you which framework to choose. The reports will be similar. For this tutorial, we’ll use Jasmine.
|
||||
|
||||
After you have written couple of tests, you can setup a new Jenkins job. Give it a name and a description:
|
||||
|
||||

|
||||
|
||||
Then make sure it grabs always the newest version of your repository:
|
||||
|
||||

|
||||
|
||||
**Now the important part:** Create a `build` step to execute shell commands. The `build` step needs to build your project. Since this demo project only tests an external app, you don't need to build anything. Just install the node dependencies and run the command `npm test` (which is an alias for `node_modules/.bin/wdio test/wdio.conf.js`).
|
||||
|
||||
If you have installed a plugin like AnsiColor, but logs are still not colored, run tests with environment variable `FORCE_COLOR=1` (e.g., `FORCE_COLOR=1 npm test`).
|
||||
|
||||

|
||||
|
||||
After your test, you’ll want Jenkins to track your XUnit report. To do so, you have to add a post-build action called _"Publish JUnit test result report"_.
|
||||
|
||||
You could also install an external XUnit plugin to track your reports. The JUnit one comes with the basic Jenkins installation and is sufficient enough for now.
|
||||
|
||||
According to the config file, the XUnit reports will be saved in the project’s root directory. These reports are XML files. So, all you need to do in order to track the reports is to point Jenkins to all XML files in your root directory:
|
||||
|
||||

|
||||
|
||||
That's it! You’ve now set up Jenkins to run your WebdriverIO jobs. Your job will now provide detailed test results with history charts, stacktrace information on failed jobs, and a list of commands with payload that got used in each test.
|
||||
|
||||

|
||||
|
|
@ -1,189 +0,0 @@
|
|||
---
|
||||
id: mocksandspies
|
||||
title: Request Mocks and Spies
|
||||
---
|
||||
|
||||
WebdriverIO comes with built-in support for modifying network responses that allows you to focus testing your frontend application without having to setup your backend or a mock server. You can define custom responses for web resources like REST API requests in your test and modify them dynamically.
|
||||
|
||||
:::info
|
||||
|
||||
Note that using the `mock` command requires support for Chrome DevTools protocol. That support is given if you run tests locally in a Chromium-based browser, via a Selenium Grid v4 or higher, or through a cloud vendor with support for the Chrome DevTools protocol (e.g. SauceLabs, BrowserStack, LambdaTest). Full cross-browser support will be available once the required primitives land in [Webdriver Bidi](https://wpt.fyi/results/webdriver/tests/bidi/network?label=experimental&label=master&aligned) and get implemented in the respective browser.
|
||||
|
||||
:::
|
||||
|
||||
## Creating a mock
|
||||
|
||||
Before you can modify any responses you have define a mock first. This mock is described by the resource url and can be filtered by the [request method](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods) or [headers](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers). The resource supports glob expressions by [minimatch](https://www.npmjs.com/package/minimatch):
|
||||
|
||||
```js
|
||||
// mock all resources ending with "/users/list"
|
||||
const userListMock = await browser.mock('**/users/list')
|
||||
|
||||
// or you can specify the mock by filtering resources by headers or
|
||||
// status code, only mock successful requests to json resources
|
||||
const strictMock = await browser.mock('**', {
|
||||
// mock all json responses
|
||||
requestHeaders: { 'Content-Type': 'application/json' },
|
||||
// that were successful
|
||||
statusCode: 200
|
||||
})
|
||||
```
|
||||
|
||||
## Specifying custom responses
|
||||
|
||||
Once you have defined a mock you can define custom responses for it. Those custom responses can be either an object to respond a JSON, a local file to respond with a custom fixture or a web resource to replace the response with a resource from the internet.
|
||||
|
||||
### Mocking API Requests
|
||||
|
||||
In order to mock API requests where you expect a JSON response all you need to do is to call `respond` on the mock object with an arbitrary object you want to return, e.g.:
|
||||
|
||||
```js
|
||||
const mock = await browser.mock('https://todo-backend-express-knex.herokuapp.com/')
|
||||
|
||||
mock.respond([{
|
||||
title: 'Injected (non) completed Todo',
|
||||
order: null,
|
||||
completed: false
|
||||
}, {
|
||||
title: 'Injected completed Todo',
|
||||
order: null,
|
||||
completed: true
|
||||
}], {
|
||||
headers: {
|
||||
'Access-Control-Allow-Origin': '*'
|
||||
},
|
||||
fetchResponse: false
|
||||
})
|
||||
|
||||
await browser.url('https://todobackend.com/client/index.html?https://todo-backend-express-knex.herokuapp.com/')
|
||||
|
||||
await $('#todo-list li').waitForExist()
|
||||
console.log(await $$('#todo-list li').map(el => el.getText()))
|
||||
// outputs: "[ 'Injected (non) completed Todo', 'Injected completed Todo' ]"
|
||||
```
|
||||
|
||||
You can also modify the response headers as well as the status code by passing in some mock response params as follows:
|
||||
|
||||
```js
|
||||
mock.respond({ ... }, {
|
||||
// respond with status code 404
|
||||
statusCode: 404,
|
||||
// merge response headers with following headers
|
||||
headers: { 'x-custom-header': 'foobar' }
|
||||
})
|
||||
```
|
||||
|
||||
If you want the mock not to call the backend at all, you can pass `false` for the `fetchResponse` flag.
|
||||
|
||||
```js
|
||||
mock.respond({ ... }, {
|
||||
// do not call the actual backend
|
||||
fetchResponse: false
|
||||
})
|
||||
```
|
||||
|
||||
It is recommend to store custom responses in fixture files so you can just require them in your test as follows:
|
||||
|
||||
```js
|
||||
// requires Node.js v16.14.0 or higher to support JSON import assertions
|
||||
import responseFixture from './__fixtures__/apiResponse.json' assert { type: 'json' }
|
||||
mock.respond(responseFixture)
|
||||
```
|
||||
|
||||
### Mocking text resources
|
||||
|
||||
If you like to modify text resources like JavaScript, CSS files or other text based resources you can just pass in a file path and WebdriverIO will replaces the original resource with it, e.g.:
|
||||
|
||||
```js
|
||||
const scriptMock = await browser.mock('**/script.min.js')
|
||||
scriptMock.respond('./tests/fixtures/script.js')
|
||||
|
||||
// or respond with your custom JS
|
||||
scriptMock.respond('alert("I am a mocked resource")')
|
||||
```
|
||||
|
||||
### Redirect web resources
|
||||
|
||||
You can also just replace a web resource with another web resource if your desired response is already hosted on the web. This works with individual page resources as well as with a webpage itself, e.g.:
|
||||
|
||||
```js
|
||||
const pageMock = await browser.mock('https://google.com/')
|
||||
await pageMock.respond('https://webdriver.io')
|
||||
await browser.url('https://google.com')
|
||||
console.log(await browser.getTitle()) // returns "WebdriverIO · Next-gen browser and mobile automation test framework for Node.js"
|
||||
```
|
||||
|
||||
### Dynamic responses
|
||||
|
||||
If your mock response depends on the original resource response you can also dynamically modify the resource by passing in a function receives the original response as parameter and sets the mock based on the return value, e.g.:
|
||||
|
||||
```js
|
||||
const mock = await browser.mock('https://todo-backend-express-knex.herokuapp.com/', {
|
||||
method: 'get'
|
||||
})
|
||||
|
||||
mock.respond((req) => {
|
||||
// replace todo content with their list number
|
||||
return req.body.map((item, i) => ({ ...item, title: i }))
|
||||
})
|
||||
|
||||
await browser.url('https://todobackend.com/client/index.html?https://todo-backend-express-knex.herokuapp.com/')
|
||||
|
||||
await $('#todo-list li').waitForExist()
|
||||
console.log(await $$('#todo-list li label').map((el) => el.getText()))
|
||||
// returns
|
||||
// [
|
||||
// '0', '1', '2', '19', '20',
|
||||
// '21', '3', '4', '5', '6',
|
||||
// '7', '8', '9', '10', '11',
|
||||
// '12', '13', '14', '15', '16',
|
||||
// '17', '18', '22'
|
||||
// ]
|
||||
```
|
||||
|
||||
## Aborting mocks
|
||||
|
||||
Instead of returning a custom response you can also just abort the request with one of the following HTTP errors:
|
||||
|
||||
- Failed
|
||||
- Aborted
|
||||
- TimedOut
|
||||
- AccessDenied
|
||||
- ConnectionClosed
|
||||
- ConnectionReset
|
||||
- ConnectionRefused
|
||||
- ConnectionAborted
|
||||
- ConnectionFailed
|
||||
- NameNotResolved
|
||||
- InternetDisconnected
|
||||
- AddressUnreachable
|
||||
- BlockedByClient
|
||||
- BlockedByResponse
|
||||
|
||||
This is very useful if you want to block 3rd party script from your page that have a negative influence on your functional test. You can abort a mock by just calling `abort` or `abortOnce`, e.g.:
|
||||
|
||||
```js
|
||||
const mock = await browser.mock('https://www.google-analytics.com/**')
|
||||
mock.abort('Failed')
|
||||
```
|
||||
|
||||
## Spies
|
||||
|
||||
Every mock is automatically a spy that counts the amount of requests the browser made to that resource. If you don't apply a custom response or abort reason to the mock it continues with the default response you would normally receive. This allows you to check how many times the browser made the request, e.g. to a certain API endpoint.
|
||||
|
||||
```js
|
||||
const mock = await browser.mock('**/user', { method: 'post' })
|
||||
console.log(mock.calls.length) // returns 0
|
||||
|
||||
// register user
|
||||
await $('#username').setValue('randomUser')
|
||||
await $('password').setValue('password123')
|
||||
await $('password_repeat').setValue('password123')
|
||||
await $('button[type="submit"]').click()
|
||||
|
||||
// check if API request was made
|
||||
expect(mock.calls.length).toBe(1)
|
||||
|
||||
// assert response
|
||||
expect(mock.calls[0].body).toEqual({ success: true })
|
||||
```
|
||||
|
|
@ -1,256 +0,0 @@
|
|||
---
|
||||
id: multiremote
|
||||
title: Multiremote
|
||||
---
|
||||
|
||||
WebdriverIO allows you to run multiple automated sessions in a single test. This becomes handy when you’re testing features that require multiple users (for example, chat or WebRTC applications).
|
||||
|
||||
Instead of creating a couple of remote instances where you need to execute common commands like [`newSession`](/docs/api/webdriver#newsession) or [`url`](/docs/api/browser/url) on each instance, you can simply create a **multiremote** instance and control all browsers at the same time.
|
||||
|
||||
To do so, just use the `multiremote()` function, and pass in an object with names keyed to `capabilities` for values. By giving each capability a name, you can easily select and access that single instance when executing commands on a single instance.
|
||||
|
||||
:::info
|
||||
|
||||
Multiremote is _not_ meant to execute all your tests in parallel. It is intended to help coordinate multiple browsers and/or mobile devices for special integration tests (e.g. chat applications).
|
||||
|
||||
:::
|
||||
|
||||
All multiremote instances return an array of results. The first result represents the capability defined first in the capability object the second result the second capability and so on.
|
||||
|
||||
## Using Standalone Mode
|
||||
|
||||
Here is an example of how to create a multiremote instance in __standalone mode__:
|
||||
|
||||
```js
|
||||
import { multiremote } from 'webdriverio'
|
||||
|
||||
(async () => {
|
||||
const browser = await multiremote({
|
||||
myChromeBrowser: {
|
||||
capabilities: {
|
||||
browserName: 'chrome'
|
||||
}
|
||||
},
|
||||
myFirefoxBrowser: {
|
||||
capabilities: {
|
||||
browserName: 'firefox'
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// open url with both browser at the same time
|
||||
await browser.url('http://json.org')
|
||||
|
||||
// call commands at the same time
|
||||
const title = await browser.getTitle()
|
||||
expect(title).toEqual(['JSON', 'JSON'])
|
||||
|
||||
// click on an element at the same time
|
||||
const elem = await browser.$('#someElem')
|
||||
await elem.click()
|
||||
|
||||
// only click with one browser (Firefox)
|
||||
await elem.getInstance('myFirefoxBrowser').click()
|
||||
})()
|
||||
```
|
||||
|
||||
## Using WDIO Testrunner
|
||||
|
||||
In order to use multiremote in the WDIO testrunner, just define the `capabilities` object in your `wdio.conf.js` as an object with the browser names as keys (instead of a list of capabilities):
|
||||
|
||||
```js
|
||||
export const config = {
|
||||
// ...
|
||||
capabilities: {
|
||||
myChromeBrowser: {
|
||||
capabilities: {
|
||||
browserName: 'chrome'
|
||||
}
|
||||
},
|
||||
myFirefoxBrowser: {
|
||||
capabilities: {
|
||||
browserName: 'firefox'
|
||||
}
|
||||
}
|
||||
}
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
This will create two WebDriver sessions with Chrome and Firefox. Instead of just Chrome and Firefox you can also boot up two mobile devices using [Appium](http://appium.io) or one mobile device and one browser.
|
||||
|
||||
You can also run multiremote in parallel by putting the browser capabilities object in an array. Please make sure to have `capabilities` field included in each browser, as this is how we tell each mode apart.
|
||||
|
||||
```js
|
||||
export const config = {
|
||||
// ...
|
||||
capabilities: [{
|
||||
myChromeBrowser0: {
|
||||
capabilities: {
|
||||
browserName: 'chrome'
|
||||
}
|
||||
},
|
||||
myFirefoxBrowser0: {
|
||||
capabilities: {
|
||||
browserName: 'firefox'
|
||||
}
|
||||
}
|
||||
}, {
|
||||
myChromeBrowser1: {
|
||||
capabilities: {
|
||||
browserName: 'chrome'
|
||||
}
|
||||
},
|
||||
myFirefoxBrowser1: {
|
||||
capabilities: {
|
||||
browserName: 'firefox'
|
||||
}
|
||||
}
|
||||
}]
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
You can even boot up one of the [cloud services backend](https://webdriver.io/docs/cloudservices.html) together with local Webdriver/Appium, or Selenium Standalone instances. WebdriverIO automatically detect cloud backend capabilities if you specified either of `bstack:options` ([Browserstack](https://webdriver.io/docs/browserstack-service.html)), `sauce:options` ([SauceLabs](https://webdriver.io/docs/sauce-service.html)), or `tb:options` ([TestingBot](https://webdriver.io/docs/testingbot-service.html)) in browser capabilities.
|
||||
|
||||
```js
|
||||
export const config = {
|
||||
// ...
|
||||
user: process.env.BROWSERSTACK_USERNAME,
|
||||
key: process.env.BROWSERSTACK_ACCESS_KEY,
|
||||
capabilities: {
|
||||
myChromeBrowser: {
|
||||
capabilities: {
|
||||
browserName: 'chrome'
|
||||
}
|
||||
},
|
||||
myBrowserStackFirefoxBrowser: {
|
||||
capabilities: {
|
||||
browserName: 'firefox',
|
||||
'bstack:options': {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
services: [
|
||||
['browserstack', 'selenium-standalone']
|
||||
],
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
Any kind of OS/browser combination is possible here (including mobile and desktop browsers). All commands your tests call via the `browser` variable are executed in parallel with each instance. This helps streamline your integration tests and speed up their execution.
|
||||
|
||||
For example, if you open up a URL:
|
||||
|
||||
```js
|
||||
browser.url('https://socketio-chat-h9jt.herokuapp.com/')
|
||||
```
|
||||
|
||||
Each command’s result will be an object with the browser names as the key, and the command result as value, like so:
|
||||
|
||||
```js
|
||||
// wdio testrunner example
|
||||
await browser.url('https://www.whatismybrowser.com')
|
||||
|
||||
const elem = await $('.string-major')
|
||||
const result = await elem.getText()
|
||||
|
||||
console.log(result[0]) // returns: 'Chrome 40 on Mac OS X (Yosemite)'
|
||||
console.log(result[1]) // returns: 'Firefox 35 on Mac OS X (Yosemite)'
|
||||
```
|
||||
|
||||
Notice that each command is executed one by one. This means that the command finishes once all browsers have executed it. This is helpful because it keeps the browser actions synced, which makes it easier to understand what’s currently happening.
|
||||
|
||||
Sometimes it is necessary to do different things in each browser in order to test something. For instance, if we want to test a chat application, there has to be one browser who sends a text message while another browser waits to receive it, and then run an assertion on it.
|
||||
|
||||
When using the WDIO testrunner, it registers the browser names with their instances to the global scope:
|
||||
|
||||
```js
|
||||
const myChromeBrowser = browser.getInstance('myChromeBrowser')
|
||||
await myChromeBrowser.$('#message').setValue('Hi, I am Chrome')
|
||||
await myChromeBrowser.$('#send').click()
|
||||
|
||||
// wait until messages arrive
|
||||
await $('.messages').waitForExist()
|
||||
// check if one of the messages contain the Chrome message
|
||||
assert.true(
|
||||
(
|
||||
await $$('.messages').map((m) => m.getText())
|
||||
).includes('Hi, I am Chrome')
|
||||
)
|
||||
```
|
||||
|
||||
In this example, the `myFirefoxBrowser` instance will start waiting on a message once the `myChromeBrowser` instance has clicked on `#send` button.
|
||||
|
||||
Multiremote makes it easy and convenient to control multiple browsers, whether you want them doing the same thing in parallel, or different things in concert.
|
||||
|
||||
## Accessing browser instances using strings via the browser object
|
||||
In addition to accessing the browser instance via their global variables (e.g. `myChromeBrowser`, `myFirefoxBrowser`), you can also access them via the `browser` object, e.g. `browser["myChromeBrowser"]` or `browser["myFirefoxBrowser"]`. You can get a list of all your instances via `browser.instances`. This is especially useful when writing re-usable test steps that can be performed in either browser, e.g.:
|
||||
|
||||
wdio.conf.js:
|
||||
```js
|
||||
capabilities: {
|
||||
userA: {
|
||||
capabilities: {
|
||||
browserName: 'chrome'
|
||||
}
|
||||
},
|
||||
userB: {
|
||||
capabilities: {
|
||||
browserName: 'chrome'
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Cucumber file:
|
||||
```feature
|
||||
When User A types a message into the chat
|
||||
```
|
||||
|
||||
Step definition file:
|
||||
```js
|
||||
When(/^User (.) types a message into the chat/, async (userId) => {
|
||||
await browser.getInstance(`user${userId}`).$('#message').setValue('Hi, I am Chrome')
|
||||
await browser.getInstance(`user${userId}`).$('#send').click()
|
||||
})
|
||||
```
|
||||
|
||||
## Extending TypeScript Types
|
||||
|
||||
If you are using TypeScript and like to access the driver instance from the multiremote object directly, you can also extend the multiremote types to do so. For example, given the following capabilities:
|
||||
|
||||
```ts title=wdio.conf.ts
|
||||
export const config: WebdriverIO.MultiremoteConfig = {
|
||||
// ...
|
||||
capabilities: {
|
||||
myAppiumDriver: {
|
||||
// ...
|
||||
},
|
||||
myChromeDriver: {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
You can extend the multiremote instance by adding your custom driver names, e.g.:
|
||||
|
||||
```ts title=wdio.d.ts
|
||||
declare namespace WebdriverIO {
|
||||
interface MultiRemoteBrowser {
|
||||
myAppiumDriver: WebdriverIO.Browser
|
||||
myChromeDriver: WebdriverIO.Browser
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Now you can access the drivers directly via, e.g.:
|
||||
|
||||
```ts
|
||||
multiremotebrowser.myAppiumDriver.$$(...)
|
||||
multiremotebrowser.myChromeDriver.$(...)
|
||||
```
|
||||
|
|
@ -1,339 +0,0 @@
|
|||
---
|
||||
id: organizingsuites
|
||||
title: Organizing Test Suite
|
||||
---
|
||||
|
||||
As projects grow, inevitably more and more integration tests are added. This increases build time and slows productivity.
|
||||
|
||||
To prevent this, you should run your tests in parallel. WebdriverIO already tests each spec (or _feature file_ in Cucumber) in parallel within a single session. In general, try to test only a single feature per spec file. Try to not have too many or too few tests in one file. (However, there is no golden rule here.)
|
||||
|
||||
Once your tests have several spec files, you should start running your tests concurrently. To do so, adjust the `maxInstances` property in your config file. WebdriverIO allows you to run your tests with maximum concurrency—meaning that no matter how many files and tests you have, they can all run in parallel. (This is still subject to certain limits, like your computer’s CPU, concurrency restrictions, etc.)
|
||||
|
||||
> Let's say you have 3 different capabilities (Chrome, Firefox, and Safari) and you have set `maxInstances` to `1`. The WDIO test runner will spawn 3 processes. Therefore, if you have 10 spec files and you set `maxInstances` to `10`, _all_ spec files will be tested simultaneously, and 30 processes will be spawned.
|
||||
|
||||
You can define the `maxInstances` property globally to set the attribute for all browsers.
|
||||
|
||||
If you run your own WebDriver grid, you may (for example) have more capacity for one browser than another. In that case, you can _limit_ the `maxInstances` in your capability object:
|
||||
|
||||
```js
|
||||
// wdio.conf.js
|
||||
export const config = {
|
||||
// ...
|
||||
// set maxInstance for all browser
|
||||
maxInstances: 10,
|
||||
// ...
|
||||
capabilities: [{
|
||||
browserName: 'firefox'
|
||||
}, {
|
||||
// maxInstances can get overwritten per capability. So if you have an in-house WebDriver
|
||||
// grid with only 5 firefox instance available you can make sure that not more than
|
||||
// 5 instance gets started at a time.
|
||||
browserName: 'chrome'
|
||||
}],
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
## Inherit From Main Config File
|
||||
|
||||
If you run your test suite in multiple environments (e.g., dev and integration) it may help to use multiple configuration files to keep things manageable.
|
||||
|
||||
Similar to the [page object concept](pageobjects), the first thing you’ll need is a main config file. It contains all configurations you share across environments.
|
||||
|
||||
Then create another config file for each environment, and supplement the the main config with the environment-specific ones:
|
||||
|
||||
```js
|
||||
// wdio.dev.config.js
|
||||
import { deepmerge } from 'deepmerge-ts'
|
||||
import wdioConf from './wdio.conf.js'
|
||||
|
||||
// have main config file as default but overwrite environment specific information
|
||||
export const config = deepmerge(wdioConf.config, {
|
||||
capabilities: [
|
||||
// more caps defined here
|
||||
// ...
|
||||
],
|
||||
|
||||
// run tests on sauce instead locally
|
||||
user: process.env.SAUCE_USERNAME,
|
||||
key: process.env.SAUCE_ACCESS_KEY,
|
||||
services: ['sauce']
|
||||
}, { clone: false })
|
||||
|
||||
// add an additional reporter
|
||||
config.reporters.push('allure')
|
||||
```
|
||||
|
||||
## Grouping Test Specs In Suites
|
||||
|
||||
You can group test specs in suites and run single specific suites instead of all of them.
|
||||
|
||||
First, define your suites in your WDIO config:
|
||||
|
||||
```js
|
||||
// wdio.conf.js
|
||||
export const config = {
|
||||
// define all tests
|
||||
specs: ['./test/specs/**/*.spec.js'],
|
||||
// ...
|
||||
// define specific suites
|
||||
suites: {
|
||||
login: [
|
||||
'./test/specs/login.success.spec.js',
|
||||
'./test/specs/login.failure.spec.js'
|
||||
],
|
||||
otherFeature: [
|
||||
// ...
|
||||
]
|
||||
},
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
Now, if you want to only run a single suite, you can pass the suite name as a CLI argument:
|
||||
|
||||
```sh
|
||||
wdio wdio.conf.js --suite login
|
||||
```
|
||||
|
||||
Or, run multiple suites at once:
|
||||
|
||||
```sh
|
||||
wdio wdio.conf.js --suite login --suite otherFeature
|
||||
```
|
||||
|
||||
## Grouping Test Specs To Run Sequentially
|
||||
|
||||
As described above, there are benefits in running the tests concurrently. However, there are cases where it would be beneficial to group tests together to run sequentially in a single instance. Examples of this are mainly where there is a large setup cost e.g. transpiling code or provisioning cloud instances, but there are also advanced usage models that benefit from this capability.
|
||||
|
||||
To group tests to run in a single instance, define them as an array within the specs definition.
|
||||
|
||||
```json
|
||||
"specs": [
|
||||
[
|
||||
"./test/specs/test_login.js",
|
||||
"./test/specs/test_product_order.js",
|
||||
"./test/specs/test_checkout.js"
|
||||
],
|
||||
"./test/specs/test_b*.js",
|
||||
],
|
||||
```
|
||||
In the example above, the tests 'test_login.js', 'test_product_order.js' and 'test_checkout.js' will be run sequentially in a single instance and each of the "test_b*" tests will run concurrently in individual instances.
|
||||
|
||||
It is also possible to group specs defined in suites, so you can now also define suites like this:
|
||||
```json
|
||||
"suites": {
|
||||
end2end: [
|
||||
[
|
||||
"./test/specs/test_login.js",
|
||||
"./test/specs/test_product_order.js",
|
||||
"./test/specs/test_checkout.js"
|
||||
]
|
||||
],
|
||||
allb: ["./test/specs/test_b*.js"]
|
||||
},
|
||||
```
|
||||
and in this case all of the tests of the "end2end" suite would be run in a single instance.
|
||||
|
||||
When running tests sequentially using a pattern, it will run the spec files in an alphabetical order
|
||||
|
||||
```json
|
||||
"suites": {
|
||||
end2end: ["./test/specs/test_*.js"]
|
||||
},
|
||||
```
|
||||
|
||||
This will run the files matching the pattern above in the following order:
|
||||
|
||||
```
|
||||
[
|
||||
"./test/specs/test_checkout.js",
|
||||
"./test/specs/test_login.js",
|
||||
"./test/specs/test_product_order.js"
|
||||
]
|
||||
```
|
||||
|
||||
## Run Selected Tests
|
||||
|
||||
In some cases, you may wish to only execute a single test (or subset of tests) of your suites.
|
||||
|
||||
With the `--spec` parameter, you can specify which _suite_ (Mocha, Jasmine) or _feature_ (Cucumber) should be run. The path is resolved relative from your current working directory.
|
||||
|
||||
For example, to run only your login test:
|
||||
|
||||
```sh
|
||||
wdio wdio.conf.js --spec ./test/specs/e2e/login.js
|
||||
```
|
||||
|
||||
Or run multiple specs at once:
|
||||
|
||||
```sh
|
||||
wdio wdio.conf.js --spec ./test/specs/signup.js --spec ./test/specs/forgot-password.js
|
||||
```
|
||||
|
||||
If the `--spec` value does not point to a particular spec file, it is instead used to filter the spec filenames defined in your configuration.
|
||||
|
||||
To run all specs with the word “dialog” in the spec file names, you could use:
|
||||
|
||||
```sh
|
||||
wdio wdio.conf.js --spec dialog
|
||||
```
|
||||
|
||||
Note that each test file is running in a single test runner process. Since we don't scan files in advance (see the next section for information on piping filenames to `wdio`), you _can't_ use (for example) `describe.only` at the top of your spec file to instruct Mocha to run only that suite.
|
||||
|
||||
This feature will help you to accomplish the same goal.
|
||||
|
||||
When the `--spec` option is provided, it will override any patterns defined by the config or capability level's `specs` parameter.
|
||||
|
||||
## Exclude Selected Tests
|
||||
|
||||
When needed, if you need to exclude particular spec file(s) from a run, you can use the `--exclude` parameter (Mocha, Jasmine) or feature (Cucumber).
|
||||
|
||||
For example, to exclude your login test from the test run:
|
||||
|
||||
```sh
|
||||
wdio wdio.conf.js --exclude ./test/specs/e2e/login.js
|
||||
```
|
||||
|
||||
Or, exclude multiple spec files:
|
||||
|
||||
```sh
|
||||
wdio wdio.conf.js --exclude ./test/specs/signup.js --exclude ./test/specs/forgot-password.js
|
||||
```
|
||||
|
||||
Or, exclude a spec file when filtering using a suite:
|
||||
|
||||
```sh
|
||||
wdio wdio.conf.js --suite login --exclude ./test/specs/e2e/login.js
|
||||
```
|
||||
|
||||
If the `--exclude` value does not point to a particular spec file, it is instead used to filter the spec filenames defined in your configuration.
|
||||
|
||||
To exclude all specs with the word “dialog” in the spec file names, you could use:
|
||||
|
||||
```sh
|
||||
wdio wdio.conf.js --exclude dialog
|
||||
```
|
||||
|
||||
When the `--exclude` option is provided, it will override any patterns defined by the config or capability level's `exclude` parameter.
|
||||
|
||||
## Run Suites and Test Specs
|
||||
|
||||
Run an entire suite along with individual specs.
|
||||
|
||||
```sh
|
||||
wdio wdio.conf.js --suite login --spec ./test/specs/signup.js
|
||||
```
|
||||
|
||||
## Run Multiple, Specific Test Specs
|
||||
|
||||
It is sometimes necessary—in the context of continuous integration and otherwise—to specify multiple sets of specs to run. WebdriverIO's `wdio` command line utility accepts piped-in filenames (from `find`, `grep`, or others).
|
||||
|
||||
Piped-in filenames override the list of globs or filenames specified in the configuration's `spec` list.
|
||||
|
||||
```sh
|
||||
grep -r -l --include "*.js" "myText" | wdio wdio.conf.js
|
||||
```
|
||||
|
||||
_**Note:** This will_ not _override the `--spec` flag for running a single spec._
|
||||
|
||||
## Running Specific Tests with MochaOpts
|
||||
|
||||
You can also filter which specific `suite|describe` and/or `it|test` you want to run by passing a mocha specific argument: `--mochaOpts.grep` to the wdio CLI.
|
||||
|
||||
```sh
|
||||
wdio wdio.conf.js --mochaOpts.grep myText
|
||||
wdio wdio.conf.js --mochaOpts.grep "Text with spaces"
|
||||
```
|
||||
|
||||
_**Note:** Mocha will filter the tests after the WDIO test runner creates the instances, so you might see several instances being spawned but not actually executed._
|
||||
|
||||
## Exclude Specific Tests with MochaOpts
|
||||
|
||||
You can also filter which specific `suite|describe` and/or `it|test` you want to exclude by passing a mocha specific argument: `--mochaOpts.invert` to the wdio CLI. `--mochaOpts.invert` performs opposite of `--mochaOpts.grep`
|
||||
|
||||
```sh
|
||||
wdio wdio.conf.js --mochaOpts.grep "string|regex" --mochaOpts.invert
|
||||
wdio wdio.conf.js --spec ./test/specs/e2e/login.js --mochaOpts.grep "string|regex" --mochaOpts.invert
|
||||
```
|
||||
|
||||
_**Note:** Mocha will filter the tests after the WDIO test runner creates the instances, so you might see several instances being spawned but not actually executed._
|
||||
|
||||
## Stop testing after failure
|
||||
|
||||
With the `bail` option, you can tell WebdriverIO to stop testing after any test fails.
|
||||
|
||||
This is helpful with large test suites when you already know that your build will break, but you want to avoid the lengthy wait of a full testing run.
|
||||
|
||||
The `bail` option expects a number, which specifies how many test failures can occur before WebDriver stop the entire testing run. The default is `0`, meaning that it always runs all tests specs it can find.
|
||||
|
||||
Please see [Options Page](configuration) for additional information on the bail configuration.
|
||||
## Run options hierarchy
|
||||
|
||||
When declaring what specs to run, there is a certain hierarchy defining what pattern will take precedence. Currently, this is how it works, from highest priority to lowest:
|
||||
|
||||
> CLI `--spec` argument > capability `specs` pattern > config `specs` pattern CLI `--exclude` argument > config `exclude` pattern > capability `exclude` pattern
|
||||
|
||||
If only the config parameter is given, it will be used for all capabilities. However, if defining the pattern at the capability level, it will be used instead of the config pattern. Finally, any spec pattern defined on the command line will override all other patterns given.
|
||||
|
||||
### Using capability-defined spec patterns
|
||||
|
||||
When you define a spec pattern at the capability level, it will override any patterns defined at the config level. This is useful when needing to separate tests based on differentiating device capabilities. In cases like this, it is more useful to use a generic spec pattern at the config level, and more specific patterns at the capability level.
|
||||
|
||||
For example, let's say you had two directories, with one for Android tests, and one for iOS tests.
|
||||
|
||||
Your config file may define the pattern as such, for non-specific device tests:
|
||||
|
||||
```js
|
||||
{
|
||||
specs: ['tests/general/**/*.js']
|
||||
}
|
||||
```
|
||||
|
||||
but then, you will have different capabilities for your Android and iOS devices, where the patterns could look like such:
|
||||
|
||||
```json
|
||||
{
|
||||
"platformName": "Android",
|
||||
"specs": [
|
||||
"tests/android/**/*.js"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"platformName": "iOS",
|
||||
"specs": [
|
||||
"tests/ios/**/*.js"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
If you require both of these capabilities in your config file, then the Android device will only run the tests under the "android" namespace, and the iOS tests will run only tests under the "ios" namespace!
|
||||
|
||||
```js
|
||||
//wdio.conf.js
|
||||
export const config = {
|
||||
"specs": [
|
||||
"tests/general/**/*.js"
|
||||
],
|
||||
"capabilities": [
|
||||
{
|
||||
platformName: "Android",
|
||||
specs: ["tests/android/**/*.js"],
|
||||
//...
|
||||
},
|
||||
{
|
||||
platformName: "iOS",
|
||||
specs: ["tests/ios/**/*.js"],
|
||||
//...
|
||||
},
|
||||
{
|
||||
platformName: "Chrome",
|
||||
//config level specs will be used
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
|
|
@ -1,130 +0,0 @@
|
|||
---
|
||||
id: pageobjects
|
||||
title: Page Object Pattern
|
||||
---
|
||||
|
||||
Version 5 of WebdriverIO was designed with Page Object Pattern support in mind. By introducing the "elements as first class citizens" principle, it is now possible to build up large test suites using this pattern.
|
||||
|
||||
There are no additional packages required to create page objects. It turns out that clean, modern classes provide all necessary features we need:
|
||||
|
||||
- inheritance between page objects
|
||||
- lazy loading of elements
|
||||
- encapsulation of methods and actions
|
||||
|
||||
The goal of using page objects is to abstract any page information away from the actual tests. Ideally, you should store all selectors or specific instructions that are unique for a certain page in a page object, so that you still can run your test after you've completely redesigned your page.
|
||||
|
||||
## Making A Page Object
|
||||
|
||||
First off, we need a main page object that we call `Page.js`. It will contain general selectors or methods which all page objects will inherit from.
|
||||
|
||||
```js
|
||||
// Page.js
|
||||
export default class Page {
|
||||
constructor() {
|
||||
this.title = 'My Page'
|
||||
}
|
||||
|
||||
async open (path) {
|
||||
await browser.url(path)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
We will always `export` an instance of a page object, and never create that instance in the test. Since we are writing end-to-end tests, we always consider the page as a stateless construct—just as each HTTP request is a stateless construct.
|
||||
|
||||
Sure, the browser can carry session information and therefore can display different pages based on different sessions, but this shouldn't be reflected within a page object. These sorts of state changes should live in your actual tests.
|
||||
|
||||
Let's start testing the first page. For demo purposes, we use [The Internet](http://the-internet.herokuapp.com) website by [Elemental Selenium](http://elementalselenium.com) as guinea pig. Let's try to build a page object example for the [login page](http://the-internet.herokuapp.com/login).
|
||||
|
||||
## `Get` -ing Your Selectors
|
||||
|
||||
The first step is to write all important selectors that are required in our `login.page` object as getter functions:
|
||||
|
||||
```js
|
||||
// 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()
|
||||
```
|
||||
|
||||
Defining selectors in getter functions might look a little weird, but it’s really useful. These functions are evaluated _when you access the property_, not when you generate the object. With that you always request the element before you do an action on it.
|
||||
|
||||
## Chaining Commands
|
||||
|
||||
WebdriverIO internally remembers the last result of a command. If you chain an element command with an action command, it finds the element from the previous command and uses the result to execute the action. With that you can remove the selector (first parameter) and the command looks as simple as:
|
||||
|
||||
```js
|
||||
await LoginPage.username.setValue('Max Mustermann')
|
||||
```
|
||||
|
||||
Which is basically the same thing as:
|
||||
|
||||
```js
|
||||
let elem = await $('#username')
|
||||
await elem.setValue('Max Mustermann')
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```js
|
||||
await $('#username').setValue('Max Mustermann')
|
||||
```
|
||||
|
||||
## Using Page Objects In Your Tests
|
||||
|
||||
After you've defined the necessary elements and methods for the page, you can start to write the test for it. All you need to do to use the page object is to `import` (or `require`) it. That's it!
|
||||
|
||||
Since you exported an already-created instance of the page object, importing it lets you start using it right away.
|
||||
|
||||
If you use an assertion framework, your tests can be even more expressive:
|
||||
|
||||
```js
|
||||
// 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!')
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
From the structural side, it makes sense to separate spec files and page objects into different directories. Additionally you can give each page object the ending: `.page.js`. This makes it more clear that you import a page object.
|
||||
|
||||
## Going Further
|
||||
|
||||
This is the basic principle of how to write page objects with WebdriverIO. But you can build up way more complex page object structures than this! For example, you might have specific page objects for modals, or split up a huge page object into different classes (each representing a different part of the overall web page) that inherit from the main page object. The pattern really provides a lot of opportunities to separate page information from your tests, which is important to keep your test suite structured and clear in times where the project and number of tests grows.
|
||||
|
||||
You can find this example (and even more page object examples) in the [`example` folder](https://github.com/webdriverio/webdriverio/tree/main/examples/pageobject) on GitHub.
|
||||
|
|
@ -1,185 +0,0 @@
|
|||
---
|
||||
id: parameterize-tests
|
||||
title: Parameterize Tests
|
||||
---
|
||||
|
||||
You can simply parameterize tests on a test level, via simple `for` loops e.g.:
|
||||
|
||||
```ts title=example.spec.js
|
||||
const people = ['Alice', 'Bob']
|
||||
describe('my tests', () => {
|
||||
for (const name of people) {
|
||||
it(`testing with ${name}`, async () => {
|
||||
// ...
|
||||
})
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
or by extracting tests into dynamic functions, e.g.:
|
||||
|
||||
```js title=dynamic.spec.js
|
||||
import { browser } from '@wdio/globals'
|
||||
|
||||
function testComponent(componentName, options) {
|
||||
it(`should test my ${componentName}`, async () => {
|
||||
await browser.url(`/${componentName}`)
|
||||
await expect($('input')).toHaveValue(options.expectedValue)
|
||||
})
|
||||
}
|
||||
|
||||
describe('page components', () => {
|
||||
testComponent('component-a', { expectedValue: 'some expected value' })
|
||||
testComponent('component-b', { expectedValue: 'some other expected value' })
|
||||
})
|
||||
```
|
||||
|
||||
## Passing Environment Variables
|
||||
|
||||
You can use environment variables to configure tests from the command line.
|
||||
|
||||
For example, consider the following test file that needs a username and a password. It is usually a good idea not to store your secrets in the source code, so we'll need a way to pass secrets from outside.
|
||||
|
||||
```ts title=example.spec.ts
|
||||
it(`example test`, async () => {
|
||||
// ...
|
||||
await $('#username').setValue(process.env.USERNAME)
|
||||
await $('#password').setValue(process.env.PASSWORD)
|
||||
})
|
||||
```
|
||||
|
||||
You can run this test with your secret username and password set in the command line.
|
||||
|
||||
<Tabs
|
||||
defaultValue="bash"
|
||||
values={[
|
||||
{label: 'Bash', value: 'bash'},
|
||||
{label: 'Powershell', value: 'powershell'},
|
||||
{label: 'Batch', value: 'batch'},
|
||||
]
|
||||
}>
|
||||
<TabItem value="bash">
|
||||
|
||||
```sh
|
||||
USERNAME=me PASSWORD=secret npx wdio run wdio.conf.js
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="powershell">
|
||||
|
||||
```sh
|
||||
$env:USERNAME=me
|
||||
$env:PASSWORD=secret
|
||||
npx wdio run wdio.conf.js
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="batch">
|
||||
|
||||
```sh
|
||||
set USERNAME=me
|
||||
set PASSWORD=secret
|
||||
npx wdio run wdio.conf.js
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
Similarly, configuration file can also read environment variables passed through the command line.
|
||||
|
||||
```ts title=wdio.config.js
|
||||
export const config = {
|
||||
// ...
|
||||
baseURL: process.env.STAGING === '1'
|
||||
? 'http://staging.example.test/'
|
||||
: 'http://example.test/',
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
Now, you can run tests against a staging or a production environment:
|
||||
|
||||
<Tabs
|
||||
defaultValue="bash"
|
||||
values={[
|
||||
{label: 'Bash', value: 'bash'},
|
||||
{label: 'Powershell', value: 'powershell'},
|
||||
{label: 'Batch', value: 'batch'},
|
||||
]
|
||||
}>
|
||||
<TabItem value="bash">
|
||||
|
||||
```sh
|
||||
STAGING=1 npx wdio run wdio.conf.js
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="powershell">
|
||||
|
||||
```sh
|
||||
$env:STAGING=1
|
||||
npx wdio run wdio.conf.js
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="batch">
|
||||
|
||||
```sh
|
||||
set STAGING=1
|
||||
npx wdio run wdio.conf.js
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## `.env` files
|
||||
|
||||
To make environment variables easier to manage, consider something like `.env` files. WebdriverIO loads `.env` files automatically into your environment. Instead of defining the environment variable as part of the command call, you can define the following `.env`:
|
||||
|
||||
```bash title=".env"
|
||||
# .env file
|
||||
STAGING=0
|
||||
USERNAME=me
|
||||
PASSWORD=secret
|
||||
```
|
||||
|
||||
Run tests as usual, your environment variables should be picked up.
|
||||
|
||||
```sh
|
||||
npx wdio run wdio.conf.js
|
||||
```
|
||||
|
||||
## Create tests via a CSV file
|
||||
|
||||
The WebdriverIO test-runner runs in Node.js, this means you can directly read files from the file system and parse them with your preferred CSV library.
|
||||
|
||||
See for example this CSV file, in our example input.csv:
|
||||
|
||||
```csv
|
||||
"test_case","some_value","some_other_value"
|
||||
"value 1","value 11","foobar1"
|
||||
"value 2","value 22","foobar21"
|
||||
"value 3","value 33","foobar321"
|
||||
"value 4","value 44","foobar4321"
|
||||
```
|
||||
|
||||
Based on this we'll generate some tests by using the csv-parse library from NPM:
|
||||
|
||||
```js title=test.spec.ts
|
||||
import fs from 'node:fs'
|
||||
import path from 'node:path'
|
||||
import { parse } from 'csv-parse/sync'
|
||||
|
||||
const records = parse(fs.readFileSync(path.join(__dirname, 'input.csv')), {
|
||||
columns: true,
|
||||
skip_empty_lines: true
|
||||
})
|
||||
|
||||
describe('my test suite', () => {
|
||||
for (const record of records) {
|
||||
it(`foo: ${record.test_case}`, async () => {
|
||||
console.log(record.test_case, record.some_value, record.some_other_value)
|
||||
})
|
||||
}
|
||||
})
|
||||
```
|
||||
|
|
@ -1,114 +0,0 @@
|
|||
---
|
||||
id: protractor-migration
|
||||
title: From Protractor
|
||||
---
|
||||
|
||||
This tutorial is for people who are using Protractor and want to migrate their framework to WebdriverIO. It was initiated after the Angular team [has announced](https://github.com/angular/protractor/issues/5502) that Protractor won't be longer supported. WebdriverIO has been influenced by a lot of Protractors design decisions which is why it is probably the closest framework to migrate over. The WebdriverIO team appreciates the work of every single Protractor contributor and hopes that this tutorial makes the transition to WebdriverIO easy and straightforward.
|
||||
|
||||
While we would love to have a fully automated process for this the reality looks different. Everyone has a different setup and uses Protractor in different ways. Every step should be seen as guidance and less like a step by step instruction. If you have issues with the migration, don't hesitate to [contact us](https://github.com/webdriverio/codemod/discussions/new).
|
||||
|
||||
## Setup
|
||||
|
||||
The Protractor and WebdriverIO API is actually very similar, to a point where the majority of commands can be rewritten in an automted way through a [codemod](https://github.com/webdriverio/codemod).
|
||||
|
||||
To install the codemod, run:
|
||||
|
||||
```sh
|
||||
npm install jscodeshift @wdio/codemod
|
||||
```
|
||||
|
||||
## Strategy
|
||||
|
||||
There are many migration strategies. Depending on the size of your team, amount of test files and the urgency to migrate you can try to transform all tests at once or file by file. Given that Protractor will continued to be maintained until Angular version 15 (end of 2022) you still have enough time. You can have Protractor and WebdriverIO tests running at the same time and start writing new tests in WebdriverIO. Given your time budget you can then start migrating the important test cases first and work your way down to tests you might even can delete.
|
||||
|
||||
## First the Config File
|
||||
|
||||
After we have installed the codemod we can start transforming the first file. Have a look first into [WebdriverIOs configuration options](configuration). Config files can become very complex and it might make sense to only port the essential parts and see how the rest can be added once the corresponding tests that need certain options are being migrated.
|
||||
|
||||
For the first migration we only transform the config file and run:
|
||||
|
||||
```sh
|
||||
npx jscodeshift -t ./node_modules/@wdio/codemod/protractor ./conf.ts
|
||||
```
|
||||
|
||||
:::info
|
||||
|
||||
Your config can be named differently, however the principle should be the same: start migration the config first.
|
||||
|
||||
:::
|
||||
|
||||
## Install WebdriverIO Dependencies
|
||||
|
||||
Next step is to configure a minimal WebdriverIO setup that we start building up as we migrate from one framework to another. First we install the WebdriverIO CLI via:
|
||||
|
||||
```sh
|
||||
npm install --save-dev @wdio/cli
|
||||
```
|
||||
|
||||
Next we run the configuration wizard:
|
||||
|
||||
```sh
|
||||
npx wdio config
|
||||
```
|
||||
|
||||
This will walk you through a couple of questions. For this migration scenario you:
|
||||
- pick the default choices
|
||||
- we recommend not to auto-generate example files
|
||||
- pick a different folder for WebdriverIO files
|
||||
- and to choose Mocha above Jasmine.
|
||||
|
||||
:::info Why Mocha?
|
||||
Even though you might have been using Protractor with Jasmine before, Mocha however provides better retry mechanisms. The choice is yours!
|
||||
:::
|
||||
|
||||
After the little questionaire the wizard will install all necessary packages and stores them in your `package.json`.
|
||||
|
||||
## Migrate Configuration File
|
||||
|
||||
After we have a transformed `conf.ts` and a new `wdio.conf.ts`, it is now time to migrate the configuration from one config to another. Make sure to only port code that is essential for all tests to be able to run. In ours we port the hook function and framework timeout.
|
||||
|
||||
We will now continue with our `wdio.conf.ts` file only and therefore won't need any changes to the original Protractor config anymore. We can revert those so that both frameworks can run next to each other and we can port on file at the time.
|
||||
|
||||
## Migrate Test File
|
||||
|
||||
We are now set to port over the first test file. To start simple, let's start with one that has not many dependencies to 3rd party packages or other files like PageObjects. In our example the first file to migrate is `first-test.spec.ts`. First create the directory where the new WebdriverIO configuration expects its files and then move it over:
|
||||
|
||||
```sh
|
||||
mv mkdir -p ./test/specs/
|
||||
mv test-suites/first-test.spec.ts ./test/specs
|
||||
```
|
||||
|
||||
Now let's transform this file:
|
||||
|
||||
```sh
|
||||
npx jscodeshift -t ./node_modules/@wdio/codemod/protractor ./test/specs/first-test.spec.ts
|
||||
```
|
||||
|
||||
That's it! This file is so simple that we don't need any additional changes anymore and directly can try to run WebdriverIO via:
|
||||
|
||||
```sh
|
||||
npx wdio run wdio.conf.ts
|
||||
```
|
||||
|
||||
Congratulations 🥳 you just migrated the first file!
|
||||
|
||||
## Next Steps
|
||||
|
||||
From this point you continue to transform test by test and page object by page object. There are chances that the codemod will fail for certain files with an error such as:
|
||||
|
||||
```
|
||||
ERR /path/to/project/test/testdata/failing_submit.js Transformation error (Error transforming /test/testdata/failing_submit.js:2)
|
||||
Error transforming /test/testdata/failing_submit.js:2
|
||||
|
||||
> login_form.submit()
|
||||
^
|
||||
|
||||
The command "submit" is not supported in WebdriverIO. We advise to use the click command to click on the submit button instead. For more information on this configuration, see https://webdriver.io/docs/api/element/click.
|
||||
at /path/to/project/test/testdata/failing_submit.js:132:0
|
||||
```
|
||||
|
||||
For some Protractor commands there is just no replacement for it in WebdriverIO. In this case the codemod will give you some advice how to refactor it. If you stumble upon such error messages too often, feel free to [raise an issue](https://github.com/webdriverio/codemod/issues/new) and request to add a certain transformation. While the codemod already transforms the majority of the Protractor API there is still a lot of room for improvements.
|
||||
|
||||
## Conclusion
|
||||
|
||||
We hope this tutorial guides you a little bit through the migration process to WebdriverIO. The community continues to improve the codemod while testing it with various teams in various organisations. Don't hesitate to [raise an issue](https://github.com/webdriverio/codemod/issues/new) if you have feedback or [start a discussion](https://github.com/webdriverio/codemod/discussions/new) if you struggle during the migration process.
|
||||
|
|
@ -1,69 +0,0 @@
|
|||
---
|
||||
id: proxy
|
||||
title: Proxy Setup
|
||||
---
|
||||
|
||||
You can tunnel two different types of request through a proxy:
|
||||
|
||||
- connection between your test script and the browser driver (or WebDriver endpoint)
|
||||
- connection between the browser and the internet
|
||||
|
||||
## Proxy Between Driver And Test
|
||||
|
||||
If your company has a corporate proxy (e.g. on `http://my.corp.proxy.com:9090`) for all outgoing requests, follow the below steps to install and configure [undici](https://github.com/nodejs/undici).
|
||||
|
||||
### Install undici
|
||||
|
||||
```bash npm2yarn
|
||||
npm install undici --save-dev
|
||||
```
|
||||
|
||||
### Add undici setGlobalDispatcher to your config file
|
||||
|
||||
Add the following require statement to the top of your config file.
|
||||
|
||||
```js title="wdio.conf.js"
|
||||
import { setGlobalDispatcher, ProxyAgent } from 'undici';
|
||||
|
||||
const dispatcher = new ProxyAgent({ uri: new URL(process.env.https_proxy).toString() });
|
||||
setGlobalDispatcher(dispatcher);
|
||||
|
||||
export const config = {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
Additional information about configuring the proxy can be located [here](https://github.com/nodejs/undici/blob/main/docs/docs/api/ProxyAgent.md).
|
||||
|
||||
If you use [Sauce Connect Proxy](https://docs.saucelabs.com/secure-connections/sauce-connect-5), start it via:
|
||||
|
||||
```sh
|
||||
sc -u $SAUCE_USERNAME -k $SAUCE_ACCESS_KEY --no-autodetect -p http://my.corp.proxy.com:9090
|
||||
```
|
||||
|
||||
## Proxy Between Browser And Internet
|
||||
|
||||
In order to tunnel the connection between the browser and the internet, you can set up a proxy which can be useful to (for example) capture network information and other data with tools like [BrowserMob Proxy](https://github.com/lightbody/browsermob-proxy).
|
||||
|
||||
The `proxy` parameters can be applied via the standard capabilities the following way:
|
||||
|
||||
```js title="wdio.conf.js"
|
||||
export const config = {
|
||||
// ...
|
||||
capabilities: [{
|
||||
browserName: 'chrome',
|
||||
// ...
|
||||
proxy: {
|
||||
proxyType: "manual",
|
||||
httpProxy: "corporate.proxy:8080",
|
||||
socksUsername: "codeceptjs",
|
||||
socksPassword: "secret",
|
||||
noProxy: "127.0.0.1,localhost"
|
||||
},
|
||||
// ...
|
||||
}],
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
For more information, see the [WebDriver specification](https://w3c.github.io/webdriver/#proxy).
|
||||
|
|
@ -1,62 +0,0 @@
|
|||
---
|
||||
id: record
|
||||
title: Record Tests
|
||||
---
|
||||
|
||||
Chrome DevTools has a _Recorder_ panel that allows users to record and playback automated steps within Chrome. These steps can be [exported into WebdriverIO tests with an extension](https://chrome.google.com/webstore/detail/webdriverio-chrome-record/pllimkccefnbmghgcikpjkmmcadeddfn?hl=en) making writing test very easy.
|
||||
|
||||
## What is Chrome DevTools Recorder
|
||||
|
||||
The [Chrome DevTools Recorder](https://developer.chrome.com/docs/devtools/recorder/) is a tool which allows you to record and replay test actions directly in the browser and also export them as JSON (or export them in e2e test), as well as measure test performance.
|
||||
|
||||
The tool is straightforward, and since it’s plugged into the browser, we have the convenience of not switching the context or dealing with any third-party tool.
|
||||
|
||||
## How to Record a Test with Chrome DevTools Recorder
|
||||
|
||||
If you have the latest Chrome you will have the Recorder already installed and available for you. Just open any website, do a Right-Click and select _"Inspect"_. Within DevTools you can open the Recorder by pressing `CMD/Control` + `Shift` + `p` and enter _"Show Recorder"_.
|
||||
|
||||

|
||||
|
||||
To start recording a user journey, click on _"Start new recording"_, give your test a name and then use the browser to record your test:
|
||||
|
||||

|
||||
|
||||
Next step, click on _"Replay"_ to check if the recording was successful and does what you wanted to do. If everything is ok, click on the [export](https://developer.chrome.com/docs/devtools/recorder/reference/#recorder-extension) icon and select _"Export as a WebdriverIO Test Script"_:
|
||||
|
||||
The _"Export as a WebdriverIO Test Script"_ option only available if you install the [WebdriverIO Chrome Recorder](https://chrome.google.com/webstore/detail/webdriverio-chrome-record/pllimkccefnbmghgcikpjkmmcadeddfn) extension.
|
||||
|
||||
|
||||

|
||||
|
||||
That's it!
|
||||
|
||||
## Export Recording
|
||||
|
||||
If you exported the flow as WebdriverIO test script, it should download script that you can copy&paste in your test suite. For example the above recording looks as follows:
|
||||
|
||||
```ts
|
||||
describe("My WebdriverIO Test", function () {
|
||||
it("tests My WebdriverIO Test", function () {
|
||||
await browser.setWindowSize(1026, 688)
|
||||
await browser.url("https://webdriver.io/")
|
||||
await browser.$("#__docusaurus > div.main-wrapper > header > div").click()
|
||||
await browser.$("#__docusaurus > nav > div.navbar__inner > div:nth-child(1) > a:nth-child(3)").click()rec
|
||||
await browser.$("#__docusaurus > div.main-wrapper.docs-wrapper.docs-doc-page > div > aside > div > nav > ul > li:nth-child(4) > div > a").click()
|
||||
await browser.$("#__docusaurus > div.main-wrapper.docs-wrapper.docs-doc-page > div > aside > div > nav > ul > li:nth-child(4) > ul > li:nth-child(2) > a").click()
|
||||
await browser.$("#__docusaurus > nav > div.navbar__inner > div.navbar__items.navbar__items--right > div.searchBox_qEbK > button > span.DocSearch-Button-Container > span").click()
|
||||
await browser.$("#docsearch-input").setValue("click")
|
||||
await browser.$("#docsearch-item-0 > a > div > div.DocSearch-Hit-content-wrapper > span").click()
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
Make sure you revisit some of the locators and replace them with more resilient [selector types](/docs/selectors) one if necessary. You can also export the flow as JSON file and use the [`@wdio/chrome-recorder`](https://github.com/webdriverio/chrome-recorder) package to transform it into an actual test script.
|
||||
|
||||
## Next Steps
|
||||
|
||||
You can use this flow to easily create tests for your applications. The Chrome DevTools Recorder has various additional features, e.g.:
|
||||
|
||||
- [Simulate slow network](https://developer.chrome.com/docs/devtools/recorder/#simulate-slow-network) or
|
||||
- [Measure performance of your tests](https://developer.chrome.com/docs/devtools/recorder/#measure)
|
||||
|
||||
Make sure to check out their [docs](https://developer.chrome.com/docs/devtools/recorder).
|
||||
|
|
@ -1,170 +0,0 @@
|
|||
---
|
||||
id: retry
|
||||
title: Retry Flaky Tests
|
||||
---
|
||||
|
||||
You can rerun certain tests with the WebdriverIO testrunner that turn out to be unstable due to things like a flaky network or race conditions. (However, it is not recommended to simply increase the rerun rate if tests become unstable!)
|
||||
|
||||
## Rerun suites in Mocha
|
||||
|
||||
Since version 3 of Mocha, you can rerun whole test suites (everything inside an `describe` block). If you use Mocha you should favor this retry mechanism instead of the WebdriverIO implementation that only allows you to rerun certain test blocks (everything within an `it` block). In order to use the `this.retries()` method, the suite block `describe` must use an unbound function `function(){}` instead of a fat arrow function `() => {}`, as described in [Mocha docs](https://mochajs.org/#arrow-functions). Using Mocha you can also set a retry count for all specs using `mochaOpts.retries` in your `wdio.conf.js`.
|
||||
|
||||
Here is an example:
|
||||
|
||||
```js
|
||||
describe('retries', function () {
|
||||
// Retry all tests in this suite up to 4 times
|
||||
this.retries(4)
|
||||
|
||||
beforeEach(async () => {
|
||||
await browser.url('http://www.yahoo.com')
|
||||
})
|
||||
|
||||
it('should succeed on the 3rd try', async function () {
|
||||
// Specify this test to only retry up to 2 times
|
||||
this.retries(2)
|
||||
console.log('run')
|
||||
await expect($('.foo')).toBeDisplayed()
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
## Rerun single tests in Jasmine or Mocha
|
||||
|
||||
To rerun a certain test block you can just apply the number of reruns as last parameter after the test block function:
|
||||
|
||||
<Tabs
|
||||
defaultValue="mocha"
|
||||
values={[
|
||||
{label: 'Mocha', value: 'mocha'},
|
||||
{label: 'Jasmine', value: 'jasmine'},
|
||||
]
|
||||
}>
|
||||
<TabItem value="mocha">
|
||||
|
||||
```js
|
||||
describe('my flaky app', () => {
|
||||
/**
|
||||
* spec that runs max 4 times (1 actual run + 3 reruns)
|
||||
*/
|
||||
it('should rerun a test at least 3 times', async function () {
|
||||
console.log(this.wdioRetries) // returns number of retries
|
||||
// ...
|
||||
}, 3)
|
||||
})
|
||||
```
|
||||
|
||||
The same works for hooks too:
|
||||
|
||||
```js
|
||||
describe('my flaky app', () => {
|
||||
/**
|
||||
* hook that runs max 2 times (1 actual run + 1 rerun)
|
||||
*/
|
||||
beforeEach(async () => {
|
||||
// ...
|
||||
}, 1)
|
||||
|
||||
// ...
|
||||
})
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="jasmine">
|
||||
|
||||
```js
|
||||
describe('my flaky app', () => {
|
||||
/**
|
||||
* spec that runs max 4 times (1 actual run + 3 reruns)
|
||||
*/
|
||||
it('should rerun a test at least 3 times', async function () {
|
||||
console.log(this.wdioRetries) // returns number of retries
|
||||
// ...
|
||||
}, jasmine.DEFAULT_TIMEOUT_INTERVAL, 3)
|
||||
})
|
||||
```
|
||||
|
||||
The same works for hooks too:
|
||||
|
||||
```js
|
||||
describe('my flaky app', () => {
|
||||
/**
|
||||
* hook that runs max 2 times (1 actual run + 1 rerun)
|
||||
*/
|
||||
beforeEach(async () => {
|
||||
// ...
|
||||
}, jasmine.DEFAULT_TIMEOUT_INTERVAL, 1)
|
||||
|
||||
// ...
|
||||
})
|
||||
```
|
||||
|
||||
If you are using Jasmine, the second parameter is reserved for timeout. To apply a retry parameter you need to set the timeout to its default value `jasmine.DEFAULT_TIMEOUT_INTERVAL` and then apply your retry count.
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
This retry mechanism only allows to retry single hooks or test blocks. If your test is accompanied with a hook to set up your application, this hook is not being run. [Mocha offers](https://mochajs.org/#retry-tests) native test retries that provide this behavior while Jasmine doesn't. You can access the number of executed retries in the `afterTest` hook.
|
||||
|
||||
## Rerunning in Cucumber
|
||||
|
||||
### Rerun full suites in Cucumber
|
||||
|
||||
For cucumber >=6 you can provide the [`retry`](https://github.com/cucumber/cucumber-js/blob/master/docs/cli.md#retry-failing-tests) configuration option along with a `retryTagFilter` optional parameter to have all or some of your failing scenarios get additional retries until succeeded. For this feature to work you need to set the `scenarioLevelReporter` to `true`.
|
||||
|
||||
### Rerun Step Definitions in Cucumber
|
||||
|
||||
To define a rerun rate for a certain step definitions just apply a retry option to it, like:
|
||||
|
||||
```js
|
||||
export default function () {
|
||||
/**
|
||||
* step definition that runs max 3 times (1 actual run + 2 reruns)
|
||||
*/
|
||||
this.Given(/^some step definition$/, { wrapperOptions: { retry: 2 } }, async () => {
|
||||
// ...
|
||||
})
|
||||
// ...
|
||||
})
|
||||
```
|
||||
|
||||
Reruns can only be defined in your step definitions file, never in your feature file.
|
||||
|
||||
## Add retries on a per-specfile basis
|
||||
|
||||
Previously, only test- and suite-level retries were available, which are fine in most cases.
|
||||
|
||||
But in any tests which involve state (such as on a server or in a database) the state may be left invalid after the first test failure. Any subsequent retries may have no chance of passing, due to the invalid state they would start with.
|
||||
|
||||
A new `browser` instance is created for each specfile, which makes this an ideal place to hook and setup any other states (server, databases). Retries on this level mean that the whole setup process will simply be repeated, just as if it were for a new specfile.
|
||||
|
||||
```js title="wdio.conf.js"
|
||||
export const config = {
|
||||
// ...
|
||||
/**
|
||||
* The number of times to retry the entire specfile when it fails as a whole
|
||||
*/
|
||||
specFileRetries: 1,
|
||||
/**
|
||||
* Delay in seconds between the spec file retry attempts
|
||||
*/
|
||||
specFileRetriesDelay: 0,
|
||||
/**
|
||||
* Retried specfiles are inserted at the beginning of the queue and retried immediately
|
||||
*/
|
||||
specFileRetriesDeferred: false
|
||||
}
|
||||
```
|
||||
|
||||
## Run a specific test multiple times
|
||||
|
||||
This is to help prevent flaky tests from being introduced in a codebase. By adding the `--repeat` cli option it will run the specified specs or suites N times. When using this cli flag the `--spec` or `--suite` flag must also be specified.
|
||||
|
||||
When adding new tests to a codebase, especially through a CI/CD process the tests could pass and get merged but become flaky later on. This flakiness could come from a number of things like network issues, server load, database size, etc. Using the `--repeat` flag in your CD/CD process can help catch these flaky tests before they get merged to a main codebase.
|
||||
|
||||
One strategy to use is run your tests like regular in your CI/CD process but if you're introducing a new test you can then run another set of tests with the new spec specified in `--spec` along with `--repeat` so it runs the new test x number of times. If the test fails any of those times then the test will not get merged and will need to be looked at why it failed.
|
||||
|
||||
```sh
|
||||
# This will run the example.e2e.js spec 5 times
|
||||
npx wdio run ./wdio.conf.js --spec example.e2e.js --repeat 5
|
||||
```
|
||||
|
|
@ -1,256 +0,0 @@
|
|||
---
|
||||
id: runner
|
||||
title: Runner
|
||||
---
|
||||
|
||||
import CodeBlock from '@theme/CodeBlock';
|
||||
|
||||
A runner in WebdriverIO orchestrates how and where tests are being run when using the testrunner. WebdriverIO currently supports two different types of runner: local and browser runner.
|
||||
|
||||
## Local Runner
|
||||
|
||||
The [Local Runner](https://www.npmjs.com/package/@wdio/local-runner) initiates your framework (e.g. Mocha, Jasmine or Cucumber) within worker a process and runs all your test files within your Node.js environment. Every test file is being run in a separate worker process per capability allowing for maximum concurrency. Every worker process uses a single browser instance and therefore runs its own browser session allowing for maximum isolation.
|
||||
|
||||
Given every test is run in its own isolated process, it is not possible to share data across test files. There are two ways to work around this:
|
||||
|
||||
- use the [`@wdio/shared-store-service`](https://www.npmjs.com/package/@wdio/shared-store-service) to share data across all workers
|
||||
- group spec files (read more in [Organizing Test Suite](https://webdriver.io/docs/organizingsuites#grouping-test-specs-to-run-sequentially))
|
||||
|
||||
If nothing else is defined in the `wdio.conf.js` the Local Runner is the default runner in WebdriverIO.
|
||||
|
||||
### Install
|
||||
|
||||
To use the Local Runner you can install it via:
|
||||
|
||||
```sh
|
||||
npm install --save-dev @wdio/local-runner
|
||||
```
|
||||
|
||||
### Setup
|
||||
|
||||
The Local Runner is the default runner in WebdriverIO so there is no need to define it within your `wdio.conf.js`. If you want to explicitly set it, you can define it as follows:
|
||||
|
||||
```js
|
||||
// wdio.conf.js
|
||||
export const {
|
||||
// ...
|
||||
runner: 'local',
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
## Browser Runner
|
||||
|
||||
As opposed to the [Local Runner](https://www.npmjs.com/package/@wdio/local-runner) the [Browser Runner](https://www.npmjs.com/package/@wdio/browser-runner) initiates and executes the framework within the browser. This allows you to run unit tests or component tests in an actual browser rather than in a JSDOM like many other test frameworks.
|
||||
|
||||
While [JSDOM](https://www.npmjs.com/package/jsdom) is widely used for testing purposes, it is in the end not an actual browser nor can you emulate mobile environments with it. With this runner WebdriverIO enables you to easily run your tests in the browser and use WebDriver commands to interact with elements rendered on the page.
|
||||
|
||||
Here is an overview of running tests within JSDOM vs. WebdriverIOs Browser Runner
|
||||
|
||||
| | JSDOM | WebdriverIO Browser Runner |
|
||||
| -- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- |
|
||||
| 1. | Runs your tests within Node.js using a re-implementation of web standards, notably the WHATWG DOM and HTML Standards | Executes your test in an actual browser and runs the code in an environment that your users use |
|
||||
| 2. | Interactions with components can only be imitated via JavaScript | You can use the [WebdriverIO API](api) to interact with elements through the WebDriver protocol |
|
||||
| 3. | Canvas support requires [additional dependencies](https://www.npmjs.com/package/canvas) and [has limitations](https://github.com/Automattic/node-canvas/issues) | You have access to the real [Canvas API](https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API) |
|
||||
| 4. | JSDOM has some [caveats](https://github.com/jsdom/jsdom#caveats) and unsupported Web APIs | All Web APIs are supported as test run in an actual browser |
|
||||
| 5. | Impossible to detect errors cross browser | Support for all browsers including mobile browser |
|
||||
| 6. | Can __not__ test for element pseudo states | Support for pseudo states such as `:hover` or `:active` |
|
||||
|
||||
This runner uses [Vite](https://vitejs.dev/) to compile your test code and load it in the browser. It comes with presets for the following component frameworks:
|
||||
|
||||
- React
|
||||
- Preact
|
||||
- Vue.js
|
||||
- Svelte
|
||||
- SolidJS
|
||||
- Stencil
|
||||
|
||||
Every test file / test file group runs within a single page which means that between each test the page is being reloaded to guarantee isolation between tests.
|
||||
|
||||
### Install
|
||||
|
||||
To use the Browser Runner you can install it via:
|
||||
|
||||
```sh
|
||||
npm install --save-dev @wdio/browser-runner
|
||||
```
|
||||
|
||||
### Setup
|
||||
|
||||
To use the Browser runner, you have to define a `runner` property within your `wdio.conf.js` file, e.g.:
|
||||
|
||||
```js
|
||||
// wdio.conf.js
|
||||
export const {
|
||||
// ...
|
||||
runner: 'browser',
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
### Runner Options
|
||||
|
||||
The Browser runner allows following configurations:
|
||||
|
||||
#### `preset`
|
||||
|
||||
If you test components using one of the mentioned frameworks above, you can define a preset that ensures everything is configured out of the box. This option can't be used together with `viteConfig`.
|
||||
|
||||
__Type:__ `vue` | `svelte` | `solid` | `react` | `preact` | `stencil`<br /> __Example:__
|
||||
|
||||
```js title="wdio.conf.js"
|
||||
export const {
|
||||
// ...
|
||||
runner: ['browser', {
|
||||
preset: 'svelte'
|
||||
}],
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
#### `viteConfig`
|
||||
|
||||
Define your own [Vite configuration](https://vitejs.dev/config/). You can either pass in a custom object or import an existing `vite.conf.ts` file if you use Vite.js for development. Note that WebdriverIO keeps custom Vite configurations to set up the test harness.
|
||||
|
||||
__Type:__ `string` or [`UserConfig`](https://github.com/vitejs/vite/blob/52e64eb43287d241f3fd547c332e16bd9e301e95/packages/vite/src/node/config.ts#L119-L272) or `(env: ConfigEnv) => UserConfig | Promise<UserConfig>`<br /> __Example:__
|
||||
|
||||
```js title="wdio.conf.ts"
|
||||
import viteConfig from '../vite.config.ts'
|
||||
|
||||
export const {
|
||||
// ...
|
||||
runner: ['browser', { viteConfig }],
|
||||
// or just:
|
||||
runner: ['browser', { viteConfig: '../vites.config.ts' }],
|
||||
// or use a function if your vite config contains a lot of plugins
|
||||
// which you only want to resolve when value is read
|
||||
runner: ['browser', {
|
||||
viteConfig: () => ({
|
||||
// ...
|
||||
})
|
||||
}],
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
#### `headless`
|
||||
|
||||
If set to `true` the runner will update capabilities to run tests headless. By default this is enabled within CI environments where a `CI` environment variable is set to `'1'` or `'true'`.
|
||||
|
||||
__Type:__ `boolean`<br /> __Default:__ `false`, set to `true` if `CI` environment variable is set
|
||||
|
||||
#### `rootDir`
|
||||
|
||||
Project root directory.
|
||||
|
||||
__Type:__ `string`<br /> __Default:__ `process.cwd()`
|
||||
|
||||
#### `coverage`
|
||||
|
||||
WebdriverIO supports test coverage reporting through [`istanbul`](https://istanbul.js.org/). See [Coverage Options](#coverage-options) for more details.
|
||||
|
||||
__Type:__ `object`<br /> __Default:__ `undefined`
|
||||
|
||||
### Coverage Options
|
||||
|
||||
The following options allow to configure coverage reporting.
|
||||
|
||||
#### `enabled`
|
||||
|
||||
Enables coverage collection.
|
||||
|
||||
__Type:__ `boolean`<br /> __Default:__ `false`
|
||||
|
||||
#### `include`
|
||||
|
||||
List of files included in coverage as glob patterns.
|
||||
|
||||
__Type:__ `string[]`<br /> __Default:__ `[**]`
|
||||
|
||||
#### `exclude`
|
||||
|
||||
List of files excluded in coverage as glob patterns.
|
||||
|
||||
__Type:__ `string[]`<br /> __Default:__
|
||||
|
||||
```
|
||||
[
|
||||
'coverage/**',
|
||||
'dist/**',
|
||||
'packages/*/test{,s}/**',
|
||||
'**/*.d.ts',
|
||||
'cypress/**',
|
||||
'test{,s}/**',
|
||||
'test{,-*}.{js,cjs,mjs,ts,tsx,jsx}',
|
||||
'**/*{.,-}test.{js,cjs,mjs,ts,tsx,jsx}',
|
||||
'**/*{.,-}spec.{js,cjs,mjs,ts,tsx,jsx}',
|
||||
'**/__tests__/**',
|
||||
'**/{karma,rollup,webpack,vite,vitest,jest,ava,babel,nyc,cypress,tsup,build}.config.*',
|
||||
'**/.{eslint,mocha,prettier}rc.{js,cjs,yml}',
|
||||
]
|
||||
```
|
||||
|
||||
#### `extension`
|
||||
|
||||
List of file extensions the report should include.
|
||||
|
||||
__Type:__ `string | string[]`<br /> __Default:__ `['.js', '.cjs', '.mjs', '.ts', '.mts', '.cts', '.tsx', '.jsx', '.vue', '.svelte']`
|
||||
|
||||
#### `reportsDirectory`
|
||||
|
||||
Directory to write coverage report to.
|
||||
|
||||
__Type:__ `string`<br /> __Default:__ `./coverage`
|
||||
|
||||
#### `reporter`
|
||||
|
||||
Coverage reporters to use. See [istanbul documentation](https://istanbul.js.org/docs/advanced/alternative-reporters/) for detailed list of all reporters.
|
||||
|
||||
__Type:__ `string[]`<br /> __Default:__ `['text', 'html', 'clover', 'json-summary']`
|
||||
|
||||
#### `perFile`
|
||||
|
||||
Check thresholds per file. See `lines`, `functions`, `branches` and `statements` for the actual thresholds.
|
||||
|
||||
__Type:__ `boolean`<br /> __Default:__ `false`
|
||||
|
||||
#### `clean`
|
||||
|
||||
Clean coverage results before running tests.
|
||||
|
||||
__Type:__ `boolean`<br /> __Default:__ `true`
|
||||
|
||||
#### `lines`
|
||||
|
||||
Threshold for lines.
|
||||
|
||||
__Type:__ `number`<br /> __Default:__ `undefined`
|
||||
|
||||
#### `functions`
|
||||
|
||||
Threshold for functions.
|
||||
|
||||
__Type:__ `number`<br /> __Default:__ `undefined`
|
||||
|
||||
#### `branches`
|
||||
|
||||
Threshold for branches.
|
||||
|
||||
__Type:__ `number`<br /> __Default:__ `undefined`
|
||||
|
||||
#### `statements`
|
||||
|
||||
Threshold for statements.
|
||||
|
||||
__Type:__ `number`<br /> __Default:__ `undefined`
|
||||
|
||||
### Limitations
|
||||
|
||||
When using the WebdriverIO browser runner, it's important to note that thread blocking dialogs like `alert` or `confirm` cannot be used natively. This is because they block the web page, which means WebdriverIO cannot continue communicating with the page, causing the execution to hang.
|
||||
|
||||
In such situations, WebdriverIO provides default mocks with default returned values for these APIs. This ensures that if the user accidentally uses synchronous popup web APIs, the execution would not hang. However, it's still recommended for the user to mock these web APIs for better experience. Read more in [Mocking](/docs/component-testing/mocking).
|
||||
|
||||
### Examples
|
||||
|
||||
Make sure to check out the docs around [component testing](https://webdriver.io/docs/component-testing) and have a look into the [example repository](https://github.com/webdriverio/component-testing-examples) for examples using these and various other frameworks.
|
||||
|
||||
|
|
@ -1,599 +0,0 @@
|
|||
---
|
||||
id: selectors
|
||||
title: Selectors
|
||||
---
|
||||
|
||||
The [WebDriver Protocol](https://w3c.github.io/webdriver/) provides several selector strategies to query an element. WebdriverIO simplifies them to keep selecting elements simple. Please note that even though the command to query elements is called `$` and `$$`, they have nothing to do with jQuery or the [Sizzle Selector Engine](https://github.com/jquery/sizzle).
|
||||
|
||||
While there are so many different selectors available, only a few of them provide a resilient way to find the right element. For example, given the following button:
|
||||
|
||||
```html
|
||||
<button
|
||||
id="main"
|
||||
class="btn btn-large"
|
||||
name="submission"
|
||||
role="button"
|
||||
data-testid="submit"
|
||||
>
|
||||
Submit
|
||||
</button>
|
||||
```
|
||||
|
||||
We __do__ and __do not__ recommend the following selectors:
|
||||
|
||||
| Selector | Recommended | Notes |
|
||||
| --------------------------------------------- | ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `$('button')` | 🚨 Never | Worst - too generic, no context. |
|
||||
| `$('.btn.btn-large')` | 🚨 Never | Bad. Coupled to styling. Highly subject to change. |
|
||||
| `$('#main')` | ⚠️ Sparingly | Better. But still coupled to styling or JS event listeners. |
|
||||
| `$(() => document.queryElement('button'))` | ⚠️ Sparingly | Effective querying, complex to write. |
|
||||
| `$('button[name="submission"]')` | ⚠️ Sparingly | Coupled to the `name` attribute which has HTML semantics. |
|
||||
| `$('button[data-testid="submit"]')` | ✅ Good | Requires additional attribute, not connected to a11y. |
|
||||
| `$('aria/Submit')` or `$('button=Submit')` | ✅ Always | Best. Resembles how the user interacts with the page. It is recommended to use your frontend's translation files so your tests never fail when the translations are updated |
|
||||
|
||||
## CSS Query Selector
|
||||
|
||||
If not indicated otherwise, WebdriverIO will query elements using the [CSS selector](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors) pattern, e.g.:
|
||||
|
||||
```js reference useHTTPS
|
||||
https://github.com/webdriverio/example-recipes/blob/e8b147e88e7a38351b0918b4f7efbd9ae292201d/selectors/example.js#L7-L8
|
||||
```
|
||||
|
||||
## Link Text
|
||||
|
||||
To get an anchor element with a specific text in it, query the text starting with an equals (`=`) sign.
|
||||
|
||||
For example:
|
||||
|
||||
```html reference
|
||||
https://github.com/webdriverio/example-recipes/blob/e8b147e88e7a38351b0918b4f7efbd9ae292201d/selectors/example.html#L3
|
||||
```
|
||||
|
||||
You can query this element by calling:
|
||||
|
||||
```js reference useHTTPS
|
||||
https://github.com/webdriverio/example-recipes/blob/e8b147e88e7a38351b0918b4f7efbd9ae292201d/selectors/example.js#L16-L18
|
||||
```
|
||||
|
||||
## Partial Link Text
|
||||
|
||||
To find a anchor element whose visible text partially matches your search value, query it by using `*=` in front of the query string (e.g. `*=driver`).
|
||||
|
||||
You can query the element from the example above by also calling:
|
||||
|
||||
```js reference useHTTPS
|
||||
https://github.com/webdriverio/example-recipes/blob/e8b147e88e7a38351b0918b4f7efbd9ae292201d/selectors/example.js#L24-L26
|
||||
```
|
||||
|
||||
__Note:__ You can't mix multiple selector strategies in one selector. Use multiple chained element queries to reach the same goal, e.g.:
|
||||
|
||||
```js
|
||||
const elem = await $('header h1*=Welcome') // doesn't work!!!
|
||||
// use instead
|
||||
const elem = await $('header').$('*=driver')
|
||||
```
|
||||
|
||||
## Element with certain text
|
||||
|
||||
The same technique can be applied to elements as well. Additionally, it is also possible to do a case-insensitive matching using `.=` or `.*=` within the query.
|
||||
|
||||
For example, here's a query for a level 1 heading with the text "Welcome to my Page":
|
||||
|
||||
```html reference
|
||||
https://github.com/webdriverio/example-recipes/blob/e8b147e88e7a38351b0918b4f7efbd9ae292201d/selectors/example.html#L2
|
||||
```
|
||||
|
||||
You can query this element by calling:
|
||||
|
||||
```js reference useHTTPS
|
||||
https://github.com/webdriverio/example-recipes/blob/13eddfac6f18a2a4812cc09ed7aa5e468f392060/selectors/example.js#L35C1-L38
|
||||
```
|
||||
|
||||
Or using query partial text:
|
||||
|
||||
```js reference useHTTPS
|
||||
https://github.com/webdriverio/example-recipes/blob/13eddfac6f18a2a4812cc09ed7aa5e468f392060/selectors/example.js#L44C9-L47
|
||||
```
|
||||
|
||||
The same works for `id` and `class` names:
|
||||
|
||||
```html reference
|
||||
https://github.com/webdriverio/example-recipes/blob/e8b147e88e7a38351b0918b4f7efbd9ae292201d/selectors/example.html#L4
|
||||
```
|
||||
|
||||
You can query this element by calling:
|
||||
|
||||
```js reference useHTTPS
|
||||
https://github.com/webdriverio/example-recipes/blob/13eddfac6f18a2a4812cc09ed7aa5e468f392060/selectors/example.js#L49-L67
|
||||
```
|
||||
|
||||
__Note:__ You can't mix multiple selector strategies in one selector. Use multiple chained element queries to reach the same goal, e.g.:
|
||||
|
||||
```js
|
||||
const elem = await $('header h1*=Welcome') // doesn't work!!!
|
||||
// use instead
|
||||
const elem = await $('header').$('h1*=Welcome')
|
||||
```
|
||||
|
||||
## Tag Name
|
||||
|
||||
To query an element with a specific tag name, use `<tag>` or `<tag />`.
|
||||
|
||||
```html reference
|
||||
https://github.com/webdriverio/example-recipes/blob/e8b147e88e7a38351b0918b4f7efbd9ae292201d/selectors/example.html#L5
|
||||
```
|
||||
|
||||
You can query this element by calling:
|
||||
|
||||
```js reference useHTTPS
|
||||
https://github.com/webdriverio/example-recipes/blob/e8b147e88e7a38351b0918b4f7efbd9ae292201d/selectors/example.js#L61-L62
|
||||
```
|
||||
|
||||
## Name Attribute
|
||||
|
||||
For querying elements with a specific name attribute you can either use a normal CSS3 selector or the provided name strategy from the [JSONWireProtocol](https://github.com/SeleniumHQ/selenium/wiki/JsonWireProtocol) by passing something like [name="some-name"] as selector parameter:
|
||||
|
||||
```html reference
|
||||
https://github.com/webdriverio/example-recipes/blob/e8b147e88e7a38351b0918b4f7efbd9ae292201d/selectors/example.html#L6
|
||||
```
|
||||
|
||||
```js reference useHTTPS
|
||||
https://github.com/webdriverio/example-recipes/blob/e8b147e88e7a38351b0918b4f7efbd9ae292201d/selectors/example.js#L68-L69
|
||||
```
|
||||
|
||||
__Note:__ This selector strategy it deprecated and only works in old browser that are run by the JSONWireProtocol protocol or by using Appium.
|
||||
|
||||
## xPath
|
||||
|
||||
It is also possible to query elements via a specific [xPath](https://developer.mozilla.org/en-US/docs/Web/XPath).
|
||||
|
||||
An xPath selector has a format like `//body/div[6]/div[1]/span[1]`.
|
||||
|
||||
```html reference
|
||||
https://github.com/webdriverio/example-recipes/blob/e8b147e88e7a38351b0918b4f7efbd9ae292201d/selectors/xpath.html
|
||||
```
|
||||
|
||||
You can query the second paragraph by calling:
|
||||
|
||||
```js reference useHTTPS
|
||||
https://github.com/webdriverio/example-recipes/blob/e8b147e88e7a38351b0918b4f7efbd9ae292201d/selectors/example.js#L75-L76
|
||||
```
|
||||
|
||||
You can use xPath to also traverse up and down the DOM tree:
|
||||
|
||||
```js reference useHTTPS
|
||||
https://github.com/webdriverio/example-recipes/blob/e8b147e88e7a38351b0918b4f7efbd9ae292201d/selectors/example.js#L78-L79
|
||||
```
|
||||
|
||||
## Accessibility Name Selector
|
||||
|
||||
Query elements by their accessible name. The accessible name is what is announced by a screen reader when that element receives focus. The value of the accessible name can be both visual content or hidden text alternatives.
|
||||
|
||||
:::info
|
||||
|
||||
You can read more about this selector in our [release blog post](/blog/2022/09/05/accessibility-selector)
|
||||
|
||||
:::
|
||||
|
||||
### Fetch by `aria-label`
|
||||
|
||||
```html reference
|
||||
https://github.com/webdriverio/example-recipes/blob/e8b147e88e7a38351b0918b4f7efbd9ae292201d/selectors/aria.html#L1
|
||||
```
|
||||
|
||||
```js reference useHTTPS
|
||||
https://github.com/webdriverio/example-recipes/blob/e8b147e88e7a38351b0918b4f7efbd9ae292201d/selectors/example.js#L86-L87
|
||||
```
|
||||
|
||||
### Fetch by `aria-labelledby`
|
||||
|
||||
```html reference
|
||||
https://github.com/webdriverio/example-recipes/blob/e8b147e88e7a38351b0918b4f7efbd9ae292201d/selectors/aria.html#L2-L3
|
||||
```
|
||||
|
||||
```js reference useHTTPS
|
||||
https://github.com/webdriverio/example-recipes/blob/e8b147e88e7a38351b0918b4f7efbd9ae292201d/selectors/example.js#L93-L94
|
||||
```
|
||||
|
||||
### Fetch by content
|
||||
|
||||
```html reference
|
||||
https://github.com/webdriverio/example-recipes/blob/e8b147e88e7a38351b0918b4f7efbd9ae292201d/selectors/aria.html#L4
|
||||
```
|
||||
|
||||
```js reference useHTTPS
|
||||
https://github.com/webdriverio/example-recipes/blob/e8b147e88e7a38351b0918b4f7efbd9ae292201d/selectors/example.js#L100-L101
|
||||
```
|
||||
|
||||
### Fetch by title
|
||||
|
||||
```html reference
|
||||
https://github.com/webdriverio/example-recipes/blob/e8b147e88e7a38351b0918b4f7efbd9ae292201d/selectors/aria.html#L5
|
||||
```
|
||||
|
||||
```js reference useHTTPS
|
||||
https://github.com/webdriverio/example-recipes/blob/e8b147e88e7a38351b0918b4f7efbd9ae292201d/selectors/example.js#L107-L108
|
||||
```
|
||||
|
||||
### Fetch by `alt` property
|
||||
|
||||
```html reference
|
||||
https://github.com/webdriverio/example-recipes/blob/e8b147e88e7a38351b0918b4f7efbd9ae292201d/selectors/aria.html#L6
|
||||
```
|
||||
|
||||
```js reference useHTTPS
|
||||
https://github.com/webdriverio/example-recipes/blob/e8b147e88e7a38351b0918b4f7efbd9ae292201d/selectors/example.js#L114-L115
|
||||
```
|
||||
|
||||
## ARIA - Role Attribute
|
||||
|
||||
For querying elements based on [ARIA roles](https://www.w3.org/TR/html-aria/#docconformance), you can directly specify role of the element like `[role=button]` as selector parameter:
|
||||
|
||||
```html reference
|
||||
https://github.com/webdriverio/example-recipes/blob/e8b147e88e7a38351b0918b4f7efbd9ae292201d/selectors/aria.html#L13
|
||||
```
|
||||
|
||||
```js reference useHTTPS
|
||||
https://github.com/webdriverio/example-recipes/blob/e8b147e88e7a38351b0918b4f7efbd9ae292201d/selectors/example.js#L131-L132
|
||||
```
|
||||
|
||||
## ID Attribute
|
||||
|
||||
Locator strategy "id" is not supported in WebDriver protocol, one should use either CSS or xPath selector strategies instead to find elements using ID.
|
||||
|
||||
However some drivers (e.g. [Appium You.i Engine Driver](https://github.com/YOU-i-Labs/appium-youiengine-driver#selector-strategies)) might still [support](https://github.com/YOU-i-Labs/appium-youiengine-driver#selector-strategies) this selector.
|
||||
|
||||
Current supported selector syntaxes for ID are:
|
||||
|
||||
```js
|
||||
//css locator
|
||||
const button = await $('#someid')
|
||||
//xpath locator
|
||||
const button = await $('//*[@id="someid"]')
|
||||
//id strategy
|
||||
// Note: works only in Appium or similar frameworks which supports locator strategy "ID"
|
||||
const button = await $('id=resource-id/iosname')
|
||||
```
|
||||
|
||||
## JS Function
|
||||
|
||||
You can also use JavaScript functions to fetch elements using web native APIs. Of course, you can only do this inside a web context (e.g., `browser`, or web context in mobile).
|
||||
|
||||
Given the following HTML structure:
|
||||
|
||||
```html reference
|
||||
https://github.com/webdriverio/example-recipes/blob/e8b147e88e7a38351b0918b4f7efbd9ae292201d/selectors/js.html
|
||||
```
|
||||
|
||||
You can query the sibling element of `#elem` as follows:
|
||||
|
||||
```js reference useHTTPS
|
||||
https://github.com/webdriverio/example-recipes/blob/e8b147e88e7a38351b0918b4f7efbd9ae292201d/selectors/example.js#L139-L143
|
||||
```
|
||||
|
||||
## Deep Selectors
|
||||
|
||||
:::warning
|
||||
|
||||
Starting with `v9` of WebdriverIO there is no need for this special selector as WebdriverIO automatically pierces through the Shadow DOM for you. It is recommended to migrate off this selector by removing the `>>>` in front it.
|
||||
|
||||
:::
|
||||
|
||||
Many frontend applications heavily rely on elements with [shadow DOM](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_shadow_DOM). It is technically impossible to query elements within the shadow DOM without workarounds. The [`shadow$`](https://webdriver.io/docs/api/element/shadow$) and [`shadow$$`](https://webdriver.io/docs/api/element/shadow$$) have been such workarounds that had their [limitations](https://github.com/Georgegriff/query-selector-shadow-dom#how-is-this-different-to-shadow). With the deep selector you can now query all elements within any shadow DOM using the common query command.
|
||||
|
||||
Given we have an application with the following structure:
|
||||
|
||||

|
||||
|
||||
With this selector you can query the `<button />` element that is nested within another shadow DOM, e.g.:
|
||||
|
||||
```js reference useHTTPS
|
||||
https://github.com/webdriverio/example-recipes/blob/e8b147e88e7a38351b0918b4f7efbd9ae292201d/selectors/example.js#L147-L149
|
||||
```
|
||||
|
||||
## Mobile Selectors
|
||||
|
||||
For hybrid mobile testing, it's important that the automation server is in the correct *context* before executing commands. For automating gestures, the driver ideally should be set to native context. But to select elements from the DOM, the driver will need to be set to the platform's webview context. Only *then* can the methods mentioned above can be used.
|
||||
|
||||
For native mobile testing, there is no switching between contexts, as you have to use mobile strategies and use the underlying device automation technology directly. This is especially useful when a test needs some fine-grained control over finding elements.
|
||||
|
||||
### Android UiAutomator
|
||||
|
||||
Android’s UI Automator framework provides a number of ways to find elements. You can use the [UI Automator API](https://developer.android.com/tools/testing-support-library/index.html#uia-apis), in particular the [UiSelector class](https://developer.android.com/reference/androidx/test/uiautomator/UiSelector) to locate elements. In Appium you send the Java code, as a string, to the server, which executes it in the application’s environment, returning the element or elements.
|
||||
|
||||
```js
|
||||
const selector = 'new UiSelector().text("Cancel").className("android.widget.Button")'
|
||||
const button = await $(`android=${selector}`)
|
||||
await button.click()
|
||||
```
|
||||
|
||||
### Android DataMatcher and ViewMatcher (Espresso only)
|
||||
|
||||
Android's DataMatcher strategy provides a way to find elements by [Data Matcher](https://developer.android.com/reference/android/support/test/espresso/DataInteraction)
|
||||
|
||||
```js
|
||||
const menuItem = await $({
|
||||
"name": "hasEntry",
|
||||
"args": ["title", "ViewTitle"]
|
||||
})
|
||||
await menuItem.click()
|
||||
```
|
||||
|
||||
And similarly [View Matcher](https://developer.android.com/reference/android/support/test/espresso/ViewInteraction)
|
||||
|
||||
```js
|
||||
const menuItem = await $({
|
||||
"name": "hasEntry",
|
||||
"args": ["title", "ViewTitle"],
|
||||
"class": "androidx.test.espresso.matcher.ViewMatchers"
|
||||
})
|
||||
await menuItem.click()
|
||||
```
|
||||
|
||||
### Android View Tag (Espresso only)
|
||||
|
||||
The view tag strategy provides a convenient way to find elements by their [tag](https://developer.android.com/reference/android/support/test/espresso/matcher/ViewMatchers.html#withTagValue%28org.hamcrest.Matcher%3Cjava.lang.Object%3E%29).
|
||||
|
||||
```js
|
||||
const elem = await $('-android viewtag:tag_identifier')
|
||||
await elem.click()
|
||||
```
|
||||
|
||||
### iOS UIAutomation
|
||||
|
||||
When automating an iOS application, Apple’s [UI Automation framework](https://developer.apple.com/library/prerelease/tvos/documentation/DeveloperTools/Conceptual/InstrumentsUserGuide/UIAutomation.html) can be used to find elements.
|
||||
|
||||
This JavaScript [API](https://developer.apple.com/library/ios/documentation/DeveloperTools/Reference/UIAutomationRef/index.html#//apple_ref/doc/uid/TP40009771) has methods to access to the view and everything on it.
|
||||
|
||||
```js
|
||||
const selector = 'UIATarget.localTarget().frontMostApp().mainWindow().buttons()[0]'
|
||||
const button = await $(`ios=${selector}`)
|
||||
await button.click()
|
||||
```
|
||||
|
||||
You can also use predicate searching within iOS UI Automation in Appium to refine element selection even further. See [here](https://github.com/appium/appium/blob/master/docs/en/writing-running-appium/ios/ios-predicate.md) for details.
|
||||
|
||||
### iOS XCUITest predicate strings and class chains
|
||||
|
||||
With iOS 10 and above (using the `XCUITest` driver), you can use [predicate strings](https://github.com/facebook/WebDriverAgent/wiki/Predicate-Queries-Construction-Rules):
|
||||
|
||||
```js
|
||||
const selector = `type == 'XCUIElementTypeSwitch' && name CONTAINS 'Allow'`
|
||||
const switch = await $(`-ios predicate string:${selector}`)
|
||||
await switch.click()
|
||||
```
|
||||
|
||||
And [class chains](https://github.com/facebook/WebDriverAgent/wiki/Class-Chain-Queries-Construction-Rules):
|
||||
|
||||
```js
|
||||
const selector = '**/XCUIElementTypeCell[`name BEGINSWITH "D"`]/**/XCUIElementTypeButton'
|
||||
const button = await $(`-ios class chain:${selector}`)
|
||||
await button.click()
|
||||
```
|
||||
|
||||
### Accessibility ID
|
||||
|
||||
The `accessibility id` locator strategy is designed to read a unique identifier for a UI element. This has the benefit of not changing during localization or any other process that might change text. In addition, it can be an aid in creating cross-platform tests, if elements that are functionally the same have the same accessibility id.
|
||||
|
||||
- For iOS this is the `accessibility identifier` laid out by Apple [here](https://developer.apple.com/library/prerelease/ios/documentation/UIKit/Reference/UIAccessibilityIdentification_Protocol/index.html).
|
||||
- For Android the `accessibility id` maps to the `content-description` for the element, as described [here](https://developer.android.com/training/accessibility/accessible-app.html).
|
||||
|
||||
For both platforms, getting an element (or multiple elements) by their `accessibility id` is usually the best method. It is also the preferred way over the deprecated `name` strategy.
|
||||
|
||||
```js
|
||||
const elem = await $('~my_accessibility_identifier')
|
||||
await elem.click()
|
||||
```
|
||||
|
||||
### Class Name
|
||||
|
||||
The `class name` strategy is a `string` representing a UI element on the current view.
|
||||
|
||||
- For iOS it is the full name of a [UIAutomation class](https://developer.apple.com/library/prerelease/tvos/documentation/DeveloperTools/Conceptual/InstrumentsUserGuide/UIAutomation.html), and will begin with `UIA-`, such as `UIATextField` for a text field. A full reference can be found [here](https://developer.apple.com/library/ios/navigation/#section=Frameworks&topic=UIAutomation).
|
||||
- For Android it is the fully qualified name of a [UI Automator](https://developer.android.com/tools/testing-support-library/index.html#UIAutomator) [class](https://developer.android.com/reference/android/widget/package-summary.html), such `android.widget.EditText` for a text field. A full reference can be found [here](https://developer.android.com/reference/android/widget/package-summary.html).
|
||||
- For Youi.tv it is the full name of a Youi.tv class, and will being with `CYI-`, such as `CYIPushButtonView` for a push button element. A full reference can be found at [You.i Engine Driver's GitHub page](https://github.com/YOU-i-Labs/appium-youiengine-driver)
|
||||
|
||||
```js
|
||||
// iOS example
|
||||
await $('UIATextField').click()
|
||||
// Android example
|
||||
await $('android.widget.DatePicker').click()
|
||||
// Youi.tv example
|
||||
await $('CYIPushButtonView').click()
|
||||
```
|
||||
|
||||
## Chain Selectors
|
||||
|
||||
If you want to be more specific in your query, you can chain selectors until you've found the right element. If you call `element` before your actual command, WebdriverIO starts the query from that element.
|
||||
|
||||
For example, if you have a DOM structure like:
|
||||
|
||||
```html
|
||||
<div class="row">
|
||||
<div class="entry">
|
||||
<label>Product A</label>
|
||||
<button>Add to cart</button>
|
||||
<button>More Information</button>
|
||||
</div>
|
||||
<div class="entry">
|
||||
<label>Product B</label>
|
||||
<button>Add to cart</button>
|
||||
<button>More Information</button>
|
||||
</div>
|
||||
<div class="entry">
|
||||
<label>Product C</label>
|
||||
<button>Add to cart</button>
|
||||
<button>More Information</button>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
And you want to add product B to the cart, it would be difficult to do that just by using the CSS selector.
|
||||
|
||||
With selector chaining, it's way easier. Simply narrow down the desired element step by step:
|
||||
|
||||
```js
|
||||
await $('.row .entry:nth-child(2)').$('button*=Add').click()
|
||||
```
|
||||
|
||||
### Appium Image Selector
|
||||
|
||||
Using the `-image` locator strategy, it is possible to send an Appium an image file representing an element you want to access.
|
||||
|
||||
Supported file formats `jpg,png,gif,bmp,svg`
|
||||
|
||||
Full reference can be found [here](https://github.com/appium/appium/blob/master/packages/images-plugin/docs/find-by-image.md)
|
||||
|
||||
```js
|
||||
const elem = await $('./file/path/of/image/test.jpg')
|
||||
await elem.click()
|
||||
```
|
||||
|
||||
**Note**: The way how Appium works with this selector is that it will internally make a (app)screenshot and use the provided image selector to verify if the element can be found in that (app)screenshot.
|
||||
|
||||
Be aware of the fact that Appium might resize the taken (app)screenshot to make it match the CSS-size of your (app)screen (this will happen on iPhones but also on Mac machines with a Retina display because the DPR is bigger than 1). This will result in not finding a match because the provided image selector might have been taken from the original screenshot. You can fix this by updating the Appium Server settings, see the [Appium docs](https://github.com/appium/appium/blob/master/packages/images-plugin/docs/find-by-image.md#related-settings) for the settings and [this comment](https://github.com/webdriverio/webdriverio/issues/6097#issuecomment-726675579) on a detailed explanation.
|
||||
|
||||
## React Selectors
|
||||
|
||||
WebdriverIO provides a way to select React components based on the component name. To do this, you have a choice of two commands: `react$` and `react$$`.
|
||||
|
||||
These commands allow you to select components off the [React VirtualDOM](https://reactjs.org/docs/faq-internals.html) and return either a single WebdriverIO Element or an array of elements (depending on which function is used).
|
||||
|
||||
**Note**: The commands `react$` and `react$$` are similar in functionality, except that `react$$` will return *all* matching instances as an array of WebdriverIO elements, and `react$` will return the first found instance.
|
||||
|
||||
#### Basic example
|
||||
|
||||
```jsx
|
||||
// index.jsx
|
||||
import React from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
|
||||
function MyComponent() {
|
||||
return (
|
||||
<div>
|
||||
MyComponent
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function App() {
|
||||
return (<MyComponent />)
|
||||
}
|
||||
|
||||
ReactDOM.render(<App />, document.querySelector('#root'))
|
||||
```
|
||||
|
||||
In the above code there is a simple `MyComponent` instance inside the application, which React is rendering inside a HTML element with `id="root"`.
|
||||
|
||||
With the `browser.react$` command, you can select an instance of `MyComponent`:
|
||||
|
||||
```js
|
||||
const myCmp = await browser.react$('MyComponent')
|
||||
```
|
||||
|
||||
Now that you have the WebdriverIO element stored in `myCmp` variable, you can execute element commands against it.
|
||||
|
||||
#### Filtering components
|
||||
|
||||
The library that WebdriverIO uses internally allows to filter your selection by props and/or state of the component. To do so, you need to pass a second argument for props and/or a third argument for state to the browser command.
|
||||
|
||||
```jsx
|
||||
// index.jsx
|
||||
import React from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
|
||||
function MyComponent(props) {
|
||||
return (
|
||||
<div>
|
||||
Hello { props.name || 'World' }!
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function App() {
|
||||
return (
|
||||
<div>
|
||||
<MyComponent name="WebdriverIO" />
|
||||
<MyComponent />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
ReactDOM.render(<App />, document.querySelector('#root'))
|
||||
```
|
||||
|
||||
If you want to select the instance of `MyComponent` that has a prop `name` as `WebdriverIO`, you can execute the command like so:
|
||||
|
||||
```js
|
||||
const myCmp = await browser.react$('MyComponent', {
|
||||
props: { name: 'WebdriverIO' }
|
||||
})
|
||||
```
|
||||
|
||||
If you wanted to filter our selection by state, the `browser` command would looks something like so:
|
||||
|
||||
```js
|
||||
const myCmp = await browser.react$('MyComponent', {
|
||||
state: { myState: 'some value' }
|
||||
})
|
||||
```
|
||||
|
||||
#### Dealing with `React.Fragment`
|
||||
|
||||
When using the `react$` command to select React [fragments](https://reactjs.org/docs/fragments.html), WebdriverIO will return the first child of that component as the component's node. If you use `react$$`, you will receive an array containing all the HTML nodes inside the fragments that match the selector.
|
||||
|
||||
```jsx
|
||||
// index.jsx
|
||||
import React from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
|
||||
function MyComponent() {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<div>
|
||||
MyComponent
|
||||
</div>
|
||||
<div>
|
||||
MyComponent
|
||||
</div>
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
|
||||
function App() {
|
||||
return (<MyComponent />)
|
||||
}
|
||||
|
||||
ReactDOM.render(<App />, document.querySelector('#root'))
|
||||
```
|
||||
|
||||
Given the above example, this is how the commands would work:
|
||||
|
||||
```js
|
||||
await browser.react$('MyComponent') // returns the WebdriverIO Element for the first <div />
|
||||
await browser.react$$('MyComponent') // returns the WebdriverIO Elements for the array [<div />, <div />]
|
||||
```
|
||||
|
||||
**Note:** If you have multiple instances of `MyComponent` and you use `react$$` to select these fragment components, you will be returned an one-dimensional array of all the nodes. In other words, if you have 3 `<MyComponent />` instances, you will be returned an array with six WebdriverIO elements.
|
||||
|
||||
## Custom Selector Strategies
|
||||
|
||||
If your app requires a specific way to fetch elements you can define yourself a custom selector strategy that you can use with `custom$` and `custom$$`. For that register your strategy once in the beginning of the test, e.g. in a `before` hook:
|
||||
|
||||
```js reference
|
||||
https://github.com/webdriverio/example-recipes/blob/f5730428ec3605e856e90bf58be17c9c9da891de/queryElements/customStrategy.js#L2-L11
|
||||
```
|
||||
|
||||
Given the following HTML snippet:
|
||||
|
||||
```html reference
|
||||
https://github.com/webdriverio/example-recipes/blob/f5730428ec3605e856e90bf58be17c9c9da891de/queryElements/example.html#L8-L12
|
||||
```
|
||||
|
||||
Then use it by calling:
|
||||
|
||||
```js reference
|
||||
https://github.com/webdriverio/example-recipes/blob/f5730428ec3605e856e90bf58be17c9c9da891de/queryElements/customStrategy.js#L16-L19
|
||||
```
|
||||
|
||||
**Note:** this only works in an web environment in which the [`execute`](/docs/api/browser/execute) command can be run.
|
||||
|
|
@ -1,90 +0,0 @@
|
|||
---
|
||||
id: seleniumgrid
|
||||
title: Selenium Grid
|
||||
---
|
||||
|
||||
You can use WebdriverIO with your existing Selenium Grid instance. To connect your tests to Selenium Grid, you just need to update the options in your test runner configurations.
|
||||
|
||||
Here is a code snippet from sample wdio.conf.ts.
|
||||
|
||||
```ts title=wdio.conf.ts
|
||||
export const config: Options.Testrunner = {
|
||||
// ...
|
||||
protocol: 'https',
|
||||
hostname: 'yourseleniumgridhost.yourdomain.com',
|
||||
port: 443,
|
||||
path: '/wd/hub',
|
||||
// ...
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
You need to provide the appropriate values for the protocol, hostname, port, and path based on your Selenium Grid setup.
|
||||
If you are running Selenium Grid on the same machine as your test scripts, here are some typical options:
|
||||
|
||||
```ts title=wdio.conf.ts
|
||||
export const config: Options.Testrunner = {
|
||||
// ...
|
||||
protocol: 'http',
|
||||
hostname: 'localhost',
|
||||
port: 4444,
|
||||
path: '/wd/hub',
|
||||
// ...
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
### Basic authentication with protected Selenium Grid
|
||||
|
||||
It is highly recommended to secure your Selenium Grid. If you have a protected Selenium Grid that requires authentication, you can pass authentication headers via options.
|
||||
Please refer to the [headers](https://webdriver.io/docs/configuration/#headers) section in the documentation for more information.
|
||||
|
||||
### Timeout configurations with dynamic Selenium Grid
|
||||
|
||||
When using a dynamic Selenium Grid where browser pods are spun up on demand, session creation may face a cold start. In such cases, it is advised to increase the session creation timeouts. The default value in the options is 120 seconds, but you can increase it if your grid takes more time to create a new session.
|
||||
|
||||
```ts
|
||||
connectionRetryTimeout: 180000,
|
||||
```
|
||||
|
||||
### Advanced configurations
|
||||
|
||||
For advanced configurations, please refer to the Testrunner [configuration file](https://webdriver.io/docs/configurationfile).
|
||||
|
||||
### File operations with Selenium Grid
|
||||
|
||||
When running test cases with a remote Selenium Grid, the browser runs on a remote machine, and you need to take special care with test cases involving file uploads and downloads.
|
||||
|
||||
### File downloads
|
||||
|
||||
For Chromium-based browsers, you can refer to the [Download file](https://webdriver.io/docs/api/browser/downloadFile) documentation. If your test scripts need to read the content of a downloaded file, you need to download it from the remote Selenium node to the test runner machine. Here is an example code snippet from the sample `wdio.conf.ts` configuration for the Chrome browser:
|
||||
|
||||
```ts title=wdio.conf.ts
|
||||
export const config: WebdriverIO.Config = {
|
||||
// ...
|
||||
protocol: 'https',
|
||||
hostname: 'yourseleniumgridhost.yourdomain.com',
|
||||
port: 443,
|
||||
path: '/wd/hub',
|
||||
// ...
|
||||
capabilities: [{
|
||||
browserName: 'chrome',
|
||||
'se:downloadsEnabled': true
|
||||
}],
|
||||
//...
|
||||
}
|
||||
```
|
||||
|
||||
### File upload with remote Selenium Grid
|
||||
|
||||
To upload a file to a web app in the remote browser, you first need to upload the file to the remote grid. You can refer to the [uploadFile](https://webdriver.io/docs/api/browser/uploadFile) documentation for details.
|
||||
|
||||
### Other file/grid operations
|
||||
|
||||
There are a few more operations that you can perform with Selenium Grid. The instructions for Selenium Standalone should work fine with Selenium Grid as well. Please refer to the [Selenium Standalone](https://webdriver.io/docs/api/selenium/) documentation for available options.
|
||||
|
||||
### Selenium Grid Official documentation
|
||||
|
||||
For more information on Selenium Grid, you can refer to the official Selenium Grid [documentation](https://www.selenium.dev/documentation/grid/).
|
||||
|
||||
If you wish to run Selenium Grid in Docker, Docker compose or Kubernetes, please refer to the Selenium-Docker [GitHub repository](https://github.com/SeleniumHQ/docker-selenium).
|
||||
|
|
@ -1,71 +0,0 @@
|
|||
---
|
||||
id: setuptypes
|
||||
title: Setup Types
|
||||
---
|
||||
|
||||
WebdriverIO can be used for various purposes. It implements the WebDriver protocol API and can run a browser in an automated way. The framework is designed to work in any arbitrary environment and for any kind of task. It is independent from any 3rd party frameworks and only requires Node.js to run.
|
||||
|
||||
## Protocol Bindings
|
||||
|
||||
For basic interactions with the WebDriver and other automation protocols WebdriverIO uses its own protocol bindings based on the [`webdriver`](https://www.npmjs.com/package/webdriver) NPM package:
|
||||
|
||||
<Tabs
|
||||
defaultValue="webdriver"
|
||||
values={[
|
||||
{label: 'WebDriver', value: 'webdriver'},
|
||||
{label: 'Chrome DevTools', value: 'devtools'},
|
||||
]
|
||||
}>
|
||||
<TabItem value="webdriver">
|
||||
|
||||
```js reference useHTTPS
|
||||
https://github.com/webdriverio/example-recipes/blob/e8b147e88e7a38351b0918b4f7efbd9ae292201d/setup/webdriver.js#L5-L20
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="devtools">
|
||||
|
||||
```js reference useHTTPS
|
||||
https://github.com/webdriverio/example-recipes/blob/e8b147e88e7a38351b0918b4f7efbd9ae292201d/setup/devtools.js#L2-L17
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
All [protocol commands](api/webdriver) return the raw response from the automation driver. The package is very lightweight and there is __no__ smart logic like auto-waits to simplify the interaction with the protocol usage.
|
||||
|
||||
The protocol commands applied to the instance depend on the initial session response of the driver. For example if the response indicates that a mobile session was started, the package applies all Appium and Mobile JSON Wire protocol commands to the instance prototype.
|
||||
|
||||
You can run the same set of commands (except mobile ones) using the Chrome DevTools protocol when importing the [`devtools`](https://www.npmjs.com/package/devtools) NPM package. It has the same interface as the `webdriver` package but runs its automation based on [Puppeteer](https://pptr.dev/).
|
||||
|
||||
For more information on these package interfaces, see [Modules API](/docs/api/modules).
|
||||
|
||||
## Standalone Mode
|
||||
|
||||
To simplify the interaction with the WebDriver protocol the `webdriverio` package implements a variety of commands on top of the protocol (e.g. the [`dragAndDrop`](api/element/dragAndDrop) command) and core concepts such as [smart selectors](selectors) or [auto-waits](autowait). The example from above can be simplified like this:
|
||||
|
||||
```js reference useHTTPS
|
||||
https://github.com/webdriverio/example-recipes/blob/e8b147e88e7a38351b0918b4f7efbd9ae292201d/setup/standalone.js#L2-L19
|
||||
```
|
||||
|
||||
Using WebdriverIO in standalone mode still gives you access to all protocol commands but provides a super set of additional commands that provide a higher level interaction with the browser. It allows you to integrate this automation tool in your own (test) project to create a new automation library. Popular examples include [Oxygen](https://github.com/oxygenhq/oxygen) or [CodeceptJS](http://codecept.io). You can also write plain Node scripts to scrape the web for content (or anything else that requires a running browser).
|
||||
|
||||
If no specific options are set WebdriverIO will always attempt to download and setup the browser driver that matches `browserName` property in your capabilities. In case of Chrome and Firefox it might also install them depending on whether it can find the corresponding browser on the machine.
|
||||
|
||||
For more information on the `webdriverio` package interfaces, see [Modules API](/docs/api/modules).
|
||||
|
||||
## The WDIO Testrunner
|
||||
|
||||
The main purpose of WebdriverIO, though, is end-to-end testing on a big scale. We therefore implemented a test runner that helps you to build a reliable test suite that is easy to read and maintain.
|
||||
|
||||
The test runner takes care of many problems that are common when working with plain automation libraries. For one, it organizes your test runs and splits up test specs so your tests can be executed with maximum concurrency. It also handles session management and provides lots of features to help you to debug problems and find errors in your tests.
|
||||
|
||||
Here is the same example from above, written as a test spec and executed by WDIO:
|
||||
|
||||
```js reference useHTTPS
|
||||
https://github.com/webdriverio/example-recipes/blob/e8b147e88e7a38351b0918b4f7efbd9ae292201d/setup/testrunner.js
|
||||
```
|
||||
|
||||
The test runner is an abstraction of popular test frameworks like Mocha, Jasmine, or Cucumber. To run your tests using the WDIO test runner, check out the [Getting Started](gettingstarted) section for more information.
|
||||
|
||||
For more information on the `@wdio/cli` testrunner package interface, see [Modules API](/docs/api/modules).
|
||||
|
|
@ -1,66 +0,0 @@
|
|||
---
|
||||
id: sharding
|
||||
title: Sharding
|
||||
---
|
||||
|
||||
By default, WebdriverIO runs tests in parallel and strives for optimal utilization of CPU cores on your machine. In order to achieve even greater parallelisation, you can further scale WebdriverIO test execution by running tests on multiple machines simultaneously. We call this mode of operation "sharding".
|
||||
|
||||
## Sharding tests between multiple machines
|
||||
|
||||
To shard the test suite, pass `--shard=x/y` to the command line. For example, to split the suite into four shards, each running one fourth of the tests:
|
||||
|
||||
```sh
|
||||
npx wdio run wdio.conf.js --shard=1/4
|
||||
npx wdio run wdio.conf.js --shard=2/4
|
||||
npx wdio run wdio.conf.js --shard=3/4
|
||||
npx wdio run wdio.conf.js --shard=4/4
|
||||
```
|
||||
|
||||
Now, if you run these shards in parallel on different computers, your test suite completes four times faster.
|
||||
|
||||
## GitHub Actions example
|
||||
|
||||
GitHub Actions supports [sharding tests between multiple jobs](https://docs.github.com/en/actions/using-jobs/using-a-matrix-for-your-jobs) using the [`jobs.<job_id>.strategy.matrix`](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstrategymatrix) option. The matrix option will run a separate job for every possible combination of the provided options.
|
||||
|
||||
The following example shows you how to configure a job to run your tests on four machines in parallel. You can find the whole pipeline setup in the [Cucumber Boilerplate](https://github.com/webdriverio/cucumber-boilerplate/blob/main/.github/workflows/test.yaml) project.
|
||||
|
||||
- First we add a matrix option to our job configuration with the shard option containing the number of shards we want to create. `shard: [1, 2, 3, 4]` will create four shards, each with a different shard number.
|
||||
- Then we run our WebdriverIO tests with the `--shard ${{ matrix.shard }}/${{ strategy.job-total }}` option. This will be our test command for each shard.
|
||||
- Finally we upload our wdio log report to the GitHub Actions Artifacts. This will make logs available in case the shard fails.
|
||||
|
||||
The test pipeline is defined as follows:
|
||||
|
||||
```yaml title=.github/workflows/test.yaml
|
||||
name: Test
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
# ...
|
||||
unit:
|
||||
# ...
|
||||
e2e:
|
||||
name: 🧪 Test (${{ matrix.shard }}/${{ strategy.job-total }})
|
||||
runs-on: ubuntu-latest
|
||||
needs: [lint, unit]
|
||||
strategy:
|
||||
matrix:
|
||||
shard: [1, 2, 3, 4]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: ./.github/workflows/actions/setup
|
||||
- name: E2E Test
|
||||
run: npm run test:features -- --shard ${{ matrix.shard }}/${{ strategy.job-total }}
|
||||
- uses: actions/upload-artifact@v1
|
||||
if: failure()
|
||||
with:
|
||||
name: logs-${{ matrix.shard }}
|
||||
path: logs
|
||||
```
|
||||
|
||||
This will run all shards in parallel, reducing executing time for the tests by 4:
|
||||
|
||||

|
||||
|
||||
See commit [`96d444e`](https://github.com/webdriverio/cucumber-boilerplate/commit/96d444ea23919389682b9b1c9408ed91c452c7f8) from the [Cucumber Boilerplate](https://github.com/webdriverio/cucumber-boilerplate) project that introduced sharding to its test pipeline which helped reduce the overall execution time from `2:23 min` down to `1:30 min`, a reduction of __37%__ 🎉.
|
||||
|
|
@ -1,101 +0,0 @@
|
|||
---
|
||||
id: snapshot
|
||||
title: Snapshot
|
||||
---
|
||||
|
||||
Snapshot tests can be very useful for asserting a wide range of aspects of your component or logic at the same time. In WebdriverIO you can take snapshots of any arbitrary object as well as a WebElement DOM structure or WebdriverIO command results.
|
||||
|
||||
Similar to other test frameworks WebdriverIO will take a snapshot of the given value, then compare it to a reference snapshot file stored alongside the test. The test will fail if the two snapshots do not match: either the change is unexpected, or the reference snapshot needs to be updated to the new version of the result.
|
||||
|
||||
:::info Cross Platform Support
|
||||
|
||||
These snapshot capabilities are available for running end-to-end tests within the Node.js environment as well as for running [unit and component](/docs/component-testing) tests in the browser or on mobile devices.
|
||||
|
||||
:::
|
||||
|
||||
## Use Snapshots
|
||||
|
||||
To snapshot a value, you can use the `toMatchSnapshot()` from [`expect()`](/docs/api/expect-webdriverio) API:
|
||||
|
||||
```ts
|
||||
import { browser, expect } from '@wdio/globals'
|
||||
|
||||
it('can take a DOM snapshot', () => {
|
||||
await browser.url('https://guinea-pig.webdriver.io/')
|
||||
await expect($('.findme')).toMatchSnapshot()
|
||||
})
|
||||
```
|
||||
|
||||
The first time this test is run, WebdriverIO creates a snapshot file that looks like this:
|
||||
|
||||
```js
|
||||
// Snapshot v1
|
||||
|
||||
exports[`main suite 1 > can take a DOM snapshot 1`] = `"<h1 class="findme">Test CSS Attributes</h1>"`;
|
||||
```
|
||||
|
||||
The snapshot artifact should be committed alongside code changes, and reviewed as part of your code review process. On subsequent test runs, WebdriverIO will compare the rendered output with the previous snapshot. If they match, the test will pass. If they don't match, either the test runner found a bug in your code that should be fixed, or the implementation has changed and the snapshot needs to be updated.
|
||||
|
||||
To update the snapshot, pass in the `-s` flag (or `--updateSnapshot`) to the `wdio` command, e.g.:
|
||||
|
||||
```sh
|
||||
npx wdio run wdio.conf.js -s
|
||||
```
|
||||
|
||||
**Note:** if you run tests with multiple browsers in parallel only one snapshot is being created and compared against. If you like to have a separate snapshot per capability, please [raise an issue](https://github.com/webdriverio/webdriverio/issues/new?assignees=\&labels=Idea+%F0%9F%92%A1%2CNeeds+Triaging+%E2%8F%B3\&projects=\&template=feature-request.yml\&title=%5B%F0%9F%92%A1+Feature%5D%3A+%3Ctitle%3E) and let us know about your use case.
|
||||
|
||||
## Inline Snapshots
|
||||
|
||||
Similarly, you can use the `toMatchInlineSnapshot()` to store the snapshot inline within the test file.
|
||||
|
||||
```ts
|
||||
import { expect, $ } from '@wdio/globals'
|
||||
|
||||
it('can take inline DOM snapshots', () => {
|
||||
const elem = $('.container')
|
||||
await expect(elem.getCSSProperty()).toMatchInlineSnapshot()
|
||||
})
|
||||
```
|
||||
|
||||
Instead of creating a snapshot file, Vitest will modify the test file directly to update the snapshot as a string:
|
||||
|
||||
```ts
|
||||
import { expect, $ } from '@wdio/globals'
|
||||
|
||||
it('can take inline DOM snapshots', () => {
|
||||
const elem = $('.container')
|
||||
await expect(elem.getCSSProperty()).toMatchInlineSnapshot(`
|
||||
{
|
||||
"parsed": {
|
||||
"alpha": 0,
|
||||
"hex": "#000000",
|
||||
"rgba": "rgba(0,0,0,0)",
|
||||
"type": "color",
|
||||
},
|
||||
"property": "background-color",
|
||||
"value": "rgba(0,0,0,0)",
|
||||
}
|
||||
`)
|
||||
})
|
||||
```
|
||||
|
||||
This allows you to see the expected output directly without jumping across different files.
|
||||
|
||||
## Visual Snapshots
|
||||
|
||||
Taking a DOM snapshot of an element might not be the best idea, especially if the DOM structure is too big and contains dynamic element properties. In these cases, it is recommended to rely on visual snapshots for elements.
|
||||
|
||||
To enable visual snapshots, add the `@wdio/visual-service` to your setup. You can follow the set-up instructions in the [documentation](/docs/visual-testing#installation) for Visual Testing.
|
||||
|
||||
You can then take a visual snapshot via `toMatchElementSnapshot()`, e.g.:
|
||||
|
||||
```ts
|
||||
import { expect, $ } from '@wdio/globals'
|
||||
|
||||
it('can take inline DOM snapshots', () => {
|
||||
const elem = $('.container')
|
||||
await expect(elem.getCSSProperty()).toMatchInlineSnapshot()
|
||||
})
|
||||
```
|
||||
|
||||
An image is then stored in the baseline directory. Check out the [Visual Testing](/docs/visual-testing) for more information.
|
||||
|
|
@ -1,149 +0,0 @@
|
|||
---
|
||||
id: sponsor
|
||||
title: Become a WebdriverIO Sponsor
|
||||
---
|
||||
|
||||
WebdriverIO, an open-source project under the MIT license, is freely accessible for use. The sustainability of this expansive ecosystem, along with the development of innovative features, is made possible through the generous financial support of our sponsors, who contribute significantly to the maintenance and continuous growth of the project.
|
||||
|
||||
## How to Sponsor
|
||||
Sponsorships can be done via [GitHub Sponsors](https://github.com/sponsors/webdriverio), [Tidelift](enterprise) or [OpenCollective](https://opencollective.com/webdriverio). Invoices can be obtained via GitHub's payment system. Both monthly recurring sponsorships and one-time donations are accepted. Recurring sponsorships are entitled to logo placements as specified in Sponsorship Tiers.
|
||||
|
||||
If you have questions regarding tiers, payment logistics, or sponsor exposure data, please reach out to [sponsor@webdriver.io](mailto:sponsor@webdriver.io).
|
||||
|
||||
You can also head over to the WebdriverIO [Swag Store](https://shop.webdriver.io/) where all earnings on purchases will be going back to the project development.
|
||||
|
||||
## Sponsoring WebdriverIO as a Business
|
||||
Sponsoring WebdriverIO gives you great exposure through our website (60k+ page views per month) and GitHub project READMEs. In addition, supporting OSS improves the reputation of your brand, which is an important asset for any company that interacts with developers.
|
||||
|
||||
If you are using WebdriverIO to test a revenue-generating product, it makes business sense to sponsor WebdriverIO's development: it ensures the project that your product relies on stays healthy and actively maintained. The exposure and positive brand image in the WebdriverIO community also make it easier to attract and recruit developers and QA engineers with WebdriverIO experience.
|
||||
|
||||
__Note:__ We do not accept donations from gambling sites, paper writing services, political groups, hate groups, adult entertainment sites, or any other organization that we don't believe is contributing to the good of the project. We don't offer advertising, we are an open-source project looking for financial support from our users.
|
||||
|
||||
## Sponsoring WebdriverIO as an Individual
|
||||
If you are an individual user and have enjoyed the productivity of using WebdriverIO, consider donating as a sign of appreciation - like buying us coffee once in a while. Many of our team members accept sponsorships and donations via GitHub Sponsors.
|
||||
|
||||
You can also try to convince your employer to sponsor WebdriverIO as a business. This may not be easy, but business sponsorships typically make a much larger impact on the sustainability of OSS projects than individual donations, so you will help us much more if you succeed.
|
||||
|
||||
## Tier Benefits
|
||||
|
||||
- __💎 Premium (USD $1000/mo or higher):__
|
||||
- Limited to two sponsors globally
|
||||
- Exclusive above-the-fold logo placement on the front page of webdriver.io (~2.8k daily unique visitors).
|
||||
- Most prominent logo placement in all locations from tiers below.
|
||||
- Guaranteed exclusivity for a minimum of one year, during which no other entity can "outbid" or replace them, ensuring a fixed period of partnership and visibility.
|
||||
- __🥇 Gold (USD $500 / mo):__
|
||||
- Large logo placement on the front page of [webdriver.io](https://webdriver.io/)
|
||||
- Large logo placement in the [`README.md`](https://github.com/webdriverio/webdriverio/blob/main/README.md) and [`BACKERS.md`](https://github.com/webdriverio/webdriverio/blob/main/BACKERS.md) of the [`webdriverio/webdriverio`](https://github.com/webdriverio/webdriverio) repository.
|
||||
- __🥈 Silver (USD $250 / mo):__
|
||||
- Medium logo placement on this sponsors page
|
||||
- Medium logo placement in the [`README.md`](https://github.com/webdriverio/webdriverio/blob/main/README.md) and [`BACKERS.md`](https://github.com/webdriverio/webdriverio/blob/main/BACKERS.md) of the [`webdriverio/webdriverio`](https://github.com/webdriverio/webdriverio) repository.
|
||||
- __🥉 Bronze (USD $100 / mo):__
|
||||
- Your name or company logo (small) will be put in [`webdriverio/webdriverio`](https://github.com/webdriverio/webdriverio), [`README.md`](https://github.com/webdriverio/webdriverio/blob/main/README.md) and [`BACKERS.md`](https://github.com/webdriverio/webdriverio/blob/main/BACKERS.md) files.
|
||||
- __🍺 Generous Backer (USD $50 / mo):__
|
||||
- Your name will be listed under the "Generous Backers" section in [`webdriverio/webdriverio`](https://github.com/webdriverio/webdriverio), [`README.md`](https://github.com/webdriverio/webdriverio/blob/main/README.md) and [`BACKERS.md`](https://github.com/webdriverio/webdriverio/blob/main/BACKERS.md) files.
|
||||
- __☕️ Individual Backer (USD $5 / mo):__
|
||||
- Name listed in the [`BACKERS.md`](https://github.com/webdriverio/webdriverio/blob/main/BACKERS.md) file of the [`webdriverio/webdriverio`](https://github.com/webdriverio/webdriverio) repository.
|
||||
|
||||
---
|
||||
|
||||
## Current Sponsors
|
||||
|
||||
### 💎 Premium
|
||||
|
||||
<ImageSwitcher
|
||||
lightImageSrc="/img/sponsors/browserstack_black.svg"
|
||||
darkImageSrc="/img/sponsors/browserstack_white.svg"
|
||||
alt="BrowserStack"
|
||||
target="_blank"
|
||||
link="https://www.browserstack.com/automation-webdriverio"
|
||||
/>
|
||||
|
||||
<br />
|
||||
<br />
|
||||
|
||||
<br />
|
||||
<br />
|
||||
|
||||
### 🥇 Gold
|
||||
|
||||
<ImageSwitcher
|
||||
lightImageSrc="/img/sponsors/route4me.svg"
|
||||
darkImageSrc="/img/sponsors/route4me.svg"
|
||||
alt="Route4Me"
|
||||
link="https://www.route4me.com/"
|
||||
width="250"
|
||||
target="_blank"
|
||||
style={{ marginRight: '20px', position: 'relative', top: '8px' }}
|
||||
/>
|
||||
|
||||
<ImageSwitcher
|
||||
lightImageSrc="/img/sponsors/lambdatest_black.svg"
|
||||
darkImageSrc="/img/sponsors/lambdatest_white.svg"
|
||||
alt="Lambdatest"
|
||||
target="_blank"
|
||||
link="https://www.lambdatest.com/"
|
||||
width="250"
|
||||
/>
|
||||
|
||||
<ImageSwitcher lightImageSrc="/img/sponsors/lambdatest_black.svg" darkImageSrc="/img/sponsors/lambdatest_white.svg" alt="Lambdatest" target="_blank" link="https://www.lambdatest.com/" width="250" />
|
||||
|
||||
Become a [Gold Sponsor](https://opencollective.com/webdriverio/contribute/gold-sponsor-26921/checkout?interval=month&amount=500&contributeAs=me).
|
||||
|
||||
<br />
|
||||
|
||||
### 🥈 Silver
|
||||
|
||||
<ImageSwitcher
|
||||
lightImageSrc="/img/sponsors/testingbot.svg"
|
||||
darkImageSrc="/img/sponsors/testingbot.svg"
|
||||
alt="TestingBot"
|
||||
link="https://testingbot.com/"
|
||||
width="150"
|
||||
target="_blank"
|
||||
/>
|
||||
|
||||
<ImageSwitcher lightImageSrc="/img/sponsors/testingbot.svg" darkImageSrc="/img/sponsors/testingbot.svg" alt="TestingBot" link="https://testingbot.com/" width="250" target="_blank" style={{ marginLeft: '30px', position: 'relative', top: '-8px' }} />
|
||||
|
||||
Become a [Silver Sponsor](https://opencollective.com/webdriverio/contribute/silver-sponsor-69223/checkout?interval=month&amount=250&contributeAs=me).
|
||||
|
||||
<br />
|
||||
|
||||
### 🥉 Bronze
|
||||
|
||||
<ImageSwitcher
|
||||
lightImageSrc="/img/sponsors/eslint_black.svg"
|
||||
darkImageSrc="/img/sponsors/eslint_white.svg"
|
||||
alt="Eslint"
|
||||
target="_blank"
|
||||
link="https://eslint.org/"
|
||||
width="150"
|
||||
/>
|
||||
|
||||
<ImageSwitcher
|
||||
lightImageSrc="/img/sponsors/gridlastic.png"
|
||||
darkImageSrc="/img/sponsors/gridlastic.png"
|
||||
alt="Gridlastic"
|
||||
target="_blank"
|
||||
link="https://www.gridlastic.com/webdriverio.html"
|
||||
width="150"
|
||||
/>
|
||||
|
||||
<ImageSwitcher lightImageSrc="/img/sponsors/gridlastic.png" darkImageSrc="/img/sponsors/gridlastic.png" alt="Gridlastic" target="_blank" link="https://www.gridlastic.com/webdriverio.html" width="150" />
|
||||
|
||||
Become a [Bronze Sponsor](https://opencollective.com/webdriverio/contribute/bronze-sponsor-69224/checkout?interval=month&amount=100&contributeAs=me).
|
||||
|
||||
<br />
|
||||
<br />
|
||||
|
||||
### 🙇 Notable Past Sponsors
|
||||
|
||||
We are grateful for the support of all our sponsors! Here are some of the companies that have supported WebdriverIO in the past.
|
||||
|
||||
<ImageSwitcher
|
||||
lightImageSrc="/img/sponsors/saucelabs_black.svg"
|
||||
darkImageSrc="/img/sponsors/saucelabs_white.svg"
|
||||
alt="Sauce Labs"
|
||||
link="https://saucelabs.com/"
|
||||
width="150"
|
||||
target="_blank"
|
||||
/>
|
||||
|
|
@ -1,154 +0,0 @@
|
|||
---
|
||||
id: async-migration
|
||||
title: From Sync to Async
|
||||
---
|
||||
|
||||
Due to changes in V8 the WebdriverIO team [announced](https://webdriver.io/blog/2021/07/28/sync-api-deprecation) to deprecate synchronous command execution by April 2023. The team has been working hard to make the transition as easy as possible. In this guide we explain how you can slowly migrate your test suite from sync to async. As an example project we use the [Cucumber Boilerplate](https://github.com/webdriverio/cucumber-boilerplate) but the approach is the same with all other projects as well.
|
||||
|
||||
## Promises in JavaScript
|
||||
|
||||
The reason why synchronous execution was popular in WebdriverIO is because it removes the complexity of dealing with promises. Particularly if you come from other languages where this concept doesn't exist this way, it can be confusing in the beginning. However Promises are a very powerful tool to deal with asynchronous code and today's JavaScript makes it actually easy to deal with it. If you never worked with Promises, we recommend to check out the [MDN reference guide](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) to it as it would be out of scope to explain it here.
|
||||
|
||||
## Async Transition
|
||||
|
||||
The WebdriverIO testrunner can handle async and sync execution within the same test suite. This means that you can slowly migrate your tests and PageObjects step by step at your pace. For example, the Cucumber Boilerplate has defined [a large set of step definition](https://github.com/webdriverio/cucumber-boilerplate/tree/main/src/support/action) for you to copy into your project. We can go ahead and migrate one step definition or one file at a time.
|
||||
|
||||
:::tip
|
||||
|
||||
WebdriverIO offers a [codemod](https://github.com/webdriverio/codemod) that allows to transform your sync code into async code almost full automatically. Run the codemod as described in the docs first and use this guide for manual migration if needed.
|
||||
|
||||
:::
|
||||
|
||||
In many cases, everything that is necessary to do is to make the function in which you call WebdriverIO commands `async` and add an `await` in front of every command. Looking at the first file `clearInputField.ts` to transform in the boilerplate project, we transform from:
|
||||
|
||||
```ts
|
||||
export default (selector: Selector) => {
|
||||
$(selector).clearValue();
|
||||
};
|
||||
```
|
||||
|
||||
to:
|
||||
|
||||
```ts
|
||||
export default async (selector: Selector) => {
|
||||
await $(selector).clearValue();
|
||||
};
|
||||
```
|
||||
|
||||
That's it. You can see the complete commit with all rewrite examples here:
|
||||
|
||||
#### Commits:
|
||||
|
||||
- _transform all step definitions_ [[af6625f]](https://github.com/webdriverio/cucumber-boilerplate/pull/481/commits/af6625fcd01dc087479e84562f237ecf38b3537d)
|
||||
|
||||
:::info
|
||||
This transition is independent of whether you use TypeScript or not. If you use TypeScript just make sure that you eventually change the `types` property in your `tsconfig.json` from `webdriverio/sync` to `@wdio/globals/types`. Also make sure that your compile target is set to at least `ES2018`.
|
||||
:::
|
||||
|
||||
## Special Cases
|
||||
|
||||
There are of course always special cases where you need to pay a bit more attention.
|
||||
|
||||
### ForEach Loops
|
||||
|
||||
If you have a `forEach` loop, e.g. to iterate over elements, you need to make sure that the iterator callback is handled properly in an async manner, e.g.:
|
||||
|
||||
```js
|
||||
const elems = $$('div')
|
||||
elems.forEach((elem) => {
|
||||
elem.click()
|
||||
})
|
||||
```
|
||||
|
||||
The function we pass into `forEach` is an iterator function. In a synchronous world it would click on all elements before it moves on. If we transform this into asynchronous code, we have to ensure that we wait for every iterator function to finish execution. By adding `async`/`await` these iterator functions will return a promise that we need to resolve. Now, `forEach` is then not ideal to iterate over the elements anymore because it doesn't return the result of the iterator function, the promise we need to wait for. Therefore we need to replace `forEach` with `map` which returns that promise. The `map` as well as all other iterator methods of Arrays like `find`, `every`, `reduce` and more are implemented so that they respect promises within the iterator functions and are therefor simplified for using them in an async context. The above example looks transformed like this:
|
||||
|
||||
```js
|
||||
const elems = await $$('div')
|
||||
await elems.forEach((elem) => {
|
||||
return elem.click()
|
||||
})
|
||||
```
|
||||
|
||||
For example in order to fetch all `<h3 />` elements and get their text content, you can run:
|
||||
|
||||
```js
|
||||
await browser.url('https://webdriver.io')
|
||||
|
||||
const h3Texts = await browser.$$('h3').map((img) => img.getText())
|
||||
console.log(h3Texts);
|
||||
/**
|
||||
* returns:
|
||||
* [
|
||||
* 'Extendable',
|
||||
* 'Compatible',
|
||||
* 'Feature Rich',
|
||||
* 'Who is using WebdriverIO?',
|
||||
* 'Support for Modern Web and Mobile Frameworks',
|
||||
* 'Google Lighthouse Integration',
|
||||
* 'Watch Talks about WebdriverIO',
|
||||
* 'Get Started With WebdriverIO within Minutes'
|
||||
* ]
|
||||
*/
|
||||
```
|
||||
|
||||
If this looks too complicated you might want to consider using simple for loops, e.g.:
|
||||
|
||||
```js
|
||||
const elems = await $$('div')
|
||||
for (const elem of elems) {
|
||||
await elem.click()
|
||||
}
|
||||
```
|
||||
|
||||
### WebdriverIO Assertions
|
||||
|
||||
If you use the WebdriverIO assertion helper [`expect-webdriverio`](https://webdriver.io/docs/api/expect-webdriverio) make sure to set an `await` in front of every `expect` call, e.g.:
|
||||
|
||||
```ts
|
||||
expect($('input')).toHaveAttributeContaining('class', 'form')
|
||||
```
|
||||
|
||||
needs to be transformed to:
|
||||
|
||||
```ts
|
||||
await expect($('input')).toHaveAttributeContaining('class', 'form')
|
||||
```
|
||||
|
||||
### Sync PageObject Methods and Async Tests
|
||||
|
||||
If you have been writing PageObjects in your test suite in a synchronous way, you won't be able to use them in asynchronous tests anymore. If you need to use a PageObject method in both sync and async tests we recommend duplicating the method and offer them for both environments, e.g.:
|
||||
|
||||
```js
|
||||
class MyPageObject extends Page {
|
||||
/**
|
||||
* define elements
|
||||
*/
|
||||
get btnStart () { return $('button=Start') }
|
||||
get loadedPage () { return $('#finish') }
|
||||
|
||||
someMethod () {
|
||||
// sync code
|
||||
}
|
||||
|
||||
someMethodAsync () {
|
||||
// async version of MyPageObject.someMethod()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Once you've finished the migration you can remove the synchronous PageObject methods and clean up the naming.
|
||||
|
||||
If you don't like to maintain two different version of a PageObject method you can also migrate the whole PageObject to async and use [`browser.call`](https://webdriver.io/docs/api/browser/call) to execute the method in a synchronous environment, e.g.:
|
||||
|
||||
```js
|
||||
// before:
|
||||
// MyPageObject.someMethod()
|
||||
// after:
|
||||
browser.call(() => MyPageObject.someMethod())
|
||||
```
|
||||
|
||||
The `call` command will make sure that the asynchronous `someMethod` is resolved before moving on to the next command.
|
||||
|
||||
## Conclusion
|
||||
|
||||
As you can see in the [resulting rewrite PR](https://github.com/webdriverio/cucumber-boilerplate/pull/481/files) the complexity of this rewrite is fairly easy. Remember you can rewrite one step-definition at the time. WebdriverIO is perfectly able to handle sync and async execution in a single framework.
|
||||
|
|
@ -1,213 +0,0 @@
|
|||
---
|
||||
id: testrunner
|
||||
title: Testrunner
|
||||
---
|
||||
|
||||
WebdriverIO comes with its own test runner to help you start testing as quickly as possible. It is supposed to do all the work for you, allows integrate to 3rd party services, and helps you to run your tests as efficiently as possible.
|
||||
|
||||
WebdriverIO's testrunner is bundled separately in the NPM package `@wdio/cli`.
|
||||
|
||||
Install it like this:
|
||||
|
||||
```sh npm2yarn
|
||||
npm install @wdio/cli
|
||||
```
|
||||
|
||||
To see the command line interface help, type the following command in your terminal:
|
||||
|
||||
```sh
|
||||
$ npx wdio --help
|
||||
|
||||
wdio <command>
|
||||
|
||||
Commands:
|
||||
wdio config Initialize WebdriverIO and setup configuration in
|
||||
your current project.
|
||||
wdio install <type> <name> Add a `reporter`, `service`, or `framework` to
|
||||
your WebdriverIO project
|
||||
wdio repl <option> [capabilities] Run WebDriver session in command line
|
||||
wdio run <configPath> Run your WDIO configuration file to initialize
|
||||
your tests.
|
||||
|
||||
Options:
|
||||
--version Show version number [boolean]
|
||||
--help Show help [boolean]
|
||||
```
|
||||
|
||||
Sweet! Now you need to define a configuration file where all information about your tests, capabilities, and settings are set. Switch over to the [Configuration File](/docs/configuration) section to see what that file should look like.
|
||||
|
||||
With the `wdio` configuration helper, it is super easy to generate your config file. Just run:
|
||||
|
||||
```sh
|
||||
$ npx wdio config
|
||||
```
|
||||
|
||||
...and it launches the helper utility.
|
||||
|
||||
It will ask you questions and generate a config file for you in less than a minute.
|
||||
|
||||

|
||||
|
||||
Once you have your configuration file set up, you can start your tests by running:
|
||||
|
||||
```sh
|
||||
npx wdio run wdio.conf.js
|
||||
```
|
||||
|
||||
You can also initialize your test run without the `run` command:
|
||||
|
||||
```sh
|
||||
npx wdio wdio.conf.js
|
||||
```
|
||||
|
||||
That's it! Now, you can access to the selenium instance via the global variable `browser`.
|
||||
|
||||
## Commands
|
||||
|
||||
### `wdio config`
|
||||
|
||||
The `config` command runs the WebdriverIO configuration helper. This helper will ask you a few questions about your WebdriverIO project and create a `wdio.conf.js` file based on your answers.
|
||||
|
||||
Example:
|
||||
|
||||
```sh
|
||||
wdio config
|
||||
```
|
||||
|
||||
Options:
|
||||
|
||||
```
|
||||
--help prints WebdriverIO help menu [boolean]
|
||||
--npm Wether to install the packages using NPM instead of yarn [boolean]
|
||||
```
|
||||
|
||||
### `wdio run`
|
||||
|
||||
> This is the default command to run your configuration.
|
||||
|
||||
The `run` command initializes your WebdriverIO configuration file and runs your tests.
|
||||
|
||||
Example:
|
||||
|
||||
```sh
|
||||
wdio run ./wdio.conf.js --watch
|
||||
```
|
||||
|
||||
Options:
|
||||
|
||||
```
|
||||
--help prints WebdriverIO help menu [boolean]
|
||||
--version prints WebdriverIO version [boolean]
|
||||
--hostname, -h automation driver host address [string]
|
||||
--port, -p automation driver port [number]
|
||||
--user, -u username if using a cloud service as automation backend
|
||||
[string]
|
||||
--key, -k corresponding access key to the user [string]
|
||||
--watch watch specs for changes [boolean]
|
||||
--logLevel, -l level of logging verbosity
|
||||
[choices: "trace", "debug", "info", "warn", "error", "silent"]
|
||||
--bail stop test runner after specific amount of tests have
|
||||
failed [number]
|
||||
--baseUrl shorten url command calls by setting a base url [string]
|
||||
--waitforTimeout, -w timeout for all waitForXXX commands [number]
|
||||
--framework, -f defines the framework (Mocha, Jasmine or Cucumber) to
|
||||
run the specs [string]
|
||||
--reporters, -r reporters to print out the results on stdout [array]
|
||||
--suite overwrites the specs attribute and runs the defined
|
||||
suite [array]
|
||||
--spec run a certain spec file or wildcards - overrides specs piped
|
||||
from stdin [array]
|
||||
--exclude exclude spec file(s) from a run - overrides specs piped
|
||||
from stdin [array]
|
||||
--repeat Repeat specific specs and/or suites N times [number]
|
||||
--mochaOpts Mocha options
|
||||
--jasmineOpts Jasmine options
|
||||
--cucumberOpts Cucumber options
|
||||
```
|
||||
|
||||
> Note: Autocompiling can be easily controlled with `tsx` ENV Vars. See also the [TypeScript documentation](/docs/typescript).
|
||||
|
||||
### `wdio install`
|
||||
The `install` command allows you to add reporters and services to your WebdriverIO projects via the CLI.
|
||||
|
||||
Example:
|
||||
|
||||
```sh
|
||||
wdio install service sauce # installs @wdio/sauce-service
|
||||
wdio install reporter dot # installs @wdio/dot-reporter
|
||||
wdio install framework mocha # installs @wdio/mocha-framework
|
||||
```
|
||||
|
||||
If you want to install the packages using `yarn` instead, you can pass the `--yarn` flag to the command:
|
||||
|
||||
```sh
|
||||
wdio install service sauce --yarn
|
||||
```
|
||||
|
||||
You could also pass a custom configuration path if your WDIO config file is not in the same folder you're working on:
|
||||
|
||||
```sh
|
||||
wdio install service sauce --config="./path/to/wdio.conf.js"
|
||||
```
|
||||
|
||||
#### List of supported services
|
||||
|
||||
```
|
||||
sauce
|
||||
testingbot
|
||||
firefox-profile
|
||||
devtools
|
||||
browserstack
|
||||
appium
|
||||
intercept
|
||||
zafira-listener
|
||||
reportportal
|
||||
docker
|
||||
wiremock
|
||||
lambdatest
|
||||
vite
|
||||
nuxt
|
||||
```
|
||||
|
||||
#### List of supported reporters
|
||||
|
||||
```
|
||||
dot
|
||||
spec
|
||||
junit
|
||||
allure
|
||||
sumologic
|
||||
concise
|
||||
reportportal
|
||||
video
|
||||
html
|
||||
json
|
||||
mochawesome
|
||||
timeline
|
||||
```
|
||||
|
||||
#### List of supported frameworks
|
||||
|
||||
```
|
||||
mocha
|
||||
jasmine
|
||||
cucumber
|
||||
```
|
||||
|
||||
### `wdio repl`
|
||||
|
||||
The repl command allows to start an interactive command line interface to run WebdriverIO commands. It can be used for testing purposes or to just quickly spin up WebdriverIO session.
|
||||
|
||||
Run tests in local chrome:
|
||||
|
||||
```sh
|
||||
wdio repl chrome
|
||||
```
|
||||
|
||||
or run tests on Sauce Labs:
|
||||
|
||||
```sh
|
||||
wdio repl chrome -u $SAUCE_USERNAME -k $SAUCE_ACCESS_KEY
|
||||
```
|
||||
|
||||
You can apply the same arguments as you can in the [run command](#wdio-run).
|
||||
|
|
@ -1,155 +0,0 @@
|
|||
---
|
||||
id: timeouts
|
||||
title: Timeouts
|
||||
---
|
||||
|
||||
Each command in WebdriverIO is an asynchronous operation. A request is fired to the Selenium server (or a cloud service like [Sauce Labs](https://saucelabs.com)), and its response contains the result once the action has completed or failed.
|
||||
|
||||
Therefore, time is a crucial component in the whole testing process. When a certain action depends on the state of a different action, you need to make sure that they get executed in the right order. Timeouts play an important role when dealing with these issues.
|
||||
|
||||
<LiteYouTubeEmbed
|
||||
id="5oI37h4qxEw"
|
||||
title="Timeouts"
|
||||
/>
|
||||
|
||||
## WebDriver Timeouts
|
||||
|
||||
### Session Script Timeout
|
||||
|
||||
A session has an associated session script timeout that specifies a time to wait for asynchronous scripts to run. Unless stated otherwise, it is 30 seconds. You can set this timeout like so:
|
||||
|
||||
```js
|
||||
await browser.setTimeout({ 'script': 60000 })
|
||||
await browser.executeAsync((done) => {
|
||||
console.log('this should not fail')
|
||||
setTimeout(done, 59000)
|
||||
})
|
||||
```
|
||||
|
||||
### Session Page Load Timeout
|
||||
|
||||
A session has an associated session page load timeout that specifies a time to wait for the page loading to complete. Unless stated otherwise, it is 300,000 milliseconds.
|
||||
|
||||
You can set this timeout like so:
|
||||
|
||||
```js
|
||||
await browser.setTimeout({ 'pageLoad': 10000 })
|
||||
```
|
||||
|
||||
> The `pageLoad` keyword is a part of the official WebDriver [specification](https://www.w3.org/TR/webdriver/#set-timeouts), but might not be [supported](https://github.com/seleniumhq/selenium-google-code-issue-archive/issues/687) for your browser (the previous name is `page load`).
|
||||
|
||||
### Session Implicit Wait Timeout
|
||||
|
||||
A session has an associated session implicit wait timeout. This specifies the time to wait for the implicit element location strategy when locating elements using the [`findElement`](/docs/api/webdriver#findelement) or [`findElements`](/docs/api/webdriver#findelements) commands ([`$`](/docs/api/browser/$) or [`$$`](/docs/api/browser/$$), respectively, when running WebdriverIO with or without the WDIO testrunner). Unless stated otherwise, it is 0 milliseconds.
|
||||
|
||||
You can set this timeout via:
|
||||
|
||||
```js
|
||||
await browser.setTimeout({ 'implicit': 5000 })
|
||||
```
|
||||
|
||||
## WebdriverIO related timeouts
|
||||
|
||||
### `WaitFor*` timeout
|
||||
|
||||
WebdriverIO provides multiple commands to wait on elements to reach a certain state (e.g. enabled, visible, existing). These commands take a selector argument and a timeout number, which determines how long the instance should wait for that element to reach the state. The `waitforTimeout` option allows you to set the global timeout for all `waitFor*` commands, so you don't need to set the same timeout over and over again. _(Note the lowercase `f`!)_
|
||||
|
||||
```js
|
||||
// wdio.conf.js
|
||||
export const config = {
|
||||
// ...
|
||||
waitforTimeout: 5000,
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
In your tests, you now can do this:
|
||||
|
||||
```js
|
||||
const myElem = await $('#myElem')
|
||||
await myElem.waitForDisplayed()
|
||||
|
||||
// you can also overwrite the default timeout if needed
|
||||
await myElem.waitForDisplayed({ timeout: 10000 })
|
||||
```
|
||||
|
||||
## Framework related timeouts
|
||||
|
||||
The testing framework you’re using with WebdriverIO has to deal with timeouts, especially since everything is asynchronous. It ensures that the test process doesn't get stuck if something goes wrong.
|
||||
|
||||
By default, the timeout is 10 seconds, which means that a single test should not take longer than that.
|
||||
|
||||
A single test in Mocha looks like:
|
||||
|
||||
```js
|
||||
it('should login into the application', async () => {
|
||||
await browser.url('/login')
|
||||
|
||||
const form = await $('form')
|
||||
const username = await $('#username')
|
||||
const password = await $('#password')
|
||||
|
||||
await username.setValue('userXY')
|
||||
await password.setValue('******')
|
||||
await form.submit()
|
||||
|
||||
expect(await browser.getTitle()).to.be.equal('Admin Area')
|
||||
})
|
||||
```
|
||||
|
||||
In Cucumber, the timeout applies to a single step definition. However, if you want to increase the timeout because your test takes longer than the default value, you need to set it in the framework options.
|
||||
|
||||
<Tabs
|
||||
defaultValue="mocha"
|
||||
values={[
|
||||
{label: 'Mocha', value: 'mocha'},
|
||||
{label: 'Jasmine', value: 'jasmine'},
|
||||
{label: 'Cucumber', value: 'cucumber'}
|
||||
]
|
||||
}>
|
||||
<TabItem value="mocha">
|
||||
|
||||
```js
|
||||
// wdio.conf.js
|
||||
export const config = {
|
||||
// ...
|
||||
framework: 'mocha',
|
||||
mochaOpts: {
|
||||
timeout: 20000
|
||||
},
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="jasmine">
|
||||
|
||||
```js
|
||||
// wdio.conf.js
|
||||
export const config = {
|
||||
// ...
|
||||
framework: 'jasmine',
|
||||
jasmineOpts: {
|
||||
defaultTimeoutInterval: 20000
|
||||
},
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="cucumber">
|
||||
|
||||
```js
|
||||
// wdio.conf.js
|
||||
export const config = {
|
||||
// ...
|
||||
framework: 'cucumber',
|
||||
cucumberOpts: {
|
||||
timeout: 20000
|
||||
},
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
|
@ -1,136 +0,0 @@
|
|||
---
|
||||
id: typescript
|
||||
title: TypeScript Setup
|
||||
---
|
||||
|
||||
You can write tests using [TypeScript](http://www.typescriptlang.org) to get auto-completion and type safety.
|
||||
|
||||
You will need [`tsx`](https://github.com/privatenumber/tsx) installed in `devDependencies`, via:
|
||||
|
||||
```bash npm2yarn
|
||||
$ npm install tsx --save-dev
|
||||
```
|
||||
|
||||
WebdriverIO will automatically detect if these dependencies are installed and will compile your config and tests for you. Ensure to have a `tsconfig.json` in the same directory as your WDIO config.
|
||||
|
||||
#### Custom TSConfig
|
||||
|
||||
If you need to set a different path for `tsconfig.json` please set the TSCONFIG_PATH environment variable with your desired path, or use wdio config's [tsConfigPath setting](/docs/configurationfile).
|
||||
|
||||
Alternatively, you can use the [environment variable](https://tsx.is/dev-api/node-cli#custom-tsconfig-json-path) for `tsx`.
|
||||
|
||||
|
||||
#### Type Checking
|
||||
|
||||
Note that `tsx` does not support type-checking - if you wish to check your types then you will need to do this in a separate step with `tsc`.
|
||||
|
||||
## Framework Setup
|
||||
|
||||
Your `tsconfig.json` needs the following:
|
||||
|
||||
```json title="tsconfig.json"
|
||||
{
|
||||
"compilerOptions": {
|
||||
"types": ["node", "@wdio/globals/types"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Please avoid importing `webdriverio` or `@wdio/sync` explicitly. `WebdriverIO` and `WebDriver` types are accessible from anywhere once added to `types` in `tsconfig.json`. If you use additional WebdriverIO services, plugins or the `devtools` automation package, please also add them to the `types` list as many provide additional typings.
|
||||
|
||||
## Framework Types
|
||||
|
||||
Depending on the framework you use, you will need to add the types for that framework to your `tsconfig.json` types property, as well as install its type definitions. This is especially important if you want to have type support for the built-in assertion library [`expect-webdriverio`](https://www.npmjs.com/package/expect-webdriverio).
|
||||
|
||||
For instance, if you decide to use the Mocha framework, you need to install `@types/mocha` and add it like this to have all types globally available:
|
||||
|
||||
<Tabs
|
||||
defaultValue="mocha"
|
||||
values={[
|
||||
{label: 'Mocha', value: 'mocha'},
|
||||
{label: 'Jasmine', value: 'jasmine'},
|
||||
{label: 'Cucumber', value: 'cucumber'},
|
||||
]
|
||||
}>
|
||||
<TabItem value="mocha">
|
||||
|
||||
```json title="tsconfig.json"
|
||||
{
|
||||
"compilerOptions": {
|
||||
"types": ["node", "@wdio/globals/types", "@wdio/mocha-framework"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="jasmine">
|
||||
|
||||
```json title="tsconfig.json"
|
||||
{
|
||||
"compilerOptions": {
|
||||
"types": ["node", "@wdio/globals/types", "@wdio/jasmine-framework"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="cucumber">
|
||||
|
||||
```json title="tsconfig.json"
|
||||
{
|
||||
"compilerOptions": {
|
||||
"types": ["node", "@wdio/globals/types", "@wdio/cucumber-framework"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## Services
|
||||
|
||||
If you use services that add commands to the browser scope you also need to include these into your `tsconfig.json`. For example if you use the `@wdio/lighthouse-service` ensure that you add it to the `types` as well, e.g.:
|
||||
|
||||
```json title="tsconfig.json"
|
||||
{
|
||||
"compilerOptions": {
|
||||
"types": [
|
||||
"node",
|
||||
"@wdio/globals/types",
|
||||
"@wdio/mocha-framework",
|
||||
"@wdio/lighthouse-service"
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Adding services and reporters to your TypeScript config also strengthen the type safety of your WebdriverIO config file.
|
||||
|
||||
## Type Definitions
|
||||
|
||||
When running WebdriverIO commands all properties are usually typed so that you don't have to deal with importing additional types. However there are cases where you want to define variables upfront. To ensure that these are type safe you can use all types defined in the [`@wdio/types`](https://www.npmjs.com/package/@wdio/types) package. For example if you like to define the remote option for `webdriverio` you can do:
|
||||
|
||||
```ts
|
||||
import type { Options } from '@wdio/types'
|
||||
|
||||
// Here is an example where you might want to import the types directly
|
||||
const remoteConfig: Options.WebdriverIO = {
|
||||
hostname: 'http://localhost',
|
||||
port: '4444' // Error: Type 'string' is not assignable to type 'number'.ts(2322)
|
||||
capabilities: {
|
||||
browserName: 'chrome'
|
||||
}
|
||||
}
|
||||
|
||||
// For other cases, you can use the `WebdriverIO` namespace
|
||||
export const config: WebdriverIO.Config = {
|
||||
...remoteConfig
|
||||
// Other configs options
|
||||
}
|
||||
```
|
||||
|
||||
## Tips and Hints
|
||||
|
||||
### Compile & Lint
|
||||
|
||||
To be entirely safe, you may consider following the best practices: compile your code with TypeScript compiler (run `tsc` or `npx tsc`) and have [eslint](https://www.npmjs.com/package/@typescript-eslint/eslint-plugin) running on [pre-commit hook](https://github.com/typicode/husky).
|
||||
|
|
@ -1,137 +0,0 @@
|
|||
---
|
||||
id: vscode-extension-testing
|
||||
title: VS Code Extension Testing
|
||||
---
|
||||
|
||||
WebdriverIO allows you to seamlessly test your [VS Code](https://code.visualstudio.com/) extensions from end to end in the VS Code Desktop IDE or as web extension. You only need to provide a path to your extension and the framework does the rest. With the [`wdio-vscode-service`](https://www.npmjs.com/package/wdio-vscode-service) everything is taken care of and much more:
|
||||
|
||||
- 🏗️ Installing VSCode (either stable, insiders or a specified version)
|
||||
- ⬇️ Downloading Chromedriver specific to given VSCode version
|
||||
- 🚀 Enables you to access the VSCode API from your tests
|
||||
- 🖥️ Starting VSCode with custom user settings (including support for VSCode on Ubuntu, MacOS and Windows)
|
||||
- 🌐 Or serves VSCode from a server to be accessed by any browser for testing web extensions
|
||||
- 📔 Bootstrapping page objects with locators matching your VSCode version
|
||||
|
||||
## Getting Started
|
||||
|
||||
To initiate a new WebdriverIO project, run:
|
||||
|
||||
```sh
|
||||
npm create wdio@latest ./
|
||||
```
|
||||
|
||||
An installation wizard will guide you through the process. Ensure you select TypeScript as compiler and don't have it generate page objects for you given this project comes with all page objects needed. Then make sure to select `chromedriver` and `vscode` within the list of services, e.g.:
|
||||
|
||||

|
||||
|
||||
|
||||
__Note:__ remove `chromedriver` from the list of services in the generated `wdio.conf.ts` afterwards. See also configuration example below.
|
||||
|
||||
For more information on how to install `WebdriverIO`, please check the [project docs](https://webdriver.io/docs/gettingstarted).
|
||||
|
||||
## Example Configuration
|
||||
|
||||
To use the service you need to add `vscode` to your list of services, optionally followed by a configuration object. This will make WebdriverIO download given VSCode binaries and appropiate Chromedriver version:
|
||||
|
||||
```js
|
||||
// wdio.conf.ts
|
||||
export const config = {
|
||||
outputDir: 'trace',
|
||||
// ...
|
||||
capabilities: [{
|
||||
browserName: 'vscode',
|
||||
browserVersion: '1.71.0', // "insiders" or "stable" for latest VSCode version
|
||||
'wdio:vscodeOptions': {
|
||||
extensionPath: __dirname,
|
||||
userSettings: {
|
||||
"editor.fontSize": 14
|
||||
}
|
||||
}
|
||||
}],
|
||||
services: ['vscode'],
|
||||
/**
|
||||
* optionally you can define the path WebdriverIO stores all
|
||||
* VSCode and Chromedriver binaries, e.g.:
|
||||
* services: [['vscode', { cachePath: __dirname }]]
|
||||
*/
|
||||
// ...
|
||||
};
|
||||
```
|
||||
|
||||
If you define `wdio:vscodeOptions` with any other `browserName` but `vscode`, e.g. `chrome`, the service will serve the extension as web extension. If you test on Chrome no additional driver service is required, e.g.:
|
||||
|
||||
```js
|
||||
// wdio.conf.ts
|
||||
export const config = {
|
||||
outputDir: 'trace',
|
||||
// ...
|
||||
capabilities: [{
|
||||
browserName: 'chrome',
|
||||
'wdio:vscodeOptions': {
|
||||
extensionPath: __dirname
|
||||
}
|
||||
}],
|
||||
services: ['vscode'],
|
||||
// ...
|
||||
};
|
||||
```
|
||||
|
||||
_Note:_ when testing web extensions you can only choose between `stable` or `insiders` as `browserVersion`.
|
||||
|
||||
### TypeScript Setup
|
||||
|
||||
In your `tsconfig.json` make sure to add `wdio-vscode-service` to your list of types:
|
||||
|
||||
```json
|
||||
{
|
||||
"compilerOptions": {
|
||||
"types": [
|
||||
"node",
|
||||
"webdriverio/async",
|
||||
"@wdio/mocha-framework",
|
||||
"expect-webdriverio",
|
||||
"wdio-vscode-service"
|
||||
],
|
||||
"target": "es2020",
|
||||
"moduleResolution": "node16"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
You can then use the `getWorkbench` method to access the page objects for the locators matching your desired VSCode version:
|
||||
|
||||
```ts
|
||||
describe('WDIO VSCode Service', () => {
|
||||
it('should be able to load VSCode', async () => {
|
||||
const workbench = await browser.getWorkbench()
|
||||
expect(await workbench.getTitleBar().getTitle())
|
||||
.toBe('[Extension Development Host] - README.md - wdio-vscode-service - Visual Studio Code')
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
From there you can access all page objects by using the right page object methods. Find out more about all available page objects and their methods in the [page object docs](https://webdriverio-community.github.io/wdio-vscode-service/).
|
||||
|
||||
### Accessing VSCode APIs
|
||||
|
||||
If you like to execute certain automation through the [VSCode API](https://code.visualstudio.com/api/references/vscode-api) you can do that by running remote commands via the custom `executeWorkbench` command. This command allows to remote execute code from your test inside the VSCode environment and enables to access the VSCode API. You can pass arbitrary paramaters into the function which will then be propagated into the function. The `vscode` object will be always passed in as first argument following the outer function parameters. Note that you can not access variables outside of the function scoped as the callback is executed remotely. Here is an example:
|
||||
|
||||
```ts
|
||||
const workbench = await browser.getWorkbench()
|
||||
await browser.executeWorkbench((vscode, param1, param2) => {
|
||||
vscode.window.showInformationMessage(`I am an ${param1} ${param2}!`)
|
||||
}, 'API', 'call')
|
||||
|
||||
const notifs = await workbench.getNotifications()
|
||||
console.log(await notifs[0].getMessage()) // outputs: "I am an API call!"
|
||||
```
|
||||
|
||||
For the full page object documentation, check out the [docs](https://webdriverio-community.github.io/wdio-vscode-service/modules.html). You can find various usage examples in this [project's test suite](https://github.com/webdriverio-community/wdio-vscode-service/blob/main/test/specs).
|
||||
|
||||
## More Information
|
||||
|
||||
You can learn more about how to configure the [`wdio-vscode-service`](https://www.npmjs.com/package/wdio-vscode-service) and how create custom page objects in the [service docs](/docs/wdio-vscode-service). You can also watch the following talk by [Christian Bromann](https://twitter.com/bromann) on [_Testing Complex VSCode Extensions With the Power of Web Standards_](https://www.youtube.com/watch?v=PhGNTioBUiU):
|
||||
|
||||
<LiteYouTubeEmbed id="PhGNTioBUiU" title="Testing Complex VSCode Extensions With the Power of Web Standards" />
|
||||
|
|
@ -1,424 +0,0 @@
|
|||
---
|
||||
id: visual-testing
|
||||
title: Visual Testing
|
||||
---
|
||||
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
|
||||
## What can it do?
|
||||
|
||||
WebdriverIO provides image comparisons on screens, elements or a full-page for
|
||||
|
||||
- 🖥️ Desktop browsers (Chrome / Firefox / Safari / Microsoft Edge)
|
||||
- 📱 Mobile / Tablet browsers (Chrome on Android emulators / Safari on iOS Simulators / Simulators / real devices) via Appium
|
||||
- 📱 Native Apps (Android emulators / iOS Simulators / real devices) via Appium (🌟 **NEW** 🌟)
|
||||
- 📳 Hybrid apps via Appium
|
||||
|
||||
through the [`@wdio/visual-service`](https://www.npmjs.com/package/@wdio/visual-service) which is a lightweight WebdriverIO service.
|
||||
|
||||
This allows you to:
|
||||
|
||||
- save or compare **screens/elements/full-page** screens against a baseline
|
||||
- automatically **create a baseline** when no baseline is there
|
||||
- **block out custom regions** and even **automatically exclude** a status and or toolbars (mobile only) during a comparison
|
||||
- increase the element dimensions screenshots
|
||||
- **hide text** during website comparison to:
|
||||
- **improve stability** and prevent font rendering flakiness
|
||||
- only focus on the **layout** of a website
|
||||
- use **different comparison methods** and a set of **additional matchers** for better readable tests
|
||||
- verify how your website will **support tabbing with your keyboard)**, see also [Tabbing through a website](#tabbing-through-a-website)
|
||||
- and much more, see the [service](./visual-testing/service-options) and [method](./visual-testing/method-options) options
|
||||
|
||||
The service is a lightweight module to retrieve the needed data and screenshots for all browsers/devices. The comparison power comes from [ResembleJS](https://github.com/Huddle/Resemble.js). If you want to compare images online you can check the [online tool](http://rsmbl.github.io/Resemble.js/).
|
||||
|
||||
:::info NOTE For Native/Hybrid Apps
|
||||
|
||||
Please use the property `isHybridApp:true` in your service settings when you want to use it for Hybrid Apps.
|
||||
:::
|
||||
|
||||
## Installation
|
||||
|
||||
The easiest way is to keep `@wdio/visual-service` as a dev-dependency in your `package.json`, via:
|
||||
|
||||
```sh
|
||||
npm install --save-dev @wdio/visual-service
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
`@wdio/visual-service` can be used as a normal service. You can set it up in your configuration file with the following:
|
||||
|
||||
```js
|
||||
import path from "node:path";
|
||||
|
||||
// wdio.conf.ts
|
||||
export const config = {
|
||||
// ...
|
||||
// =====
|
||||
// Setup
|
||||
// =====
|
||||
services: [
|
||||
[
|
||||
"visual",
|
||||
{
|
||||
// Some options, see the docs for more
|
||||
baselineFolder: path.join(process.cwd(), "tests", "baseline"),
|
||||
formatImageName: "{tag}-{logName}-{width}x{height}",
|
||||
screenshotPath: path.join(process.cwd(), "tmp"),
|
||||
savePerInstance: true,
|
||||
// ... more options
|
||||
},
|
||||
],
|
||||
],
|
||||
// ...
|
||||
};
|
||||
```
|
||||
|
||||
More service options can be found [here](/docs/visual-testing/service-options).
|
||||
|
||||
Once set up in your WebdriverIO configuration, you can go ahead and add visual assertions to [your tests](/docs/visual-testing/writing-tests).
|
||||
|
||||
### Capabilities
|
||||
|
||||
To use the Visual Testing module, **you don’t need to add any extra options to your capabilities**. However, in some cases, you may want to add additional metadata to your visual tests, such as a `logName`.
|
||||
|
||||
The `logName` allows you to assign a custom name to each capability, which can then be included in the image filenames. This is particularly useful for distinguishing screenshots taken across different browsers, devices, or configurations.
|
||||
|
||||
To enable this, you can define `logName` in the `capabilities` section and ensure the `formatImageName` option in the Visual Testing service references it. Here's how you can set it up:
|
||||
|
||||
```js
|
||||
import path from "node:path";
|
||||
|
||||
// wdio.conf.ts
|
||||
export const config = {
|
||||
// ...
|
||||
// =====
|
||||
// Setup
|
||||
// =====
|
||||
capabilities: [
|
||||
{
|
||||
browserName: 'chrome',
|
||||
'wdio-ics:options': {
|
||||
logName: 'chrome-mac-15', // Custom log name for Chrome
|
||||
},
|
||||
}
|
||||
{
|
||||
browserName: 'firefox',
|
||||
'wdio-ics:options': {
|
||||
logName: 'firefox-mac-15', // Custom log name for Firefox
|
||||
},
|
||||
}
|
||||
],
|
||||
services: [
|
||||
[
|
||||
"visual",
|
||||
{
|
||||
// Some options, see the docs for more
|
||||
baselineFolder: path.join(process.cwd(), "tests", "baseline"),
|
||||
screenshotPath: path.join(process.cwd(), "tmp"),
|
||||
// The format below will use the `logName` from capabilities
|
||||
formatImageName: "{tag}-{logName}-{width}x{height}",
|
||||
// ... more options
|
||||
},
|
||||
],
|
||||
],
|
||||
// ...
|
||||
};
|
||||
```
|
||||
|
||||
#### How it works
|
||||
|
||||
1. Setting Up the `logName`:
|
||||
|
||||
- In the `capabilities` section, assign a unique `logName` to each browser or device. For example, `chrome-mac-15` identifies tests running on Chrome on macOS version 15.
|
||||
|
||||
2. Custom Image Naming:
|
||||
|
||||
- The `formatImageName` option integrates the `logName` into the screenshot filenames. For example, if the `tag` is homepage and the resolution is `1920x1080`, the resulting filename might look like this:
|
||||
|
||||
`homepage-chrome-mac-15-1920x1080.png`
|
||||
|
||||
3. Benefits of Custom Naming:
|
||||
|
||||
- Distinguishing between screenshots from different browsers or devices becomes much easier, especially when managing baselines and debugging discrepancies.
|
||||
|
||||
4. Note on Defaults:
|
||||
|
||||
-If `logName` is not set in the capabilities, the `formatImageName` option will show it as an empty string in the filenames (`homepage--15-1920x1080.png`)
|
||||
|
||||
### WebdriverIO MultiRemote
|
||||
|
||||
We also support [MultiRemote](https://webdriver.io/docs/multiremote/). To make this work properly make sure that you add `wdio-ics:options` to your
|
||||
capabilities as you can see below. This will make sure that each screenshot will have its own unique name.
|
||||
|
||||
[Writing your tests](/docs/visual-testing/writing-tests) will not be any different in comparison to using the [testrunner](https://webdriver.io/docs/testrunner)
|
||||
|
||||
```js
|
||||
// wdio.conf.js
|
||||
export const config = {
|
||||
capabilities: {
|
||||
chromeBrowserOne: {
|
||||
capabilities: {
|
||||
browserName: "chrome",
|
||||
"goog:chromeOptions": {
|
||||
args: ["disable-infobars"],
|
||||
},
|
||||
// THIS!!!
|
||||
"wdio-ics:options": {
|
||||
logName: "chrome-latest-one",
|
||||
},
|
||||
},
|
||||
},
|
||||
chromeBrowserTwo: {
|
||||
capabilities: {
|
||||
browserName: "chrome",
|
||||
"goog:chromeOptions": {
|
||||
args: ["disable-infobars"],
|
||||
},
|
||||
// THIS!!!
|
||||
"wdio-ics:options": {
|
||||
logName: "chrome-latest-two",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
### Running Programmatically
|
||||
|
||||
Here is a minimal example of how to use `@wdio/visual-service` via `remote` options:
|
||||
|
||||
```js
|
||||
import { remote } from "webdriverio";
|
||||
import VisualService from "@wdio/visual-service";
|
||||
|
||||
let visualService = new VisualService({
|
||||
autoSaveBaseline: true,
|
||||
});
|
||||
|
||||
const browser = await remote({
|
||||
logLevel: "silent",
|
||||
capabilities: {
|
||||
browserName: "chrome",
|
||||
},
|
||||
});
|
||||
|
||||
// "Start" the service to add the custom commands to the `browser`
|
||||
visualService.remoteSetup(browser);
|
||||
|
||||
await browser.url("https://webdriver.io/");
|
||||
|
||||
// or use this for ONLY saving a screenshot
|
||||
await browser.saveFullPageScreen("examplePaged", {});
|
||||
|
||||
// or use this for validating. Both methods don't need to be combined, see the FAQ
|
||||
await browser.checkFullPageScreen("examplePaged", {});
|
||||
|
||||
await browser.deleteSession();
|
||||
```
|
||||
|
||||
### Tabbing through a website
|
||||
|
||||
You can check if a website is accessible by using the keyboard <kbd>TAB</kbd>-key. Testing this part of accessibility has always been a time-consuming (manual) job and pretty hard to do through automation.
|
||||
With the methods `saveTabbablePage` and `checkTabbablePage`, you can now draw lines and dots on your website to verify the tabbing order.
|
||||
|
||||
Be aware of the fact that this is only useful for desktop browsers and **NOT\*\*** for mobile devices. All desktop browsers support this feature.
|
||||
|
||||
:::note
|
||||
|
||||
The work is inspired by [Viv Richards](https://github.com/vivrichards600) his blog post about ["AUTOMATING PAGE TABABILITY (IS THAT A WORD?) WITH VISUAL TESTING"](https://vivrichards.co.uk/accessibility/automating-page-tab-flows-using-visual-testing-and-javascript).
|
||||
|
||||
The way tabbable elements are selected is based on the module [tabbable](https://github.com/davidtheclark/tabbable). If there are any issues regarding the tabbing please check the [README.md](https://github.com/davidtheclark/tabbable/blob/master/README.md) and especially the [More ](https://github.com/davidtheclark/tabbable/blob/master/README.md#more-details)Details section.
|
||||
|
||||
:::
|
||||
|
||||
#### How does it work
|
||||
|
||||
Both methods will create a `canvas` element on your website and draw lines and dots to show you where your TAB would go if an end-user would use it. After that, it will create a full-page screenshot to give you a good overview of the flow.
|
||||
|
||||
:::important
|
||||
|
||||
\*\*Use the `saveTabbablePage` only when you need to create a screenshot and DON'T want to compare it \*\*with a **baseline** image.\*\*\*\*
|
||||
|
||||
:::
|
||||
|
||||
When you want to compare the tabbing flow with a baseline, then you can use the `checkTabbablePage`-method. You **DON'T** need to use the two methods together. If there is already a baseline image created, which can automatically be done by providing `autoSaveBaseline: true` when you instantiate the service,
|
||||
the `checkTabbablePage` will first create the _actual_ image and then compare it against the baseline.
|
||||
|
||||
##### Options
|
||||
|
||||
Both methods use the same options as the [`saveFullPageScreen`](https://github.com/wswebcreation/webdriver-image-comparison/blob/master/docs/OPTIONS.md#savefullpagescreen-or-savetabbablepage) or the
|
||||
[`compareFullPageScreen`](https://github.com/wswebcreation/webdriver-image-comparison/blob/master/docs/OPTIONS.md#comparefullpagescreen-or-comparetabbablepage).
|
||||
|
||||
#### Example
|
||||
|
||||
This is an example of how the tabbing works on our [guinea pig website](https://guinea-pig.webdriver.io/image-compare.html):
|
||||
|
||||

|
||||
|
||||
### Automatically update failed Visual Snapshots
|
||||
|
||||
Update the baseline images through the command line by adding the argument `--update-visual-baseline`. This will
|
||||
|
||||
- automatically copy the actual take screenshot and put it in the baseline folder
|
||||
- if there are differences it will let the test pass because the baseline has been updated
|
||||
|
||||
**Usage:**
|
||||
|
||||
```sh
|
||||
npm run test.local.desktop --update-visual-baseline
|
||||
```
|
||||
|
||||
When running logs info/debug mode you will see the following logs added
|
||||
|
||||
```logs
|
||||
[0-0] ..............
|
||||
[0-0] #####################################################################################
|
||||
[0-0] INFO:
|
||||
[0-0] Updated the actual image to
|
||||
[0-0] /Users/wswebcreation/Git/wdio/visual-testing/localBaseline/chromel/demo-chrome-1366x768.png
|
||||
[0-0] #####################################################################################
|
||||
[0-0] ..........
|
||||
```
|
||||
|
||||
## Typescript support
|
||||
|
||||
This module includes TypeScript support, allowing you to benefit from auto-completion, type safety, and improved developer experience when using the Visual Testing service.
|
||||
|
||||
### Step 1: Add Type Definitions
|
||||
|
||||
To ensure TypeScript recognizes the module types, add the following entry to the types field in your tsconfig.json:
|
||||
|
||||
```json
|
||||
{
|
||||
"compilerOptions": {
|
||||
"types": ["@wdio/visual-service"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Step 2: Enable Type Safety for Service Options
|
||||
|
||||
To enforce type checking on the service options, update your WebdriverIO configuration:
|
||||
|
||||
```ts
|
||||
// wdio.conf.ts
|
||||
import { join } from 'node:path';
|
||||
// Import the type definition
|
||||
import type { VisualServiceOptions } from '@wdio/visual-service';
|
||||
|
||||
export const config = {
|
||||
// ...
|
||||
// =====
|
||||
// Setup
|
||||
// =====
|
||||
services: [
|
||||
[
|
||||
"visual",
|
||||
{
|
||||
// Service options
|
||||
baselineFolder: join(process.cwd(), './__snapshots__/'),
|
||||
formatImageName: '{tag}-{logName}-{width}x{height}',
|
||||
screenshotPath: join(process.cwd(), '.tmp/'),
|
||||
} satisfies VisualServiceOptions, // Ensures type safety
|
||||
],
|
||||
],
|
||||
// ...
|
||||
};
|
||||
```
|
||||
|
||||
## System Requirements
|
||||
|
||||
### Version 5 and up
|
||||
|
||||
For version 5 and up, this module is a purely JavaScript-based module with no additional system dependencies beyond the general [project requirements](/docs/gettingstarted#system-requirements). It uses [Jimp](https://github.com/jimp-dev/jimp) which is an image processing library for Node written entirely in JavaScript, with zero native dependencies.
|
||||
|
||||
### Version 4 and Lower
|
||||
|
||||
For version 4 and lower, this module relies on [Canvas](https://github.com/Automattic/node-canvas), a canvas implementation for Node.js. Canvas depends on [Cairo](https://cairographics.org/).
|
||||
|
||||
#### Installation Details
|
||||
|
||||
By default, binaries for macOS, Linux and Windows will be downloaded during your project's `npm install`. If you don't have a supported OS or processor architecture, the module will be compiled on your system. This requires several dependencies, including Cairo and Pango.
|
||||
|
||||
For detailed installation information, see the [node-canvas wiki](https://github.com/Automattic/node-canvas/wiki/_pages). Below are one-line installation instructions for common operating systems. Note that `libgif/giflib`, `librsvg`, and `libjpeg` are optional and only needed for GIF, SVG, and JPEG support, respectively. Cairo v1.10.0 or later is required.
|
||||
|
||||
<Tabs
|
||||
defaultValue="osx"
|
||||
values={[
|
||||
{label: 'OS', value: 'osx'},
|
||||
{label: 'Ubuntu', value: 'ubuntu'},
|
||||
{label: 'Fedora', value: 'fedora'},
|
||||
{label: 'Solaris', value: 'solaris'},
|
||||
{label: 'OpenBSD', value: 'openbsd'},
|
||||
{label: 'Window', value: 'windows'},
|
||||
{label: 'Others', value: 'others'},
|
||||
]}
|
||||
|
||||
> <TabItem value="osx">
|
||||
|
||||
````
|
||||
Using [Homebrew](https://brew.sh/):
|
||||
|
||||
```sh
|
||||
brew install pkg-config cairo pango libpng jpeg giflib librsvg pixman
|
||||
```
|
||||
|
||||
**Mac OS X v10.11+:** If you have recently updated to Mac OS X v10.11+ and are experiencing trouble when compiling, run the following command: `xcode-select --install`. Read more about the problem [on Stack Overflow](http://stackoverflow.com/a/32929012/148072).
|
||||
If you have Xcode 10.0 or higher installed, to build from source you need NPM 6.4.1 or higher.
|
||||
````
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="ubuntu">
|
||||
|
||||
````
|
||||
```sh
|
||||
sudo apt-get install build-essential libcairo2-dev libpango1.0-dev libjpeg-dev libgif-dev librsvg2-dev
|
||||
```
|
||||
````
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="fedora">
|
||||
|
||||
````
|
||||
```sh
|
||||
sudo yum install gcc-c++ cairo-devel pango-devel libjpeg-turbo-devel giflib-devel
|
||||
```
|
||||
````
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="solaris">
|
||||
|
||||
````
|
||||
```sh
|
||||
pkgin install cairo pango pkg-config xproto renderproto kbproto xextproto
|
||||
```
|
||||
````
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="openbsd">
|
||||
|
||||
````
|
||||
```sh
|
||||
doas pkg_add cairo pango png jpeg giflib
|
||||
```
|
||||
````
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="windows">
|
||||
|
||||
```
|
||||
See the [wiki](https://github.com/Automattic/node-canvas/wiki/Installation:-Windows)
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="others">
|
||||
|
||||
```
|
||||
See the [wiki](https://github.com/Automattic/node-canvas/wiki)
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
---
|
||||
id: watcher
|
||||
title: Watch Test Files
|
||||
---
|
||||
|
||||
With the WDIO testrunner you can watch files while you are working on them. They automatically rerun if you change either something in your app or in your test files. By adding a `--watch` flag when calling the `wdio` command the testrunner will wait for file changes after it ran all tests, e.g.
|
||||
|
||||
```sh
|
||||
wdio wdio.conf.js --watch
|
||||
```
|
||||
|
||||
By default it only watches for changes in your `specs` files. However by setting a `filesToWatch` property in your `wdio.conf.js` that contains a list of file paths (globbing supported) it will also watch for these files to be changed in order to rerun the whole suite. This is useful if you want to automatically rerun all your tests if you have changed your application code, e.g.
|
||||
|
||||
```js
|
||||
// wdio.conf.js
|
||||
export const config = {
|
||||
// ...
|
||||
filesToWatch: [
|
||||
// watch for all JS files in my app
|
||||
'./src/app/**/*.js'
|
||||
],
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
:::info
|
||||
Try to run tests in parallel as much as possible. E2E tests are, by nature, slow. Rerunning tests is only useful if you can keep the individual test run time short. In order to save time, the testrunner keeps WebDriver sessions alive while waiting for file changes. Make sure your WebDriver backend can be modified so that it doesn't automatically close the session if no command was executed after some duration of time.
|
||||
:::
|
||||
|
|
@ -1,95 +0,0 @@
|
|||
---
|
||||
id: web-extension-testing
|
||||
title: Web Extension Testing
|
||||
---
|
||||
|
||||
WebdriverIO is the ideal tool to automate a browser. Web Extensions are a part of the browser and can be automated in the same way. Whenever your web extension uses content scripts to run JavaScript on websites or offer a popup modal, you can run an e2e test for that using WebdriverIO.
|
||||
|
||||
## Loading a Web Extension into the Browser
|
||||
|
||||
As a first step we have to load the extension under test into the browser as part of our session. This works differently for Chrome and Firefox.
|
||||
|
||||
:::info
|
||||
|
||||
These docs leave out Safari web extensions as their support for it is way behind and user demand not high. If you are building a web extension for Safari, please [raise an issue](https://github.com/webdriverio/webdriverio/issues/new?assignees=&labels=Docs+%F0%9F%93%96%2CNeeds+Triaging+%E2%8F%B3&template=documentation.yml&title=%5B%F0%9F%93%96+Docs%5D%3A+%3Ctitle%3E) and collaborate on including it here as well.
|
||||
|
||||
:::
|
||||
|
||||
### Chrome
|
||||
|
||||
Loading a web extension in Chrome can be done through providing a `base64` encoded string of the `crx` file or by providing a path to the web extension folder. The easiest is just to do the latter by defining your Chrome capabilities as following:
|
||||
|
||||
```js wdio.conf.js
|
||||
import path from 'node:path'
|
||||
import url from 'node:url'
|
||||
|
||||
const __dirname = url.fileURLToPath(new URL('.', import.meta.url))
|
||||
|
||||
export const config = {
|
||||
// ...
|
||||
capabilities: [{
|
||||
browserName,
|
||||
'goog:chromeOptions': {
|
||||
// given your wdio.conf.js is in the root directory and your compiled
|
||||
// web extension files are located in the `./dist` folder
|
||||
args: [`--load-extension=${path.join(__dirname, '..', '..', 'dist')}`]
|
||||
}
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
:::info
|
||||
|
||||
If you automate a different browser than Chrome, e.g. Brave, Edge or Opera, chances are that the browser option match with the example above, just using a different capability name, e.g. `ms:edgeOptions`.
|
||||
|
||||
:::
|
||||
|
||||
If you compile your extension as `.crx` file using e.g. the [crx](https://www.npmjs.com/package/crx) NPM package, you can also inject the bundled extension via:
|
||||
|
||||
```js wdio.conf.js
|
||||
import path from 'node:path'
|
||||
import url from 'node:url'
|
||||
|
||||
const __dirname = url.fileURLToPath(new URL('.', import.meta.url))
|
||||
const extPath = path.join(__dirname, `web-extension-chrome.crx`)
|
||||
const chromeExtension = (await fs.readFile(extPath)).toString('base64')
|
||||
|
||||
export const config = {
|
||||
// ...
|
||||
capabilities: [{
|
||||
browserName,
|
||||
'goog:chromeOptions': {
|
||||
extensions: [chromeExtension]
|
||||
}
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
### Firefox
|
||||
|
||||
To create a Firefox profile that includes extensions you can use the [Firefox Profile Service](/docs/firefox-profile-service) to set up your session accordingly. However you might run into issues where your local developed extension can't be loaded due to signing issues. In this case you can also load an extension in the `before` hook via the [`installAddOn`](/docs/api/gecko#installaddon) command, e.g.:
|
||||
|
||||
```js wdio.conf.js
|
||||
import path from 'node:path'
|
||||
import url from 'node:url'
|
||||
|
||||
const __dirname = url.fileURLToPath(new URL('.', import.meta.url))
|
||||
const extensionPath = path.resolve(__dirname, `web-extension.xpi`)
|
||||
|
||||
export const config = {
|
||||
// ...
|
||||
before: async (capabilities) => {
|
||||
const browserName = (capabilities as WebdriverIO.Capabilities).browserName
|
||||
if (browserName === 'firefox') {
|
||||
const extension = await fs.readFile(extensionPath)
|
||||
await browser.installAddOn(extension.toString('base64'), true)
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
In order to generate an `.xpi` file, it is recommended to use the [`web-ext`](https://www.npmjs.com/package/web-ext) NPM package. You can bundle your extension using the following example command:
|
||||
|
||||
```sh
|
||||
npx web-ext build -s dist/ -a . -n web-extension-firefox.xpi
|
||||
```
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
---
|
||||
id: why-webdriverio
|
||||
title: Why Webdriver.IO?
|
||||
---
|
||||
|
||||
WebdriverIO is a progressive automation framework built to automate modern web and mobile applications. It simplifies the interaction with your app and provides a set of plugins that help you create a scalable, robust and stable test suite.
|
||||
|
||||
It is designed to be:
|
||||
|
||||
- __Extendable__ - Adding helper functions, or more complicated sets and combinations of existing commands is __simple__ and __really useful__
|
||||
- __Compatible__ - WebdriverIO can be run on the [WebDriver Protocol](https://w3c.github.io/webdriver/) for __true cross-browser testing__ as well as [Chrome DevTools Protocol](https://chromedevtools.github.io/devtools-protocol/) for Chromium based automation using [Puppeteer](https://pptr.dev/).
|
||||
- __Feature Rich__ - The huge variety of built-in and community plugins allows you to __easily integrate__ and __extend__ your setup to fulfill your requirements.
|
||||
|
||||
You can use WebdriverIO to automate:
|
||||
|
||||
- 🌐 <span> </span> __modern web applications__ written in React, Vue, Angular, Svelte or other frontend frameworks
|
||||
- 📱 <span> </span> __hybrid__ or __native mobile applications__ running in an emulator/simulator or on a real device
|
||||
- 💻 <span> </span> __native desktop applications__ (e.g. written with Electron.js)
|
||||
- 📦 <span> </span> __unit or component testing__ of web components in the browser
|
||||
|
||||
## Based on Web Standards
|
||||
|
||||
WebdriverIO leverages the power of the [WebDriver](https://w3c.github.io/webdriver/) and [WebDriver-BiDi](https://github.com/w3c/webdriver-bidi) protocol that is developed and supported by all browser vendors and guarantees a true cross-browser testing experience. While other automation tools require you to download modified browser engines that aren't used by actual users or emulate user behavior by injecting JavaScript, WebdriverIO relies on a common agreed standard for automation that is [properly tested](https://wpt.fyi/results/webdriver/tests?label=experimental&label=master&aligned) and ensures compatibility for decades to come.
|
||||
|
||||
Furthermore WebdriverIO has also support for alternative, proprietary automation protocols like [Chrome DevTools](https://chromedevtools.github.io/devtools-protocol/) for debugging and introspection purposes. This allows the user to seamlessly switch between conventional commands based on WebDriver and powerful browser interactions through [Puppeteer](https://pptr.dev/).
|
||||
|
||||
Read more about the differences of these automation standards in the section on [Automation Protocols](automationProtocols).
|
||||
|
||||
## True Open Source
|
||||
|
||||
Compared to many automation tools in the ecosystem, WebdriverIO is a truly open source project that is run with open governance and owned by a non-profit entity called [OpenJS Foundation](https://openjsf.org/). This legally binds the project to grow and be directed in the interest of all participants. The project team values openness and collaboration and is not driven by monetary interests.
|
||||
|
||||
This makes the project independent in how it is being developed and where it is supposed to go. It allows us to provide free 24/7 support in our [community channel](https://discord.webdriver.io) as we build a sustainable community that supports and learns from each other. Lastly, it gives a lot of opportunities to the people that contribute and engage with the project due to its [open governance](https://github.com/webdriverio/webdriverio/blob/main/GOVERNANCE.md).
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
---
|
||||
id: axe-core
|
||||
title: Axe Core
|
||||
---
|
||||
|
||||
You can include accessibility tests within your WebdriverIO test suite using the open-source accessibility tools [from Deque called Axe](https://www.deque.com/axe/). The setup is very easy, all you need to do is to install the WebdriverIO Axe adapter via:
|
||||
|
||||
```bash npm2yarn
|
||||
npm install -g @axe-core/webdriverio
|
||||
```
|
||||
|
||||
The Axe adapter can be used either in [standalone or testrunner](/docs/setuptypes) mode by simply importing and initializing it with the [browser object](/docs/api/browser), e.g.:
|
||||
|
||||
```ts
|
||||
import { browser } from '@wdio/globals'
|
||||
import AxeBuilder from '@axe-core/webdriverio'
|
||||
|
||||
describe('Accessibility Test', () => {
|
||||
it('should get the accessibility results from a page', async () => {
|
||||
const builder = new AxeBuilder({ client: browser })
|
||||
|
||||
await browser.url('https://testingbot.com')
|
||||
const result = await builder.analyze()
|
||||
console.log('Acessibility Results:', result)
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
You can find more documentation on the Axe WebdriverIO adapter [on GitHub](https://github.com/dequelabs/axe-core-npm/tree/develop/packages/webdriverio#usage).
|
||||
|
|
@ -1,62 +0,0 @@
|
|||
---
|
||||
id: browserstack
|
||||
title: BrowserStack Accessibility Testing
|
||||
---
|
||||
|
||||
# BrowserStack Accessibility Testing
|
||||
|
||||
You can easily integrate accessibility tests in your WebdriverIO test suites using the [Automated tests feature of BrowserStack Accessibility Testing](https://www.browserstack.com/docs/accessibility/automated-tests?utm_source=webdriverio\&utm_medium=partnered\&utm_campaign=documentation).
|
||||
|
||||
## Advantages of Automated Tests in BrowserStack Accessibility Testing
|
||||
|
||||
To use Automated tests in BrowserStack Accessibility Testing, your tests should be running on BrowserStack Automate.
|
||||
|
||||
وفيما يلي مزايا الاختبارات المؤتمتة:
|
||||
|
||||
- Seamlessly integrates into your pre-existing automation test suite.
|
||||
- لا حاجة للتعديل على الشيفرة البرمجية في حالات الاختبار.
|
||||
- ويتطلب ذلك صفر من الصيانة الإضافية لاختبار قابلية الوصول.
|
||||
- Understand historical trends and gain test-case insights.
|
||||
|
||||
## Get Started with BrowserStack Accessibility Testing
|
||||
|
||||
اتبع هذه الخطوات لدمج مجموعات اختبار WebdriverIO الخاصة بك مع اختبار قابلية وصول المتصفح:
|
||||
|
||||
1. Install `@wdio/browserstack-service` npm package.
|
||||
|
||||
```bash npm2yarn
|
||||
```
|
||||
|
||||
2. تحديث ملف التكوين \`wdio.conf.js'.
|
||||
|
||||
```javascript
|
||||
exports.config = {
|
||||
//...
|
||||
user: '<browserstack_username>' || process.env.BROWSERSTACK_USERNAME,
|
||||
key: '<browserstack_access_key>' || process.env.BROWSERSTACK_ACCESS_KEY,
|
||||
commonCapabilities: {
|
||||
'bstack:options': {
|
||||
projectName: "Your static project name goes here",
|
||||
buildName: "Your static build/job name goes here"
|
||||
}
|
||||
},
|
||||
services: [
|
||||
['browserstack', {
|
||||
accessibility: true,
|
||||
// Optional configuration options
|
||||
accessibilityOptions: {
|
||||
'wcagVersion': 'wcag21a',
|
||||
'includeIssueType': {
|
||||
'bestPractice': false,
|
||||
'needsReview': true
|
||||
},
|
||||
'includeTagsInTestingScope': ['Specify tags of test cases to be included'],
|
||||
'excludeTagsInTestingScope': ['Specify tags of test cases to be excluded']
|
||||
},
|
||||
}]
|
||||
],
|
||||
//...
|
||||
};
|
||||
```
|
||||
|
||||
You can view detailed instructions [here](https://www.browserstack.com/docs/accessibility/automated-tests/get-started/webdriverio?utm_source=webdriverio\&utm_medium=partnered\&utm_campaign=documentation).
|
||||
|
|
@ -1,63 +0,0 @@
|
|||
---
|
||||
id: browserstack-accessibility-testing
|
||||
title: BrowserStack Accessibility Testing
|
||||
---
|
||||
|
||||
# BrowserStack Accessibility Testing
|
||||
|
||||
You can easily integrate accessibility tests in your WebdriverIO test suites using the [Automated tests feature of BrowserStack Accessibility Testing](https://www.browserstack.com/docs/accessibility/automated-tests?utm_source=webdriverio\&utm_medium=partnered\&utm_campaign=documentation).
|
||||
|
||||
## Advantages of Automated tests in BrowserStack Accessibility Testing
|
||||
|
||||
To use Automated tests in BrowserStack Accessibility Testing, your tests should be running on BrowserStack Automate.
|
||||
|
||||
The following are the advantages of Automated tests:
|
||||
|
||||
- Seamlessly integrates into your pre-existing automation test suite.
|
||||
- No code changes are required in test cases.
|
||||
- Requires zero additional maintenance for accessibility testing.
|
||||
- Understand historical trends and gain test-case insights.
|
||||
|
||||
## Get Started with BrowserStack Accessibility Testing
|
||||
|
||||
Follow these steps to integrate your WebdriverIO test suites with BrowserStack's Accessibility Testing:
|
||||
|
||||
1. Update `@wdio/browserstack-service` npm package.
|
||||
|
||||
```bash
|
||||
npm update @wdio/browserstack-service
|
||||
```
|
||||
|
||||
2. Update `wdio.conf.js` config file.
|
||||
|
||||
```javascript
|
||||
exports.config = {
|
||||
//...
|
||||
user: ‘<browserstack_username>’ || process.env.BROWSERSTACK_USERNAME,
|
||||
key: 'browserstack_access_key> || process.env.BROWSERSTACK_ACCESS_KEY,
|
||||
commonCapabilities: {
|
||||
'bstack:options': {
|
||||
projectName: "Your static project name goes here",
|
||||
buildName: "Your static build/job name goes here"
|
||||
}
|
||||
},
|
||||
services: [
|
||||
['browserstack', {
|
||||
accessibility: true,
|
||||
// Optional configuration options
|
||||
accessibilityOptions: {
|
||||
'wcagVersion': 'wcag21a',
|
||||
'includeIssueType': {
|
||||
'bestPractice': false,
|
||||
'needsReview': true
|
||||
},
|
||||
'includeTagsInTestingScope': ['Specify tags of test cases to be included'],
|
||||
'excludeTagsInTestingScope': ['Specify tags of test cases to be excluded']
|
||||
},
|
||||
}]
|
||||
],
|
||||
//...
|
||||
};
|
||||
```
|
||||
|
||||
You can view detailed instructions [here](https://www.browserstack.com/docs/accessibility/automated-tests/get-started/webdriverio?utm_source=webdriverio\&utm_medium=partnered\&utm_campaign=documentation).
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
---
|
||||
id: axe-core
|
||||
title: Axe Core
|
||||
---
|
||||
|
||||
You can include accessibility tests within your WebdriverIO test suite using the open-source accessibility tools [from Deque called Axe](https://www.deque.com/axe/). The setup is very easy, all you need to do is to install the WebdriverIO Axe adapter via:
|
||||
|
||||
```bash npm2yarn
|
||||
npm install -g @axe-core/webdriverio
|
||||
```
|
||||
|
||||
The Axe adapter can be used either in [standalone or testrunner](/docs/setuptypes) mode by simply importing and initializing it with the [browser object](/docs/api/browser), e.g.:
|
||||
|
||||
```ts
|
||||
import { browser } from '@wdio/globals'
|
||||
import AxeBuilder from '@axe-core/webdriverio'
|
||||
|
||||
describe('Accessibility Test', () => {
|
||||
it('should get the accessibility results from a page', async () => {
|
||||
const builder = new AxeBuilder({ client: browser })
|
||||
|
||||
await browser.url('https://testingbot.com')
|
||||
const result = await builder.analyze()
|
||||
console.log('Acessibility Results:', result)
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
You can find more documentation on the Axe WebdriverIO adapter [on GitHub](https://github.com/dequelabs/axe-core-npm/tree/develop/packages/webdriverio#usage).
|
||||
|
|
@ -1,238 +0,0 @@
|
|||
---
|
||||
id: browser
|
||||
title: The Browser Object
|
||||
---
|
||||
|
||||
__Extends:__ [EventEmitter](https://nodejs.org/api/events.html#class-eventemitter)
|
||||
|
||||
The browser object is the session instance you use to control the browser or mobile device with. If you use the WDIO test runner, you can access the WebDriver instance through the global `browser` or `driver` object or import it using [`@wdio/globals`](/docs/api/globals). If you use WebdriverIO in standalone mode the browser object is returned by the [`remote`](/docs/api/modules#remoteoptions-modifier) method.
|
||||
|
||||
The session is initialized by the test runner. The same goes for ending the session. This is also done by the test runner process.
|
||||
|
||||
## Properties
|
||||
|
||||
A browser object has the following properties:
|
||||
|
||||
| Name | Type | Details |
|
||||
| ----------------------- | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| `capabilities` | `Object` | Assigned capabilities from the remote server.<br /><b>Example:</b><pre>\{<br /> acceptInsecureCerts: false,<br /> browserName: 'chrome',<br /> browserVersion: '105.0.5195.125',<br /> chrome: \{<br /> chromedriverVersion: '105.0.5195.52',<br /> userDataDir: '/var/folders/3_/pzc_f56j15vbd9z3r0j050sh0000gn/T/.com.google.Chrome.76HD3S'<br /> \},<br /> 'goog:chromeOptions': \{ debuggerAddress: 'localhost:64679' \},<br /> networkConnectionEnabled: false,<br /> pageLoadStrategy: 'normal',<br /> platformName: 'mac os x',<br /> proxy: \{},<br /> setWindowRect: true,<br /> strictFileInteractability: false,<br /> timeouts: \{ implicit: 0, pageLoad: 300000, script: 30000 \},<br /> unhandledPromptBehavior: 'dismiss and notify',<br /> 'webauthn:extension:credBlob': true,<br /> 'webauthn:extension:largeBlob': true,<br /> 'webauthn:virtualAuthenticators': true<br />\}</pre> |
|
||||
| `requestedCapabilities` | `Object` | Capabilities requested from the remote server.<br /><b>Example:</b><pre>\{ browserName: 'chrome' \}</pre> |
|
||||
| `sessionId` | `String` | Session id assigned from the remote server. |
|
||||
| `options` | `Object` | WebdriverIO [options](/docs/configuration) depending on how the browser object was created. See more [setup types](/docs/setuptypes). |
|
||||
| `commandList` | `String[]` | A list of commands registered to the browser instance |
|
||||
| `isW3C` | `Boolean` | Indicates if this is a W3C session |
|
||||
| `isChrome` | `Boolean` | Indicates if this Chrome instance |
|
||||
| `isFirefox` | `Boolean` | Indicates if this Firefox instance |
|
||||
| `isBidi` | `Boolean` | Indicates if this session uses Bidi |
|
||||
| `isSauce` | `Boolean` | Indicates if this session is Running on Sauce Labs |
|
||||
| `isMacApp` | `Boolean` | Indicates if this session is Running for a native Mac App |
|
||||
| `isWindowsApp` | `Boolean` | Indicates if this session is Running for a native Windows App |
|
||||
| `isMobile` | `Boolean` | Indicates a mobile session. See more under [Mobile Flags](#mobile-flags). |
|
||||
| `isIOS` | `Boolean` | Indicates an iOS session. See more under [Mobile Flags](#mobile-flags). |
|
||||
| `isAndroid` | `Boolean` | Indicates an Android session. See more under [Mobile Flags](#mobile-flags). |
|
||||
| `isNativeContext` | `Boolean` | Indicates if the mobile is in the `NATIVE_APP` context. See more under [Mobile Flags](#mobile-flags). |
|
||||
| `mobileContext` | `string` | The will provide the **current** context the driver is in, for example `NATIVE_APP`, `WEBVIEW_<packageName>` for Android or `WEBVIEW_<pid>` for iOS. It will save an extra WebDriver to `driver.getContext()`. See more under [Mobile Flags](#mobile-flags). |
|
||||
|
||||
|
||||
## Methods
|
||||
|
||||
Based on the automation backend used for your session, WebdriverIO identifies which [Protocol Commands](/docs/api/protocols) will be attached to the [browser object](/docs/api/browser). For example if you run an automated session in Chrome, you will have access to Chromium specific commands like [`elementHover`](/docs/api/chromium#elementhover) but not any of the [Appium commands](/docs/api/appium).
|
||||
|
||||
Furthermore WebdriverIO provides a set of convenient methods that are recommended to use, to interact with the [browser](/docs/api/browser) or [elements](/docs/api/element) on the page.
|
||||
|
||||
In addition to that the following commands are available:
|
||||
|
||||
| Name | Parameters | Details |
|
||||
| -------------------- | ---------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `addCommand` | - `commandName` (Type: `String`)<br />- `fn` (Type: `Function`)<br />- `attachToElement` (Type: `boolean`) | Allows to define custom commands that can be called from the browser object for composition purposes. Read more in the [Custom Command](/docs/customcommands) guide. |
|
||||
| `overwriteCommand` | - `commandName` (Type: `String`)<br />- `fn` (Type: `Function`)<br />- `attachToElement` (Type: `boolean`) | Allows to overwrite any browser command with custom functionality. Use carefully as it can confuse framework users. Read more in the [Custom Command](/docs/customcommands#overwriting-native-commands) guide. |
|
||||
| `addLocatorStrategy` | - `strategyName` (Type: `String`)<br />- `fn` (Type: `Function`) | Allows to define a custom selector strategy, read more in the [Selectors](/docs/selectors#custom-selector-strategies) guide. |
|
||||
|
||||
## Remarks
|
||||
|
||||
### Mobile Flags
|
||||
|
||||
If you need to modify your test based on whether or not your session runs on a mobile device, you can access the mobile flags to check.
|
||||
|
||||
For example, given this config:
|
||||
|
||||
```js
|
||||
// wdio.conf.js
|
||||
export const config = {
|
||||
// ...
|
||||
capabilities: \\{
|
||||
platformName: 'iOS',
|
||||
app: 'net.company.SafariLauncher',
|
||||
udid: '123123123123abc',
|
||||
deviceName: 'iPhone',
|
||||
// ...
|
||||
}
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
You can access these flags in your test like so:
|
||||
|
||||
```js
|
||||
// Note: `driver` is the equivalent to the `browser` object but semantically more correct
|
||||
// you can choose which global variable you want to use
|
||||
console.log(driver.isMobile) // outputs: true
|
||||
console.log(driver.isIOS) // outputs: true
|
||||
console.log(driver.isAndroid) // outputs: false
|
||||
```
|
||||
|
||||
This can be useful if, for example, you want to define selectors in your [page objects](../pageobjects) based on the device type, like this:
|
||||
|
||||
```js
|
||||
// mypageobject.page.js
|
||||
import Page from './page'
|
||||
|
||||
class LoginPage extends Page {
|
||||
// ...
|
||||
get username() {
|
||||
const selectorAndroid = 'new UiSelector().text("Cancel").className("android.widget.Button")'
|
||||
const selectorIOS = 'UIATarget.localTarget().frontMostApp().mainWindow().buttons()[0]'
|
||||
const selectorType = driver.isAndroid ? 'android' : 'ios'
|
||||
const selector = driver.isAndroid ? selectorAndroid : selectorIOS
|
||||
return $(`${selectorType}=${selector}`)
|
||||
}
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
You can also use these flags to run only certain tests for certain device types:
|
||||
|
||||
```js
|
||||
// mytest.e2e.js
|
||||
describe('my test', () => {
|
||||
// ...
|
||||
// only run test with Android devices
|
||||
if (driver.isAndroid) {
|
||||
it('tests something only for Android', () => {
|
||||
// ...
|
||||
})
|
||||
}
|
||||
// ...
|
||||
})
|
||||
```
|
||||
|
||||
### Events
|
||||
The browser object is an EventEmitter and a couple of events are emitted for your use cases.
|
||||
|
||||
Here is a list of events. Keep in mind that this is not the full list of available events yet. Feel free to contribute to update the document by adding descriptions of more events here.
|
||||
|
||||
#### `request.start`
|
||||
This event is fired before a WebDriver request is sent to the driver. It contains information about the request and its payload.
|
||||
|
||||
```ts
|
||||
browser.on('request.start', (ev: RequestInit) => {
|
||||
// ...
|
||||
})
|
||||
```
|
||||
|
||||
#### `request.end`
|
||||
This event is fired once the request to the driver received a response. The event object either contains the response body as result or an error if the WebDriver command failed.
|
||||
|
||||
```ts
|
||||
browser.on('request.end', (ev: { result: unknown, error?: Error }) => {
|
||||
// ...
|
||||
})
|
||||
```
|
||||
|
||||
#### `request.retry`
|
||||
The retry event can notify you when WebdriverIO attempts to retry running the command, e.g. due to a network issue. It contains information about the error that caused the retry and the amount of retries already done.
|
||||
|
||||
```ts
|
||||
browser.on('request.retry', (ev: { error: Error, retryCount: number }) => {
|
||||
// ...
|
||||
})
|
||||
```
|
||||
|
||||
#### `command`
|
||||
|
||||
This event is emitted whenever WebdriverIO sends a WebDriver Classic command. It contains the following information:
|
||||
|
||||
- `command`: the command name, e.g. `navigateTo`
|
||||
- `method`: the HTTP method used to send the command request, e.g. `POST`
|
||||
- `endpoint`: the command endpoint, e.g. `/session/fc8dbda381a8bea36a225bd5fd0c069b/url`
|
||||
- `body`: the command payload, e.g. `{ url: 'https://webdriver.io' }`
|
||||
|
||||
#### `result`
|
||||
|
||||
This event is emitted whenever WebdriverIO receives a result of a WebDriver Classic command. It contains the same information as the `command` event with the addition of the following information:
|
||||
|
||||
- `result`: the command result
|
||||
|
||||
#### `bidiCommand`
|
||||
|
||||
This event is emitted whenever WebdriverIO sends a WebDriver Bidi command to the browser driver. It contains information about:
|
||||
|
||||
- `method`: WebDriver Bidi command method
|
||||
- `params`: associated command parameter (see [API](/docs/api/webdriverBidi))
|
||||
|
||||
#### `bidiResult`
|
||||
|
||||
In case of a successful command execution, the event payload will be:
|
||||
|
||||
- `type`: `success`
|
||||
- `id`: the command id
|
||||
- `result`: the command result (see [API](/docs/api/webdriverBidi))
|
||||
|
||||
In case of a command error, the event payload will be:
|
||||
|
||||
- `type`: `error`
|
||||
- `id`: the command id
|
||||
- `error`: the error code, e.g. `invalid argument`
|
||||
- `message`: details about the error
|
||||
- `stacktrace`: a stack trace
|
||||
|
||||
#### `request.start`
|
||||
This event is fired before a WebDriver request is sent to the driver. It contains information about the request and its payload.
|
||||
|
||||
```ts
|
||||
browser.on('request.start', (ev: RequestInit) => {
|
||||
// ...
|
||||
})
|
||||
```
|
||||
|
||||
#### `request.end`
|
||||
This event is fired once the request to the driver received a response. The event object either contains the response body as result or an error if the WebDriver command failed.
|
||||
|
||||
```ts
|
||||
browser.on('request.end', (ev: { result: unknown, error?: Error }) => {
|
||||
// ...
|
||||
})
|
||||
```
|
||||
|
||||
#### `request.retry`
|
||||
The retry event can notify you when WebdriverIO attempts to retry running the command, e.g. due to a network issue. It contains information about the error that caused the retry and the amount of retries already done.
|
||||
|
||||
```ts
|
||||
browser.on('request.retry', (ev: { error: Error, retryCount: number }) => {
|
||||
// ...
|
||||
})
|
||||
```
|
||||
|
||||
#### `request.performance`
|
||||
This is an event to measure WebDriver level operations. Whenever WebdriverIO sends a request to the WebDriver backend, this event will be emitted with some useful information:
|
||||
|
||||
- `durationMillisecond`: Time duration of the request in millisecond.
|
||||
- `error`: Error object if the request failed.
|
||||
- `request`: Request object. You can find url, method, headers, etc.
|
||||
- `retryCount`: If it's `0`, the request was the first attempt. It will increase when WebDriverIO retries under the hood.
|
||||
- `success`: Boolean to represent the request was succeeded or not. If it's `false`, `error` property will be provided as well.
|
||||
|
||||
An example event:
|
||||
```js
|
||||
Object {
|
||||
"durationMillisecond": 0.01770925521850586,
|
||||
"error": [Error: Timeout],
|
||||
"request": Object { ... },
|
||||
"retryCount": 0,
|
||||
"success": false,
|
||||
},
|
||||
```
|
||||
|
||||
### Custom Commands
|
||||
|
||||
You can set custom commands on the browser scope to abstract away workflows that are commonly used. Check out our guide on [Custom Commands](/docs/customcommands#adding-custom-commands) for more information.
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
---
|
||||
id: clock
|
||||
title: The Clock Object
|
||||
---
|
||||
|
||||
You can modify the browser system clock using the [`emulate`](/docs/emulation) command. It overrides native global functions related to time allowing them to be controlled synchronously via `clock.tick()` or the yielded clock object. This includes controlling:
|
||||
|
||||
- `setTimeout`
|
||||
- `clearTimeout`
|
||||
- `setInterval`
|
||||
- `clearInterval`
|
||||
- `Date Objects`
|
||||
|
||||
The clock starts at the unix epoch (timestamp of 0). This means that when you instantiate new Date in your application, it will have a time of January 1st, 1970 if you don't pass any other options to the `emulate` command.
|
||||
|
||||
## Example
|
||||
|
||||
When calling `browser.emulate('clock', { ... })` it will immediately overwrite the global functions for the current page as well as all following pages, e.g.:
|
||||
|
||||
```ts
|
||||
const clock = await browser.emulate('clock', { now: new Date(1989, 7, 4) })
|
||||
|
||||
console.log(await browser.execute(() => (new Date()).toString()))
|
||||
// returns "Fri Aug 04 1989 00:00:00 GMT-0700 (Pacific Daylight Time)"
|
||||
|
||||
await browser.url('https://webdriverio')
|
||||
console.log(await browser.execute(() => (new Date()).toString()))
|
||||
// returns "Fri Aug 04 1989 00:00:00 GMT-0700 (Pacific Daylight Time)"
|
||||
|
||||
await clock.restore()
|
||||
|
||||
console.log(await browser.execute(() => (new Date()).toString()))
|
||||
// returns "Thu Aug 01 2024 17:59:59 GMT-0700 (Pacific Daylight Time)"
|
||||
|
||||
await browser.url('http://guinea-pig.webdriver.io/pointer.html')
|
||||
console.log(await browser.execute(() => (new Date()).toString()))
|
||||
// returns "Thu Aug 01 2024 17:59:59 GMT-0700 (Pacific Daylight Time)"
|
||||
```
|
||||
|
||||
You can modify the system time by calling [`setSystemTime`](/docs/api/clock/setSystemTime) or [`tick`](/docs/api/clock/tick).
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
---
|
||||
id: dialog
|
||||
title: The Dialog Object
|
||||
---
|
||||
|
||||
Dialog objects are dispatched by [`browser`](/docs/api/browser) via the `browser.on('dialog')` event.
|
||||
|
||||
An example of using the Dialog object:
|
||||
|
||||
```ts
|
||||
import { browser } from '@wdio/globals'
|
||||
|
||||
await browser.url('https://webdriver.io')
|
||||
browser.on('dialog', async (dialog) => {
|
||||
console.log(dialog.message()) // outputs: "Hello Dialog"
|
||||
await dialog.dismiss()
|
||||
})
|
||||
|
||||
await browser.execute(() => alert('Hello Dialog'))
|
||||
```
|
||||
|
||||
:::note
|
||||
|
||||
Dialogs are dismissed automatically, unless there is a `browser.on('dialog')` listener. When listener is present, it must either [`dialog.accept()`](/docs/api/dialog/accept) or [`dialog.dismiss()`](/docs/api/dialog/dismiss) the dialog - otherwise the page will freeze waiting for the dialog, and actions like click will never finish.
|
||||
|
||||
:::
|
||||
|
|
@ -1,89 +0,0 @@
|
|||
---
|
||||
id: element
|
||||
title: The Element Object
|
||||
---
|
||||
|
||||
An Element Object is an object representing an element on the remote user agent, e.g. a [DOM Node](https://developer.mozilla.org/en-US/docs/Web/API/Element) when running a session within a browser or [a mobile element](https://developer.apple.com/documentation/swift/sequence/element) for mobile. It can be received using one of the many element query commands, e.g. [`$`](/docs/api/element/$), [`custom$`](/docs/api/element/custom$), [`react$`](/docs/api/element/react$) or [`shadow$`](/docs/api/element/shadow$).
|
||||
|
||||
## Properties
|
||||
|
||||
An element object has the following properties:
|
||||
|
||||
| Name | Type | Details |
|
||||
| ----------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `sessionId` | `String` | Session id assigned from the remote server. |
|
||||
| `elementId` | `String` | Associated [web element reference](https://w3c.github.io/webdriver/#elements) that can be used to interact with the element on the protocol level |
|
||||
| `selector` | `String` | [Selector](/docs/selectors) used to query the element. |
|
||||
| `parent` | `Object` | Either the [Browser Object](/docs/api/browser) when element was fetched from the it (e.g. `const elem = browser.$('selector')`) or an [Element Object](/docs/api/element) if it was fetched from an element scope (e.g. `elem.$('selector')`) |
|
||||
| `options` | `Object` | WebdriverIO [options](/docs/configuration) depending on how the browser object was created. See more [setup types](/docs/setuptypes). |
|
||||
|
||||
## Methods
|
||||
An element object provides all methods from the protocol section, e.g. [WebDriver](/docs/api/webdriver) protocol as well as commands listed within the element section. Available protocol commands depend on the type of session. If you run an automated browser session, none of the Appium [commands](/docs/api/appium) will be available and vice versa.
|
||||
|
||||
In addition to that the following commands are available:
|
||||
|
||||
| Name | Parameters | Details |
|
||||
| ------------------ | --------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `addCommand` | - `commandName` (Type: `String`)<br />- `fn` (Type: `Function`) | Allows to define custom commands that can be called from the browser object for composition purposes. Read more in the [Custom Command](/docs/customcommands) guide. |
|
||||
| `overwriteCommand` | - `commandName` (Type: `String`)<br />- `fn` (Type: `Function`) | Allows to overwrite any browser command with custom functionality. Use carefully as it can confuse framework users. Read more in the [Custom Command](/docs/customcommands#overwriting-native-commands) guide. |
|
||||
|
||||
## Remarks
|
||||
|
||||
### Element Chain
|
||||
|
||||
When working with elements WebdriverIO provides special syntax to simplify querying them and composite complex nested element lookups. As element objects allow you to find elements within their tree branch using common query methods, users can fetch nested elements as follows:
|
||||
|
||||
```js
|
||||
const header = await $('#header')
|
||||
const headline = await header.$('#headline')
|
||||
console.log(await headline.getText()) // outputs "I am a headline"
|
||||
```
|
||||
|
||||
With deep nested structures assigning any nested element to an array to then use it can be quite verbose. Therefore WebdriverIO has the concept of chained element queries that allow fetching nested elements like this:
|
||||
|
||||
```js
|
||||
console.log(await $('#header').$('#headline').getText())
|
||||
```
|
||||
|
||||
This also works when fetching a set of elements, e.g.:
|
||||
|
||||
```js
|
||||
// get the text of the 3rd headline within the 2nd header
|
||||
console.log(await $$('#header')[1].$$('#headline')[2].getText())
|
||||
```
|
||||
|
||||
When working with a set of elements this can be especially useful when trying to interact with them, so instead of doing:
|
||||
|
||||
```js
|
||||
const elems = await $$('div')
|
||||
const locations = await Promise.all(
|
||||
elems.map((el) => el.getLocation())
|
||||
)
|
||||
```
|
||||
|
||||
You can directly call Array methods on the element chain, e.g.:
|
||||
|
||||
```js
|
||||
const location = await $$('div').map((el) => el.getLocation())
|
||||
```
|
||||
|
||||
same as:
|
||||
|
||||
```js
|
||||
const divs = await $$('div')
|
||||
const location = await divs.map((el) => el.getLocation())
|
||||
```
|
||||
|
||||
WebdriverIO uses a custom implementation that supports asynchronous iterators under the hood so all commands from their API are also supported for these use cases.
|
||||
|
||||
__Note:__ all async iterators return a promise even if your callback doesn't return one, e.g.:
|
||||
|
||||
```ts
|
||||
const divs = await $$('div')
|
||||
console.log(divs.map((div) => div.selector)) // ❌ returns "Promise<string>[]"
|
||||
console.log(await divs.map((div) => div.selector)) // ✅ returns "string[]"
|
||||
```
|
||||
|
||||
### Custom Commands
|
||||
|
||||
You can set custom commands on the browser scope to abstract away workflows that are commonly used. Check out our guide on [Custom Commands](/docs/customcommands#adding-custom-commands) for more information.
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
---
|
||||
id: environment
|
||||
title: Environment Variables
|
||||
---
|
||||
|
||||
WebdriverIO sets the following environment variables within every worker:
|
||||
|
||||
## `NODE_ENV`
|
||||
|
||||
Set to `'test'` if it's not already set to something else.
|
||||
|
||||
## `WDIO_LOG_LEVEL`
|
||||
|
||||
Can be set to values `trace`, `debug`, `info`, `warn`, `error`, `silent` to write logs with corresponding details. Has priority over the passed `logLevel` value.
|
||||
|
||||
## `WDIO_WORKER_ID`
|
||||
|
||||
An unique id that helps identify the worker process. It has format of `{number}-{number}` where the first number identifies the capability and the second the spec file that capability is running, e.g. `0-5` indicates a worker the first running the 6th spec file for the first capability.
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
---
|
||||
id: globals
|
||||
title: Globals
|
||||
---
|
||||
|
||||
In your test files, WebdriverIO puts each of these methods and objects into the global environment. You don't have to import anything to use them. However, if you prefer explicit imports, you can do `import { browser, $, $$, expect } from '@wdio/globals'` and set `injectGlobals: false` in your WDIO configuration.
|
||||
|
||||
The following global objects are set if not configured otherwise:
|
||||
|
||||
- `browser`: WebdriverIO [Browser object](https://webdriver.io/docs/api/browser)
|
||||
- `driver`: alias to `browser` (used when running mobile tests)
|
||||
- `multiremotebrowser`: alias to `browser` or `driver` but only set for [Multiremote](/docs/multiremote) sessions
|
||||
- `$`: command to fetch an element (see more in [API docs](/docs/api/browser/$))
|
||||
- `$$`: command to fetch elements (see more in [API docs](/docs/api/browser/$$))
|
||||
- `expect`: assertion framework for WebdriverIO (see [API docs](/docs/api/expect-webdriverio))
|
||||
|
||||
__Note:__ WebdriverIO has no control of used frameworks (e.g. Mocha or Jasmine) setting global variables when bootstrapping their environment.
|
||||
|
|
@ -1,180 +0,0 @@
|
|||
---
|
||||
id: mobile
|
||||
title: Mobile Commands
|
||||
---
|
||||
|
||||
# Introduction to custom and enhanced Mobile Commands in WebdriverIO
|
||||
|
||||
Testing mobile apps and mobile web applications comes with its own challenges, especially when dealing with platform-specific differences between Android and iOS. While Appium provides the flexibility to handle these differences, it often requires you to dive deep into complex, platform-dependent docs ([Android](https://github.com/appium/appium-uiautomator2-driver/blob/master/docs/android-mobile-gestures.md), [iOS](https://appium.github.io/appium-xcuitest-driver/latest/reference/execute-methods/)) and commands. This can make writing test scripts more time-consuming, error-prone, and difficult to maintain.
|
||||
|
||||
To simplify the process, WebdriverIO introduces **custom and enhanced mobile commands** tailored specifically for mobile web and native app testing. These commands abstract away the intricacies of underlying Appium APIs, enabling you to write concise, intuitive, and platform-agnostic test scripts. By focusing on ease of use, we aim to reduce the extra load while developing Appium scripts and empower you to automate mobile apps effortlessly.
|
||||
|
||||
## Why Custom Mobile Commands?
|
||||
|
||||
### 1. **Simplifying Complex APIs**
|
||||
|
||||
Some Appium commands, like gestures or element interactions, involve verbose and intricate syntax. For example, executing a long press action with the native Appium API requires constructing an `action` chain manually:
|
||||
|
||||
```ts
|
||||
const element = $('~Contacts')
|
||||
|
||||
await browser
|
||||
.action( 'pointer', { parameters: { pointerType: 'touch' } })
|
||||
.move({ origin: element })
|
||||
.down()
|
||||
.pause(1500)
|
||||
.up()
|
||||
.perform()
|
||||
```
|
||||
|
||||
With WebdriverIO's custom commands, the same action can be performed with a single, expressive line of code:
|
||||
|
||||
```ts
|
||||
await $('~Contacts').longPress();
|
||||
```
|
||||
|
||||
This drastically reduces boilerplate code, making your scripts cleaner and easier to understand.
|
||||
|
||||
### 2. **Cross-Platform Abstraction**
|
||||
|
||||
Mobile apps often require platform-specific handling. For instance, scrolling in native apps differs significantly between [Android](https://github.com/appium/appium-uiautomator2-driver/blob/master/docs/android-mobile-gestures.md#mobile-scrollgesture) and [iOS](https://appium.github.io/appium-xcuitest-driver/latest/reference/execute-methods/#mobile-scroll). WebdriverIO bridges this gap by providing unified commands like `scrollIntoView()` that work seamlessly across platforms, regardless of the underlying implementation.
|
||||
|
||||
```ts
|
||||
await $('~element').scrollIntoView();
|
||||
```
|
||||
|
||||
This abstraction ensures your tests are portable and do not require constant branching or conditional logic to account for OS differences.
|
||||
|
||||
### 3. **Increased Productivity**
|
||||
|
||||
By reducing the need to understand and implement low-level Appium commands, WebdriverIO's mobile commands enable you to focus on testing your app's functionality rather than wrestling with platform-specific nuances. This is especially beneficial for teams with limited experience in mobile automation or those seeking to accelerate their development cycle.
|
||||
|
||||
### 4. **Consistency and Maintainability**
|
||||
|
||||
Custom commands bring uniformity to your test scripts. Instead of having varying implementations for similar actions, your team can rely on standardized, reusable commands. This not only makes the codebase more maintainable but also lowers the barrier for onboarding new team members.
|
||||
|
||||
## Why enhance certain mobile commands?
|
||||
|
||||
### 1. Adding Flexibility
|
||||
|
||||
Certain mobile commands are enhanced to provide additional options and parameters that aren't available in the default Appium APIs. For example, WebdriverIO adds retry logic, timeouts, and the ability to filter webviews by specific criteria, enabling more control over complex scenarios.
|
||||
|
||||
```ts
|
||||
// Example: Customizing retry intervals and timeouts for webview detection
|
||||
await driver.getContexts({
|
||||
returnDetailedContexts: true,
|
||||
androidWebviewConnectionRetryTime: 1000, // Retry every 1 second
|
||||
androidWebviewConnectTimeout: 10000, // Timeout after 10 seconds
|
||||
});
|
||||
```
|
||||
|
||||
These options help adapt automation scripts to dynamic app behavior without additional boilerplate code.
|
||||
|
||||
### 2. Improving Usability
|
||||
|
||||
Enhanced commands abstract away complexities and repetitive patterns found in the native APIs. They allow you to perform more actions with fewer lines of code, reducing the learning curve for new users and making scripts easier to read and maintain.
|
||||
|
||||
```ts
|
||||
// Example: Enhanced command for switching context by title
|
||||
await driver.switchContext({
|
||||
title: 'My Webview Title',
|
||||
});
|
||||
```
|
||||
|
||||
Compared to the default Appium methods, enhanced commands eliminate the need for additional steps like manually retrieving available contexts and filtering through them.
|
||||
|
||||
### 3. Standardizing Behavior
|
||||
|
||||
WebdriverIO ensures that enhanced commands behave consistently across platforms like Android and iOS. This cross-platform abstraction minimizes the need for conditionally branching logic based on the operating system, leading to more maintainable test scripts.
|
||||
|
||||
```ts
|
||||
// Example: Unified scroll command for both platforms
|
||||
await $('~element').scrollIntoView();
|
||||
```
|
||||
|
||||
This standardization simplifies codebases, especially for teams automating tests on multiple platforms.
|
||||
|
||||
### 4. Increasing Reliability
|
||||
|
||||
By incorporating retry mechanisms, smart defaults, and detailed error messages, enhanced commands reduce the likelihood of flaky tests. These improvements ensure your tests are resilient to issues like delays in webview initialization or transient app states.
|
||||
|
||||
```ts
|
||||
// Example: Enhanced webview switching with robust matching logic
|
||||
await driver.switchContext({
|
||||
url: /.*my-app\/dashboard/,
|
||||
androidWebviewConnectionRetryTime: 500,
|
||||
androidWebviewConnectTimeout: 7000,
|
||||
});
|
||||
```
|
||||
|
||||
This makes test execution more predictable and less prone to failures caused by environmental factors.
|
||||
|
||||
### 5. Enhancing Debugging Capabilities
|
||||
|
||||
Enhanced commands often return richer metadata, enabling easier debugging of complex scenarios, particularly in hybrid apps. For instance, commands like getContext and getContexts can return detailed information about webviews, including title, url, and visibility status.
|
||||
|
||||
```ts
|
||||
// Example: Retrieving detailed metadata for debugging
|
||||
const contexts = await driver.getContexts({ returnDetailedContexts: true });
|
||||
console.log(contexts);
|
||||
```
|
||||
|
||||
This metadata helps identify and resolve issues faster, improving the overall debugging experience.
|
||||
|
||||
By enhancing mobile commands, WebdriverIO not only makes automation easier but also aligns with its mission to provide developers with tools that are powerful, reliable, and intuitive to use.
|
||||
|
||||
---
|
||||
|
||||
## Hybrid Apps
|
||||
|
||||
Hybrid apps combine web content with native functionality and require specialized handling during automation. These apps use webviews to render web content within a native application. WebdriverIO provides enhanced methods for working with hybrid apps effectively.
|
||||
|
||||
### Understanding Webviews
|
||||
|
||||
A webview is a browser-like component embedded in a native app:
|
||||
|
||||
- **Android:** Webviews are based on Chrome/System Webview and may contain multiple pages (similar to browser tabs). These webviews require ChromeDriver to automate interactions. Appium can automatically determine the required ChromeDriver version based on the version of the System WebView or Chrome installed on the device and download it automatically if not already available. This approach ensures seamless compatibility and minimizes manual setup. Refer to the [Appium UIAutomator2-documentation](https://github.com/appium/appium-uiautomator2-driver?tab=readme-ov-file#automatic-discovery-of-compatible-chromedriver) to learn how Appium automatically downloads the correct ChromeDriver version.
|
||||
- **iOS:** Webviews are powered by Safari (WebKit) and identified by generic IDs like `WEBVIEW_{id}`.
|
||||
|
||||
### Challenges with Hybrid Apps
|
||||
|
||||
1. Identifying the correct webview among multiple options.
|
||||
2. Retrieving additional metadata such as the title, URL, or package name for better context.
|
||||
3. Handling platform-specific differences between Android and iOS.
|
||||
4. Switching to the correct context in a hybrid app reliably.
|
||||
|
||||
### Key Commands for Hybrid Apps
|
||||
|
||||
#### 1. `getContext`
|
||||
|
||||
Retrieves the current context of the session. By default, it behaves like Appium's getContext method but can provide detailed context information when `returnDetailedContext` is enabled. For more information see [`getContext`](/docs/api/mobile/getContext)
|
||||
|
||||
#### 2. `getContexts`
|
||||
|
||||
Returns a detailed list of available contexts, improving upon Appium's contexts method. This makes it easier to identify the correct webview for interaction without calling extra commands to determine title, url or active `bundleId|packageName`. For more information see [`getContexts`](/docs/api/mobile/getContexts)
|
||||
|
||||
#### 3. `switchContext`
|
||||
|
||||
Switches to a specific webview based on name, title, or url. Provides additional flexibility, such as using regular expressions for matching. For more information see [`switchContext`](/docs/api/mobile/switchContext)
|
||||
|
||||
### Key Features for Hybrid Apps
|
||||
|
||||
1. Detailed Metadata: Retrieve comprehensive details for debugging and reliable context switching.
|
||||
2. Cross-Platform Consistency: Unified behavior for Android and iOS, handling platform-specific quirks seamlessly.
|
||||
3. Custom Retry Logic (Android): Adjust retry intervals and timeouts for webview detection.
|
||||
|
||||
:::info Notes and Limitations
|
||||
|
||||
- Android provides additional metadata, such as `packageName` and `webviewPageId`, while iOS focuses on `bundleId`.
|
||||
- Retry logic is customizable for Android but not applicable to iOS.
|
||||
- There are several cases that iOS can't find the Webview. Appium provides different extra capabilities for the `appium-xcuitest-driver` to find the Webview. If you believe that the Webview is not found, you can try to set one of the following capabilities:
|
||||
- `appium:includeSafariInWebviews`: Add Safari web contexts to the list of contexts available during a native/webview app test. This is useful if the test opens Safari and needs to be able to interact with it. Defaults to `false`.
|
||||
- `appium:webviewConnectRetries`: The maximum number of retries before giving up on web view pages detection. The delay between each retry is 500ms, default is `10` retries.
|
||||
- `appium:webviewConnectTimeout`: The maximum amount of time in milliseconds to wait for a web view page to be detected. Default is `5000` ms.
|
||||
|
||||
For advanced examples and details, see the WebdriverIO Mobile API documentation.
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
Our growing set of commands reflects our commitment to making mobile automation accessible and elegant. Whether you’re performing intricate gestures or working with native app elements, these commands align with WebdriverIO’s philosophy of creating a seamless automation experience. And we’re not stopping here—if there’s a feature you’d like to see, we welcome your feedback. Feel free to submit your requests via [this link](https://github.com/webdriverio/webdriverio/issues/new/choose).
|
||||
|
|
@ -1,182 +0,0 @@
|
|||
---
|
||||
id: mock
|
||||
title: The Mock Object
|
||||
---
|
||||
|
||||
The mock object is an object that represents a network mock and contains information about requests that were matching given `url` and `filterOptions`. It can be received using the [`mock`](/docs/api/browser/mock) command.
|
||||
|
||||
:::info
|
||||
|
||||
Note that using the `mock` command requires support for Chrome DevTools protocol. That support is given if you run tests locally in Chromium based browser or if you use a Selenium Grid v4 or higher. This command can __not__ be used when running automated tests in the cloud. Find out more in the [Automation Protocols](/docs/automationProtocols) section.
|
||||
|
||||
:::
|
||||
|
||||
You can read more about mocking requests and responses in WebdriverIO in our [Mocks and Spies](/docs/mocksandspies) guide.
|
||||
|
||||
## Properties
|
||||
|
||||
A mock object contains the following properties:
|
||||
|
||||
| Name | Type | Details |
|
||||
| --------------- | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `url` | `String` | The url passed into the mock command |
|
||||
| `filterOptions` | `Object` | The resource filter options passed into the mock command |
|
||||
| `browser` | `Object` | The [Browser Object](/docs/api/browser) used to get the mock object. |
|
||||
| `calls` | `Object[]` | Information about matching browser requests, containing properties such as `url`, `method`, `headers`, `initialPriority`, `referrerPolic`, `statusCode`, `responseHeaders` and `body` |
|
||||
|
||||
## Methods
|
||||
|
||||
Mock objects provide various commands, listed in the `mock` section, that allow users to modify the behavior of the request or response.
|
||||
|
||||
- [`abort`](/docs/api/mock/abort)
|
||||
- [`abortOnce`](/docs/api/mock/abortOnce)
|
||||
- [`clear`](/docs/api/mock/clear)
|
||||
- [`request`](/docs/api/mock/request)
|
||||
- [`requestOnce`](/docs/api/mock/requestOnce)
|
||||
- [`respond`](/docs/api/mock/respond)
|
||||
- [`respondOnce`](/docs/api/mock/respondOnce)
|
||||
- [`restore`](/docs/api/mock/restore)
|
||||
|
||||
## Events
|
||||
|
||||
The mock object is an EventEmitter and a couple of events are emitted for your use cases.
|
||||
|
||||
Here is a list of events.
|
||||
|
||||
### `request`
|
||||
|
||||
This event is being emitted when launching a network request that matches mock patterns. Request is passed in event callback.
|
||||
|
||||
Request interface:
|
||||
```ts
|
||||
interface RequestEvent {
|
||||
requestId: number
|
||||
request: Matches
|
||||
responseStatusCode: number
|
||||
responseHeaders: Record<string, string>
|
||||
}
|
||||
```
|
||||
|
||||
### `overwrite`
|
||||
|
||||
This event is being emitted when network response is overwrited with [`respond`](/docs/api/mock/respond) or [`respondOnce`](/docs/api/mock/respondOnce). Response is passed in event callback.
|
||||
|
||||
Response interface:
|
||||
```ts
|
||||
interface OverwriteEvent {
|
||||
requestId: number
|
||||
responseCode: number
|
||||
responseHeaders: Record<string, string>
|
||||
body?: string | Record<string, any>
|
||||
}
|
||||
```
|
||||
|
||||
### `fail`
|
||||
|
||||
This event is being emitted when network request is aborted with [`abort`](/docs/api/mock/abort) or [`abortOnce`](/docs/api/mock/abortOnce). Fail is passed in event callback.
|
||||
|
||||
Fail interface:
|
||||
```ts
|
||||
interface FailEvent {
|
||||
requestId: number
|
||||
errorReason: Protocol.Network.ErrorReason
|
||||
}
|
||||
```
|
||||
|
||||
### `match`
|
||||
|
||||
This event is being emitted when new match is added, before `continue` or `overwrite`. Match is passed in event callback.
|
||||
|
||||
Match interface:
|
||||
```ts
|
||||
interface MatchEvent {
|
||||
url: string // Request URL (without fragment).
|
||||
urlFragment?: string // Fragment of the requested URL starting with hash, if present.
|
||||
method: string // HTTP request method.
|
||||
headers: Record<string, string> // HTTP request headers.
|
||||
postData?: string // HTTP POST request data.
|
||||
hasPostData?: boolean // True when the request has POST data.
|
||||
mixedContentType?: MixedContentType // The mixed content export type of the request.
|
||||
initialPriority: ResourcePriority // Priority of the resource request at the time request is sent.
|
||||
referrerPolicy: ReferrerPolicy // The referrer policy of the request, as defined in https://www.w3.org/TR/referrer-policy/
|
||||
isLinkPreload?: boolean // Whether is loaded via link preload.
|
||||
body: string | Buffer | JsonCompatible // Body response of actual resource.
|
||||
responseHeaders: Record<string, string> // HTTP response headers.
|
||||
statusCode: number // HTTP response status code.
|
||||
mockedResponse?: string | Buffer // If mock, emitting the event, also modified it's response.
|
||||
}
|
||||
```
|
||||
|
||||
### `continue`
|
||||
|
||||
This event is being emitted when the network response has neither been overwritten nor interrupted, or if response was already sent by another mock. `requestId` is passed in event callback.
|
||||
|
||||
## Examples
|
||||
|
||||
Getting a number of pending requests:
|
||||
|
||||
```js
|
||||
let pendingRequests = 0
|
||||
const mock = await browser.mock('**') // it is important to match all requests otherwise, the resulting value can be very confusing.
|
||||
mock.on('request', ({request}) => {
|
||||
pendingRequests++
|
||||
console.log(`matched request to ${request.url}, pending ${pendingRequests} requests`)
|
||||
})
|
||||
mock.on('match', ({url}) => {
|
||||
pendingRequests--
|
||||
console.log(`resolved request to ${url}, pending ${pendingRequests} requests`)
|
||||
})
|
||||
```
|
||||
|
||||
Throwing an error on 404 network fail:
|
||||
|
||||
```js
|
||||
browser.addCommand('loadPageWithout404', (url, {selector, predicate}) => new Promise(async (resolve, reject) => {
|
||||
const mock = await this.mock('**')
|
||||
|
||||
mock.on('match', ({url, statusCode}) => {
|
||||
if (statusCode === 404) {
|
||||
reject(new Error(`request to ${url} failed with "Not Found"`))
|
||||
}
|
||||
})
|
||||
|
||||
await this.url(url).catch(reject)
|
||||
|
||||
// waiting here, because some requests can still be pending
|
||||
if (selector) {
|
||||
await this.$(selector).waitForExist().catch(reject)
|
||||
}
|
||||
|
||||
if (predicate) {
|
||||
await this.waitUntil(predicate).catch(reject)
|
||||
}
|
||||
|
||||
resolve()
|
||||
}))
|
||||
|
||||
await browser.loadPageWithout404(browser, 'some/url', { selector: 'main' })
|
||||
```
|
||||
|
||||
Determining if mock respond value was used:
|
||||
|
||||
```js
|
||||
const firstMock = await browser.mock('**/foo/**')
|
||||
const secondMock = await browser.mock('**/foo/bar/**')
|
||||
|
||||
firstMock.respondOnce({id: 3, title: 'three'})
|
||||
secondMock.respond({id: 4, title: 'four'})
|
||||
|
||||
firstMock.on('overwrite', () => {
|
||||
// triggers for first request to '**/foo/**'
|
||||
}).on('continue', () => {
|
||||
// triggers for rest requests to '**/foo/**'
|
||||
})
|
||||
|
||||
secondMock.on('continue', () => {
|
||||
// triggers for first request to '**/foo/bar/**'
|
||||
}).on('overwrite', () => {
|
||||
// triggers for rest requests to '**/foo/bar/**'
|
||||
})
|
||||
```
|
||||
|
||||
In this example, `firstMock` was defined first and has one `respondOnce` call, so the `secondMock` response value will not be used for the first request, but will be used for the rest of them.
|
||||
|
|
@ -1,263 +0,0 @@
|
|||
---
|
||||
id: modules
|
||||
title: Modules
|
||||
---
|
||||
|
||||
WebdriverIO publishes various of modules to NPM and other registries that you can use to build your own automation framework. See more documentation on WebdriverIO setup types [here](/docs/setuptypes).
|
||||
|
||||
## `webdriver` and `devtools`
|
||||
|
||||
The protocol packages ([`webdriver`](https://www.npmjs.com/package/webdriver) and [`devtools`](https://www.npmjs.com/package/devtools)) expose a class with the following static functions attached that allow you to initiate sessions:
|
||||
|
||||
#### `newSession(options, modifier, userPrototype, customCommandWrapper)`
|
||||
|
||||
Starts a new session with specific capabilities. Based on the session response commands from different protocols will be provided.
|
||||
|
||||
##### Paramaters
|
||||
|
||||
- `options`: [WebDriver Options](/docs/configuration#webdriver-options)
|
||||
- `modifier`: function that allows to modify the client instance before it is being returned
|
||||
- `userPrototype`: properties object that allows to extend the instance prototype
|
||||
- `customCommandWrapper`: function that allows to wrap functionality around function calls
|
||||
|
||||
##### Returns
|
||||
|
||||
- [Browser](/docs/api/browser) object
|
||||
|
||||
##### Example
|
||||
|
||||
```js
|
||||
const client = await WebDriver.newSession({
|
||||
capabilities: { browserName: 'chrome' }
|
||||
})
|
||||
```
|
||||
|
||||
#### `attachToSession(attachInstance, modifier, userPrototype, customCommandWrapper)`
|
||||
|
||||
Attaches to a running WebDriver or DevTools session.
|
||||
|
||||
##### Paramaters
|
||||
|
||||
- `attachInstance`: instance to attach a session to or at least an object with a property `sessionId` (e.g. `{ sessionId: 'xxx' }`)
|
||||
- `modifier`: function that allows to modify the client instance before it is being returned
|
||||
- `userPrototype`: properties object that allows to extend the instance prototype
|
||||
- `customCommandWrapper`: function that allows to wrap functionality around function calls
|
||||
|
||||
##### Returns
|
||||
|
||||
- [Browser](/docs/api/browser) object
|
||||
|
||||
##### Example
|
||||
|
||||
```js
|
||||
const client = await WebDriver.newSession({...})
|
||||
const clonedClient = await WebDriver.attachToSession(client)
|
||||
```
|
||||
|
||||
#### `reloadSession(instance)`
|
||||
|
||||
Reloads a session given provided instance.
|
||||
|
||||
##### Paramaters
|
||||
|
||||
- `instance`: package instance to reload
|
||||
|
||||
##### Example
|
||||
|
||||
```js
|
||||
const client = await WebDriver.newSession({...})
|
||||
await WebDriver.reloadSession(client)
|
||||
```
|
||||
|
||||
## `webdriverio`
|
||||
|
||||
Similar as to the protocol packages (`webdriver` and `devtools`) you can also use the WebdriverIO package APIs to manage sessions. The APIs can be imported using `import { remote, attach, multiremote } from 'webdriverio` and contain the following functionality:
|
||||
|
||||
#### `remote(options, modifier)`
|
||||
|
||||
Starts a WebdriverIO session. The instance contains all commands as the protocol package but with additional higher order functions, see [API docs](/docs/api).
|
||||
|
||||
##### Paramaters
|
||||
|
||||
- `options`: [WebdriverIO Options](/docs/configuration#webdriverio)
|
||||
- `modifier`: function that allows to modify the client instance before it is being returned
|
||||
|
||||
##### Returns
|
||||
|
||||
- [Browser](/docs/api/browser) object
|
||||
|
||||
##### Example
|
||||
|
||||
```js
|
||||
import { remote } from 'webdriverio'
|
||||
|
||||
const browser = await remote({
|
||||
capabilities: { browserName: 'chrome' }
|
||||
})
|
||||
```
|
||||
|
||||
#### `attach(attachOptions)`
|
||||
|
||||
Attaches to a running WebdriverIO session.
|
||||
|
||||
##### Paramaters
|
||||
|
||||
- `attachOptions`: instance to attach a session to or at least an object with a property `sessionId` (e.g. `{ sessionId: 'xxx' }`)
|
||||
|
||||
##### Returns
|
||||
|
||||
- [Browser](/docs/api/browser) object
|
||||
|
||||
##### Example
|
||||
|
||||
```js
|
||||
import { remote, attach } from 'webdriverio'
|
||||
|
||||
const browser = await remote({...})
|
||||
const newBrowser = await attach(browser)
|
||||
```
|
||||
|
||||
#### `multiremote(multiremoteOptions)`
|
||||
|
||||
Initiates a multiremote instance which allows you to control multiple session within a single instance. Checkout our [multiremote examples](https://github.com/webdriverio/webdriverio/tree/main/examples/multiremote) for concrete use cases.
|
||||
|
||||
##### Paramaters
|
||||
|
||||
- `multiremoteOptions`: an object with keys representing the browser name and their [WebdriverIO Options](/docs/configuration#webdriverio).
|
||||
|
||||
##### Returns
|
||||
|
||||
- [Browser](/docs/api/browser) object
|
||||
|
||||
##### Example
|
||||
|
||||
```js
|
||||
import { multiremote } from 'webdriverio'
|
||||
|
||||
const matrix = await multiremote({
|
||||
myChromeBrowser: {
|
||||
capabilities: { browserName: 'chrome' }
|
||||
},
|
||||
myFirefoxBrowser: {
|
||||
capabilities: { browserName: 'firefox' }
|
||||
}
|
||||
})
|
||||
await matrix.url('http://json.org')
|
||||
await matrix.getInstance('browserA').url('https://google.com')
|
||||
|
||||
console.log(await matrix.getTitle())
|
||||
// returns ['Google', 'JSON']
|
||||
```
|
||||
|
||||
## `@wdio/cli`
|
||||
|
||||
Instead of calling the `wdio` command, you can also include the test runner as module and run it in an arbitrary environment. For that, you'll need to require the `@wdio/cli` package as module, like this:
|
||||
|
||||
<Tabs
|
||||
defaultValue="esm"
|
||||
values={[
|
||||
{label: 'EcmaScript Modules', value: 'esm'},
|
||||
{label: 'CommonJS', value: 'cjs'}
|
||||
]
|
||||
}>
|
||||
<TabItem value="esm">
|
||||
|
||||
```js
|
||||
import Launcher from '@wdio/cli'
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="cjs">
|
||||
|
||||
```js
|
||||
const Launcher = require('@wdio/cli').default
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
After that, create an instance of the launcher, and run the test.
|
||||
|
||||
#### `Launcher(configPath, opts)`
|
||||
|
||||
The `Launcher` class constructor expects the URL to the config file, and an `opts` object with settings that will overwrite those in the config.
|
||||
|
||||
##### Paramaters
|
||||
|
||||
- `configPath`: path to the `wdio.conf.js` to run
|
||||
- `opts`: arguments ([`<RunCommandArguments>`](https://github.com/webdriverio/webdriverio/blob/main/packages/wdio-cli/src/types.ts#L51-L77)) to overwrite values from the config file
|
||||
|
||||
##### Example
|
||||
|
||||
```js
|
||||
const wdio = new Launcher(
|
||||
'/path/to/my/wdio.conf.js',
|
||||
{ spec: '/path/to/a/single/spec.e2e.js' }
|
||||
)
|
||||
|
||||
wdio.run().then((exitCode) => {
|
||||
process.exit(exitCode)
|
||||
}, (error) => {
|
||||
console.error('Launcher failed to start the test', error.stacktrace)
|
||||
process.exit(1)
|
||||
})
|
||||
```
|
||||
|
||||
The `run` command returns a [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise). It is resolved if tests ran successfully or failed, and it is rejected if the launcher was unable to start run the tests.
|
||||
|
||||
## `@wdio/browser-runner`
|
||||
|
||||
When running unit or component tests using WebdriverIO's [browser runner](/docs/runner#browser-runner) you can import mocking utilities for your tests, e.g.:
|
||||
|
||||
```ts
|
||||
import { fn, spyOn, mock, unmock } from '@wdio/browser-runner'
|
||||
```
|
||||
|
||||
The following named exports are available:
|
||||
|
||||
#### `fn`
|
||||
|
||||
Mock function, see more in the official [Vitest docs](https://vitest.dev/api/mock.html#mock-functions).
|
||||
|
||||
#### `spyOn`
|
||||
|
||||
Spy function, see more in the official [Vitest docs](https://vitest.dev/api/mock.html#mock-functions).
|
||||
|
||||
#### `mock`
|
||||
|
||||
Method to mock file or dependency module.
|
||||
|
||||
##### Paramaters
|
||||
|
||||
- `moduleName`: either a relative path to the file to be mocked or a module name.
|
||||
- `factory`: function to return the mocked value (optional)
|
||||
|
||||
##### Example
|
||||
|
||||
```js
|
||||
mock('../src/constants.ts', () => ({
|
||||
SOME_DEFAULT: 'mocked out'
|
||||
}))
|
||||
|
||||
mock('lodash', (origModuleFactory) => {
|
||||
const origModule = await origModuleFactory()
|
||||
return {
|
||||
...origModule,
|
||||
pick: fn()
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
#### `unmock`
|
||||
|
||||
Unmock dependency that is defined within the manual mock (`__mocks__`) directory.
|
||||
|
||||
##### Paramaters
|
||||
|
||||
- `moduleName`: name of the module to be unmocked.
|
||||
|
||||
##### Example
|
||||
|
||||
```js
|
||||
unmock('lodash')
|
||||
```
|
||||
|
|
@ -1,68 +0,0 @@
|
|||
---
|
||||
id: protocols
|
||||
title: Protocol Commands
|
||||
---
|
||||
|
||||
WebdriverIO is an automation framework that relies on various of automation protocols to control a remote agent, e.g. for a browser, mobile device or television. Based on the remote device different protocols come into play. These commands are assigned to the [Browser](/docs/api/browser) or [Element](/docs/api/element) Object depending on the session information by the remote server (e.g. browser driver).
|
||||
|
||||
Internally WebdriverIO uses protocol commands for almost all interactions with the remote agent. However additional commands assigned to the [Browser](/docs/api/browser) or [Element](/docs/api/element) Object simplify the usage of WebdriverIO, e.g. getting the text of an element using protocol commands would look like this:
|
||||
|
||||
```js
|
||||
const searchInput = await browser.findElement('css selector', '#lst-ib')
|
||||
await client.getElementText(searchInput['element-6066-11e4-a52e-4f735466cecf'])
|
||||
```
|
||||
|
||||
Using the convenient commands of the [Browser](/docs/api/browser) or [Element](/docs/api/element) Object this can be reduced to:
|
||||
|
||||
```js
|
||||
$('#lst-ib').getText()
|
||||
```
|
||||
|
||||
The following section explain each individual protocol.
|
||||
|
||||
## WebDriver Protocol
|
||||
|
||||
The [WebDriver](https://w3c.github.io/webdriver/#elements) protocol is a web standard for automating browser. As oppose to some other E2E tools it guarantees that automation can be done on actual browser that are used by your users, e.g. Firefox, Safari and Chrome and Chromium based browser like Edge, and not only on browser engines, e.g. WebKit, which are very different.
|
||||
|
||||
The advantage of using the WebDriver protocol as oppose to debugging protocols like [Chrome DevTools](https://w3c.github.io/webdriver/#elements) is that you have a specific set of commands that allow to interact with the browser the same way across all browser which reduces the likelihood for flakiness. Furthermore offers this protocol abilities for massive scalability by using cloud vendors such as [Sauce Labs](https://saucelabs.com/), [BrowserStack](https://www.browserstack.com/) and [others](https://github.com/christian-bromann/awesome-selenium#cloud-services).
|
||||
|
||||
## WebDriver Bidi Protocol
|
||||
|
||||
The [WebDriver Bidi](https://w3c.github.io/webdriver-bidi/) protocol is the second generation of the protocol and is currently being worked on by most browser vendors. Compared to its pre-predecessor the protocol supports a bi-directional communication (hence "Bidi") between the framework and the remote device. It furthermore introduces additional primitives for better browser introspection to better automate modern web applications in browser.
|
||||
|
||||
Given this protocol is currently work in progress more features will be added over time and supported by browser. If you use WebdriverIOs convenient commands nothing will change for you. WebdriverIO will make use of these new protocol capabilities as soon as they are available and supported in the browser.
|
||||
|
||||
## Appium
|
||||
|
||||
The [Appium](https://appium.io/) project provides capabilities to automate mobile, desktop and all other kinds of IoT devices. While WebDriver focuses on browser and the web, the vision of Appium is to use the same approach but for any arbitrary device. In addition to the commands that WebDriver defines, it has special commands that often are specific to the remote device that is being automated. For mobile testing scenarios this is ideal when you want to write and run the same tests for both Android and iOS applications.
|
||||
|
||||
According to Appium [documentation](https://appium.github.io/appium.io/docs/en/about-appium/intro/?lang=en) it was designed to meet mobile automation needs according to a philosophy outlined by the following four tenets:
|
||||
|
||||
- You shouldn't have to recompile your app or modify it in any way in order to automate it.
|
||||
- You shouldn't be locked into a specific language or framework to write and run your tests.
|
||||
- A mobile automation framework shouldn't reinvent the wheel when it comes to automation APIs.
|
||||
- A mobile automation framework should be open source, in spirit and practice as well as in name!
|
||||
|
||||
## Chromium
|
||||
|
||||
The Chromium protocol offers a super set of commands on top of the WebDriver protocol that is only supported when running automated session through [Chromedriver](https://chromedriver.chromium.org/chromedriver-canary) or [Edgedriver](https://developer.microsoft.com/fr-fr/microsoft-edge/tools/webdriver).
|
||||
|
||||
## Firefox
|
||||
|
||||
The Firefox protocol offers a super set of commands on top of the WebDriver protocol that is only supported when running automated session through [Geckodriver](https://github.com/mozilla/geckodriver).
|
||||
|
||||
## Sauce Labs
|
||||
|
||||
The [Sauce Labs](https://saucelabs.com/) protocol offers a super set of commands on top of the WebDriver protocol that is only supported when running automated session using the Sauce Labs cloud.
|
||||
|
||||
## Selenium Standalone
|
||||
|
||||
The [Selenium Standalone](https://www.selenium.dev/documentation/grid/advanced_features/endpoints/) protocol offers a super set of commands on top of the WebDriver protocol that is only supported when running automated session using the Selenium Grid.
|
||||
|
||||
## JSON Wire Protocol
|
||||
|
||||
The [JSON Wire Protocol](https://www.selenium.dev/documentation/legacy/json_wire_protocol/) is the pre-predecessor of the WebDriver protocol and __deprecated__ today. While some commands might still be supported in certain environments, it is not recommended to use any of its commands.
|
||||
|
||||
## Mobile JSON Wire Protocol
|
||||
|
||||
The [Mobile JSON Wire Protocol](https://github.com/SeleniumHQ/mobile-spec/blob/master/spec-draft.md) is a super set of mobile commands on top of the JSON Wire Protocol. Given this one is deprecated the Mobile JSON Wire Protocol also got __deprecated__. Appium might still support some of its commands but it is not recommended to use them.
|
||||
|
|
@ -1,80 +0,0 @@
|
|||
---
|
||||
id: browser-logs
|
||||
title: Browser Logs
|
||||
---
|
||||
|
||||
When running tests the browser may log important information that you are interested or want to assert against.
|
||||
|
||||
<Tabs
|
||||
defaultValue="bidi"
|
||||
values={[
|
||||
{label: 'Bidi', value: 'bidi'},
|
||||
{label: 'Classic (Deprecated)', value: 'classic'
|
||||
}]
|
||||
}>
|
||||
|
||||
<TabItem value='bidi'>
|
||||
|
||||
When using WebDriver Bidi, which is the default way how WebdriverIO automates the browser, you can subscribe to events coming from the browser. For log events you want to listen on `log.entryAdded'`, e.g.:
|
||||
|
||||
```ts
|
||||
await browser.sessionSubscribe({ events: ['log.entryAdded'] })
|
||||
|
||||
/**
|
||||
* returns: {"type":"console","method":"log","realm":null,"args":[{"type":"string","value":"Hello Bidi"}],"level":"info","text":"Hello Bidi","timestamp":1657282076037}
|
||||
*/
|
||||
browser.on('log.entryAdded', (entryAdded) => console.log('received %s', entryAdded))
|
||||
```
|
||||
|
||||
In a test you can just push log events to an array an assert that array once your action is done, e.g.:
|
||||
|
||||
```ts
|
||||
import type { local } from 'webdriver'
|
||||
|
||||
describe('should log when doing a certain action', () => {
|
||||
const logs: string[] = []
|
||||
|
||||
function logEvents (event: local.LogEntry) {
|
||||
logs.push(event.text) // add log message to the array
|
||||
}
|
||||
|
||||
before(async () => {
|
||||
await browser.sessionSubscribe({ events: ['log.entryAdded'] })
|
||||
browser.on('log.entryAdded', logEvents)
|
||||
})
|
||||
|
||||
it('should trigger the console event', () => {
|
||||
// trigger the browser send a message to the console
|
||||
...
|
||||
|
||||
// assert if log was captured
|
||||
expect(logs).toContain('Hello Bidi')
|
||||
})
|
||||
|
||||
// clean up listener afterwards
|
||||
after(() => {
|
||||
browser.off('log.entryAdded', logEvents)
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
|
||||
<TabItem value='classic'>
|
||||
|
||||
If you still use WebDriver Classic or disabled Bidi usage via the `'wdio:enforceWebDriverClassic': true` capability, you can use the `getLogs` JSONWire command to fetch the latest logs. Since WebdriverIO has removed these deprecated commands you will have to use the [JSONWP Service](https://github.com/webdriverio-community/wdio-jsonwp-service) to add the command back to your browser instance.
|
||||
|
||||
After you added or initiate the service you can fetch logs via:
|
||||
|
||||
```ts
|
||||
const logs = await browser.getLogs('browser')
|
||||
const logMessage = logs.find((log) => log.message.includes('Hello Bidi'))
|
||||
expect(logMessage).toBeTruthy()
|
||||
```
|
||||
|
||||
Note: the `getLogs` command can only fetch the most recent logs from the browser. It may clean up log messages eventually if they become to old.
|
||||
</TabItem>
|
||||
|
||||
</Tabs>
|
||||
|
||||
Please note that you can use this method to retrieve error messages and verify whether your application has encountered any errors.
|
||||
|
|
@ -1,111 +0,0 @@
|
|||
---
|
||||
id: تنزيل-الملف
|
||||
title: تنزيل الملف
|
||||
---
|
||||
|
||||
عند أتمتة تنزيل الملف في اختبار الويب من الأساسي تنفيذها بشكل مستمر عبر متصفحات مختلفة للتأكد من موثوقية تنفيذ الاختبار.
|
||||
|
||||
هنا، نحن نوفر أفضل الممارسات لتنزيل الملفات ونوضح كيفية تكوين أدلة التحميل لـ **Google Chrome**، **Mozilla Firefox**، و **Microsoft Edge**.
|
||||
|
||||
## مسارات التنزيل
|
||||
|
||||
**الترميز الثابت** مسارات التنزيل في نص الاختبار يمكن أن يؤدي إلى مشاكل في الصيانة وإمكانية النقل. استخدام **المسارات النسبية** لمسارات التنزيل لضمان قابلية النقل والتوافق عبر البيئات المختلفة.
|
||||
|
||||
```javascript
|
||||
// 👎
|
||||
// Hardcoded download path
|
||||
const downloadPath = '/path/to/downloads';
|
||||
|
||||
// 👍
|
||||
// Relative download path
|
||||
const downloadPath = path.join(__dirname, 'downloads');
|
||||
```
|
||||
|
||||
## إستراتيجيات الإنتظار
|
||||
|
||||
Failing to implement proper wait strategies can lead to race conditions or unreliable tests, especially for download completion. Implement **explicit** wait strategies to wait for file downloads to complete, ensuring synchronization between test steps.
|
||||
|
||||
```javascript
|
||||
// 👎
|
||||
// No explicit wait for download completion
|
||||
await browser.pause(5000);
|
||||
|
||||
// 👍
|
||||
// Wait for file download completion
|
||||
await waitUntil(async ()=> await fs.existsSync(downloadPath), 5000);
|
||||
```
|
||||
|
||||
## Configuring Download Directories
|
||||
|
||||
To override file download behavior for **Google Chrome**, **Mozilla Firefox**, and **Microsoft Edge**, provide the download directory in the WebDriverIO capabilities:
|
||||
|
||||
<Tabs
|
||||
defaultValue="chrome"
|
||||
values={[
|
||||
{label: 'Chrome', value: 'chrome'},
|
||||
{label: 'Firefox', value: 'firefox'},
|
||||
{label: 'Microsoft Edge', value: 'edge'},
|
||||
]
|
||||
}>
|
||||
|
||||
<TabItem value='chrome'>
|
||||
|
||||
```javascript reference title="wdio.conf.js"
|
||||
|
||||
https://github.com/webdriverio/example-recipes/blob/84dda93011234d0b2a34ee0cfb3cdfa2a06136a5/testDownloadBehavior/wdio.conf.js#L8-L16
|
||||
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
|
||||
<TabItem value='firefox'>
|
||||
|
||||
```javascript reference title="wdio.conf.js"
|
||||
|
||||
https://github.com/webdriverio/example-recipes/blob/84dda93011234d0b2a34ee0cfb3cdfa2a06136a5/testDownloadBehavior/wdio.conf.js#L20-L32
|
||||
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
|
||||
<TabItem value='edge'>
|
||||
|
||||
```javascript reference title="wdio.conf.js"
|
||||
|
||||
https://github.com/webdriverio/example-recipes/blob/84dda93011234d0b2a34ee0cfb3cdfa2a06136a5/testDownloadBehavior/wdio.conf.js#L36-L44
|
||||
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
|
||||
</Tabs>
|
||||
|
||||
For an example implementation, refer to the [WebdriverIO Test Download Behavior Recipe](https://github.com/webdriverio/example-recipes/tree/main/testDownloadBehavior).
|
||||
|
||||
## Configuring Chromium Browser Downloads
|
||||
|
||||
To change the download path for __Chromium-based__ browsers (such as Chrome, Edge, Brave, etc.) using WebDriverIOs `getPuppeteer` method for accessing Chrome DevTools.
|
||||
|
||||
```javascript
|
||||
const page = await browser.getPuppeteer();
|
||||
// Initiate a CDP Session:
|
||||
const cdpSession = await page.target().createCDPSession();
|
||||
// Set the Download Path:
|
||||
await cdpSession.send('Browser.setDownloadBehavior', { behavior: 'allow', downloadPath: downloadPath });
|
||||
```
|
||||
|
||||
## Handling Multiple File Downloads
|
||||
|
||||
When dealing with scenarios involving multiple file downloads, it's essential to implement strategies to manage and validate each download effectively. Consider the following approaches:
|
||||
|
||||
__Sequential Download Handling:__ Download files one by one and verify each download before initiating the next one to ensure orderly execution and accurate validation.
|
||||
|
||||
__Parallel Download Handling:__ Utilize asynchronous programming techniques to initiate multiple file downloads simultaneously, optimizing test execution time. Implement robust validation mechanisms to verify all downloads upon completion.
|
||||
|
||||
## Cross-Browser Compatibility Considerations
|
||||
|
||||
While WebDriverIO provides a unified interface for browser automation, it's essential to account for variations in browser behavior and capabilities. Consider testing your file download functionality across different browsers to ensure compatibility and consistency.
|
||||
|
||||
__Browser-Specific Configurations:__ Adjust download path settings and wait strategies to accommodate differences in browser behavior and preferences across Chrome, Firefox, Edge, and other supported browsers.
|
||||
|
||||
__Browser Version Compatibility:__ Regularly update your WebDriverIO and browser versions to leverage the latest features and enhancements while ensuring compatibility with your existing test suite.
|
||||
|
|
@ -1,69 +0,0 @@
|
|||
---
|
||||
id: lit
|
||||
title: Lit
|
||||
---
|
||||
|
||||
Lit is a simple library for building fast, lightweight web components. Testing Lit web components with WebdriverIO is very easy thanks to WebdriverIOs [shadow DOM selectors](/docs/selectors#deep-selectors) you can query in shadow roots nested elements with just one single command.
|
||||
|
||||
## Setup
|
||||
|
||||
To setup WebdriverIO within your Lit project, follow the [instructions](/docs/component-testing#set-up) in our component testing docs. For Lit you don't need a preset as Lit web components don't need to run through a compiler, they are pure web component enhancements.
|
||||
|
||||
Once set-up, you can start the tests by running:
|
||||
|
||||
```sh
|
||||
npx wdio run ./wdio.conf.js
|
||||
```
|
||||
|
||||
## Writing Tests
|
||||
|
||||
Given you have the following Lit component:
|
||||
|
||||
```ts title="./components/Component.ts"
|
||||
import { LitElement, css, html } from 'lit'
|
||||
import { customElement, property } from 'lit/decorators.js'
|
||||
|
||||
@customElement('simple-greeting')
|
||||
export class SimpleGreeting extends LitElement {
|
||||
@property()
|
||||
name?: string = 'World'
|
||||
|
||||
// Render the UI as a function of component state
|
||||
render() {
|
||||
return html`<p>Hello, ${this.name}!</p>`
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
In order test the component you have to render it into the test page before the test starts and ensure it gets cleaned up afterwards:
|
||||
|
||||
```ts title="lit.test.js"
|
||||
import expect from 'expect'
|
||||
import { waitFor } from '@testing-library/dom'
|
||||
|
||||
// import Lit component
|
||||
import './components/Component.ts'
|
||||
|
||||
describe('Lit Component testing', () => {
|
||||
let elem: HTMLElement
|
||||
|
||||
beforeEach(() => {
|
||||
elem = document.createElement('simple-greeting')
|
||||
})
|
||||
|
||||
it('should render component', async () => {
|
||||
elem.setAttribute('name', 'WebdriverIO')
|
||||
document.body.appendChild(elem)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(elem.shadowRoot.textContent).toBe('Hello, WebdriverIO!')
|
||||
})
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
elem.remove()
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
You can find a full example of a WebdriverIO component test suite for Lit in our [example repository](https://github.com/webdriverio/component-testing-examples/tree/main/lit-typescript-vite).
|
||||
|
|
@ -1,87 +0,0 @@
|
|||
---
|
||||
id: preact
|
||||
title: Preact
|
||||
---
|
||||
|
||||
[Preact](https://preactjs.com/) is a fast 3kB alternative to React with the same modern API. You can test Preact components directly in a real browser using WebdriverIO and its [browser runner](/docs/runner#browser-runner).
|
||||
|
||||
## Setup
|
||||
|
||||
To setup WebdriverIO within your Preact project, follow the [instructions](/docs/component-testing#set-up) in our component testing docs. Make sure to select `preact` as preset within your runner options, e.g.:
|
||||
|
||||
```js
|
||||
// wdio.conf.js
|
||||
export const config = {
|
||||
// ...
|
||||
runner: ['browser', {
|
||||
preset: 'preact'
|
||||
}],
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
:::info
|
||||
|
||||
If you are already using [Vite](https://vitejs.dev/) as development server you can also just re-use your configuration in `vite.config.ts` within your WebdriverIO config. For more information, see `viteConfig` in [runner options](/docs/runner#runner-options).
|
||||
|
||||
:::
|
||||
|
||||
The Preact preset requires `@preact/preset-vite` to be installed. Also we recommend using [Testing Library](https://testing-library.com/) for rendering the component into the test page. Therefor you'll need to install the following additional dependencies:
|
||||
|
||||
```sh npm2yarn
|
||||
npm install --save-dev @testing-library/preact @preact/preset-vite
|
||||
```
|
||||
|
||||
You can then start the tests by running:
|
||||
|
||||
```sh
|
||||
npx wdio run ./wdio.conf.js
|
||||
```
|
||||
|
||||
## Writing Tests
|
||||
|
||||
Given you have the following Preact component:
|
||||
|
||||
```tsx title="./components/Component.jsx"
|
||||
import { h } from 'preact'
|
||||
import { useState } from 'preact/hooks'
|
||||
|
||||
interface Props {
|
||||
initialCount: number
|
||||
}
|
||||
|
||||
export function Counter({ initialCount }: Props) {
|
||||
const [count, setCount] = useState(initialCount)
|
||||
const increment = () => setCount(count + 1)
|
||||
|
||||
return (
|
||||
<div>
|
||||
Current value: {count}
|
||||
<button onClick={increment}>Increment</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
In your test use the `render` method from `@testing-library/preact` to attach the component to the test page. To interact with the component we recommend to use WebdriverIO commands as they behave more close to actual user interactions, e.g.:
|
||||
|
||||
```ts title="app.test.tsx"
|
||||
import { expect } from 'expect'
|
||||
import { render, screen } from '@testing-library/preact'
|
||||
|
||||
import { Counter } from './components/PreactComponent.js'
|
||||
|
||||
describe('Preact Component Testing', () => {
|
||||
it('should increment after "Increment" button is clicked', async () => {
|
||||
const component = await $(render(<Counter initialCount={5} />))
|
||||
await expect(component).toHaveText(expect.stringContaining('Current value: 5'))
|
||||
|
||||
const incrElem = await $(screen.getByText('Increment'))
|
||||
await incrElem.click()
|
||||
await expect(component).toHaveText(expect.stringContaining('Current value: 6'))
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
You can find a full example of a WebdriverIO component test suite for Preact in our [example repository](https://github.com/webdriverio/component-testing-examples/tree/main/preact-typescript-vite).
|
||||
|
|
@ -1,88 +0,0 @@
|
|||
---
|
||||
id: react
|
||||
title: React
|
||||
---
|
||||
|
||||
[React](https://reactjs.org/) makes it painless to create interactive UIs. Design simple views for each state in your application, and React will efficiently update and render just the right components when your data changes. You can test React components directly in a real browser using WebdriverIO and its [browser runner](/docs/runner#browser-runner).
|
||||
|
||||
## Setup
|
||||
|
||||
To setup WebdriverIO within your React project, follow the [instructions](/docs/component-testing#set-up) in our component testing docs. Make sure to select `react` as preset within your runner options, e.g.:
|
||||
|
||||
```js
|
||||
// wdio.conf.js
|
||||
export const config = {
|
||||
// ...
|
||||
runner: ['browser', {
|
||||
preset: 'react'
|
||||
}],
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
:::info
|
||||
|
||||
If you are already using [Vite](https://vitejs.dev/) as development server you can also just re-use your configuration in `vite.config.ts` within your WebdriverIO config. For more information, see `viteConfig` in [runner options](/docs/runner#runner-options).
|
||||
|
||||
:::
|
||||
|
||||
The React preset requires `@vitejs/plugin-react` to be installed. Also we recommend using [Testing Library](https://testing-library.com/) for rendering the component into the test page. Therefor you'll need to install the following additional dependencies:
|
||||
|
||||
```sh npm2yarn
|
||||
npm install --save-dev @testing-library/react @vitejs/plugin-react
|
||||
```
|
||||
|
||||
You can then start the tests by running:
|
||||
|
||||
```sh
|
||||
npx wdio run ./wdio.conf.js
|
||||
```
|
||||
|
||||
## Writing Tests
|
||||
|
||||
Given you have the following React component:
|
||||
|
||||
```tsx title="./components/Component.jsx"
|
||||
import React, { useState } from 'react'
|
||||
|
||||
function App() {
|
||||
const [theme, setTheme] = useState('light')
|
||||
|
||||
const toggleTheme = () => {
|
||||
const nextTheme = theme === 'light' ? 'dark' : 'light'
|
||||
setTheme(nextTheme)
|
||||
}
|
||||
|
||||
return <button onClick={toggleTheme}>
|
||||
Current theme: {theme}
|
||||
</button>
|
||||
}
|
||||
|
||||
export default App
|
||||
```
|
||||
|
||||
In your test use the `render` method from `@testing-library/react` to attach the component to the test page. To interact with the component we recommend to use WebdriverIO commands as they behave more close to actual user interactions, e.g.:
|
||||
|
||||
```ts title="app.test.tsx"
|
||||
import { expect } from '@wdio/globals'
|
||||
import { render, screen } from '@testing-library/react'
|
||||
import userEvent from '@testing-library/user-event'
|
||||
|
||||
import * as matchers from '@testing-library/jest-dom/matchers'
|
||||
expect.extend(matchers)
|
||||
|
||||
import App from './components/Component.jsx'
|
||||
|
||||
describe('React Component Testing', () => {
|
||||
it('Test theme button toggle', async () => {
|
||||
render(<App />)
|
||||
const buttonEl = screen.getByText(/Current theme/i)
|
||||
|
||||
await $(buttonEl).click()
|
||||
expect(buttonEl).toContainHTML('dark')
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
You can find a full example of a WebdriverIO component test suite for React in our [example repository](https://github.com/webdriverio/component-testing-examples/tree/main/react-typescript-vite).
|
||||
|
||||
|
|
@ -1,98 +0,0 @@
|
|||
---
|
||||
id: solid
|
||||
title: SolidJS
|
||||
---
|
||||
|
||||
[SolidJS](https://www.solidjs.com/) is a framework to build user interfaces with simple and performant reactivity. You can test SolidJS components directly in a real browser using WebdriverIO and its [browser runner](/docs/runner#browser-runner).
|
||||
|
||||
## Setup
|
||||
|
||||
To setup WebdriverIO within your SolidJS project, follow the [instructions](/docs/component-testing#set-up) in our component testing docs. Make sure to select `solid` as preset within your runner options, e.g.:
|
||||
|
||||
```js
|
||||
// wdio.conf.js
|
||||
export const config = {
|
||||
// ...
|
||||
runner: ['browser', {
|
||||
preset: 'solid'
|
||||
}],
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
:::info
|
||||
|
||||
If you are already using [Vite](https://vitejs.dev/) as development server you can also just re-use your configuration in `vite.config.ts` within your WebdriverIO config. For more information, see `viteConfig` in [runner options](/docs/runner#runner-options).
|
||||
|
||||
:::
|
||||
|
||||
The SolidJS preset requires `vite-plugin-solid` to be installed:
|
||||
|
||||
```sh npm2yarn
|
||||
npm install --save-dev vite-plugin-solid
|
||||
```
|
||||
|
||||
You can then start the tests by running:
|
||||
|
||||
```sh
|
||||
npx wdio run ./wdio.conf.js
|
||||
```
|
||||
|
||||
## Writing Tests
|
||||
|
||||
Given you have the following SolidJS component:
|
||||
|
||||
```html title="./components/Component.tsx"
|
||||
import { createSignal } from 'solid-js'
|
||||
|
||||
function App() {
|
||||
const [theme, setTheme] = createSignal('light')
|
||||
|
||||
const toggleTheme = () => {
|
||||
const nextTheme = theme() === 'light' ? 'dark' : 'light'
|
||||
setTheme(nextTheme)
|
||||
}
|
||||
|
||||
return <button onClick={toggleTheme}>
|
||||
Current theme: {theme()}
|
||||
</button>
|
||||
}
|
||||
|
||||
export default App
|
||||
```
|
||||
|
||||
In your test use the `render` method from `solid-js/web` to attach the component to the test page. To interact with the component we recommend to use WebdriverIO commands as they behave more close to actual user interactions, e.g.:
|
||||
|
||||
```ts title="app.test.tsx"
|
||||
import { expect } from '@wdio/globals'
|
||||
import { render } from 'solid-js/web'
|
||||
|
||||
import App from './components/Component.jsx'
|
||||
|
||||
describe('Solid Component Testing', () => {
|
||||
/**
|
||||
* ensure we render the component for every test in a
|
||||
* new root container
|
||||
*/
|
||||
let root: Element
|
||||
beforeEach(() => {
|
||||
if (root) {
|
||||
root.remove()
|
||||
}
|
||||
|
||||
root = document.createElement('div')
|
||||
document.body.appendChild(root)
|
||||
})
|
||||
|
||||
it('Test theme button toggle', async () => {
|
||||
render(<App />, root)
|
||||
const buttonEl = await $('button')
|
||||
|
||||
await buttonEl.click()
|
||||
expect(buttonEl).toContainHTML('dark')
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
You can find a full example of a WebdriverIO component test suite for SolidJS in our [example repository](https://github.com/webdriverio/component-testing-examples/tree/main/solidjs-typescript-vite).
|
||||
|
||||
|
|
@ -1,207 +0,0 @@
|
|||
---
|
||||
id: stencil
|
||||
title: Stencil
|
||||
---
|
||||
|
||||
[Stencil](https://stenciljs.com/) is a library for building reusable, scalable component libraries. You can test Stencil components directly in a real browser using WebdriverIO and its [browser runner](/docs/runner#browser-runner).
|
||||
|
||||
## Setup
|
||||
|
||||
To set up WebdriverIO within your Stencil project, follow the [instructions](/docs/component-testing#set-up) in our component testing docs. Make sure to select `stencil` as preset within your runner options, e.g.:
|
||||
|
||||
```js
|
||||
// wdio.conf.js
|
||||
export const config = {
|
||||
// ...
|
||||
runner: ['browser', {
|
||||
preset: 'stencil'
|
||||
}],
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
:::info
|
||||
|
||||
In case you use Stencil with a framework like React or Vue, you should keep the preset for these frameworks.
|
||||
|
||||
:::
|
||||
|
||||
You can then start the tests by running:
|
||||
|
||||
```sh
|
||||
npx wdio run ./wdio.conf.ts
|
||||
```
|
||||
|
||||
## Writing Tests
|
||||
|
||||
Given you have the following Stencil components:
|
||||
|
||||
```tsx title="./components/Component.tsx"
|
||||
import { Component, Prop, h } from '@stencil/core'
|
||||
|
||||
@Component({
|
||||
tag: 'my-name',
|
||||
shadow: true
|
||||
})
|
||||
export class MyName {
|
||||
@Prop() name: string
|
||||
|
||||
normalize(name: string): string {
|
||||
if (name) {
|
||||
return name.slice(0, 1).toUpperCase() + name.slice(1).toLowerCase()
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div class="text">
|
||||
<p>Hello! My name is {this.normalize(this.name)}.</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### `render`
|
||||
|
||||
In your test use the `render` method from `@wdio/browser-runner/stencil` to attach the component to the test page. To interact with the component we recommend using WebdriverIO commands as they behave closer to actual user interactions, e.g.:
|
||||
|
||||
```tsx title="app.test.tsx"
|
||||
import { expect } from '@wdio/globals'
|
||||
import { render } from '@wdio/browser-runner/stencil'
|
||||
|
||||
import MyNameComponent from './components/Component.tsx'
|
||||
|
||||
describe('Stencil Component Testing', () => {
|
||||
it('should render component correctly', async () => {
|
||||
await render({
|
||||
components: [MyNameComponent],
|
||||
template: () => (
|
||||
<my-name name={'stencil'}></my-name>
|
||||
)
|
||||
})
|
||||
await expect($('.text')).toHaveText('Hello! My name is Stencil.')
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
#### Render Options
|
||||
|
||||
The `render` method provides the following options:
|
||||
|
||||
##### `components`
|
||||
|
||||
An array of components to test. Component classes can be imported into the spec file, then their reference should be added to the `component` array to be used throughout the test.
|
||||
|
||||
__Type:__ `CustomElementConstructor[]`<br /> __Default:__ `[]`
|
||||
|
||||
##### `flushQueue`
|
||||
|
||||
If `false`, do not flush the render queue on the initial test setup.
|
||||
|
||||
__Type:__ `boolean`<br /> __Default:__ `true`
|
||||
|
||||
##### `template`
|
||||
|
||||
The initial JSX that is used to generate the test. Use `template` when you want to initialize a component using their properties, instead of their HTML attributes. It will render the specified template (JSX) into `document.body`.
|
||||
|
||||
__Type:__ `JSX.Template`
|
||||
|
||||
##### `html`
|
||||
|
||||
The initial HTML used to generate the test. This can be useful to construct a collection of components working together, and assign HTML attributes.
|
||||
|
||||
__Type:__ `string`
|
||||
|
||||
##### `language`
|
||||
|
||||
Sets the mocked `lang` attribute on `<html>`.
|
||||
|
||||
__Type:__ `string`
|
||||
|
||||
##### `autoApplyChanges`
|
||||
|
||||
By default, any changes to component properties and attributes must `env.waitForChanges()` to test the updates. As an option, `autoApplyChanges` continuously flushes the queue in the background.
|
||||
|
||||
__Type:__ `boolean`<br /> __Default:__ `false`
|
||||
|
||||
##### `attachStyles`
|
||||
|
||||
By default, styles are not attached to the DOM and they are not reflected in the serialized HTML. Setting this option to `true` will include the component's styles in the serializable output.
|
||||
|
||||
__Type:__ `boolean`<br /> __Default:__ `false`
|
||||
|
||||
#### Render Environment
|
||||
|
||||
The `render` method returns an environment object that provides certain utility helpers to manage the component's environment.
|
||||
|
||||
##### `flushAll`
|
||||
|
||||
After changes have been made to a component, such as an update to a property or attribute, the test page does not automatically apply the changes. To wait for, and apply the update, call `await flushAll()`
|
||||
|
||||
__Type:__ `() => void`
|
||||
|
||||
##### `unmount`
|
||||
|
||||
Removes the container element from the DOM.
|
||||
|
||||
__Type:__ `() => void`
|
||||
|
||||
##### `styles`
|
||||
|
||||
All styles defined by components.
|
||||
|
||||
__Type:__ `Record<string, string>`
|
||||
|
||||
##### `container`
|
||||
|
||||
Container element in which the template is being rendered.
|
||||
|
||||
__Type:__ `HTMLElement`
|
||||
|
||||
##### `$container`
|
||||
|
||||
The container element as a WebdriverIO element.
|
||||
|
||||
__Type:__ `WebdriverIO.Element`
|
||||
|
||||
##### `root`
|
||||
|
||||
The root component of the template.
|
||||
|
||||
__Type:__ `HTMLElement`
|
||||
|
||||
##### `$root`
|
||||
|
||||
The root component as a WebdriverIO element.
|
||||
|
||||
__Type:__ `WebdriverIO.Element`
|
||||
|
||||
### `waitForChanges`
|
||||
|
||||
Helper method to wait for the component to be ready.
|
||||
|
||||
```ts
|
||||
import { render, waitForChanges } from '@wdio/browser-runner/stencil'
|
||||
import { MyComponent } from './component.tsx'
|
||||
|
||||
const page = render({
|
||||
components: [MyComponent],
|
||||
html: '<my-component></my-component>'
|
||||
})
|
||||
|
||||
expect(page.root.querySelector('div')).not.toBeDefined()
|
||||
await waitForChanges()
|
||||
expect(page.root.querySelector('div')).toBeDefined()
|
||||
```
|
||||
|
||||
## Element Updates
|
||||
|
||||
If you define properties or states in your Stencil component you have to manage when these changes should be applied to the component to be re-rendered.
|
||||
|
||||
|
||||
## Examples
|
||||
|
||||
You can find a full example of a WebdriverIO component test suite for Stencil in our [example repository](https://github.com/webdriverio/component-testing-examples/tree/main/stencil-component-starter).
|
||||
|
||||
|
|
@ -1,82 +0,0 @@
|
|||
---
|
||||
id: svelte
|
||||
title: Svelte
|
||||
---
|
||||
|
||||
[Svelte](https://svelte.dev/) is a radical new approach to building user interfaces. Whereas traditional frameworks like React and Vue do the bulk of their work in the browser, Svelte shifts that work into a compile step that happens when you build your app. You can test Svelte components directly in a real browser using WebdriverIO and its [browser runner](/docs/runner#browser-runner).
|
||||
|
||||
## Setup
|
||||
|
||||
To setup WebdriverIO within your Svelte project, follow the [instructions](/docs/component-testing#set-up) in our component testing docs. Make sure to select `svelte` as preset within your runner options, e.g.:
|
||||
|
||||
```js
|
||||
// wdio.conf.js
|
||||
export const config = {
|
||||
// ...
|
||||
runner: ['browser', {
|
||||
preset: 'svelte'
|
||||
}],
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
:::info
|
||||
|
||||
If you are already using [Vite](https://vitejs.dev/) as development server you can also just re-use your configuration in `vite.config.ts` within your WebdriverIO config. For more information, see `viteConfig` in [runner options](/docs/runner#runner-options).
|
||||
|
||||
:::
|
||||
|
||||
The Svelte preset requires `@sveltejs/vite-plugin-svelte` to be installed. Also we recommend using [Testing Library](https://testing-library.com/) for rendering the component into the test page. Therefor you'll need to install the following additional dependencies:
|
||||
|
||||
```sh npm2yarn
|
||||
npm install --save-dev @testing-library/svelte @sveltejs/vite-plugin-svelte
|
||||
```
|
||||
|
||||
You can then start the tests by running:
|
||||
|
||||
```sh
|
||||
npx wdio run ./wdio.conf.js
|
||||
```
|
||||
|
||||
## Writing Tests
|
||||
|
||||
Given you have the following Svelte component:
|
||||
|
||||
```html title="./components/Component.svelte"
|
||||
<script>
|
||||
export let name
|
||||
|
||||
let buttonText = 'Button'
|
||||
|
||||
function handleClick() {
|
||||
buttonText = 'Button Clicked'
|
||||
}
|
||||
</script>
|
||||
|
||||
<h1>Hello {name}!</h1>
|
||||
<button on:click="{handleClick}">{buttonText}</button>
|
||||
```
|
||||
|
||||
In your test use the `render` method from `@testing-library/svelte` to attach the component to the test page. To interact with the component we recommend to use WebdriverIO commands as they behave more close to actual user interactions, e.g.:
|
||||
|
||||
```ts title="svelte.test.js"
|
||||
import expect from 'expect'
|
||||
|
||||
import { render, fireEvent, screen } from '@testing-library/svelte'
|
||||
import '@testing-library/jest-dom'
|
||||
|
||||
import Component from './components/Component.svelte'
|
||||
|
||||
describe('Svelte Component Testing', () => {
|
||||
it('changes button text on click', async () => {
|
||||
render(Component, { name: 'World' })
|
||||
const button = await $('button')
|
||||
await expect(button).toHaveText('Button')
|
||||
await button.click()
|
||||
await expect(button).toHaveText('Button Clicked')
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
You can find a full example of a WebdriverIO component test suite for Svelte in our [example repository](https://github.com/webdriverio/component-testing-examples/tree/main/svelte-typescript-vite).
|
||||
|
||||
|
|
@ -1,278 +0,0 @@
|
|||
---
|
||||
id: vue
|
||||
title: Vue.js
|
||||
---
|
||||
|
||||
[Vue.js](https://vuejs.org/) is an approachable, performant and versatile framework for building web user interfaces. You can test Vue.js components directly in a real browser using WebdriverIO and its [browser runner](/docs/runner#browser-runner).
|
||||
|
||||
## Setup
|
||||
|
||||
To setup WebdriverIO within your Vue.js project, follow the [instructions](/docs/component-testing#set-up) in our component testing docs. Make sure to select `vue` as preset within your runner options, e.g.:
|
||||
|
||||
```js
|
||||
// wdio.conf.js
|
||||
export const config = {
|
||||
// ...
|
||||
runner: ['browser', {
|
||||
preset: 'vue'
|
||||
}],
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
:::info
|
||||
|
||||
If you are already using [Vite](https://vitejs.dev/) as development server you can also just re-use your configuration in `vite.config.ts` within your WebdriverIO config. For more information, see `viteConfig` in [runner options](/docs/runner#runner-options).
|
||||
|
||||
:::
|
||||
|
||||
The Vue preset requires `@vitejs/plugin-vue` to be installed. Also we recommend using [Testing Library](https://testing-library.com/) for rendering the component into the test page. Therefor you'll need to install the following additional dependencies:
|
||||
|
||||
```sh npm2yarn
|
||||
npm install --save-dev @testing-library/vue @vitejs/plugin-vue
|
||||
```
|
||||
|
||||
You can then start the tests by running:
|
||||
|
||||
```sh
|
||||
npx wdio run ./wdio.conf.js
|
||||
```
|
||||
|
||||
## Writing Tests
|
||||
|
||||
Given you have the following Vue.js component:
|
||||
|
||||
```tsx title="./components/Component.vue"
|
||||
<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>
|
||||
```
|
||||
|
||||
In your test render the component into the DOM and run assertions on it. We recommend to either use [`@vue/test-utils`](https://test-utils.vuejs.org/) or [`@testing-library/vue`](https://testing-library.com/docs/vue-testing-library/intro/) to attach the component to the test page. To interact with the component use WebdriverIO commands as they behave more close to actual user interactions, e.g.:
|
||||
|
||||
<Tabs
|
||||
defaultValue="utils"
|
||||
values={[
|
||||
{label: '@vue/test-utils', value: 'utils'},
|
||||
{label: '@testing-library/vue', value: 'testinglib'}
|
||||
]
|
||||
}>
|
||||
<TabItem value="utils">
|
||||
|
||||
```ts title="vue.test.js"
|
||||
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
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="testinglib">
|
||||
|
||||
```ts title="vue.test.js"
|
||||
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
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
You can find a full example of a WebdriverIO component test suite for Vue.js in our [example repository](https://github.com/webdriverio/component-testing-examples/tree/main/vue-typescript-vite).
|
||||
|
||||
## Testing Async Components in Vue3
|
||||
|
||||
If you are using Vue v3 and are testing [async components](https://vuejs.org/guide/built-ins/suspense.html#async-setup) like the following:
|
||||
|
||||
```vue
|
||||
<script setup>
|
||||
const res = await fetch(...)
|
||||
const posts = await res.json()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
{{ posts }}
|
||||
</template>
|
||||
```
|
||||
|
||||
We recommend to use [`@vue/test-utils`](https://www.npmjs.com/package/@vue/test-utils) and a little suspense wrapper to get the component rendered. Unfortunately [`@testing-library/vue`](https://github.com/testing-library/vue-testing-library/issues/230) has no support for this yet. Create a `helper.ts` file with the following content:
|
||||
|
||||
```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 })
|
||||
}
|
||||
```
|
||||
|
||||
Then import and test the component as following:
|
||||
|
||||
```ts
|
||||
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()
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
## Testing Vue Components in Nuxt
|
||||
|
||||
If you are using the web framework [Nuxt](https://nuxt.com/), WebdriverIO will automatically enable the [auto-import](https://nuxt.com/docs/guide/concepts/auto-imports) feature and makes testing your Vue components and Nuxt pages easy. However any [Nuxt modules](https://nuxt.com/modules) that you might define in your config and requires context to the Nuxt application can not be supported.
|
||||
|
||||
__Reasons for that are:__
|
||||
- WebdriverIO can't initiate a Nuxt application soley in a browser environment
|
||||
- Having component tests depend too much on the Nuxt environment creates complexity and we recommend to run these tests as e2e tests
|
||||
|
||||
:::info
|
||||
|
||||
WebdriverIO also provides a service for running e2e tests on Nuxt applications, see [`webdriverio-community/wdio-nuxt-service`](https://github.com/webdriverio-community/wdio-nuxt-service) for information.
|
||||
|
||||
:::
|
||||
|
||||
### Mocking built-in composables
|
||||
|
||||
In case your component uses a native Nuxt composable, e.g. [`useNuxtData`](https://nuxt.com/docs/api/composables/use-nuxt-data), WebdriverIO will automatically mock these functions and allows you to modify their behavior or assert against them, e.g.:
|
||||
|
||||
```ts
|
||||
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: [...]
|
||||
})
|
||||
```
|
||||
|
||||
### Handling 3rd party composables
|
||||
|
||||
All [3rd party modules](https://nuxt.com/modules) that can supercharge your Nuxt project can't automatically get mocked. In those cases you need to manually mock them, e.g. given your application uses the [Supabase](https://nuxt.com/modules/supabase) module plugin:
|
||||
|
||||
```js title=""
|
||||
export default defineNuxtConfig({
|
||||
modules: [
|
||||
"@nuxtjs/supabase",
|
||||
// ...
|
||||
],
|
||||
// ...
|
||||
});
|
||||
```
|
||||
|
||||
and you create an instance of Supabase somewhere in your composables, e.g.:
|
||||
|
||||
```ts
|
||||
const superbase = useSupabaseClient()
|
||||
```
|
||||
|
||||
the test will fail due to:
|
||||
|
||||
```
|
||||
ReferenceError: useSupabaseClient is not defined
|
||||
```
|
||||
|
||||
Here, we recommend to either mock out the whole module that uses the `useSupabaseClient` function or create a global variable that mocks this function, e.g.:
|
||||
|
||||
```ts
|
||||
import { fn } from '@wdio/browser-runner'
|
||||
globalThis.useSupabaseClient = fn().mockReturnValue({})
|
||||
```
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
---
|
||||
id: coverage
|
||||
title: Coverage
|
||||
---
|
||||
|
||||
WebdriverIO's browser runner supports code coverage reporting using [`istanbul`](https://istanbul.js.org/). The testrunner will automatically instrument your code and capture code coverage for you.
|
||||
|
||||
## Setup
|
||||
|
||||
In order to enable code coverage reporting, enable it through the WebdriverIO browser runner configuration, e.g.:
|
||||
|
||||
```js title=wdio.conf.js
|
||||
export const config = {
|
||||
// ...
|
||||
runner: ['browser', {
|
||||
preset: process.env.WDIO_PRESET,
|
||||
coverage: {
|
||||
enabled: true
|
||||
}
|
||||
}],
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
Checkout all [coverage options](/docs/runner#coverage-options), to learn how to properly configure it.
|
||||
|
||||
## Ignoring Code
|
||||
|
||||
There may be some sections of your codebase that you wish to purposefully exclude from coverage tracking, to do so you can use the following parsing hints:
|
||||
|
||||
- `/* istanbul ignore if */`: ignore the next if statement.
|
||||
- `/* istanbul ignore else */`: ignore the else portion of an if statement.
|
||||
- `/* istanbul ignore next */`: ignore the next thing in the source-code ( functions, if statements, classes, you name it).
|
||||
- `/* istanbul ignore file */`: ignore an entire source-file (this should be placed at the top of the file).
|
||||
|
||||
:::info
|
||||
|
||||
It is recommended to exclude your test files from the coverage reporting as it could cause errors, e.g. when calling `execute` or `executeAsync` commands. If you like to keep them in your report, ensure your exclude instrumenting them via:
|
||||
|
||||
```ts
|
||||
await browser.execute(/* istanbul ignore next */() => {
|
||||
// ...
|
||||
})
|
||||
```
|
||||
|
||||
:::
|
||||
|
|
@ -1,308 +0,0 @@
|
|||
---
|
||||
id: mocking
|
||||
title: Mocking
|
||||
---
|
||||
|
||||
When writing tests it's only a matter of time before you need to create a "fake" version of an internal — or external — service. This is commonly referred to as mocking. WebdriverIO provides utility functions to help you out. You can `import { fn, spyOn, mock, unmock } from '@wdio/browser-runner'` to access it. See more information about the available mocking utilities in the [API docs](/docs/api/modules#wdiobrowser-runner).
|
||||
|
||||
## Functions
|
||||
|
||||
In order to validate whether certain function handler are called as part of your component tests, the `@wdio/browser-runner` module exports mocking primitives you can use to test, if these functions have been called. You can import these methods via:
|
||||
|
||||
```js
|
||||
import { fn, spyOn } from '@wdio/browser-runner'
|
||||
```
|
||||
|
||||
By importing `fn` you can create a spy function (mock) to track its execution and with `spyOn` track a method on an already created object.
|
||||
|
||||
<Tabs
|
||||
defaultValue="mocks"
|
||||
values={[
|
||||
{label: 'Mocks', value: 'mocks'},
|
||||
{label: 'Spies', value: 'spies'}
|
||||
]
|
||||
}>
|
||||
<TabItem value="mocks">
|
||||
|
||||
The full example can be found in the [Component Testing Example](https://github.com/webdriverio/component-testing-examples/blob/main/react-typescript-vite/src/tests/LoginForm.test.tsx) repository.
|
||||
|
||||
```ts
|
||||
import React from 'react'
|
||||
import { $, expect } from '@wdio/globals'
|
||||
import { fn } from '@wdio/browser-runner'
|
||||
import { Key } from 'webdriverio'
|
||||
import { render } from '@testing-library/react'
|
||||
|
||||
import LoginForm from '../components/LoginForm'
|
||||
|
||||
describe('LoginForm', () => {
|
||||
it('should call onLogin handler if username and password was provided', async () => {
|
||||
const onLogin = fn()
|
||||
render(<LoginForm onLogin={onLogin} />)
|
||||
await $('input[name="username"]').setValue('testuser123')
|
||||
await $('input[name="password"]').setValue('s3cret')
|
||||
await browser.keys(Key.Enter)
|
||||
|
||||
/**
|
||||
* verify the handler was called
|
||||
*/
|
||||
expect(onLogin).toBeCalledTimes(1)
|
||||
expect(onLogin).toBeCalledWith(expect.equal({
|
||||
username: 'testuser123',
|
||||
password: 's3cret'
|
||||
}))
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="spies">
|
||||
|
||||
The full example can be found in the [examples](https://github.com/webdriverio/webdriverio/blob/main/examples/wdio/browser-runner/lit.test.js) directory.
|
||||
|
||||
```js
|
||||
import { expect, $ } from '@wdio/globals'
|
||||
import { spyOn } from '@wdio/browser-runner'
|
||||
import { html, render } from 'lit'
|
||||
import { SimpleGreeting } from './components/LitComponent.ts'
|
||||
|
||||
const getQuestionFn = spyOn(SimpleGreeting.prototype, 'getQuestion')
|
||||
|
||||
describe('Lit Component testing', () => {
|
||||
it('should render component', async () => {
|
||||
render(
|
||||
html`<simple-greeting name="WebdriverIO" />`,
|
||||
document.body
|
||||
)
|
||||
|
||||
const innerElem = await $('simple-greeting').$('p')
|
||||
expect(await innerElem.getText()).toBe('Hello, WebdriverIO! How are you today?')
|
||||
})
|
||||
|
||||
it('should render with mocked component function', async () => {
|
||||
getQuestionFn.mockReturnValue('Does this work?')
|
||||
render(
|
||||
html`<simple-greeting name="WebdriverIO" />`,
|
||||
document.body
|
||||
)
|
||||
|
||||
const innerElem = await $('simple-greeting').$('p')
|
||||
expect(await innerElem.getText()).toBe('Hello, WebdriverIO! Does this work?')
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
WebdriverIO just re-exports [`@vitest/spy`](https://www.npmjs.com/package/@vitest/spy) here which is a lightweight Jest compatible spy implementation that can be used with WebdriverIOs [`expect`](/docs/api/expect-webdriverio) matchers. You can find more documentation on these mock functions on the [Vitest project page](https://vitest.dev/api/mock.html).
|
||||
|
||||
Of course, you can also install and import any other spy framework, e.g. [SinonJS](https://sinonjs.org/), as long as it supports the browser environment.
|
||||
|
||||
## Modules
|
||||
|
||||
Mock local modules or observe third-party-libraries, that are invoked in some other code, allowing you to test arguments, output or even redeclare its implementation.
|
||||
|
||||
There are two ways to mock functions: Either by creating a mock function to use in test code, or writing a manual mock to override a module dependency.
|
||||
|
||||
### Mocking File Imports
|
||||
|
||||
Let's imagine our component is importing a utility method from a file to handle a click.
|
||||
|
||||
```js title=utils.js
|
||||
export function handleClick () {
|
||||
// handler implementation
|
||||
}
|
||||
```
|
||||
|
||||
In our component the click handler is used as following:
|
||||
|
||||
```ts title=LitComponent.js
|
||||
import { handleClick } from './utils.js'
|
||||
|
||||
@customElement('simple-button')
|
||||
export class SimpleButton extends LitElement {
|
||||
render() {
|
||||
return html`<button @click="${handleClick}">Click me!</button>`
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
To mock the `handleClick` from `utils.js` we can use the `mock` method in our test as following:
|
||||
|
||||
```js title=LitComponent.test.js
|
||||
import { expect, $ } from '@wdio/globals'
|
||||
import { mock, fn } from '@wdio/browser-runner'
|
||||
import { html, render } from 'lit'
|
||||
|
||||
import { SimpleButton } from './LitComponent.ts'
|
||||
import { handleClick } from './utils.js'
|
||||
|
||||
/**
|
||||
* mock named export "handleClick" of `utils.ts` file
|
||||
*/
|
||||
mock('./utils.ts', () => ({
|
||||
handleClick: fn()
|
||||
}))
|
||||
|
||||
describe('Simple Button Component Test', () => {
|
||||
it('call click handler', async () => {
|
||||
render(html`<simple-button />`, document.body)
|
||||
await $('simple-button').$('button').click()
|
||||
expect(handleClick).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
### Mocking Dependencies
|
||||
|
||||
Suppose we have a class that fetches users from our API. The class uses [`axios`](https://github.com/axios/axios) to call the API then returns the data attribute which contains all the users:
|
||||
|
||||
```js title=users.js
|
||||
import axios from 'axios';
|
||||
|
||||
class Users {
|
||||
static all() {
|
||||
return axios.get('/users.json').then(resp => resp.data)
|
||||
}
|
||||
}
|
||||
|
||||
export default Users
|
||||
```
|
||||
|
||||
Now, in order to test this method without actually hitting the API (and thus creating slow and fragile tests), we can use the `mock(...)` function to automatically mock the axios module.
|
||||
|
||||
Once we mock the module we can provide a [`mockResolvedValue`](https://vitest.dev/api/mock.html#mockresolvedvalue) for `.get` that returns the data we want our test to assert against. In effect, we are saying that we want `axios.get('/users.json')` to return a fake response.
|
||||
|
||||
```js title=users.test.js
|
||||
import axios from 'axios'; // imports defined mock
|
||||
import { mock, fn } from '@wdio/browser-runner'
|
||||
|
||||
import Users from './users.js'
|
||||
|
||||
/**
|
||||
* mock default export of `axios` dependency
|
||||
*/
|
||||
mock('axios', () => ({
|
||||
default: {
|
||||
get: fn()
|
||||
}
|
||||
}))
|
||||
|
||||
describe('User API', () => {
|
||||
it('should fetch users', async () => {
|
||||
const users = [{name: 'Bob'}]
|
||||
const resp = {data: users}
|
||||
axios.get.mockResolvedValue(resp)
|
||||
|
||||
// or you could use the following depending on your use case:
|
||||
// axios.get.mockImplementation(() => Promise.resolve(resp))
|
||||
|
||||
const data = await Users.all()
|
||||
expect(data).toEqual(users)
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
## Partials
|
||||
|
||||
Subsets of a module can be mocked and the rest of the module can keep their actual implementation:
|
||||
|
||||
```js title=foo-bar-baz.js
|
||||
export const foo = 'foo';
|
||||
export const bar = () => 'bar';
|
||||
export default () => 'baz';
|
||||
```
|
||||
|
||||
The original module will be passed into the mock factory which you can use to e.g. partially mock a dependency:
|
||||
|
||||
```js
|
||||
import { mock, fn } from '@wdio/browser-runner'
|
||||
import defaultExport, { bar, foo } from './foo-bar-baz.js';
|
||||
|
||||
mock('./foo-bar-baz.js', async (originalModule) => {
|
||||
// Mock the default export and named export 'foo'
|
||||
// and propagate named export from the original module
|
||||
return {
|
||||
__esModule: true,
|
||||
...originalModule,
|
||||
default: fn(() => 'mocked baz'),
|
||||
foo: 'mocked foo',
|
||||
}
|
||||
})
|
||||
|
||||
describe('partial mock', () => {
|
||||
it('should do a partial mock', () => {
|
||||
const defaultExportResult = defaultExport();
|
||||
expect(defaultExportResult).toBe('mocked baz');
|
||||
expect(defaultExport).toHaveBeenCalled();
|
||||
|
||||
expect(foo).toBe('mocked foo');
|
||||
expect(bar()).toBe('bar');
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
## Manual Mocks
|
||||
|
||||
Manual mocks are defined by writing a module in a `__mocks__/` (see also `automockDir` option) subdirectory. If the module you are mocking is a Node module (e.g.: `lodash`), the mock should be placed in the `__mocks__` directory and will be automatically mocked. There's no need to explicitly call `mock('module_name')`.
|
||||
|
||||
Scoped modules (also known as scoped packages) can be mocked by creating a file in a directory structure that matches the name of the scoped module. For example, to mock a scoped module called `@scope/project-name`, create a file at `__mocks__/@scope/project-name.js`, creating the `@scope/` directory accordingly.
|
||||
|
||||
```
|
||||
.
|
||||
├── config
|
||||
├── __mocks__
|
||||
│ ├── axios.js
|
||||
│ ├── lodash.js
|
||||
│ └── @scope
|
||||
│ └── project-name.js
|
||||
├── node_modules
|
||||
└── views
|
||||
```
|
||||
|
||||
When a manual mock exists for a given module, WebdriverIO will use that module when explicitly calling `mock('moduleName')`. However, when automock is set to true, the manual mock implementation will be used instead of the automatically created mock, even if `mock('moduleName')` is not called. To opt out of this behavior you will need to explicitly call `unmock('moduleName')` in tests that should use the actual module implementation, e.g.:
|
||||
|
||||
```js
|
||||
import { unmock } from '@wdio/browser-runner'
|
||||
|
||||
unmock('lodash')
|
||||
```
|
||||
|
||||
## Hoisting
|
||||
|
||||
In order to get mocking to work in the browser, WebdriverIO rewrites the test files and hoists the mock calls above everything else (see also [this blog post](https://www.coolcomputerclub.com/posts/jest-hoist-await/) on the hoisting problem in Jest). This limits the way you can pass in variables into the mock resolver, e.g.:
|
||||
|
||||
```js title=component.test.js
|
||||
import dep from 'dependency'
|
||||
const variable = 'foobar'
|
||||
|
||||
/**
|
||||
* ❌ this fails as `dep` and `variable` are not defined inside the mock resolver
|
||||
*/
|
||||
mock('./some/module.ts', () => ({
|
||||
exportA: dep,
|
||||
exportB: variable
|
||||
}))
|
||||
```
|
||||
|
||||
To fix this you have to define all used variables inside the resolver, e.g.:
|
||||
|
||||
```js title=component.test.js
|
||||
/**
|
||||
* ✔️ this works as all variables are defined within the resolver
|
||||
*/
|
||||
mock('./some/module.ts', async () => {
|
||||
const dep = await import('dependency')
|
||||
const variable = 'foobar'
|
||||
|
||||
return {
|
||||
exportA: dep,
|
||||
exportB: variable
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
## Requests
|
||||
|
||||
If you are looking for mocking browser requests, e.g. API calls, head over to the [Request Mock and Spies](/docs/mocksandspies) section.
|
||||
|
|
@ -1,56 +0,0 @@
|
|||
---
|
||||
id: electron
|
||||
title: Electron
|
||||
---
|
||||
|
||||
Electron is a framework for building desktop applications using JavaScript, HTML, and CSS. By embedding Chromium and Node.js into its binary, Electron allows you to maintain one JavaScript codebase and create cross-platform apps that work on Windows, macOS, and Linux — no native development experience is required.
|
||||
|
||||
WebdriverIO provides an integrated service that simplifies the interaction with your Electron app and makes testing it very simple. The advantages of using WebdriverIO for testing Electron applications are:
|
||||
|
||||
- 🚗 auto-setup of required Chromedriver
|
||||
- 📦 automatic path detection of your Electron application - supports [Electron Forge](https://www.electronforge.io/) and [Electron Builder](https://www.electron.build/)
|
||||
- 🧩 access Electron APIs within your tests
|
||||
- 🕵️ mocking of Electron APIs via a Vitest-like API
|
||||
|
||||
You just need a few simple steps to get started. Watch this simple step-by-step getting started video tutorial from the [WebdriverIO YouTube](https://www.youtube.com/@webdriverio) channel:
|
||||
|
||||
<LiteYouTubeEmbed id="iQNxTdWedk0" title="Getting Started with ElectronJS Testing in WebdriverIO" />
|
||||
|
||||
Or follow the guide in the following section.
|
||||
|
||||
## Getting Started
|
||||
|
||||
To initiate a new WebdriverIO project, run:
|
||||
|
||||
```sh
|
||||
npm create wdio@latest ./
|
||||
```
|
||||
|
||||
An installation wizard will guide you through the process. Ensure you select _"Desktop Testing - of Electron Applications"_ when it asks you what type of testing you'd like to do. Afterwards provide the path to your compiled Electron application, e.g. `./dist`, then just keep the defaults or modify based on your preference.
|
||||
|
||||
The configuration wizard will install all required packages and create a `wdio.conf.js` or `wdio.conf.ts` with the necessary configuration to test your application. If you agree to autogenerate some test files you can run your first test via `npm run wdio`.
|
||||
|
||||
## Manual Setup
|
||||
|
||||
If you are already using WebdriverIO in your project you can skip the installation wizard and just add the following dependencies:
|
||||
|
||||
```sh
|
||||
npm install --save-dev wdio-electron-service
|
||||
```
|
||||
|
||||
Then you can use the following configuration:
|
||||
|
||||
```ts
|
||||
// wdio.conf.ts
|
||||
export const config: WebdriverIO.Config = {
|
||||
// ...
|
||||
services: [['electron', {
|
||||
appEntryPoint: './path/to/bundled/electron/main.bundle.js',
|
||||
appArgs: [/** ... */],
|
||||
}]]
|
||||
}
|
||||
```
|
||||
|
||||
That's it 🎉
|
||||
|
||||
Learn more about how [to configure the Electron Service](/docs/desktop-testing/electron/configuration), [how to mock Electron APIs](/docs/desktop-testing/electron/mocking) and [how to access Electron APIs](/docs/desktop-testing/electron/api).
|
||||
|
|
@ -1,44 +0,0 @@
|
|||
---
|
||||
id: macos
|
||||
title: MacOS
|
||||
---
|
||||
|
||||
WebdriverIO can automate arbitrary MacOS application using [Appium](https://appium.io/docs/en/2.0/). All you need is [XCode](https://developer.apple.com/xcode/) installed on your system, Appium and the [Mac2 Driver](https://github.com/appium/appium-mac2-driver) installed as dependency and the correct capabilities set.
|
||||
|
||||
## Getting Started
|
||||
|
||||
To initiate a new WebdriverIO project, run:
|
||||
|
||||
```sh
|
||||
npm create wdio@latest ./
|
||||
```
|
||||
|
||||
An installation wizard will guide you through the process. Ensure you select _"Desktop Testing - of MacOS Applications"_ when it asks you what type of testing you'ld like to do. Afterwards just keep the defaults or modify based on your preference.
|
||||
|
||||
The configuration wizard will install all required Appium packages and creates a `wdio.conf.js` or `wdio.conf.ts` with the necessary configuration to test on MacOS. If you agreed to autogenerate some tests files you can run your first test via `npm run wdio`.
|
||||
|
||||
<CreateMacOSProjectAnimation />
|
||||
|
||||
That's it 🎉
|
||||
|
||||
## Example
|
||||
|
||||
This is how a simple test can look like that opens the Calculator application, makes a calculation and verifies its result:
|
||||
|
||||
```js
|
||||
describe('My Login application', () => {
|
||||
it('should set a text to a text view', async function () {
|
||||
await $('//XCUIElementTypeButton[@label="seven"]').click()
|
||||
await $('//XCUIElementTypeButton[@label="multiply"]').click()
|
||||
await $('//XCUIElementTypeButton[@label="six"]').click()
|
||||
await $('//XCUIElementTypeButton[@title="="]').click()
|
||||
await expect($('//XCUIElementTypeStaticText[@label="main display"]')).toHaveText('42')
|
||||
});
|
||||
})
|
||||
```
|
||||
|
||||
__Note:__ the calculator app was opened automatically at the beginning of the session because `'appium:bundleId': 'com.apple.calculator'` was defined as capability option. You can switch apps during the session at all times.
|
||||
|
||||
## More Information
|
||||
|
||||
For information about specifics around testing on MacOS we recommend to go checkout the [Appium Mac2 Driver](https://github.com/appium/appium-mac2-driver) project.
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
---
|
||||
id: windows
|
||||
title: Windows
|
||||
---
|
||||
|
||||
Unfortunately there is at the moment no stable driver for automating Windows applications. Even though Appium maintains a [Windows Driver](https://github.com/appium/appium-windows-driver) that is based on Microsoft's [WinAppDriver server](https://github.com/microsoft/WinAppDriver), we would not recommend using it given that Microsoft has stopped maintaining the server and there are a lot of [known bugs](https://github.com/search?q=repo%3Amicrosoft%2FWinAppDriver+webdriverio&type=issues).
|
||||
|
||||
If anyone is aware of a way to automate Windows applications, please let us know on [Discord](https://discord.webdriver.io).
|
||||
|
|
@ -1,130 +0,0 @@
|
|||
---
|
||||
id: vscode-extensions
|
||||
title: VS Code Extension Testing
|
||||
---
|
||||
|
||||
WebdriverIO allows you to seamlessly test your [VS Code](https://code.visualstudio.com/) extensions from end to end in the VS Code Desktop IDE or as web extension. You only need to provide a path to your extension and the framework does the rest. With the [`wdio-vscode-service`](https://www.npmjs.com/package/wdio-vscode-service) everything is taken care of and much more:
|
||||
|
||||
- 🏗️ Installing VSCode (either stable, insiders or a specified version)
|
||||
- ⬇️ Downloading Chromedriver specific to given VSCode version
|
||||
- 🚀 Enables you to access the VSCode API from your tests
|
||||
- 🖥️ Starting VSCode with custom user settings (including support for VSCode on Ubuntu, MacOS and Windows)
|
||||
- 🌐 Or serves VSCode from a server to be accessed by any browser for testing web extensions
|
||||
- 📔 Bootstrapping page objects with locators matching your VSCode version
|
||||
|
||||
## Getting Started
|
||||
|
||||
To initiate a new WebdriverIO project, run:
|
||||
|
||||
```sh
|
||||
npm create wdio@latest ./
|
||||
```
|
||||
|
||||
An installation wizard will guide you through the process. Ensure you select _"VS Code Extension Testing"_ when it asks you what type of testing you'ld like to do, afterwards just keep the defaults or modify based on your preference.
|
||||
|
||||
## Example Configuration
|
||||
|
||||
To use the service you need to add `vscode` to your list of services, optionally followed by a configuration object. This will make WebdriverIO download given VSCode binaries and appropiate Chromedriver version:
|
||||
|
||||
```js
|
||||
// wdio.conf.ts
|
||||
export const config = {
|
||||
outputDir: 'trace',
|
||||
// ...
|
||||
capabilities: [{
|
||||
browserName: 'vscode',
|
||||
browserVersion: '1.71.0', // "insiders" or "stable" for latest VSCode version
|
||||
'wdio:vscodeOptions': {
|
||||
extensionPath: __dirname,
|
||||
userSettings: {
|
||||
"editor.fontSize": 14
|
||||
}
|
||||
}
|
||||
}],
|
||||
services: ['vscode'],
|
||||
/**
|
||||
* optionally you can define the path WebdriverIO stores all
|
||||
* VSCode and Chromedriver binaries, e.g.:
|
||||
* services: [['vscode', { cachePath: __dirname }]]
|
||||
*/
|
||||
// ...
|
||||
};
|
||||
```
|
||||
|
||||
If you define `wdio:vscodeOptions` with any other `browserName` but `vscode`, e.g. `chrome`, the service will serve the extension as web extension. If you test on Chrome no additional driver service is required, e.g.:
|
||||
|
||||
```js
|
||||
// wdio.conf.ts
|
||||
export const config = {
|
||||
outputDir: 'trace',
|
||||
// ...
|
||||
capabilities: [{
|
||||
browserName: 'chrome',
|
||||
'wdio:vscodeOptions': {
|
||||
extensionPath: __dirname
|
||||
}
|
||||
}],
|
||||
services: ['vscode'],
|
||||
// ...
|
||||
};
|
||||
```
|
||||
|
||||
_Note:_ when testing web extensions you can only choose between `stable` or `insiders` as `browserVersion`.
|
||||
|
||||
### TypeScript Setup
|
||||
|
||||
In your `tsconfig.json` make sure to add `wdio-vscode-service` to your list of types:
|
||||
|
||||
```json
|
||||
{
|
||||
"compilerOptions": {
|
||||
"types": [
|
||||
"node",
|
||||
"webdriverio/async",
|
||||
"@wdio/mocha-framework",
|
||||
"expect-webdriverio",
|
||||
"wdio-vscode-service"
|
||||
],
|
||||
"target": "es2020",
|
||||
"moduleResolution": "node16"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
You can then use the `getWorkbench` method to access the page objects for the locators matching your desired VSCode version:
|
||||
|
||||
```ts
|
||||
describe('WDIO VSCode Service', () => {
|
||||
it('should be able to load VSCode', async () => {
|
||||
const workbench = await browser.getWorkbench()
|
||||
expect(await workbench.getTitleBar().getTitle())
|
||||
.toBe('[Extension Development Host] - README.md - wdio-vscode-service - Visual Studio Code')
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
From there you can access all page objects by using the right page object methods. Find out more about all available page objects and their methods in the [page object docs](https://webdriverio-community.github.io/wdio-vscode-service/).
|
||||
|
||||
### Accessing VSCode APIs
|
||||
|
||||
If you like to execute certain automation through the [VSCode API](https://code.visualstudio.com/api/references/vscode-api) you can do that by running remote commands via the custom `executeWorkbench` command. This command allows to remote execute code from your test inside the VSCode environment and enables to access the VSCode API. You can pass arbitrary paramaters into the function which will then be propagated into the function. The `vscode` object will be always passed in as first argument following the outer function parameters. Note that you can not access variables outside of the function scoped as the callback is executed remotely. Here is an example:
|
||||
|
||||
```ts
|
||||
const workbench = await browser.getWorkbench()
|
||||
await browser.executeWorkbench((vscode, param1, param2) => {
|
||||
vscode.window.showInformationMessage(`I am an ${param1} ${param2}!`)
|
||||
}, 'API', 'call')
|
||||
|
||||
const notifs = await workbench.getNotifications()
|
||||
console.log(await notifs[0].getMessage()) // outputs: "I am an API call!"
|
||||
```
|
||||
|
||||
For the full page object documentation, check out the [docs](https://webdriverio-community.github.io/wdio-vscode-service/modules.html). You can find various usage examples in this [project's test suite](https://github.com/webdriverio-community/wdio-vscode-service/blob/main/test/specs).
|
||||
|
||||
## More Information
|
||||
|
||||
You can learn more about how to configure the [`wdio-vscode-service`](https://www.npmjs.com/package/wdio-vscode-service) and how create custom page objects in the [service docs](/docs/wdio-vscode-service). You can also watch the following talk by [Christian Bromann](https://twitter.com/bromann) on [_Testing Complex VSCode Extensions With the Power of Web Standards_](https://www.youtube.com/watch?v=PhGNTioBUiU):
|
||||
|
||||
<LiteYouTubeEmbed id="PhGNTioBUiU" title="Testing Complex VSCode Extensions With the Power of Web Standards" />
|
||||
|
|
@ -1,156 +0,0 @@
|
|||
---
|
||||
id: web-extensions
|
||||
title: Web Extension Testing
|
||||
---
|
||||
|
||||
WebdriverIO is the ideal tool to automate a browser. Web Extensions are a part of the browser and can be automated in the same way. Whenever your web extension uses content scripts to run JavaScript on websites or offer a popup modal, you can run an e2e test for that using WebdriverIO.
|
||||
|
||||
## Loading a Web Extension into the Browser
|
||||
|
||||
As a first step we have to load the extension under test into the browser as part of our session. This works differently for Chrome and Firefox.
|
||||
|
||||
:::info
|
||||
|
||||
These docs leave out Safari web extensions as their support for it is way behind and user demand not high. If you are building a web extension for Safari, please [raise an issue](https://github.com/webdriverio/webdriverio/issues/new?assignees=&labels=Docs+%F0%9F%93%96%2CNeeds+Triaging+%E2%8F%B3&template=documentation.yml&title=%5B%F0%9F%93%96+Docs%5D%3A+%3Ctitle%3E) and collaborate on including it here as well.
|
||||
|
||||
:::
|
||||
|
||||
### Chrome
|
||||
|
||||
Loading a web extension in Chrome can be done through providing a `base64` encoded string of the `crx` file or by providing a path to the web extension folder. The easiest is just to do the latter by defining your Chrome capabilities as following:
|
||||
|
||||
```js wdio.conf.js
|
||||
import path from 'node:path'
|
||||
import url from 'node:url'
|
||||
|
||||
const __dirname = url.fileURLToPath(new URL('.', import.meta.url))
|
||||
|
||||
export const config = {
|
||||
// ...
|
||||
capabilities: [{
|
||||
browserName,
|
||||
'goog:chromeOptions': {
|
||||
// given your wdio.conf.js is in the root directory and your compiled
|
||||
// web extension files are located in the `./dist` folder
|
||||
args: [`--load-extension=${path.join(__dirname, '..', '..', 'dist')}`]
|
||||
}
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
:::info
|
||||
|
||||
If you automate a different browser than Chrome, e.g. Brave, Edge or Opera, chances are that the browser option match with the example above, just using a different capability name, e.g. `ms:edgeOptions`.
|
||||
|
||||
:::
|
||||
|
||||
If you compile your extension as `.crx` file using e.g. the [crx](https://www.npmjs.com/package/crx) NPM package, you can also inject the bundled extension via:
|
||||
|
||||
```js wdio.conf.js
|
||||
import path from 'node:path'
|
||||
import url from 'node:url'
|
||||
|
||||
const __dirname = url.fileURLToPath(new URL('.', import.meta.url))
|
||||
const extPath = path.join(__dirname, `web-extension-chrome.crx`)
|
||||
const chromeExtension = (await fs.readFile(extPath)).toString('base64')
|
||||
|
||||
export const config = {
|
||||
// ...
|
||||
capabilities: [{
|
||||
browserName,
|
||||
'goog:chromeOptions': {
|
||||
extensions: [chromeExtension]
|
||||
}
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
### Firefox
|
||||
|
||||
To create a Firefox profile that includes extensions you can use the [Firefox Profile Service](/docs/firefox-profile-service) to set up your session accordingly. However you might run into issues where your local developed extension can't be loaded due to signing issues. In this case you can also load an extension in the `before` hook via the [`installAddOn`](/docs/api/gecko#installaddon) command, e.g.:
|
||||
|
||||
```js wdio.conf.js
|
||||
import path from 'node:path'
|
||||
import url from 'node:url'
|
||||
|
||||
const __dirname = url.fileURLToPath(new URL('.', import.meta.url))
|
||||
const extensionPath = path.resolve(__dirname, `web-extension.xpi`)
|
||||
|
||||
export const config = {
|
||||
// ...
|
||||
before: async (capabilities) => {
|
||||
const browserName = (capabilities as WebdriverIO.Capabilities).browserName
|
||||
if (browserName === 'firefox') {
|
||||
const extension = await fs.readFile(extensionPath)
|
||||
await browser.installAddOn(extension.toString('base64'), true)
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
In order to generate an `.xpi` file, it is recommended to use the [`web-ext`](https://www.npmjs.com/package/web-ext) NPM package. You can bundle your extension using the following example command:
|
||||
|
||||
```sh
|
||||
npx web-ext build -s dist/ -a . -n web-extension-firefox.xpi
|
||||
```
|
||||
|
||||
## Tips & Tricks
|
||||
|
||||
The following section contains a set useful tips and tricks that can be helpful when testing a web extension.
|
||||
|
||||
### Test Popup Modal in Chrome
|
||||
|
||||
If you define a `default_popup` browser action entry in your [extension manifest](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/browser_action) you can test that HTML page directly, since clicking on the extension icon in the browser top bar won't work. Instead, you have to open the popup html file directly.
|
||||
|
||||
In Chrome this works by retrieving the extension ID and opening the popup page through `browser.url('...')`. The behavior on that page will be the same as within the popup. To do so we recommend to write the following custom command:
|
||||
|
||||
```ts customCommand.ts
|
||||
export async function openExtensionPopup (this: WebdriverIO.Browser, extensionName: string, popupUrl = 'index.html') {
|
||||
if ((this.capabilities as WebdriverIO.Capabilities).browserName !== 'chrome') {
|
||||
throw new Error('This command only works with Chrome')
|
||||
}
|
||||
await this.url('chrome://extensions/')
|
||||
|
||||
const extensions = await this.$$('extensions-item')
|
||||
const extension = await extensions.find(async (ext) => (
|
||||
await ext.$('#name').getText()) === extensionName
|
||||
)
|
||||
|
||||
if (!extension) {
|
||||
const installedExtensions = await extensions.map((ext) => ext.$('#name').getText())
|
||||
throw new Error(`Couldn't find extension "${extensionName}", available installed extensions are "${installedExtensions.join('", "')}"`)
|
||||
}
|
||||
|
||||
const extId = await extension.getAttribute('id')
|
||||
await this.url(`chrome-extension://${extId}/popup/${popupUrl}`)
|
||||
}
|
||||
|
||||
declare global {
|
||||
namespace WebdriverIO {
|
||||
interface Browser {
|
||||
openExtensionPopup: typeof openExtensionPopup
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
In your `wdio.conf.js` you can import this file and register the custom command in your `before` hook, e.g.:
|
||||
|
||||
```ts wdio.conf.ts
|
||||
import { browser } from '@wdio/globals'
|
||||
|
||||
import { openExtensionPopup } from './support/customCommands'
|
||||
|
||||
export const config: WebdriverIO.Config = {
|
||||
// ...
|
||||
before: () => {
|
||||
browser.addCommand('openExtensionPopup', openExtensionPopup)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Now, in your test, you can access the popup page via:
|
||||
|
||||
```ts
|
||||
await browser.openExtensionPopup('My Web Extension')
|
||||
```
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
---
|
||||
id: createlocalworkerprocess
|
||||
title: Create worker process
|
||||
---
|
||||
|
||||
This flowchart explains how a worker process is created.
|
||||
|
||||
<CreateFlowcharts id='createlocalworkerprocess' />
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue