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
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export const performAction2 = async (locator, fnName, options) => {
Assert.string(fnName)
Assert.object(options)
const fn = ElementActions[fnName]
const element = QuerySelector.querySelectorWithOptions(locator._selector, {
const element = QuerySelector.querySelectorWithOptions(locator._parsed, {
hasText: locator._hasText,
nth: locator._nth,
})
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as QuerySelector from './QuerySelector.ts'

export const toHaveText = (locator) => {
const element = QuerySelector.querySelectorWithOptions(locator._selector, {
const element = QuerySelector.querySelectorWithOptions(locator._parsed, {
hasText: locator._hasText,
nth: locator._nth,
})
Expand All @@ -18,7 +18,7 @@ export const toHaveText = (locator) => {
}

export const toHaveAttribute = (locator, { key, value }) => {
const element = QuerySelector.querySelectorWithOptions(locator._selector, {
const element = QuerySelector.querySelectorWithOptions(locator._parsed, {
hasText: locator._hasText,
nth: locator._nth,
})
Expand All @@ -36,7 +36,7 @@ export const toHaveAttribute = (locator, { key, value }) => {
}

export const toHaveCount = (locator) => {
const elements = QuerySelector.querySelector(locator._selector)
const elements = QuerySelector.querySelector(locator._parsed)
const actualCount = elements.length
return {
actual: actualCount,
Expand Down Expand Up @@ -65,7 +65,7 @@ export const toBeFocused = (locator) => {
}

export const toHaveClass = (locator, { className }) => {
const [element] = QuerySelector.querySelector(locator._selector)
const [element] = QuerySelector.querySelector(locator._parsed)
if (!element) {
return {
actual: '',
Expand All @@ -79,7 +79,7 @@ export const toHaveClass = (locator, { className }) => {
}

export const toHaveId = (locator) => {
const [element] = QuerySelector.querySelector(locator._selector)
const [element] = QuerySelector.querySelector(locator._parsed)
if (!element) {
return {
actual: '',
Expand All @@ -93,7 +93,7 @@ export const toHaveId = (locator) => {
}

export const toHaveCss = (locator, { key }) => {
const [element] = QuerySelector.querySelector(locator._selector)
const [element] = QuerySelector.querySelector(locator._parsed)
if (!element) {
return {
actual: '',
Expand All @@ -109,7 +109,7 @@ export const toHaveCss = (locator, { key }) => {
}

export const toHaveJSProperty = (locator, { key }) => {
const [element] = QuerySelector.querySelector(locator._selector)
const [element] = QuerySelector.querySelector(locator._parsed)
if (!element) {
return {
actual: '',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export type ParsedCssSelector =
| {
readonly text: string
readonly type: 'text'
}
| {
readonly selector: string
readonly type: 'css'
}
| {
readonly selector: string
readonly text: string
readonly type: 'css+text'
}
51 changes: 33 additions & 18 deletions packages/renderer-process/src/parts/TestFrameWork/QuerySelector.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { htmlElements } from './HtmlElements.ts'
import type { ParsedCssSelector } from './ParsedCssSelector.ts'

const querySelectorByText = (root, text) => {
let node
Expand All @@ -22,22 +23,17 @@ const isElement = (selector) => {
return htmlElements.includes(selector)
}

export const querySelector = (selector) => {
if (typeof selector !== 'string') {
throw new TypeError('selector must be of type string')
const selectorToString = (parsedSelector: ParsedCssSelector) => {
if (parsedSelector.type === 'text') {
return `text=${parsedSelector.text}`
}
if (selector.startsWith('text=')) {
return querySelectorByText(document.body, selector.slice('text='.length))
}
if (selector.includes('text=')) {
const index = selector.indexOf('text=')
const elements = querySelectorByCss(selector.slice(0, index))
const text = selector.slice(index + 'text='.length)
return elements.flatMap((element) => {
return querySelectorByText(element, text)
})
// for(const element of elements)
if (parsedSelector.type === 'css+text') {
return `${parsedSelector.selector} text=${parsedSelector.text}`
}
return parsedSelector.selector
}

const queryCssSelector = (selector: string) => {
if (selector.startsWith('.')) {
return querySelectorByCss(selector)
}
Expand All @@ -59,8 +55,27 @@ export const querySelector = (selector) => {
throw new Error(`unsupported selector: ${selector}`)
}

export const querySelectorWithOptions = (selector, { hasText = '', nth = -1 } = {}) => {
let elements = querySelector(selector)
export const querySelector = (parsedSelector: ParsedCssSelector) => {
if (!parsedSelector || typeof parsedSelector !== 'object' || Array.isArray(parsedSelector)) {
throw new TypeError('parsedSelector must be of type object')
}
if (parsedSelector.type === 'text') {
return querySelectorByText(document.body, parsedSelector.text)
}
if (parsedSelector.type === 'css+text') {
const elements = queryCssSelector(parsedSelector.selector)
return elements.flatMap((element) => {
return querySelectorByText(element, parsedSelector.text)
})
}
if (parsedSelector.type === 'css') {
return queryCssSelector(parsedSelector.selector)
}
throw new Error(`unsupported selector: ${selectorToString(parsedSelector)}`)
}

export const querySelectorWithOptions = (parsedSelector: ParsedCssSelector, { hasText = '', nth = -1 } = {}) => {
let elements = querySelector(parsedSelector)
if (hasText) {
elements = elements.filter((element) => element.textContent === hasText)
}
Expand All @@ -72,11 +87,11 @@ export const querySelectorWithOptions = (selector, { hasText = '', nth = -1 } =
return element
}
if (nth === -1) {
throw new Error(`too many matching elements for ${selector}, matching ${elements.length}`)
throw new Error(`too many matching elements for ${selectorToString(parsedSelector)}, matching ${elements.length}`)
}
const element = elements[nth]
if (!element) {
throw new Error(`selector not found: ${selector}`)
throw new Error(`selector not found: ${selectorToString(parsedSelector)}`)
}
return element
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export const performAction = async (locator, fnName, options) => {
Assert.string(fnName)
Assert.object(options)
const fn = ElementActions[fnName]
const element = QuerySelector.querySelectorWithOptions(locator._selector, {
const element = QuerySelector.querySelectorWithOptions(locator._parsed, {
hasText: locator._hasText,
nth: locator._nth,
})
Expand All @@ -57,7 +57,7 @@ export const performKeyBoardAction = (fnName, options) => {

export const checkSingleElementCondition = async (locator, fnName, options): Promise<ConditionResult> => {
const fn = SingleElementConditions[fnName]
const element = QuerySelector.querySelectorWithOptions(locator._selector, {
const element = QuerySelector.querySelectorWithOptions(locator._parsed, {
hasText: locator._hasText,
nth: locator._nth,
})
Expand All @@ -74,7 +74,7 @@ export const checkSingleElementCondition = async (locator, fnName, options): Pro

export const checkMultiElementCondition = async (locator, fnName, options): Promise<ConditionResult> => {
const fn = MultiElementConditions[fnName]
const elements = QuerySelector.querySelector(locator._selector)
const elements = QuerySelector.querySelector(locator._parsed)
const successful = fn(elements, options)
if (successful) {
return {
Expand Down
61 changes: 61 additions & 0 deletions packages/renderer-process/test/QuerySelector.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/**
* @jest-environment jsdom
*/
import { expect, test } from '@jest/globals'
import * as QuerySelector from '../src/parts/TestFrameWork/QuerySelector.ts'

test('querySelector finds css selector from parsed selector', () => {
document.body.innerHTML = '<div class="target"></div>'
const result = QuerySelector.querySelector({
selector: '.target',
type: 'css',
})
expect(result).toHaveLength(1)
expect(result[0]?.className).toBe('target')
})

test('querySelector finds text selector from parsed selector', () => {
document.body.innerHTML = '<div>alpha</div><div>beta</div>'
const result = QuerySelector.querySelector({
text: 'beta',
type: 'text',
})
expect(result).toHaveLength(1)
expect(result[0]?.textContent).toBe('beta')
})

test('querySelector finds css+text selector from parsed selector', () => {
document.body.innerHTML = '<div><span>alpha</span></div><div class="target"><span>beta</span></div>'
const result = QuerySelector.querySelector({
selector: '.target',
text: 'beta',
type: 'css+text',
})
expect(result).toHaveLength(1)
expect(result[0]?.textContent).toBe('beta')
})

test('querySelectorWithOptions filters by hasText and nth', () => {
document.body.innerHTML = '<button>alpha</button><button>beta</button><button>beta</button>'
const result = QuerySelector.querySelectorWithOptions(
{
selector: 'button',
type: 'css',
},
{
hasText: 'beta',
nth: 1,
},
)
expect(result?.textContent).toBe('beta')
})

test('querySelectorWithOptions throws for too many matching elements without nth', () => {
document.body.innerHTML = '<button>alpha</button><button>beta</button>'
expect(() =>
QuerySelector.querySelectorWithOptions({
selector: 'button',
type: 'css',
}),
).toThrow(new Error('too many matching elements for button, matching 2'))
})
Loading