Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: bump ppt v22.x #4249

Merged
merged 11 commits into from
Mar 14, 2024
Merged
Show file tree
Hide file tree
Changes from 7 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
2 changes: 1 addition & 1 deletion .github/workflows/puppeteer.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,6 @@ jobs:
- uses: browser-actions/setup-chrome@v1
- run: chrome --version
- name: run tests
run: "./bin/codecept.js run -c test/acceptance/codecept.Puppeteer.js --grep @Puppeteer --debug"
run: "./bin/codecept.js run-workers 2 -c test/acceptance/codecept.Puppeteer.js --grep @Puppeteer --debug"
- name: run unit tests
run: ./node_modules/.bin/mocha test/helper/Puppeteer_test.js
181 changes: 123 additions & 58 deletions lib/helper/Puppeteer.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ const consoleLogStore = new Console();
* @prop {string} [browser=chrome] - can be changed to `firefox` when using [puppeteer-firefox](https://codecept.io/helpers/Puppeteer-firefox).
* @prop {object} [chrome] - pass additional [Puppeteer run options](https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#puppeteerlaunchoptions).
* @prop {boolean} [highlightElement] - highlight the interacting elements. Default: false. Note: only activate under verbose mode (--verbose).
*/
*/
const config = {};

/**
Expand Down Expand Up @@ -369,7 +369,7 @@ class Puppeteer extends Helper {
this.debugSection('Incognito Tab', 'opened');
this.activeSessionName = name;

const bc = await this.browser.createIncognitoBrowserContext();
const bc = await this.browser.createBrowserContext();
await bc.newPage();

// Create a new page inside context.
Expand Down Expand Up @@ -884,7 +884,8 @@ class Puppeteer extends Helper {
* {{ react }}
*/
async _locate(locator) {
return findElements(await this.context, locator);
const context = await this.context;
return findElements.call(this, context, locator);
}

/**
Expand All @@ -910,7 +911,7 @@ class Puppeteer extends Helper {
* ```
*/
async _locateClickable(locator) {
const context = await this._getContext();
const context = await this.context;
return findClickable.call(this, context, locator);
}

Expand Down Expand Up @@ -1368,9 +1369,9 @@ class Puppeteer extends Helper {
const tag = await el.getProperty('tagName').then(el => el.jsonValue());
const editable = await el.getProperty('contenteditable').then(el => el.jsonValue());
if (tag === 'INPUT' || tag === 'TEXTAREA') {
await this._evaluateHandeInContext(el => el.value = '', el);
await this.context.evaluateHandle(el => el.value = '', el);
} else if (editable) {
await this._evaluateHandeInContext(el => el.innerHTML = '', el);
await this.context.evaluateHandle(el => el.innerHTML = '', el);
}

highlightActiveElement.call(this, el, await this._getContext());
Expand Down Expand Up @@ -1687,11 +1688,9 @@ class Puppeteer extends Helper {
* {{> executeScript }}
*/
async executeScript(...args) {
let context = this.page;
if (this.context && this.context.constructor.name === 'Frame') {
context = this.context; // switching to iframe context
}
return context.evaluate.apply(context, args);
const executionContext = this.context || this.page.mainFrame(); // Use the main frame if no specific context is provided

return executionContext.evaluate.apply(executionContext, args);
}

/**
Expand Down Expand Up @@ -1769,7 +1768,7 @@ class Puppeteer extends Helper {
*/
async grabHTMLFromAll(locator) {
const els = await this._locate(locator);
const values = await Promise.all(els.map(el => el.executionContext().evaluate(element => element.innerHTML, el)));
const values = await Promise.all(els.map(el => el.evaluate(element => element.innerHTML, el)));
return values;
}

Expand All @@ -1792,7 +1791,7 @@ class Puppeteer extends Helper {
*/
async grabCssPropertyFromAll(locator, cssProperty) {
const els = await this._locate(locator);
const res = await Promise.all(els.map(el => el.executionContext().evaluate(el => JSON.parse(JSON.stringify(getComputedStyle(el))), el)));
const res = await Promise.all(els.map(el => el.evaluate(el => JSON.parse(JSON.stringify(getComputedStyle(el))), el)));
const cssValues = res.map(props => props[toCamelCase(cssProperty)]);

return cssValues;
Expand Down Expand Up @@ -1854,35 +1853,32 @@ class Puppeteer extends Helper {
* {{ react }}
*/
async seeAttributesOnElements(locator, attributes) {
const res = await this._locate(locator);
assertElementExists(res, locator);
const elements = await this._locate(locator);
assertElementExists(elements, locator);

const elemAmount = res.length;
const commands = [];
res.forEach((el) => {
Object.keys(attributes).forEach((prop) => {
commands.push(el
.executionContext()
.evaluateHandle((el, attr) => el[attr] || el.getAttribute(attr), el, prop)
.then(el => el.jsonValue()));
});
});
let attrs = await Promise.all(commands);
const values = Object.keys(attributes).map(key => attributes[key]);
if (!Array.isArray(attrs)) attrs = [attrs];
let chunked = chunkArray(attrs, values.length);
chunked = chunked.filter((val) => {
for (let i = 0; i < val.length; ++i) {
const _actual = Number.isNaN(val[i]) || (typeof values[i]) === 'string' ? val[i] : Number.parseInt(values[i], 10);
const _expected = Number.isNaN(values[i]) || (typeof values[i]) === 'string' ? values[i] : Number.parseInt(values[i], 10);
// the attribute could be a boolean
if (typeof _actual === 'boolean') return _actual === _expected;
// if the attribute doesn't exist, returns false as well
if (!_actual || !_actual.includes(_expected)) return false;
}
return true;
const expectedAttributes = Object.entries(attributes);

const valuesPromises = elements.map(async (element) => {
const elementAttributes = {};
await Promise.all(expectedAttributes.map(async ([attribute, expectedValue]) => {
const actualValue = await element.evaluate((el, attr) => el[attr] || el.getAttribute(attr), attribute);
elementAttributes[attribute] = actualValue;
}));
return elementAttributes;
});
return equals(`all elements (${(new Locator(locator))}) to have attributes ${JSON.stringify(attributes)}`).assert(chunked.length, elemAmount);

const actualAttributes = await Promise.all(valuesPromises);

const matchingElements = actualAttributes.filter((attrs) => expectedAttributes.every(([attribute, expectedValue]) => {
const actualValue = attrs[attribute];
return expectedValue === actualValue;
}));

const elementsCount = elements.length;
const matchingCount = matchingElements.length;

return equals(`all elements (${(new Locator(locator))}) to have attributes ${JSON.stringify(attributes)}`)
.assert(matchingCount, elementsCount);
}

/**
Expand Down Expand Up @@ -2138,7 +2134,7 @@ class Puppeteer extends Helper {
if (locator.isCSS()) {
waiter = context.waitForSelector(locator.simplify(), { timeout: waitTimeout });
} else {
waiter = context.waitForXPath(locator.value, { timeout: waitTimeout });
waiter = _waitForElement.call(this, locator, { timeout: waitTimeout });
}
return waiter.catch((err) => {
throw new Error(`element (${locator.toString()}) still not present on page after ${waitTimeout / 1000} sec\n${err.message}`);
Expand All @@ -2159,7 +2155,7 @@ class Puppeteer extends Helper {
if (locator.isCSS()) {
waiter = context.waitForSelector(locator.simplify(), { timeout: waitTimeout, visible: true });
} else {
waiter = context.waitForXPath(locator.value, { timeout: waitTimeout, visible: true });
waiter = _waitForElement.call(this, locator, { timeout: waitTimeout, visible: true });
}
return waiter.catch((err) => {
throw new Error(`element (${locator.toString()}) still not visible after ${waitTimeout / 1000} sec\n${err.message}`);
Expand All @@ -2178,7 +2174,7 @@ class Puppeteer extends Helper {
if (locator.isCSS()) {
waiter = context.waitForSelector(locator.simplify(), { timeout: waitTimeout, hidden: true });
} else {
waiter = context.waitForXPath(locator.value, { timeout: waitTimeout, hidden: true });
waiter = _waitForElement.call(this, locator, { timeout: waitTimeout, hidden: true });
}
return waiter.catch((err) => {
throw new Error(`element (${locator.toString()}) still visible after ${waitTimeout / 1000} sec\n${err.message}`);
Expand All @@ -2196,7 +2192,7 @@ class Puppeteer extends Helper {
if (locator.isCSS()) {
waiter = context.waitForSelector(locator.simplify(), { timeout: waitTimeout, hidden: true });
} else {
waiter = context.waitForXPath(locator.value, { timeout: waitTimeout, hidden: true });
waiter = _waitForElement.call(this, locator, { timeout: waitTimeout, hidden: true });
}
return waiter.catch((err) => {
throw new Error(`element (${locator.toString()}) still not hidden after ${waitTimeout / 1000} sec\n${err.message}`);
Expand Down Expand Up @@ -2278,7 +2274,7 @@ class Puppeteer extends Helper {
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout;
let waiter;

const contextObject = await this._getContext();
const contextObject = await this.context;

if (context) {
const locator = new Locator(context, 'css');
Expand Down Expand Up @@ -2345,35 +2341,37 @@ class Puppeteer extends Helper {
async switchTo(locator) {
if (Number.isInteger(locator)) {
// Select by frame index of current context

let childFrames = null;
let frames = [];
if (this.context && typeof this.context.childFrames === 'function') {
childFrames = this.context.childFrames();
frames = await this.context.childFrames();
} else {
childFrames = this.page.mainFrame().childFrames();
frames = await this.page.mainFrame().childFrames();
}

if (locator >= 0 && locator < childFrames.length) {
this.context = childFrames[locator];
if (locator >= 0 && locator < frames.length) {
this.context = frames[locator];
} else {
throw new Error('Element #invalidIframeSelector was not found by text|CSS|XPath');
throw new Error('Frame index out of bounds');
}
return;
}

if (!locator) {
this.context = await this.page.mainFrame().$('body');
this.context = await this.page.mainFrame();
return;
}

// iframe by selector
// Select iframe by selector
const els = await this._locate(locator);
assertElementExists(els, locator);
const contentFrame = await els[0].contentFrame();

const iframeElement = els[0];
const contentFrame = await iframeElement.contentFrame();

if (contentFrame) {
this.context = contentFrame;
} else {
this.context = els[0];
throw new Error('No content frame found');
}
}

Expand Down Expand Up @@ -2469,7 +2467,7 @@ class Puppeteer extends Helper {
module.exports = Puppeteer;

async function findElements(matcher, locator) {
if (locator.react) return findReact(matcher.executionContext(), locator);
if (locator.react) return findReactElements.call(this, locator);
locator = new Locator(locator, 'css');
if (!locator.isXPath()) return matcher.$$(locator.simplify());
// puppeteer version < 19.4.0 is no longer supported. This one is backward support.
Expand Down Expand Up @@ -2506,7 +2504,7 @@ async function proceedClick(locator, context = null, options = {}) {
}

async function findClickable(matcher, locator) {
if (locator.react) return findReact(matcher.executionContext(), locator);
if (locator.react) return findReactElements.call(this, locator);
locator = new Locator(locator);
if (!locator.isFuzzy()) return findElements.call(this, matcher, locator);

Expand Down Expand Up @@ -2855,3 +2853,70 @@ function highlightActiveElement(element, context) {
highlightElement(element, context);
}
}

function _waitForElement(locator, options) {
try {
return this.context.waitForXPath(locator.value, options);
} catch (e) {
return this.context.waitForSelector(`::-p-xpath(${locator.value})`, options);
}
}

async function findReactElements(locator, props = {}, state = {}) {
const resqScript = await fs.promises.readFile(require.resolve('resq'), 'utf-8');
await this.page.evaluate(resqScript.toString());

await this.page.evaluate(() => window.resq.waitToLoadReact());
const arrayHandle = await this.page.evaluateHandle((obj) => {
const { selector, props, state } = obj;
let elements = window.resq.resq$$(selector);
if (Object.keys(props).length) {
elements = elements.byProps(props);
}
if (Object.keys(state).length) {
elements = elements.byState(state);
}

if (!elements.length) {
return [];
}

// resq returns an array of HTMLElements if the React component is a fragment
// this avoids having nested arrays of nodes which the driver does not understand
// [[div, div], [div, div]] => [div, div, div, div]
let nodes = [];

elements.forEach((element) => {
let { node, isFragment } = element;

if (!node) {
isFragment = true;
node = element.children;
}

if (isFragment) {
nodes = nodes.concat(node);
} else {
nodes.push(node);
}
});

return [...nodes];
}, {
selector: locator.react,
props: locator.props || {},
state: locator.state || {},
});

const properties = await arrayHandle.getProperties();
const result = [];
for (const property of properties.values()) {
const elementHandle = property.asElement();
if (elementHandle) {
result.push(elementHandle);
}
}

await arrayHandle.dispose();
return result;
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@
"jsdoc-typeof-plugin": "1.0.0",
"json-server": "0.10.1",
"playwright": "1.41.1",
"puppeteer": "21.1.1",
"puppeteer": "22.4.1",
"qrcode-terminal": "0.12.0",
"rosie": "2.1.1",
"runok": "0.9.3",
Expand Down
2 changes: 1 addition & 1 deletion test/helper/Puppeteer_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1097,7 +1097,7 @@ describe('Puppeteer - Trace', () => {
await I.amOnPage('/form/focus_blur_elements');

const webElements = await I.grabWebElements('#button');
assert.include(webElements[0].constructor.name, 'CDPElementHandle');
assert.include(webElements[0].constructor.name, 'CdpElementHandle');
assert.isAbove(webElements.length, 0);
});
});
Expand Down
Loading