Skip to content
Open
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
1 change: 1 addition & 0 deletions src/matchers.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export * from './matchers/browser/toHaveClipboardText.js'
export * from './matchers/browser/toHaveLocalStorageItem.js'
export * from './matchers/browser/toHaveTitle.js'
export * from './matchers/browser/toHaveUrl.js'
export * from './matchers/element/toBeClickable.js'
Expand Down
54 changes: 54 additions & 0 deletions src/matchers/browser/toHaveLocalStorageItem.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { waitUntil, enhanceError, compareText } from '../../utils.js'
import { DEFAULT_OPTIONS } from '../../constants.js'

export async function toHaveLocalStorageItem(
browser: WebdriverIO.Browser,
key: string,
expectedValue?: string | RegExp | ExpectWebdriverIO.PartialMatcher,
options: ExpectWebdriverIO.StringOptions = DEFAULT_OPTIONS
) {
const isNot = this.isNot
const { expectation = 'localStorage item', verb = 'have' } = this

await options.beforeAssertion?.({
matcherName: 'toHaveLocalStorageItem',
expectedValue: expectedValue ? [key, expectedValue] : key,
options,
})
let actual
const pass = await waitUntil(async () => {
actual = await browser.execute((storageKey) => {
return localStorage.getItem(storageKey)
}, key)
// if no expected value is provided, we just check if the item exists
if (expectedValue === undefined) {
return actual !== null
}
// no localStorage item found
if (actual === null) {
return false
}
return compareText(actual, expectedValue, options).result
}, isNot, options)
const message = enhanceError(
'browser',
expectedValue !== undefined ? expectedValue : `localStorage item "${key}"`,
actual,
this,
verb,
expectation,
key,
options
)
const result: ExpectWebdriverIO.AssertionResult = {
pass,
message: () => message
}
await options.afterAssertion?.({
matcherName: 'toHaveLocalStorageItem',
expectedValue: expectedValue ? [key, expectedValue] : key,
options,
result
})
return result
}
1 change: 1 addition & 0 deletions test/matchers.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { matchers, expect as expectLib } from '../src/index.js'
const ALL_MATCHERS = [
// browser
'toHaveClipboardText',
'toHaveLocalStorageItem',
'toHaveTitle',
'toHaveUrl',

Expand Down
147 changes: 147 additions & 0 deletions test/matchers/browser/toHaveLocalStorageItem.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
import { vi, expect, describe, it, beforeEach } from 'vitest'
import { browser } from '@wdio/globals'
import { toHaveLocalStorageItem } from '../../../src/matchers/browser/toHaveLocalStorageItem.js'

vi.mock('@wdio/globals')

const beforeAssertion = vi.fn()
const afterAssertion = vi.fn()

describe('toHaveLocalStorageItem', () => {
beforeEach(() => {
vi.clearAllMocks()
})

it('passes when localStorage item exists with correct value', async () => {
browser.execute = vi.fn().mockResolvedValue('someLocalStorageValue')

const result = await toHaveLocalStorageItem.call(
{}, // this context
browser,
'someLocalStorageKey',
'someLocalStorageValue',
{ ignoreCase: true, beforeAssertion, afterAssertion }
)

expect(result.pass).toBe(true)

// Check that browser.execute was called with correct arguments
expect(browser.execute).toHaveBeenCalledWith(
expect.any(Function),
'someLocalStorageKey'
)

expect(beforeAssertion).toHaveBeenCalledWith({
matcherName: 'toHaveLocalStorageItem',
expectedValue: ['someLocalStorageKey', 'someLocalStorageValue'],
options: { ignoreCase: true, beforeAssertion, afterAssertion }
})

expect(afterAssertion).toHaveBeenCalledWith({
matcherName: 'toHaveLocalStorageItem',
expectedValue: ['someLocalStorageKey', 'someLocalStorageValue'],
options: { ignoreCase: true, beforeAssertion, afterAssertion },
result
})
})

it('fails when localStorage item has different value', async () => {
browser.execute = vi.fn().mockResolvedValue('actualValue')

const result = await toHaveLocalStorageItem.call(
{},
browser,
'someKey',
'expectedValue'
)

expect(result.pass).toBe(false)
})

it('fails when localStorage item does not exist', async () => {
// Mock browser.execute to return null (item doesn't exist)
browser.execute = vi.fn().mockResolvedValue(null)

const result = await toHaveLocalStorageItem.call(
{},
browser,
'nonExistentKey',
'someValue'
)

expect(result.pass).toBe(false)
expect(browser.execute).toHaveBeenCalledWith(
expect.any(Function),
'nonExistentKey'
)
})

it('passes when only checking key existence', async () => {
// Mock browser.execute to return any non-null value
browser.execute = vi.fn().mockResolvedValue('anyValue')

const result = await toHaveLocalStorageItem.call(
{},
browser,
'existingKey'
// no expectedValue parameter
)

expect(result.pass).toBe(true)
})

it('ignores case when ignoreCase is true', async () => {
browser.execute = vi.fn().mockResolvedValue('UPPERCASE')

const result = await toHaveLocalStorageItem.call(
{},
browser,
'key',
'uppercase',
{ ignoreCase: true }
)

expect(result.pass).toBe(true)
})

it('trims whitespace when trim is true', async () => {
browser.execute = vi.fn().mockResolvedValue(' value ')

const result = await toHaveLocalStorageItem.call(
{},
browser,
'key',
'value',
{ trim: true }
)

expect(result.pass).toBe(true)
})

it('checks containing when containing is true', async () => {
browser.execute = vi.fn().mockResolvedValue('this is a long value')

const result = await toHaveLocalStorageItem.call(
{},
browser,
'key',
'long',
{ containing: true }
)

expect(result.pass).toBe(true)
})

it('passes when localStorage value matches regex', async () => {
browser.execute = vi.fn().mockResolvedValue('user_123')

const result = await toHaveLocalStorageItem.call(
{},
browser,
'userId',
/^user_\d+$/
)

expect(result.pass).toBe(true)
})
})