Skip to content

Commit 3385276

Browse files
committed
Multi-remote values in error message for $$()
1 parent ae83010 commit 3385276

3 files changed

Lines changed: 61 additions & 12 deletions

File tree

src/util/elementsUtil.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ export const isElementOrArrayOrMultiRemoteElementLike = (obj: unknown): obj is W
112112
*/
113113
export const awaitElementOrArray = async(
114114
received: MaybeSomeWdioElementOrArrayMaybePromise | PromiseLike<WebdriverIO.Element> | WdioMultiRemoteElements | unknown
115-
): Promise<{ selector?: WdioElements | WebdriverIO.Element | WebdriverIO.MultiRemoteElement[], elements?: WdioElements | WebdriverIO.MultiRemoteElement[], element?: WebdriverIO.Element, other?: unknown, isEmptyElements?: boolean, multiRemote?: WebdriverIO.MultiRemoteElement }> => {
115+
): Promise<{ selector?: WdioElements | WebdriverIO.Element | WebdriverIO.MultiRemoteElement[], elements?: WdioElements | WebdriverIO.MultiRemoteElement[], element?: WebdriverIO.Element, other?: unknown, isEmptyElements?: boolean, multiRemoteSelector?: WebdriverIO.MultiRemoteElement }> => {
116116
if (!received || typeof received !== 'object') {
117117
return { other: received }
118118
}
@@ -133,7 +133,7 @@ export const awaitElementOrArray = async(
133133
if ('getElement' in awaitedElements) {
134134
if (isMultiRemoteElement(awaitedElements)) {
135135
const elements = await awaitedElements.getElement()
136-
return { selector: elements, elements, multiRemote: awaitedElements }
136+
return { selector: elements, elements, multiRemoteSelector: awaitedElements }
137137
}
138138

139139
const element = await (awaitedElements as WebdriverIO.Element).getElement()

src/util/executeCommand.ts

Lines changed: 47 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -130,15 +130,15 @@ export const multipleElementResultsStrategy = async <Actual, Expected>(
130130
{ isNot, isSome, iteration }: { isNot: boolean; isSome: boolean; iteration: number },
131131
{ allowEmptyElements = false, allowArrayWithSingleElement = false } = {}
132132
): Promise<StrategyResult<MaybeArrayOrMultiRemoteValues<Actual>>> => {
133-
const { selector, other, multiRemote } = await awaitElementOrArray(unresolvedElements)
133+
const { selector, other, multiRemoteSelector } = await awaitElementOrArray(unresolvedElements)
134134

135135
if (iteration > 0 && isStrictlyElementArray(selector)) {
136136
// TODO dprevost adapt for Multi-remote?
137137
// WARNING: This synchronize the element's array with the latest refetched elements and so altering selector state!
138138
await refreshElementArray(selector)
139139
}
140140

141-
const subject = multiRemote ?? selector ?? other
141+
const subject = multiRemoteSelector ?? selector ?? other
142142

143143
// --- Empty / no element case ---
144144
if (!selector || (Array.isArray(selector) && selector.length === 0)) {
@@ -176,12 +176,35 @@ export const multipleElementResultsStrategy = async <Actual, Expected>(
176176

177177
let multiRemoteActual: MultiRemoteValuesWithArray<Actual> | undefined
178178
// --- Multi-remote $() single element case ---
179-
if (multiRemote && isMultiRemoteValues(expectedValues, multiRemote.instances)) {
180-
multiRemoteActual = {}
179+
if (multiRemoteSelector && !Array.isArray(expectedValues)) {
180+
let multiRemoteExpected = expectedValues
181+
if (!isMultiRemoteValues(expectedValues, multiRemoteSelector.instances)) {
182+
multiRemoteExpected = multiRemoteSelector.instances.reduce((acc, instance) => {
183+
acc[instance] = expectedValues as Expected
184+
return acc
185+
}, {} as MultiRemoteValues<Expected>)
186+
}
187+
188+
if (!isMultiRemoteValues(multiRemoteExpected, multiRemoteSelector.instances)) { throw new Error('multiRemoteExpected is not a MultiRemoteValues') }
189+
181190
results = await Promise.all(
182-
Object.keys(expectedValues).map(async (instance) => {
183-
const element = multiRemote.getInstance(instance)
184-
const expectValue = expectedValues[instance]
191+
Object.keys(multiRemoteExpected).map(async (instance) => {
192+
const expectValue = multiRemoteExpected[instance]
193+
194+
// TODO: non-existing multi-remote element should probably be a element with error and not to throw??
195+
let element: WebdriverIO.Element
196+
try {
197+
element = multiRemoteSelector.getInstance(instance)
198+
} catch (error) {
199+
if (error instanceof Error && error.message.includes('Multiremote object has no instance named')) {
200+
if (!multiRemoteActual) {throw new Error('multiRemoteActual is undefined')}
201+
multiRemoteActual[instance] = undefined as Actual
202+
203+
return { success: false, actual: undefined as Actual }
204+
}
205+
throw error
206+
}
207+
185208
const result = await singleElementCompare(element, expectValue as Expected)
186209
if (!multiRemoteActual) {throw new Error('multiRemoteActual is undefined')}
187210
multiRemoteActual[instance] = result.actual
@@ -194,14 +217,28 @@ export const multipleElementResultsStrategy = async <Actual, Expected>(
194217
for (const [index, element] of Array.from(selector.entries())) {
195218
const instanceResults = await Promise.all(
196219
element.instances.map(async (instance) => {
197-
const elementInstance = element.getInstance(instance)
220+
if (!multiRemoteActual) { throw new Error('multiRemoteActual is undefined') }
221+
if (!multiRemoteActual[instance]) { multiRemoteActual[instance] = [] }
222+
const typedActual = multiRemoteActual[instance] as Actual[]
223+
224+
let elementInstance: WebdriverIO.Element
225+
try {
226+
elementInstance = element.getInstance(instance)
227+
} catch (error) {
228+
if (error instanceof Error && error.message.includes('Multiremote object has no instance named')) {
229+
if (!multiRemoteActual) {throw new Error('multiRemoteActual is undefined')}
230+
typedActual.push(undefined as Actual)
231+
232+
return { success: false, actual: undefined as Actual }
233+
}
234+
throw error
235+
}
198236

199237
const instanceValue = isMultiRemoteValues(expectedValues, element.instances) ? expectedValues[instance] : expectedValues
200238
const indexedExpected = Array.isArray(instanceValue) ? instanceValue[index] : instanceValue
201-
if (!multiRemoteActual) {throw new Error('multiRemoteActual is undefined')}
202239

203240
const result = await singleElementCompare(elementInstance, indexedExpected, index)
204-
multiRemoteActual[instance] = result.actual
241+
typedActual.push(result.actual)
205242
return result
206243
})
207244
)

src/util/formatMessage.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,18 @@ export const enhanceErrorBe = (
210210
acc[instance] = expectedValue
211211
return acc
212212
}, {} as MultiRemoteValues<string>)
213+
} else if (isMultiRemoteElements(subject)) {
214+
const typedActuals = actuals as MultiRemoteValues<boolean[]>
215+
actual = subject[0].instances.reduce((acc, instance) => {
216+
acc[instance] = typedActuals[instance].map(actual => isSuccess(isNot, actual) ? `${not(isNot)}${expectation}` : `${not(!isNot)}${expectation}`)
217+
return acc
218+
}, {} as MultiRemoteValues<string[]>)
219+
expected = subject[0].instances.reduce((acc, instance) => {
220+
acc[instance] = Array(typedActuals[instance].length).fill(expectedValue)
221+
return acc
222+
}, {} as MultiRemoteValues<string[]>)
223+
} else {
224+
throw new Error('Unsupported Multi-remote object type for enhanceErrorBe')
213225
}
214226
} else if (isElementArrayLike(subject)) {
215227
expected = subject.length === 0 ? 'at least one result' : Array(subject.length).fill(expectedValue)

0 commit comments

Comments
 (0)