Skip to content

Commit

Permalink
make providing an expected result optional
Browse files Browse the repository at this point in the history
  • Loading branch information
christian-bromann committed Jan 11, 2024
1 parent d362a69 commit 79746f0
Show file tree
Hide file tree
Showing 3 changed files with 103 additions and 42 deletions.
24 changes: 20 additions & 4 deletions packages/service/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,47 +94,63 @@ declare global {
/**
* cks that if current screen matches with snapshot of baseline.
* @param tag snapshot name
* @param expectedResult either a number representing a mismatch percentage or an asymmetric matcher
* @param expectedResult either a number representing a mismatch percentage (defaults to 0) or an asymmetric matcher
* @param options options to pass into the `checkScreen` method
*/
toMatchScreenSnapshot(
tag: string,
expectedResult?: number | ExpectWebdriverIO.PartialMatcher,
options?: WdioCheckScreenMethodOptions
): R
toMatchScreenSnapshot(
tag: string,
options?: WdioCheckScreenMethodOptions
): R
/**
* Checks that if the full page screenshot matches with snapshot of baseline.
* @param tag snapshot name
* @param expectedResult either a number representing a mismatch percentage or an asymmetric matcher
* @param expectedResult either a number representing a mismatch percentage (defaults to 0) or an asymmetric matcher
* @param options options to pass into the `checkFullPageScreen` method
*/
toMatchFullPageSnapshot(
tag: string,
expectedResult?: number | ExpectWebdriverIO.PartialMatcher,
options?: WdioCheckFullPageMethodOptions
): R
toMatchFullPageSnapshot(
tag: string,
options?: WdioCheckFullPageMethodOptions
): R
/**
* Checks that if given element matches with snapshot of baseline.
* @param tag snapshot name
* @param expectedResult either a number representing a mismatch percentage or an asymmetric matcher
* @param expectedResult either a number representing a mismatch percentage (defaults to 0) or an asymmetric matcher
* @param options options to pass into the `checkElement` method
*/
toMatchElementSnapshot(
tag: string,
expectedResult?: number | ExpectWebdriverIO.PartialMatcher,
options?: WdioCheckElementMethodOptions
): R
toMatchElementSnapshot(
tag: string,
options?: WdioCheckElementMethodOptions
): R
/**
* Checks that if the full page screenshot including tab marks matches with snapshot of baseline.
* @param tag snapshot name
* @param expectedResult either a number representing a mismatch percentage or an asymmetric matcher
* @param expectedResult either a number representing a mismatch percentage (defaults to 0) or an asymmetric matcher
* @param options options to pass into the `checkTabbablePage` method
*/
toMatchTabbablePageSnapshot(
tag: string,
expectedResult?: number | ExpectWebdriverIO.PartialMatcher,
options?: WdioCheckFullPageMethodOptions
): R
toMatchTabbablePageSnapshot(
tag: string,
options?: WdioCheckFullPageMethodOptions
): R
}
}
}
Expand Down
91 changes: 72 additions & 19 deletions packages/service/src/matcher.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { getBrowserObject } from './utils.js'
import type { ImageCompareResult } from 'webdriver-image-comparison'

