Skip to content

Commit

Permalink
feat: use an xpath query handler
Browse files Browse the repository at this point in the history
  • Loading branch information
jrandolf committed Aug 2, 2022
1 parent 0c8bce1 commit d5add33
Show file tree
Hide file tree
Showing 4 changed files with 108 additions and 157 deletions.
46 changes: 0 additions & 46 deletions src/common/DOMWorld.ts
Expand Up @@ -717,52 +717,6 @@ export class DOMWorld {
return elementHandle;
}

async waitForXPath(
xpath: string,
options: WaitForSelectorOptions
): Promise<ElementHandle<Node> | null> {
const {
visible: waitForVisible = false,
hidden: waitForHidden = false,
timeout = this.#timeoutSettings.timeout(),
} = options;
const polling = waitForVisible || waitForHidden ? 'raf' : 'mutation';
const title = `XPath \`${xpath}\`${waitForHidden ? ' to be hidden' : ''}`;
function predicate(
root: Element | Document,
xpath: string,
waitForVisible: boolean,
waitForHidden: boolean
): Node | null | boolean {
const node = document.evaluate(
xpath,
root,
null,
XPathResult.FIRST_ORDERED_NODE_TYPE,
null
).singleNodeValue;
return checkWaitForOptions(node, waitForVisible, waitForHidden);
}
const waitTaskOptions: WaitTaskOptions = {
domWorld: this,
predicateBody: makePredicateString(predicate),
predicateAcceptsContextElement: true,
title,
polling,
timeout,
args: [xpath, waitForVisible, waitForHidden],
root: options.root,
};
const waitTask = new WaitTask(waitTaskOptions);
const jsHandle = await waitTask.promise;
const elementHandle = jsHandle.asElement();
if (!elementHandle) {
await jsHandle.dispose();
return null;
}
return elementHandle;
}

waitForFunction(
pageFunction: Function | string,
options: {polling?: string | number; timeout?: number} = {},
Expand Down
52 changes: 6 additions & 46 deletions src/common/ElementHandle.ts
Expand Up @@ -140,6 +140,8 @@ export class ElementHandle<
}

/**
* @deprecated Use {@link waitForSelector} with an xpath selector.
*
* Wait for the `xpath` within the element. If at the moment of calling the
* method the `xpath` already exists, the method will return immediately. If
* the `xpath` doesn't appear after the `timeout` milliseconds of waiting, the
Expand Down Expand Up @@ -197,27 +199,7 @@ export class ElementHandle<
timeout?: number;
} = {}
): Promise<ElementHandle<Node> | null> {
const frame = this._context.frame();
assert(frame);
const secondaryContext = await frame._secondaryWorld.executionContext();
const adoptedRoot = await secondaryContext._adoptElementHandle(this);
xpath = xpath.startsWith('//') ? '.' + xpath : xpath;
if (!xpath.startsWith('.//')) {
await adoptedRoot.dispose();
throw new Error('Unsupported xpath expression: ' + xpath);
}
const handle = await frame._secondaryWorld.waitForXPath(xpath, {
...options,
root: adoptedRoot,
});
await adoptedRoot.dispose();
if (!handle) {
return null;
}
const mainExecutionContext = await frame._mainWorld.executionContext();
const result = await mainExecutionContext._adoptElementHandle(handle);
await handle.dispose();
return result;
return this.waitForSelector(xpath, options);
}

override asElement(): ElementHandle<ElementType> | null {
Expand Down Expand Up @@ -964,36 +946,14 @@ export class ElementHandle<
}

/**
* @deprecated Use {@link this.$$}.
*
* The method evaluates the XPath expression relative to the elementHandle.
* If there are no such elements, the method will resolve to an empty array.
* @param expression - Expression to {@link https://developer.mozilla.org/en-US/docs/Web/API/Document/evaluate | evaluate}
*/
async $x(expression: string): Promise<Array<ElementHandle<Node>>> {
const arrayHandle = await this.evaluateHandle((element, expression) => {
const doc = element.ownerDocument || document;
const iterator = doc.evaluate(
expression,
element,
null,
XPathResult.ORDERED_NODE_ITERATOR_TYPE
);
const array = [];
let item;
while ((item = iterator.iterateNext())) {
array.push(item);
}
return array;
}, expression);
const properties = await arrayHandle.getProperties();
await arrayHandle.dispose();
const result = [];
for (const property of properties.values()) {
const elementHandle = property.asElement();
if (elementHandle) {
result.push(elementHandle);
}
}
return result;
return this.$$(expression);
}

/**
Expand Down
12 changes: 3 additions & 9 deletions src/common/FrameManager.ts
Expand Up @@ -1374,7 +1374,8 @@ export class Frame {
}

/**
* @remarks
* @deprecated Use {@link waitForSelector}.
*
* Wait for the `xpath` to appear in page. If at the moment of calling the
* method the `xpath` already exists, the method will return immediately. If
* the xpath doesn't appear after the `timeout` milliseconds of waiting, the
Expand All @@ -1392,14 +1393,7 @@ export class Frame {
xpath: string,
options: WaitForSelectorOptions = {}
): Promise<ElementHandle<Node> | null> {
const handle = await this._secondaryWorld.waitForXPath(xpath, options);
if (!handle) {
return null;
}
const mainExecutionContext = await this._mainWorld.executionContext();
const result = await mainExecutionContext._adoptElementHandle(handle);
await handle.dispose();
return result;
return this.waitForSelector(xpath, options);
}

/**
Expand Down

0 comments on commit d5add33

Please sign in to comment.