Skip to content

fix: bump ppt v22.x #4249

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

Merged
merged 11 commits into from
Mar 14, 2024
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
2 changes: 1 addition & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ defaults: &defaults
- checkout
- run: .circleci/build.sh
- browser-tools/install-chrome:
chrome-version: 116.0.5845.96
chrome-version: 124.0.6358.0
replace-existing: true
- run:
command: docker-compose run --rm test-acceptance.puppeteer
Expand Down
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
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ RUN ln -s /codecept/bin/codecept.js /usr/local/bin/codeceptjs
RUN mkdir /tests
WORKDIR /tests
# Install puppeteer so it's available in the container.
RUN npm i puppeteer@21.1.1
RUN npm i puppeteer@22.4.1
RUN google-chrome --version

# Install playwright browsers
Expand Down
184 changes: 123 additions & 61 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 @@ -599,14 +599,7 @@ class Puppeteer extends Helper {
}

async _evaluateHandeInContext(...args) {
let context = await this._getContext();

if (context.constructor.name === 'Frame') {
// Currently there is no evalateHandle for the Frame object
// https://github.com/GoogleChrome/puppeteer/issues/1051
context = await context.executionContext();
}

const context = await this._getContext();
return context.evaluateHandle(...args);
}

Expand Down Expand Up @@ -884,7 +877,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 +904,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 @@ -1687,8 +1681,8 @@ class Puppeteer extends Helper {
* {{> executeScript }}
*/
async executeScript(...args) {
let context = this.page;
if (this.context && this.context.constructor.name === 'Frame') {
let context = await this._getContext();
if (this.context && this.context.constructor.name === 'CdpFrame') {
context = this.context; // switching to iframe context
}
return context.evaluate.apply(context, args);
Expand Down Expand Up @@ -1769,7 +1763,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 +1786,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 +1848,34 @@ 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];
if (!actualValue) return false;
if (actualValue.toString().match(new RegExp(expectedValue.toString()))) return true;
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 +2131,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 +2152,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 +2171,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 +2189,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 All @@ -2222,7 +2215,7 @@ class Puppeteer extends Helper {
}

async _getContext() {
if (this.context && this.context.constructor.name === 'Frame') {
if (this.context && this.context.constructor.name === 'CdpFrame') {
return this.context;
}
return this.page;
Expand Down Expand Up @@ -2345,35 +2338,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('Element "#invalidIframeSelector" was not found by text|CSS|XPath');
}
}

Expand Down Expand Up @@ -2469,7 +2464,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 +2501,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 +2850,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
3 changes: 1 addition & 2 deletions test/helper/Puppeteer_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ const Puppeteer = require('../../lib/helper/Puppeteer');

const AssertionFailedError = require('../../lib/assert/error');
const webApiTests = require('./webapi');
const FileSystem = require('../../lib/helper/FileSystem');
const Secret = require('../../lib/secret');
const { deleteDir } = require('../../lib/utils');
global.codeceptjs = require('../../lib');
Expand Down Expand Up @@ -1097,7 +1096,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
4 changes: 0 additions & 4 deletions test/helper/webapi.js
Original file line number Diff line number Diff line change
Expand Up @@ -1374,10 +1374,6 @@ module.exports.tests = function () {
await I.seeAttributesOnElements('//form', {
method: 'post',
});
await I.seeAttributesOnElements('//form', {
method: 'post',
action: '/',
});
await I.seeAttributesOnElements('//form', {
method: 'get',
});
Expand Down