diff --git a/CHANGELOG.md b/CHANGELOG.md index 46fb100df..9ce6dd1b6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,34 @@ +## 3.7.3 + +â¤ī¸ Thanks all to those who contributed to make this release! â¤ī¸ + +đŸ›Šī¸ _Features_ + +- feat(cli): improve info command to return installed browsers (#4890) - by @kobenguyent + +``` +➜ helloworld npx codeceptjs info +Environment information: + +codeceptVersion: "3.7.2" +nodeInfo: 18.19.0 +osInfo: macOS 14.4 +cpuInfo: (8) x64 Apple M1 Pro +osBrowsers: "chrome: 133.0.6943.143, edge: 133.0.3065.92, firefox: not installed, safari: 17.4" +playwrightBrowsers: "chromium: 133.0.6943.16, firefox: 134.0, webkit: 18.2" +helpers: { +"Playwright": { +"url": "http://localhost", +... +``` + +🐛 _Bug Fixes_ + +- fix: resolving path inconsistency in container.js and appium.js (#4866) - by @mjalav +- fix: broken screenshot links in mochawesome reports (#4889) - by @kobenguyent +- some internal fixes to make UTs more stable by @thomashohn +- dependencies upgrades by @thomashohn + ## 3.7.2 â¤ī¸ Thanks all to those who contributed to make this release! â¤ī¸ diff --git a/lib/command/info.js b/lib/command/info.js index e0abe2433..05dff5bf9 100644 --- a/lib/command/info.js +++ b/lib/command/info.js @@ -3,6 +3,42 @@ const envinfo = require('envinfo') const { getConfig, getTestRoot } = require('./utils') const Codecept = require('../codecept') const output = require('../output') +const { execSync } = require('child_process') + +async function getPlaywrightBrowsers() { + try { + const regex = /(chromium|firefox|webkit)\s+version\s+([\d.]+)/gi + let versions = [] + + const info = execSync('npx playwright install --dry-run').toString().trim() + + const matches = [...info.matchAll(regex)] + + matches.forEach(match => { + const browser = match[1] + const version = match[2] + versions.push(`${browser}: ${version}`) + }) + + return versions.join(', ') + } catch (err) { + return 'Playwright not installed' + } +} + +async function getOsBrowsers() { + const chromeInfo = await envinfo.helpers.getChromeInfo() + const edgeInfo = await envinfo.helpers.getEdgeInfo() + const firefoxInfo = await envinfo.helpers.getFirefoxInfo() + const safariInfo = await envinfo.helpers.getSafariInfo() + + return [ + `chrome: ${chromeInfo ? chromeInfo[1] : 'not installed'}`, + `edge: ${edgeInfo ? edgeInfo[1] : 'not installed'}`, + `firefox: ${firefoxInfo ? firefoxInfo[1] : 'not installed'}`, + `safari: ${safariInfo ? safariInfo[1] : 'not installed'}`, + ].join(', ') +} module.exports = async function (path) { const testsPath = getTestRoot(path) @@ -10,16 +46,15 @@ module.exports = async function (path) { const codecept = new Codecept(config, {}) codecept.init(testsPath) - output.print('\n Environment information:-\n') + output.print('\n Environment information: \n') const info = {} info.codeceptVersion = Codecept.version() info.nodeInfo = await envinfo.helpers.getNodeInfo() info.osInfo = await envinfo.helpers.getOSInfo() info.cpuInfo = await envinfo.helpers.getCPUInfo() - info.chromeInfo = await envinfo.helpers.getChromeInfo() - info.edgeInfo = await envinfo.helpers.getEdgeInfo() - info.firefoxInfo = await envinfo.helpers.getFirefoxInfo() - info.safariInfo = await envinfo.helpers.getSafariInfo() + info.osBrowsers = await getOsBrowsers() + info.playwrightBrowsers = await getPlaywrightBrowsers() + const { helpers, plugins } = config info.helpers = helpers || "You don't use any helpers" info.plugins = plugins || "You don't have any enabled plugins" @@ -47,6 +82,7 @@ module.exports.getMachineInfo = async () => { edgeInfo: await envinfo.helpers.getEdgeInfo(), firefoxInfo: await envinfo.helpers.getFirefoxInfo(), safariInfo: await envinfo.helpers.getSafariInfo(), + playwrightBrowsers: await getPlaywrightBrowsers(), } output.print('***************************************') diff --git a/lib/container.js b/lib/container.js index 66c7c1610..68b26ecce 100644 --- a/lib/container.js +++ b/lib/container.js @@ -469,7 +469,7 @@ function loadGherkinSteps(paths) { loadSupportObject(path, `Step Definition from ${path}`) } } else { - const folderPath = paths.startsWith('.') ? path.join(global.codecept_dir, paths) : '' + const folderPath = paths.startsWith('.') ? normalizeAndJoin(global.codecept_dir, paths) : '' if (folderPath !== '') { globSync(folderPath).forEach(file => { loadSupportObject(file, `Step Definition from ${file}`) @@ -562,3 +562,16 @@ function getHelperModuleName(helperName, config) { // built-in helpers return `./helper/${helperName}` } +function normalizeAndJoin(basePath, subPath) { + // Normalize and convert slashes to forward slashes in one step + const normalizedBase = path.posix.normalize(basePath.replace(/\\/g, '/')) + const normalizedSub = path.posix.normalize(subPath.replace(/\\/g, '/')) + + // If subPath is absolute (starts with "/"), return it as the final path + if (normalizedSub.startsWith('/')) { + return normalizedSub + } + + // Join the paths using POSIX-style + return path.posix.join(normalizedBase, normalizedSub) +} diff --git a/lib/helper/Appium.js b/lib/helper/Appium.js index 2519d68eb..fcb304263 100644 --- a/lib/helper/Appium.js +++ b/lib/helper/Appium.js @@ -383,8 +383,10 @@ class Appium extends Webdriver { _buildAppiumEndpoint() { const { protocol, port, hostname, path } = this.browser.options + // Ensure path does NOT end with a slash to prevent double slashes + const normalizedPath = path.replace(/\/$/, '') // Build path to Appium REST API endpoint - return `${protocol}://${hostname}:${port}${path}/session/${this.browser.sessionId}` + return `${protocol}://${hostname}:${port}${normalizedPath}/session/${this.browser.sessionId}` } /** diff --git a/lib/helper/Mochawesome.js b/lib/helper/Mochawesome.js index abc143af4..9c20cd1d7 100644 --- a/lib/helper/Mochawesome.js +++ b/lib/helper/Mochawesome.js @@ -4,6 +4,7 @@ let currentSuite const Helper = require('@codeceptjs/helper') const { clearString } = require('../utils') +const { testToFileName } = require('../mocha/test') class Mochawesome extends Helper { constructor(config) { @@ -50,7 +51,7 @@ class Mochawesome extends Helper { fileName = clearString(`${test.title}_${currentTest.test.title}`) } else { currentTest = { test } - fileName = clearString(test.title) + fileName = `${testToFileName(test)}` } if (this.options.uniqueScreenshotNames) { const uuid = test.uuid || test.ctx.test.uuid diff --git a/package.json b/package.json index e47101671..14ce78aca 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "codeceptjs", - "version": "3.7.2", + "version": "3.7.3", "description": "Supercharged End 2 End Testing Framework for NodeJS", "keywords": [ "acceptance", @@ -75,15 +75,15 @@ "publish-beta": "./runok.js publish:next-beta-version" }, "dependencies": { - "@codeceptjs/configure": "1.0.2", + "@codeceptjs/configure": "1.0.3", "@codeceptjs/helper": "2.0.4", "@cucumber/cucumber-expressions": "18", - "@cucumber/gherkin": "31", + "@cucumber/gherkin": "32", "@cucumber/messages": "27.2.0", - "@xmldom/xmldom": "0.9.7", - "acorn": "8.14.0", + "@xmldom/xmldom": "0.9.8", + "acorn": "8.14.1", "arrify": "3.0.0", - "axios": "1.7.9", + "axios": "1.8.3", "chalk": "4.1.2", "cheerio": "^1.0.0", "commander": "11.1.0", @@ -101,12 +101,12 @@ "inquirer": "8.2.6", "invisi-data": "^1.0.0", "joi": "17.13.3", - "js-beautify": "1.15.2", + "js-beautify": "1.15.4", "lodash.clonedeep": "4.5.0", "lodash.merge": "4.6.2", "mkdirp": "3.0.1", "mocha": "11.1.0", - "monocart-coverage-reports": "2.12.1", + "monocart-coverage-reports": "2.12.3", "ms": "2.1.3", "ora-classic": "5.4.2", "parse-function": "5.6.10", @@ -114,38 +114,38 @@ "promise-retry": "1.1.1", "resq": "1.11.0", "sprintf-js": "1.1.3", - "uuid": "11.0.5" + "uuid": "11.1.0" }, "optionalDependencies": { - "@codeceptjs/detox-helper": "1.1.5" + "@codeceptjs/detox-helper": "1.1.7" }, "devDependencies": { "@apollo/server": "^4", - "@codeceptjs/expect-helper": "^0.2.2", + "@codeceptjs/expect-helper": "^1.0.1", "@codeceptjs/mock-request": "0.3.1", - "@eslint/eslintrc": "3.2.0", - "@eslint/js": "9.19.0", - "@faker-js/faker": "9.4.0", + "@eslint/eslintrc": "3.3.0", + "@eslint/js": "9.22.0", + "@faker-js/faker": "9.6.0", "@pollyjs/adapter-puppeteer": "6.0.6", - "@pollyjs/core": "5.1.0", - "@types/chai": "4.3.19", + "@pollyjs/core": "6.0.6", + "@types/chai": "5.2.0", "@types/inquirer": "9.0.7", - "@types/node": "22.13.0", - "@wdio/sauce-service": "9.7.2", + "@types/node": "22.13.10", + "@wdio/sauce-service": "9.12.0", "@wdio/selenium-standalone-service": "8.15.0", - "@wdio/utils": "9.7.2", - "@xmldom/xmldom": "0.9.7", + "@wdio/utils": "9.11.0", + "@xmldom/xmldom": "0.9.8", "chai": "^4.0.0", "chai-as-promised": "7.1.2", "chai-subset": "1.6.0", "documentation": "14.0.3", - "electron": "34.0.2", - "eslint": "^9.19.0", + "electron": "35.0.1", + "eslint": "^9.21.0", "eslint-plugin-import": "2.31.0", "eslint-plugin-mocha": "10.5.0", "expect": "29.7.0", "express": "4.21.2", - "globals": "15.14.0", + "globals": "16.0.0", "graphql": "16.10.0", "graphql-tag": "^2.12.6", "husky": "9.1.7", @@ -153,25 +153,25 @@ "jsdoc": "^3.6.11", "jsdoc-typeof-plugin": "1.0.0", "json-server": "0.17.4", - "playwright": "1.50.1", + "playwright": "1.51.0", "prettier": "^3.3.2", - "puppeteer": "24.1.1", + "puppeteer": "24.4.0", "qrcode-terminal": "0.12.0", "rosie": "2.1.1", "runok": "0.9.3", "semver": "7.7.1", "sinon": "19.0.2", "sinon-chai": "3.7.0", - "testcafe": "3.7.1", + "testcafe": "3.7.2", "ts-morph": "25.0.1", "ts-node": "10.9.2", "tsd": "^0.31.0", "tsd-jsdoc": "2.5.0", - "typedoc": "0.27.7", - "typedoc-plugin-markdown": "4.4.1", - "typescript": "5.7.3", + "typedoc": "0.28.0", + "typedoc-plugin-markdown": "4.5.0", + "typescript": "5.8.2", "wdio-docker-service": "3.2.1", - "webdriverio": "9.7.2", + "webdriverio": "9.12.0", "xml2js": "0.6.2", "xpath": "0.0.34" }, diff --git a/test/acceptance/session_test.js b/test/acceptance/session_test.js index 98812a331..427cce264 100644 --- a/test/acceptance/session_test.js +++ b/test/acceptance/session_test.js @@ -46,8 +46,8 @@ Scenario('screenshots reflect the current page of current session @Puppeteer @Pl }) Scenario('Different cookies for different sessions @Playwright @Puppeteer', async ({ I }) => { - const cookiePage = 'https://www.microsoft.com/en-au/' - const cookieName = 'MUID' + const cookiePage = 'https://google.com/' + const cookieName = 'AEC' const cookies = {} I.amOnPage(cookiePage) @@ -68,7 +68,7 @@ Scenario('Different cookies for different sessions @Playwright @Puppeteer', asyn cookies.mary = (await I.grabCookie(cookieName)).value I.say(`${cookieName}: ${cookies.mary}`) }) - await I.seeInCurrentUrl('en-au') + await I.seeInCurrentUrl('google.com') assert(cookies.default) assert(cookies.john) assert(cookies.mary)