import { getBrowserObject } from './utils.js'
import type {
Result,
WdioCheckFullPageMethodOptions,
WdioCheckElementMethodOptions,
WdioCheckScreenMethodOptions
Expand All @@ -14,16 +14,20 @@ const asymmetricMatcher =
? Symbol.for('jest.asymmetricMatcher')
: 0x13_57_a5

function compareResult (actual: Result, expected: number | ExpectWebdriverIO.PartialMatcher) {
function isAsymmetricMatcher (expected: unknown): expected is ExpectWebdriverIO.PartialMatcher {
return Boolean(expected && typeof expected === 'object' && '$$typeof' in expected && expected.$$typeof === asymmetricMatcher && 'asymmetricMatch' in expected)
}

function compareResult (result: ImageCompareResult, expected: number | ExpectWebdriverIO.PartialMatcher) {
/**
* expected value is an asymmetric matcher, e.g.
*
* ```ts
* expect(browser).toMatchScreenSnapshot('foo', expect.closeTo(0, 2))
* ```
*/
if (typeof expected === 'object' && '$$typeof' in expected && expected.$$typeof === asymmetricMatcher && expected.asymmetricMatch) {
const pass = expected.asymmetricMatch(actual)
if (isAsymmetricMatcher(expected)) {
const pass = expected.asymmetricMatch(result.misMatchPercentage)
return {
pass,
message: () => expected.sample
Expand All @@ -39,51 +43,100 @@ function compareResult (actual: Result, expected: number | ExpectWebdriverIO.Par
*/
if (typeof expected === 'number') {
return {
pass: actual === expected,
message: () => `Expected image to have a mismatch percentage of ${expected}%, but was ${actual}%`
pass: result.misMatchPercentage === expected,
message: () => (
`Expected image to have a mismatch percentage of ${expected}%, but was ${result.misMatchPercentage}%\n` +
'Please compare the images manually and update the baseline if the new screenshot is correct.\n' +
`\nBaseline: ${result.folders.baseline}\n` +
`Actual Screenshot: ${result.folders.actual}\n` +
`Difference: ${result.folders.diff}\n` +
'\nSee https://webdriver.io/docs/api/visual-regression.html for more information.'
)
}
}

throw new Error(`Invalid matcher, expect either a number or an asymmetric matcher, but found ${expected}`)
}

function parseMatcherParams (
tag: string,
expectedResult?: number | ExpectWebdriverIO.PartialMatcher,
options?: WdioCheckFullPageMethodOptions
) {
/**
* throw if `tag` is not a string
*/
if (typeof tag !== 'string') {
throw new Error(`Expected a snapshot tag as a string but received "${typeof tag}"`)
}

/**
* if `expectedResult` is an object, it is an options object
* ```ts
* expect(browser).toMatchScreenSnapshot('foo', { hideAfterFirstScroll: [element] })
* ```
*/
if (typeof expectedResult === 'object' && !isAsymmetricMatcher(expectedResult)) {
options = expectedResult
expectedResult = DEFAULT_EXPECTED_RESULT
}

/**
* make sure `options` is an object
*/
if (typeof options !== 'object') {
options = {}
}

/**
* overwrite `returnAllCompareData` to allow us to provide a better assertion message
*/
options.returnAllCompareData = true

return { expectedResult, options }
}

export async function toMatchScreenSnapshot (
browser: WebdriverIO.Browser,
tag: string,
expectedResult?: number | ExpectWebdriverIO.PartialMatcher,
options?: WdioCheckScreenMethodOptions
expectedResultOrOptions?: number | ExpectWebdriverIO.PartialMatcher,
optionsOrUndefined?: WdioCheckScreenMethodOptions
) {
const result = await browser.checkScreen(tag, options)
const { expectedResult, options } = parseMatcherParams(tag, expectedResultOrOptions, optionsOrUndefined)
const result = await browser.checkScreen(tag, options) as ImageCompareResult
return compareResult(result, expectedResult || DEFAULT_EXPECTED_RESULT)
}

export async function toMatchFullPageSnapshot (
browser: WebdriverIO.Browser,
tag: string,
expectedResult?: number | ExpectWebdriverIO.PartialMatcher,
options?: WdioCheckFullPageMethodOptions
expectedResultOrOptions?: number | ExpectWebdriverIO.PartialMatcher,
optionsOrUndefined?: WdioCheckFullPageMethodOptions
) {
const result = await browser.checkFullPageScreen(tag, options)
const { expectedResult, options } = parseMatcherParams(tag, expectedResultOrOptions, optionsOrUndefined)
const result = await browser.checkFullPageScreen(tag, options) as ImageCompareResult
return compareResult(result, expectedResult || DEFAULT_EXPECTED_RESULT)
}

export async function toMatchElementSnapshot (
element: WebdriverIO.Element,
tag: string,
expectedResult?: number | ExpectWebdriverIO.PartialMatcher,
options?: WdioCheckElementMethodOptions
expectedResultOrOptions?: number | ExpectWebdriverIO.PartialMatcher,
optionsOrUndefined?: WdioCheckElementMethodOptions
) {
const { expectedResult, options } = parseMatcherParams(tag, expectedResultOrOptions, optionsOrUndefined)
const browser = getBrowserObject(await element)
const result = await browser.checkElement(await element, tag, options)
const result = await browser.checkElement(await element, tag, options) as ImageCompareResult
return compareResult(result, expectedResult || DEFAULT_EXPECTED_RESULT)
}

export async function toMatchTabbablePageSnapshot (
browser: WebdriverIO.Browser,
tag: string,
expectedResult?: number | ExpectWebdriverIO.PartialMatcher,
options?: WdioCheckFullPageMethodOptions
expectedResultOrOptions?: number | ExpectWebdriverIO.PartialMatcher,
optionsOrUndefined?: WdioCheckFullPageMethodOptions
) {
const result = await browser.checkTabbablePage(tag, options)
const { expectedResult, options } = parseMatcherParams(tag, expectedResultOrOptions, optionsOrUndefined)
const result = await browser.checkTabbablePage(tag, options) as ImageCompareResult
return compareResult(result, expectedResult || DEFAULT_EXPECTED_RESULT)
}
30 changes: 11 additions & 19 deletions tests/specs/desktop.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,33 +14,25 @@ describe('@wdio/visual-service desktop', () => {
afterEach(async () => await browser.execute('window.scrollTo(0, 0);', []))

it(`should compare an element successful with a baseline for '${browserName}'`, async () => {
await expect(
await browser.checkElement(
await $('.hero__title-logo'),
'wdioLogo',
{
removeElements: [await $('nav.navbar')]
}
)
).toEqual(0)
await expect($('.hero__title-logo')).toMatchElementSnapshot('wdioLogo', {
removeElements: [await $('nav.navbar')]
})
})

it(`should compare a full page screenshot successful with a baseline for '${browserName}'`, async () => {
await expect(
await browser.checkFullPageScreen('fullPage', {
fullPageScrollTimeout: 1500,
hideAfterFirstScroll: [
await $('nav.navbar'),
],
})
).toEqual(0)
await expect(browser).toMatchFullPageSnapshot('fullPage', {
fullPageScrollTimeout: 1500,
hideAfterFirstScroll: [
await $('nav.navbar'),
],
})
})

it(`should compare a tabbable screenshot successful with a baseline for '${browserName}'`, async () => {
await expect(await browser.checkTabbablePage('tabbable', {
await expect(browser).toMatchTabbablePageSnapshot('tabbable', {
hideAfterFirstScroll: [
await $('nav.navbar'),
],
})).toEqual(0)
})
})
})

0 comments on commit 79746f0

Please sign in to comment.