diff --git a/.github/workflows/acceptance-tests.yml b/.github/workflows/acceptance-tests.yml index 63107de63..92315d047 100644 --- a/.github/workflows/acceptance-tests.yml +++ b/.github/workflows/acceptance-tests.yml @@ -29,7 +29,7 @@ jobs: # Install Docker Compose - name: Install Docker Compose run: | - sudo apt-get update + sudo apt-get update --allow-releaseinfo-change sudo apt-get install -y docker-compose # Run rest tests using docker-compose diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index a33def88e..bba6560b4 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -9,11 +9,21 @@ jobs: push_to_registry: name: Build and push Docker image to Docker Hub runs-on: ubuntu-22.04 - if: startsWith(github.event.ref_name, 'release-') + env: + DOCKER_REPO: ${{ secrets.DOCKERHUB_REPOSITORY }} steps: - - name: Check out the repo with the latest code + - name: Checkout code uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Get version from package.json + id: get_version + run: | + VERSION=$(jq -r .version package.json) + echo "VERSION=$VERSION" >> $GITHUB_ENV + echo "version=$VERSION" >> $GITHUB_OUTPUT - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 @@ -24,19 +34,19 @@ jobs: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_PASSWORD }} - - name: Get the current tag - id: currentTag + - name: Check if Docker tag exists on Docker Hub + id: tag_check run: | - git fetch --prune --unshallow - TAG=$(git describe --tags --abbrev=0) - echo $TAG - echo "TAG=$TAG" >> $GITHUB_ENV + STATUS_CODE=$(curl -s -o /dev/null -w "%{http_code}" \ + https://hub.docker.com/v2/repositories/${{ env.DOCKER_REPO }}/tags/${{ steps.get_version.outputs.version }}/) + echo "status_code=$STATUS_CODE" >> $GITHUB_OUTPUT - name: Build and push Docker image + if: steps.tag_check.outputs.status_code != '200' uses: docker/build-push-action@v6 with: context: . push: true tags: | - ${{ secrets.DOCKERHUB_REPOSITORY }}:latest - ${{ secrets.DOCKERHUB_REPOSITORY }}:${{ env.TAG }} + ${{ env.DOCKER_REPO }}:latest + ${{ env.DOCKER_REPO }}:${{ env.VERSION }} \ No newline at end of file diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index 1004d072f..d00fe7196 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -35,6 +35,9 @@ jobs: npm i --force env: PUPPETEER_SKIP_CHROMIUM_DOWNLOAD: true + - name: Allow Release info Change + run: | + sudo apt-get update --allow-releaseinfo-change - name: Install browsers and deps run: npx playwright install && npx playwright install-deps - name: check diff --git a/.github/workflows/plugin.yml b/.github/workflows/plugin.yml index ec456fa7f..6795da6bf 100644 --- a/.github/workflows/plugin.yml +++ b/.github/workflows/plugin.yml @@ -15,7 +15,6 @@ env: jobs: build: - runs-on: ubuntu-22.04 strategy: @@ -23,22 +22,25 @@ jobs: node-version: [20.x] steps: - - uses: actions/checkout@v4 - - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v4 - with: - node-version: ${{ matrix.node-version }} - - uses: shivammathur/setup-php@v2 - with: - php-version: 7.4 - - name: npm install - run: | - npm i --force - env: - PUPPETEER_SKIP_CHROMIUM_DOWNLOAD: true - - name: Install browsers and deps - run: npx playwright install chromium && npx playwright install-deps - - name: start a server - run: "php -S 127.0.0.1:8000 -t test/data/app &" - - name: run plugin tests - run: npm run test:plugin + - uses: actions/checkout@v4 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + - uses: shivammathur/setup-php@v2 + with: + php-version: 7.4 + - name: npm install + run: | + npm i --force + env: + PUPPETEER_SKIP_CHROMIUM_DOWNLOAD: true + - name: Allow Release info Change + run: | + sudo apt-get update --allow-releaseinfo-change + - name: Install browsers and deps + run: npx playwright install chromium && npx playwright install-deps + - name: start a server + run: 'php -S 127.0.0.1:8000 -t test/data/app &' + - name: run plugin tests + run: npm run test:plugin diff --git a/.gitignore b/.gitignore index 899a0b988..fc1f70320 100644 --- a/.gitignore +++ b/.gitignore @@ -16,6 +16,7 @@ examples/output examples/selenoid-example/output test/data/app/db test/data/sandbox/steps.d.ts +test/data/sandbox/configs/custom-reporter-plugin/output/result.json testpullfilecache* .DS_Store package-lock.json diff --git a/Dockerfile b/Dockerfile index 7d4f6bc9d..d637da4b5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,6 +2,8 @@ FROM mcr.microsoft.com/playwright:v1.48.1-noble ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD true +RUN apt-get update --allow-releaseinfo-change + # Installing the pre-required packages and libraries RUN apt-get update && \ apt-get install -y libgtk2.0-0 \ diff --git a/docs/plugins.md b/docs/plugins.md index 682b24bd9..b2f967ae9 100644 --- a/docs/plugins.md +++ b/docs/plugins.md @@ -9,7 +9,7 @@ title: Plugins ## analyze -Uses AI to analyze test failures and provide insights. +Uses AI to analyze test failures and provide insights This plugin analyzes failed tests using AI to provide detailed explanations and group similar failures. When enabled with --ai flag, it generates reports after test execution. @@ -60,10 +60,12 @@ exports.config = { ### Parameters - `config` **[Object][1]** Plugin configuration (optional, default `{}`) - Returns **void** -## authLogs user in for the first test and reuses session for next tests. +Returns **void** +## auth + +Logs user in for the first test and reuses session for next tests. Works by saving cookies into memory or file. If a session expires automatically logs in again. @@ -294,13 +296,17 @@ Scenario('login', async ({ I, login }) => { - `config` -## autoDelaySometimes it takes some time for a page to respond to user's actions. +## autoDelay +Sometimes it takes some time for a page to respond to user's actions. Depending on app's performance this can be either slow or fast. + For instance, if you click a button and nothing happens - probably JS event is not attached to this button yet Also, if you fill field and input validation doesn't accept your input - maybe because you typed value too fast. + This plugin allows to slow down tests execution when a test running too fast. It puts a tiny delay for before and after action commands. + Commands affected (by default): - `click` @@ -332,110 +338,112 @@ Possible config options: ## commentStep -### Parameters +This plugin is **deprecated**, use `Section` instead. -- `config` +Add descriptive nested steps for your tests: -**Meta** +```js +Scenario('project update test', async I => { + __`Given` + const projectId = await I.have('project') -- **deprecated**: Add descriptive nested steps for your tests: + __`When` + projectPage.update(projectId, { title: 'new title' }) - ```js - Scenario('project update test', async I => { - __`Given` - const projectId = await I.have('project') + __`Then` + projectPage.open(projectId) + I.see('new title', 'h1') +}) +``` - __`When` - projectPage.update(projectId, { title: 'new title' }) +Steps prefixed with `__` will be printed as nested steps in `--steps` output: - __`Then` - projectPage.open(projectId) - I.see('new title', 'h1') - }) - ``` + Given + I have "project" + When + projectPage update + Then + projectPage open + I see "new title", "h1" - Steps prefixed with `__` will be printed as nested steps in `--steps` output: +Also those steps will be exported to allure reports. - Given - I have "project" - When - projectPage update - Then - projectPage open - I see "new title", "h1" +This plugin can be used - Also those steps will be exported to allure reports. +### Config - This plugin can be used +- `enabled` - (default: false) enable a plugin +- `registerGlobal` - (default: false) register `__` template literal function globally. You can override function global name by providing a name as a value. - ### Config +### Examples - - `enabled` - (default: false) enable a plugin - - `registerGlobal` - (default: false) register `__` template literal function globally. You can override function global name by providing a name as a value. +Registering `__` globally: - ### Examples +```js +plugins: { + commentStep: { + enabled: true, + registerGlobal: true + } +} +``` - Registering `__` globally: +Registering `Step` globally: - ```js - plugins: { - commentStep: { - enabled: true, - registerGlobal: true - } +```js +plugins: { + commentStep: { + enabled: true, + registerGlobal: 'Step' } - ``` +} +``` - Registering `Step` globally: +Using only local function names: - ```js - plugins: { - commentStep: { - enabled: true, - registerGlobal: 'Step' - } +```js +plugins: { + commentStep: { + enabled: true } - ``` +} +``` - Using only local function names: +Then inside a test import a comment function from a plugin. +For instance, you can prepare Given/When/Then functions to use them inside tests: - ```js - plugins: { - commentStep: { - enabled: true - } - } - ``` +```js +// inside a test +const step = codeceptjs.container.plugins('commentStep') - Then inside a test import a comment function from a plugin. - For instance, you can prepare Given/When/Then functions to use them inside tests: +const Given = () => step`Given` +const When = () => step`When` +const Then = () => step`Then` +``` - ```js - // inside a test - const step = codeceptjs.container.plugins('commentStep') +Scenario('project update test', async (I) => { +Given(); +const projectId = await I.have('project'); - const Given = () => step`Given` - const When = () => step`When` - const Then = () => step`Then` - ``` +When(); +projectPage.update(projectId, { title: 'new title' }); - Scenario('project update test', async (I) => { - Given(); - const projectId = await I.have('project'); +Then(); +projectPage.open(projectId); +I.see('new title', 'h1'); +}); + +``` - When(); - projectPage.update(projectId, { title: 'new title' }); +``` - Then(); - projectPage.open(projectId); - I.see('new title', 'h1'); - }); +### Parameters - ``` +- `config` - ``` +## coverage -## coverageDumps code coverage from Playwright/Puppeteer after every test. +Dumps code coverage from Playwright/Puppeteer after every test. #### Configuration @@ -462,7 +470,9 @@ Possible config options, More could be found at [monocart-coverage-reports][2] - `config` -## customLocatorCreates a [custom locator][3] by using special attributes in HTML. +## customLocator + +Creates a [custom locator][3] by using special attributes in HTML. If you have a convention to use `data-test-id` or `data-qa` attributes to mark active elements for e2e tests, you can enable this plugin to simplify matching elements with these attributes: @@ -569,20 +579,25 @@ I.click('=sign-up') // matches => [data-qa=sign-up],[data-test=sign-up] - `config` -## customReporterSample custom reporter for CodeceptJS. +## customReporter + +Sample custom reporter for CodeceptJS. ### Parameters - `config` -## eachElementProvides `eachElement` global function to iterate over found elements to perform actions on them. +## eachElement + +Provides `eachElement` global function to iterate over found elements to perform actions on them. `eachElement` takes following args: - `purpose` - the goal of an action. A comment text that will be displayed in output. - `locator` - a CSS/XPath locator to match elements - `fn(element, index)` - **asynchronous** function which will be executed for each matched element. - Example of usage: + +Example of usage: ```js // this example works with Playwright and Puppeteer helper @@ -611,6 +626,7 @@ await eachElement('check all items are visible', '.item', async el => { ``` This method works with WebDriver, Playwright, Puppeteer, Appium helpers. + Function parameter `el` represents a matched element. Depending on a helper API of `el` can be different. Refer to API of corresponding browser testing engine for a complete API list: @@ -621,7 +637,8 @@ Depending on a helper API of `el` can be different. Refer to API of correspondin #### Configuration - `registerGlobal` - to register `eachElement` function globally, true by default - If `registerGlobal` is false you can use eachElement from the plugin: + +If `registerGlobal` is false you can use eachElement from the plugin: ```js const eachElement = codeceptjs.container.plugins('eachElement') @@ -632,13 +649,23 @@ const eachElement = codeceptjs.container.plugins('eachElement') - `purpose` **[string][7]** - `locator` **CodeceptJS.LocatorOrString** - `fn` **[Function][8]** - Returns **([Promise][9]\ | [undefined][10])** -## fakerTransformUse the `@faker-js/faker` package to generate fake data inside examples on your gherkin tests +Returns **([Promise][9]\ | [undefined][10])** + +## fakerTransform + +Use the `@faker-js/faker` package to generate fake data inside examples on your gherkin tests #### Usage -To start please install `@faker-js/faker` package npm install -D @faker-js/faker yarn add -D @faker-js/faker +To start please install `@faker-js/faker` package + + npm install -D @faker-js/faker + + + + yarn add -D @faker-js/faker + Add this plugin to config file: ```js @@ -665,7 +692,9 @@ Scenario Outline: ... - `config` -## healSelf-healing tests with AI. +## heal + +Self-healing tests with AI. Read more about heaking in [Self-Healing Tests][11] @@ -685,11 +714,14 @@ More config options are available: - `config` (optional, default `{}`) -## pageInfoCollects information from web page after each failed test and adds it to the test as an artifact. +## pageInfo +Collects information from web page after each failed test and adds it to the test as an artifact. It is suggested to enable this plugin if you run tests on CI and you need to debug failed tests. This plugin can be paired with `analyze` plugin to provide more context. + It collects URL, HTML errors (by classes), and browser logs. + Enable this plugin in config: ```js @@ -708,7 +740,9 @@ Additional config options: - `config` (optional, default `{}`) -## pauseOnFailAutomatically launches [interactive pause][12] when a test fails. +## pauseOnFail + +Automatically launches [interactive pause][12] when a test fails. Useful for debugging flaky tests on local environment. Add this plugin to config file: @@ -720,9 +754,13 @@ plugins: { ``` Unlike other plugins, `pauseOnFail` is not recommended to be enabled by default. -Enable it manually on each run via `-p` option: npx codeceptjs run -p pauseOnFail +Enable it manually on each run via `-p` option: + + npx codeceptjs run -p pauseOnFail + +## retryFailedStep -## retryFailedStepRetries each failed step in a test. +Retries each failed step in a test. Add this plugin to config file: @@ -734,7 +772,9 @@ plugins: { } ``` -Run tests with plugin enabled: npx codeceptjs run --plugins retryFailedStep +Run tests with plugin enabled: + + npx codeceptjs run --plugins retryFailedStep #### Configuration: @@ -769,7 +809,9 @@ plugins: { } ``` -#### Disable Per TestThis plugin can be disabled per test. In this case you will need to stet `I.retry()` to all flaky steps: +#### Disable Per Test + +This plugin can be disabled per test. In this case you will need to stet `I.retry()` to all flaky steps: Use scenario configuration to disable plugin for a test @@ -783,9 +825,12 @@ Scenario('scenario tite', { disableRetryFailedStep: true }, () => { - `config` -## screenshotOnFailCreates screenshot on failure. Screenshot is saved into `output` directory. +## screenshotOnFail + +Creates screenshot on failure. Screenshot is saved into `output` directory. Initially this functionality was part of corresponding helper but has been moved into plugin since 1.4 + This plugin is **enabled by default**. #### Configuration @@ -809,8 +854,9 @@ Possible config options: - `config` -## selenoid[Selenoid][13] plugin automatically starts browsers and video recording. +## selenoid +[Selenoid][13] plugin automatically starts browsers and video recording. Works with WebDriver helper. ### Prerequisite @@ -829,6 +875,7 @@ Selenoid plugin can be started in two ways: #### Automatic If you are new to Selenoid and you want plug and play setup use automatic mode. + Add plugin configuration in `codecept.conf.js`: ```js @@ -847,6 +894,7 @@ plugins: { When `autoCreate` is enabled it will pull the [latest Selenoid from DockerHub][15] and start Selenoid automatically. It will also create `browsers.json` file required by Selenoid. + In automatic mode the latest version of browser will be used for tests. It is recommended to specify exact version of each browser inside `browsers.json` file. > **If you are using Windows machine or if `autoCreate` does not work properly, create container manually** @@ -860,7 +908,8 @@ This is especially useful for Continous Integration server as you can configure 1. Create `browsers.json` file in the same directory `codecept.conf.js` is located [Refer to Selenoid documentation][16] to know more about browsers.json. - _Sample browsers.json_ + +_Sample browsers.json_ ```js { @@ -878,8 +927,12 @@ This is especially useful for Continous Integration server as you can configure ``` > It is recommended to use specific versions of browsers in `browsers.json` instead of latest. This will prevent tests fail when browsers will be updated. -> **⚠ At first launch selenoid plugin takes extra time to download all Docker images before tests starts**. 2. Create Selenoid container -> Run the following command to create a container. To know more [refer here][17] + +**⚠ At first launch selenoid plugin takes extra time to download all Docker images before tests starts**. + +2. Create Selenoid container + +Run the following command to create a container. To know more [refer here][17] ```bash docker create \ @@ -895,8 +948,10 @@ aerokube/selenoid:latest-release ### Video Recording This plugin allows to record and save video per each executed tests. + When `enableVideo` is `true` this plugin saves video in `output/videos` directory with each test by name To save space videos for all succesful tests are deleted. This can be changed by `deletePassed` option. + When `allure` plugin is enabled a video is attached to report automatically. ### Options: @@ -916,12 +971,17 @@ When `allure` plugin is enabled a video is attached to report automatically. - `config` -## stepByStepReport![step-by-step-report][19] +## stepByStepReport + +![step-by-step-report][19] Generates step by step report for a test. After each step in a test a screenshot is created. After test executed screenshots are combined into slideshow. By default, reports are generated only for failed tests. -Run tests with plugin enabled: npx codeceptjs run --plugins stepByStepReport + +Run tests with plugin enabled: + + npx codeceptjs run --plugins stepByStepReport #### Configuration @@ -947,7 +1007,9 @@ Possible config options: - `config` **any** -## stepTimeoutSet timeout for test steps globally. +## stepTimeout + +Set timeout for test steps globally. Add this plugin to config file: @@ -959,7 +1021,9 @@ plugins: { } ``` -Run tests with plugin enabled: npx codeceptjs run --plugins stepTimeout +Run tests with plugin enabled: + + npx codeceptjs run --plugins stepTimeout #### Configuration: @@ -1000,7 +1064,9 @@ plugins: { - `config` -## subtitlesAutomatically captures steps as subtitle, and saves it as an artifact when a video is found for a failed test +## subtitles + +Automatically captures steps as subtitle, and saves it as an artifact when a video is found for a failed test #### Configuration @@ -1012,7 +1078,9 @@ plugins: { } ``` -## wdioWebdriverio services runner. +## wdio + +Webdriverio services runner. This plugin allows to run webdriverio services like: @@ -1021,19 +1089,22 @@ This plugin allows to run webdriverio services like: - testingbot - browserstack - appium - A complete list of all available services can be found on [webdriverio website][20]. + +A complete list of all available services can be found on [webdriverio website][20]. #### Setup 1. Install a webdriverio service 2. Enable `wdio` plugin in config 3. Add service name to `services` array inside wdio plugin config. - See examples below: + +See examples below: #### Selenium Standalone Service Install ` @wdio/selenium-standalone-service` package, as [described here][21]. It is important to make sure it is compatible with current webdriverio version. + Enable `wdio` plugin in plugins list and add `selenium-standalone` service: ```js @@ -1050,6 +1121,7 @@ plugins: { Install `@wdio/sauce-service` package, as [described here][22]. It is important to make sure it is compatible with current webdriverio version. + Enable `wdio` plugin in plugins list and add `sauce` service: ```js @@ -1064,7 +1136,9 @@ plugins: { } ``` -\*\*\*In the same manner additional services from webdriverio can be installed, enabled, and configured. +--- + +In the same manner additional services from webdriverio can be installed, enabled, and configured. #### Configuration diff --git a/lib/helper/Mochawesome.js b/lib/helper/Mochawesome.js index 9c20cd1d7..0f45ff723 100644 --- a/lib/helper/Mochawesome.js +++ b/lib/helper/Mochawesome.js @@ -1,4 +1,3 @@ -let addMochawesomeContext let currentTest let currentSuite @@ -16,7 +15,8 @@ class Mochawesome extends Helper { disableScreenshots: false, } - addMochawesomeContext = require('mochawesome/addContext') + this._addContext = require('mochawesome/addContext') + this._createConfig(config) } @@ -44,28 +44,27 @@ class Mochawesome extends Helper { if (this.options.disableScreenshots) return let fileName // Get proper name if we are fail on hook - if (test.ctx.test.type === 'hook') { + if (test.ctx?.test?.type === 'hook') { currentTest = { test: test.ctx.test } // ignore retries if we are in hook test._retries = -1 fileName = clearString(`${test.title}_${currentTest.test.title}`) } else { currentTest = { test } - fileName = `${testToFileName(test)}` + fileName = testToFileName(test) } if (this.options.uniqueScreenshotNames) { - const uuid = test.uuid || test.ctx.test.uuid - fileName = `${fileName.substring(0, 10)}_${uuid}` + fileName = testToFileName(test, { unique: true }) } if (test._retries < 1 || test._retries === test.retryNum) { fileName = `${fileName}.failed.png` - return addMochawesomeContext(currentTest, fileName) + return this._addContext(currentTest, fileName) } } addMochawesomeContext(context) { if (currentTest === '') currentTest = { test: currentSuite.ctx.test } - return addMochawesomeContext(currentTest, context) + return this._addContext(currentTest, context) } } diff --git a/lib/mocha/test.js b/lib/mocha/test.js index 14c202085..7ff53721d 100644 --- a/lib/mocha/test.js +++ b/lib/mocha/test.js @@ -135,9 +135,19 @@ function cloneTest(test) { return deserializeTest(serializeTest(test)) } -function testToFileName(test, suffix = '') { +/** + * Get a filename from the test object + * @param {CodeceptJS.Test} test + * @param {Object} options + * @param {string} options.suffix Add a suffix to the filename + * @param {boolean} options.unique Add a unique suffix to the file + * + * @returns {string} the filename + */ +function testToFileName(test, { suffix = '', unique = false } = {}) { let fileName = test.title + if (unique) fileName = `${fileName}_${test?.uid || Math.floor(new Date().getTime() / 1000)}` if (suffix) fileName = `${fileName}_${suffix}` // remove tags with empty string (disable for now) // fileName = fileName.replace(/\@\w+/g, '') @@ -151,6 +161,7 @@ function testToFileName(test, suffix = '') { // fileName = `${clearString(test.parent.title)}_${fileName}` // } fileName = clearString(fileName).slice(0, 100) + return fileName } diff --git a/lib/pause.js b/lib/pause.js index c4ba0a0e1..de887a08a 100644 --- a/lib/pause.js +++ b/lib/pause.js @@ -175,7 +175,12 @@ async function parseInput(cmd) { output.print(output.styles.success(' OK '), cmd) } if (cmd?.startsWith('I.grab')) { - output.print(output.styles.debug(val)) + try { + output.print(output.styles.debug(JSON.stringify(val, null, 2))) + } catch (err) { + output.print(output.styles.error(' ERROR '), 'Failed to stringify result:', err.message) + output.print(output.styles.error(' RAW VALUE '), String(val)) + } } history.push(cmd) // add command to history when successful diff --git a/lib/plugin/commentStep.js b/lib/plugin/commentStep.js index b368f3ca1..6d6601bb5 100644 --- a/lib/plugin/commentStep.js +++ b/lib/plugin/commentStep.js @@ -7,7 +7,7 @@ let currentCommentStep const defaultGlobalName = '__' /** - * @deprecated + * This plugin is **deprecated**, use `Section` instead. * * Add descriptive nested steps for your tests: * diff --git a/lib/plugin/screenshotOnFail.js b/lib/plugin/screenshotOnFail.js index aeba20834..04e855f92 100644 --- a/lib/plugin/screenshotOnFail.js +++ b/lib/plugin/screenshotOnFail.js @@ -86,7 +86,7 @@ module.exports = function (config) { let fileName if (options.uniqueScreenshotNames && test) { - fileName = `${testToFileName(test, _getUUID(test))}.failed.png` + fileName = `${testToFileName(test, { unique: true })}.failed.png` } else { fileName = `${testToFileName(test)}.failed.png` } @@ -137,12 +137,4 @@ module.exports = function (config) { true, ) }) - - function _getUUID(test) { - if (test.uid) { - return test.uid - } - - return Math.floor(new Date().getTime() / 1000) - } } diff --git a/package.json b/package.json index 14ce78aca..5568d63e6 100644 --- a/package.json +++ b/package.json @@ -75,15 +75,15 @@ "publish-beta": "./runok.js publish:next-beta-version" }, "dependencies": { - "@codeceptjs/configure": "1.0.3", + "@codeceptjs/configure": "1.0.6", "@codeceptjs/helper": "2.0.4", "@cucumber/cucumber-expressions": "18", - "@cucumber/gherkin": "32", + "@cucumber/gherkin": "32.1.2", "@cucumber/messages": "27.2.0", "@xmldom/xmldom": "0.9.8", "acorn": "8.14.1", "arrify": "3.0.0", - "axios": "1.8.3", + "axios": "1.8.4", "chalk": "4.1.2", "cheerio": "^1.0.0", "commander": "11.1.0", @@ -95,8 +95,8 @@ "figures": "3.2.0", "fn-args": "4.0.0", "fs-extra": "11.3.0", - "glob": ">=9.0.0 <12", "fuse.js": "^7.0.0", + "glob": ">=9.0.0 <12", "html-minifier-terser": "7.2.0", "inquirer": "8.2.6", "invisi-data": "^1.0.0", @@ -105,47 +105,47 @@ "lodash.clonedeep": "4.5.0", "lodash.merge": "4.6.2", "mkdirp": "3.0.1", - "mocha": "11.1.0", - "monocart-coverage-reports": "2.12.3", + "mocha": "11.6.0", + "monocart-coverage-reports": "2.12.6", "ms": "2.1.3", "ora-classic": "5.4.2", "parse-function": "5.6.10", - "parse5": "7.2.1", + "parse5": "7.3.0", "promise-retry": "1.1.1", "resq": "1.11.0", "sprintf-js": "1.1.3", "uuid": "11.1.0" }, "optionalDependencies": { - "@codeceptjs/detox-helper": "1.1.7" + "@codeceptjs/detox-helper": "1.1.8" }, "devDependencies": { "@apollo/server": "^4", - "@codeceptjs/expect-helper": "^1.0.1", + "@codeceptjs/expect-helper": "^1.0.2", "@codeceptjs/mock-request": "0.3.1", - "@eslint/eslintrc": "3.3.0", - "@eslint/js": "9.22.0", - "@faker-js/faker": "9.6.0", + "@eslint/eslintrc": "3.3.1", + "@eslint/js": "9.31.0", + "@faker-js/faker": "9.8.0", "@pollyjs/adapter-puppeteer": "6.0.6", "@pollyjs/core": "6.0.6", - "@types/chai": "5.2.0", + "@types/chai": "5.2.2", "@types/inquirer": "9.0.7", - "@types/node": "22.13.10", - "@wdio/sauce-service": "9.12.0", + "@types/node": "24.0.10", + "@wdio/sauce-service": "9.12.5", "@wdio/selenium-standalone-service": "8.15.0", - "@wdio/utils": "9.11.0", + "@wdio/utils": "9.15.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": "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": "16.0.0", + "electron": "37.1.0", + "eslint": "^9.24.0", + "eslint-plugin-import": "2.32.0", + "eslint-plugin-mocha": "11.1.0", + "expect": "30.0.4", + "express": "5.1.0", + "globals": "16.2.0", "graphql": "16.10.0", "graphql-tag": "^2.12.6", "husky": "9.1.7", @@ -153,25 +153,26 @@ "jsdoc": "^3.6.11", "jsdoc-typeof-plugin": "1.0.0", "json-server": "0.17.4", - "playwright": "1.51.0", + "mochawesome": "^7.1.3", + "playwright": "1.54.1", "prettier": "^3.3.2", - "puppeteer": "24.4.0", + "puppeteer": "24.8.0", "qrcode-terminal": "0.12.0", "rosie": "2.1.1", "runok": "0.9.3", - "semver": "7.7.1", - "sinon": "19.0.2", + "semver": "7.7.2", + "sinon": "21.0.0", "sinon-chai": "3.7.0", "testcafe": "3.7.2", - "ts-morph": "25.0.1", + "ts-morph": "26.0.0", "ts-node": "10.9.2", - "tsd": "^0.31.0", + "tsd": "^0.32.0", "tsd-jsdoc": "2.5.0", - "typedoc": "0.28.0", - "typedoc-plugin-markdown": "4.5.0", - "typescript": "5.8.2", + "typedoc": "0.28.7", + "typedoc-plugin-markdown": "4.7.0", + "typescript": "5.8.3", "wdio-docker-service": "3.2.1", - "webdriverio": "9.12.0", + "webdriverio": "9.12.5", "xml2js": "0.6.2", "xpath": "0.0.34" }, diff --git a/runok.js b/runok.js index 62fb7ca5c..07d2a0b4e 100755 --- a/runok.js +++ b/runok.js @@ -45,8 +45,6 @@ module.exports = { async docsPlugins() { // generate documentation for plugins - // broken for now - return await npx(`documentation build lib/plugin/*.js -o docs/plugins.md ${documentjsCliArgs}`) await replaceInFile('docs/plugins.md', cfg => { diff --git a/test/acceptance/config_test.js b/test/acceptance/config_test.js index 919a384e4..d8415aad5 100644 --- a/test/acceptance/config_test.js +++ b/test/acceptance/config_test.js @@ -32,7 +32,7 @@ Scenario('change config 5 @WebDriverIO @Puppeteer @Playwright', ({ I }) => { Scenario('make API call and check response @Playwright', ({ I }) => { I.amOnPage('/') - I.makeApiRequest('get', 'https://reqres.in/api/users?page=2') + I.makeApiRequest('get', 'https://reqres.in/api/users?page=2', { headers: {'x-api-key': 'reqres-free-v1'}}) I.seeResponseCodeIsSuccessful() }) diff --git a/test/rest/REST_test.js b/test/rest/REST_test.js index f996caa09..317fdd333 100644 --- a/test/rest/REST_test.js +++ b/test/rest/REST_test.js @@ -150,7 +150,7 @@ describe('REST', () => { }) it('should be able to parse JSON responses', async () => { - await I.sendGetRequest('https://reqres.in/api/comments/1') + await I.sendGetRequest('https://reqres.in/api/comments/1', { 'x-api-key': 'reqres-free-v1'}) await jsonResponse.seeResponseCodeIsSuccessful() await jsonResponse.seeResponseContainsKeys(['data', 'support']) }) diff --git a/test/unit/plugin/screenshotOnFail_test.js b/test/unit/plugin/screenshotOnFail_test.js index 1cda0ec3f..a7ea44819 100644 --- a/test/unit/plugin/screenshotOnFail_test.js +++ b/test/unit/plugin/screenshotOnFail_test.js @@ -10,6 +10,8 @@ const event = require('../../../lib/event') const recorder = require('../../../lib/recorder') const { createTest } = require('../../../lib/mocha/test') const { deserializeSuite } = require('../../../lib/mocha/suite') +const MochawesomeHelper = require('../../../lib/helper/Mochawesome') + let screenshotSaved describe('screenshotOnFail', () => { @@ -101,5 +103,36 @@ describe('screenshotOnFail', () => { await recorder.promise() expect(!screenshotSaved.called).is.ok }) + + it('should have the same unique file name as the mochawesome helper when the uuid is present', async () => { + screenshotOnFail({ uniqueScreenshotNames: true }) + const test = createTest('test1') + test.uid = '1234' + + const helper = new MochawesomeHelper({ uniqueScreenshotNames: true }) + const spy = sinon.spy(helper, '_addContext') + helper._failed(test) + + event.dispatcher.emit(event.test.failed, test) + await recorder.promise() + + const screenshotFileName = screenshotSaved.getCall(0).args[0] + expect(spy.getCall(0).args[1]).to.equal(screenshotFileName) + }) + + it('should have the same unique file name as the mochawesome helper when the uuid is not present', async () => { + screenshotOnFail({ uniqueScreenshotNames: true }) + const test = createTest('test1') + + const helper = new MochawesomeHelper({ uniqueScreenshotNames: true }) + const spy = sinon.spy(helper, '_addContext') + helper._failed(test) + + event.dispatcher.emit(event.test.failed, test) + await recorder.promise() + + const screenshotFileName = screenshotSaved.getCall(0).args[0] + expect(spy.getCall(0).args[1]).to.equal(screenshotFileName) + }) // TODO: write more tests for different options }) diff --git a/typings/jsdoc.conf.js b/typings/jsdoc.conf.js index 6b38e805f..466b44834 100644 --- a/typings/jsdoc.conf.js +++ b/typings/jsdoc.conf.js @@ -9,6 +9,7 @@ module.exports = { './lib/container.js', './lib/data/table.js', './lib/data/dataTableArgument.js', + './lib/effects.js', './lib/event.js', './lib/index.js', './lib/locator.js', diff --git a/typings/tests/global-variables.types.ts b/typings/tests/global-variables.types.ts index aa21f2cca..2d2a0a512 100644 --- a/typings/tests/global-variables.types.ts +++ b/typings/tests/global-variables.types.ts @@ -83,3 +83,13 @@ expectType(AfterSuite((args) => { // @ts-ignore expectType(args.I) })) + +// @ts-ignore +expectType>(tryTo(() => { + return true; +})); + +// @ts-ignore +expectType>(tryTo(async () => { + return false; +}));