diff --git a/src/matchers/browser/toHaveClipboardText.ts b/src/matchers/browser/toHaveClipboardText.ts index 00b023408..bcf863e7d 100644 --- a/src/matchers/browser/toHaveClipboardText.ts +++ b/src/matchers/browser/toHaveClipboardText.ts @@ -10,7 +10,6 @@ export async function toHaveClipboardText( expectedValue: string | RegExp | WdioAsymmetricMatcher, options: ExpectWebdriverIO.StringOptions = DEFAULT_OPTIONS ) { - const isNot = this.isNot const { expectation = 'clipboard text', verb = 'have' } = this await options.beforeAssertion?.({ @@ -28,7 +27,7 @@ export async function toHaveClipboardText( .catch((err) => log.warn(`Couldn't set clipboard permissions: ${err}`)) actual = await browser.execute(() => window.navigator.clipboard.readText()) return compareText(actual, expectedValue, options).result - }, isNot, options) + }, options) const message = enhanceError('browser', expectedValue, actual, this, verb, expectation, '', options) const result: ExpectWebdriverIO.AssertionResult = { diff --git a/src/matchers/browser/toHaveTitle.ts b/src/matchers/browser/toHaveTitle.ts index 4c18dd7f8..236c55d4d 100644 --- a/src/matchers/browser/toHaveTitle.ts +++ b/src/matchers/browser/toHaveTitle.ts @@ -6,7 +6,6 @@ export async function toHaveTitle( expectedValue: string | RegExp | WdioAsymmetricMatcher, options: ExpectWebdriverIO.StringOptions = DEFAULT_OPTIONS ) { - const isNot = this.isNot const { expectation = 'title', verb = 'have' } = this await options.beforeAssertion?.({ @@ -20,7 +19,7 @@ export async function toHaveTitle( actual = await browser.getTitle() return compareText(actual, expectedValue, options).result - }, isNot, options) + }, options) const message = enhanceError('window', expectedValue, actual, this, verb, expectation, '', options) const result: ExpectWebdriverIO.AssertionResult = { diff --git a/src/matchers/browser/toHaveUrl.ts b/src/matchers/browser/toHaveUrl.ts index 06719ac5d..3dcc50195 100644 --- a/src/matchers/browser/toHaveUrl.ts +++ b/src/matchers/browser/toHaveUrl.ts @@ -6,7 +6,6 @@ export async function toHaveUrl( expectedValue: string | RegExp | WdioAsymmetricMatcher, options: ExpectWebdriverIO.StringOptions = DEFAULT_OPTIONS ) { - const isNot = this.isNot const { expectation = 'url', verb = 'have' } = this await options.beforeAssertion?.({ @@ -20,7 +19,7 @@ export async function toHaveUrl( actual = await browser.getUrl() return compareText(actual, expectedValue, options).result - }, isNot, options) + }, options) const message = enhanceError('window', expectedValue, actual, this, verb, expectation, '', options) const result: ExpectWebdriverIO.AssertionResult = { diff --git a/src/matchers/element/toHaveAttribute.ts b/src/matchers/element/toHaveAttribute.ts index 9d42293e7..c6ca5ac9d 100644 --- a/src/matchers/element/toHaveAttribute.ts +++ b/src/matchers/element/toHaveAttribute.ts @@ -27,7 +27,6 @@ async function conditionAttrAndValue(el: WebdriverIO.Element, attribute: string, } export async function toHaveAttributeAndValue(received: WdioElementMaybePromise, attribute: string, value: string | RegExp | WdioAsymmetricMatcher, options: ExpectWebdriverIO.StringOptions = DEFAULT_OPTIONS) { - const isNot = this.isNot const { expectation = 'attribute', verb = 'have' } = this let el = await received?.getElement() @@ -38,7 +37,7 @@ export async function toHaveAttributeAndValue(received: WdioElementMaybePromise, attr = result.values return result.success - }, isNot, options) + }, options) const expected = wrapExpectedWithArray(el, attr, value) const message = enhanceError(el, expected, attr, this, verb, expectation, attribute, options) @@ -49,9 +48,8 @@ export async function toHaveAttributeAndValue(received: WdioElementMaybePromise, } as ExpectWebdriverIO.AssertionResult } -async function toHaveAttributeFn(received: WdioElementMaybePromise, attribute: string) { - const isNot = this.isNot - const { expectation = 'attribute', verb = 'have' } = this +async function toHaveAttributeFn(received: WdioElementMaybePromise, attribute: string, options: ExpectWebdriverIO.StringOptions) { + const { expectation = 'attribute', verb = 'have', isNot } = this let el = await received?.getElement() @@ -60,9 +58,9 @@ async function toHaveAttributeFn(received: WdioElementMaybePromise, attribute: s el = result.el as WebdriverIO.Element return result.success - }, isNot, {}) + }, options) - const message = enhanceError(el, !isNot, pass, this, verb, expectation, attribute, {}) + const message = enhanceError(el, !isNot, pass, this, verb, expectation, attribute, options) return { pass, @@ -86,7 +84,7 @@ export async function toHaveAttribute( // Name and value is passed in e.g. el.toHaveAttribute('attr', 'value', (opts)) ? await toHaveAttributeAndValue.call(this, received, attribute, value, options) // Only name is passed in e.g. el.toHaveAttribute('attr') - : await toHaveAttributeFn.call(this, received, attribute) + : await toHaveAttributeFn.call(this, received, attribute, options) await options.afterAssertion?.({ matcherName: 'toHaveAttribute', diff --git a/src/matchers/element/toHaveChildren.ts b/src/matchers/element/toHaveChildren.ts index f9b035965..712070e55 100644 --- a/src/matchers/element/toHaveChildren.ts +++ b/src/matchers/element/toHaveChildren.ts @@ -35,7 +35,6 @@ export async function toHaveChildren( expectedValue?: number | ExpectWebdriverIO.NumberOptions, options: ExpectWebdriverIO.StringOptions = DEFAULT_OPTIONS ) { - const isNot = this.isNot const { expectation = 'children', verb = 'have' } = this await options.beforeAssertion?.({ @@ -56,7 +55,7 @@ export async function toHaveChildren( children = result.values return result.success - }, isNot, { ...numberOptions, ...options }) + }, { ...numberOptions, ...options }) const error = numberError(numberOptions) const expectedArray = wrapExpectedWithArray(el, children, error) diff --git a/src/matchers/element/toHaveClass.ts b/src/matchers/element/toHaveClass.ts index 864d4ad04..0c7d53b9d 100644 --- a/src/matchers/element/toHaveClass.ts +++ b/src/matchers/element/toHaveClass.ts @@ -42,7 +42,6 @@ export async function toHaveElementClass( expectedValue: string | RegExp | Array | WdioAsymmetricMatcher, options: ExpectWebdriverIO.StringOptions = DEFAULT_OPTIONS ) { - const isNot = this.isNot const { expectation = 'class', verb = 'have' } = this await options.beforeAssertion?.({ @@ -62,7 +61,7 @@ export async function toHaveElementClass( attr = result.values return result.success - }, isNot, options) + }, options) const message = enhanceError(el, wrapExpectedWithArray(el, attr, expectedValue), attr, this, verb, expectation, '', options) const result: ExpectWebdriverIO.AssertionResult = { diff --git a/src/matchers/element/toHaveComputedLabel.ts b/src/matchers/element/toHaveComputedLabel.ts index 50e2a9324..f5729919c 100644 --- a/src/matchers/element/toHaveComputedLabel.ts +++ b/src/matchers/element/toHaveComputedLabel.ts @@ -26,7 +26,6 @@ export async function toHaveComputedLabel( expectedValue: string | RegExp | WdioAsymmetricMatcher | Array, options: ExpectWebdriverIO.StringOptions = DEFAULT_OPTIONS ) { - const isNot = this.isNot const { expectation = 'computed label', verb = 'have' } = this await options.beforeAssertion?.({ @@ -46,7 +45,6 @@ export async function toHaveComputedLabel( return result.success }, - isNot, options ) diff --git a/src/matchers/element/toHaveComputedRole.ts b/src/matchers/element/toHaveComputedRole.ts index 916506b97..72209acb4 100644 --- a/src/matchers/element/toHaveComputedRole.ts +++ b/src/matchers/element/toHaveComputedRole.ts @@ -26,7 +26,6 @@ export async function toHaveComputedRole( expectedValue: string | RegExp | WdioAsymmetricMatcher | Array, options: ExpectWebdriverIO.StringOptions = DEFAULT_OPTIONS ) { - const isNot = this.isNot const { expectation = 'computed role', verb = 'have' } = this await options.beforeAssertion?.({ @@ -46,7 +45,6 @@ export async function toHaveComputedRole( return result.success }, - isNot, options ) diff --git a/src/matchers/element/toHaveElementProperty.ts b/src/matchers/element/toHaveElementProperty.ts index cdf4146b0..68456901a 100644 --- a/src/matchers/element/toHaveElementProperty.ts +++ b/src/matchers/element/toHaveElementProperty.ts @@ -17,10 +17,13 @@ async function condition( const { asString = false } = options let prop = await el.getProperty(property) + + // As specified in the w3c spec, cases where property does not exist if (prop === null || prop === undefined) { return { result: false, value: prop } } + // As specified in the w3c spec, cases where property simply exists, missing undefined here? if (value === null) { return { result: true, value: prop } } @@ -36,11 +39,10 @@ async function condition( export async function toHaveElementProperty( received: WdioElementMaybePromise, property: string, - value?: string | RegExp | WdioAsymmetricMatcher, + value?: string | RegExp | WdioAsymmetricMatcher | null, options: ExpectWebdriverIO.StringOptions = DEFAULT_OPTIONS ) { - const isNot = this.isNot - const { expectation = 'property', verb = 'have' } = this + const { expectation = 'property', verb = 'have', isNot } = this await options.beforeAssertion?.({ matcherName: 'toHaveElementProperty', @@ -58,7 +60,6 @@ export async function toHaveElementProperty( return result.success }, - isNot, options ) diff --git a/src/matchers/element/toHaveHTML.ts b/src/matchers/element/toHaveHTML.ts index 1f7b976e2..d8ceafb52 100644 --- a/src/matchers/element/toHaveHTML.ts +++ b/src/matchers/element/toHaveHTML.ts @@ -22,7 +22,6 @@ export async function toHaveHTML( expectedValue: string | RegExp | WdioAsymmetricMatcher | Array, options: ExpectWebdriverIO.HTMLOptions = DEFAULT_OPTIONS ) { - const isNot = this.isNot const { expectation = 'HTML', verb = 'have' } = this await options.beforeAssertion?.({ @@ -44,7 +43,7 @@ export async function toHaveHTML( actualHTML = result.values return result.success - }, isNot, options) + }, options) const message = enhanceError(el, wrapExpectedWithArray(el, actualHTML, expectedValue), actualHTML, this, verb, expectation, '', options) diff --git a/src/matchers/element/toHaveHeight.ts b/src/matchers/element/toHaveHeight.ts index 0905d2183..5666b3338 100644 --- a/src/matchers/element/toHaveHeight.ts +++ b/src/matchers/element/toHaveHeight.ts @@ -22,7 +22,6 @@ export async function toHaveHeight( expectedValue: number | ExpectWebdriverIO.NumberOptions, options: ExpectWebdriverIO.CommandOptions = DEFAULT_OPTIONS ) { - const isNot = this.isNot const { expectation = 'height', verb = 'have' } = this await options.beforeAssertion?.({ @@ -53,7 +52,6 @@ export async function toHaveHeight( return result.success }, - isNot, { ...numberOptions, ...options } ) diff --git a/src/matchers/element/toHaveSize.ts b/src/matchers/element/toHaveSize.ts index a2d2cc80a..0976e7cc7 100644 --- a/src/matchers/element/toHaveSize.ts +++ b/src/matchers/element/toHaveSize.ts @@ -19,7 +19,6 @@ export async function toHaveSize( expectedValue: { height: number; width: number }, options: ExpectWebdriverIO.CommandOptions = DEFAULT_OPTIONS ) { - const isNot = this.isNot const { expectation = 'size', verb = 'have' } = this await options.beforeAssertion?.({ @@ -40,7 +39,6 @@ export async function toHaveSize( return result.success }, - isNot, options ) diff --git a/src/matchers/element/toHaveStyle.ts b/src/matchers/element/toHaveStyle.ts index 5d61baf41..d13c032dc 100644 --- a/src/matchers/element/toHaveStyle.ts +++ b/src/matchers/element/toHaveStyle.ts @@ -17,7 +17,6 @@ export async function toHaveStyle( expectedValue: { [key: string]: string; }, options: ExpectWebdriverIO.StringOptions = DEFAULT_OPTIONS ) { - const isNot = this.isNot const { expectation = 'style', verb = 'have' } = this await options.beforeAssertion?.({ @@ -35,7 +34,7 @@ export async function toHaveStyle( actualStyle = result.values return result.success - }, isNot, options) + }, options) const message = enhanceError(el, wrapExpectedWithArray(el, actualStyle, expectedValue), actualStyle, this, verb, expectation, '', options) diff --git a/src/matchers/element/toHaveText.ts b/src/matchers/element/toHaveText.ts index 82d4450c7..edcab174e 100644 --- a/src/matchers/element/toHaveText.ts +++ b/src/matchers/element/toHaveText.ts @@ -42,7 +42,6 @@ export async function toHaveText( expectedValue: string | RegExp | WdioAsymmetricMatcher | Array, options: ExpectWebdriverIO.StringOptions = DEFAULT_OPTIONS ) { - const isNot = this.isNot const { expectation = 'text', verb = 'have' } = this await options.beforeAssertion?.({ @@ -64,7 +63,7 @@ export async function toHaveText( actualText = result.values return result.success - }, isNot, options) + }, options) const message = enhanceError(el, wrapExpectedWithArray(el, actualText, expectedValue), actualText, this, verb, expectation, '', options) const result: ExpectWebdriverIO.AssertionResult = { diff --git a/src/matchers/element/toHaveWidth.ts b/src/matchers/element/toHaveWidth.ts index 6f706ffcb..42eb8c0ad 100644 --- a/src/matchers/element/toHaveWidth.ts +++ b/src/matchers/element/toHaveWidth.ts @@ -22,7 +22,6 @@ export async function toHaveWidth( expectedValue: number | ExpectWebdriverIO.NumberOptions, options: ExpectWebdriverIO.CommandOptions = DEFAULT_OPTIONS ) { - const isNot = this.isNot const { expectation = 'width', verb = 'have' } = this await options.beforeAssertion?.({ @@ -53,7 +52,6 @@ export async function toHaveWidth( return result.success }, - isNot, { ...numberOptions, ...options } ) diff --git a/src/matchers/elements/toBeElementsArrayOfSize.ts b/src/matchers/elements/toBeElementsArrayOfSize.ts index 39cd07ccd..9e3621a49 100644 --- a/src/matchers/elements/toBeElementsArrayOfSize.ts +++ b/src/matchers/elements/toBeElementsArrayOfSize.ts @@ -8,7 +8,6 @@ export async function toBeElementsArrayOfSize( expectedValue: number | ExpectWebdriverIO.NumberOptions, options: ExpectWebdriverIO.StringOptions = DEFAULT_OPTIONS ) { - const isNot = this.isNot const { expectation = 'elements array of size', verb = 'be' } = this await options.beforeAssertion?.({ @@ -38,7 +37,7 @@ export async function toBeElementsArrayOfSize( } elements = await refetchElements(elements, numberOptions.wait, true) return false - }, isNot, { ...numberOptions, ...options }) + }, { ...numberOptions, ...options }) if (Array.isArray(received) && pass) { for (let index = originalLength; index < elements.length; index++) { diff --git a/src/matchers/mock/toBeRequestedTimes.ts b/src/matchers/mock/toBeRequestedTimes.ts index 0a50b3af7..e6953dd22 100644 --- a/src/matchers/mock/toBeRequestedTimes.ts +++ b/src/matchers/mock/toBeRequestedTimes.ts @@ -7,7 +7,6 @@ export async function toBeRequestedTimes( expectedValue: number | ExpectWebdriverIO.NumberOptions = {}, options: ExpectWebdriverIO.StringOptions = DEFAULT_OPTIONS ) { - const isNot = this.isNot || false const { expectation = `called${typeof expectedValue === 'number' ? ' ' + expectedValue : '' } time${expectedValue !== 1 ? 's' : ''}`, verb = 'be' } = this await options.beforeAssertion?.({ @@ -25,7 +24,7 @@ export async function toBeRequestedTimes( const pass = await waitUntil(async () => { actual = received.calls.length return compareNumbers(actual, numberOptions) - }, isNot, { ...numberOptions, ...options }) + }, { ...numberOptions, ...options }) const error = numberError(numberOptions) const message = enhanceError('mock', error, actual, this, verb, expectation, '', numberOptions) diff --git a/src/matchers/mock/toBeRequestedWith.ts b/src/matchers/mock/toBeRequestedWith.ts index 338fb83df..c735c4057 100644 --- a/src/matchers/mock/toBeRequestedWith.ts +++ b/src/matchers/mock/toBeRequestedWith.ts @@ -24,8 +24,7 @@ export async function toBeRequestedWith( expectedValue: ExpectWebdriverIO.RequestedWith = {}, options: ExpectWebdriverIO.CommandOptions = DEFAULT_OPTIONS ) { - const isNot = this.isNot || false - const { expectation = 'called with', verb = 'be' } = this + const { expectation = 'called with', verb = 'be', isNot } = this await options.beforeAssertion?.({ matcherName: 'toBeRequestedWith', @@ -54,7 +53,6 @@ export async function toBeRequestedWith( return false }, - isNot, { ...options, wait: isNot ? 0 : options.wait } ) diff --git a/src/utils.ts b/src/utils.ts index 3987241ab..8720b86cc 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -39,49 +39,32 @@ function isStringContainingMatcher(expected: unknown): expected is WdioAsymmetri */ const waitUntil = async ( condition: () => Promise, - isNot = false, { wait = DEFAULT_OPTIONS.wait, interval = DEFAULT_OPTIONS.interval } = {} ): Promise => { - // single attempt - if (wait === 0) { - return await condition() - } - - let error: Error | undefined - - // wait for condition to be truthy - try { - const start = Date.now() - while (true) { - if (Date.now() - start > wait) { - throw new Error('timeout') - } - - try { - const result = isNot !== (await condition()) - error = undefined - if (result) { - break - } - await sleep(interval) - } catch (err) { - error = err - await sleep(interval) + const start = Date.now() + let error: unknown + let result = false + + do { + try { + result = await condition() + error = undefined + if (result) { + break } + } catch (err) { + error = err } - if (error) { - throw error + // No need to sleep again if time is already over + if (Date.now() - start < wait) { + await sleep(interval) } + } while (Date.now() - start < wait) - return !isNot - } catch { - if (error) { - throw error - } + if (error) { throw error } - return isNot - } + return result } async function executeCommandBe( @@ -89,7 +72,7 @@ async function executeCommandBe( command: (el: WebdriverIO.Element) => Promise, options: ExpectWebdriverIO.CommandOptions ): ExpectWebdriverIO.AsyncAssertionResult { - const { isNot, expectation, verb = 'be' } = this + const { expectation, verb = 'be' } = this let el = await received?.getElement() const pass = await waitUntil( @@ -103,7 +86,6 @@ async function executeCommandBe( el = result.el as WebdriverIO.Element return result.success }, - isNot, options ) diff --git a/test/__fixtures__/utils.ts b/test/__fixtures__/utils.ts index 1149d08d0..95bca72a4 100644 --- a/test/__fixtures__/utils.ts +++ b/test/__fixtures__/utils.ts @@ -2,6 +2,11 @@ export function matcherNameToString(matcherName: string) { return matcherName.replace(/([A-Z])/g, ' $1').toLowerCase() } +export function matcherLastWordName(matcherName: string) { + return matcherName.replace(/^toHave/, '').replace(/^toBe/, '') + .replace(/([A-Z])/g, ' $1').trim().toLowerCase() +} + export function getExpectMessage(msg: string) { return msg.split('\n')[0] } diff --git a/test/__mocks__/@wdio/globals.ts b/test/__mocks__/@wdio/globals.ts index d14bc54d0..d9f4a3b4a 100644 --- a/test/__mocks__/@wdio/globals.ts +++ b/test/__mocks__/@wdio/globals.ts @@ -15,7 +15,7 @@ const getElementMethods = () => ({ isClickable: vi.spyOn({ isClickable: async () => true }, 'isClickable'), isFocused: vi.spyOn({ isFocused: async () => true }, 'isFocused'), isEnabled: vi.spyOn({ isEnabled: async () => true }, 'isEnabled'), - getProperty: vi.spyOn({ getProperty: async (_prop: string) => undefined }, 'getProperty'), + getProperty: vi.spyOn({ getProperty: async (_prop: string) => '1' }, 'getProperty'), getText: vi.spyOn({ getText: async () => ' Valid Text ' }, 'getText'), getHTML: vi.spyOn({ getHTML: async () => { return '' } }, 'getHTML'), getComputedLabel: vi.spyOn({ getComputedLabel: async () => 'Computed Label' }, 'getComputedLabel'), @@ -25,6 +25,7 @@ const getElementMethods = () => ({ if (prop === 'height') { return 50 } return { width: 100, height: 50 } satisfies Size } }, 'getSize') as unknown as WebdriverIO.Element['getSize'], + getAttribute: vi.spyOn({ getAttribute: async (_attr: string) => 'some attribute' }, 'getAttribute'), } satisfies Partial) function $(_selector: string) { diff --git a/test/matchers.test.ts b/test/matchers.test.ts index f96455a01..7cb19b8aa 100644 --- a/test/matchers.test.ts +++ b/test/matchers.test.ts @@ -1,5 +1,8 @@ -import { test, expect, vi } from 'vitest' +import { test, expect, vi, describe } from 'vitest' import { matchers, expect as expectLib } from '../src/index.js' +import { $ } from '@wdio/globals' + +vi.mock('@wdio/globals') const ALL_MATCHERS = [ // browser @@ -71,3 +74,384 @@ test('Generic asymmetric matchers from Expect library should work', () => { expectLib(1).toEqual(expectLib.closeTo(1.0001, 0.0001)) expectLib(['apple', 'banana', 'cherry']).toEqual(expectLib.arrayOf(expectLib.any(String))) }) + +describe('Custom Wdio Matchers Integration Tests', async () => { + + describe('Matchers pass with success with default mocked values', async () => { + const el = await $('selector') + + test('toBe matchers', async () => { + await expectLib(el).toBeDisplayed() + await expectLib(el).toBeExisting() + await expectLib(el).toBeEnabled() + await expectLib(el).toBeClickable() + await expectLib(el).toBeFocused() + await expectLib(el).toBeSelected() + }) + + test('toHave matchers', async () => { + await expectLib(el).toHaveText('Valid Text') + await expectLib(el).toHaveHTML('') + await expectLib(el).toHaveComputedLabel('Computed Label') + await expectLib(el).toHaveComputedRole('Computed Role') + await expectLib(el).toHaveSize({ width: 100, height: 50 }) + await expectLib(el).toHaveHeight(50) + await expectLib(el).toHaveWidth(100) + await expectLib(el).toHaveAttribute('someAttribute', 'some attribute') + await expectLib(el).toHaveAttribute('someAttribute') + await expectLib(el).toHaveAttr('someAttribute', 'some attribute') + await expectLib(el).toHaveElementProperty('someProperty', '1') + }) + }) + + describe('Matchers fails when using `.not` with proper message', async () => { + const el = await $('selector') + + test('toBe matchers', async () => { + await expect(() => expectLib(el).not.toBeDisplayed({ wait: 1 })).rejects.toThrow(`\ +Expect $(\`selector\`) not to be displayed + +Expected [not]: "not displayed" +Received : "displayed"` + ) + + await expect(() => expectLib(el).not.toBeExisting({ wait: 1 })).rejects.toThrow(`\ +Expect $(\`selector\`) not to be existing + +Expected [not]: "not existing" +Received : "existing"` + ) + + await expect(() => expectLib(el).not.toBeEnabled({ wait: 1 })).rejects.toThrow(`\ +Expect $(\`selector\`) not to be enabled + +Expected [not]: "not enabled" +Received : "enabled"` + ) + + await expect(() => expectLib(el).not.toBeClickable({ wait: 1 })).rejects.toThrow(`\ +Expect $(\`selector\`) not to be clickable + +Expected [not]: "not clickable" +Received : "clickable"` + ) + + await expect(() => expectLib(el).not.toBeFocused({ wait: 1 })).rejects.toThrow(`\ +Expect $(\`selector\`) not to be focused + +Expected [not]: "not focused" +Received : "focused"` + ) + + await expect(() => expectLib(el).not.toBeSelected({ wait: 1 })).rejects.toThrow(`\ +Expect $(\`selector\`) not to be selected + +Expected [not]: "not selected" +Received : "selected"` + ) + }) + + test('toHave matchers', async () => { + await expect(() => expectLib(el).not.toHaveText(' Valid Text ', { trim: false, wait: 1 })).rejects.toThrow(`\ +Expect $(\`selector\`) not to have text + +Expected [not]: " Valid Text " +Received : " Valid Text "` + ) + + await expect(() => expectLib(el).not.toHaveHTML('', { wait: 1 })).rejects.toThrow(`\ +Expect $(\`selector\`) not to have HTML + +Expected [not]: "" +Received : ""` + ) + + await expect(() => expectLib(el).not.toHaveComputedLabel('Computed Label', { wait: 1 })).rejects.toThrow(`\ +Expect $(\`selector\`) not to have computed label + +Expected [not]: "Computed Label" +Received : "Computed Label"` + ) + + await expect(() => expectLib(el).not.toHaveComputedRole('Computed Role', { wait: 1 })).rejects.toThrow(`\ +Expect $(\`selector\`) not to have computed role + +Expected [not]: "Computed Role" +Received : "Computed Role"` + ) + }) + + test('size matchers', async () => { + await expect(() => expectLib(el).not.toHaveSize({ width: 100, height: 50 }, { wait: 1 })).rejects.toThrow(`\ +Expect $(\`selector\`) not to have size + +Expected [not]: {"height": 50, "width": 100} +Received : {"height": 50, "width": 100}` + ) + + await expect(() => expectLib(el).not.toHaveHeight(50, { wait: 1 })).rejects.toThrow(`\ +Expect $(\`selector\`) not to have height + +Expected [not]: 50 +Received : 50` + ) + + await expect(() => expectLib(el).not.toHaveWidth(100, { wait: 1 })).rejects.toThrow(`\ +Expect $(\`selector\`) not to have width + +Expected [not]: 100 +Received : 100` + ) + }) + + test('attribute and property matchers', async () => { + await expect(() => expectLib(el).not.toHaveAttribute('someAttribute', 'some attribute')).rejects.toThrow(`\ +Expect $(\`selector\`) not to have attribute someAttribute + +Expected [not]: "some attribute" +Received : "some attribute"` + ) + + await expect(() => expectLib(el).not.toHaveAttribute('someAttribute')).rejects.toThrow(`\ +Expect $(\`selector\`) not to have attribute someAttribute + +Expected [not]: false +Received : true` + ) + + await expect(() => expectLib(el).not.toHaveAttr('someAttribute', 'some attribute')).rejects.toThrow(`\ +Expect $(\`selector\`) not to have attribute someAttribute + +Expected [not]: "some attribute" +Received : "some attribute"` + ) + await expect(() => expectLib(el).not.toHaveElementProperty('someProperty', '1')).rejects.toThrow(`\ +Expect $(\`selector\`) not to have property someProperty + +Expected [not]: "1" +Received : "1"` + ) + }) + }) + + describe('Matchers fails with proper messages', async () => { + const el = await $('selector') + vi.mocked(el.isDisplayed).mockResolvedValue(false) + vi.mocked(el.isExisting).mockResolvedValue(false) + vi.mocked(el.isEnabled).mockResolvedValue(false) + vi.mocked(el.isClickable).mockResolvedValue(false) + vi.mocked(el.isFocused).mockResolvedValue(false) + vi.mocked(el.isSelected).mockResolvedValue(false) + + test('Ensure toBe matchers throws and show proper failing message', async () => { + await expect(() => expectLib(el).toBeDisplayed({ wait: 1 })).rejects.toThrow(`\ +Expect $(\`selector\`) to be displayed + +Expected: "displayed" +Received: "not displayed"`) + + await expect(() => expectLib(el).toBeExisting({ wait: 1 })).rejects.toThrow(`\ +Expect $(\`selector\`) to be existing + +Expected: "existing" +Received: "not existing"`) + + await expect(() => expectLib(el).toExist({ wait: 1 })).rejects.toThrow(`\ +Expect $(\`selector\`) to exist + +Expected: "exist" +Received: "not exist"`) + + await expect(() => expectLib(el).toBeEnabled({ wait: 1 })).rejects.toThrow(`\ +Expect $(\`selector\`) to be enabled + +Expected: "enabled" +Received: "not enabled"`) + + await expect(() => expectLib(el).toBeClickable({ wait: 1 })).rejects.toThrow(`\ +Expect $(\`selector\`) to be clickable + +Expected: "clickable" +Received: "not clickable"`) + + await expect(() => expectLib(el).toBeFocused({ wait: 1 })).rejects.toThrow(`\ +Expect $(\`selector\`) to be focused + +Expected: "focused" +Received: "not focused"`) + + await expect(() => expectLib(el).toBeSelected({ wait: 1 })).rejects.toThrow(`\ +Expect $(\`selector\`) to be selected + +Expected: "selected" +Received: "not selected"`) + + }) + + test('Ensure toHave matchers throws and show proper failing message', async () => { + await expect(() => expectLib(el).toHaveText('Some other text', { wait: 1 })).rejects.toThrow(`\ +Expect $(\`selector\`) to have text + +Expected: "Some other text" +Received: " Valid Text "`) + + await expect(() => expectLib(el).toHaveHTML('', { wait: 1 })).rejects.toThrow(`\ +Expect $(\`selector\`) to have HTML + +Expected: "" +Received: ""`) + + await expect(() => expectLib(el).toHaveComputedLabel('Some Other Computed Label', { wait: 1 })).rejects.toThrow(`\ +Expect $(\`selector\`) to have computed label + +Expected: "Some Other Computed Label" +Received: "Computed Label"`) + + await expect(() => expectLib(el).toHaveComputedRole('Some Other Computed Role', { wait: 1 })).rejects.toThrow(`\ +Expect $(\`selector\`) to have computed role + +Expected: "Some Other Computed Role" +Received: "Computed Role"`) + + await expect(() => expectLib(el).toHaveElementProperty('someProperty', 'some other value', { wait: 1 })).rejects.toThrow(`\ +Expect $(\`selector\`) to have property someProperty + +Expected: "some other value" +Received: "1"`) + + }) + + test('Ensure toHaveAttribute matchers throw and show proper failing message', async () => { + await expect(() => expectLib(el).toHaveAttribute('someAttribute', 'some other attribute', { wait: 1 })).rejects.toThrow(`\ +Expect $(\`selector\`) to have attribute someAttribute + +Expected: "some other attribute" +Received: "some attribute"`) + + vi.mocked(el.getAttribute).mockResolvedValue(null as unknown as string) + await expect(() => expectLib(el).toHaveAttribute('notExistingAttribute')).rejects.toThrow(`\ +Expect $(\`selector\`) to have attribute notExistingAttribute + +Expected: true +Received: false`) + await expect(() => expectLib(el).toHaveAttr('someAttribute', 'some other attribute', { wait: 1 })).rejects.toThrow(`\ +Expect $(\`selector\`) to have attribute someAttribute + +Expected: "some other attribute" +Received: null`) + }) + + test('Ensure toHaveSize, toHaveHeight, toHaveWidth matchers throw and show proper failing message', async () => { + await expect(() => expectLib(el).toHaveSize({ width: 200, height: 100 }, { wait: 1 })).rejects.toThrow(`\ +Expect $(\`selector\`) to have size + +- Expected - 2 ++ Received + 2 + + Object { +- "height": 100, +- "width": 200, ++ "height": 50, ++ "width": 100, + }`) + await expect(() => expectLib(el).toHaveHeight(100, { wait: 1 })).rejects.toThrow(`\ +Expect $(\`selector\`) to have height + +Expected: 100 +Received: 50`) + + await expect(() => expectLib(el).toHaveWidth(200, { wait: 1 })).rejects.toThrow(`\ +Expect $(\`selector\`) to have width + +Expected: 200 +Received: 100`) + }) + + }) + + describe('Matchers pass when using `.not`', async () => { + const el = await $('selector') + vi.mocked(el.isDisplayed).mockResolvedValue(false) + vi.mocked(el.isExisting).mockResolvedValue(false) + vi.mocked(el.isEnabled).mockResolvedValue(false) + vi.mocked(el.isClickable).mockResolvedValue(false) + vi.mocked(el.isFocused).mockResolvedValue(false) + vi.mocked(el.isSelected).mockResolvedValue(false) + + test('toBe matchers', async () => { + await expectLib(el).not.toBeDisplayed({ wait: 1 }) + await expectLib(el).not.toBeExisting({ wait: 1 }) + await expectLib(el).not.toBeEnabled({ wait: 1 }) + await expectLib(el).not.toBeClickable({ wait: 1 }) + await expectLib(el).not.toBeFocused({ wait: 1 }) + await expectLib(el).not.toBeSelected({ wait: 1 }) + }) + + test('toHave matchers', async () => { + await expectLib(el).not.toHaveText('Some other text', { wait: 1 }) + await expectLib(el).not.toHaveHTML('', { wait: 1 }) + await expectLib(el).not.toHaveComputedLabel('Some Other Computed Label', { wait: 1 }) + await expectLib(el).not.toHaveComputedRole('Some Other Computed Role', { wait: 1 }) + await expectLib(el).not.toHaveElementProperty('someProperty', 'some other value', { wait: 1 }) + await expectLib(el).not.toHaveAttribute('someAttribute', 'some other attribute', { wait: 1 }) + await expectLib(el).not.toHaveAttr('someAttribute', 'some other attribute', { wait: 1 }) + await expectLib(el).not.toHaveSize({ width: 200, height: 100 }, { wait: 1 }) + await expectLib(el).not.toHaveHeight(100, { wait: 1 }) + await expectLib(el).not.toHaveWidth(200, { wait: 1 }) + }) + + }) + + describe('Matcher eventually passing', async () => { + + test('when element eventually is displayed, matcher and .not matcher should be consistent', async () => { + const el = await $('selector') + vi.mocked(el.isDisplayed) + .mockResolvedValueOnce(false) + .mockResolvedValueOnce(false) + .mockResolvedValueOnce(true) + + // Passes when element becomes displayed + await expectLib(el).toBeDisplayed({ wait: 300, interval: 100 }) + + vi.mocked(el.isDisplayed) + .mockResolvedValueOnce(false) + .mockResolvedValueOnce(false) + .mockResolvedValueOnce(true) + + // Should not pass with the same scenario to be consistent + await expect(() => expectLib(el).not.toBeDisplayed({ wait: 300, interval: 100 })).rejects.toThrow(`\ +Expect $(\`selector\`) not to be displayed + +Expected [not]: "not displayed" +Received : "displayed"`) + + expect(el.isDisplayed).toHaveBeenCalledTimes(6) + }) + + test('when element eventually is not displayed, matcher and .not matcher should be consistent', async () => { + const el = await $('selector') + vi.mocked(el.isDisplayed) + .mockResolvedValueOnce(false) + .mockResolvedValueOnce(false) + .mockResolvedValueOnce(false) + + // Does not pass since element never becomes displayed + await expect(expectLib(el).toBeDisplayed({ wait: 300, interval: 100 })).rejects.toThrow(`\ +Expect $(\`selector\`) to be displayed + +Expected: "displayed" +Received: "not displayed"`) + + vi.mocked(el.isDisplayed) + .mockResolvedValueOnce(false) + .mockResolvedValueOnce(false) + .mockResolvedValueOnce(false) + + // Should pass with the same scenario to be consistent + await expectLib(el).not.toBeDisplayed({ wait: 300, interval: 100 }) + + expect(el.isDisplayed).toHaveBeenCalledTimes(6) + }) + }) +}) diff --git a/test/matchers/beMatchers.test.ts b/test/matchers/beMatchers.test.ts index 4ebf22cd9..c2eba4864 100644 --- a/test/matchers/beMatchers.test.ts +++ b/test/matchers/beMatchers.test.ts @@ -1,6 +1,6 @@ import { vi, test, describe, expect } from 'vitest' import { $ } from '@wdio/globals' -import { getExpectMessage, getReceived, matcherNameToString } from '../__fixtures__/utils.js' +import { matcherLastWordName } from '../__fixtures__/utils.js' import * as Matchers from '../../src/matchers.js' vi.mock('@wdio/globals') @@ -82,56 +82,58 @@ describe('be* matchers', () => { expect(el[elementFnName]).toHaveBeenCalledTimes(1) }) - test('not - failure', async () => { + test('not - failure - pass should be true', async () => { const el = await $('sel') const result = await matcherFn.call({ isNot: true }, el, { wait: 0 }) as ExpectWebdriverIO.AssertionResult - const received = getReceived(result.message()) - expect(received).not.toContain('not') - expect(result.pass).toBe(true) + expect(result.pass).toBe(true) // failure, boolean is inverted later because of `.not` + expect(result.message()).toEqual(`\ +Expect $(\`sel\`) not to be ${matcherLastWordName(matcherName)} + +Expected [not]: "not ${matcherLastWordName(matcherName)}" +Received : "${matcherLastWordName(matcherName)}"` + ) }) - test('not - success', async () => { + test('not - success - pass should be false', async () => { const el = await $('sel') el[elementFnName] = vi.fn().mockResolvedValue(false) const result = await matcherFn.call({ isNot: true }, el, { wait: 0 }) as ExpectWebdriverIO.AssertionResult - const received = getReceived(result.message()) - expect(received).toContain('not') - expect(result.pass).toBe(false) + expect(result.pass).toBe(false) // success, boolean is inverted later because of `.not` }) - test('not - failure (with wait)', async () => { + test('not - failure (with wait) - pass should be true', async () => { const el = await $('sel') const result = await matcherFn.call({ isNot: true }, el, { wait: 1 }) as ExpectWebdriverIO.AssertionResult - const received = getReceived(result.message()) - expect(received).not.toContain('not') - expect(result.pass).toBe(true) + expect(result.pass).toBe(true) // failure, boolean is inverted later because of `.not` }) - test('not - success (with wait)', async () => { + test('not - success (with wait) - pass should be false', async () => { const el = await $('sel') el[elementFnName] = vi.fn().mockResolvedValue(false) - const result = await matcherFn.call({ isNot: true }, el, { wait: 1 }) as ExpectWebdriverIO.AssertionResult - const received = getReceived(result.message()) - expect(received).toContain('not') - expect(result.pass).toBe(false) + expect(result.pass).toBe(false) // success, boolean is inverted later because of `.not` }) test('message', async () => { const el = await $('sel') el[elementFnName] = vi.fn().mockResolvedValue(false) - const result = await matcherFn.call({}, el) as ExpectWebdriverIO.AssertionResult - expect(getExpectMessage(result.message())) - .toContain(matcherNameToString(matcherName)) + const result = await matcherFn.call({}, el, { wait: 1 }) as ExpectWebdriverIO.AssertionResult + expect(result.pass).toBe(false) + expect(result.message()).toBe(`\ +Expect $(\`sel\`) to be ${matcherLastWordName(matcherName)} + +Expected: "${matcherLastWordName(matcherName)}" +Received: "not ${matcherLastWordName(matcherName)}"` + ) }) }) }) diff --git a/test/matchers/browserMatchers.test.ts b/test/matchers/browserMatchers.test.ts index 72f9a0a4d..f017b5674 100644 --- a/test/matchers/browserMatchers.test.ts +++ b/test/matchers/browserMatchers.test.ts @@ -1,7 +1,7 @@ import { vi, test, describe, expect } from 'vitest' import { browser } from '@wdio/globals' -import { getExpectMessage, getReceived, matcherNameToString, getExpected } from '../__fixtures__/utils.js' +import { getExpectMessage, matcherNameToString, matcherLastWordName } from '../__fixtures__/utils.js' import * as Matchers from '../../src/matchers.js' vi.mock('@wdio/globals') @@ -28,7 +28,7 @@ describe('browser matchers', () => { expect(browser[browserFnName]).toHaveBeenCalledTimes(3) }) - test('wait but failure', async () => { + test('wait but error', async () => { browser[browserFnName] = vi.fn().mockRejectedValue(new Error('some error')) await expect(() => matcherFn.call({}, browser, validText, { trim: false })) @@ -61,48 +61,47 @@ describe('browser matchers', () => { expect(browser[browserFnName]).toHaveBeenCalledTimes(1) }) - test('not - failure', async () => { + test('not - failure - pass should be true', async () => { + browser[browserFnName] = vi.fn().mockResolvedValue(validText) const result = await matcherFn.call({ isNot: true }, browser, validText, { wait: 0, trim: false }) as ExpectWebdriverIO.AssertionResult - expect(getExpectMessage(result.message())).toContain('not') - expect(getExpected(result.message())).toContain('not') + expect(result.pass).toBe(true) // failure, boolean is inverted later because of `.not` + expect(result.message()).toEqual(`\ +Expect window not to have ${matcherLastWordName(matcherName)} - expect(result.pass).toBe(true) +Expected [not]: " Valid Text " +Received : " Valid Text "` + ) }) - test('not - success', async () => { + test('not - success - pass should be false', async () => { browser[browserFnName] = vi.fn().mockResolvedValue(wrongText) const result = await matcherFn.call({ isNot: true }, browser, validText, { wait: 0 }) as ExpectWebdriverIO.AssertionResult - expect(getExpectMessage(result.message())).toContain('not') - expect(getExpected(result.message())).toContain('Valid') - expect(getReceived(result.message())).toContain('Wrong') - - expect(result.pass).toBe(false) + expect(result.pass).toBe(false) // success, boolean is inverted later because of `.not` }) - test('not - failure (with wait)', async () => { + test('not - failure (with wait) - pass should be true', async () => { browser[browserFnName] = vi.fn().mockResolvedValue(validText) const result = await matcherFn.call({ isNot: true }, browser, validText, { wait: 1, trim: false }) as ExpectWebdriverIO.AssertionResult - expect(getExpectMessage(result.message())).toContain('not') - expect(getExpected(result.message())).toContain('not') + expect(result.pass).toBe(true) // failure, boolean is inverted later because of `.not` + expect(result.message()).toEqual(`\ +Expect window not to have ${matcherLastWordName(matcherName)} - expect(result.pass).toBe(true) +Expected [not]: " Valid Text " +Received : " Valid Text "` + ) }) - test('not - success (with wait)', async () => { + test('not - success (with wait) - pass should be false', async () => { browser[browserFnName] = vi.fn().mockResolvedValue(wrongText) const result = await matcherFn.call({ isNot: true }, browser, validText, { wait: 1 }) as ExpectWebdriverIO.AssertionResult - expect(getExpectMessage(result.message())).toContain('not') - expect(getExpected(result.message())).toContain('Valid') - expect(getReceived(result.message())).toContain('Wrong') - - expect(result.pass).toBe(false) + expect(result.pass).toBe(false) // success, boolean is inverted later because of `.not` }) test('message', async () => { diff --git a/test/matchers/element/toBeDisabled.test.ts b/test/matchers/element/toBeDisabled.test.ts index f53601413..1b0dac3e4 100644 --- a/test/matchers/element/toBeDisabled.test.ts +++ b/test/matchers/element/toBeDisabled.test.ts @@ -1,7 +1,6 @@ import { vi, test, describe, expect } from 'vitest' import { $ } from '@wdio/globals' -import { getExpectMessage, getReceived } from '../../__fixtures__/utils.js' import { toBeDisabled } from '../../../src/matchers/element/toBeDisabled.js' vi.mock('@wdio/globals') @@ -69,55 +68,65 @@ describe('toBeDisabled', () => { expect(el.isEnabled).toHaveBeenCalledTimes(1) }) - test('not - failure', async () => { + test('not - failure - pass should be true', async () => { const el = await $('sel') el.isEnabled = vi.fn().mockResolvedValue(false) const result = await toBeDisabled.call({ isNot: true }, el, { wait: 0 }) - const received = getReceived(result.message()) - expect(received).not.toContain('not') - expect(result.pass).toBe(true) + expect(result.pass).toBe(true) // failure, boolean is inverted later because of `.not` + expect(result.message()).toEqual(`\ +Expect $(\`sel\`) not to be disabled + +Expected [not]: "not disabled" +Received : "disabled"` + ) }) - test('not - success', async () => { + test('not - success - pass should be false', async () => { const el = await $('sel') el.isEnabled = vi.fn().mockResolvedValue(true) const result = await toBeDisabled.call({ isNot: true }, el, { wait: 0 }) - const received = getReceived(result.message()) - expect(received).toContain('not') - expect(result.pass).toBe(false) + expect(result.pass).toBe(false) // success, boolean is inverted later because of `.not` }) - test('not - failure (with wait)', async () => { + test('not - failure (with wait) - pass should be true', async () => { const el = await $('sel') el.isEnabled = vi.fn().mockResolvedValue(false) const result = await toBeDisabled.call({ isNot: true }, el, { wait: 1 }) - const received = getReceived(result.message()) - expect(received).not.toContain('not') - expect(result.pass).toBe(true) + expect(result.pass).toBe(true) // failure, boolean is inverted later because of `.not` + expect(result.message()).toEqual(`\ +Expect $(\`sel\`) not to be disabled + +Expected [not]: "not disabled" +Received : "disabled"` + ) }) - test('not - success (with wait)', async () => { + test('not - success (with wait) - pass should be false', async () => { const el = await $('sel') el.isEnabled = vi.fn().mockResolvedValue(true) const result = await toBeDisabled.call({ isNot: true }, el, { wait: 1 }) - const received = getReceived(result.message()) - expect(received).toContain('not') - expect(result.pass).toBe(false) + expect(result.pass).toBe(false) // success, boolean is inverted later because of `.not` }) test('message', async () => { const el = await $('sel') - el.isEnabled = vi.fn().mockResolvedValue(false) + el.isEnabled = vi.fn().mockResolvedValue(true) const result = await toBeDisabled.call({}, el) - expect(getExpectMessage(result.message())).toContain('to be disabled') + expect(result.pass).toBe(false) + expect(result.message()).toEqual(`\ +Expect $(\`sel\`) to be disabled + +Expected: "disabled" +Received: "not disabled"` + ) }) }) diff --git a/test/matchers/element/toBeDisplayed.test.ts b/test/matchers/element/toBeDisplayed.test.ts index 84a660674..250ead0bd 100644 --- a/test/matchers/element/toBeDisplayed.test.ts +++ b/test/matchers/element/toBeDisplayed.test.ts @@ -1,7 +1,6 @@ import { vi, test, describe, expect } from 'vitest' import { $ } from '@wdio/globals' -import { getExpectMessage, getReceived } from '../../__fixtures__/utils.js' import { toBeDisplayed } from '../../../src/matchers/element/toBeDisplayed.js' import { executeCommandBe } from '../../../src/utils.js' import { DEFAULT_OPTIONS } from '../../../src/constants.js' @@ -123,45 +122,42 @@ describe('toBeDisplayed', () => { expect(el.isDisplayed).toHaveBeenCalledTimes(1) }) - test('not - failure', async () => { + test('not - failure - pass must be true', async () => { const el = await $('sel') - const result = await toBeDisplayed.call({ isNot: true }, el, { wait: 0 }) - const received = getReceived(result.message()) - expect(received).not.toContain('not') - expect(result.pass).toBe(true) + expect(result.pass).toBe(true) // failure, boolean is inverted later because of `.not` }) - test('not - success', async () => { + test('not - success - pass should be false', async () => { const el = await $('sel') el.isDisplayed = vi.fn().mockResolvedValue(false) const result = await toBeDisplayed.call({ isNot: true }, el, { wait: 0 }) - const received = getReceived(result.message()) - expect(received).toContain('not') - expect(result.pass).toBe(false) + expect(result.pass).toBe(false) // success, boolean is inverted later because of `.not` }) - test('not - failure (with wait)', async () => { + test('not - failure (with wait) - pass should be true', async () => { const el = await $('sel') - const result = await toBeDisplayed.call({ isNot: true }, el, { wait: 1 }) - const received = getReceived(result.message()) - expect(received).not.toContain('not') - expect(result.pass).toBe(true) + expect(result.pass).toBe(true) // failure, boolean is inverted later because of `.not` + expect(result.message()).toEqual(`\ +Expect $(\`sel\`) not to be displayed + +Expected [not]: "not displayed" +Received : "displayed"` + ) }) - test('not - success (with wait)', async () => { + test('not - success (with wait) - pass should be false', async () => { const el = await $('sel') el.isDisplayed = vi.fn().mockResolvedValue(false) const result = await toBeDisplayed.call({ isNot: true }, el, { wait: 1 }) - const received = getReceived(result.message()) expect(el.isDisplayed).toHaveBeenCalledWith( { @@ -175,8 +171,7 @@ describe('toBeDisplayed', () => { wait: 1, interval: DEFAULT_OPTIONS.interval })) - expect(received).toContain('not') - expect(result.pass).toBe(false) + expect(result.pass).toBe(false) // success, boolean is inverted later because of `.not` }) test('message', async () => { @@ -184,7 +179,14 @@ describe('toBeDisplayed', () => { el.isDisplayed = vi.fn().mockResolvedValue(false) - const result = await toBeDisplayed.call({}, el) - expect(getExpectMessage(result.message())).toContain('to be displayed') + const result = await toBeDisplayed.call({}, el, { wait: 0 }) + + expect(result.pass).toBe(false) + expect(result.message()).toEqual(`\ +Expect $(\`sel\`) to be displayed + +Expected: "displayed" +Received: "not displayed"` + ) }) }) diff --git a/test/matchers/element/toHaveChildren.test.ts b/test/matchers/element/toHaveChildren.test.ts index f37bb3d98..4699cb5dc 100644 --- a/test/matchers/element/toHaveChildren.test.ts +++ b/test/matchers/element/toHaveChildren.test.ts @@ -66,17 +66,25 @@ describe('toHaveChildren', () => { expect(result.pass).toBe(false) }) - test('.not exact value - failure', async () => { + test('.not exact value - failure - pass should be true', async () => { const el = await $('sel') const result = await toHaveChildren.bind({ isNot: true })(el, { eq: 2, wait: 0 }) - expect(result.pass).toBe(true) + + expect(result.pass).toBe(true) // failure, boolean is inverted later because of `.not` + expect(result.message()).toEqual(`\ +Expect $(\`sel\`) not to have children + +Expected [not]: 2 +Received : 2`) + }) - test('.not exact value - success', async () => { + test('.not exact value - success - pass should be false', async () => { const el = await $('sel') const result = await toHaveChildren.bind({ isNot: true })(el, { eq: 3, wait: 1 }) - expect(result.pass).toBe(false) + + expect(result.pass).toBe(false) // success, boolean is inverted later because of `.not` }) }) diff --git a/test/matchers/element/toHaveComputedLabel.test.ts b/test/matchers/element/toHaveComputedLabel.test.ts index ae8c2a1df..02f95a5b2 100644 --- a/test/matchers/element/toHaveComputedLabel.test.ts +++ b/test/matchers/element/toHaveComputedLabel.test.ts @@ -67,30 +67,35 @@ describe('toHaveComputedLabel', () => { expect(el.getComputedLabel).toHaveBeenCalledTimes(1) }) - test('not - failure', async () => { + test('not - failure - pass should be true', async () => { const el = await $('sel') el.getComputedLabel = vi.fn().mockResolvedValue('WebdriverIO') const result = await toHaveComputedLabel.call({ isNot: true }, el, 'WebdriverIO', { wait: 0 }) - const received = getReceived(result.message()) - expect(received).not.toContain('not') - expect(result.pass).toBe(true) + expect(result.pass).toBe(true) // failure, boolean is inverted later because of `.not` + expect(result.message()).toEqual(`\ +Expect $(\`sel\`) not to have computed label + +Expected [not]: "WebdriverIO" +Received : "WebdriverIO"` + ) }) - test("should return false if computed labels don't match", async () => { + test('not - success - pass should be false', async () => { const el = await $('sel') el.getComputedLabel = vi.fn().mockResolvedValue('WebdriverIO') - const result = await toHaveComputedLabel.bind({ isNot: true })(el, 'foobar', { wait: 1 }) - expect(result.pass).toBe(false) + const result = await toHaveComputedLabel.call({ isNot: true }, el, 'not WebdriverIO', { wait: 0 }) + + expect(result.pass).toBe(false) // success, boolean is inverted later because of `.not` }) test('should return true if computed labels match', async () => { const el = await $('sel') el.getComputedLabel = vi.fn().mockResolvedValue('WebdriverIO') - const result = await toHaveComputedLabel.bind({ isNot: true })(el, 'WebdriverIO', { wait: 1 }) + const result = await toHaveComputedLabel.bind({})(el, 'WebdriverIO', { wait: 1 }) expect(result.pass).toBe(true) }) @@ -101,6 +106,7 @@ describe('toHaveComputedLabel', () => { const result = await toHaveComputedLabel.bind({})(el, 'BrowserdriverIO', { replace: ['Web', 'Browser'], }) + expect(result.pass).toBe(true) }) diff --git a/test/matchers/element/toHaveComputedRole.test.ts b/test/matchers/element/toHaveComputedRole.test.ts index a2b143744..f01df18af 100644 --- a/test/matchers/element/toHaveComputedRole.test.ts +++ b/test/matchers/element/toHaveComputedRole.test.ts @@ -69,31 +69,35 @@ describe('toHaveComputedcomputed role', () => { expect(el.getComputedRole).toHaveBeenCalledTimes(1) }) - test('not - failure', async () => { + test('not - failure - pass should be true', async () => { const el = await $('sel') el.getComputedRole = vi.fn().mockResolvedValueOnce('WebdriverIO') const result = await toHaveComputedRole.call({ isNot: true }, el, 'WebdriverIO', { wait: 0 }) - const received = getReceived(result.message()) - expect(received).not.toContain('not') - expect(result.pass).toBe(true) + expect(result.pass).toBe(true) // failure, boolean is inverted later because of `.not` + expect(result.message()).toEqual(`\ +Expect $(\`sel\`) not to have computed role + +Expected [not]: "WebdriverIO" +Received : "WebdriverIO"` + ) }) - test("should return false if computed roles don't match", async () => { + test('not - success - pass should be false', async () => { const el = await $('sel') el.getComputedRole = vi.fn().mockResolvedValueOnce('WebdriverIO') - const result = await toHaveComputedRole.bind({ isNot: true })(el, 'foobar', { wait: 1 }) - expect(result.pass).toBe(false) + const result = await toHaveComputedRole.call({ isNot: true }, el, 'not WebdriverIO', { wait: 0 }) + + expect(result.pass).toBe(false) // success, boolean is inverted later because of `.not` }) test('should return true if computed roles match', async () => { const el = await $('sel') el.getComputedRole = vi.fn().mockResolvedValueOnce('WebdriverIO') - const result = await toHaveComputedRole.bind({ isNot: true })(el, 'WebdriverIO', { wait: 1 }) - + const result = await toHaveComputedRole.bind({})(el, 'WebdriverIO', { wait: 1 }) expect(result.pass).toBe(true) }) diff --git a/test/matchers/element/toHaveElementProperty.test.ts b/test/matchers/element/toHaveElementProperty.test.ts index 977e75b1e..affb68bf3 100644 --- a/test/matchers/element/toHaveElementProperty.test.ts +++ b/test/matchers/element/toHaveElementProperty.test.ts @@ -45,18 +45,41 @@ describe('toHaveElementProperty', () => { const el = await $('sel') el.getProperty = vi.fn().mockResolvedValue('iphone') - const result = await toHaveElementProperty.bind({ isNot: true })(el, 'property', 'foobar', { wait: 1 }) + const result = await toHaveElementProperty.bind({})(el, 'property', 'foobar', { wait: 1 }) expect(result.pass).toBe(false) }) + test('should return success (false) if values dont match when isNot is true', async () => { + const el = await $('sel') + el.getProperty = vi.fn().mockResolvedValue('iphone') + + const result = await toHaveElementProperty.bind({ isNot: true })(el, 'property', 'foobar', { wait: 1 }) + + expect(result.pass).toBe(false) // success, boolean is inverted later because of `.not` + }) + test('should return true if values match', async () => { const el = await $('sel') + el.getProperty = vi.fn().mockResolvedValue('iphone') + const result = await toHaveElementProperty.bind({})(el, 'property', 'iphone', { wait: 1 }) + expect(result.pass).toBe(true) + }) + + test('should return failure (true) if values match when isNot is true', async () => { + const el = await $('sel') el.getProperty = vi.fn().mockResolvedValue('iphone') const result = await toHaveElementProperty.bind({ isNot: true })(el, 'property', 'iphone', { wait: 1 }) - expect(result.pass).toBe(true) + + expect(result.pass).toBe(true) // failure, boolean is inverted later because of `.not` + expect(result.message()).toEqual(`\ +Expect $(\`sel\`) not to have property property + +Expected [not]: "iphone" +Received : "iphone"` + ) }) test('with RegExp should return true if values match', async () => { @@ -68,31 +91,112 @@ describe('toHaveElementProperty', () => { expect(result.pass).toBe(true) }) - test('should return false for null input', async () => { + test.for([ + { propertyActualValue: null }, + { propertyActualValue: undefined }] + )('return false for not defined actual if expected is defined since property does not exist', async ( { propertyActualValue }) => { + const el = await $('sel') + el.getProperty = vi.fn().mockResolvedValue(propertyActualValue) + + const result = await toHaveElementProperty.bind({})(el, 'property', 'iphone', { wait: 1 }) + expect(result.pass).toBe(false) + }) + + test.for([ + { propertyActualValue: null }, + { propertyActualValue: undefined }] + )('return success (false) for not defined actual and defined expected when isNot is true since property does not exist', async ({ propertyActualValue }) => { const el = await $('sel') - el.getProperty = vi.fn().mockResolvedValue(undefined) + el.getProperty = vi.fn().mockResolvedValue(propertyActualValue) const result = await toHaveElementProperty.bind({ isNot: true })(el, 'property', 'iphone', { wait: 1 }) - expect(result.pass).toBe(false) + expect(result.pass).toBe(false) // success, boolean is inverted later because of `.not` }) - test('should return true if value is null', async () => { + test.for([ + { expectedValue: null }, + // { expectedValue: undefined } // fails a bug? + ] + )('should return true when property does exist by passing an not defined expected value', async ( { expectedValue }) => { const el = await $('sel') el.getProperty = vi.fn().mockResolvedValue('Test Value') - const result = await toHaveElementProperty.bind({ isNot: true })(el, 'property', null as any) + const result = await toHaveElementProperty.bind({})(el, 'property', expectedValue) expect(result.pass).toBe(true) }) + test.for([ + { expectedValue: null }, + //{ expectedValue: undefined } // fails a bug? + ] + )('should return failure (true) if property exists by passing not defined expected value when isNot is true', async ( { expectedValue }) => { + const el = await $('sel') + el.getProperty = vi.fn().mockResolvedValue('Test Value') + + const result = await toHaveElementProperty.bind({ isNot: true })(el, 'property', expectedValue) + + expect(result.pass).toBe(true) // failure, boolean is inverted later because of `.not` + }) + + // Bug? When requesting to have element property and it does exist should we return true here? + test.skip('return true if property is present', async () => { + const el = await $('sel') + el.getProperty = vi.fn().mockResolvedValue('Test Value') + + const result = await toHaveElementProperty.bind({})(el, 'property') + expect(result.pass).toBe(true) + }) + + // Bug? When requesting to not have element property and it does exist should we have a failure (pass=true? + test.skip('return failure (true) if property is present when isNot is true', async () => { + const el = await $('sel') + el.getProperty = vi.fn().mockResolvedValue('Test Value') + + const result = await toHaveElementProperty.bind({ isNot: true })(el, 'property') + expect(result.pass).toBe(true) // failure, boolean is inverted later because of `.not` + }) + + test.for([ + { expectedValue: null }, + { expectedValue: undefined } + ] + )('return false if property is not present', async ({ expectedValue }) => { + const el = await $('sel') + el.getProperty = vi.fn().mockResolvedValue(expectedValue) + + const result = await toHaveElementProperty.bind({})(el, 'property') + expect(result.pass).toBe(false) + }) + test.for([ + { expectedValue: null }, + { expectedValue: undefined } + ] + )('return success (false) if value is not present when isNot is true', async ({ expectedValue }) => { + const el = await $('sel') + el.getProperty = vi.fn().mockResolvedValue(expectedValue) + + const result = await toHaveElementProperty.bind({ isNot: true })(el, 'property') + + expect(result.pass).toBe(false) // success, boolean is inverted later because of `.not` + }) + test('should return false if value is non-string', async () => { const el = await $('sel') el.getProperty = vi.fn().mockResolvedValue(5) + const result = await toHaveElementProperty.bind({})(el, 'property', 'Test Value') + expect(result.pass).toBe(false) + }) + + test('should return success (false) if value is non-string when isNot is true', async () => { + const el = await $('sel') + el.getProperty = vi.fn().mockResolvedValue(5) + const result = await toHaveElementProperty.bind({ isNot: true })(el, 'property', 'Test Value') - expect(result.pass).toBe(false) + expect(result.pass).toBe(false) // success, boolean is inverted later because of `.not` }) describe('failure with RegExp when value does not match', () => { @@ -102,7 +206,7 @@ describe('toHaveElementProperty', () => { const el = await $('sel') el.getProperty = vi.fn().mockResolvedValue('iphone') - result = await toHaveElementProperty.call({}, el, 'property', /WDIO/) + result = await toHaveElementProperty.call({}, el, 'property', /WDIO/, { wait: 1 }) }) test('failure', () => { diff --git a/test/matchers/element/toHaveHTML.test.ts b/test/matchers/element/toHaveHTML.test.ts index 5e380d012..f8dbbd2a2 100755 --- a/test/matchers/element/toHaveHTML.test.ts +++ b/test/matchers/element/toHaveHTML.test.ts @@ -70,29 +70,59 @@ describe('toHaveHTML', () => { expect(element.getHTML).toHaveBeenCalledTimes(1) }) - test('not - failure', async () => { + test('not - failure - pass should be true', async () => { const element = await $('sel') element.getHTML = vi.fn().mockResolvedValue('
foo
') + const result = await toHaveHTML.call({ isNot: true }, element, '
foo
', { wait: 0 }) - const received = getReceived(result.message()) - expect(received).not.toContain('not') - expect(result.pass).toBe(true) + expect(result.pass).toBe(true) // failure, boolean is inverted later because of `.not` + expect(result.message()).toEqual(`\ +Expect $(\`sel\`) not to have HTML + +Expected [not]: "
foo
" +Received : "
foo
"` + ) + }) + + test('not - success - pass should be false', async () => { + const el = await $('sel') + el.getHTML = vi.fn().mockResolvedValue('
foo
') + + const result = await toHaveHTML.call({ isNot: true }, el, '
Notfoo
', { wait: 0 }) + + expect(result.pass).toBe(false) // success, boolean is inverted later because of `.not` }) test("should return false if htmls don't match", async () => { const element = await $('sel') element.getHTML = vi.fn().mockResolvedValue('
foo
') - const result = await toHaveHTML.bind({ isNot: true })(element, 'foobar', { wait: 1 }) + const result = await toHaveHTML.bind({})(element, 'foobar', { wait: 1 }) expect(result.pass).toBe(false) }) + test("should suceeds (false) if htmls don't match when isNot is true", async () => { + const element = await $('sel') + element.getHTML = vi.fn().mockReturnValue('
foo
') + + const result = await toHaveHTML.bind({ isNot: true })(element, 'foobar', { wait: 1 }) + expect(result.pass).toBe(false) // success, boolean is inverted later because of `.not` + }) + + test('should fails (pass=true) if htmls match when isNot is true', async () => { + const element = await $('sel') + element.getHTML = vi.fn().mockReturnValue('
foo
') + + const result = await toHaveHTML.bind({ isNot: true })(element, '
foo
', { wait: 1 }) + expect(result.pass).toBe(true) // success, boolean is inverted later because of `.not` + }) + test('should return true if htmls match', async () => { const element = await $('sel') element.getHTML = vi.fn().mockResolvedValue('
foo
') - const result = await toHaveHTML.bind({ isNot: true })(element, '
foo
', { wait: 1 }) + const result = await toHaveHTML.bind({})(element, '
foo
', { wait: 1 }) expect(result.pass).toBe(true) }) diff --git a/test/matchers/element/toHaveHeight.test.ts b/test/matchers/element/toHaveHeight.test.ts index 83f289cba..8f6134cf7 100755 --- a/test/matchers/element/toHaveHeight.test.ts +++ b/test/matchers/element/toHaveHeight.test.ts @@ -1,7 +1,7 @@ import { vi, test, describe, expect } from 'vitest' import { $ } from '@wdio/globals' -import { getExpectMessage, getReceived } from '../../__fixtures__/utils.js' +import { getExpectMessage } from '../../__fixtures__/utils.js' import { toHaveHeight } from '../../../src/matchers/element/toHaveHeight.js' vi.mock('@wdio/globals') @@ -84,15 +84,27 @@ describe('toHaveHeight', () => { expect(el.getSize).toHaveBeenCalledTimes(1) }) - test('not - failure', async () => { + test('not - failure - pass should be true', async () => { const el = await $('sel') el.getSize = vi.fn().mockResolvedValue(32) + const result = await toHaveHeight.call({ isNot: true }, el, 32, { wait: 0 }) - const result = await toHaveHeight.call({}, el, 32, { wait: 0 }) - const received = getReceived(result.message()) + expect(result.pass).toBe(true) // failure, boolean is inverted later because of `.not` + expect(result.message()).toEqual(`\ +Expect $(\`sel\`) not to have height - expect(received).not.toContain('not') - expect(result.pass).toBe(true) +Expected [not]: 32 +Received : 32` + ) + }) + + test('not - success - pass should be false', async () => { + const el = await $('sel') + el.getSize = vi.fn().mockResolvedValue(31) + + const result = await toHaveHeight.call({ isNot: true }, el, 32, { wait: 0 }) + + expect(result.pass).toBe(false) // success, boolean is inverted later because of `.not` }) test("should return false if sizes don't match", async () => { diff --git a/test/matchers/element/toHaveId.test.ts b/test/matchers/element/toHaveId.test.ts index 382fa3141..3c67a939c 100644 --- a/test/matchers/element/toHaveId.test.ts +++ b/test/matchers/element/toHaveId.test.ts @@ -31,20 +31,20 @@ describe('toHaveId', () => { const afterAssertion = vi.fn() beforeEach(async () => { - result = await toHaveId.call({}, el, 'an attribute', { beforeAssertion, afterAssertion }) + result = await toHaveId.call({}, el, 'an attribute', { beforeAssertion, afterAssertion, wait: 0 }) }) test('failure', () => { expect(beforeAssertion).toBeCalledWith({ matcherName: 'toHaveId', expectedValue: 'an attribute', - options: { beforeAssertion, afterAssertion } + options: { beforeAssertion, afterAssertion, wait: 0 } }) expect(result.pass).toBe(false) expect(afterAssertion).toBeCalledWith({ matcherName: 'toHaveId', expectedValue: 'an attribute', - options: { beforeAssertion, afterAssertion }, + options: { beforeAssertion, afterAssertion, wait: 0 }, result }) }) diff --git a/test/matchers/element/toHaveSize.test.ts b/test/matchers/element/toHaveSize.test.ts index de8d86146..74665b118 100755 --- a/test/matchers/element/toHaveSize.test.ts +++ b/test/matchers/element/toHaveSize.test.ts @@ -1,7 +1,6 @@ import { vi, test, describe, expect } from 'vitest' import { $ } from '@wdio/globals' -import { getExpectMessage, getReceived } from '../../__fixtures__/utils.js' import { toHaveSize } from '../../../src/matchers/element/toHaveSize.js' vi.mock('@wdio/globals') @@ -68,15 +67,18 @@ describe('toHaveSize', () => { expect(el.getSize).toHaveBeenCalledTimes(1) }) - test('not - failure', async () => { + test('not - failure - pass should be true', async () => { const el = await $('sel') el.getSize = vi.fn().mockResolvedValue({ width: 32, height: 32 }) + const result = await toHaveSize.call({ isNot: true }, el, { width: 32, height: 32 }, { wait: 0 }) - const result = await toHaveSize.call({}, el, { width: 32, height: 32 }, { wait: 0 }) - const received = getReceived(result.message()) + expect(result.pass).toBe(true) // failure, boolean is inverted later because of `.not` + expect(result.message()).toEqual(`\ +Expect $(\`sel\`) not to have size - expect(received).not.toContain('not') - expect(result.pass).toBe(true) +Expected [not]: {"height": 32, "width": 32} +Received : {"height": 32, "width": 32}` + ) }) test("should return false if sizes don't match", async () => { @@ -103,6 +105,11 @@ describe('toHaveSize', () => { const result = await toHaveSize.call({}, el, { width: 32, height: 32 }) - expect(getExpectMessage(result.message())).toContain('to have size') + expect(result.pass).toBe(false) + expect(result.message()).toEqual(`\ +Expect $(\`sel\`) to have size + +Expected: {"height": 32, "width": 32} +Received: null`) }) }) diff --git a/test/matchers/element/toHaveStyle.test.ts b/test/matchers/element/toHaveStyle.test.ts index e7c43f20e..225e5204e 100644 --- a/test/matchers/element/toHaveStyle.test.ts +++ b/test/matchers/element/toHaveStyle.test.ts @@ -1,7 +1,6 @@ import { vi, test, describe, expect } from 'vitest' import { $ } from '@wdio/globals' -import { getExpectMessage, getReceived } from '../../__fixtures__/utils.js' import { toHaveStyle } from '../../../src/matchers/element/toHaveStyle.js' vi.mock('@wdio/globals') @@ -80,16 +79,36 @@ describe('toHaveStyle', () => { expect(el.getCSSProperty).toHaveBeenCalledTimes(3) }) - test('not - failure', async () => { + test('not - failure - pass should be true', async () => { const el = await $('sel') el.getCSSProperty = vi.fn().mockImplementation((property: string) => { return { value: mockStyle[property] } }) const result = await toHaveStyle.call({ isNot: true }, el, mockStyle, { wait: 0 }) - const received = getReceived(result.message()) - expect(received).not.toContain('not') - expect(result.pass).toBe(true) + expect(result.pass).toBe(true) // failure, boolean is inverted later because of `.not` + expect(result.message()).toEqual(`\ +Expect $(\`sel\`) not to have style + +Expected [not]: {"color": "#000", "font-family": "Faktum", "font-size": "26px"} +Received : {"color": "#000", "font-family": "Faktum", "font-size": "26px"}` + ) + }) + + test('not - success - pass should be false', async () => { + const el = await $('sel') + el.getCSSProperty = vi.fn().mockImplementation((property: string) => { + return { value: mockStyle[property] } + }) + const wrongStyle: { [key: string]: string; } = { + 'font-family': 'Incorrect Font', + 'font-size': '100px', + 'color': '#fff' + } + + const result = await toHaveStyle.bind({ isNot: true })(el, wrongStyle, { wait: 1 }) + + expect(result.pass).toBe(false) // success, boolean is inverted later because of `.not` }) test('should return false if styles dont match', async () => { @@ -104,7 +123,7 @@ describe('toHaveStyle', () => { 'color': '#fff' } - const result = await toHaveStyle.bind({ isNot: true })(el, wrongStyle, { wait: 1 }) + const result = await toHaveStyle.bind({ })(el, wrongStyle, { wait: 1 }) expect(result.pass).toBe(false) }) @@ -114,7 +133,7 @@ describe('toHaveStyle', () => { return { value: mockStyle[property] } }) - const result = await toHaveStyle.bind({ isNot: true })(el, mockStyle, { wait: 1 }) + const result = await toHaveStyle.bind({})(el, mockStyle, { wait: 1 }) expect(result.pass).toBe(true) }) @@ -124,7 +143,14 @@ describe('toHaveStyle', () => { const result = await toHaveStyle.call({}, el, 'WebdriverIO' as any) - expect(getExpectMessage(result.message())).toContain('to have style') + expect(result.pass).toBe(false) + expect(result.message()).toEqual(`\ +Expect $(\`sel\`) to have style + +Expected: "WebdriverIO" +Received: {"0": "Wrong Value", "1": "Wrong Value", "10": "Wrong Value", "2": "Wrong Value", "3": "Wrong Value", "4": "Wrong Value", "5": "Wrong Value", "6": "Wrong Value", "7": "Wrong Value", "8": "Wrong Value", "9": "Wrong Value"}` + ) + }) test('success if style matches with ignoreCase', async () => { diff --git a/test/matchers/element/toHaveText.test.ts b/test/matchers/element/toHaveText.test.ts index 345791d40..92aa72a29 100755 --- a/test/matchers/element/toHaveText.test.ts +++ b/test/matchers/element/toHaveText.test.ts @@ -105,33 +105,59 @@ describe('toHaveText', () => { expect(el.getText).toHaveBeenCalledTimes(1) }) - test('not - failure', async () => { + test('not - failure - pass should be true', async () => { const el = await $('sel') - el.getText = vi.fn().mockResolvedValue('WebdriverIO') const result = await toHaveText.call({ isNot: true }, el, 'WebdriverIO', { wait: 0 }) - const received = getReceived(result.message()) - expect(received).not.toContain('not') - expect(result.pass).toBe(true) + expect(result.pass).toBe(true) // failure, boolean is inverted later because of `.not` + expect(result.message()).toEqual(`\ +Expect $(\`sel\`) not to have text + +Expected [not]: "WebdriverIO" +Received : "WebdriverIO"` + ) }) - test("should return false if texts don't match", async () => { + test('not - success - pass should be false', async () => { const el = await $('sel') el.getText = vi.fn().mockResolvedValue('WebdriverIO') - const result = await toHaveText.bind({ isNot: true })(el, 'foobar', { wait: 1 }) + const result = await toHaveText.call({ isNot: true }, el, 'not WebdriverIO', { wait: 0 }) - expect(result.pass).toBe(false) + expect(result.pass).toBe(false) // success, boolean is inverted later because of `.not` }) - test('should return true if texts match', async () => { + test('not with no trim - failure - pass should be true', async () => { + const el = await $('sel') + el.getText = vi.fn().mockResolvedValue(' WebdriverIO ') + + const result = await toHaveText.call({ isNot: true }, el, ' WebdriverIO ', { trim: false, wait: 0 }) + + expect(result.pass).toBe(true) // failure, boolean is inverted later because of `.not` + expect(result.message()).toEqual(`\ +Expect $(\`sel\`) not to have text + +Expected [not]: " WebdriverIO " +Received : " WebdriverIO "` + ) + }) + + test('not - success - pass should be false', async () => { const el = await $('sel') el.getText = vi.fn().mockResolvedValue('WebdriverIO') - const result = await toHaveText.bind({ isNot: true })(el, 'WebdriverIO', { wait: 1 }) + const result = await toHaveText.call({ isNot: true }, el, 'not WebdriverIO', { wait: 0 }) + + expect(result.pass).toBe(false) // success, boolean is inverted later because of `.not` + }) + + test('should return true if texts match', async () => { + const el = await $('sel') + el.getText = vi.fn().mockResolvedValue('WebdriverIO') + const result = await toHaveText.bind({})(el, 'WebdriverIO', { wait: 1 }) expect(result.pass).toBe(true) }) diff --git a/test/matchers/element/toHaveWidth.test.ts b/test/matchers/element/toHaveWidth.test.ts index 10ca730c2..8a13f0700 100755 --- a/test/matchers/element/toHaveWidth.test.ts +++ b/test/matchers/element/toHaveWidth.test.ts @@ -1,7 +1,7 @@ import { vi, test, describe, expect } from 'vitest' import { $ } from '@wdio/globals' -import { getExpectMessage, getReceived } from '../../__fixtures__/utils.js' +import { getExpectMessage } from '../../__fixtures__/utils.js' import { toHaveWidth } from '../../../src/matchers/element/toHaveWidth.js' vi.mock('@wdio/globals') @@ -81,15 +81,27 @@ describe('toHaveWidth', () => { expect(el.getSize).toHaveBeenCalledTimes(1) }) - test('not - failure', async () => { + test('not - failure - pass should be true', async () => { const el = await $('sel') el.getSize = vi.fn().mockResolvedValue(50) + const result = await toHaveWidth.call({ isNot: true }, el, 50, { wait: 0 }) - const result = await toHaveWidth.call({}, el, 50, { wait: 0 }) - const received = getReceived(result.message()) + expect(result.pass).toBe(true) // failure, boolean is inverted later because of `.not` + expect(result.message()).toEqual(`\ +Expect $(\`sel\`) not to have width - expect(received).not.toContain('not') - expect(result.pass).toBe(true) +Expected [not]: 50 +Received : 50` + ) + }) + + test('not - success - pass should be false', async () => { + const el = await $('sel') + el.getSize = vi.fn().mockResolvedValue(50) + + const result = await toHaveWidth.call({ isNot: true }, el, 40, { wait: 0 }) + + expect(result.pass).toBe(false) // success, boolean is inverted later because of `.not` }) test("should return false if sizes don't match", async () => { diff --git a/test/matchers/elements/toBeElementsArrayOfSize.test.ts b/test/matchers/elements/toBeElementsArrayOfSize.test.ts index 2b68dac36..e2ed9b5ef 100644 --- a/test/matchers/elements/toBeElementsArrayOfSize.test.ts +++ b/test/matchers/elements/toBeElementsArrayOfSize.test.ts @@ -46,23 +46,23 @@ describe('toBeElementsArrayOfSize', () => { test('array of size 2', async () => { const beforeAssertion = vi.fn() const afterAssertion = vi.fn() - const result = await toBeElementsArrayOfSize.call({}, els, 2, { beforeAssertion, afterAssertion }) + const result = await toBeElementsArrayOfSize.call({}, els, 2, { beforeAssertion, afterAssertion, wait: 0 }) expect(result.pass).toBe(true) expect(beforeAssertion).toBeCalledWith({ matcherName: 'toBeElementsArrayOfSize', expectedValue: 2, - options: { beforeAssertion, afterAssertion } + options: { beforeAssertion, afterAssertion, wait: 0 } }) expect(afterAssertion).toBeCalledWith({ matcherName: 'toBeElementsArrayOfSize', expectedValue: 2, - options: { beforeAssertion, afterAssertion }, + options: { beforeAssertion, afterAssertion, wait: 0 }, result }) }) test('array of size 5', async () => { els = createMockElementArray(5) - const result = await toBeElementsArrayOfSize.call({}, els, 5, {}) + const result = await toBeElementsArrayOfSize.call({}, els, 5, { wait : 0 }) expect(result.pass).toBe(true) }) }) @@ -71,7 +71,7 @@ describe('toBeElementsArrayOfSize', () => { let result: AssertionResult beforeEach(async () => { - result = await toBeElementsArrayOfSize.call({}, els, 5, {}) + result = await toBeElementsArrayOfSize.call({}, els, 5, { wait: 0 }) }) test('fails', () => { @@ -97,7 +97,7 @@ describe('toBeElementsArrayOfSize', () => { }) test('works if size contains options', async () => { - const result = await toBeElementsArrayOfSize.call({}, els, { lte: 5 }) + const result = await toBeElementsArrayOfSize.call({}, els, { lte: 5 }, { wait: 0 }) expect(result.pass).toBe(true) }) }) @@ -108,9 +108,9 @@ describe('toBeElementsArrayOfSize', () => { ['lte', 1, false], ['gte', 1, true], ['gte', 10, false], - ['gte and lte', { gte: 1, lte: 10 }, true], - ['not gte but is lte', { gte: 10, lte: 10 }, false], - ['not lte but is gte', { gte: 1, lte: 1 }, false], + ['gte and lte', { gte: 1, lte: 10, wait: 0 }, true], + ['not gte but is lte', { gte: 10, lte: 10, wait: 0 }, false], + ['not lte but is gte', { gte: 1, lte: 1, wait: 0 }, false], ])('should handle %s correctly', async (_, option, expected) => { const result = await toBeElementsArrayOfSize.call({}, els, typeof option === 'object' ? option : { [_ as string]: option }) expect(result.pass).toBe(expected) @@ -173,17 +173,17 @@ describe('toBeElementsArrayOfSize', () => { test('array of size 0', async () => { const beforeAssertion = vi.fn() const afterAssertion = vi.fn() - const result = await toBeElementsArrayOfSize.call({}, elements, 0, { beforeAssertion, afterAssertion }) + const result = await toBeElementsArrayOfSize.call({}, elements, 0, { beforeAssertion, afterAssertion, wait: 0 }) expect(result.pass).toBe(true) expect(beforeAssertion).toBeCalledWith({ matcherName: 'toBeElementsArrayOfSize', expectedValue: 0, - options: { beforeAssertion, afterAssertion } + options: { beforeAssertion, afterAssertion, wait: 0 } }) expect(afterAssertion).toBeCalledWith({ matcherName: 'toBeElementsArrayOfSize', expectedValue: 0, - options: { beforeAssertion, afterAssertion }, + options: { beforeAssertion, afterAssertion, wait: 0 }, result }) }) @@ -193,7 +193,7 @@ describe('toBeElementsArrayOfSize', () => { let result: AssertionResult beforeEach(async () => { - result = await toBeElementsArrayOfSize.call({}, elements, 5, {}) + result = await toBeElementsArrayOfSize.call({}, elements, 5, { wait: 0 }) }) test('fails', () => { @@ -222,17 +222,17 @@ describe('toBeElementsArrayOfSize', () => { test('array of size 1', async () => { const beforeAssertion = vi.fn() const afterAssertion = vi.fn() - const result = await toBeElementsArrayOfSize.call({}, elements, 1, { beforeAssertion, afterAssertion }) + const result = await toBeElementsArrayOfSize.call({}, elements, 1, { beforeAssertion, afterAssertion, wait: 0 }) expect(result.pass).toBe(true) expect(beforeAssertion).toBeCalledWith({ matcherName: 'toBeElementsArrayOfSize', expectedValue: 1, - options: { beforeAssertion, afterAssertion } + options: { beforeAssertion, afterAssertion, wait: 0 } }) expect(afterAssertion).toBeCalledWith({ matcherName: 'toBeElementsArrayOfSize', expectedValue: 1, - options: { beforeAssertion, afterAssertion }, + options: { beforeAssertion, afterAssertion, wait: 0 }, result }) }) @@ -242,7 +242,7 @@ describe('toBeElementsArrayOfSize', () => { let result: AssertionResult beforeEach(async () => { - result = await toBeElementsArrayOfSize.call({}, elements, 5, {}) + result = await toBeElementsArrayOfSize.call({}, elements, 5, { wait: 0 }) }) test('fails', () => { diff --git a/test/matchers/mock/toBeRequested.test.ts b/test/matchers/mock/toBeRequested.test.ts index 237d41a71..05ae9d7cb 100644 --- a/test/matchers/mock/toBeRequested.test.ts +++ b/test/matchers/mock/toBeRequested.test.ts @@ -4,8 +4,6 @@ import type { Matches, Mock } from 'webdriverio' import { toBeRequested } from '../../../src/matchers/mock/toBeRequested.js' -import { getExpected, getExpectMessage, getReceived, removeColors } from '../../__fixtures__/utils.js' - vi.mock('@wdio/globals') class TestMock implements Mock { @@ -69,26 +67,38 @@ describe('toBeRequested', () => { test('not to be called', async () => { const mock: Mock = new TestMock() - // expect(mock).not.toBeRequested() should pass + // expect(mock).not.toBeRequested() should pass=false const result = await toBeRequested.call({ isNot: true }, mock) - expect(result.pass).toBe(false) + expect(result.pass).toBe(false) // success, boolean is inverted later becuase of `.not` mock.calls.push(mockMatch) // expect(mock).not.toBeRequested() should fail const result4 = await toBeRequested.call({ isNot: true }, mock) - expect(result4.pass).toBe(true) + expect(result4.pass).toBe(true) // failure, boolean is inverted later because of `.not` }) test('message', async () => { const mock: Mock = new TestMock() - const message = removeColors((await toBeRequested(mock)).message()) - expect(getExpectMessage(message)).toBe('Expect mock to be called') - expect(getReceived(message)).toBe('Received: 0') - expect(getExpected(message)).toBe('Expected: ">= 1"') + const result = await toBeRequested(mock) + expect(result.pass).toBe(false) + expect(result.message()).toEqual(`\ +Expect mock to be called + +Expected: ">= 1" +Received: 0` + ) + mock.calls.push(mockMatch) const result2 = await toBeRequested.call({ isNot: true }, mock) - expect(result2.message()).toContain('Expect mock not to be called') + + expect(result2.pass).toBe(true) // failure, boolean is inverted later because of `.not` + expect(result2.message()).toEqual(`\ +Expect mock not to be called + +Expected [not]: ">= 1" +Received : 1` + ) }) }) diff --git a/test/matchers/mock/toBeRequestedTimes.test.ts b/test/matchers/mock/toBeRequestedTimes.test.ts index 22cd9eb78..65df6b0bc 100644 --- a/test/matchers/mock/toBeRequestedTimes.test.ts +++ b/test/matchers/mock/toBeRequestedTimes.test.ts @@ -3,7 +3,6 @@ import { vi, test, describe, expect } from 'vitest' import type { Matches, Mock } from 'webdriverio' import { toBeRequestedTimes } from '../../../src/matchers/mock/toBeRequestedTimes.js' -import { removeColors, getReceived, getExpected, getExpectMessage } from '../../__fixtures__/utils.js' vi.mock('@wdio/globals') @@ -102,39 +101,54 @@ describe('toBeRequestedTimes', () => { // expect(mock).not.toBeRequestedTimes(0) should fail const result = await toBeRequestedTimes.call({ isNot: true }, mock, 0) - expect(result.pass).toBe(true) + expect(result.pass).toBe(true) // failure, boolean inverted later because of .not + expect(result.message()).toEqual(`\ +Expect mock not to be called 0 times + +Expected [not]: 0 +Received : 0` + ) // expect(mock).not.toBeRequestedTimes(1) should pass const result2 = await toBeRequestedTimes.call({ isNot: true }, mock, 1) - expect(result2.pass).toBe(false) + expect(result2.pass).toBe(false) // success, boolean inverted later because of .not mock.calls.push(mockMatch) // expect(mock).not.toBeRequestedTimes(0) should pass const result3 = await toBeRequestedTimes.call({ isNot: true }, mock, 0) - expect(result3.pass).toBe(false) + expect(result3.pass).toBe(false) // success, boolean inverted later because of .not // expect(mock).not.toBeRequestedTimes(1) should fail const result4 = await toBeRequestedTimes.call({ isNot: true }, mock, 1) - expect(result4.pass).toBe(true) + expect(result4.pass).toBe(true) // failure, boolean inverted later because of .not + expect(result4.message()).toEqual(`\ +Expect mock not to be called 1 time + +Expected [not]: 1 +Received : 1` + ) }) test('message', async () => { const mock: Mock = new TestMock() - const result = await toBeRequestedTimes.call({}, mock, 0) + const result = await toBeRequestedTimes.call({}, mock, 0, { wait: 1 }) expect(result.message()).toContain('Expect mock to be called 0 times') - const result2 = await toBeRequestedTimes.call({}, mock, 1) + const result2 = await toBeRequestedTimes.call({}, mock, 1, { wait: 1 }) expect(result2.message()).toContain('Expect mock to be called 1 time') - const result3 = await toBeRequestedTimes.call({}, mock, 2) + const result3 = await toBeRequestedTimes.call({}, mock, 2, { wait: 1 }) expect(result3.message()).toContain('Expect mock to be called 2 times') - const result4 = await toBeRequestedTimes.call({}, mock, { gte: 3 }) - const message4 = removeColors(result4.message()) - expect(getExpectMessage(message4)).toBe('Expect mock to be called times') - expect(getExpected(message4)).toBe('Expected: ">= 3"') - expect(getReceived(message4)).toBe('Received: 0') + const result4 = await toBeRequestedTimes.call({}, mock, { gte: 3 }, { wait: 1 }) + expect(result4.pass).toBe(false) + expect(result4.message()).toEqual(`\ +Expect mock to be called times + +Expected: ">= 3" +Received: 0` + ) }) }) diff --git a/test/matchers/mock/toBeRequestedWith.test.ts b/test/matchers/mock/toBeRequestedWith.test.ts index 0fd51ff70..d4c8adab2 100644 --- a/test/matchers/mock/toBeRequestedWith.test.ts +++ b/test/matchers/mock/toBeRequestedWith.test.ts @@ -163,7 +163,7 @@ describe('toBeRequestedWith', () => { expect(result.pass).toBe(false) }) - test('wait for NOT failure, empty params', async () => { + test('wait for NOT - failure with empty params and pass expected to be true', async () => { const mock: any = new TestMock() mock.calls.push({ ...mockGet }, { ...mockPost }) setTimeout(() => { @@ -171,10 +171,16 @@ describe('toBeRequestedWith', () => { }, 10) const result = await toBeRequestedWith.call({ isNot: true }, mock, {}) - expect(result.pass).toBe(true) + expect(result.pass).toBe(true) // failure, boolean inverted later because of .not + expect(result.message()).toEqual(`\ +Expect mock not to be called with + +Expected [not]: {} +Received : {}` + ) }) - test('wait for NOT success', async () => { + test('wait for NOT - success with pass expected to be false', async () => { const mock: any = new TestMock() setTimeout(() => { @@ -182,7 +188,7 @@ describe('toBeRequestedWith', () => { }, 10) const result = await toBeRequestedWith.call({ isNot: true }, mock, { method: 'DELETE' }) - expect(result.pass).toBe(false) + expect(result.pass).toBe(false) // success, boolean inverted later because of .not }) const scenarios: Scenario[] = [ diff --git a/test/softAssertions.test.ts b/test/softAssertions.test.ts index 69e07d1c0..d31d65c9b 100644 --- a/test/softAssertions.test.ts +++ b/test/softAssertions.test.ts @@ -21,7 +21,7 @@ describe('Soft Assertions', () => { const softService = SoftAssertService.getInstance() softService.setCurrentTest('test-1', 'test name', 'test file') - await expectWdio.soft(el).toHaveText('Expected Text') + await expectWdio.soft(el).toHaveText('Expected Text', { wait: 0 }) // Verify the failure was recorded const failures = expectWdio.getSoftFailures() @@ -36,7 +36,7 @@ describe('Soft Assertions', () => { softService.setCurrentTest('test-2', 'test name', 'test file') // This should not throw even though it fails - await expectWdio.soft(el).not.toHaveText('Actual Text') + await expectWdio.soft(el).not.toHaveText('Actual Text', { wait: 0 }) // Verify the failure was recorded const failures = expectWdio.getSoftFailures() @@ -50,9 +50,9 @@ describe('Soft Assertions', () => { softService.setCurrentTest('test-3', 'test name', 'test file') // These should not throw even though they fail - await expectWdio.soft(el).toHaveText('First Expected') - await expectWdio.soft(el).toHaveText('Second Expected') - await expectWdio.soft(el).toHaveText('Third Expected') + await expectWdio.soft(el).toHaveText('First Expected', { wait: 0 }) + await expectWdio.soft(el).toHaveText('Second Expected', { wait: 0 }) + await expectWdio.soft(el).toHaveText('Third Expected', { wait: 0 }) // Verify all failures were recorded const failures = expectWdio.getSoftFailures() @@ -170,8 +170,8 @@ describe('Soft Assertions', () => { softService.setCurrentTest('boolean-test', 'boolean test', 'test file') // Test boolean matcher - await expectWdio.soft(el).toBeDisplayed() - await expectWdio.soft(el).toBeClickable() + await expectWdio.soft(el).toBeDisplayed({ wait: 0 }) + await expectWdio.soft(el).toBeClickable({ wait: 0 }) const failures = expectWdio.getSoftFailures() expect(failures.length).toBe(2) @@ -183,7 +183,7 @@ describe('Soft Assertions', () => { const softService = SoftAssertService.getInstance() softService.setCurrentTest('attribute-test', 'attribute test', 'test file') - await expectWdio.soft(el).toHaveAttribute('class', 'expected-class') + await expectWdio.soft(el).toHaveAttribute('class', 'expected-class', { wait: 0 }) const failures = expectWdio.getSoftFailures() expect(failures.length).toBe(1) @@ -194,7 +194,7 @@ describe('Soft Assertions', () => { const softService = SoftAssertService.getInstance() softService.setCurrentTest('options-test', 'options test', 'test file') - await expectWdio.soft(el).toHaveText('Expected', { ignoreCase: true, wait: 1000 }) + await expectWdio.soft(el).toHaveText('Expected', { ignoreCase: true, wait: 0 }) const failures = expectWdio.getSoftFailures() expect(failures.length).toBe(1) @@ -208,12 +208,12 @@ describe('Soft Assertions', () => { // Test 1 softService.setCurrentTest('isolation-test-1', 'test 1', 'file1') - await expectWdio.soft(el).toHaveText('Expected Text 1') + await expectWdio.soft(el).toHaveText('Expected Text 1', { wait: 0 }) expect(expectWdio.getSoftFailures().length).toBe(1) // Test 2 - should have separate failures softService.setCurrentTest('isolation-test-2', 'test 2', 'file2') - await expectWdio.soft(el).toHaveText('Expected Text 2') + await expectWdio.soft(el).toHaveText('Expected Text 2', { wait: 0 }) // Test 2 should only see its own failure expect(expectWdio.getSoftFailures('isolation-test-2').length).toBe(1) @@ -245,9 +245,9 @@ describe('Soft Assertions', () => { // Fire multiple assertions rapidly const promises = [ - expectWdio.soft(el).toHaveText('Expected 1'), - expectWdio.soft(el).toBeDisplayed(), - expectWdio.soft(el).toBeClickable() + expectWdio.soft(el).toHaveText('Expected 1', { wait: 0 }), + expectWdio.soft(el).toBeDisplayed({ wait: 0 }), + expectWdio.soft(el).toBeClickable({ wait: 0 }) ] await Promise.all(promises) @@ -291,7 +291,7 @@ describe('Soft Assertions', () => { softService.setCurrentTest('long-error-test', 'long error', 'test file') const veryLongText = 'A'.repeat(10000) - await expectWdio.soft(el).toHaveText(veryLongText) + await expectWdio.soft(el).toHaveText(veryLongText, { wait: 0 }) const failures = expectWdio.getSoftFailures() expect(failures.length).toBe(1) @@ -299,12 +299,13 @@ describe('Soft Assertions', () => { }) it('should handle null/undefined values gracefully', async () => { + vi.mocked(el.getAttribute).mockResolvedValue(null as any) const softService = SoftAssertService.getInstance() softService.setCurrentTest('null-test', 'null test', 'test file') // Test with null/undefined values - await expectWdio.soft(el).toHaveText(null as any) - await expectWdio.soft(el).toHaveAttribute('class', undefined as any) + await expectWdio.soft(el).toHaveText(null as any, { wait: 0 }) + await expectWdio.soft(el).toHaveAttribute('class') const failures = expectWdio.getSoftFailures() expect(failures.length).toBe(2) @@ -314,7 +315,7 @@ describe('Soft Assertions', () => { const softService = SoftAssertService.getInstance() softService.setCurrentTest('location-test', 'location test', 'test file') - await expectWdio.soft(el).toHaveText('Expected Text') + await expectWdio.soft(el).toHaveText('Expected Text', { wait: 0 }) const failures = expectWdio.getSoftFailures() expect(failures.length).toBe(1) @@ -333,7 +334,7 @@ describe('Soft Assertions', () => { // Generate many failures const promises = [] for (let i = 0; i < 150; i++) { - promises.push(expectWdio.soft(el).toHaveText(`Expected ${i}`)) + promises.push(expectWdio.soft(el).toHaveText(`Expected ${i}`), { wait: 0 }) } await Promise.all(promises) diff --git a/test/utils.test.ts b/test/utils.test.ts index 444a8e61f..cba27af79 100644 --- a/test/utils.test.ts +++ b/test/utils.test.ts @@ -1,5 +1,5 @@ -import { describe, test, expect } from 'vitest' -import { compareNumbers, compareObject, compareText, compareTextWithArray } from '../src/utils.js' +import { describe, test, expect, vi } from 'vitest' +import { compareNumbers, compareObject, compareText, compareTextWithArray, waitUntil } from '../src/utils' describe('utils', () => { describe('compareText', () => { @@ -158,4 +158,96 @@ describe('utils', () => { expect(compareObject([{ 'foo': 'bar' }], { 'foo': 'bar' }).result).toBe(false) }) }) + + describe('waitUntil', () => { + + describe('should be pass=true for normal success and pass=true for `isNot` failure', () => { + test('should return true when condition is met', async () => { + const condition = vi.fn().mockResolvedValue(true) + + const result = await waitUntil(condition, { wait: 1000, interval: 100 }) + + expect(result).toBe(true) + }) + + test('should return true with wait 0', async () => { + const condition = vi.fn().mockResolvedValue(true) + + const result = await waitUntil(condition, { wait: 0 }) + + expect(result).toBe(true) + }) + + test('should return true when condition is met within wait time', async () => { + const condition = vi.fn().mockResolvedValueOnce(false).mockResolvedValueOnce(false).mockResolvedValueOnce(true) + + const result = await waitUntil(condition, { wait: 1000, interval: 50 }) + + expect(result).toBe(true) + expect(condition).toBeCalledTimes(3) + }) + + test('should return true when condition errors but still is met within wait time', async () => { + const condition = vi.fn().mockRejectedValueOnce(new Error('Test error')).mockRejectedValueOnce(new Error('Test error')).mockResolvedValueOnce(true) + + const result = await waitUntil(condition, { wait: 1000, interval: 50 }) + + expect(result).toBe(true) + expect(condition).toBeCalledTimes(3) + }) + + test('should use default options when not provided', async () => { + const condition = vi.fn().mockResolvedValue(true) + + const result = await waitUntil(condition) + + expect(result).toBe(true) + }) + }) + + describe('should be pass=false for normal failure or pass=false for `isNot` success', () => { + + test('should return false when condition is not met within wait time', async () => { + const condition = vi.fn().mockResolvedValue(false) + + const result = await waitUntil(condition, { wait: 200, interval: 50 }) + + expect(result).toBe(false) + }) + + test('should return false when condition is not met and wait is 0', async () => { + const condition = vi.fn().mockResolvedValue(false) + + const result = await waitUntil(condition, { wait: 0 }) + + expect(result).toBe(false) + }) + + test('should return false if condition throws but still return false', async () => { + const condition = vi.fn().mockRejectedValueOnce(new Error('Always failing')).mockRejectedValueOnce(new Error('Always failing')).mockResolvedValue(false) + + const result = await waitUntil(condition, { wait: 200, interval: 50 }) + + expect(result).toBe(false) + expect(condition).toBeCalledTimes(4) + }) + }) + + describe('when condition throws', () => { + const error = new Error('failing') + + test('should throw with wait', async () => { + const condition = vi.fn().mockRejectedValue(error) + + await expect(() => waitUntil(condition, { wait: 200, interval: 50 })).rejects.toThrowError('failing') + }) + + test('should throw with wait 0', async () => { + const condition = vi.fn().mockRejectedValue(error) + + await expect(() => waitUntil(condition, { wait: 0 })).rejects.toThrowError('failing') + + }) + }) + }) }) diff --git a/types/expect-webdriverio.d.ts b/types/expect-webdriverio.d.ts index 74963f437..9b729ff34 100644 --- a/types/expect-webdriverio.d.ts +++ b/types/expect-webdriverio.d.ts @@ -188,11 +188,14 @@ interface WdioElementOrArrayMatchers<_R, ActualT = unknown> { /** * `WebdriverIO.Element` -> `getProperty` */ - toHaveElementProperty: FnWhenElementOrArrayLike, - value?: unknown, - options?: ExpectWebdriverIO.StringOptions - ) => Promise> + toHaveElementProperty: FnWhenElementOrArrayLike< + ActualT, + ( + property: string, + value?: string | RegExp | WdioAsymmetricMatcher | null, + options?: ExpectWebdriverIO.StringOptions, + ) => Promise + > /** * `WebdriverIO.Element` -> `getProperty` value