Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 17 additions & 4 deletions __mocks__/@wdio/globals.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { vi } from 'vitest'

const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms))

function beFn() {
Expand Down Expand Up @@ -32,9 +34,7 @@ function getSizeFn(property?: 'height' | 'width') {
return this._size ? this._size(property) : undefined
}

const element = {
$,
$$,
const elementMethods = {
isDisplayed: beFn,
isDisplayedInViewport: beFn,
isExisting: beFn,
Expand All @@ -50,23 +50,36 @@ const element = {
getSize: getSizeFn,
}

const element = {
$,
$$,
...elementMethods
}

export function $(selector) {
const el: any = {
...element,
selector
}
for (const [prop, method] of Object.entries(elementMethods)) {
el[prop] = vi.fn(method)
}
el.getElement = async () => el
return el
}

export function $$(selector) {
const length = this?._length || 2
const els: any = Array(length).map((_, index) => {
return {
const el = {
...element,
selector,
index
}
for (const [prop, method] of Object.entries(elementMethods)) {
el[prop] = vi.fn(method)
}
return el
})
// Required to refetch
const parent: any = element
Expand Down
45 changes: 21 additions & 24 deletions src/matchers/element/toBeDisplayed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,33 +2,17 @@ import { executeCommandBe } from '../../utils.js'
import { DEFAULT_OPTIONS } from '../../constants.js'
import type { WdioElementMaybePromise } from '../../types.js'

interface ToBeDisplayedOptions {
/**
* `true` to check if the element is within the viewport. false by default.
*/
withinViewport?: boolean
/**
* `true` to check if the element content-visibility property has (or inherits) the value auto,
* and it is currently skipping its rendering. `true` by default.
* @default true
*/
contentVisibilityAuto?: boolean
/**
* `true` to check if the element opacity property has (or inherits) a value of 0. `true` by default.
* @default true
*/
opacityProperty?: boolean
/**
* `true` to check if the element is invisible due to the value of its visibility property. `true` by default.
* @default true
*/
visibilityProperty?: boolean
const DEFAULT_OPTIONS_DISPLAYED: ExpectWebdriverIO.ToBeDisplayedOptions = {
...DEFAULT_OPTIONS,
withinViewport: false,
contentVisibilityAuto: true,
opacityProperty: true,
visibilityProperty: true
}

export async function toBeDisplayed(
received: WdioElementMaybePromise,
{ withinViewport = false, contentVisibilityAuto = true, opacityProperty = true, visibilityProperty = true }: ToBeDisplayedOptions = {},
options: ExpectWebdriverIO.CommandOptions = DEFAULT_OPTIONS
options: ExpectWebdriverIO.ToBeDisplayedOptions = DEFAULT_OPTIONS_DISPLAYED,
) {
this.expectation = this.expectation || 'displayed'

Expand All @@ -37,7 +21,20 @@ export async function toBeDisplayed(
options,
})

const result = await executeCommandBe.call(this, received, el => el?.isDisplayed({ withinViewport, contentVisibilityAuto, opacityProperty, visibilityProperty }), options)
const {
withinViewport,
contentVisibilityAuto,
opacityProperty,
visibilityProperty,
...commandOptions
} = { ...DEFAULT_OPTIONS_DISPLAYED, ...options }

const result = await executeCommandBe.call(this, received, el => el?.isDisplayed({
withinViewport,
contentVisibilityAuto,
opacityProperty,
visibilityProperty
}), commandOptions)

await options.afterAssertion?.({
matcherName: 'toBeDisplayed',
Expand Down
87 changes: 80 additions & 7 deletions test/matchers/element/toBeDisplayed.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,18 @@ 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'

vi.mock('@wdio/globals')
vi.mock('../../../src/utils.js', async (importOriginal) => {
// eslint-disable-next-line @typescript-eslint/consistent-type-imports
const actual = await importOriginal<typeof import('../../../src/utils.js')>()
return {
...actual,
executeCommandBe: vi.fn(actual.executeCommandBe)
}
})

describe('toBeDisplayed', () => {
/**
Expand All @@ -24,7 +34,21 @@ describe('toBeDisplayed', () => {

const beforeAssertion = vi.fn()
const afterAssertion = vi.fn()
const result = await toBeDisplayed.call({}, el, {}, { beforeAssertion, afterAssertion })

const result = await toBeDisplayed.call({}, el, { beforeAssertion, afterAssertion })

expect(el.isDisplayed).toHaveBeenCalledWith(
{
withinViewport: false,
contentVisibilityAuto: true,
opacityProperty: true,
visibilityProperty: true
}
)
expect(executeCommandBe).toHaveBeenCalledWith(el, expect.anything(), expect.objectContaining({
wait: DEFAULT_OPTIONS.wait,
interval: DEFAULT_OPTIONS.interval
}))
expect(result.pass).toBe(true)
expect(el._attempts).toBe(0)
expect(beforeAssertion).toBeCalledWith({
Expand All @@ -38,6 +62,28 @@ describe('toBeDisplayed', () => {
})
})

test('success with ToBeDisplayed and command options', async () => {
const el: any = await $('sel')
el._value = function (): boolean {
return true
}
const result = await toBeDisplayed.call({}, el, { wait: 1, withinViewport: true })

expect(el.isDisplayed).toHaveBeenCalledWith(
{
withinViewport: true,
contentVisibilityAuto: true,
opacityProperty: true,
visibilityProperty: true
}
)
expect(executeCommandBe).toHaveBeenCalledWith(el, expect.anything(), expect.objectContaining({
wait: 1,
interval: DEFAULT_OPTIONS.interval
}))
expect(result.pass).toBe(true)
})

test('wait but failure', async () => {
const el: any = await $('sel')
el._value = function (): boolean {
Expand Down Expand Up @@ -69,7 +115,8 @@ describe('toBeDisplayed', () => {
return false
}

const result = await toBeDisplayed.call({}, el, {}, { wait: 0 })
const result = await toBeDisplayed.call({}, el, { wait: 0 })

expect(result.pass).toBe(false)
expect(el._attempts).toBe(1)
})
Expand All @@ -82,7 +129,21 @@ describe('toBeDisplayed', () => {
return true
}

const result = await toBeDisplayed.call({}, el, {}, { wait: 0 })
const result = await toBeDisplayed.call({}, el, { wait: 0 })

expect(el.isDisplayed).toHaveBeenCalledWith(
{
withinViewport: false,
contentVisibilityAuto: true,
opacityProperty: true,
visibilityProperty: true
}
)
expect(executeCommandBe).toHaveBeenCalledWith(el, expect.anything(), expect.objectContaining({
wait: 0,
interval: DEFAULT_OPTIONS.interval
}))

expect(result.pass).toBe(true)
expect(el._attempts).toBe(1)
})
Expand All @@ -92,7 +153,7 @@ describe('toBeDisplayed', () => {
el._value = function (): boolean {
return true
}
const result = await toBeDisplayed.call({ isNot: true }, el, {}, { wait: 0 })
const result = await toBeDisplayed.call({ isNot: true }, el, { wait: 0 })
const received = getReceived(result.message())

expect(received).not.toContain('not')
Expand All @@ -104,7 +165,7 @@ describe('toBeDisplayed', () => {
el._value = function (): boolean {
return false
}
const result = await toBeDisplayed.call({ isNot: true }, el, {}, { wait: 0 })
const result = await toBeDisplayed.call({ isNot: true }, el, { wait: 0 })
const received = getReceived(result.message())

expect(received).toContain('not')
Expand All @@ -116,7 +177,7 @@ describe('toBeDisplayed', () => {
el._value = function (): boolean {
return true
}
const result = await toBeDisplayed.call({ isNot: true }, el, {}, { wait: 1 })
const result = await toBeDisplayed.call({ isNot: true }, el, { wait: 1 })
const received = getReceived(result.message())

expect(received).not.toContain('not')
Expand All @@ -128,9 +189,21 @@ describe('toBeDisplayed', () => {
el._value = function (): boolean {
return false
}
const result = await toBeDisplayed.call({ isNot: true }, el, {}, { wait: 1 })
const result = await toBeDisplayed.call({ isNot: true }, el, { wait: 1 })
const received = getReceived(result.message())

expect(el.isDisplayed).toHaveBeenCalledWith(
{
withinViewport: false,
contentVisibilityAuto: true,
opacityProperty: true,
visibilityProperty: true
}
)
expect(executeCommandBe).toHaveBeenCalledWith(el, expect.anything(), expect.objectContaining({
wait: 1,
interval: DEFAULT_OPTIONS.interval
}))
expect(received).toContain('not')
expect(result.pass).toBe(false)
})
Expand Down
28 changes: 27 additions & 1 deletion types/expect-webdriverio.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ interface WdioElementOrArrayMatchers<_R, ActualT = unknown> {
/**
* `WebdriverIO.Element` -> `isDisplayed`
*/
toBeDisplayed: FnWhenElementOrArrayLike<ActualT, (options?: ExpectWebdriverIO.CommandOptions) => Promise<void>>
toBeDisplayed: FnWhenElementOrArrayLike<ActualT, (options?: ExpectWebdriverIO.ToBeDisplayedOptions) => Promise<void>>

/**
* `WebdriverIO.Element` -> `isExisting`
Expand Down Expand Up @@ -702,6 +702,32 @@ declare namespace ExpectWebdriverIO {
gte?: number
}

interface ToBeDisplayedOptions extends CommandOptions {
/**
* `true` to check if the element is within the viewport. false by default.
*/
withinViewport?: boolean

/**
* `true` to check if the element content-visibility property has (or inherits) the value auto,
* and it is currently skipping its rendering. `true` by default.
* @default true
*/
contentVisibilityAuto?: boolean

/**
* `true` to check if the element opacity property has (or inherits) a value of 0. `true` by default.
* @default true
*/
opacityProperty?: boolean

/**
* `true` to check if the element is invisible due to the value of its visibility property. `true` by default.
* @default true
*/
visibilityProperty?: boolean
}

type RequestedWith = {
url?: string | ExpectWebdriverIO.PartialMatcher<string>| ((url: string) => boolean)
method?: string | Array<string>
Expand Down
9 changes: 5 additions & 4 deletions vitest.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export default defineConfig({
exclude: [
'**/build/**',
'**/__fixtures__/**',
'**/__mocks__/**',
'**/*.test.ts',
'lib',
'test-types',
Expand All @@ -29,10 +30,10 @@ export default defineConfig({
'types-checks-filter-out-node_modules.js',
],
thresholds: {
lines: 87.3,
functions: 85.8,
statements: 87,
branches: 78.6,
lines: 88.4,
functions: 86.9,
statements: 88.3,
branches: 79.4,
}
}
}
Expand Down