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

release 3.5.9 #4056

Merged
merged 4 commits into from
Dec 7, 2023
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 .github/workflows/playwright.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,5 +50,5 @@ jobs:
run: "BROWSER=firefox node ./bin/codecept.js run -c test/acceptance/codecept.Playwright.js --grep @Playwright --debug"
- name: run webkit tests
run: "BROWSER=webkit node ./bin/codecept.js run -c test/acceptance/codecept.Playwright.js --grep @Playwright --debug"
- name: run unit tests
- name: run chromium unit tests
run: ./node_modules/.bin/mocha test/helper/Playwright_test.js --timeout 5000
161 changes: 160 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,170 @@
## 3.5.9

❤️ Thanks all to those who contributed to make this release! ❤️

🛩️ *Features*
* feat: expose WebElement (#4043) - by @KobeNguyenT
```
Now we expose the WebElements that are returned by the WebHelper and you could make the subsequence actions on them.

// Playwright helper would return the Locator

I.amOnPage('/form/focus_blur_elements');
const webElements = await I.grabWebElements('#button');
webElements[0].click();
```
* feat(playwright): support HAR replaying (#3990) - by @KobeNguyenT
```
Replaying from HAR

// Replay API requests from HAR.
// Either use a matching response from the HAR,
// or abort the request if nothing matches.
I.replayFromHar('./output/har/something.har', { url: "*/**/api/v1/fruits" });
I.amOnPage('https://demo.playwright.dev/api-mocking');
I.see('CodeceptJS');
[Parameters]
harFilePath [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) Path to recorded HAR file
opts [object](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object)? [Options for replaying from HAR](https://playwright.dev/docs/api/class-page#page-route-from-har)
```
* feat(playwright): support HAR recording (#3986) - by @KobeNguyenT
```
A HAR file is an HTTP Archive file that contains a record of all the network requests that are made when a page is loaded.
It contains information about the request and response headers, cookies, content, timings, and more.
You can use HAR files to mock network requests in your tests. HAR will be saved to output/har.
More info could be found here https://playwright.dev/docs/api/class-browser#browser-new-context-option-record-har.

...
recordHar: {
mode: 'minimal', // possible values: 'minimal'|'full'.
content: 'embed' // possible values: "omit"|"embed"|"attach".
}
...
```
* improvement(playwright): support partial string for option (#4016) - by @KobeNguyenT
```
await I.amOnPage('/form/select');
await I.selectOption('Select your age', '21-');
```

🐛 *Bug Fixes*
* fix(playwright): proceedSee could not find the element (#4006) - by @hatufacci
* fix(appium): remove the vendor prefix of 'bstack:options' (#4053) - by @mojtabaalavi
* fix(workers): event improvements (#3953) - by @KobeNguyenT
```
Emit the new event: event.workers.result.

CodeceptJS also exposes the env var `process.env.RUNS_WITH_WORKERS` when running tests with run-workers command so that you could handle the events better in your plugins/helpers.

const { event } = require('codeceptjs');

module.exports = function() {
// this event would trigger the `_publishResultsToTestrail` when running `run-workers` command
event.dispatcher.on(event.workers.result, async () => {
await _publishResultsToTestrail();
});

// this event would not trigger the `_publishResultsToTestrail` multiple times when running `run-workers` command
event.dispatcher.on(event.all.result, async () => {
// when running `run` command, this env var is undefined
if (!process.env.RUNS_WITH_WORKERS) await _publishResultsToTestrail();
});
}
```
* fix: ai html updates (#3962) - by @davert
```
replaced minify library with a modern and more secure fork. Fixes html-minifier@4.0.0 Regular Expression Denial of Service vulnerability #3829
AI class is implemented as singleton
refactored heal.js plugin to work on edge cases
add configuration params on number of fixes performed by ay heal
improved recorder class to add more verbose log
improved recorder class to ignore some of errors
```
* fix(appium): closeApp supports both Android/iOS (#4046) - by @KobeNguyenT
* fix: some security vulnerability of some packages (#4045) - by @KobeNguyenT
* fix: seeAttributesOnElements check condition (#4029) - by @KobeNguyenT
* fix: waitForText locator issue (#4039) - by @KobeNguyenT
```
Fixed this error:

locator.isVisible: Unexpected token "s" while parsing selector ":has-text('Were you able to resolve the resident's issue?') >> nth=0"
at Playwright.waitForText (node_modules\codeceptjs\lib\helper\Playwright.js:2584:79)
```
* fix: move to sha256 (#4038) - by @KobeNguyenT
* fix: respect retries from retryfailedstep plugin in helpers (#4028) - by @KobeNguyenT
```
Currently inside the _before() of helpers for example Playwright, the retries is set there, however, when retryFailedStep plugin is enabled, the retries of recorder is still using the value from _before() not the value from retryFailedStep plugin.

Fix:

- introduce the process.env.FAILED_STEP_RETIRES which could be access everywhere as the helper won't know anything about the plugin.
- set default retries of Playwright to 3 to be on the same page with Puppeteer.
```
* fix: examples in test title (#4030) - by @KobeNguyenT
```
When test title doesn't have the data in examples:

Feature: Faker examples

Scenario Outline: Below are the users
Examples:
| user | role |
| John | admin |
| Tim | client |

Faker examples --
[1] Starting recording promises
Timeouts:
Below are the users {"user":"John","role":"admin"}
✔ OK in 4ms

Below are the users {"user":"Tim","role":"client"}
✔ OK in 1ms

When test title includes the data in examples:


Feature: Faker examples

Scenario Outline: Below are the users - <user> - <role>
Examples:
| user | role |
| John | admin |
| Tim | client |


Faker examples --
[1] Starting recording promises
Timeouts:
Below are the users - John - admin
✔ OK in 4ms

Below are the users - Tim - client
✔ OK in 1ms
```
* fix: disable retryFailedStep when using with tryTo (#4022) - by @KobeNguyenT
* fix: locator builder returns error when class name contains hyphen (#4024) - by @KobeNguyenT
* fix: seeCssPropertiesOnElements failed when font-weight is a number (#4026) - by @KobeNguyenT
* fix(appium): missing await on some steps of runOnIOS and runOnAndroid (#4018) - by @KobeNguyenT
* fix(cli): no error of failed tests when using retry with scenario only (#4020) - by @KobeNguyenT
* fix: set getPageTimeout to 30s (#4031) - by @KobeNguyenT
* fix(appium): expose switchToContext (#4015) - by @KobeNguyenT
* fix: promise issue (#4013) - by @KobeNguyenT
* fix: seeCssPropertiesOnElements issue with improper condition (#4057) - by @KobeNguyenT

📖 *Documentation*
* docs: Update clearCookie documentation for Playwright helper (#4005) - by @Hellosager
* docs: improve the example code for autoLogin (#4019) - by @KobeNguyenT
![Screenshot 2023-11-22 at 14 40 11](https://github.com/codeceptjs/CodeceptJS/assets/7845001/c05ac436-efd0-4bc0-a46c-386f915c0f17)

## 3.5.8

Thanks all to those who contributed to make this release!

🐛 *Bug Fixes*
fix(appium): type of setNetworkConnection() (#3994) - by @mirao
fix: improve the way to show deprecated appium v1 message (#3992) - by @KobeNguyenT
fix: missing exit condition of some wait functions - by @kobenguyent
fix: missing exit condition of some wait functions - by @KobeNguyenT

## 3.5.7

Expand Down
10 changes: 10 additions & 0 deletions lib/colorUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -226,15 +226,25 @@ function isColorProperty(prop) {
'color',
'background',
'backgroundColor',
'background-color',
'borderColor',
'border-color',
'borderBottomColor',
'border-bottom-color',
'borderLeftColor',
'border-left-color',
'borderRightColor',
'borderTopColor',
'caretColor',
'columnRuleColor',
'outlineColor',
'textDecorationColor',
'border-right-color',
'border-top-color',
'caret-color',
'column-rule-color',
'outline-color',
'text-decoration-color',
].indexOf(prop) > -1;
}

Expand Down
17 changes: 7 additions & 10 deletions lib/helper/Playwright.js
Original file line number Diff line number Diff line change
Expand Up @@ -2119,29 +2119,26 @@ class Playwright extends Helper {

const cssPropertiesCamelCase = convertCssPropertiesToCamelCase(cssProperties);
const elemAmount = res.length;
const commands = [];
let props = [];

for (const element of res) {
const cssProperties = await element.evaluate((el) => getComputedStyle(el));

Object.keys(cssPropertiesCamelCase).forEach(prop => {
for (const prop of Object.keys(cssProperties)) {
const cssProp = await this.grabCssPropertyFrom(locator, prop);
if (isColorProperty(prop)) {
props.push(convertColorToRGBA(cssProperties[prop]));
props.push(convertColorToRGBA(cssProp));
} else {
props.push(cssProperties[prop]);
props.push(cssProp);
}
});
}
}

const values = Object.keys(cssPropertiesCamelCase).map(key => cssPropertiesCamelCase[key]);
if (!Array.isArray(props)) props = [props];
let chunked = chunkArray(props, values.length);
chunked = chunked.filter((val) => {
for (let i = 0; i < val.length; ++i) {
const _acutal = Number.isNaN(val[i]) || (typeof values[i]) === 'string' ? val[i] : Number.parseInt(val[i], 10);
const _expected = Number.isNaN(values[i]) || (typeof values[i]) === 'string' ? values[i] : Number.parseInt(values[i], 10);
if (_acutal !== _expected) return false;
// eslint-disable-next-line eqeqeq
if (val[i] != values[i]) return false;
}
return true;
});
Expand Down
35 changes: 15 additions & 20 deletions lib/helper/Puppeteer.js
Original file line number Diff line number Diff line change
Expand Up @@ -1770,31 +1770,26 @@ class Puppeteer extends Helper {

const cssPropertiesCamelCase = convertCssPropertiesToCamelCase(cssProperties);
const elemAmount = res.length;
const commands = [];
res.forEach((el) => {
Object.keys(cssPropertiesCamelCase).forEach((prop) => {
commands.push(el.executionContext()
.evaluate((el) => {
const style = window.getComputedStyle ? getComputedStyle(el) : el.currentStyle;
return JSON.parse(JSON.stringify(style));
}, el)
.then((props) => {
if (isColorProperty(prop)) {
return convertColorToRGBA(props[prop]);
}
return props[prop];
}));
});
});
let props = await Promise.all(commands);
let props = [];

for (const element of res) {
for (const prop of Object.keys(cssProperties)) {
const cssProp = await this.grabCssPropertyFrom(locator, prop);
if (isColorProperty(prop)) {
props.push(convertColorToRGBA(cssProp));
} else {
props.push(cssProp);
}
}
}

const values = Object.keys(cssPropertiesCamelCase).map(key => cssPropertiesCamelCase[key]);
if (!Array.isArray(props)) props = [props];
let chunked = chunkArray(props, values.length);
chunked = chunked.filter((val) => {
for (let i = 0; i < val.length; ++i) {
const _acutal = Number.isNaN(val[i]) || (typeof values[i]) === 'string' ? val[i] : Number.parseInt(val[i], 10);
const _expected = Number.isNaN(values[i]) || (typeof values[i]) === 'string' ? values[i] : Number.parseInt(values[i], 10);
if (_acutal !== _expected) return false;
// eslint-disable-next-line eqeqeq
if (val[i] != values[i]) return false;
}
return true;
});
Expand Down
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "codeceptjs",
"version": "3.5.8",
"version": "3.5.9",
"description": "Supercharged End 2 End Testing Framework for NodeJS",
"keywords": [
"acceptance",
Expand Down Expand Up @@ -59,7 +59,8 @@
"publish:site": "./runok.js publish:site",
"update-contributor-faces": "./runok.js contributor:faces",
"dtslint": "dtslint typings --localTs './node_modules/typescript/lib'",
"prepare": "husky install"
"prepare": "husky install",
"prepare-release": "./runok.js versioning && ./runok.js get:commit-log"
},
"dependencies": {
"@codeceptjs/configure": "0.10.0",
Expand Down
8 changes: 4 additions & 4 deletions runok.js
Original file line number Diff line number Diff line change
Expand Up @@ -429,14 +429,14 @@ title: ${name}
const changelog = fs.readFileSync(file).toString();

const _changelog = `## ${newVersion}\n
Thanks all to those who contributed to make this release!
❤️ Thanks all to those who contributed to make this release! ❤️

🛩️ *Features*

🐛 *Bug Fixes*

📖 *Documentation*

🛩️ *Features*

${changelog}`;

fs.writeFileSync(`./${file}`, _changelog);
Expand All @@ -448,7 +448,7 @@ ${changelog}`;

async getCommitLog() {
console.log('Gathering commits...');
const logs = await exec('git log --pretty=\'format:%s - by %aN\' $(git describe --abbrev=0 --tags)..HEAD');
const logs = await exec('git log --pretty=\'format:* %s - by @%aN\' $(git describe --abbrev=0 --tags)..HEAD');
console.log(logs.data.stdout);
},

Expand Down
16 changes: 16 additions & 0 deletions test/helper/Playwright_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ describe('Playwright', function () {
I = new Playwright({
url: siteUrl,
windowSize: '500x700',
browser: process.env.BROWSER || 'chromium',
show: false,
waitForTimeout: 5000,
waitForAction: 500,
Expand Down Expand Up @@ -112,6 +113,17 @@ describe('Playwright', function () {
});
});

describe('#seeCssPropertiesOnElements', () => {
it('should check background-color css property for given element', async () => {
try {
await I.amOnPage('https://codecept.io/helpers/Playwright/');
await I.seeCssPropertiesOnElements('.navbar', { 'background-color': 'rgb(128, 90, 213)' });
} catch (e) {
e.message.should.include('expected element (.navbar) to have CSS property { \'background-color\': \'rgb(128, 90, 213)\' }');
}
});
});

webApiTests.tests();

describe('#click', () => {
Expand Down Expand Up @@ -1051,6 +1063,7 @@ describe('Playwright', function () {

describe('#startRecordingWebSocketMessages, #grabWebSocketMessages, #stopRecordingWebSocketMessages', () => {
it('should throw error when calling grabWebSocketMessages before startRecordingWebSocketMessages', () => {
if (process.env.BROWSER === 'firefox') this.skip();
try {
I.amOnPage('https://websocketstest.com/');
I.waitForText('Work for You!');
Expand All @@ -1061,6 +1074,7 @@ describe('Playwright', function () {
});

it('should flush the WS messages', async () => {
if (process.env.BROWSER === 'firefox') this.skip();
await I.startRecordingWebSocketMessages();
I.amOnPage('https://websocketstest.com/');
I.waitForText('Work for You!');
Expand All @@ -1070,6 +1084,7 @@ describe('Playwright', function () {
});

it('should see recording WS messages', async () => {
if (process.env.BROWSER === 'firefox') this.skip();
await I.startRecordingWebSocketMessages();
await I.amOnPage('https://websocketstest.com/');
I.waitForText('Work for You!');
Expand All @@ -1078,6 +1093,7 @@ describe('Playwright', function () {
});

it('should not see recording WS messages', async () => {
if (process.env.BROWSER === 'firefox') this.skip();
await I.startRecordingWebSocketMessages();
await I.amOnPage('https://websocketstest.com/');
I.waitForText('Work for You!');
Expand Down
Loading