');
const element = await page.$('text/a b');
expect(
await element?.evaluate(e => {
return e.textContent;
})
).toBe('a b');
});
it('should clear caches', async () => {
const {page} = getTestState();
await page.setContent(
'
text
text
'
);
const div = (await page.$('#target1')) as ElementHandle
;
const input = (await page.$(
'#target2'
)) as ElementHandle;
await div.evaluate(div => {
div.textContent = 'text';
});
expect(
await page.$eval(`text/text`, e => {
return e.id;
})
).toBe('target1');
await div.evaluate(div => {
div.textContent = 'foo';
});
expect(
await page.$eval(`text/text`, e => {
return e.id;
})
).toBe('target2');
await input.evaluate(input => {
input.value = '';
});
await input.type('foo');
expect(
await page.$eval(`text/text`, e => {
return e.id;
})
).toBe('target3');
await div.evaluate(div => {
div.textContent = 'text';
});
await input.evaluate(input => {
input.value = '';
});
await input.type('text');
expect(
await page.$$eval(`text/text`, es => {
return es.length;
})
).toBe(3);
await div.evaluate(div => {
div.textContent = 'foo';
});
expect(
await page.$$eval(`text/text`, es => {
return es.length;
})
).toBe(2);
await input.evaluate(input => {
input.value = '';
});
await input.type('foo');
expect(
await page.$$eval(`text/text`, es => {
return es.length;
})
).toBe(1);
});
});
describe('in ElementHandles', function () {
it('should query existing element', async () => {
const {page} = getTestState();
await page.setContent('a
');
const elementHandle = (await page.$('div'))!;
expect(await elementHandle.$(`text/a`)).toBeTruthy();
expect((await elementHandle.$$(`text/a`)).length).toBe(1);
});
it('should return null for non-existing element', async () => {
const {page} = getTestState();
await page.setContent('');
const elementHandle = (await page.$('div'))!;
expect(await elementHandle.$(`text/a`)).toBeFalsy();
expect((await elementHandle.$$(`text/a`)).length).toBe(0);
});
});
});
describe('XPath selectors', function () {
describe('in Page', function () {
it('should query existing element', async () => {
const {page} = getTestState();
await page.setContent('');
expect(await page.$('xpath/html/body/section')).toBeTruthy();
expect((await page.$$('xpath/html/body/section')).length).toBe(1);
});
it('should return empty array for non-existing element', async () => {
const {page} = getTestState();
expect(
await page.$('xpath/html/body/non-existing-element')
).toBeFalsy();
expect(
(await page.$$('xpath/html/body/non-existing-element')).length
).toBe(0);
});
it('should return first element', async () => {
const {page} = getTestState();
await page.setContent('a
');
const element = await page.$('xpath/html/body/div');
expect(
await element?.evaluate(e => {
return e.textContent === 'a';
})
).toBeTruthy();
});
it('should return multiple elements', async () => {
const {page} = getTestState();
await page.setContent('');
const elements = await page.$$('xpath/html/body/div');
expect(elements.length).toBe(2);
});
});
describe('in ElementHandles', function () {
it('should query existing element', async () => {
const {page} = getTestState();
await page.setContent('a
');
const elementHandle = (await page.$('div'))!;
expect(await elementHandle.$(`xpath/span`)).toBeTruthy();
expect((await elementHandle.$$(`xpath/span`)).length).toBe(1);
});
it('should return null for non-existing element', async () => {
const {page} = getTestState();
await page.setContent('a
');
const elementHandle = (await page.$('div'))!;
expect(await elementHandle.$(`xpath/span`)).toBeFalsy();
expect((await elementHandle.$$(`xpath/span`)).length).toBe(0);
});
});
});
describe('P selectors', () => {
beforeEach(async () => {
const {page} = getTestState();
await page.setContent(
'hello
'
);
Puppeteer.clearCustomQueryHandlers();
});
it('should work with CSS selectors', async () => {
const {page} = getTestState();
const element = await page.$('div > button');
assert(element, 'Could not find element');
expect(
await element.evaluate(element => {
return element.tagName === 'BUTTON';
})
).toBeTruthy();
});
it('should work with text selectors', async () => {
const {page} = getTestState();
const element = await page.$('div ::-p-text(world)');
assert(element, 'Could not find element');
expect(
await element.evaluate(element => {
return element.tagName === 'BUTTON';
})
).toBeTruthy();
});
it('should work ARIA selectors', async () => {
const {page} = getTestState();
const element = await page.$('div ::-p-aria(world)');
assert(element, 'Could not find element');
expect(
await element.evaluate(element => {
return element.tagName === 'BUTTON';
})
).toBeTruthy();
});
it('should work XPath selectors', async () => {
const {page} = getTestState();
const element = await page.$('div ::-p-xpath(//button)');
assert(element, 'Could not find element');
expect(
await element.evaluate(element => {
return element.tagName === 'BUTTON';
})
).toBeTruthy();
});
it('should work with custom selectors', async () => {
Puppeteer.registerCustomQueryHandler('div', {
queryOne() {
return document.querySelector('div');
},
});
const {page} = getTestState();
const element = await page.$('::-p-div');
assert(element, 'Could not find element');
expect(
await element.evaluate(element => {
return element.tagName === 'DIV';
})
).toBeTruthy();
});
it('should work with custom selectors with args', async () => {
const {page} = getTestState();
Puppeteer.registerCustomQueryHandler('div', {
queryOne(_, selector) {
if (selector === 'true') {
return document.querySelector('div');
} else {
return document.querySelector('button');
}
},
});
{
const element = await page.$('::-p-div(true)');
assert(element, 'Could not find element');
expect(
await element.evaluate(element => {
return element.tagName === 'DIV';
})
).toBeTruthy();
}
{
const element = await page.$('::-p-div("true")');
assert(element, 'Could not find element');
expect(
await element.evaluate(element => {
return element.tagName === 'DIV';
})
).toBeTruthy();
}
{
const element = await page.$("::-p-div('true')");
assert(element, 'Could not find element');
expect(
await element.evaluate(element => {
return element.tagName === 'DIV';
})
).toBeTruthy();
}
{
const element = await page.$('::-p-div');
assert(element, 'Could not find element');
expect(
await element.evaluate(element => {
return element.tagName === 'BUTTON';
})
).toBeTruthy();
}
});
it('should work with :hover', async () => {
const {page} = getTestState();
let button = await page.$('div ::-p-text(world)');
assert(button, 'Could not find element');
await button.hover();
await button.dispose();
button = await page.$('div ::-p-text(world):hover');
assert(button, 'Could not find element');
const value = await button.evaluate(span => {
return {textContent: span.textContent, tagName: span.tagName};
});
expect(value).toMatchObject({textContent: 'world', tagName: 'BUTTON'});
});
it('should work with selector lists', async () => {
const {page} = getTestState();
const elements = await page.$$('div, ::-p-text(world)');
expect(elements.length).toStrictEqual(2);
});
const permute = (inputs: T[]): T[][] => {
const results: T[][] = [];
for (let i = 0; i < inputs.length; ++i) {
const permutation = permute(
inputs.slice(0, i).concat(inputs.slice(i + 1))
);
const value = inputs[i] as T;
if (permutation.length === 0) {
results.push([value]);
continue;
}
for (const part of permutation) {
results.push([value].concat(part));
}
}
return results;
};
it('should match querySelector* ordering', async () => {
const {page} = getTestState();
for (const list of permute(['div', 'button', 'span'])) {
const expected = await page.evaluate(selector => {
return [...document.querySelectorAll(selector)].map(element => {
return element.tagName;
});
}, list.join(','));
const elements = await page.$$(
list
.map(selector => {
return selector === 'button' ? '::-p-text(world)' : selector;
})
.join(',')
);
const actual = await Promise.all(
elements.map(element => {
return element.evaluate(element => {
return element.tagName;
});
})
);
expect(actual.join()).toStrictEqual(expected.join());
}
});
it('should not have duplicate elements from selector lists', async () => {
const {page} = getTestState();
const elements = await page.$$('::-p-text(world), button');
expect(elements.length).toStrictEqual(1);
});
});
});