From e1f9951c6c60770789717db241b6f13fa79b32e0 Mon Sep 17 00:00:00 2001 From: Vitalii Mykhailiuk <82405549+mykhailiukVitalii@users.noreply.github.com> Date: Mon, 13 Feb 2023 12:18:31 +0200 Subject: [PATCH 01/64] Create a new simple example for testing project with Mocha + TypeScript + Testomat.io plugins. (#26) --- mocha-typescript/.env.example | 1 + mocha-typescript/.eslintrc.json | 55 +++++++++++ mocha-typescript/.gitignore | 107 +++++++++++++++++++++ mocha-typescript/.mocharc.js | 6 ++ mocha-typescript/README.md | 67 +++++++++++++ mocha-typescript/package.json | 40 ++++++++ mocha-typescript/rpConfig.js | 6 ++ mocha-typescript/spec/test.example.spec.ts | 31 ++++++ mocha-typescript/tsconfig.json | 12 +++ 9 files changed, 325 insertions(+) create mode 100644 mocha-typescript/.env.example create mode 100644 mocha-typescript/.eslintrc.json create mode 100644 mocha-typescript/.gitignore create mode 100644 mocha-typescript/.mocharc.js create mode 100644 mocha-typescript/README.md create mode 100644 mocha-typescript/package.json create mode 100644 mocha-typescript/rpConfig.js create mode 100644 mocha-typescript/spec/test.example.spec.ts create mode 100644 mocha-typescript/tsconfig.json diff --git a/mocha-typescript/.env.example b/mocha-typescript/.env.example new file mode 100644 index 00000000..dd135e61 --- /dev/null +++ b/mocha-typescript/.env.example @@ -0,0 +1 @@ +TESTOMAT_API_KEY= \ No newline at end of file diff --git a/mocha-typescript/.eslintrc.json b/mocha-typescript/.eslintrc.json new file mode 100644 index 00000000..1d1c7e17 --- /dev/null +++ b/mocha-typescript/.eslintrc.json @@ -0,0 +1,55 @@ +{ + "env": { + "browser": true, + "es2021": true, + "node": true + }, + "extends": [ + "eslint:recommended", + "plugin:@typescript-eslint/recommended", + "plugin:prettier/recommended", + "plugin:import/recommended", + "plugin:import/typescript" + ], + "parser": "@typescript-eslint/parser", + "parserOptions": { + "ecmaVersion": 12, + "sourceType": "module", + "project": "./tsconfig.json", + "tsconfigRootDir": "./" + }, + "plugins": ["@typescript-eslint", "prettier", "no-only-tests"], + "rules": { + "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/explicit-module-boundary-types": [ + "off", + { + "allowArgumentsExplicitlyTypedAsAny": true + } + ], + "indent": ["error", 2], + "quotes": ["error", "single", {"avoidEscape": true}], + "semi": ["error", "never"], + "no-undef": "off", + "no-only-tests/no-only-tests": ["error", {"fix": true}], + "prettier/prettier": [ + "error", + { + "semi": false, + "singleQuote": true, + "printWidth": 80, + "tabWidth": 2, + "arrowParens": "avoid", + "trailingComma": "none", + "endOfLine": "auto" + } + ] + }, + "settings": { + "import/resolver": { + "typescript": { + "alwaysTryTypes": true + } + } + } +} diff --git a/mocha-typescript/.gitignore b/mocha-typescript/.gitignore new file mode 100644 index 00000000..0eaacc0e --- /dev/null +++ b/mocha-typescript/.gitignore @@ -0,0 +1,107 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +/node_modules/* +/jspm_packages/* + +# TypeScript v1 declaration files +typings/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env +.env.test + +# parcel-bundler cache (https://parceljs.org/) +.cache + +# Next.js build output +.next + +# Nuxt.js build / generate output +.nuxt +dist + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and *not* Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +#idea basic folder +.idea/* \ No newline at end of file diff --git a/mocha-typescript/.mocharc.js b/mocha-typescript/.mocharc.js new file mode 100644 index 00000000..0ae78cd4 --- /dev/null +++ b/mocha-typescript/.mocharc.js @@ -0,0 +1,6 @@ +module.exports = { + require: ['ts-node/register', 'dotenv/config'], + spec: './spec/**/*.spec.ts', + reporter: 'mocha-multi-reporters', + reporterOptions: ['configFile=rpConfig.js'] +} \ No newline at end of file diff --git a/mocha-typescript/README.md b/mocha-typescript/README.md new file mode 100644 index 00000000..8bace4ab --- /dev/null +++ b/mocha-typescript/README.md @@ -0,0 +1,67 @@ +This repo contains simple template for testing project with Mocha + TypeScript + Testomat.io plugins. + +# Installation +This is a playground for your first steps in testing, so instead of installing it from NPM it is recommended to clone it from repo instead. + +1) Clone this repository: + +``` +git clone git@github.com:testomatio/examples.git && cd examples/mocha-typescript +``` +## Requirements: + **in order to run this project, NodeJS (version 18+) must be installed** + + +2) Install dependencies via npm: + +``` +npm i +``` + +_This will install mocha & typescript env + Testomat.io reporter_ + +3) Run tests localy without sending report to the Testomat.io: + +``` +npm tests +``` +_(if the tests are completed => you can start setting up the testomat.io reporter)_ + +## Loading Tests to Testomat.io + +1. Create empty project in Testomat.io +2. Obtain API key from Testomat.io +2. Run `npx check-tests` to upload tests data into testomat.io. Pass api key as `TESTOMATIO` environment variable: + +``` +TESTOMATIO={apiKey} npx check-tests@latest mocha "**/*{.,_}{test,spec}.ts" --typescript -d spec +``` + +## Publishing Test Results to Testomat.io + +1. Get API key from a project in Testomat.io. + +2. Create a specific .env file: +> **Environment variables** +```.env``` file using ```env.example``` as a template +### SET key variable +**(if you don't want to use the TESTOMATIO API key when running testomatIO, then you can store the token in the ```.env``` file (see env.example example))** + +3. Run tests and generate reports(Testomat.io): + +``` +npx mocha +``` +OR +``` +npm test +``` + +### reporterOptions additinal options +**In our case, we use multiple reporters: spec & testomatio.** + +_!! Pay Attantion on the rpConfgi.js file. Using key-name should be equal path to the testomatio adapter(testomatio/reporter/lib/adapter/mocha.js)._ + Like: +``` +testomatioReporterLibAdapterMochaJsReporterOptions +``` \ No newline at end of file diff --git a/mocha-typescript/package.json b/mocha-typescript/package.json new file mode 100644 index 00000000..f927aec2 --- /dev/null +++ b/mocha-typescript/package.json @@ -0,0 +1,40 @@ +{ + "name": "mocha-typescript", + "version": "1.0.1", + "description": "Example testomatio project with Mocha+TypeScript", + "main": "index.js", + "scripts": { + "test": "mocha" + }, + "keywords": [ + "mocha", + "typescript", + "e2e", + "test automation" + ], + "author": "Vitalii Mykhailiuk (vmykhailiuk.chanel@gmail.com)", + "license": "MIT", + "dependencies": { + "mocha": "10.2.0", + "typescript": "4.9.4", + "ts-node": "10.9.1", + "dotenv": "16.0.3", + "mocha-multi-reporters": "1.5.1", + "chai": "4.3.7", + "@types/mocha": "10.0.1", + "@types/node": "18.11.18", + "@types/chai": "4.3.4", + "@testomatio/reporter": "^0.7.4" + }, + "devDependencies": { + "@typescript-eslint/eslint-plugin": "5.48.2", + "@typescript-eslint/parser": "5.48.2", + "eslint": "8.32.0", + "eslint-config-prettier": "8.6.0", + "eslint-import-resolver-typescript": "3.5.3", + "eslint-plugin-import": "2.27.5", + "eslint-plugin-no-only-tests": "3.1.0", + "eslint-plugin-prettier": "4.2.1", + "prettier": "2.8.3" + } +} diff --git a/mocha-typescript/rpConfig.js b/mocha-typescript/rpConfig.js new file mode 100644 index 00000000..2633bda2 --- /dev/null +++ b/mocha-typescript/rpConfig.js @@ -0,0 +1,6 @@ +module.exports = { + reporterEnabled: 'spec, @testomatio/reporter/lib/adapter/mocha.js', + testomatioReporterLibAdapterMochaJsReporterOptions: { + apiKey: process.env.TESTOMAT_API_KEY + } +} \ No newline at end of file diff --git a/mocha-typescript/spec/test.example.spec.ts b/mocha-typescript/spec/test.example.spec.ts new file mode 100644 index 00000000..0cf3d98d --- /dev/null +++ b/mocha-typescript/spec/test.example.spec.ts @@ -0,0 +1,31 @@ +import { expect } from 'chai'; + +describe('Simple Mocha + TypeScript example.', () => { + before(() => { + console.log('Running before hook'); + // You can add any code here... + }) + beforeEach(() => { + console.log('Running beforeEach hook'); + // You can add any code here... + }) + + it('[Positive case]: Should be true.', () => { + console.log('Running test 1'); + expect(true).to.equal(true); + }) + it.skip('[Positive case]: Should be skipped.', () => { + console.log('Running test 2'); + expect(true).to.equal(true); + }) + + afterEach(() => { + console.log('Running afterEach hook'); + // You can add any code here... + }) + + after(() => { + console.log('Running afterEach hook'); + // You can add any code here... + }) +}) diff --git a/mocha-typescript/tsconfig.json b/mocha-typescript/tsconfig.json new file mode 100644 index 00000000..2adf7221 --- /dev/null +++ b/mocha-typescript/tsconfig.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "esModuleInterop": true, + "moduleResolution": "Node", + "module": "commonjs", + "outDir": "built", + "target": "ES2019", + "sourceMap": true + }, + "include": ["**/*.ts", "**/*.js"], + "exclude": ["node_modules"] +} \ No newline at end of file From 144cc39eda07a1fadad29ca2d8441e6837e6c7c5 Mon Sep 17 00:00:00 2001 From: Michael Bodnarchuk Date: Mon, 13 Feb 2023 12:54:52 +0200 Subject: [PATCH 02/64] Update test.example.spec.ts --- mocha-typescript/spec/test.example.spec.ts | 88 +++++++++++++++------- 1 file changed, 59 insertions(+), 29 deletions(-) diff --git a/mocha-typescript/spec/test.example.spec.ts b/mocha-typescript/spec/test.example.spec.ts index 0cf3d98d..5a785d28 100644 --- a/mocha-typescript/spec/test.example.spec.ts +++ b/mocha-typescript/spec/test.example.spec.ts @@ -1,31 +1,61 @@ import { expect } from 'chai'; -describe('Simple Mocha + TypeScript example.', () => { - before(() => { - console.log('Running before hook'); - // You can add any code here... - }) - beforeEach(() => { - console.log('Running beforeEach hook'); - // You can add any code here... - }) - - it('[Positive case]: Should be true.', () => { - console.log('Running test 1'); - expect(true).to.equal(true); - }) - it.skip('[Positive case]: Should be skipped.', () => { - console.log('Running test 2'); - expect(true).to.equal(true); - }) - - afterEach(() => { - console.log('Running afterEach hook'); - // You can add any code here... - }) - - after(() => { - console.log('Running afterEach hook'); - // You can add any code here... - }) -}) +describe("Array", function() { + let arr; + + beforeEach(function() { + arr = [1, 2, 3]; + }); + + // Test Case 1 + it("should return -1 when the value is not present", function() { + expect(arr.indexOf(4)).to.equal(-1); + }); + + // Test Case 2 + it("should return the index of the element when it is present", function() { + expect(arr.indexOf(2)).to.equal(1); + }); + + // Test Case 3 + it("should return the length of the array after adding an element", function() { + arr.push(4); + expect(arr.length).to.equal(4); + }); + + // Test Case 4 + it("should return the first element of the array", function() { + expect(arr[0]).to.equal(1); + }); + + // Test Case 5 + it("should return the sum of all elements in the array", function() { + expect(arr.reduce((a, b) => a + b, 0)).to.equal(6); + }); + + // Test Case 6 + it("should return the last element of the array", function() { + expect(arr[2]).to.equal(3); + }); + + // Test Case 7 + it("should return a reversed array", function() { + expect(arr.reverse()).to.deep.equal([3, 2, 1]); + }); + + // Test Case 8 + it("should return the array after removing the first element", function() { + expect(arr.slice(1)).to.deep.equal([2, 3]); + }); + + // Test Case 9 + it("should return a new array with all elements multiplied by 2", function() { + expect(arr.map(x => x * 2)).to.deep.equal([2, 4, 6]); + }); + + // Test Case 10 + it("should return a sorted array in ascending order", function() { + expect([3, 2, 1].sort()).to.deep.equal([1, 2, 3]); + }); +}); + From 1f9a6119bc74b565fa301a99b15ee4d0c6537763 Mon Sep 17 00:00:00 2001 From: Michael Bodnarchuk Date: Mon, 13 Feb 2023 13:19:08 +0200 Subject: [PATCH 03/64] Update .mocharc.js --- mocha-typescript/.mocharc.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mocha-typescript/.mocharc.js b/mocha-typescript/.mocharc.js index 0ae78cd4..67698760 100644 --- a/mocha-typescript/.mocharc.js +++ b/mocha-typescript/.mocharc.js @@ -2,5 +2,5 @@ module.exports = { require: ['ts-node/register', 'dotenv/config'], spec: './spec/**/*.spec.ts', reporter: 'mocha-multi-reporters', - reporterOptions: ['configFile=rpConfig.js'] -} \ No newline at end of file + reporterOptions: ['configFile=reporter.conf.js'] +} From a04728981400087d69b8999b1bee086bd793daf0 Mon Sep 17 00:00:00 2001 From: Michael Bodnarchuk Date: Mon, 13 Feb 2023 13:19:35 +0200 Subject: [PATCH 04/64] Update and rename rpConfig.js to reporter.conf.js --- mocha-typescript/{rpConfig.js => reporter.conf.js} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename mocha-typescript/{rpConfig.js => reporter.conf.js} (78%) diff --git a/mocha-typescript/rpConfig.js b/mocha-typescript/reporter.conf.js similarity index 78% rename from mocha-typescript/rpConfig.js rename to mocha-typescript/reporter.conf.js index 2633bda2..cdb8fbfe 100644 --- a/mocha-typescript/rpConfig.js +++ b/mocha-typescript/reporter.conf.js @@ -1,6 +1,6 @@ module.exports = { reporterEnabled: 'spec, @testomatio/reporter/lib/adapter/mocha.js', testomatioReporterLibAdapterMochaJsReporterOptions: { - apiKey: process.env.TESTOMAT_API_KEY + apiKey: process.env.TESTOMATIO } -} \ No newline at end of file +} From 2c923800183c50911ed9908055d1248465945517 Mon Sep 17 00:00:00 2001 From: Michael Bodnarchuk Date: Mon, 13 Feb 2023 13:19:58 +0200 Subject: [PATCH 05/64] Update .env.example --- mocha-typescript/.env.example | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mocha-typescript/.env.example b/mocha-typescript/.env.example index dd135e61..e95861d1 100644 --- a/mocha-typescript/.env.example +++ b/mocha-typescript/.env.example @@ -1 +1 @@ -TESTOMAT_API_KEY= \ No newline at end of file +TESTOMATIO= From 559e1878a292bc516855cca7635cfe164b1c4314 Mon Sep 17 00:00:00 2001 From: Michael Bodnarchuk Date: Mon, 13 Feb 2023 13:42:15 +0200 Subject: [PATCH 06/64] Update test.example.spec.ts --- mocha-typescript/spec/test.example.spec.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/mocha-typescript/spec/test.example.spec.ts b/mocha-typescript/spec/test.example.spec.ts index 5a785d28..59028f3c 100644 --- a/mocha-typescript/spec/test.example.spec.ts +++ b/mocha-typescript/spec/test.example.spec.ts @@ -23,6 +23,15 @@ describe("Array", function() { expect(arr.length).to.equal(4); }); + it("should fail and return -1 as the value is not present", function() { + expect(arr.indexOf(4)).to.equal(3); + }); + + it.skip("should be skipped", function() { + arr.push(4); + expect(arr.length).to.equal(4); + }); + // Test Case 4 it("should return the first element of the array", function() { expect(arr[0]).to.equal(1); From 5351e153cfada612801594a541d5b21a87fd42b9 Mon Sep 17 00:00:00 2001 From: Vitalii Mykhailiuk <82405549+mykhailiukVitalii@users.noreply.github.com> Date: Thu, 16 Feb 2023 21:22:00 +0200 Subject: [PATCH 07/64] mocha-example: small description fixes. (#27) Co-authored-by: Vitaliy Mykhailiuk AQA --- {mocha-typescript => mocha-ts-multi-reporters}/.env.example | 0 {mocha-typescript => mocha-ts-multi-reporters}/.eslintrc.json | 0 {mocha-typescript => mocha-ts-multi-reporters}/.gitignore | 0 {mocha-typescript => mocha-ts-multi-reporters}/.mocharc.js | 0 {mocha-typescript => mocha-ts-multi-reporters}/README.md | 3 ++- {mocha-typescript => mocha-ts-multi-reporters}/package.json | 0 .../reporter.conf.js | 0 .../spec/test.example.spec.ts | 0 {mocha-typescript => mocha-ts-multi-reporters}/tsconfig.json | 0 9 files changed, 2 insertions(+), 1 deletion(-) rename {mocha-typescript => mocha-ts-multi-reporters}/.env.example (100%) rename {mocha-typescript => mocha-ts-multi-reporters}/.eslintrc.json (100%) rename {mocha-typescript => mocha-ts-multi-reporters}/.gitignore (100%) rename {mocha-typescript => mocha-ts-multi-reporters}/.mocharc.js (100%) rename {mocha-typescript => mocha-ts-multi-reporters}/README.md (89%) rename {mocha-typescript => mocha-ts-multi-reporters}/package.json (100%) rename {mocha-typescript => mocha-ts-multi-reporters}/reporter.conf.js (100%) rename {mocha-typescript => mocha-ts-multi-reporters}/spec/test.example.spec.ts (100%) rename {mocha-typescript => mocha-ts-multi-reporters}/tsconfig.json (100%) diff --git a/mocha-typescript/.env.example b/mocha-ts-multi-reporters/.env.example similarity index 100% rename from mocha-typescript/.env.example rename to mocha-ts-multi-reporters/.env.example diff --git a/mocha-typescript/.eslintrc.json b/mocha-ts-multi-reporters/.eslintrc.json similarity index 100% rename from mocha-typescript/.eslintrc.json rename to mocha-ts-multi-reporters/.eslintrc.json diff --git a/mocha-typescript/.gitignore b/mocha-ts-multi-reporters/.gitignore similarity index 100% rename from mocha-typescript/.gitignore rename to mocha-ts-multi-reporters/.gitignore diff --git a/mocha-typescript/.mocharc.js b/mocha-ts-multi-reporters/.mocharc.js similarity index 100% rename from mocha-typescript/.mocharc.js rename to mocha-ts-multi-reporters/.mocharc.js diff --git a/mocha-typescript/README.md b/mocha-ts-multi-reporters/README.md similarity index 89% rename from mocha-typescript/README.md rename to mocha-ts-multi-reporters/README.md index 8bace4ab..0c3d31a8 100644 --- a/mocha-typescript/README.md +++ b/mocha-ts-multi-reporters/README.md @@ -1,4 +1,5 @@ -This repo contains simple template for testing project with Mocha + TypeScript + Testomat.io plugins. +# 📊 Mocha + TypeScript + Multi Reporters Example +This repo contains simple template for testing project with Multiple reports based on the stack = Mocha + TypeScript + Spec report & Testomat.io plugins. # Installation This is a playground for your first steps in testing, so instead of installing it from NPM it is recommended to clone it from repo instead. diff --git a/mocha-typescript/package.json b/mocha-ts-multi-reporters/package.json similarity index 100% rename from mocha-typescript/package.json rename to mocha-ts-multi-reporters/package.json diff --git a/mocha-typescript/reporter.conf.js b/mocha-ts-multi-reporters/reporter.conf.js similarity index 100% rename from mocha-typescript/reporter.conf.js rename to mocha-ts-multi-reporters/reporter.conf.js diff --git a/mocha-typescript/spec/test.example.spec.ts b/mocha-ts-multi-reporters/spec/test.example.spec.ts similarity index 100% rename from mocha-typescript/spec/test.example.spec.ts rename to mocha-ts-multi-reporters/spec/test.example.spec.ts diff --git a/mocha-typescript/tsconfig.json b/mocha-ts-multi-reporters/tsconfig.json similarity index 100% rename from mocha-typescript/tsconfig.json rename to mocha-ts-multi-reporters/tsconfig.json From 9f6958fd7eed9739f67f608f3759aa7f5d8012c3 Mon Sep 17 00:00:00 2001 From: Vitalii Mykhailiuk <82405549+mykhailiukVitalii@users.noreply.github.com> Date: Mon, 6 Mar 2023 10:08:15 +0200 Subject: [PATCH 08/64] Issue#29: Migrate cypress to the latest version + add new tests to testomatio/example (#30) * ISSUE#29: removed old tests & update cypress version to 12.7.0 * ISSUE#29: have added a new feature test files with pages and ste_definitions * ISSUE#29: README.md small updates * ISSUE#29: invalid scenarion small renaming * ISSUE#29: added test for login page verification * ISSUE#29: small basic updates. * ISSUE#29: small fix for mocha-ts readme.md * ISSUE#29: CUCUMBER count argument small fix --- cypress-cucumber/README.md | 17 ++++++++- cypress-cucumber/cypress.config.js | 23 ++++++++++++ cypress-cucumber/cypress.json | 1 - .../integration/features/github.feature | 15 -------- .../integration/features/homePage.feature | 27 ++++++++++++++ .../features/invalidPwdLogin.feature | 6 ++++ .../features/loginpageVerification.feature | 13 +++++++ .../features/successfullLogin.feature | 6 ++++ cypress-cucumber/cypress/plugins/index.js | 26 -------------- .../cypress/support/{index.js => e2e.js} | 4 +-- .../cypress/support/pages/HomePage.js | 28 +++++++++++++++ .../cypress/support/pages/LoginPage.js | 29 +++++++++++++++ .../support/step_definitions/github.js | 19 ---------- .../support/step_definitions/homePage.js | 30 ++++++++++++++++ .../support/step_definitions/loginToApp.js | 36 +++++++++++++++++++ cypress-cucumber/package.json | 13 ++++--- mocha-ts-multi-reporters/README.md | 2 +- 17 files changed, 226 insertions(+), 69 deletions(-) create mode 100644 cypress-cucumber/cypress.config.js delete mode 100644 cypress-cucumber/cypress.json delete mode 100644 cypress-cucumber/cypress/integration/features/github.feature create mode 100644 cypress-cucumber/cypress/integration/features/homePage.feature create mode 100644 cypress-cucumber/cypress/integration/features/invalidPwdLogin.feature create mode 100644 cypress-cucumber/cypress/integration/features/loginpageVerification.feature create mode 100644 cypress-cucumber/cypress/integration/features/successfullLogin.feature delete mode 100644 cypress-cucumber/cypress/plugins/index.js rename cypress-cucumber/cypress/support/{index.js => e2e.js} (88%) create mode 100644 cypress-cucumber/cypress/support/pages/HomePage.js create mode 100644 cypress-cucumber/cypress/support/pages/LoginPage.js delete mode 100644 cypress-cucumber/cypress/support/step_definitions/github.js create mode 100644 cypress-cucumber/cypress/support/step_definitions/homePage.js create mode 100644 cypress-cucumber/cypress/support/step_definitions/loginToApp.js diff --git a/cypress-cucumber/README.md b/cypress-cucumber/README.md index cdae5bad..2e8e4c1d 100644 --- a/cypress-cucumber/README.md +++ b/cypress-cucumber/README.md @@ -4,10 +4,13 @@ This repo contains tests for **Cypress.io test application in Gherkin language** This is a playground for your first steps in testing, so instead of installing it from NPM it is recommended to clone it from repo instead. +**If you're using `npm` to install this example, we support only _Node.js_ 18.x and above** +|So you should install latest Node.js or switch using `nvm` + 1) Clone this repository ``` -git clone git@github.com:testomatio/examples.git && cd examples/cypress +git clone git@github.com:testomatio/examples.git && cd examples/cypress-cucumber ``` 2) Install dependencies via npm: @@ -16,6 +19,18 @@ git clone git@github.com:testomatio/examples.git && cd examples/cypress npm i ``` +### Confirm test results localy + +To open the example with the Cypress browser and execute .feature specs: +``` +npx cypress open +``` + +To execute tests in console log only(get screens + videos + logs): +``` +npx cypress run +``` + This will install cypress & Testomat.io reporter ## Loading Tests to Testomat.io diff --git a/cypress-cucumber/cypress.config.js b/cypress-cucumber/cypress.config.js new file mode 100644 index 00000000..11ef6b28 --- /dev/null +++ b/cypress-cucumber/cypress.config.js @@ -0,0 +1,23 @@ +const { defineConfig } = require("cypress"); +const createBundler = require("@bahmutov/cypress-esbuild-preprocessor"); +const addCucumberPreprocessorPlugin = require("@badeball/cypress-cucumber-preprocessor").addCucumberPreprocessorPlugin; +const createEsbuildPlugin = require("@badeball/cypress-cucumber-preprocessor/esbuild").createEsbuildPlugin; +const testomatioReporter = require('@testomatio/reporter/lib/adapter/cypress-plugin'); + +module.exports = defineConfig({ + e2e: { + async setupNodeEvents(on, config) { + // implement node event listeners here + const bundler = createBundler({ + plugins: [createEsbuildPlugin(config)], + }); + on("file:preprocessor", bundler); + await addCucumberPreprocessorPlugin(on, config); + //Testomat.io reporter + testomatioReporter(on, config); + + return config; + }, + specPattern: "cypress/integration/**/*.feature", + }, +}); \ No newline at end of file diff --git a/cypress-cucumber/cypress.json b/cypress-cucumber/cypress.json deleted file mode 100644 index 9e26dfee..00000000 --- a/cypress-cucumber/cypress.json +++ /dev/null @@ -1 +0,0 @@ -{} \ No newline at end of file diff --git a/cypress-cucumber/cypress/integration/features/github.feature b/cypress-cucumber/cypress/integration/features/github.feature deleted file mode 100644 index 21d373a7..00000000 --- a/cypress-cucumber/cypress/integration/features/github.feature +++ /dev/null @@ -1,15 +0,0 @@ -Feature: "Github header" - - Scenario: Checking GitHub header - - When I open the GitHub home page - Then I should see correct h1 - - Scenario Outline: Checking web pages - - Given I open page - - Examples: - | url | - | "https://testomat.io" | - | "https://lb.ua1" | diff --git a/cypress-cucumber/cypress/integration/features/homePage.feature b/cypress-cucumber/cypress/integration/features/homePage.feature new file mode 100644 index 00000000..aa6d9cab --- /dev/null +++ b/cypress-cucumber/cypress/integration/features/homePage.feature @@ -0,0 +1,27 @@ +Feature: Home page verification + + Background: + Given I visit Home page + + Scenario: I should see the UI Test Automation Playground title + Then I should see UI Test Automation Playground title + + Scenario: I should see that Home is active menu + Then I should see that Home menu is active by default + + Scenario: I verify that Home menu attr equal /home + Then Home menu should be home + + Scenario: TOP h1 header should be visisble + Then I should see the Playground h1 header at the TOP page part + + Scenario: cube image should be visible + Then I should see the cude image + + Scenario: I want to search the links in Home page + Given Verify additional links + + Examples: + | count | + | 18 | + \ No newline at end of file diff --git a/cypress-cucumber/cypress/integration/features/invalidPwdLogin.feature b/cypress-cucumber/cypress/integration/features/invalidPwdLogin.feature new file mode 100644 index 00000000..23eafd8b --- /dev/null +++ b/cypress-cucumber/cypress/integration/features/invalidPwdLogin.feature @@ -0,0 +1,6 @@ +Feature: Login to App + + Scenario: Login to the application as not-existing user + When I visit sampleapp page + Then I login as user with invalid pwd + Then I should see invalid message \ No newline at end of file diff --git a/cypress-cucumber/cypress/integration/features/loginpageVerification.feature b/cypress-cucumber/cypress/integration/features/loginpageVerification.feature new file mode 100644 index 00000000..5763b73b --- /dev/null +++ b/cypress-cucumber/cypress/integration/features/loginpageVerification.feature @@ -0,0 +1,13 @@ +Feature: Login page verification + + Background: + When I visit sampleapp page + + Scenario: I should see the the h3 header + Then I should see the the h3 header + + Scenario: I should see the Login button + Then I should see the Login button + + Scenario: I should see placeholder in User Name field + Then I should see placeholder in User Name field \ No newline at end of file diff --git a/cypress-cucumber/cypress/integration/features/successfullLogin.feature b/cypress-cucumber/cypress/integration/features/successfullLogin.feature new file mode 100644 index 00000000..bee77a35 --- /dev/null +++ b/cypress-cucumber/cypress/integration/features/successfullLogin.feature @@ -0,0 +1,6 @@ +Feature: Login to App + + Scenario: Login to the application as an exist user + When I visit sampleapp page + Then I login as valid user + Then I should see successfully message \ No newline at end of file diff --git a/cypress-cucumber/cypress/plugins/index.js b/cypress-cucumber/cypress/plugins/index.js deleted file mode 100644 index b1bf2c6a..00000000 --- a/cypress-cucumber/cypress/plugins/index.js +++ /dev/null @@ -1,26 +0,0 @@ -// *********************************************************** -// This example plugins/index.js can be used to load plugins -// -// You can change the location of this file or turn off loading -// the plugins file with the 'pluginsFile' configuration option. -// -// You can read more here: -// https://on.cypress.io/plugins-guide -// *********************************************************** - -// This function is called when a project is opened or re-opened (e.g. due to -// the project's config changing) - -const cucumber = require('cypress-cucumber-preprocessor').default; -const testomatioReporter = require('@testomatio/reporter/lib/adapter/cypress-plugin'); - -module.exports = (on, config) => { - // `on` is used to hook into various events Cypress emits - // `config` is the resolved Cypress config - - on('file:preprocessor', cucumber()); - - testomatioReporter(on, config); - - return config; -}; diff --git a/cypress-cucumber/cypress/support/index.js b/cypress-cucumber/cypress/support/e2e.js similarity index 88% rename from cypress-cucumber/cypress/support/index.js rename to cypress-cucumber/cypress/support/e2e.js index d68db96d..0e7290a1 100644 --- a/cypress-cucumber/cypress/support/index.js +++ b/cypress-cucumber/cypress/support/e2e.js @@ -1,5 +1,5 @@ // *********************************************************** -// This example support/index.js is processed and +// This example support/e2e.js is processed and // loaded automatically before your test files. // // This is a great place to put global configuration and @@ -17,4 +17,4 @@ import './commands' // Alternatively you can use CommonJS syntax: -// require('./commands') +// require('./commands') \ No newline at end of file diff --git a/cypress-cucumber/cypress/support/pages/HomePage.js b/cypress-cucumber/cypress/support/pages/HomePage.js new file mode 100644 index 00000000..88846bff --- /dev/null +++ b/cypress-cucumber/cypress/support/pages/HomePage.js @@ -0,0 +1,28 @@ +class HomePage { + openHomePage() { + return cy.visit("http://uitestingplayground.com"); + } + verifyHomepageTitle(title) { + return cy.title().should('eq', title); + } + verifyHomepageActiveMenuName() { + return cy.get(".nav-item.active > a").should('have.text', 'Home'); + } + verifyHomepageActiveMenuAttr() { + return cy.get(".nav-item.active > a").should('have.attr', 'href', '/home'); + } + playgroundTitleShouldBeVisible() { + return cy.contains("Playground").should('be.visible'); + } + cubeImageShouldBeVisible() { + return cy.get("img.img-fluid").should('be.visible'); + } + verifyLinkCount(count) { + return cy.get("h3 > a").should('have.length', count); + } +} + +const homePage = new HomePage(); + +export default homePage; + diff --git a/cypress-cucumber/cypress/support/pages/LoginPage.js b/cypress-cucumber/cypress/support/pages/LoginPage.js new file mode 100644 index 00000000..3c02f9e6 --- /dev/null +++ b/cypress-cucumber/cypress/support/pages/LoginPage.js @@ -0,0 +1,29 @@ +class LoginPage { + openLoginPage() { + return cy.visit("http://uitestingplayground.com/sampleapp"); + } + loginToApp(userName, password) { + cy.get('input[name="UserName"]').type(userName); + cy.get('input[name="Password"]').type(password); + cy.get('#login').click(); + } + verifyLoginStatus(status) { + return cy.get('#loginstatus').should('have.text', status); + } + verifyH3Header(header) { + return cy.get('.container > h3').should('have.text', header); + } + loginBtnIsVisible() { + return cy.get('#login').should('be.visible'); + } + verifyUserNamePlaceholder() { + return cy.get('input[name="UserName"]') + .should('have.attr', 'placeholder') + .and('eq', 'User Name'); + } +} + +const loginPage = new LoginPage(); + +export default loginPage; + diff --git a/cypress-cucumber/cypress/support/step_definitions/github.js b/cypress-cucumber/cypress/support/step_definitions/github.js deleted file mode 100644 index cf901aa4..00000000 --- a/cypress-cucumber/cypress/support/step_definitions/github.js +++ /dev/null @@ -1,19 +0,0 @@ -import { Given, When, Then } from 'cypress-cucumber-preprocessor/steps'; - -When('I open the GitHub home page', () => { - cy.visit('https://github.com'); -}); - -Then('I should see correct h1', () => { - cy.get('h1') - .next() - .should( - 'have.text', - 'Millions of developers and companies build, ship, and maintain their software on GitHub—the largest and most advanced development platform in the world.' - ); -}); - -Given('I open {string} page', (url) => { - cy.visit(url); -}); - diff --git a/cypress-cucumber/cypress/support/step_definitions/homePage.js b/cypress-cucumber/cypress/support/step_definitions/homePage.js new file mode 100644 index 00000000..4d470d9d --- /dev/null +++ b/cypress-cucumber/cypress/support/step_definitions/homePage.js @@ -0,0 +1,30 @@ +import { Given, Then } from "@badeball/cypress-cucumber-preprocessor"; +import homePage from "../pages/HomePage"; + +Given("I visit Home page", () => { + homePage.openHomePage(); +}); + +Then("I should see UI Test Automation Playground title", () => { + homePage.verifyHomepageTitle("UI Test Automation Playground"); +}); + +Then("I should see the Playground h1 header at the TOP page part", () => { + homePage.playgroundTitleShouldBeVisible(); +}); + +Then("I should see the cude image", () => { + homePage.cubeImageShouldBeVisible(); +}); + +Then("I should see that Home menu is active by default", () => { + homePage.verifyHomepageActiveMenuName(); +}); + +Then("Home menu should be home", () => { + homePage.verifyHomepageActiveMenuAttr(); +}); + +Given('Verify additional links {int}', (count) => { + homePage.verifyLinkCount(count); +}); \ No newline at end of file diff --git a/cypress-cucumber/cypress/support/step_definitions/loginToApp.js b/cypress-cucumber/cypress/support/step_definitions/loginToApp.js new file mode 100644 index 00000000..8ce0b094 --- /dev/null +++ b/cypress-cucumber/cypress/support/step_definitions/loginToApp.js @@ -0,0 +1,36 @@ +import { When, Then } from "@badeball/cypress-cucumber-preprocessor"; +import loginPage from "../pages/LoginPage"; + +const userName = "sampleUser"; + +When("I visit sampleapp page", () => { + loginPage.openLoginPage(); +}); + +Then("I login as valid user", () => { + loginPage.loginToApp(userName, "pwd"); +}); + +Then("I login as user with invalid pwd", () => { + loginPage.loginToApp(userName, "Errorpwd"); +}); + +Then("I should see successfully message", () => { + loginPage.verifyLoginStatus(`Welcome, ${userName}!`); +}); + +Then("I should see invalid message", () => { + loginPage.verifyLoginStatus("Invalid username/password"); +}); + +Then("I should see the the h3 header", () => { + loginPage.verifyH3Header("Sample App"); +}); + +Then("I should see the Login button", () => { + loginPage.loginBtnIsVisible(); +}); + +Then("I should see placeholder in User Name field", () => { + loginPage.verifyUserNamePlaceholder(); +}); diff --git a/cypress-cucumber/package.json b/cypress-cucumber/package.json index bbcee635..a05807cf 100644 --- a/cypress-cucumber/package.json +++ b/cypress-cucumber/package.json @@ -11,10 +11,15 @@ "keywords": [], "author": "", "license": "ISC", + "dependencies": { + "@testomatio/reporter": "^0.7.5", + "cypress": "^12.7.0" + }, "devDependencies": { - "@testomatio/reporter": "^0.5.10", - "cypress": "9.2.0", - "cypress-cucumber-preprocessor": "4.3.1", - "mocha": "^9.1.3" + "@badeball/cypress-cucumber-preprocessor": "^15.1.4", + "@bahmutov/cypress-esbuild-preprocessor": "^2.2.0" + }, + "engines": { + "node": ">=v18.13.0" } } diff --git a/mocha-ts-multi-reporters/README.md b/mocha-ts-multi-reporters/README.md index 0c3d31a8..a1b9f8e2 100644 --- a/mocha-ts-multi-reporters/README.md +++ b/mocha-ts-multi-reporters/README.md @@ -7,7 +7,7 @@ This is a playground for your first steps in testing, so instead of installing i 1) Clone this repository: ``` -git clone git@github.com:testomatio/examples.git && cd examples/mocha-typescript +git clone git@github.com:testomatio/examples.git && cd examples/mocha-ts-multi-reporters ``` ## Requirements: **in order to run this project, NodeJS (version 18+) must be installed** From 9cb27e4e24e2f1a638ce44214c193e763689d135 Mon Sep 17 00:00:00 2001 From: Oleksandr Pelykh Date: Sun, 12 Mar 2023 11:32:46 +0200 Subject: [PATCH 09/64] add newman example project --- newman/README.md | 47 ++++++ newman/collection.json | 259 ++++++++++++++++++++++++++++++++ newman/collection_fail.json | 291 ++++++++++++++++++++++++++++++++++++ newman/env.json | 39 +++++ newman/package.json | 16 ++ 5 files changed, 652 insertions(+) create mode 100644 newman/README.md create mode 100644 newman/collection.json create mode 100644 newman/collection_fail.json create mode 100644 newman/env.json create mode 100644 newman/package.json diff --git a/newman/README.md b/newman/README.md new file mode 100644 index 00000000..488a130d --- /dev/null +++ b/newman/README.md @@ -0,0 +1,47 @@ +### Example project for newman-reporter-testomatio + +This is example project for [newman-reporter-testomatio](https://www.npmjs.com/package/newman-reporter-testomatio) package usage. + +# Usage + +### Running in this project +1. Install modules: +\ +`npm i` + +2. Run tests +- `TESTOMATIO= npm run test` +- `TESTOMATIO= npm run test:fail` + + +### Running in **your** project +1. Install package +\ +`npm i newman-reporter-testomatio` + +2. Run collection and specify `testomatio` as reporter: +\ +`TESTOMATIO= npx newman run -e -r testomatio ` +\ +> -e (environment) is optional param; others are required + +Examples: +\ +`TESTOMATIO= npx newman run collection.json -e env.json -r testomatio` +\ +`TESTOMATIO= npx newman run collection_fail.json -e env.json -r testomatio` + +> No need to pass `TESTOMATIO_CREATE=1`. This param is already set by default when using `newman-reporter-testomatio`. + +## Troubleshoting +### If you got an error running your collection, read next +\ +`newman` and `newman-reporter-testomatio` should be installed in the same directory. +- If you run your tests using **globally** installed newman (`newman run ...`): +\ +intall `newman-reporter-testomatio` globally too (`npm i newman-reporter-testomatio -g`). +- If you use **locally** installed newman (within the project) (`npx newman run ...`): +\ +Follow default flow – just `npm i` + +You can verify installed packages via `npm list` or `npm list -g` diff --git a/newman/collection.json b/newman/collection.json new file mode 100644 index 00000000..3663af09 --- /dev/null +++ b/newman/collection.json @@ -0,0 +1,259 @@ +{ + "info": { + "_postman_id": "74f760d2-689d-4064-bd69-77752ad0c3a9", + "name": "Echo collection", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", + "_exporter_id": "11123710" + }, + "item": [ + { + "name": "Request with url params", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status test\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "pm.test(\"Status test\", function () {", + " const responseJson = pm.response.json();", + " pm.expect(responseJson.args.paramName).to.equal('paramValue');", + "});", + "", + "pm.test(\"Variable value is updated in pre-request script\", function () {", + " const updateVariableValue = pm.environment.get('textVar');", + " pm.expect(updateVariableValue).to.equal('textVarValue');", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "postman-echo.com/get?paramName=paramValue", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "get" + ], + "query": [ + { + "key": "paramName", + "value": "paramValue" + } + ] + } + }, + "response": [] + }, + { + "name": "Request with pre-request script", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Variable value is updated in pre-request script\", function () {", + " const updateVariableValue = pm.environment.get('textVar');", + " pm.expect(updateVariableValue).to.equal('updatedTextVar');", + "});", + "" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "console.log('This is pre-request script');", + "pm.environment.set('textVar', 'updatedTextVar');" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "postman-echo.com/get?paramName=paramValue", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "get" + ], + "query": [ + { + "key": "paramName", + "value": "paramValue" + } + ] + } + }, + "response": [] + }, + { + "name": "Post request with env variables", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "console.log('This is pre-request script');", + "console.log('Printing environment variable value:', pm.environment.get('url'));", + "" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Environment variables are got properly\", function () {", + " const responseJson = pm.response.json();", + " pm.expect(responseJson.data.testKey).to.equal(13);", + " ", + " const paramName = pm.environment.get('paramName');", + " const paramValue = pm.environment.get('paramValue');", + " pm.expect(responseJson.args[paramName]).to.equal(paramValue);", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"testKey\": {{numberVar}}\n}\n", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{url}}/post?{{paramName}}={{paramValue}}", + "host": [ + "{{url}}" + ], + "path": [ + "post" + ], + "query": [ + { + "key": "{{paramName}}", + "value": "{{paramValue}}" + } + ] + } + }, + "response": [] + }, + { + "name": "Put request with body", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status test\", function () {", + " pm.response.to.have.status(200);", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PUT", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"key1\": \"someValue\",\n \"key2\": 13\n}\n", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "https://postman-echo.com/put", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "put" + ] + } + }, + "response": [] + }, + { + "name": "Request with auth", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status test\", function () {", + " pm.expect(pm.request.auth.type).to.equal('apikey');", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "apikey", + "apikey": [ + { + "key": "key", + "value": "testTokenName", + "type": "string" + }, + { + "key": "value", + "value": "testTokenValue", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "https://postman-echo.com/get", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "get" + ] + } + }, + "response": [] + } + ] +} \ No newline at end of file diff --git a/newman/collection_fail.json b/newman/collection_fail.json new file mode 100644 index 00000000..66f32a98 --- /dev/null +++ b/newman/collection_fail.json @@ -0,0 +1,291 @@ +{ + "info": { + "_postman_id": "74f760d2-689d-4064-bd69-77752ad0c3a9", + "name": "Echo collection", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", + "_exporter_id": "11123710" + }, + "item": [ + { + "name": "Request with url params", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status test\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "pm.test(\"Status test\", function () {", + " const responseJson = pm.response.json();", + " pm.expect(responseJson.args.paramName).to.equal('paramValue');", + "});", + "", + "pm.test(\"Variable value is updated in pre-request script\", function () {", + " const updateVariableValue = pm.environment.get('textVar');", + " pm.expect(updateVariableValue).to.equal('textVarValue');", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "postman-echo.com/get?paramName=paramValue", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "get" + ], + "query": [ + { + "key": "paramName", + "value": "paramValue" + } + ] + } + }, + "response": [] + }, + { + "name": "Request with pre-request script", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Variable value is updated in pre-request script\", function () {", + " const updateVariableValue = pm.environment.get('textVar');", + " pm.expect(updateVariableValue).to.equal('updatedTextVar');", + "});", + "" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "console.log('This is pre-request script');", + "pm.environment.set('textVar', 'updatedTextVar');" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "postman-echo.com/get?paramName=paramValue", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "get" + ], + "query": [ + { + "key": "paramName", + "value": "paramValue" + } + ] + } + }, + "response": [] + }, + { + "name": "Post request with env variables", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "console.log('This is pre-request script');", + "console.log('Printing environment variable value:', pm.environment.get('url'));", + "" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Environment variables are got properly\", function () {", + " const responseJson = pm.response.json();", + " pm.expect(responseJson.data.testKey).to.equal(13);", + " ", + " const paramName = pm.environment.get('paramName');", + " const paramValue = pm.environment.get('paramValue');", + " pm.expect(responseJson.args[paramName]).to.equal(paramValue);", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"testKey\": {{numberVar}}\n}\n", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{url}}/post?{{paramName}}={{paramValue}}", + "host": [ + "{{url}}" + ], + "path": [ + "post" + ], + "query": [ + { + "key": "{{paramName}}", + "value": "{{paramValue}}" + } + ] + } + }, + "response": [] + }, + { + "name": "Put request with body", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status test\", function () {", + " pm.response.to.have.status(200);", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PUT", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"key1\": \"someValue\",\n \"key2\": 13\n}\n", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "https://postman-echo.com/put", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "put" + ] + } + }, + "response": [] + }, + { + "name": "Request with auth", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status test\", function () {", + " pm.expect(pm.request.auth.type).to.equal('apikey');", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "apikey", + "apikey": [ + { + "key": "key", + "value": "testTokenName", + "type": "string" + }, + { + "key": "value", + "value": "testTokenValue", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "https://postman-echo.com/get", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "get" + ] + } + }, + "response": [] + }, + { + "name": "Failing tests", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"This test will fail\", function () {", + " pm.response.to.have.status(222);", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "postman-echo.com/get", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "get" + ] + } + }, + "response": [] + } + ] +} \ No newline at end of file diff --git a/newman/env.json b/newman/env.json new file mode 100644 index 00000000..ba325f8c --- /dev/null +++ b/newman/env.json @@ -0,0 +1,39 @@ +{ + "id": "90577d60-1765-4393-a768-571eba881da3", + "name": "testomatio", + "values": [ + { + "key": "url", + "value": "postman-echo.com", + "type": "default", + "enabled": true + }, + { + "key": "paramName", + "value": "someParamName", + "type": "default", + "enabled": true + }, + { + "key": "textVar", + "value": "textVarValue", + "type": "default", + "enabled": true + }, + { + "key": "paramValue", + "value": "someParamValue", + "type": "default", + "enabled": true + }, + { + "key": "numberVar", + "value": "13", + "type": "default", + "enabled": true + } + ], + "_postman_variable_scope": "environment", + "_postman_exported_at": "2023-03-12T08:55:53.203Z", + "_postman_exported_using": "Postman/10.11.2" +} \ No newline at end of file diff --git a/newman/package.json b/newman/package.json new file mode 100644 index 00000000..14c0eefe --- /dev/null +++ b/newman/package.json @@ -0,0 +1,16 @@ +{ + "name": "Example project for tesomatio-reporter using newman", + "version": "0.0.1", + "description": "", + "main": "index.js", + "scripts": { + "test": "npx newman run collection.json -e env.json -r testomatio", + "test:fail": "npx newman run collection_fail.json -e env.json -r testomatio" + }, + "author": "", + "license": "ISC", + "dependencies": { + "newman": "^5.3.2", + "newman-reporter-testomatio": "file:../../reporter/packages/newman-reporter-testomatio/newman-reporter-testomatio-0.1.0.tgz" + } +} From a63ef606da176587b53902e9eac9b664b5c472d2 Mon Sep 17 00:00:00 2001 From: olexandr13 Date: Mon, 13 Mar 2023 14:03:36 +0200 Subject: [PATCH 10/64] add newman example project (#31) --- newman/README.md | 47 ++++++ newman/collection.json | 259 ++++++++++++++++++++++++++++++++ newman/collection_fail.json | 291 ++++++++++++++++++++++++++++++++++++ newman/env.json | 39 +++++ newman/package.json | 16 ++ 5 files changed, 652 insertions(+) create mode 100644 newman/README.md create mode 100644 newman/collection.json create mode 100644 newman/collection_fail.json create mode 100644 newman/env.json create mode 100644 newman/package.json diff --git a/newman/README.md b/newman/README.md new file mode 100644 index 00000000..488a130d --- /dev/null +++ b/newman/README.md @@ -0,0 +1,47 @@ +### Example project for newman-reporter-testomatio + +This is example project for [newman-reporter-testomatio](https://www.npmjs.com/package/newman-reporter-testomatio) package usage. + +# Usage + +### Running in this project +1. Install modules: +\ +`npm i` + +2. Run tests +- `TESTOMATIO= npm run test` +- `TESTOMATIO= npm run test:fail` + + +### Running in **your** project +1. Install package +\ +`npm i newman-reporter-testomatio` + +2. Run collection and specify `testomatio` as reporter: +\ +`TESTOMATIO= npx newman run -e -r testomatio ` +\ +> -e (environment) is optional param; others are required + +Examples: +\ +`TESTOMATIO= npx newman run collection.json -e env.json -r testomatio` +\ +`TESTOMATIO= npx newman run collection_fail.json -e env.json -r testomatio` + +> No need to pass `TESTOMATIO_CREATE=1`. This param is already set by default when using `newman-reporter-testomatio`. + +## Troubleshoting +### If you got an error running your collection, read next +\ +`newman` and `newman-reporter-testomatio` should be installed in the same directory. +- If you run your tests using **globally** installed newman (`newman run ...`): +\ +intall `newman-reporter-testomatio` globally too (`npm i newman-reporter-testomatio -g`). +- If you use **locally** installed newman (within the project) (`npx newman run ...`): +\ +Follow default flow – just `npm i` + +You can verify installed packages via `npm list` or `npm list -g` diff --git a/newman/collection.json b/newman/collection.json new file mode 100644 index 00000000..3663af09 --- /dev/null +++ b/newman/collection.json @@ -0,0 +1,259 @@ +{ + "info": { + "_postman_id": "74f760d2-689d-4064-bd69-77752ad0c3a9", + "name": "Echo collection", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", + "_exporter_id": "11123710" + }, + "item": [ + { + "name": "Request with url params", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status test\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "pm.test(\"Status test\", function () {", + " const responseJson = pm.response.json();", + " pm.expect(responseJson.args.paramName).to.equal('paramValue');", + "});", + "", + "pm.test(\"Variable value is updated in pre-request script\", function () {", + " const updateVariableValue = pm.environment.get('textVar');", + " pm.expect(updateVariableValue).to.equal('textVarValue');", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "postman-echo.com/get?paramName=paramValue", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "get" + ], + "query": [ + { + "key": "paramName", + "value": "paramValue" + } + ] + } + }, + "response": [] + }, + { + "name": "Request with pre-request script", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Variable value is updated in pre-request script\", function () {", + " const updateVariableValue = pm.environment.get('textVar');", + " pm.expect(updateVariableValue).to.equal('updatedTextVar');", + "});", + "" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "console.log('This is pre-request script');", + "pm.environment.set('textVar', 'updatedTextVar');" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "postman-echo.com/get?paramName=paramValue", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "get" + ], + "query": [ + { + "key": "paramName", + "value": "paramValue" + } + ] + } + }, + "response": [] + }, + { + "name": "Post request with env variables", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "console.log('This is pre-request script');", + "console.log('Printing environment variable value:', pm.environment.get('url'));", + "" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Environment variables are got properly\", function () {", + " const responseJson = pm.response.json();", + " pm.expect(responseJson.data.testKey).to.equal(13);", + " ", + " const paramName = pm.environment.get('paramName');", + " const paramValue = pm.environment.get('paramValue');", + " pm.expect(responseJson.args[paramName]).to.equal(paramValue);", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"testKey\": {{numberVar}}\n}\n", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{url}}/post?{{paramName}}={{paramValue}}", + "host": [ + "{{url}}" + ], + "path": [ + "post" + ], + "query": [ + { + "key": "{{paramName}}", + "value": "{{paramValue}}" + } + ] + } + }, + "response": [] + }, + { + "name": "Put request with body", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status test\", function () {", + " pm.response.to.have.status(200);", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PUT", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"key1\": \"someValue\",\n \"key2\": 13\n}\n", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "https://postman-echo.com/put", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "put" + ] + } + }, + "response": [] + }, + { + "name": "Request with auth", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status test\", function () {", + " pm.expect(pm.request.auth.type).to.equal('apikey');", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "apikey", + "apikey": [ + { + "key": "key", + "value": "testTokenName", + "type": "string" + }, + { + "key": "value", + "value": "testTokenValue", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "https://postman-echo.com/get", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "get" + ] + } + }, + "response": [] + } + ] +} \ No newline at end of file diff --git a/newman/collection_fail.json b/newman/collection_fail.json new file mode 100644 index 00000000..66f32a98 --- /dev/null +++ b/newman/collection_fail.json @@ -0,0 +1,291 @@ +{ + "info": { + "_postman_id": "74f760d2-689d-4064-bd69-77752ad0c3a9", + "name": "Echo collection", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", + "_exporter_id": "11123710" + }, + "item": [ + { + "name": "Request with url params", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status test\", function () {", + " pm.response.to.have.status(200);", + "});", + "", + "pm.test(\"Status test\", function () {", + " const responseJson = pm.response.json();", + " pm.expect(responseJson.args.paramName).to.equal('paramValue');", + "});", + "", + "pm.test(\"Variable value is updated in pre-request script\", function () {", + " const updateVariableValue = pm.environment.get('textVar');", + " pm.expect(updateVariableValue).to.equal('textVarValue');", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "postman-echo.com/get?paramName=paramValue", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "get" + ], + "query": [ + { + "key": "paramName", + "value": "paramValue" + } + ] + } + }, + "response": [] + }, + { + "name": "Request with pre-request script", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Variable value is updated in pre-request script\", function () {", + " const updateVariableValue = pm.environment.get('textVar');", + " pm.expect(updateVariableValue).to.equal('updatedTextVar');", + "});", + "" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "console.log('This is pre-request script');", + "pm.environment.set('textVar', 'updatedTextVar');" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "postman-echo.com/get?paramName=paramValue", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "get" + ], + "query": [ + { + "key": "paramName", + "value": "paramValue" + } + ] + } + }, + "response": [] + }, + { + "name": "Post request with env variables", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "console.log('This is pre-request script');", + "console.log('Printing environment variable value:', pm.environment.get('url'));", + "" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Environment variables are got properly\", function () {", + " const responseJson = pm.response.json();", + " pm.expect(responseJson.data.testKey).to.equal(13);", + " ", + " const paramName = pm.environment.get('paramName');", + " const paramValue = pm.environment.get('paramValue');", + " pm.expect(responseJson.args[paramName]).to.equal(paramValue);", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"testKey\": {{numberVar}}\n}\n", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{url}}/post?{{paramName}}={{paramValue}}", + "host": [ + "{{url}}" + ], + "path": [ + "post" + ], + "query": [ + { + "key": "{{paramName}}", + "value": "{{paramValue}}" + } + ] + } + }, + "response": [] + }, + { + "name": "Put request with body", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status test\", function () {", + " pm.response.to.have.status(200);", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PUT", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"key1\": \"someValue\",\n \"key2\": 13\n}\n", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "https://postman-echo.com/put", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "put" + ] + } + }, + "response": [] + }, + { + "name": "Request with auth", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status test\", function () {", + " pm.expect(pm.request.auth.type).to.equal('apikey');", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "apikey", + "apikey": [ + { + "key": "key", + "value": "testTokenName", + "type": "string" + }, + { + "key": "value", + "value": "testTokenValue", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "https://postman-echo.com/get", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "get" + ] + } + }, + "response": [] + }, + { + "name": "Failing tests", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"This test will fail\", function () {", + " pm.response.to.have.status(222);", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "postman-echo.com/get", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "get" + ] + } + }, + "response": [] + } + ] +} \ No newline at end of file diff --git a/newman/env.json b/newman/env.json new file mode 100644 index 00000000..ba325f8c --- /dev/null +++ b/newman/env.json @@ -0,0 +1,39 @@ +{ + "id": "90577d60-1765-4393-a768-571eba881da3", + "name": "testomatio", + "values": [ + { + "key": "url", + "value": "postman-echo.com", + "type": "default", + "enabled": true + }, + { + "key": "paramName", + "value": "someParamName", + "type": "default", + "enabled": true + }, + { + "key": "textVar", + "value": "textVarValue", + "type": "default", + "enabled": true + }, + { + "key": "paramValue", + "value": "someParamValue", + "type": "default", + "enabled": true + }, + { + "key": "numberVar", + "value": "13", + "type": "default", + "enabled": true + } + ], + "_postman_variable_scope": "environment", + "_postman_exported_at": "2023-03-12T08:55:53.203Z", + "_postman_exported_using": "Postman/10.11.2" +} \ No newline at end of file diff --git a/newman/package.json b/newman/package.json new file mode 100644 index 00000000..14c0eefe --- /dev/null +++ b/newman/package.json @@ -0,0 +1,16 @@ +{ + "name": "Example project for tesomatio-reporter using newman", + "version": "0.0.1", + "description": "", + "main": "index.js", + "scripts": { + "test": "npx newman run collection.json -e env.json -r testomatio", + "test:fail": "npx newman run collection_fail.json -e env.json -r testomatio" + }, + "author": "", + "license": "ISC", + "dependencies": { + "newman": "^5.3.2", + "newman-reporter-testomatio": "file:../../reporter/packages/newman-reporter-testomatio/newman-reporter-testomatio-0.1.0.tgz" + } +} From f629d4e6cb7ecc5c967fbb66e56ef6946bdc9691 Mon Sep 17 00:00:00 2001 From: Oleksandr Pelykh Date: Wed, 15 Mar 2023 09:08:04 +0200 Subject: [PATCH 11/64] fix newman-reporter-testomatio installation path --- newman/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/newman/package.json b/newman/package.json index 14c0eefe..2e7b8e40 100644 --- a/newman/package.json +++ b/newman/package.json @@ -11,6 +11,6 @@ "license": "ISC", "dependencies": { "newman": "^5.3.2", - "newman-reporter-testomatio": "file:../../reporter/packages/newman-reporter-testomatio/newman-reporter-testomatio-0.1.0.tgz" + "newman-reporter-testomatio": "^0.1.0" } } From 2495b7b46f722ca5abe60854551325f0ed070af2 Mon Sep 17 00:00:00 2001 From: Oleksandr Pelykh Date: Wed, 15 Mar 2023 09:10:32 +0200 Subject: [PATCH 12/64] update newman-reporter-testomatio version to latest --- newman/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/newman/package.json b/newman/package.json index 2e7b8e40..11e07118 100644 --- a/newman/package.json +++ b/newman/package.json @@ -11,6 +11,6 @@ "license": "ISC", "dependencies": { "newman": "^5.3.2", - "newman-reporter-testomatio": "^0.1.0" + "newman-reporter-testomatio": "latest" } } From df6df514dbe77330bc9e1edc843656dfb94f1edc Mon Sep 17 00:00:00 2001 From: Oleksandr Pelykh Date: Wed, 15 Mar 2023 11:23:49 +0200 Subject: [PATCH 13/64] fix typo --- newman/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/newman/README.md b/newman/README.md index 488a130d..7afbfce8 100644 --- a/newman/README.md +++ b/newman/README.md @@ -33,7 +33,7 @@ Examples: > No need to pass `TESTOMATIO_CREATE=1`. This param is already set by default when using `newman-reporter-testomatio`. -## Troubleshoting +## Troubleshooting ### If you got an error running your collection, read next \ `newman` and `newman-reporter-testomatio` should be installed in the same directory. From 4b47f5a4979daa2e1958fc45229fea2b275e4d1f Mon Sep 17 00:00:00 2001 From: Oleksandr Pelykh Date: Wed, 15 Mar 2023 11:47:05 +0200 Subject: [PATCH 14/64] upd readme --- newman/README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/newman/README.md b/newman/README.md index 7afbfce8..3d43a935 100644 --- a/newman/README.md +++ b/newman/README.md @@ -10,8 +10,8 @@ This is example project for [newman-reporter-testomatio](https://www.npmjs.com/p `npm i` 2. Run tests -- `TESTOMATIO= npm run test` -- `TESTOMATIO= npm run test:fail` +- `TESTOMATIO= npm run test` +- `TESTOMATIO= npm run test:fail` ### Running in **your** project @@ -21,20 +21,20 @@ This is example project for [newman-reporter-testomatio](https://www.npmjs.com/p 2. Run collection and specify `testomatio` as reporter: \ -`TESTOMATIO= npx newman run -e -r testomatio ` +`TESTOMATIO= npx newman run -e -r testomatio ` \ > -e (environment) is optional param; others are required Examples: \ -`TESTOMATIO= npx newman run collection.json -e env.json -r testomatio` +`TESTOMATIO= npx newman run collection.json -e env.json -r testomatio` \ -`TESTOMATIO= npx newman run collection_fail.json -e env.json -r testomatio` +`TESTOMATIO= npx newman run collection_fail.json -e env.json -r testomatio` > No need to pass `TESTOMATIO_CREATE=1`. This param is already set by default when using `newman-reporter-testomatio`. ## Troubleshooting -### If you got an error running your collection, read next +### If you got an error running your collection _("could not find testomatio reporter")_, read next \ `newman` and `newman-reporter-testomatio` should be installed in the same directory. - If you run your tests using **globally** installed newman (`newman run ...`): From 8985f6917a9de76245ef07e1b7807575c3145e9a Mon Sep 17 00:00:00 2001 From: Oleksandr Pelykh Date: Wed, 15 Mar 2023 11:48:50 +0200 Subject: [PATCH 15/64] fix package name --- newman/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/newman/package.json b/newman/package.json index 11e07118..7b4d41bd 100644 --- a/newman/package.json +++ b/newman/package.json @@ -1,7 +1,7 @@ { - "name": "Example project for tesomatio-reporter using newman", + "name": "example-project-for-newman-testomatio-reporter", "version": "0.0.1", - "description": "", + "description": "Example project for tesomatio-reporter using newman", "main": "index.js", "scripts": { "test": "npx newman run collection.json -e env.json -r testomatio", From 3f7e788cff450788f35a634e34eb1915312b192d Mon Sep 17 00:00:00 2001 From: Oleksandr Pelykh Date: Wed, 15 Mar 2023 11:51:09 +0200 Subject: [PATCH 16/64] fix typo --- newman/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/newman/package.json b/newman/package.json index 7b4d41bd..8f960c13 100644 --- a/newman/package.json +++ b/newman/package.json @@ -1,7 +1,7 @@ { "name": "example-project-for-newman-testomatio-reporter", "version": "0.0.1", - "description": "Example project for tesomatio-reporter using newman", + "description": "Example project for testomatio reporter using newman", "main": "index.js", "scripts": { "test": "npx newman run collection.json -e env.json -r testomatio", From 5b201a918d7bdf3c8ec4e3b2332b8b105b77a230 Mon Sep 17 00:00:00 2001 From: Oleksandr Pelykh Date: Wed, 15 Mar 2023 11:51:33 +0200 Subject: [PATCH 17/64] upd name --- newman/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/newman/package.json b/newman/package.json index 8f960c13..0d15a235 100644 --- a/newman/package.json +++ b/newman/package.json @@ -1,5 +1,5 @@ { - "name": "example-project-for-newman-testomatio-reporter", + "name": "example-project-for-newman-reporter-testomatio", "version": "0.0.1", "description": "Example project for testomatio reporter using newman", "main": "index.js", From 059f7970c6845a56e411c4368c6205d01e7f583c Mon Sep 17 00:00:00 2001 From: Oleksandr Pelykh Date: Wed, 15 Mar 2023 12:41:05 +0200 Subject: [PATCH 18/64] add ci job --- .github/workflows/newman-tests.yml | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 .github/workflows/newman-tests.yml diff --git a/.github/workflows/newman-tests.yml b/.github/workflows/newman-tests.yml new file mode 100644 index 00000000..ce895727 --- /dev/null +++ b/.github/workflows/newman-tests.yml @@ -0,0 +1,23 @@ +name: newman tests +on: + pull_request: + branches: [master] + +concurrency: + group: newman-tests-${{github.event.pull_request.number}} + cancel-in-progress: true + +jobs: + + build: + runs-on: ubuntu-latest + defaults: + run: + working-directory: newman + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v2 + with: + node-version: 18.x + - run: npm i + - run: TESTOMATIO=m6ef70356h7cnpm run test From 83c5c20c860677788f6e5c951f590eda0c7dcbef Mon Sep 17 00:00:00 2001 From: Oleksandr Pelykh Date: Wed, 15 Mar 2023 12:45:34 +0200 Subject: [PATCH 19/64] fix work dir --- .github/workflows/newman-tests.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/newman-tests.yml b/.github/workflows/newman-tests.yml index ce895727..a1c9b39a 100644 --- a/.github/workflows/newman-tests.yml +++ b/.github/workflows/newman-tests.yml @@ -11,13 +11,12 @@ jobs: build: runs-on: ubuntu-latest - defaults: - run: - working-directory: newman steps: - uses: actions/checkout@v3 - uses: actions/setup-node@v2 with: node-version: 18.x - run: npm i + working-directory: newman - run: TESTOMATIO=m6ef70356h7cnpm run test + working-directory: newman From 8ef3af211b4076dcf496527d665ce0d8e81da3a8 Mon Sep 17 00:00:00 2001 From: Oleksandr Pelykh Date: Wed, 15 Mar 2023 12:46:48 +0200 Subject: [PATCH 20/64] fix job command --- .github/workflows/newman-tests.yml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/.github/workflows/newman-tests.yml b/.github/workflows/newman-tests.yml index a1c9b39a..987424b1 100644 --- a/.github/workflows/newman-tests.yml +++ b/.github/workflows/newman-tests.yml @@ -8,15 +8,16 @@ concurrency: cancel-in-progress: true jobs: - - build: + newman-tests: runs-on: ubuntu-latest + defaults: + run: + working-directory: newman + working-directory: newman steps: - uses: actions/checkout@v3 - uses: actions/setup-node@v2 with: node-version: 18.x - run: npm i - working-directory: newman - - run: TESTOMATIO=m6ef70356h7cnpm run test - working-directory: newman + - run: TESTOMATIO=m6ef70356h7c npm run test From 84460edb5c94eb22c2a0203f390d6614e1107642 Mon Sep 17 00:00:00 2001 From: Oleksandr Pelykh Date: Wed, 15 Mar 2023 12:47:26 +0200 Subject: [PATCH 21/64] fix working-directory --- .github/workflows/newman-tests.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/newman-tests.yml b/.github/workflows/newman-tests.yml index 987424b1..fb44d2f9 100644 --- a/.github/workflows/newman-tests.yml +++ b/.github/workflows/newman-tests.yml @@ -13,7 +13,6 @@ jobs: defaults: run: working-directory: newman - working-directory: newman steps: - uses: actions/checkout@v3 - uses: actions/setup-node@v2 From e73af8b531334a6ee81faa26a60202a8dd93289b Mon Sep 17 00:00:00 2001 From: Oleksandr Pelykh Date: Wed, 15 Mar 2023 12:49:38 +0200 Subject: [PATCH 22/64] add command for global newman --- .github/workflows/newman-tests.yml | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/.github/workflows/newman-tests.yml b/.github/workflows/newman-tests.yml index fb44d2f9..400d8b76 100644 --- a/.github/workflows/newman-tests.yml +++ b/.github/workflows/newman-tests.yml @@ -8,7 +8,20 @@ concurrency: cancel-in-progress: true jobs: - newman-tests: + newman-global: + runs-on: ubuntu-latest + defaults: + run: + working-directory: newman + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v2 + with: + node-version: 18.x + - run: npm i newman-reporter-testomatio -g + - run: TESTOMATIO=m6ef70356h7c newman run collection.json -e env.json -r testomatio + + newman-local: runs-on: ubuntu-latest defaults: run: From 73a1899845dcb5bebb860b517bb07259f83a3753 Mon Sep 17 00:00:00 2001 From: Oleksandr Pelykh Date: Wed, 15 Mar 2023 12:51:38 +0200 Subject: [PATCH 23/64] test --- .github/workflows/newman-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/newman-tests.yml b/.github/workflows/newman-tests.yml index 400d8b76..b73999b1 100644 --- a/.github/workflows/newman-tests.yml +++ b/.github/workflows/newman-tests.yml @@ -18,7 +18,7 @@ jobs: - uses: actions/setup-node@v2 with: node-version: 18.x - - run: npm i newman-reporter-testomatio -g + - run: npm i newman-reporter-testomatio newman -g - run: TESTOMATIO=m6ef70356h7c newman run collection.json -e env.json -r testomatio newman-local: From 8d852c1443492cc6af42a8555ae3329bee5b27b6 Mon Sep 17 00:00:00 2001 From: Vitalii Mykhailiuk <82405549+mykhailiukVitalii@users.noreply.github.com> Date: Sun, 7 May 2023 20:00:04 +0300 Subject: [PATCH 24/64] Playwright example updates(up playwright version) + different groups: e2e & smoke (#37) * playwright example updates(to the latest version) * playwright example updates: different modes like e2e and smoke --- playwright/.gitignore | 3 + playwright/README.md | 33 ++++++++- playwright/e2e-examples/README.md | 16 +++++ .../{ => e2e-tests}/1-getting-started.spec.ts | 3 +- .../{ => e2e-tests}/2-actions.spec.ts | 0 .../{ => e2e-tests}/3-assertions.spec.ts | 0 .../{ => e2e-tests}/4-file-uploads.spec.ts | 0 .../{ => e2e-tests}/5-networking.spec.ts | 0 .../server/assets/api/v1/users.json | 0 .../server/assets/file-uploads.html | 0 .../{ => e2e-tests}/server/assets/header.html | 0 .../{ => e2e-tests}/server/assets/index.html | 0 .../server/assets/network.html | 0 .../{ => e2e-tests}/server/index.js | 0 ...ght.config.ts => playwright-e2e.config.ts} | 9 ++- .../e2e-examples/playwright-smoke.config.ts | 13 ++++ .../smoke-tests/smoke-test-1.spec.ts | 17 +++++ .../smoke-tests/smoke-test-2.spec.ts | 35 ++++++++++ .../smoke-tests/smoke-test-3.spec.ts | 36 ++++++++++ .../smoke-tests/smoke-test-4.spec.ts | 20 ++++++ .../smoke-tests/smoke-test-5.spec.ts | 69 +++++++++++++++++++ playwright/e2e-examples/src/Basic.ts | 11 +++ playwright/e2e/example.spec.ts | 7 -- playwright/package.json | 9 +-- playwright/playwright.config.ts | 28 ++++---- 25 files changed, 279 insertions(+), 30 deletions(-) rename playwright/e2e-examples/{ => e2e-tests}/1-getting-started.spec.ts (76%) rename playwright/e2e-examples/{ => e2e-tests}/2-actions.spec.ts (100%) rename playwright/e2e-examples/{ => e2e-tests}/3-assertions.spec.ts (100%) rename playwright/e2e-examples/{ => e2e-tests}/4-file-uploads.spec.ts (100%) rename playwright/e2e-examples/{ => e2e-tests}/5-networking.spec.ts (100%) rename playwright/e2e-examples/{ => e2e-tests}/server/assets/api/v1/users.json (100%) rename playwright/e2e-examples/{ => e2e-tests}/server/assets/file-uploads.html (100%) rename playwright/e2e-examples/{ => e2e-tests}/server/assets/header.html (100%) rename playwright/e2e-examples/{ => e2e-tests}/server/assets/index.html (100%) rename playwright/e2e-examples/{ => e2e-tests}/server/assets/network.html (100%) rename playwright/e2e-examples/{ => e2e-tests}/server/index.js (100%) rename playwright/e2e-examples/{playwright.config.ts => playwright-e2e.config.ts} (65%) create mode 100644 playwright/e2e-examples/playwright-smoke.config.ts create mode 100644 playwright/e2e-examples/smoke-tests/smoke-test-1.spec.ts create mode 100644 playwright/e2e-examples/smoke-tests/smoke-test-2.spec.ts create mode 100644 playwright/e2e-examples/smoke-tests/smoke-test-3.spec.ts create mode 100644 playwright/e2e-examples/smoke-tests/smoke-test-4.spec.ts create mode 100644 playwright/e2e-examples/smoke-tests/smoke-test-5.spec.ts create mode 100644 playwright/e2e-examples/src/Basic.ts delete mode 100644 playwright/e2e/example.spec.ts diff --git a/playwright/.gitignore b/playwright/.gitignore index bf771ee0..8461164f 100644 --- a/playwright/.gitignore +++ b/playwright/.gitignore @@ -2,3 +2,6 @@ node_modules/ test-results/ playwright-report/ .env +/test-results/ +/playwright-report/ +/playwright/.cache/ diff --git a/playwright/README.md b/playwright/README.md index 838f10bb..d9f40e59 100644 --- a/playwright/README.md +++ b/playwright/README.md @@ -16,6 +16,24 @@ git clone git@github.com:testomatio/examples.git && cd examples/playwright npm i ``` +2.1) Run tests to check if they work (via npm): + +``` +npm run test:e2e +``` + +2.2) Run only smoke tests (via npm): + +```sh +npm run test:smoke-examples +``` + +2.3) Run only e2e tests (via npm): + +```sh +npm run test:e2e-examples +``` + This will install Playwright with puppeteer & Testomat.io reporter ## Loading Tests to Testomat.io @@ -25,17 +43,22 @@ This will install Playwright with puppeteer & Testomat.io reporter 2. Run `npx check-tests` to upload tests data into testomat.io. Pass api key as `TESTOMATIO` environment variable: ``` -TESTOMATIO={apiKey} npx check-tests playwright --typescript "**/*{.,_}spec.ts" +TESTOMATIO={apiKey} npx check-tests@latest Playwright "**/*{.,_}{test,spec,cy}.ts" --typescript ``` > **Environment variables** It is recommended to store Testomatio API Key as environment variable and never save it in the source code. Set them directly when running tests or use [dotenv](https://www.npmjs.com/package/dotenv) package to save environment variable in a file and load them for tests. -## Publishing Test Results to Testomat.io +## Publishing Test Results to Testomat.io (all available tests) Get API key from a project in Testomat.io and set it as environment variable `TESTOMATIO`: ``` -TESTOMATIO={apiKey} npx playwright tests +TESTOMATIO={apiKey} npx playwright test +``` + +_or only smoke tests:_ +``` +TESTOMATIO={key} npx playwright test --config e2e-examples/playwright-smoke.config.ts ``` ### Configuration @@ -50,3 +73,7 @@ reporter: [ }] ], ``` + +### Pay attention + +_One test has a "FAIL" status... to fix it, see the instructions in the TODO section in file => e2e-examples/e2e-tests/1-getting-started.spec.ts_ \ No newline at end of file diff --git a/playwright/e2e-examples/README.md b/playwright/e2e-examples/README.md index ad807c66..3051cd2a 100644 --- a/playwright/e2e-examples/README.md +++ b/playwright/e2e-examples/README.md @@ -2,7 +2,23 @@ This directory contains examples for Playwright. Run them with the following command: +### e2e tests only + ```sh npm run test:e2e-examples yarn test:e2e-examples ``` + +### smoke tests only + +```sh +npm run test:smoke-examples +yarn test:smoke-examples +``` + +### all available tests + +```sh +npm run test:e2e +yarn test:e2e +``` \ No newline at end of file diff --git a/playwright/e2e-examples/1-getting-started.spec.ts b/playwright/e2e-examples/e2e-tests/1-getting-started.spec.ts similarity index 76% rename from playwright/e2e-examples/1-getting-started.spec.ts rename to playwright/e2e-examples/e2e-tests/1-getting-started.spec.ts index fcc82835..7eb053a1 100644 --- a/playwright/e2e-examples/1-getting-started.spec.ts +++ b/playwright/e2e-examples/e2e-tests/1-getting-started.spec.ts @@ -13,5 +13,6 @@ test('basic test', async ({ page }) => { await inputBox.fill('Learn Playwright'); await inputBox.press('Enter'); - await expect(todoList).toHaveText('Le1arn Playwright'); + // example of test with status FAIL + await expect(todoList).toHaveText('Le1arn Playwright'); //TODO: To fix update text to => 'Learn Playwright' }); diff --git a/playwright/e2e-examples/2-actions.spec.ts b/playwright/e2e-examples/e2e-tests/2-actions.spec.ts similarity index 100% rename from playwright/e2e-examples/2-actions.spec.ts rename to playwright/e2e-examples/e2e-tests/2-actions.spec.ts diff --git a/playwright/e2e-examples/3-assertions.spec.ts b/playwright/e2e-examples/e2e-tests/3-assertions.spec.ts similarity index 100% rename from playwright/e2e-examples/3-assertions.spec.ts rename to playwright/e2e-examples/e2e-tests/3-assertions.spec.ts diff --git a/playwright/e2e-examples/4-file-uploads.spec.ts b/playwright/e2e-examples/e2e-tests/4-file-uploads.spec.ts similarity index 100% rename from playwright/e2e-examples/4-file-uploads.spec.ts rename to playwright/e2e-examples/e2e-tests/4-file-uploads.spec.ts diff --git a/playwright/e2e-examples/5-networking.spec.ts b/playwright/e2e-examples/e2e-tests/5-networking.spec.ts similarity index 100% rename from playwright/e2e-examples/5-networking.spec.ts rename to playwright/e2e-examples/e2e-tests/5-networking.spec.ts diff --git a/playwright/e2e-examples/server/assets/api/v1/users.json b/playwright/e2e-examples/e2e-tests/server/assets/api/v1/users.json similarity index 100% rename from playwright/e2e-examples/server/assets/api/v1/users.json rename to playwright/e2e-examples/e2e-tests/server/assets/api/v1/users.json diff --git a/playwright/e2e-examples/server/assets/file-uploads.html b/playwright/e2e-examples/e2e-tests/server/assets/file-uploads.html similarity index 100% rename from playwright/e2e-examples/server/assets/file-uploads.html rename to playwright/e2e-examples/e2e-tests/server/assets/file-uploads.html diff --git a/playwright/e2e-examples/server/assets/header.html b/playwright/e2e-examples/e2e-tests/server/assets/header.html similarity index 100% rename from playwright/e2e-examples/server/assets/header.html rename to playwright/e2e-examples/e2e-tests/server/assets/header.html diff --git a/playwright/e2e-examples/server/assets/index.html b/playwright/e2e-examples/e2e-tests/server/assets/index.html similarity index 100% rename from playwright/e2e-examples/server/assets/index.html rename to playwright/e2e-examples/e2e-tests/server/assets/index.html diff --git a/playwright/e2e-examples/server/assets/network.html b/playwright/e2e-examples/e2e-tests/server/assets/network.html similarity index 100% rename from playwright/e2e-examples/server/assets/network.html rename to playwright/e2e-examples/e2e-tests/server/assets/network.html diff --git a/playwright/e2e-examples/server/index.js b/playwright/e2e-examples/e2e-tests/server/index.js similarity index 100% rename from playwright/e2e-examples/server/index.js rename to playwright/e2e-examples/e2e-tests/server/index.js diff --git a/playwright/e2e-examples/playwright.config.ts b/playwright/e2e-examples/playwright-e2e.config.ts similarity index 65% rename from playwright/e2e-examples/playwright.config.ts rename to playwright/e2e-examples/playwright-e2e.config.ts index 021eeb31..e7537f1f 100644 --- a/playwright/e2e-examples/playwright.config.ts +++ b/playwright/e2e-examples/playwright-e2e.config.ts @@ -5,9 +5,16 @@ const config: PlaywrightTestConfig = { // Run your local dev server before starting the tests: // https://playwright.dev/docs/test-advanced#launching-a-development-web-server-during-the-tests webServer: { - command: 'node ./server', + command: 'node ./e2e-tests/server', port: 4345, cwd: __dirname, }, + testMatch: '*e2e-tests/*.spec.ts', + reporter: [ + ['list'], + ['@testomatio/reporter/lib/adapter/playwright.js', { + apiKey: process.env.TESTOMATIO, + }] + ] }; export default config; diff --git a/playwright/e2e-examples/playwright-smoke.config.ts b/playwright/e2e-examples/playwright-smoke.config.ts new file mode 100644 index 00000000..8c3b704b --- /dev/null +++ b/playwright/e2e-examples/playwright-smoke.config.ts @@ -0,0 +1,13 @@ +import { PlaywrightTestConfig } from '@playwright/test'; + +// Reference: https://playwright.dev/docs/test-configuration +const config: PlaywrightTestConfig = { + testMatch: '*smoke-tests/*.spec.ts', + reporter: [ + ['list'], + ['@testomatio/reporter/lib/adapter/playwright.js', { + apiKey: process.env.TESTOMATIO, + }] + ] +}; +export default config; diff --git a/playwright/e2e-examples/smoke-tests/smoke-test-1.spec.ts b/playwright/e2e-examples/smoke-tests/smoke-test-1.spec.ts new file mode 100644 index 00000000..e8e2cc9e --- /dev/null +++ b/playwright/e2e-examples/smoke-tests/smoke-test-1.spec.ts @@ -0,0 +1,17 @@ +import { test, expect } from '@playwright/test'; +import Basic from "../src/Basic"; + +const basic = new Basic(); + +test.describe('Smoke case-1', () => { + const basicPage = basic.basicPageUrl(); + + test.beforeEach(async ({ page }) => { + await page.goto(basicPage); + }); + + test('has title', async ({ page }) => { + // Expect a title "to contain" a substring. + await expect(page).toHaveTitle(/Playwright/); + }); +}); \ No newline at end of file diff --git a/playwright/e2e-examples/smoke-tests/smoke-test-2.spec.ts b/playwright/e2e-examples/smoke-tests/smoke-test-2.spec.ts new file mode 100644 index 00000000..d6c6d69f --- /dev/null +++ b/playwright/e2e-examples/smoke-tests/smoke-test-2.spec.ts @@ -0,0 +1,35 @@ +import { test, expect } from '@playwright/test'; +import Basic from "../src/Basic"; + +const basic = new Basic(); + +test.describe('Basic', () => { + const basicPage = basic.basicPageUrl(); + + test.beforeEach(async ({ page }) => { + await page.goto(basicPage); + }); + test.describe('Main Basic tests', () => { + test.describe('[Case 1] Basic page checks 1', () => { + test('get started link', async ({ page }) => { + await test.step(`[Check 1.1] Basic page - click the link`, async () => { + await page.getByRole('link', { name: 'Get started' }).click(); + }); + await test.step(`[Check 1.2] Get started - Expects the URL to contain intro.`, async () => { + await expect(page).toHaveURL(/.*intro/); + }); + }); + }); + + test.describe('[Case 2] Basic page checks 2', () => { + test('get started link', async ({ page }) => { + await test.step(`[Check 2.1] Basic page - click the link`, async () => { + await page.getByRole('link', { name: 'Get started' }).click(); + }); + await test.step(`[Check 2.2] Get started - Expects the URL to contain intro.`, async () => { + await expect(page).toHaveURL(/.*intro/); + }); + }); + }); + }); +}); \ No newline at end of file diff --git a/playwright/e2e-examples/smoke-tests/smoke-test-3.spec.ts b/playwright/e2e-examples/smoke-tests/smoke-test-3.spec.ts new file mode 100644 index 00000000..982a2e85 --- /dev/null +++ b/playwright/e2e-examples/smoke-tests/smoke-test-3.spec.ts @@ -0,0 +1,36 @@ +import { test, expect } from '@playwright/test'; + +test.describe( + 'simple url cheks', + () => { + // Suite + test.describe('Check home page url', () => { + test('get started link', async ({ page }) => { + // Access to the basic page + await page.goto('https://playwright.dev/'); + + // Expects the 'Get started' is shown + await expect(page.getByRole('link', { name: 'Get started' })).toBeVisible(); + }); + }); + + test.describe.skip('Check home page url (skip)', () => { + test('get started link (skip)', async ({ page }) => { + // Access to the basic page + await page.goto('https://playwright.dev/'); + + // Expects the 'Get started' is shown + await expect(page.getByRole('link', { name: 'Get started' })).toBeVisible(); + }); + }); + + test.describe.fixme('Check home page url (fixme)', () => { + test('get started link (fixme)', async ({ page }) => { + // Access to the basic page + await page.goto('https://playwright.dev/'); + + // Expects the 'Get started' is shown + await expect(page.getByRole('link', { name: 'Get started' })).toBeVisible(); + }); + }); + }); \ No newline at end of file diff --git a/playwright/e2e-examples/smoke-tests/smoke-test-4.spec.ts b/playwright/e2e-examples/smoke-tests/smoke-test-4.spec.ts new file mode 100644 index 00000000..5b93826d --- /dev/null +++ b/playwright/e2e-examples/smoke-tests/smoke-test-4.spec.ts @@ -0,0 +1,20 @@ +import { test, expect } from '@playwright/test'; + +test.describe.skip('Skip condition testing', () => { + test.describe('All tests should be skipped', () => { + test('Test-1: get started linkshould be visible (first skipped)', async ({ page }) => { + // Access to the basic page + await page.goto('https://playwright.dev/'); + + // Expects the 'Get started' is shown + await expect(page.getByRole('link', { name: 'Get started' })).toBeVisible(); + }); + test('Test-2: get started linkshould be visible (second skipped)', async ({ page }) => { + // Access to the basic page + await page.goto('https://playwright.dev/'); + + // Expects the 'Get started' is shown + await expect(page.getByRole('link', { name: 'Get started' })).toBeVisible(); + }); + }); +}); \ No newline at end of file diff --git a/playwright/e2e-examples/smoke-tests/smoke-test-5.spec.ts b/playwright/e2e-examples/smoke-tests/smoke-test-5.spec.ts new file mode 100644 index 00000000..f29110f9 --- /dev/null +++ b/playwright/e2e-examples/smoke-tests/smoke-test-5.spec.ts @@ -0,0 +1,69 @@ +import { test, expect, type Page } from '@playwright/test'; + +const TODO_ITEMS = [ + 'buy some cheese', + 'feed the cat', + 'book a doctors appointment' +]; +const URL = 'https://demo.playwright.dev/todomvc'; + +test.describe('New Todo', () => { + let newTodo: any; + + test.beforeEach(async ({ page }) => { + await page.goto(URL); + }); + + test.fixme('[Need to fix] should allow me to add todo items', async ({ page }) => { + await test.step(`[Check 1.1] create a new todo locator`, async () => { + // create a new todo locator + const newTodo = page.getByPlaceholder('What needs to be done?'); + + // Create 1st todo. + await newTodo.fill(TODO_ITEMS[0]); + await newTodo.press('Enter'); + + // Make sure the list only has one todo item. + await expect(page.getByTestId('todo-title')).toHaveText([ + TODO_ITEMS[0] + ]); + }); + }); + test('should allow me to add todo items', async ({ page }) => { + await test.step(`[Check 1.1] create a new todo locator`, async () => { + // create a new todo locator + const newTodo = page.getByPlaceholder('What needs to be done?'); + + // Create 1st todo. + await newTodo.fill(TODO_ITEMS[0]); + await newTodo.press('Enter'); + + // Make sure the list only has one todo item. + await expect(page.getByTestId('todo-title')).toHaveText([ + TODO_ITEMS[0] + ]); + }); + }); + test('should allow me to add todo items (part 1)', async ({ page }) => { + await test.step(`[Check 1.2] create a new todo locator`, async () => { + // create a new todo locator + newTodo = page.getByPlaceholder('What needs to be done?'); + }); + await test.step(`[Check 1.3] Create 1st todo.`, async () => { + // Create 1st todo. + await newTodo.fill(TODO_ITEMS[0]); + await newTodo.press('Enter'); + }); + // Make sure the list only has one todo item. + await expect(page.getByTestId('todo-title')).toHaveText([ + TODO_ITEMS[0] + ]); + await checkNumberOfTodosInLocalStorage(page, 1); + }); +}); + +async function checkNumberOfTodosInLocalStorage(page: Page, expected: number) { + return await page.waitForFunction(e => { + return JSON.parse(localStorage['react-todos']).length === e; + }, expected); +} \ No newline at end of file diff --git a/playwright/e2e-examples/src/Basic.ts b/playwright/e2e-examples/src/Basic.ts new file mode 100644 index 00000000..80dc58d3 --- /dev/null +++ b/playwright/e2e-examples/src/Basic.ts @@ -0,0 +1,11 @@ +export default class Basic { + private url: string; + + public constructor() { + this.url = 'https://playwright.dev/'; + } + + public basicPageUrl(): string { + return this.url; + } +} \ No newline at end of file diff --git a/playwright/e2e/example.spec.ts b/playwright/e2e/example.spec.ts deleted file mode 100644 index 5381600b..00000000 --- a/playwright/e2e/example.spec.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { test, expect } from '@playwright/test'; - -test('basic test', async ({ page }) => { - await page.goto('https://playwright.dev/'); - await page.locator('text=Get started').click(); - await expect(page).toHaveTitle(/Getting started/); -}); diff --git a/playwright/package.json b/playwright/package.json index a84b4ff6..8090bc43 100644 --- a/playwright/package.json +++ b/playwright/package.json @@ -1,20 +1,21 @@ { "name": "new-project", - "version": "1.0.0", + "version": "1.1.0", "description": "", "main": "index.js", "scripts": { - "test:e2e-examples": "playwright test --config e2e-examples/playwright.config.ts", + "test:e2e-examples": "playwright test --config e2e-examples/playwright-e2e.config.ts", + "test:smoke-examples": "playwright test --config e2e-examples/playwright-smoke.config.ts", "test:e2e": "playwright test" }, "keywords": [], "author": "", "license": "ISC", "devDependencies": { - "@playwright/test": "^1.20.1" + "@playwright/test": "^1.33.0" }, "dependencies": { - "@testomatio/reporter": "^0.4.6", + "@testomatio/reporter": "^0.7.6", "dotenv": "^10.0.0" } } diff --git a/playwright/playwright.config.ts b/playwright/playwright.config.ts index 199ae740..24671c04 100644 --- a/playwright/playwright.config.ts +++ b/playwright/playwright.config.ts @@ -1,5 +1,4 @@ import { PlaywrightTestConfig, devices } from '@playwright/test'; -import path from 'path'; require('dotenv').config() @@ -16,10 +15,11 @@ const config: PlaywrightTestConfig = { // Run your local dev server before starting the tests: // https://playwright.dev/docs/test-advanced#launching-a-development-web-server-during-the-tests - // webServer: { - // command: 'npm run start', - // port: 3000, - // }, + webServer: { + command: 'node ./e2e-examples/e2e-tests/server', + port: 4345, + cwd: __dirname, + }, reporter: [ ['list'], ['@testomatio/reporter/lib/adapter/playwright.js', { @@ -41,14 +41,14 @@ const config: PlaywrightTestConfig = { // video: 'retain-on-failure', }, - - // projects: [ - // { - // name: 'Desktop Chrome', - // use: { - // ...devices['Desktop Chrome'], - // }, - // }, + //TODO: only Chrome mode + projects: [ + { + name: 'Desktop Chrome', + use: { + ...devices['Desktop Chrome'], + }, + }, // { // name: 'Desktop Firefox', // use: { @@ -72,6 +72,6 @@ const config: PlaywrightTestConfig = { // name: 'Mobile Safari', // use: devices['iPhone 12'], // }, - // ], + ] }; export default config; From ede6ed948653883afa797069697a460c2d5ca086 Mon Sep 17 00:00:00 2001 From: Oleksandr Pelykh Date: Fri, 26 May 2023 12:25:14 +0300 Subject: [PATCH 25/64] add newman collection with folders --- newman/collection_with_folders.json | 204 ++++++++++++++++++++++++++++ 1 file changed, 204 insertions(+) create mode 100644 newman/collection_with_folders.json diff --git a/newman/collection_with_folders.json b/newman/collection_with_folders.json new file mode 100644 index 00000000..aece8d93 --- /dev/null +++ b/newman/collection_with_folders.json @@ -0,0 +1,204 @@ +{ + "info": { + "_postman_id": "3ae15048-6df3-4905-b68a-ec869d4c348b", + "name": "Echo collection with folders", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", + "_exporter_id": "11123710" + }, + "item": [ + { + "name": "Folder 1", + "item": [ + { + "name": "Request with url params", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "postman-echo.com/get", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "get" + ] + } + }, + "response": [] + }, + { + "name": "Request with pre-request script", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Variable value is updated in pre-request script\", function () {", + " const updateVariableValue = pm.environment.get('textVar');", + " pm.expect(updateVariableValue).to.equal('updatedTextVar');", + "});", + "" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "console.log('This is pre-request script');", + "pm.environment.set('textVar', 'updatedTextVar');" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "postman-echo.com/get?paramName=paramValue", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "get" + ], + "query": [ + { + "key": "paramName", + "value": "paramValue" + } + ] + } + }, + "response": [] + } + ] + }, + { + "name": "Folder 2", + "item": [ + { + "name": "Nested folder", + "item": [ + { + "name": "Request inside nested folder", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status test\", function () {", + " pm.expect(pm.request.auth.type).to.equal('apikey');", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "apikey", + "apikey": [ + { + "key": "key", + "value": "testTokenName", + "type": "string" + }, + { + "key": "value", + "value": "testTokenValue", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "https://postman-echo.com/get", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "get" + ] + } + }, + "response": [] + } + ] + } + ] + }, + { + "name": "Empty folder", + "item": [] + }, + { + "name": "Request within collection (not in folder)", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Variable value is updated in pre-request script\", function () {", + " const updateVariableValue = pm.environment.get('textVar');", + " pm.expect(updateVariableValue).to.equal('updatedTextVar');", + "});", + "" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "console.log('This is pre-request script');", + "pm.environment.set('textVar', 'updatedTextVar');" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "postman-echo.com/get?paramName=paramValue", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "get" + ], + "query": [ + { + "key": "paramName", + "value": "paramValue" + } + ] + } + }, + "response": [] + } + ] +} \ No newline at end of file From 50937e9c7574a78150c66c48e072675f158df150 Mon Sep 17 00:00:00 2001 From: davert Date: Wed, 7 Jun 2023 01:33:28 +0300 Subject: [PATCH 26/64] codeceptjs: updated testomatio reporter --- codeceptJS/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/codeceptJS/package.json b/codeceptJS/package.json index b4c4bec8..19161342 100644 --- a/codeceptJS/package.json +++ b/codeceptJS/package.json @@ -9,7 +9,7 @@ "author": "kaflan, davert", "license": "ISC", "dependencies": { - "@testomatio/reporter": "^0.6.3", + "@testomatio/reporter": "0.8.1", "axios": "^0.19.2", "check-tests": "^0.7.6", "dotenv": "^8.6.0", From dcb818220cfc1e8986174c0e96cc7c2270af4b25 Mon Sep 17 00:00:00 2001 From: davert Date: Tue, 18 Jul 2023 02:26:12 +0300 Subject: [PATCH 27/64] Updated codeception reporter --- codeception/composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/codeception/composer.json b/codeception/composer.json index 4720a704..0553ef62 100644 --- a/codeception/composer.json +++ b/codeception/composer.json @@ -56,7 +56,7 @@ "symfony/phpunit-bridge": "*", "symfony/stopwatch": "*", "testomatio/list-tests": ">=0.1.4", - "testomatio/reporter": "^0.1.5", + "testomatio/reporter": "^0.2.0", "vlucas/phpdotenv": ">2.4" }, "config": { From 80624b53e8df3a8be88a9610f3ebaa7a24984903 Mon Sep 17 00:00:00 2001 From: Vitalii Mykhailiuk <82405549+mykhailiukVitalii@users.noreply.github.com> Date: Tue, 18 Jul 2023 22:03:22 +0300 Subject: [PATCH 28/64] [Examples #28 reporting results] (#40) * [EXAMPLE-REPORTING] added a new workflow action to Github Ci * [EXAMPLE-REPORTING] secret keys in Upper-case * [EXAMPLE-REPORTING] secret keys in Group updates * [EXAMPLE-REPORTING] remove RUN for testing * [EXAMPLE-REPORTING] testing github workflow with schedule mode * [EXAMPLE-REPORTING] testing github workflow based on the branch name * [EXAMPLE-REPORTING] testing github test 2 * [EXAMPLE-REPORTING] testing github test 3 * [EXAMPLE-REPORTING] testing github test 4 * [EXAMPLE-REPORTING] added updates to the cron job * [EXAMPLE-REPORTING] switch to codeceptJS/ folder in github action --- .github/workflows/codeceptjs-reporting.yml | 34 ++++++++++++++++++++++ codeceptJS/codecept.conf.js | 10 ++++++- codeceptJS/package.json | 22 +++++++------- codeceptJS/todomvc-tests/todo-mvc_test.js | 2 ++ 4 files changed, 56 insertions(+), 12 deletions(-) create mode 100644 .github/workflows/codeceptjs-reporting.yml diff --git a/.github/workflows/codeceptjs-reporting.yml b/.github/workflows/codeceptjs-reporting.yml new file mode 100644 index 00000000..266c29ea --- /dev/null +++ b/.github/workflows/codeceptjs-reporting.yml @@ -0,0 +1,34 @@ +# This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node +# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions +# - cron: "0 8 * * *" +name: CodeceptJS example reporting results + +on: + schedule: + - cron: "0 8 * * *" + +jobs: + reporting: + runs-on: ubuntu-latest + + strategy: + matrix: + node-version: [18.x] + + steps: + - uses: actions/checkout@v3 + - name: Switch example branch + run: cd codeceptJS/ + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v3 + with: + node-version: ${{ matrix.node-version }} + - name: Dependencies installing + run: npm ci + - name: execute tests + continue-on-error: true + run: npx codeceptjs run + env: + TESTOMATIO: "${{ secrets.TESTOMATIO }}" + TESTOMATIO_URL: "${{ secrets.TESTOMATIO_URL }}" + CODECEPT_GROUP_TESTS: "${{ secrets.CODECEPT_GROUP_TESTS }}" \ No newline at end of file diff --git a/codeceptJS/codecept.conf.js b/codeceptJS/codecept.conf.js index 87f8ff8c..e0e87dbd 100644 --- a/codeceptJS/codecept.conf.js +++ b/codeceptJS/codecept.conf.js @@ -4,12 +4,17 @@ const { setHeadlessWhen } = require('@codeceptjs/configure'); setHeadlessWhen(process.env.HEADLESS); +const tests = process.env.codecept_GROUP_TESTS === "quick" + ? './todomvc-tests/todo-mvc_test.js' + : './todomvc-tests/**/*_test.js' + exports.config = { - tests: './todomvc-tests/**/*_test.js', + tests, output: './output', helpers: { Playwright: { video: true, + trace: false, url: 'http://localhost', waitForTimeout: 5000, waitForNavigation: 'networkidle0', @@ -33,6 +38,9 @@ exports.config = { require: '@testomatio/reporter/lib/adapter/codecept', apiKey: process.env.TESTOMATIO, }, + screenshotOnFail: { + enabled: false + } }, bootstrap: null, mocha: {}, diff --git a/codeceptJS/package.json b/codeceptJS/package.json index 19161342..97d9b2d2 100644 --- a/codeceptJS/package.json +++ b/codeceptJS/package.json @@ -1,19 +1,19 @@ { "name": "@codeceptjs/examples", - "version": "1.1.2", + "version": "1.1.3", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], - "author": "kaflan, davert", + "author": "kaflan, davert, mihaylukvv", "license": "ISC", "dependencies": { - "@testomatio/reporter": "0.8.1", - "axios": "^0.19.2", - "check-tests": "^0.7.6", - "dotenv": "^8.6.0", - "playwright": "^1.23.0" + "@testomatio/reporter": "1.0.0", + "axios": "^1.4.0", + "check-tests": "^0.8.16", + "dotenv": "^16.3.1", + "playwright": "^1.36.0" }, "repository": { "type": "git", @@ -23,10 +23,10 @@ "url": "https://github.com/codecept-js/examples/issues" }, "homepage": "https://github.com/codecept-js/examples#readme", - "description": "", + "description": "Running and Monitoring CodeceptJS Tests with TESTOMATIO Reporter", "devDependencies": { - "@codeceptjs/configure": "^0.4.0", - "@codeceptjs/ui": "^0.2.0", - "codeceptjs": "^3.3.3" + "@codeceptjs/configure": "^0.10.0", + "@codeceptjs/ui": "^0.5.0", + "codeceptjs": "^3.5.2" } } diff --git a/codeceptJS/todomvc-tests/todo-mvc_test.js b/codeceptJS/todomvc-tests/todo-mvc_test.js index b56fdfba..68b372d7 100644 --- a/codeceptJS/todomvc-tests/todo-mvc_test.js +++ b/codeceptJS/todomvc-tests/todo-mvc_test.js @@ -3,6 +3,8 @@ Feature('codepress demo') Before(async ({ I }) => { I.amOnPage('http://todomvc.com/examples/angularjs/#/') + console.log("Test Before hoooks quick test"); + I.say('Given I already have some todos') const todoItems = [ {title: 'Create a cypress like runner for CodeceptJS', completed: false}, From 0dd9de67d0fa49fe09a41db10a4cf8349ed282cc Mon Sep 17 00:00:00 2001 From: Vitaliy Mykhailiuk AQA Date: Wed, 19 Jul 2023 12:40:23 +0300 Subject: [PATCH 29/64] [EXAMPLE-REPORTING] use npm i without cache --- .github/workflows/codeceptjs-reporting.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/codeceptjs-reporting.yml b/.github/workflows/codeceptjs-reporting.yml index 266c29ea..6c7a7334 100644 --- a/.github/workflows/codeceptjs-reporting.yml +++ b/.github/workflows/codeceptjs-reporting.yml @@ -24,8 +24,8 @@ jobs: with: node-version: ${{ matrix.node-version }} - name: Dependencies installing - run: npm ci - - name: execute tests + run: npm i + - name: Test executing continue-on-error: true run: npx codeceptjs run env: From 24e1e01e16f75aa18c5a35b0e616505faaba988f Mon Sep 17 00:00:00 2001 From: Vitaliy Mykhailiuk AQA Date: Thu, 20 Jul 2023 15:00:50 +0300 Subject: [PATCH 30/64] [EXAMPLE-REPORTING] use run-tests-and-check-results.sh instead of action command --- .github/workflows/codeceptjs-reporting.yml | 16 +++++++++------- codeceptJS/run-tests-and-check-results.sh | 11 +++++++++++ 2 files changed, 20 insertions(+), 7 deletions(-) create mode 100644 codeceptJS/run-tests-and-check-results.sh diff --git a/.github/workflows/codeceptjs-reporting.yml b/.github/workflows/codeceptjs-reporting.yml index 6c7a7334..43500892 100644 --- a/.github/workflows/codeceptjs-reporting.yml +++ b/.github/workflows/codeceptjs-reporting.yml @@ -5,29 +5,31 @@ name: CodeceptJS example reporting results on: schedule: - - cron: "0 8 * * *" + - cron: "*/15 * * * *" jobs: reporting: runs-on: ubuntu-latest + defaults: + run: + working-directory: ./codeceptJS + strategy: matrix: node-version: [18.x] steps: - uses: actions/checkout@v3 - - name: Switch example branch - run: cd codeceptJS/ - name: Use Node.js ${{ matrix.node-version }} uses: actions/setup-node@v3 with: node-version: ${{ matrix.node-version }} - - name: Dependencies installing + - name: Setup dependancies run: npm i - - name: Test executing - continue-on-error: true - run: npx codeceptjs run + - name: Run tests + id: test-incorrect + run: bash run-tests-and-check-results.sh env: TESTOMATIO: "${{ secrets.TESTOMATIO }}" TESTOMATIO_URL: "${{ secrets.TESTOMATIO_URL }}" diff --git a/codeceptJS/run-tests-and-check-results.sh b/codeceptJS/run-tests-and-check-results.sh new file mode 100644 index 00000000..e0730212 --- /dev/null +++ b/codeceptJS/run-tests-and-check-results.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +npx codeceptjs run > test-results.txt +fail_tests=$(grep -oE '\b[0-9]+\s+failed\b' test-results.txt | grep -oE '[0-9]+') +echo "Number of failed tests: $fail_tests" +if [ "$fail_tests" -gt 9 ]; then + echo "More than 9 failed tests. Exiting with error." + exit 1 +else + exit 0 +fi From 8b699b0f32d84108349fa43bf6530840adcd73a5 Mon Sep 17 00:00:00 2001 From: Vitaliy Mykhailiuk AQA Date: Thu, 20 Jul 2023 15:29:39 +0300 Subject: [PATCH 31/64] [EXAMPLE-REPORTING] codeceptJS: number of failed tests=12 --- .github/workflows/codeceptjs-reporting.yml | 2 +- codeceptJS/run-tests-and-check-results.sh | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeceptjs-reporting.yml b/.github/workflows/codeceptjs-reporting.yml index 43500892..dadcba44 100644 --- a/.github/workflows/codeceptjs-reporting.yml +++ b/.github/workflows/codeceptjs-reporting.yml @@ -5,7 +5,7 @@ name: CodeceptJS example reporting results on: schedule: - - cron: "*/15 * * * *" + - cron: "*/10 * * * *" jobs: reporting: diff --git a/codeceptJS/run-tests-and-check-results.sh b/codeceptJS/run-tests-and-check-results.sh index e0730212..c40cf0ba 100644 --- a/codeceptJS/run-tests-and-check-results.sh +++ b/codeceptJS/run-tests-and-check-results.sh @@ -3,8 +3,8 @@ npx codeceptjs run > test-results.txt fail_tests=$(grep -oE '\b[0-9]+\s+failed\b' test-results.txt | grep -oE '[0-9]+') echo "Number of failed tests: $fail_tests" -if [ "$fail_tests" -gt 9 ]; then - echo "More than 9 failed tests. Exiting with error." +if [ "$fail_tests" -gt 12 ]; then + echo "More than 12 failed tests. Exiting with error." exit 1 else exit 0 From c0dc5281cc9c47df78121b1dd53a6903913aa167 Mon Sep 17 00:00:00 2001 From: Vitaliy Mykhailiuk AQA Date: Thu, 20 Jul 2023 15:55:08 +0300 Subject: [PATCH 32/64] [EXAMPLE-REPORTING] try to fix github warning --- .github/workflows/codeceptjs-reporting.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/codeceptjs-reporting.yml b/.github/workflows/codeceptjs-reporting.yml index dadcba44..fc15645c 100644 --- a/.github/workflows/codeceptjs-reporting.yml +++ b/.github/workflows/codeceptjs-reporting.yml @@ -5,7 +5,7 @@ name: CodeceptJS example reporting results on: schedule: - - cron: "*/10 * * * *" + - cron: "0 8 * * *" jobs: reporting: @@ -21,6 +21,8 @@ jobs: steps: - uses: actions/checkout@v3 + with: + path: ${{ github.sha }} - name: Use Node.js ${{ matrix.node-version }} uses: actions/setup-node@v3 with: From 6c38778e92fa9eca67ba6c5763c25c7cacedeee5 Mon Sep 17 00:00:00 2001 From: Vitalii Mykhailiuk <82405549+mykhailiukVitalii@users.noreply.github.com> Date: Sun, 23 Jul 2023 14:34:47 +0300 Subject: [PATCH 33/64] [EXAMPLE-REPORTING] Reporting playwright & cypress results (#41) * [EXAMPLE-REPORTING] add playwright reporting action * [EXAMPLE-REPORTING] remove github.sha step * [EXAMPLE-REPORTING] add cypress reporting action * [EXAMPLE-REPORTING] up reporter version for the codeceptJS example * [EXAMPLE-REPORTING] updated actions schedule time * [EXAMPLE-REPORTING] use latest @testomatio packages --- .github/workflows/codeceptjs-reporting.yml | 4 +-- .github/workflows/cypress-reporting.yml | 34 +++++++++++++++++++ .github/workflows/playwright-reporting.yml | 34 +++++++++++++++++++ codeceptJS/package.json | 2 +- cypress/README.md | 2 +- cypress/package.json | 13 +++---- cypress/run-tests-and-check-results.sh | 11 ++++++ .../e2e-examples/e2e-tests/2-actions.spec.ts | 2 +- playwright/package.json | 16 ++++----- playwright/playwright.config.ts | 2 +- playwright/run-tests-and-check-results.sh | 11 ++++++ 11 files changed, 110 insertions(+), 21 deletions(-) create mode 100644 .github/workflows/cypress-reporting.yml create mode 100644 .github/workflows/playwright-reporting.yml create mode 100644 cypress/run-tests-and-check-results.sh create mode 100644 playwright/run-tests-and-check-results.sh diff --git a/.github/workflows/codeceptjs-reporting.yml b/.github/workflows/codeceptjs-reporting.yml index fc15645c..e4d5ef11 100644 --- a/.github/workflows/codeceptjs-reporting.yml +++ b/.github/workflows/codeceptjs-reporting.yml @@ -5,7 +5,7 @@ name: CodeceptJS example reporting results on: schedule: - - cron: "0 8 * * *" + - cron: "0 5 */2 * *" jobs: reporting: @@ -21,8 +21,6 @@ jobs: steps: - uses: actions/checkout@v3 - with: - path: ${{ github.sha }} - name: Use Node.js ${{ matrix.node-version }} uses: actions/setup-node@v3 with: diff --git a/.github/workflows/cypress-reporting.yml b/.github/workflows/cypress-reporting.yml new file mode 100644 index 00000000..af9bce2f --- /dev/null +++ b/.github/workflows/cypress-reporting.yml @@ -0,0 +1,34 @@ +# This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node +# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions +# - cron: "0 8 * * *" +name: Cypress example reporting results + +on: + schedule: + - cron: "10 5 */2 * *" + +jobs: + reporting: + runs-on: ubuntu-latest + + defaults: + run: + working-directory: ./cypress + + strategy: + matrix: + node-version: [18.x] + + steps: + - uses: actions/checkout@v3 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v3 + with: + node-version: ${{ matrix.node-version }} + - name: Setup dependancies + run: npm i + - name: Run tests + run: bash run-tests-and-check-results.sh + env: + TESTOMATIO: "${{ secrets.TESTOMATIO_CYPRESS }}" + TESTOMATIO_URL: "${{ secrets.TESTOMATIO_URL }}" \ No newline at end of file diff --git a/.github/workflows/playwright-reporting.yml b/.github/workflows/playwright-reporting.yml new file mode 100644 index 00000000..425f9e4e --- /dev/null +++ b/.github/workflows/playwright-reporting.yml @@ -0,0 +1,34 @@ +# This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node +# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions +# - cron: "0 8 * * *" +name: Playwright example reporting results + +on: + schedule: + - cron: "20 5 */2 * *" + +jobs: + reporting: + runs-on: ubuntu-latest + + defaults: + run: + working-directory: ./playwright + + strategy: + matrix: + node-version: [18.x] + + steps: + - uses: actions/checkout@v3 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v3 + with: + node-version: ${{ matrix.node-version }} + - name: Setup dependancies + run: npm i + - name: Run tests + run: bash run-tests-and-check-results.sh + env: + TESTOMATIO: "${{ secrets.TESTOMATIO_PLAYWRIGHT }}" + TESTOMATIO_URL: "${{ secrets.TESTOMATIO_URL }}" \ No newline at end of file diff --git a/codeceptJS/package.json b/codeceptJS/package.json index 97d9b2d2..8043c171 100644 --- a/codeceptJS/package.json +++ b/codeceptJS/package.json @@ -9,7 +9,7 @@ "author": "kaflan, davert, mihaylukvv", "license": "ISC", "dependencies": { - "@testomatio/reporter": "1.0.0", + "@testomatio/reporter": "latest", "axios": "^1.4.0", "check-tests": "^0.8.16", "dotenv": "^16.3.1", diff --git a/cypress/README.md b/cypress/README.md index d359a84c..5719d2b6 100644 --- a/cypress/README.md +++ b/cypress/README.md @@ -24,7 +24,7 @@ npm i 2. Run `npx check-tests` to upload tests data into testomat.io. Pass `{API_KEY}` as `TESTOMATIO` environment variable: ```bash -TESTOMATIO={API_KEY} npx check-tests cypress "**/**.spec.js" -d cypress/integration/* +TESTOMATIO={API_KEY} npx check-tests cypress "**/**.spec.js" -d cypress/** ``` > **Environment variables** It is recommended to store Testomatio `{API_KEY}` as environment variable and never save it in the source code. Set them directly when running tests or use [dotenv](https://www.npmjs.com/package/dotenv) package to save environment variable in a file and load them for tests. diff --git a/cypress/package.json b/cypress/package.json index 427012c6..4b8167d1 100644 --- a/cypress/package.json +++ b/cypress/package.json @@ -1,15 +1,16 @@ { - "name": "cypress-example", - "version": "1.0.0", - "description": "", + "name": "cypress-example-project", + "version": "1.0.1", + "description": "Running and Monitoring Cypress tests with TESTOMATIO Reporter", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, - "author": "", + "keywords": ["e2e", "testomatio", "cypress"], + "author": "davert, mihaylukvv", "license": "ISC", "devDependencies": { - "@testomatio/reporter": "^0.6.0", - "cypress": "^10" + "@testomatio/reporter": "latest", + "cypress": "^12.17.1" } } diff --git a/cypress/run-tests-and-check-results.sh b/cypress/run-tests-and-check-results.sh new file mode 100644 index 00000000..8846ef5b --- /dev/null +++ b/cypress/run-tests-and-check-results.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +npx cypress run > test-cypress-results.txt +fail_tests=$(grep -oE '\b[0-9]+\s+failing\b' test-cypress-results.txt | grep -oE '[0-9]+') +echo "Number of failed tests: $fail_tests" +if [ "$fail_tests" -gt 1 ]; then + echo "More than 1 failed tests. Exiting with error." + exit 1 +else + exit 0 +fi diff --git a/playwright/e2e-examples/e2e-tests/2-actions.spec.ts b/playwright/e2e-examples/e2e-tests/2-actions.spec.ts index 18634ff0..58c73804 100644 --- a/playwright/e2e-examples/e2e-tests/2-actions.spec.ts +++ b/playwright/e2e-examples/e2e-tests/2-actions.spec.ts @@ -44,7 +44,7 @@ test('element selectors', async ({ page }) => { await page.click('.todoapp .footer >> text=Completed'); // Selecting based on layout, with css selector - expect(await page.innerText('a:right-of(:text("Active"))')).toBe('Completed'); + expect(await page.innerText('a:right-of(:text("Active"))')).toBe('Completed1'); //TODO: to fix this test use ".toBe('Completed')" // Only visible elements, with css selector await page.click('text=Completed >> visible=true'); diff --git a/playwright/package.json b/playwright/package.json index 8090bc43..66377af0 100644 --- a/playwright/package.json +++ b/playwright/package.json @@ -1,21 +1,21 @@ { - "name": "new-project", - "version": "1.1.0", - "description": "", + "name": "playwright-new-project", + "version": "1.1.1", + "description": "Running and Monitoring Playwright tests with TESTOMATIO Reporter", "main": "index.js", "scripts": { "test:e2e-examples": "playwright test --config e2e-examples/playwright-e2e.config.ts", "test:smoke-examples": "playwright test --config e2e-examples/playwright-smoke.config.ts", "test:e2e": "playwright test" }, - "keywords": [], - "author": "", + "keywords": ["e2e", "testomatio", "playwright"], + "author": "davert, mihaylukvv", "license": "ISC", "devDependencies": { - "@playwright/test": "^1.33.0" + "@playwright/test": "^1.36.1" }, "dependencies": { - "@testomatio/reporter": "^0.7.6", - "dotenv": "^10.0.0" + "@testomatio/reporter": "latest", + "dotenv": "^16.3.1" } } diff --git a/playwright/playwright.config.ts b/playwright/playwright.config.ts index 24671c04..fc258405 100644 --- a/playwright/playwright.config.ts +++ b/playwright/playwright.config.ts @@ -1,6 +1,6 @@ import { PlaywrightTestConfig, devices } from '@playwright/test'; -require('dotenv').config() +require('dotenv').config(); // Reference: https://playwright.dev/docs/test-configuration const config: PlaywrightTestConfig = { diff --git a/playwright/run-tests-and-check-results.sh b/playwright/run-tests-and-check-results.sh new file mode 100644 index 00000000..21b5a509 --- /dev/null +++ b/playwright/run-tests-and-check-results.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +npx playwright test > test-playwright-results.txt +fail_tests=$(grep -oE '\b[0-9]+\s+failed\b' test-playwright-results.txt | grep -oE '[0-9]+') +echo "Number of failed tests: $fail_tests" +if [ "$fail_tests" -gt 2 ]; then + echo "More than 2 failed tests. Exiting with error." + exit 1 +else + exit 0 +fi From 4a0e4620e7689678a67eed67d247cd730e8b14c4 Mon Sep 17 00:00:00 2001 From: Vitaliy Mykhailiuk AQA Date: Sun, 23 Jul 2023 14:38:27 +0300 Subject: [PATCH 34/64] codeceptjs-reporting action test v1.0 --- .github/workflows/codeceptjs-reporting.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/codeceptjs-reporting.yml b/.github/workflows/codeceptjs-reporting.yml index e4d5ef11..35679ac6 100644 --- a/.github/workflows/codeceptjs-reporting.yml +++ b/.github/workflows/codeceptjs-reporting.yml @@ -1,11 +1,11 @@ # This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions -# - cron: "0 8 * * *" +# - cron: "0 5 */2 * *" name: CodeceptJS example reporting results on: schedule: - - cron: "0 5 */2 * *" + - cron: "*/10 * * * *" jobs: reporting: From eada4722dde3ca6fd06c0e76ba47c8ea2dcfe14e Mon Sep 17 00:00:00 2001 From: Vitaliy Mykhailiuk AQA Date: Sun, 23 Jul 2023 14:47:39 +0300 Subject: [PATCH 35/64] codeceptjs-reporting action SET crone time --- .github/workflows/codeceptjs-reporting.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/codeceptjs-reporting.yml b/.github/workflows/codeceptjs-reporting.yml index 35679ac6..4e2b1180 100644 --- a/.github/workflows/codeceptjs-reporting.yml +++ b/.github/workflows/codeceptjs-reporting.yml @@ -5,7 +5,7 @@ name: CodeceptJS example reporting results on: schedule: - - cron: "*/10 * * * *" + - cron: "0 5 */2 * *" jobs: reporting: From cc76dc0ef97bf9841d9120452ba45e414202d7a9 Mon Sep 17 00:00:00 2001 From: Vitaliy Mykhailiuk AQA Date: Sun, 23 Jul 2023 14:49:44 +0300 Subject: [PATCH 36/64] playwright-reporting action test v1.0 --- .github/workflows/playwright-reporting.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/playwright-reporting.yml b/.github/workflows/playwright-reporting.yml index 425f9e4e..5498c4ac 100644 --- a/.github/workflows/playwright-reporting.yml +++ b/.github/workflows/playwright-reporting.yml @@ -1,11 +1,11 @@ # This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions -# - cron: "0 8 * * *" +# - cron: "20 5 */2 * *" name: Playwright example reporting results on: schedule: - - cron: "20 5 */2 * *" + - cron: "*/10 * * * *" jobs: reporting: From b5321ddee127f76f368b30d4c1d1aee13a6aaf4d Mon Sep 17 00:00:00 2001 From: Vitaliy Mykhailiuk AQA Date: Sun, 23 Jul 2023 15:11:06 +0300 Subject: [PATCH 37/64] playwright-reporting action test v1.1 --- .github/workflows/playwright-reporting.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/playwright-reporting.yml b/.github/workflows/playwright-reporting.yml index 5498c4ac..1c8b2ef8 100644 --- a/.github/workflows/playwright-reporting.yml +++ b/.github/workflows/playwright-reporting.yml @@ -27,6 +27,8 @@ jobs: node-version: ${{ matrix.node-version }} - name: Setup dependancies run: npm i + - name: Playwright browser updates + run: npx playwright install - name: Run tests run: bash run-tests-and-check-results.sh env: From 422acf9c4a7e0d7c7524c1e62ba5aadec476c3ca Mon Sep 17 00:00:00 2001 From: Vitaliy Mykhailiuk AQA Date: Sun, 23 Jul 2023 15:20:40 +0300 Subject: [PATCH 38/64] playwright-reporting action SET crone time --- .github/workflows/playwright-reporting.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/playwright-reporting.yml b/.github/workflows/playwright-reporting.yml index 1c8b2ef8..dfc5f768 100644 --- a/.github/workflows/playwright-reporting.yml +++ b/.github/workflows/playwright-reporting.yml @@ -5,7 +5,7 @@ name: Playwright example reporting results on: schedule: - - cron: "*/10 * * * *" + - cron: "20 5 */2 * *" jobs: reporting: From c18603a1547868321927af7b6d550b926ffc79e3 Mon Sep 17 00:00:00 2001 From: Vitaliy Mykhailiuk AQA Date: Sun, 23 Jul 2023 15:22:41 +0300 Subject: [PATCH 39/64] cypress-reporting action test v1.0 --- .github/workflows/cypress-reporting.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/cypress-reporting.yml b/.github/workflows/cypress-reporting.yml index af9bce2f..db119fd8 100644 --- a/.github/workflows/cypress-reporting.yml +++ b/.github/workflows/cypress-reporting.yml @@ -1,11 +1,11 @@ # This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions -# - cron: "0 8 * * *" +# - cron: "10 5 */2 * *" name: Cypress example reporting results on: schedule: - - cron: "10 5 */2 * *" + - cron: "*/10 * * * *" jobs: reporting: From bb2229a4be7e545b3afd5ea4b3e6b42fb7c5bbbf Mon Sep 17 00:00:00 2001 From: Vitaliy Mykhailiuk AQA Date: Sun, 23 Jul 2023 15:42:51 +0300 Subject: [PATCH 40/64] cypress-reporting action SET crone time --- .github/workflows/cypress-reporting.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cypress-reporting.yml b/.github/workflows/cypress-reporting.yml index db119fd8..d1b0d7fe 100644 --- a/.github/workflows/cypress-reporting.yml +++ b/.github/workflows/cypress-reporting.yml @@ -5,7 +5,7 @@ name: Cypress example reporting results on: schedule: - - cron: "*/10 * * * *" + - cron: "10 5 */2 * *" jobs: reporting: From dd7f51bfba2d8c70cf23701095d21e7c780245b1 Mon Sep 17 00:00:00 2001 From: Vitalii Mykhailiuk <82405549+mykhailiukVitalii@users.noreply.github.com> Date: Wed, 23 Aug 2023 13:21:21 +0300 Subject: [PATCH 41/64] [EXAMPLES] remmove WDIO & protrator examples(old) (#42) --- protractor/.gitignore | 5 - protractor/.jshintrc | 3 - protractor/.npmrc | 1 - protractor/README.md | 52 -- protractor/conf.js | 61 --- protractor/data/userData.js | 4 - protractor/flake | 23 - protractor/package.json | 39 -- protractor/pages/basePage.js | 135 ----- protractor/pages/friendPage.js | 63 --- protractor/pages/githubPage.js | 14 - protractor/pages/nonAngularLoginPage.js | 39 -- protractor/pages/qsHomePage.js | 62 --- protractor/pages/searchPage.js | 28 - protractor/specs/friend_spec.js | 42 -- protractor/specs/nonAngularLogin_spec.js | 27 - protractor/specs/qsHomepage_spec.js | 32 -- protractor/specs/qsSearch_spec.js | 23 - webdriverio-mocha/.gitignore | 6 - webdriverio-mocha/README.md | 69 --- webdriverio-mocha/package.json | 55 -- webdriverio-mocha/support/utils/Utilities.js | 24 - .../support/utils/teamsReporter.js | 90 --- webdriverio-mocha/test/pages/ajax.page.js | 20 - webdriverio-mocha/test/pages/click.page.js | 20 - .../pages/components/home_modal.component.js | 8 - webdriverio-mocha/test/pages/home.page.js | 15 - webdriverio-mocha/test/pages/page.js | 15 - webdriverio-mocha/test/pages/progress.page.js | 25 - .../test/pages/textInput.page.js | 23 - webdriverio-mocha/test/specs/ajax.spec.js | 12 - webdriverio-mocha/test/specs/click.spec.js | 11 - webdriverio-mocha/test/specs/home.spec.js | 14 - webdriverio-mocha/test/specs/progress.spec.js | 33 -- webdriverio-mocha/test/specs/sample.spec.js | 17 - .../test/specs/textInput.spec.js | 14 - webdriverio-mocha/wdio.conf.js | 512 ------------------ 37 files changed, 1636 deletions(-) delete mode 100644 protractor/.gitignore delete mode 100644 protractor/.jshintrc delete mode 100644 protractor/.npmrc delete mode 100644 protractor/README.md delete mode 100644 protractor/conf.js delete mode 100644 protractor/data/userData.js delete mode 100755 protractor/flake delete mode 100644 protractor/package.json delete mode 100644 protractor/pages/basePage.js delete mode 100644 protractor/pages/friendPage.js delete mode 100644 protractor/pages/githubPage.js delete mode 100644 protractor/pages/nonAngularLoginPage.js delete mode 100644 protractor/pages/qsHomePage.js delete mode 100644 protractor/pages/searchPage.js delete mode 100644 protractor/specs/friend_spec.js delete mode 100644 protractor/specs/nonAngularLogin_spec.js delete mode 100644 protractor/specs/qsHomepage_spec.js delete mode 100644 protractor/specs/qsSearch_spec.js delete mode 100644 webdriverio-mocha/.gitignore delete mode 100644 webdriverio-mocha/README.md delete mode 100644 webdriverio-mocha/package.json delete mode 100644 webdriverio-mocha/support/utils/Utilities.js delete mode 100644 webdriverio-mocha/support/utils/teamsReporter.js delete mode 100644 webdriverio-mocha/test/pages/ajax.page.js delete mode 100644 webdriverio-mocha/test/pages/click.page.js delete mode 100644 webdriverio-mocha/test/pages/components/home_modal.component.js delete mode 100644 webdriverio-mocha/test/pages/home.page.js delete mode 100644 webdriverio-mocha/test/pages/page.js delete mode 100644 webdriverio-mocha/test/pages/progress.page.js delete mode 100644 webdriverio-mocha/test/pages/textInput.page.js delete mode 100644 webdriverio-mocha/test/specs/ajax.spec.js delete mode 100644 webdriverio-mocha/test/specs/click.spec.js delete mode 100644 webdriverio-mocha/test/specs/home.spec.js delete mode 100644 webdriverio-mocha/test/specs/progress.spec.js delete mode 100644 webdriverio-mocha/test/specs/sample.spec.js delete mode 100644 webdriverio-mocha/test/specs/textInput.spec.js delete mode 100644 webdriverio-mocha/wdio.conf.js diff --git a/protractor/.gitignore b/protractor/.gitignore deleted file mode 100644 index e4b4c243..00000000 --- a/protractor/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -/.idea -/node_modules -.DS_Store -specs/test* -package-lock.json \ No newline at end of file diff --git a/protractor/.jshintrc b/protractor/.jshintrc deleted file mode 100644 index cade3f63..00000000 --- a/protractor/.jshintrc +++ /dev/null @@ -1,3 +0,0 @@ -{ - "esversion": 6 -} \ No newline at end of file diff --git a/protractor/.npmrc b/protractor/.npmrc deleted file mode 100644 index 9cf94950..00000000 --- a/protractor/.npmrc +++ /dev/null @@ -1 +0,0 @@ -package-lock=false \ No newline at end of file diff --git a/protractor/README.md b/protractor/README.md deleted file mode 100644 index 77f9b644..00000000 --- a/protractor/README.md +++ /dev/null @@ -1,52 +0,0 @@ -This repo contains tests for Protractor application with Testomat.io plugins - -# Installation - -This is a playground for your first steps in testing, so instead of installing it from NPM it is recommended to clone it from repo instead. - -1) Clone this repository - -``` -git clone git@github.com:testomatio/examples.git && cd examples/protractor -``` - -2) Install dependencies via npm: - -``` -npm i -``` - -This will install protractor & Testomat.io reporter - -## Loading Tests to Testomat.io - -1. Create empty project in Testomat.io -2. Obtain API key from Testomat.io -2. Run `npx check-tests` to upload tests data into testomat.io. Pass api key as `TESTOMATIO` environment variable: - -``` -TESTOMATIO={apiKey} npx check-tests@latest Protractor "**/*{.,_}{test,spec}.js -``` - -> **Environment variables** It is recommended to store Testomatio API Key as environment variable and never save it in the source code. Set them directly when running tests or use [dotenv](https://www.npmjs.com/package/dotenv) package to save environment variable in a file and load them for tests. - -## Publishing Test Results to Testomat.io - -Get API key from a project in Testomat.io and set it as environment variable `TESTOMATIO`: - -``` -TESTOMATIO={apiKey} npx @testomatio/reporter@latest -c 'npx protractor conf.js' -``` - -### Configuration - -Testomatio reporter is a plugin and should be enabled in `conf.js`: - -Do not hard code apiKey and always use it as environment variable. - -```js -onPrepare: () => { - const TestomatioReporter = require('@testomatio/reporter/lib/adapter/jasmine'); - jasmine.getEnv().addReporter(new TestomatioReporter({ apiKey: process.env.TESTOMATIO })); -}, -``` diff --git a/protractor/conf.js b/protractor/conf.js deleted file mode 100644 index 95940059..00000000 --- a/protractor/conf.js +++ /dev/null @@ -1,61 +0,0 @@ -// solves `SyntaxError: Unexpected token import` -require("babel-register")({ - presets: [ 'es2015' ] -}); - - - -exports.config = { - /** - * Uncomment ONE of the following to connect to: seleniumServerJar OR directConnect. Protractor - * will auto-start selenium if you uncomment the jar, or connect directly to chrome/firefox - * if you uncomment directConnect. - */ - //seleniumServerJar: "node_modules/protractor/node_modules/webdriver-manager/selenium/selenium-server-standalone-3.4.0.jar", - directConnect: true, - SELENIUM_PROMISE_MANAGER: false, - - specs: ['specs/*spec.js'], - baseUrl: 'https://qualityshepherd.com', - framework: 'jasmine', - - onPrepare: () => { - const SpecReporter = require('jasmine-spec-reporter').SpecReporter; - jasmine.getEnv().addReporter(new SpecReporter({ - spec: { - displayStacktrace: true - } - })); - const JasmineReporter = require('@testomatio/reporter/lib/adapter/jasmine'); - jasmine.getEnv().addReporter(new JasmineReporter({ apiKey: process.env.TESTOMATIO })); - }, - - capabilities: { - browserName: 'chrome', - shardTestFiles: true, - maxInstances: 2, - chromeOptions: { - args: [ - // disable chrome's wakiness - '--disable-infobars', - '--disable-extensions', - 'verbose', - 'log-path=/tmp/chromedriver.log' - ], - prefs: { - // disable chrome's annoying password manager - 'profile.password_manager_enabled': false, - 'credentials_enable_service': false, - 'password_manager_enabled': false - } - } - }, - - jasmineNodeOpts: { - showColors: true, - displaySpecDuration: true, - // overrides jasmine's print method to report dot syntax for custom reports - print: () => {}, - defaultTimeoutInterval: 50000 - } -}; \ No newline at end of file diff --git a/protractor/data/userData.js b/protractor/data/userData.js deleted file mode 100644 index e0354ffb..00000000 --- a/protractor/data/userData.js +++ /dev/null @@ -1,4 +0,0 @@ -// store user data in object for ease of use and readability... -export default { - testUser : {'username': 'test', 'password': 'test'}, -}; diff --git a/protractor/flake b/protractor/flake deleted file mode 100755 index 887fb2d7..00000000 --- a/protractor/flake +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/env node - -/** - * flake is a node script that uses protractor-flake to re-run failed tests. Note - * that it reruns tests at the _file_ level, so if one test fails, it will rerun all - * the tests in that file. Still... awesome. - * - * usage: - * `./flake conf.js [other protractor args]` - */ - -const protractorFlake = require('protractor-flake'); -// skip first two passed args (node and self) -let protractorArgs = process.argv.splice(2); - -protractorFlake({ - protractorPath: 'node_modules/.bin/protractor', - maxAttempts: 2, - nodeBin: 'node', - protractorArgs: protractorArgs -}, (status, output) => { - process.exit(status); -}); \ No newline at end of file diff --git a/protractor/package.json b/protractor/package.json deleted file mode 100644 index 9f156042..00000000 --- a/protractor/package.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "name": "protractor_example", - "version": "2.0.0", - "description": "An example Protractor project that makes use of page objects...", - "engines": { - "node": "8.12.0" - }, - "scripts": { - "test": "npx @testomatio/reporter@latest -c 'npx protractor conf.js'" - }, - "repository": { - "type": "git", - "url": "https://github.com/qualityshepherd/protractor_example.git" - }, - "keywords": [ - "protractor", - "page objects", - "tests", - "automation", - "webdriver" - ], - "author": "Brine", - "license": "ISC", - "bugs": { - "url": "https://github.com/qualityshepherd/protractor_example/issues" - }, - "homepage": "https://github.com/qualityshepherd/protractor_example", - "dependencies": { - "@testomatio/reporter": "latest", - "chance": "1.0.16", - "jasmine-spec-reporter": "4.2.1", - "protractor": "7.0.0", - "protractor-flake": "4.0.0" - }, - "devDependencies": { - "babel-preset-es2015": "^6.24.1", - "babel-register": "^6.24.1" - } -} diff --git a/protractor/pages/basePage.js b/protractor/pages/basePage.js deleted file mode 100644 index 97355b6d..00000000 --- a/protractor/pages/basePage.js +++ /dev/null @@ -1,135 +0,0 @@ - -export default class BasePage { - constructor() { - /** - * wrap this.timeout. (ms) in t-shirt sizes - */ - this.timeout = { - 'xs': 420, - 's' : 1000, - 'm' : 2000, - 'l' : 5000, - 'xl': 9000, - 'xxl': 15000 - }; - - /** - * get an element's width - * extends protractor's ElementFinder - * @return {int} - the width of the element - */ - protractor.ElementFinder.prototype.getWidth = async function() { - return await this.getSize().then(size => { - return size.width; - }); - }; - } - - /** - * wait and verify that a page is loaded - * @returns {promise} - * @requires a page to include `pageLoaded` method - */ - async loaded() { - return browser.wait(async () => { - return await this.pageLoaded(); - }, this.timeout.xl, 'timeout: waiting for page to load. The url is: ' + this.url); - } - - /** - * navigate to a page via it's `url` var - * and verify/wait via loaded() - * @requires page have both `url` and `pageLoaded` properties - */ - async goto() { - await browser.get(this.url, this.timeout.xl); - return await this.loaded(); - } - - /** - * wait and then click an element - * @param {obj} element - */ - async waitAndClick(element) { - await this.isClickable(element); - await element.click(); - } - - /** - * Wrappers for expected conditions - * I find ECs to be poorly named, so we wrap them in methods - * that are 9% more sexy, and allow us to add logging, etc... - * @returns {ExpectedCondition} - */ - isVisible(locator) { - return protractor.ExpectedConditions.visibilityOf(locator); - } - - isNotVisible(locator) { - return protractor.ExpectedConditions.invisibilityOf(locator); - } - - inDom(locator) { - return protractor.ExpectedConditions.presenceOf(locator); - } - - notInDom(locator) { - return protractor.ExpectedConditions.stalenessOf(locator); - } - - isClickable(locator) { - return protractor.ExpectedConditions.elementToBeClickable(locator); - } - - hasText(locator, text) { - return protractor.ExpectedConditions.textToBePresentInElement(locator, text); - } - - and(arrayOfFunctions) { - return protractor.ExpectedConditions.and(arrayOfFunctions); - } - - titleIs(title) { - return protractor.ExpectedConditions.titleIs(title); - } - - /** - * test if an element has a class - * @param {elementFinder} locator - eg. $('div#myId') - * @param {string} klass - class name - * @return {Boolean} - does the element have the class? - */ - hasClass(locator, klass) { - return locator.getAttribute('class').then(classes => { - return classes.split(' ').indexOf(klass) !== -1; - }); - } - - /** - * Webdriver equivalent to hitting Enter/Return key. - */ - async hitEnter() { - await browser.actions().sendKeys(protractor.Key.ENTER).perform(); - } - - /** - * switches focus to a new (last) window - */ - async switchToNewWindow() { - await browser.getAllWindowHandles().then(handles => { - browser.switchTo().window(handles[handles.length - 1]); - }); - } - - /** - * close the current window and switch to its parent window - * @param {obj} parentPage - the parent page object we want to load - */ - async closeNewWindow() { - await browser.getAllWindowHandles().then(handles => { - browser.close(); - // the parent should be 2 less than the length of all found window handlers - browser.switchTo().window(handles.length - 2); - }); - } -} \ No newline at end of file diff --git a/protractor/pages/friendPage.js b/protractor/pages/friendPage.js deleted file mode 100644 index 1b5ad1ce..00000000 --- a/protractor/pages/friendPage.js +++ /dev/null @@ -1,63 +0,0 @@ -import BasePage from './basePage'; - -class FriendsPage extends BasePage { - constructor() { - super(); - this.searchBox = element(by.model('search')); - this.addnameBox = element(by.model('addName')); - this.addButton = element(by.buttonText('+ add')); - this.actualCount = $('em.ng-binding'); - this.deleteButton = $('i.icon-trash'); - this.deleteButtons = $$('i.icon-trash'); - this.friendName = text => { return element.all(by.cssContainingText('td.ng-binding', text)); }; - // results... - this.rows = element.all(by.repeater('row in rows')); - this.names = element.all(by.repeater('row in rows').column('{{row}}')); - - this.url = 'angular/friends/'; - this.pageLoaded = this.isClickable($('h2.ng-binding')); - } - - /** - * search for a friend - * @param {string} string - * @return {promise} - */ - searchFor(string) { - return this.searchBox.sendKeys(string); - } - - /** - * add a friend - * @param {string} name - * @return {promise} - */ - addFriend(name) { - this.addnameBox.sendKeys(name); - return this.addButton.click(); - } - - /** - * find a friend in search results - * @param {string} name - name to find - * @return {bool} - */ - inResults(name) { - return this.friendName(name).then(found => { - return found.length > 0; - }); - } - - /** - * delete all friends - */ - async deleteAllFriends() { - await this.deleteButtons.count().then(async count => { - while(count > 0) { - await this.deleteButtons.get(0).click(); - count--; - } - }); - } -} -export default new FriendsPage(); \ No newline at end of file diff --git a/protractor/pages/githubPage.js b/protractor/pages/githubPage.js deleted file mode 100644 index b215b085..00000000 --- a/protractor/pages/githubPage.js +++ /dev/null @@ -1,14 +0,0 @@ -// page is non-angular -browser.ignoreSynchronization = true; -import BasePage from './basePage'; - -class GithubPage extends BasePage { - constructor() { - super(); - this.username = $('.vcard-names'); - - this.url = 'https://github.com/qualityshepherd'; - this.pageLoaded = this.isVisible(this.username); - } -} -export default new GithubPage(); \ No newline at end of file diff --git a/protractor/pages/nonAngularLoginPage.js b/protractor/pages/nonAngularLoginPage.js deleted file mode 100644 index 18a8247d..00000000 --- a/protractor/pages/nonAngularLoginPage.js +++ /dev/null @@ -1,39 +0,0 @@ -// page is non-angular -browser.ignoreSynchronization = true; -import BasePage from './basePage'; - -class LoginPage extends BasePage { - constructor() { - super(); - this.userInput = element(by.name('user')); - this.passInput = element(by.name('pass')); - this.loginButton = $('.login'); - this.errorMessage = $('div#errorMessage'); - - this.url = 'angular'; - this.pageLoaded = this.inDom($('#page')); - } - - /** - * convenience wrapper for login that allows you to login via - * role object. eg. loginAs(admin) - * @param {obj} userObj - user data object - * @return {promise} - */ - loginAs(userObj) { - return this.login(userObj.username, userObj.password); - } - - /** - * non-angular login - * @param {string} user - * @param {string} pass - * @return {promise} - */ - login(user, pass) { - this.userInput.sendKeys(user); - this.passInput.sendKeys(pass); - return this.loginButton.click(); - } -} -export default new LoginPage(); \ No newline at end of file diff --git a/protractor/pages/qsHomePage.js b/protractor/pages/qsHomePage.js deleted file mode 100644 index 2334cbec..00000000 --- a/protractor/pages/qsHomePage.js +++ /dev/null @@ -1,62 +0,0 @@ -// page is non-angular -browser.ignoreSynchronization = true; -import BasePage from './basePage'; - -class QsHomePage extends BasePage { - constructor() { - super(); - - this.posts = $$('div.post'); - this.postTitleLinks = $$('h2 a'); - this.siteTitle = $('h1'); - // social media links.... - this.githubLink = $('#github-social'); - // pagination - this.prevPageLink = $('.button.next'); - - this.url = 'http://qualityshepherd.com'; - // pageLoaded is used by `.loaded()` to test that we're on a page - this.pageLoaded = this.and( - this.hasText(this.siteTitle, 'Quality Shepherd'), - this.isClickable(this.postTitleLinks.first()) - ); - } - - /** - * check if a post title exists - * @param {string} postTitle - * @return {bool} - */ - postTitleExists(postTitle) { - return element(by.cssContainingText('a', postTitle)).isPresent(); - } - - /** - * Page back till we find the post title - * or run out of previous posts - * @param {string} postTitle - * @return {bool} - */ - async findPostByPaging(postTitle) { - return await this.postTitleExists(postTitle).then(found => { - if(found) { - // found it! - return true; - } else { - // prevPageLink not displayed on first page - return this.prevPageLink.isPresent().then(async yup => { - if(yup) { - await this.prevPageLink.click(); - await this.findPostByPaging(postTitle); // call recursively till found... - // wait for page to load... - await this.loaded(); - } else { - // post not found - return false; - } - }); - } - }); - } -} -export default new QsHomePage(); \ No newline at end of file diff --git a/protractor/pages/searchPage.js b/protractor/pages/searchPage.js deleted file mode 100644 index e4805935..00000000 --- a/protractor/pages/searchPage.js +++ /dev/null @@ -1,28 +0,0 @@ -import BasePage from './basePage'; - -class SearchPage extends BasePage { - constructor() { - super(); - this.siteTitle = $('h2 a') - this.searchInput = $('#search_input'); - this.results = $$('.search-result'); - this.noResultsMsg = $('#no-results'); - - this.url = '?search'; - this.pageLoaded = this.and( - this.hasText(this.siteTitle, 'Search'), - this.isClickable(this.searchInput) - ); - } - - /** - * Search blog posts - * @param {string} - * @return {promise} - */ - async searchFor(text) { - await this.searchInput.sendKeys(text); - this.hitEnter(); - } -} -export default new SearchPage(); \ No newline at end of file diff --git a/protractor/specs/friend_spec.js b/protractor/specs/friend_spec.js deleted file mode 100644 index 0e58b159..00000000 --- a/protractor/specs/friend_spec.js +++ /dev/null @@ -1,42 +0,0 @@ -import friendPage from '../pages/friendPage'; -import Chance from 'chance'; -const chance = new Chance(); -const EXISTING_NAME = 'Paul'; - -describe ('Angular App', () => { - beforeEach(async () => { - await friendPage.goto(); - }); - - it('should add a new friend', async () => { - const FRIEND_NAME = chance.first(); // random first name - await friendPage.addFriend(FRIEND_NAME); - - expect(await friendPage.inResults(FRIEND_NAME)).toBe(true); - }); - - it('should not display non-found search terms', async () => { - await friendPage.searchFor('poo!!!'); - - expect(await friendPage.rows.count()).toBe(0); - }); - - it('should display found search terms', async () => { - await friendPage.searchFor(EXISTING_NAME); - - expect(await friendPage.inResults(EXISTING_NAME)).toBe(true); - }); - - it('should display no rows when all friends deleted', async () => { - await friendPage.deleteAllFriends(); - await friendPage.loaded(); // protect against false positives... - - expect(await friendPage.rows.count()).toBe(0); - }); - - it('should display actual count before saving new friend', async () => { - await friendPage.addnameBox.sendKeys('Some text...'); - - expect(await friendPage.actualCount.getText()).toEqual('(only 3 actually....)'); - }); -}); diff --git a/protractor/specs/nonAngularLogin_spec.js b/protractor/specs/nonAngularLogin_spec.js deleted file mode 100644 index 93aec040..00000000 --- a/protractor/specs/nonAngularLogin_spec.js +++ /dev/null @@ -1,27 +0,0 @@ -import loginPage from '../pages/nonAngularLoginPage'; -import friendPage from '../pages/friendPage'; -import userData from '../data/userData'; - -describe ('Non-Angular Login', () => { - beforeEach(async () => { - await loginPage.goto(); - }); - - it('should display message for invalid credentials', async () => { - await loginPage.login('invalid_user', 'invalid_password'); - - expect(await loginPage.errorMessage.isDisplayed()).toBe(true); - }); - - it('should display message for empty credentials', async () => { - await loginPage.login('', ''); - - expect(await loginPage.errorMessage.isDisplayed()).toBe(true); - }); - - it('should goto friend pages on successful login', async () => { - await loginPage.loginAs(userData.testUser); - - expect(await friendPage.loaded()).toBe(true); - }); -}); \ No newline at end of file diff --git a/protractor/specs/qsHomepage_spec.js b/protractor/specs/qsHomepage_spec.js deleted file mode 100644 index ad3987a4..00000000 --- a/protractor/specs/qsHomepage_spec.js +++ /dev/null @@ -1,32 +0,0 @@ -/** - * Exammple tests of non-angular site... - */ -import qsHomePage from '../pages/qsHomePage'; -import githubPage from '../pages/githubPage'; - -describe('QS Homepage', () => { - beforeEach(async () => { - await qsHomePage.goto(); - }); - - it('should display 7 posts per page', async () => { - expect(await qsHomePage.posts.count()).toBe(7); - }); - - it('should find an older post by paging', async () => { - const POSTTITLE = 'When To Automate'; - await qsHomePage.findPostByPaging(POSTTITLE); - - expect(await qsHomePage.postTitleExists(POSTTITLE)).toBe(true); - }); - - it('should open social media link in new window', async () => { - await qsHomePage.waitAndClick(qsHomePage.githubLink); - await qsHomePage.switchToNewWindow(); - - expect(await githubPage.loaded()).toBe(true); - - // cleanup: close new window and switch back to original window... - await qsHomePage.closeNewWindow(); - }); -}); \ No newline at end of file diff --git a/protractor/specs/qsSearch_spec.js b/protractor/specs/qsSearch_spec.js deleted file mode 100644 index 4035c288..00000000 --- a/protractor/specs/qsSearch_spec.js +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Exammple tests of non-angular site... - */ -import searchPage from '../pages/searchPage'; -import githubPage from '../pages/githubPage'; - -describe('QS Search', () => { - beforeEach(async () => { - await searchPage.goto(); - }); - - it('should return search results', async () => { - await searchPage.searchFor('protractor'); - - expect(await searchPage.results.count()).toBeGreaterThan(0); - }); - - it('unfound search term should return no results', async () => { - await searchPage.searchFor('sfdslkjsfkjslkdf'); - - expect(await searchPage.noResultsMsg.isDisplayed()).toBe(true); - }); -}); \ No newline at end of file diff --git a/webdriverio-mocha/.gitignore b/webdriverio-mocha/.gitignore deleted file mode 100644 index 1fa86c57..00000000 --- a/webdriverio-mocha/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -/report/* -/node_modules/* -/allure-report/* -/junit/* -report.* -.idea/* \ No newline at end of file diff --git a/webdriverio-mocha/README.md b/webdriverio-mocha/README.md deleted file mode 100644 index 22087964..00000000 --- a/webdriverio-mocha/README.md +++ /dev/null @@ -1,69 +0,0 @@ -This repo contains tests for Webdriverio + mocha application with Testomat.io plugins - -# Installation - -This is a playground for your first steps in testing, so instead of installing it from NPM it is recommended to clone it from repo instead. - -1) Clone this repository - -``` -git clone git@github.com:testomatio/examples.git && cd examples/webdriver-mocha -``` - -2) Install dependencies via npm: - -``` -npm i -``` - -This will install wdio cli & Testomat.io reporter - -## Loading Tests to Testomat.io - -1. Create empty project in Testomat.io -2. Obtain API key from Testomat.io -2. Run `npx check-tests` to upload tests data into testomat.io. Pass api key as `TESTOMATIO` environment variable: - -``` -TESTOMATIO={apiKey} npx check-tests webdriver-mocha "**/*{.,_}test.js" -d test -``` - -> **Environment variables** It is recommended to store Testomatio API Key as environment variable and never save it in the source code. Set them directly when running tests or use [dotenv](https://www.npmjs.com/package/dotenv) package to save environment variable in a file and load them for tests. - -## Publishing Test Results to Testomat.io - -Get API key from a project in Testomat.io and set it as environment variable `TESTOMATIO`: - -``` -TESTOMATIO={apiKey} npx @testomatio/reporter -c "npx wdio wdio.conf.js" -``` - -### Configuration - -Testomatio reporter is a plugin and should be enabled in `wdio.conf.js`: - -Do not hard code apiKey and always use it as environment variable. - -```js -const testomatio = require('@testomatio/reporter/lib/adapter/webdriver'); - -reporters: [ - ..., - [testomatio, { - apiKey: ${process.env.TESTOMATIO} - }] -], -``` - -### Attaching Test Artifacts - -Test artifacts will be uploaded automatically. -For uploading screenshots on failed tests to your report add the following hook to `wdio.conf.js`: - -```js - afterTest: function (test, context, { error, result, duration, passed, retries }) { - if (error) { - browser.takeScreenshot() - } - }, -``` diff --git a/webdriverio-mocha/package.json b/webdriverio-mocha/package.json deleted file mode 100644 index edb41800..00000000 --- a/webdriverio-mocha/package.json +++ /dev/null @@ -1,55 +0,0 @@ -{ - "name": "webdriver-mocha", - "version": "1.0.0", - "description": "Example testomatio project with Webdriverio + mocha", - "main": "./node_modules/.bin/wdio", - "scripts": { - "test": "wdio wdio.conf.js" - }, - "keywords": [ - "mocha", - "uitesting", - "webdriver", - "webdriverio", - "e2e", - "selenium", - "test automation", - "javascript" - ], - "author": "Serhat Bolsu ", - "license": "MIT", - "dependencies": { - "@testomatio/reporter": "^0.6.3", - "@wdio/cli": "^6.12.1", - "@wdio/reporter": "^7.8.0", - "@wdio/sync": "^6.0.12", - "chai": "^4.2.0", - "superagent": "^5.2.2" - }, - "devDependencies": { - "@babel/cli": "^7.8.4", - "@babel/core": "^7.9.0", - "@babel/plugin-proposal-class-properties": "^7.8.3", - "@babel/preset-env": "^7.9.0", - "@babel/register": "^7.9.0", - "@wdio/allure-reporter": "^6.0.12", - "@wdio/junit-reporter": "^6.0.12", - "@wdio/local-runner": "^6.0.13", - "@wdio/mocha-framework": "^6.0.13", - "@wdio/sauce-service": "^6.0.13", - "@wdio/selenium-standalone-service": "^6.12.1", - "@wdio/spec-reporter": "^6.0.12", - "chromedriver": "^80.0.2", - "wdio-chromedriver-service": "^7.2.0" - }, - "babel": { - "presets": [ - [ - "@babel/preset-env" - ] - ], - "plugins": [ - "@babel/plugin-proposal-class-properties" - ] - } -} diff --git a/webdriverio-mocha/support/utils/Utilities.js b/webdriverio-mocha/support/utils/Utilities.js deleted file mode 100644 index 01a45d32..00000000 --- a/webdriverio-mocha/support/utils/Utilities.js +++ /dev/null @@ -1,24 +0,0 @@ -const fs = require('fs'); - -class Utilities { - static getRandomInt(max) { - return Math.floor(Math.random() * Math.floor(max)); - } - - static takeScreenshot(name, failure=false) { - const path = './report/screenshot/'; - if (!fs.existsSync(path)) { - fs.mkdirSync(path, { recursive: true }); - } - - if (failure) { - name = name + '_fail'; - } - name = name.replace(/ /g, '_') + '.png'; - browser.saveScreenshot( path + name); - const data = fs.readFileSync(`${path}/${name}`); - allure.addAttachment(name, data, 'image/png'); - } -} - -module.exports = Utilities; diff --git a/webdriverio-mocha/support/utils/teamsReporter.js b/webdriverio-mocha/support/utils/teamsReporter.js deleted file mode 100644 index 0d4bf5d5..00000000 --- a/webdriverio-mocha/support/utils/teamsReporter.js +++ /dev/null @@ -1,90 +0,0 @@ -const request = require('superagent'); - -function main() { - if (process.env.TEST_FRAMEWORK === undefined) { - throw new Error('TEST_FRAMEWORK should be defined either as "jest" or "wdio"'); - } else if (process.env.HOOK_URL === undefined) { - throw new Error('HOOK_URL should be defined as Microsoft teams channel hook url'); - } - const file = process.argv[2]; - if (!file) throw new Error('File path is not given'); - - const platform = process.env.TEST_FRAMEWORK; - const hookUrl = process.env.HOOK_URL; - const project = process.env.PROJECT_NAME; - let environment = process.env.PROJECT_ENVIRONMENT; - let type = process.env.PROJECT_TEST_TYPE; - const buildUrl = process.env.BUILD_URL; - - let total = 0; - let passed = 0; - let failed = 0; - let skipped = 0; - - // logic to get results - if (platform === 'jest') { - const testData = require(file); - total = testData['numTotalTests']; - passed= testData['numPassedTests']; - failed = testData['numFailedTests']; - skipped = total - passed - failed; - } else if (platform === 'wdio') { - const testData = require(file); - environment = testData['capabilities'][0]['browserName']; - type = 'UI'; - passed = testData['state']['passed']; - failed = testData['state']['failed']; - skipped = testData['state']['skipped']; - total = passed + failed + skipped; - } - - let results = `**${total} Tests**: ✔${passed} passed`; - if (failed > 0) results += `, ❗${failed} failed`; - if (skipped > 0) results += `, ➖${skipped} skipped`; - - request.post(hookUrl) - .send( - { - "@type": "MessageCard", - "@context": "http://schema.org/extensions", - "themeColor": "33F0FF", - "summary": `Test report for ${project}`, - "sections": [ - { - - "activityTitle": `## ${project}, ${environment}, ${type}`, - // "activitySubtitle": "Status of build #90", - "facts": [ - { - "name": "Environment", - "value": environment, - }, - // { - // "name": "Type", - // "value": "UI", - // }, - { - "name": "Status", - "value": results, - }, - ], - "markdown": true, - }, - ], - "potentialAction": [ - { - "@type": "OpenUri", - "name": "View Report", - "targets": [ - { "os": "default", "uri": buildUrl }, - ], - }, - ], - }, - ) - .then( (res) => { - console.log(`Post results to Microsoft Teams with status: ${res.status}`); - }); -} - -main(); diff --git a/webdriverio-mocha/test/pages/ajax.page.js b/webdriverio-mocha/test/pages/ajax.page.js deleted file mode 100644 index 3b45198f..00000000 --- a/webdriverio-mocha/test/pages/ajax.page.js +++ /dev/null @@ -1,20 +0,0 @@ -import Page from './page'; - -class AjaxPage extends Page { - get ajaxButton() {return $('#ajaxButton');} - get ajaxContent() {return $('#content > p');} - - open() { - super.open('ajax'); - // `return this;` in order to be able to chain in methods in test case. - return this; - } - - getContent() { - this.ajaxContent.waitForExist({ timeout: 20000 }); - return this.ajaxContent.getText(); - return this; - } -} - -export default new AjaxPage(); diff --git a/webdriverio-mocha/test/pages/click.page.js b/webdriverio-mocha/test/pages/click.page.js deleted file mode 100644 index 9f2f02fb..00000000 --- a/webdriverio-mocha/test/pages/click.page.js +++ /dev/null @@ -1,20 +0,0 @@ -import Page from './page'; - -class ClickPage extends Page { - get clickButton() { return $('#badButton'); } - get ajaxContent() {return $('#content > p');} - - open() { - super.open('click'); - // `return this;` in order to be able to chain in methods in test case. - return this; - } - - getContent() { - this.ajaxContent.waitForExist({ timeout: 20000 }); - return this.ajaxContent.getText(); - return this; - } -} - -export default new ClickPage(); diff --git a/webdriverio-mocha/test/pages/components/home_modal.component.js b/webdriverio-mocha/test/pages/components/home_modal.component.js deleted file mode 100644 index a02b1923..00000000 --- a/webdriverio-mocha/test/pages/components/home_modal.component.js +++ /dev/null @@ -1,8 +0,0 @@ -export default class ModalComponent { - constructor(element) { - this.element = element; - } - - get title() {return this.element.$('h3').getText();} - get content() {return this.element.$('p').getText();} -} diff --git a/webdriverio-mocha/test/pages/home.page.js b/webdriverio-mocha/test/pages/home.page.js deleted file mode 100644 index eb7150d2..00000000 --- a/webdriverio-mocha/test/pages/home.page.js +++ /dev/null @@ -1,15 +0,0 @@ -import Page from './page'; -import ModalComponent from './components/home_modal.component'; - -class HomePage extends Page { - get pageModals() { - return $$('#overview .col-sm').map((modal) => new ModalComponent(modal)); - } - - open() { - super.open('home'); - return this; - } -} - -export default new HomePage(); diff --git a/webdriverio-mocha/test/pages/page.js b/webdriverio-mocha/test/pages/page.js deleted file mode 100644 index a27777fa..00000000 --- a/webdriverio-mocha/test/pages/page.js +++ /dev/null @@ -1,15 +0,0 @@ -export default class Page { - constructor() { - this.title = 'my Page'; - } - - open(path) { - browser.url(path); - } - - verifyTextInPage(text) { - const pageText = $('body').getText(); - const position = pageText.search(text); - chai.expect(position).to.be.above(0); - } -} diff --git a/webdriverio-mocha/test/pages/progress.page.js b/webdriverio-mocha/test/pages/progress.page.js deleted file mode 100644 index 25edf690..00000000 --- a/webdriverio-mocha/test/pages/progress.page.js +++ /dev/null @@ -1,25 +0,0 @@ -import Page from './page'; - -class ProgressPage extends Page { - get startButton() {return $('#startButton');} - get stopButton() { return $('#stopButton'); } - get progressBar() { return $('#progressBar'); } - - open() { - super.open('progressbar'); - // `return this;` in order to be able to chain in methods in test case. - return this; - } - - getProgress() { - return this.progressBar.getText(); - } - - waitForProgress(progress, timeout) { - browser.waitUntil( - () => this.getProgress() === `${progress}%`, { timeout: timeout || 10000, timeoutMsg: `Expected ${progress} but got ${this.getProgress()}` } - ); - } -} - -export default new ProgressPage(); diff --git a/webdriverio-mocha/test/pages/textInput.page.js b/webdriverio-mocha/test/pages/textInput.page.js deleted file mode 100644 index 2c848a6c..00000000 --- a/webdriverio-mocha/test/pages/textInput.page.js +++ /dev/null @@ -1,23 +0,0 @@ -import Page from './page'; - -class TextInput extends Page { - get nameField() {return $('#newButtonName');} - get button() {return $('#updatingButton');} - - open() { - super.open('textinput'); - return this; - } - - updateButtonText(text) { - this.nameField.setValue(text); - this.button.click(); - return this; - } - - getButtonText() { - return this.button.getText(); - } -} - -export default new TextInput(); diff --git a/webdriverio-mocha/test/specs/ajax.spec.js b/webdriverio-mocha/test/specs/ajax.spec.js deleted file mode 100644 index a3309ace..00000000 --- a/webdriverio-mocha/test/specs/ajax.spec.js +++ /dev/null @@ -1,12 +0,0 @@ -import ajaxPage from '../pages/ajax.page'; - -describe('Ajax Page Suite', function() { - it('should retrieve Ajax response', function() { - ajaxPage - .open() - .ajaxButton.click(); - expect(ajaxPage.ajaxContent).toHaveText( - 'Data loaded with AJAX get request.', { wait: 20000 }); - utilities.takeScreenshot('ajax_with_request_capture'); - }); -}); diff --git a/webdriverio-mocha/test/specs/click.spec.js b/webdriverio-mocha/test/specs/click.spec.js deleted file mode 100644 index 76560f85..00000000 --- a/webdriverio-mocha/test/specs/click.spec.js +++ /dev/null @@ -1,11 +0,0 @@ -import clickPage from '../pages/click.page'; - -describe('Click page', function() { - it('Should able to click the button', function() { - clickPage - .open() - .clickButton.click(); - - expect(clickPage.clickButton.getText()).toEqual('Button That Ignores DOM Click Event', { wait: 2000 }); - }); -}); diff --git a/webdriverio-mocha/test/specs/home.spec.js b/webdriverio-mocha/test/specs/home.spec.js deleted file mode 100644 index eba940d3..00000000 --- a/webdriverio-mocha/test/specs/home.spec.js +++ /dev/null @@ -1,14 +0,0 @@ -import homePage from '../pages/home.page'; - -describe('Home Page Suite', function() { - it('should have Load Delay section', function() { - // Using component to capture repeating html part - homePage - .open() - .verifyTextInPage('UI Test Automation'); - expect(homePage.pageModals[3].title).toEqual('Load Delay'); - expect(homePage.pageModals[3].content).toEqual( - 'Ensure that a test is capable of waiting for a page to load'); - utilities.takeScreenshot('homepage'); - }); -}); diff --git a/webdriverio-mocha/test/specs/progress.spec.js b/webdriverio-mocha/test/specs/progress.spec.js deleted file mode 100644 index 73e8b8a5..00000000 --- a/webdriverio-mocha/test/specs/progress.spec.js +++ /dev/null @@ -1,33 +0,0 @@ -import progressPage from '../pages/progress.page'; - -describe('Progress page', function () { - - it('Should open the progress page in browser', () => { - progressPage.open() - }); - - it('Should able to click start button', () => { - progressPage - .open() - .startButton.click() - - expect(progressPage.getProgress()).toEqual('25%', { wait: 2000 }); - }) - - it('Should progress change', () => { - progressPage - .open() - .startButton.click() - - progressPage.waitForProgress(27, 20000); - }) - - it('Should wait for 75%', () => { - progressPage - .open() - .startButton.click() - - progressPage.waitForProgress(75, 20000); - }) - -}); diff --git a/webdriverio-mocha/test/specs/sample.spec.js b/webdriverio-mocha/test/specs/sample.spec.js deleted file mode 100644 index e184b9e7..00000000 --- a/webdriverio-mocha/test/specs/sample.spec.js +++ /dev/null @@ -1,17 +0,0 @@ -import Page from '../pages/page'; - -describe('Basic tests', function () { - const page = new Page(); - - it('Should open the page in browser', () => { - page.open('home'); - }); - - it('Should able open the home page and see test', () => { - page.open('home'); - - const pageText = $('body').getText(); - const position = pageText.search('UI Test Automation'); - chai.expect(position).to.be.above(0); - }) -}); diff --git a/webdriverio-mocha/test/specs/textInput.spec.js b/webdriverio-mocha/test/specs/textInput.spec.js deleted file mode 100644 index 01a5a35a..00000000 --- a/webdriverio-mocha/test/specs/textInput.spec.js +++ /dev/null @@ -1,14 +0,0 @@ -import textInputPage from '../pages/textInput.page'; - -describe('Text Input Spec', function() { - before(function() { - textInputPage.open(); - }); - - it('Change button text with chai', function() { - console.log(textInputPage.getButtonText()); - textInputPage.updateButtonText('My Special Name'); - chai.expect(textInputPage.getButtonText()).to.be.eql('My Special Name'); - }); -}); - diff --git a/webdriverio-mocha/wdio.conf.js b/webdriverio-mocha/wdio.conf.js deleted file mode 100644 index d398cba8..00000000 --- a/webdriverio-mocha/wdio.conf.js +++ /dev/null @@ -1,512 +0,0 @@ -const utilities = require("./support/utils/Utilities"); -const chai = require('chai'); -const allure = require('@wdio/allure-reporter').default; -const testomatio = require('@testomatio/reporter/lib/adapter/webdriver'); -// Max time for single test case execution -let timeout = process.env.DEBUG ? 99999999 : 120000; -let elementTimeout = 10000; - -exports.config = { - - // ================================== - // Where should your test be launched - // ================================== - // - runner: 'local', - // - // ===================== - // Server Configurations - // ===================== - // Host address of the running Selenium server. This information is usually obsolete, as - // WebdriverIO automatically connects to localhost. Also if you are using one of the - // supported cloud services like Sauce Labs, Browserstack, Testing Bot or LambdaTest, you also don't - // need to define host and port information (because WebdriverIO can figure that out - // from your user and key information). However, if you are using a private Selenium - // backend, you should define the `hostname`, `port`, and `path` here. - // - // hostname: 'localhost', - // port: 4444, - // path: '/', - // Protocol: http | https - // protocol: 'http', - // - // ================= - // Service Providers - // ================= - // WebdriverIO supports Sauce Labs, Browserstack, Testing Bot and LambdaTest. (Other cloud providers - // should work, too.) These services define specific `user` and `key` (or access key) - // values you must put here, in order to connect to these services. - // - // user: 'webdriverio', - // key: 'xxxxxxxxxxxxxxxx-xxxxxx-xxxxx-xxxxxxxxx', - - // If you run your tests on Sauce Labs you can specify the region you want to run your tests - // in via the `region` property. Available short handles for regions are `us` (default) and `eu`. - // These regions are used for the Sauce Labs VM cloud and the Sauce Labs Real Device Cloud. - // If you don't provide the region, it defaults to `us`. - // region: 'us', - // - // Sauce Labs provides a [headless offering](https://saucelabs.com/products/web-testing/sauce-headless-testing) - // that allows you to run Chrome and Firefox tests headless. - // - // headless: false, - // - // ================== - // Specify Test Files - // ================== - // Define which test specs should run. The pattern is relative to the directory - // from which `wdio` was called. - // - // The specs are defined as an array of spec files (optionally using wildcards - // that will be expanded). The test for each spec file will be run in a separate - // worker process. In order to have a group of spec files run in the same worker - // process simply enclose them in an array within the specs array. - // - // If you are calling `wdio` from an NPM script (see https://docs.npmjs.com/cli/run-script), - // then the current working directory is where your `package.json` resides, so `wdio` - // will be called from there. - // - specs: [ - './test/specs/**/*.spec.js' - ], - // Patterns to exclude. - exclude: [ - 'path/to/excluded/files' - ], - // - // ============ - // Capabilities - // ============ - // Define your capabilities here. WebdriverIO can run multiple capabilities at the same - // time. Depending on the number of capabilities, WebdriverIO launches several test - // sessions. Within your `capabilities`, you can overwrite the `spec` and `exclude` - // options in order to group specific specs to a specific capability. - // - // First, you can define how many instances should be started at the same time. Let's - // say you have 3 different capabilities (Chrome, Firefox, and Safari) and you have - // set `maxInstances` to 1. wdio will spawn 3 processes. - // - // Therefore, if you have 10 spec files and you set `maxInstances` to 10, all spec files - // will be tested at the same time and 30 processes will be spawned. - // - // The property basically handles how many capabilities from the same test should run tests. - // - maxInstances: 10, - // - // Or set a limit to run tests with a specific capability. - // maxInstancesPerCapability: 10, - // - // If you have trouble getting all important capabilities together, check out the - // Sauce Labs platform configurator - a great tool to configure your capabilities: - // https://docs.saucelabs.com/reference/platforms-configurator - // - capabilities: [{ - maxInstances: 3, - browserName: 'chrome', - // 'goog:chromeOptions': { - // // to run chrome headless the following flags are required - // // (see https://developers.google.com/web/updates/2017/04/headless-chrome) - // // args: ['--headless', '--disable-gpu'], - // } - // - // Parameter to ignore some or all default flags - // - if value is true: ignore all DevTools 'default flags' and Puppeteer 'default arguments' - // - if value is an array: DevTools filters given default arguments - // 'wdio:devtoolsOptions': { - // ignoreDefaultArgs: true, - // ignoreDefaultArgs: ['--disable-sync', '--disable-extensions'], - // } - }, - // { - // // maxInstances can get overwritten per capability. So if you have an in house Selenium - // // grid with only 5 firefox instance available you can make sure that not more than - // // 5 instance gets started at a time. - // maxInstances: 5, - // browserName: 'firefox', - // specs: [ - // 'test/ffOnly/*' - // ], - // 'moz:firefoxOptions': { - // // flag to activate Firefox headless mode (see https://github.com/mozilla/geckodriver/blob/master/README.md#firefox-capabilities for more details about moz:firefoxOptions) - // // args: ['-headless'] - // }, - // // If outputDir is provided WebdriverIO can capture driver session logs - // // it is possible to configure which logTypes to exclude. - // // excludeDriverLogs: ['*'], // pass '*' to exclude all driver session logs - // excludeDriverLogs: ['bugreport', 'server'], - // // - // // Parameter to ignore some or all Puppeteer default arguments - // // ignoreDefaultArgs: ['-foreground'], // set value to true to ignore all default arguments - // } - ], - // - // Additional list of node arguments to use when starting child processes - // execArgv: [], - // - // =================== - // Test Configurations - // =================== - // Define all options that are relevant for the WebdriverIO instance here - // - // Level of logging verbosity: trace | debug | info | warn | error | silent - logLevel: 'info', - // - // Set specific log levels per logger - // use 'silent' level to disable logger - // logLevels: { - // webdriver: 'info', - // '@wdio/appium-service': 'info' - // }, - // - // Set directory to store all logs into - // outputDir: __dirname, - // - // If you only want to run your tests until a specific amount of tests have failed use - // bail (default is 0 - don't bail, run all tests). - bail: 0, - // - // Set a base URL in order to shorten `url()` command calls. If your `url` parameter starts - // with `/`, the `baseUrl` is prepended, not including the path portion of `baseUrl`. - // - // If your `url` parameter starts without a scheme or `/` (like `some/path`), the `baseUrl` - // gets prepended directly. - baseUrl: 'http://uitestingplayground.com', - // - // Default timeout for all waitForXXX commands. - waitforTimeout: elementTimeout, - // - // Default timeout in milliseconds for request - // if browser driver or grid doesn't send response - connectionRetryTimeout: 90000, - // - // Default request retries count - connectionRetryCount: 3, - // - // Test runner services - // Services take over a specific job you don't want to take care of. They enhance - // your test setup with almost no effort. Unlike plugins, they don't add new - // commands. Instead, they hook themselves up into the test process. - services: [['selenium-standalone', { drivers: { chrome: true } }]], - // - // Add files to watch (e.g. application code or page objects) when running `wdio` command - // with `--watch` flag. Globbing is supported. - // filesToWatch: [ - // // e.g. rerun tests if I change my application code - // // './app/**/*.js' - // ], - // - // Framework you want to run your specs with. - // The following are supported: 'mocha', 'jasmine', and 'cucumber' - // See also: https://webdriver.io/docs/frameworks.html - // - // Make sure you have the wdio adapter package for the specific framework installed before running any tests. - framework: 'mocha', - // - // The number of times to retry the entire specfile when it fails as a whole - // specFileRetries: 1, - // Delay in seconds between the spec file retry attempts - // specFileRetriesDelay: 0, - // Whether or not retried specfiles should be retried immediately or deferred to the end of the queue - // specFileRetriesDeferred: false, - // - // Test reporter for stdout. - // The only one supported by default is 'dot' - // See also: https://webdriver.io/docs/dot-reporter.html , and click on "Reporters" in left column - reporters: [ - 'spec', - [testomatio, { - apiKey: `${process.env.TESTOMATIO}` - }] - ], - // - // Options to be passed to Mocha. - // See the full list at: http://mochajs.org - mochaOpts: { - ui: 'bdd', - timeout: timeout, - require: ['@babel/register'] - }, - // - // Options to be passed to Jasmine. - // See also: https://github.com/webdriverio/webdriverio/tree/main/packages/wdio-jasmine-framework#jasmineopts-options - // jasmineOpts: { - // // - // // Jasmine default timeout - // defaultTimeoutInterval: 5000, - // // - // // The Jasmine framework allows it to intercept each assertion in order to log the state of the application - // // or website depending on the result. For example, it is pretty handy to take a screenshot every time - // // an assertion fails. - // expectationResultHandler: function(passed, assertion) { - // // do something - // }, - // // - // // Make use of Jasmine-specific grep functionality - // grep: null, - // invertGrep: null - // }, - // - // If you are using Cucumber you need to specify where your step definitions are located. - // See also: https://github.com/webdriverio/webdriverio/tree/main/packages/wdio-cucumber-framework#cucumberopts-options - // cucumberOpts: { - // require: [], // (file/dir) require files before executing features - // backtrace: false, // show full backtrace for errors - // compiler: [], // ("extension:module") require files with the given EXTENSION after requiring MODULE (repeatable) - // dryRun: false, // invoke formatters without executing steps - // failFast: false, // abort the run on first failure - // snippets: true, // hide step definition snippets for pending steps - // source: true, // hide source URIs - // strict: false, // fail if there are any undefined or pending steps - // tagExpression: '', // (expression) only execute the features or scenarios with tags matching the expression - // timeout: 20000, // timeout for step definitions - // ignoreUndefinedDefinitions: false, // Enable this config to treat undefined definitions as warnings. - // scenarioLevelReporter: false // Enable this to make webdriver.io behave as if scenarios and not steps were the tests. - // }, - // For convenience, if ts-node or @babel/register modules are detected - // they are automatically loaded for config parsing so that TypeScript and - // future ES features can be used in wdio configs, and are also - // automatically loaded for test running so that tests can be written - // using TypeScript and future ES features. - // Because this may not be ideal in every situation, the following options - // may be used to customize the loading for test running, incase it has - // other requirements. - // autoCompileOpts: { - // // - // // To disable auto-loading entirely set this to false. - // autoCompile: true, // Disable this to turn off autoloading. Note: When disabling, you will need to handle calling any such libraries yourself. - // // - // // If you have ts-node installed, you can customize how options are passed to it here: - // // Any valid ts-node config option is allowed. Alternatively the ENV Vars could also be used instead of this. - // // See also: https://github.com/TypeStrong/ts-node#cli-and-programmatic-options - // // See also RegisterOptions in https://github.com/TypeStrong/ts-node/blob/master/src/index.ts - // tsNodeOpts: { - // transpileOnly: true, - // project: 'tsconfig.json' - // }, - // // If you have tsconfig-paths installed and provide a tsConfigPathsOpts - // // option, it will be automatically registered during bootstrap. - // tsConfigPathsOpts: { - // baseUrl: './' - // }, - // // - // // If @babel/register is installed, you can customize how options are passed to it here: - // // Any valid @babel/register config option is allowed. - // // https://babeljs.io/docs/en/babel-register#specifying-options - // babelOpts: { - // ignore: [] - // }, - // }, - // - // ===== - // Hooks - // ===== - // WebdriverIO provides a several hooks you can use to interfere the test process in order to enhance - // it and build services around it. You can either apply a single function to it or an array of - // methods. If one of them returns with a promise, WebdriverIO will wait until that promise is - // resolved to continue. - // - /** - * Gets executed once before all workers get launched. - * @param {Object} config wdio configuration object - * @param {Array.} capabilities list of capabilities details - */ - // onPrepare: function (config, capabilities) { - // }, - /** - * Gets executed before a worker process is spawned and can be used to initialize specific service - * for that worker as well as modify runtime environments in an async fashion. - * @param {String} cid capability id (e.g 0-0) - * @param {[type]} caps object containing capabilities for session that will be spawn in the worker - * @param {[type]} specs specs to be run in the worker process - * @param {[type]} args object that will be merged with the main configuration once worker is initialized - * @param {[type]} execArgv list of string arguments passed to the worker process - */ - // onWorkerStart: function (cid, caps, specs, args, execArgv) { - // }, - /** - * Gets executed just after a worker process has exited. - * @param {String} cid capability id (e.g 0-0) - * @param {Number} exitCode 0 - success, 1 - fail - * @param {[type]} specs specs to be run in the worker process - * @param {Number} retries number of retries used - */ - // onWorkerEnd: function (cid, exitCode, specs, retries) { - // }, - /** - * Gets executed just before initializing the webdriver session and test framework. It allows you - * to manipulate configurations depending on the capability or spec. - * @param {Object} config wdio configuration object - * @param {Array.} capabilities list of capabilities details - * @param {Array.} specs List of spec file paths that are to be run - */ - // beforeSession: function (config, capabilities, specs) { - // }, - /** - * Gets executed before test execution begins. At this point you can access to all global - * variables like `browser`. It is the perfect place to define custom commands. - * @param {Array.} capabilities list of capabilities details - * @param {Array.} specs List of spec file paths that are to be run - * @param {Object} browser instance of created browser/device session - */ - before: function (capabilities, specs, browser) { - global.allure = allure; - global.chai = chai; - global.utilities = utilities; - }, - /** - * Gets executed before the suite starts. - * @param {Object} suite suite details - */ - // beforeSuite: function (suite) { - // }, - /** - * This hook gets executed _before_ every hook within the suite starts. - * (For example, this runs before calling `before`, `beforeEach`, `after`, `afterEach` in Mocha.). In Cucumber `context` is the World object. - * - */ - // beforeHook: function (test, context) { - // }, - /** - * Hook that gets executed _after_ every hook within the suite ends. - * (For example, this runs after calling `before`, `beforeEach`, `after`, `afterEach` in Mocha.). In Cucumber `context` is the World object. - */ - // afterHook: function (test, context, { error, result, duration, passed, retries }) { - // }, - /** - * Function to be executed before a test (in Mocha/Jasmine only) - * @param {Object} test test object - * @param {Object} context scope object the test was executed with - */ - // beforeTest: function (test, context) { - // }, - /** - * Runs before a WebdriverIO command is executed. - * @param {String} commandName hook command name - * @param {Array} args arguments that the command would receive - */ - // beforeCommand: function (commandName, args) { - // }, - /** - * Runs after a WebdriverIO command gets executed - * @param {String} commandName hook command name - * @param {Array} args arguments that command would receive - * @param {Number} result 0 - command success, 1 - command error - * @param {Object} error error object, if any - */ - // afterCommand: function (commandName, args, result, error) { - // }, - /** - * Function to be executed after a test (in Mocha/Jasmine only) - * @param {Object} test test object - * @param {Object} context scope object the test was executed with - * @param {Error} result.error error object in case the test fails, otherwise `undefined` - * @param {Any} result.result return object of test function - * @param {Number} result.duration duration of test - * @param {Boolean} result.passed true if test has passed, otherwise false - * @param {Object} result.retries informations to spec related retries, e.g. `{ attempts: 0, limit: 0 }` - */ - afterTest: function (test, context, { error, result, duration, passed, retries }) { - if (error) { - browser.takeScreenshot() - } - }, - /** - * Hook that gets executed after the suite has ended. - * @param {Object} suite suite details - */ - // afterSuite: function (suite) { - // }, - /** - * Gets executed after all tests are done. You still have access to all global variables from - * the test. - * @param {Number} result 0 - test pass, 1 - test fail - * @param {Array.} capabilities list of capabilities details - * @param {Array.} specs List of spec file paths that ran - */ - // after: function (result, capabilities, specs) { - // }, - /** - * Gets executed right after terminating the webdriver session. - * @param {Object} config wdio configuration object - * @param {Array.} capabilities list of capabilities details - * @param {Array.} specs List of spec file paths that ran - */ - // afterSession: function (config, capabilities, specs) { - // }, - /** - * Gets executed after all workers have shut down and the process is about to exit. - * An error thrown in the `onComplete` hook will result in the test run failing. - * @param {Object} exitCode 0 - success, 1 - fail - * @param {Object} config wdio configuration object - * @param {Array.} capabilities list of capabilities details - * @param {} results object containing test results - */ - // onComplete: function (exitCode, config, capabilities, results) { - // }, - /** - * Gets executed when a refresh happens. - * @param {String} oldSessionId session ID of the old session - * @param {String} newSessionId session ID of the new session - */ - // onReload: function(oldSessionId, newSessionId) { - // }, - /** - * Cucumber Hooks - * - * Runs before a Cucumber Feature. - * @param {String} uri path to feature file - * @param {GherkinDocument.IFeature} feature Cucumber feature object - */ - // beforeFeature: function (uri, feature) { - // }, - /** - * - * Runs before a Cucumber Scenario. - * @param {ITestCaseHookParameter} world world object containing information on pickle and test step - * @param {Object} context Cucumber World object - */ - // beforeScenario: function (world, context) { - // }, - /** - * - * Runs before a Cucumber Step. - * @param {Pickle.IPickleStep} step step data - * @param {IPickle} scenario scenario pickle - * @param {Object} context Cucumber World object - */ - // beforeStep: function (step, scenario, context) { - // }, - /** - * - * Runs after a Cucumber Step. - * @param {Pickle.IPickleStep} step step data - * @param {IPickle} scenario scenario pickle - * @param {Object} result results object containing scenario results - * @param {boolean} result.passed true if scenario has passed - * @param {string} result.error error stack if scenario failed - * @param {number} result.duration duration of scenario in milliseconds - * @param {Object} context Cucumber World object - */ - // afterStep: function (step, scenario, result, context) { - // }, - /** - * - * Runs after a Cucumber Scenario. - * @param {ITestCaseHookParameter} world world object containing information on pickle and test step - * @param {Object} result results object containing scenario results `{passed: boolean, error: string, duration: number}` - * @param {boolean} result.passed true if scenario has passed - * @param {string} result.error error stack if scenario failed - * @param {number} result.duration duration of scenario in milliseconds - * @param {Object} context Cucumber World object - */ - // afterScenario: function (world, result, context) { - // }, - /** - * - * Runs after a Cucumber Feature. - * @param {String} uri path to feature file - * @param {GherkinDocument.IFeature} feature Cucumber feature object - */ - // afterFeature: function (uri, feature) { - // } -} From 2ab20cb88795df3d4a0c044d66b14d569519fb44 Mon Sep 17 00:00:00 2001 From: Oleksandr Pelykh Date: Wed, 30 Aug 2023 20:02:16 +0300 Subject: [PATCH 42/64] add logger to jest --- jest/__tests__/logger.test.js | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 jest/__tests__/logger.test.js diff --git a/jest/__tests__/logger.test.js b/jest/__tests__/logger.test.js new file mode 100644 index 00000000..7a64cdb9 --- /dev/null +++ b/jest/__tests__/logger.test.js @@ -0,0 +1,8 @@ +import { logger } from '@testomatio/reporter'; + +describe('Looger test @S115af7e1', function () { + it('logs should be added to the testomatio report @T9b799166', async function () { + console.log('this is console log message from Jest'); + logger.warn('this is logger warn message from Jest'); + }); +}); From c35632296aa40e4f98c6415b354be7f46d9cfe3d Mon Sep 17 00:00:00 2001 From: Oleksandr Pelykh Date: Wed, 30 Aug 2023 20:07:10 +0300 Subject: [PATCH 43/64] add logger test for parallel testrun --- jest/__tests__/logger-parallel.test.js | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 jest/__tests__/logger-parallel.test.js diff --git a/jest/__tests__/logger-parallel.test.js b/jest/__tests__/logger-parallel.test.js new file mode 100644 index 00000000..668e69da --- /dev/null +++ b/jest/__tests__/logger-parallel.test.js @@ -0,0 +1,8 @@ +import { logger } from '@testomatio/reporter'; + +describe('Looger test 2 @Sdcf9db34', function () { + it('logs should be added to the testomatio report 2 @Tf2418f0e', async function () { + console.log('this is 2nd console log message from Jest'); + logger.warn('this is 2nd logger warn message from Jest'); + }); +}); From a1f16ba2442714e2b545a1d7d6746eaae8edac61 Mon Sep 17 00:00:00 2001 From: Mykhailo Poliarush Date: Fri, 1 Sep 2023 01:13:43 +0200 Subject: [PATCH 44/64] Update codecept.conf.js --- codeceptJS/codecept.conf.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/codeceptJS/codecept.conf.js b/codeceptJS/codecept.conf.js index e0e87dbd..ee265def 100644 --- a/codeceptJS/codecept.conf.js +++ b/codeceptJS/codecept.conf.js @@ -14,7 +14,7 @@ exports.config = { helpers: { Playwright: { video: true, - trace: false, + trace: true, url: 'http://localhost', waitForTimeout: 5000, waitForNavigation: 'networkidle0', From ac9e3353d2364c3f864822ed424baa2eb711d03a Mon Sep 17 00:00:00 2001 From: Mykhailo Poliarush Date: Fri, 1 Sep 2023 01:33:39 +0200 Subject: [PATCH 45/64] Update playwright.config.ts --- playwright/playwright.config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/playwright/playwright.config.ts b/playwright/playwright.config.ts index fc258405..36b88fc8 100644 --- a/playwright/playwright.config.ts +++ b/playwright/playwright.config.ts @@ -30,7 +30,7 @@ const config: PlaywrightTestConfig = { use: { // Retry a test if its failing with enabled tracing. This allows you to analyse the DOM, console logs, network traffic etc. // More information: https://playwright.dev/docs/trace-viewer - trace: 'on-first-retry', + trace: 'retain-on-failure', // All available context options: https://playwright.dev/docs/api/class-browser#browser-new-context contextOptions: { From d604cd6e8f20683e3a0131370b2824ff63802a8a Mon Sep 17 00:00:00 2001 From: Mykhailo Poliarush Date: Fri, 1 Sep 2023 01:38:16 +0200 Subject: [PATCH 46/64] Update playwright.config.ts --- playwright/playwright.config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/playwright/playwright.config.ts b/playwright/playwright.config.ts index 36b88fc8..da6a79aa 100644 --- a/playwright/playwright.config.ts +++ b/playwright/playwright.config.ts @@ -39,7 +39,7 @@ const config: PlaywrightTestConfig = { screenshot: 'on', - // video: 'retain-on-failure', + video: 'retain-on-failure', }, //TODO: only Chrome mode projects: [ From 7d09a1900013ecae6646d04523ca67552a9777d2 Mon Sep 17 00:00:00 2001 From: Oleksandr Pelykh Date: Fri, 1 Sep 2023 16:36:47 +0300 Subject: [PATCH 47/64] add logger to codecept --- codeceptJS/todomvc-tests/create-todos_test.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/codeceptJS/todomvc-tests/create-todos_test.js b/codeceptJS/todomvc-tests/create-todos_test.js index 0320fcf6..0363ae15 100644 --- a/codeceptJS/todomvc-tests/create-todos_test.js +++ b/codeceptJS/todomvc-tests/create-todos_test.js @@ -1,3 +1,5 @@ +const {testomatioLogger} = require('@testomatio/reporter'); + Feature('@first Create Tasks @step:06 @smoke @story:12345') Before(async ({ I, TodosPage }) => { @@ -8,6 +10,8 @@ Before(async ({ I, TodosPage }) => { * Happy Path tests */ Scenario('Create a new todo item', async ({ I, TodosPage }) => { + console.log('console log message'); + testomatioLogger.info('testomatio info log message'); I.say('Given I have an empty todo list') I.say('When I create a todo "foo"') From 330317dded62fdb5ec407b130ebe9d7d1662a227 Mon Sep 17 00:00:00 2001 From: Oleksandr Pelykh Date: Fri, 1 Sep 2023 16:45:16 +0300 Subject: [PATCH 48/64] add log messages to playwright --- playwright/e2e-examples/e2e-tests/1-getting-started.spec.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/playwright/e2e-examples/e2e-tests/1-getting-started.spec.ts b/playwright/e2e-examples/e2e-tests/1-getting-started.spec.ts index 7eb053a1..0de9aa56 100644 --- a/playwright/e2e-examples/e2e-tests/1-getting-started.spec.ts +++ b/playwright/e2e-examples/e2e-tests/1-getting-started.spec.ts @@ -1,4 +1,5 @@ import { test, expect } from '@playwright/test'; +import { testomatioLogger } from '@testomatio/reporter'; /** * Inside every test you get a new isolated page instance. @@ -6,6 +7,8 @@ import { test, expect } from '@playwright/test'; * @see https://playwright.dev/docs/api/class-page */ test('basic test', async ({ page }) => { + console.log('console log message'); + testomatioLogger.warn('testomatio logger warn message'); await page.goto('https://todomvc.com/examples/vanilla-es6/'); const inputBox = page.locator('input.new-todo'); From d2f17538a415ff47f7ac9276c10a2c45cd80c340 Mon Sep 17 00:00:00 2001 From: Oleksandr Pelykh Date: Fri, 1 Sep 2023 16:58:27 +0300 Subject: [PATCH 49/64] add logger test to mocha --- mocha-ts-multi-reporters/spec/test.example.spec.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/mocha-ts-multi-reporters/spec/test.example.spec.ts b/mocha-ts-multi-reporters/spec/test.example.spec.ts index 59028f3c..4cfd951b 100644 --- a/mocha-ts-multi-reporters/spec/test.example.spec.ts +++ b/mocha-ts-multi-reporters/spec/test.example.spec.ts @@ -1,3 +1,4 @@ +import { testomatioLogger } from '@testomatio/reporter'; import { expect } from 'chai'; describe("Array", function() { @@ -66,5 +67,13 @@ describe("Array", function() { it("should return a sorted array in ascending order", function() { expect([3, 2, 1].sort()).to.deep.equal([1, 2, 3]); }); + + + // Test Case 11 + it("should attach logs", function () { + console.warn('console warn message'); + testomatioLogger.debug('testomatio logger debug message'); + expect(arr.indexOf(4)).to.equal(-1); + }); }); From 382442fb977d81fd18868b918ad08e0f44d36baf Mon Sep 17 00:00:00 2001 From: Oleksandr Pelykh Date: Fri, 1 Sep 2023 17:05:45 +0300 Subject: [PATCH 50/64] remove test ids from jest tests --- jest/__tests__/logger-parallel.test.js | 2 +- jest/__tests__/logger.test.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/jest/__tests__/logger-parallel.test.js b/jest/__tests__/logger-parallel.test.js index 668e69da..b0584bd8 100644 --- a/jest/__tests__/logger-parallel.test.js +++ b/jest/__tests__/logger-parallel.test.js @@ -1,6 +1,6 @@ import { logger } from '@testomatio/reporter'; -describe('Looger test 2 @Sdcf9db34', function () { +describe('Logger test 2', function () { it('logs should be added to the testomatio report 2 @Tf2418f0e', async function () { console.log('this is 2nd console log message from Jest'); logger.warn('this is 2nd logger warn message from Jest'); diff --git a/jest/__tests__/logger.test.js b/jest/__tests__/logger.test.js index 7a64cdb9..c76e2860 100644 --- a/jest/__tests__/logger.test.js +++ b/jest/__tests__/logger.test.js @@ -1,6 +1,6 @@ import { logger } from '@testomatio/reporter'; -describe('Looger test @S115af7e1', function () { +describe('Logger test', function () { it('logs should be added to the testomatio report @T9b799166', async function () { console.log('this is console log message from Jest'); logger.warn('this is logger warn message from Jest'); From cf6124a584d07af1b725c3d681ac8dcdd54a1457 Mon Sep 17 00:00:00 2001 From: Vitalii Mykhailiuk <82405549+mykhailiukVitalii@users.noreply.github.com> Date: Wed, 20 Sep 2023 12:55:11 +0300 Subject: [PATCH 51/64] [GITHUB REPORTING] run tests each 6 days instead of 2 (#46) --- .github/workflows/codeceptjs-reporting.yml | 2 +- .github/workflows/cypress-reporting.yml | 2 +- .github/workflows/playwright-reporting.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeceptjs-reporting.yml b/.github/workflows/codeceptjs-reporting.yml index 4e2b1180..f6a0f310 100644 --- a/.github/workflows/codeceptjs-reporting.yml +++ b/.github/workflows/codeceptjs-reporting.yml @@ -5,7 +5,7 @@ name: CodeceptJS example reporting results on: schedule: - - cron: "0 5 */2 * *" + - cron: "0 5 */6 * *" jobs: reporting: diff --git a/.github/workflows/cypress-reporting.yml b/.github/workflows/cypress-reporting.yml index d1b0d7fe..cd31ef83 100644 --- a/.github/workflows/cypress-reporting.yml +++ b/.github/workflows/cypress-reporting.yml @@ -5,7 +5,7 @@ name: Cypress example reporting results on: schedule: - - cron: "10 5 */2 * *" + - cron: "10 5 */6 * *" jobs: reporting: diff --git a/.github/workflows/playwright-reporting.yml b/.github/workflows/playwright-reporting.yml index dfc5f768..c98a58f8 100644 --- a/.github/workflows/playwright-reporting.yml +++ b/.github/workflows/playwright-reporting.yml @@ -5,7 +5,7 @@ name: Playwright example reporting results on: schedule: - - cron: "20 5 */2 * *" + - cron: "20 5 */6 * *" jobs: reporting: From 74aadf6f0967ccb05e7faf09d39aac145f7ca6de Mon Sep 17 00:00:00 2001 From: Vitalii Mykhailiuk <82405549+mykhailiukVitalii@users.noreply.github.com> Date: Wed, 20 Sep 2023 21:14:30 +0300 Subject: [PATCH 52/64] [CODECEPTJS GITHUB] add playwright update command to codeceptjs-reporting.yml (#47) --- .github/workflows/codeceptjs-reporting.yml | 3 ++- codeceptJS/package.json | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/codeceptjs-reporting.yml b/.github/workflows/codeceptjs-reporting.yml index f6a0f310..ea3a6709 100644 --- a/.github/workflows/codeceptjs-reporting.yml +++ b/.github/workflows/codeceptjs-reporting.yml @@ -27,8 +27,9 @@ jobs: node-version: ${{ matrix.node-version }} - name: Setup dependancies run: npm i + - name: Playwright browser updates + run: npx playwright install - name: Run tests - id: test-incorrect run: bash run-tests-and-check-results.sh env: TESTOMATIO: "${{ secrets.TESTOMATIO }}" diff --git a/codeceptJS/package.json b/codeceptJS/package.json index 8043c171..4a11cd20 100644 --- a/codeceptJS/package.json +++ b/codeceptJS/package.json @@ -27,6 +27,6 @@ "devDependencies": { "@codeceptjs/configure": "^0.10.0", "@codeceptjs/ui": "^0.5.0", - "codeceptjs": "^3.5.2" + "codeceptjs": "latest" } } From 12c8838bc2d0cb1199c1599e5b9f584554fe8c87 Mon Sep 17 00:00:00 2001 From: Oleksandr Pelykh Date: Wed, 1 Nov 2023 17:27:58 +0200 Subject: [PATCH 53/64] add artifacts tests (codecept, jest, mocha, playywright, cucumber) (#48) * add artifacts test for jest and codeceptjs * codecept: remove extra test * add playwright artifacts tests * upgrade cucumber version to latest * add new cucumber project * add artifact test for cucumber * update mocha-multi: upgrade package.json, add artifacts tests * add meta test for mocha --- codeceptJS/artifact-tests/artifact.test.js | 13 +++++++++++++ codeceptJS/artifacts/artifact-test-image.png | Bin 0 -> 18453 bytes codeceptJS/artifacts/artifact-test-text.txt | 1 + codeceptJS/package.json | 3 ++- cucumber-new/artifacts/artifact-test-image.png | Bin 0 -> 18453 bytes cucumber-new/features/greeting.feature | 7 +++++++ cucumber-new/features/step_definitions/steps.js | 14 ++++++++++++++ cucumber-new/package.json | 16 ++++++++++++++++ cucumber-new/src/index.js | 10 ++++++++++ .../testomatio_tmp/artifact/artifact_4aad7217 | 1 + cucumber-new/testomatio_tmp/log/log_4aad7217 | 5 +++++ cucumber/package.json | 2 +- jest/__tests__/artifact.test.js | 15 +++++++++++++++ jest/artifacts/artifact-test-image.png | Bin 0 -> 18453 bytes jest/artifacts/artifact-test-text.txt | 1 + jest/package.json | 4 ++-- .../artifacts/artifact-test-image.png | Bin 0 -> 18453 bytes .../artifacts/artifact-test-text.txt | 1 + mocha-ts-multi-reporters/package.json | 16 ++++++++-------- mocha-ts-multi-reporters/spec/artifact.spec.ts | 11 +++++++++++ mocha-ts-multi-reporters/spec/meta.spec.ts | 11 +++++++++++ playwright/artifacts/artifact-test-image.png | Bin 0 -> 18453 bytes playwright/artifacts/artifact-test-text.txt | 1 + playwright/tests/artifacts.test.js | 12 ++++++++++++ 24 files changed, 132 insertions(+), 12 deletions(-) create mode 100644 codeceptJS/artifact-tests/artifact.test.js create mode 100644 codeceptJS/artifacts/artifact-test-image.png create mode 100644 codeceptJS/artifacts/artifact-test-text.txt create mode 100644 cucumber-new/artifacts/artifact-test-image.png create mode 100644 cucumber-new/features/greeting.feature create mode 100644 cucumber-new/features/step_definitions/steps.js create mode 100644 cucumber-new/package.json create mode 100644 cucumber-new/src/index.js create mode 100644 cucumber-new/testomatio_tmp/artifact/artifact_4aad7217 create mode 100644 cucumber-new/testomatio_tmp/log/log_4aad7217 create mode 100644 jest/__tests__/artifact.test.js create mode 100644 jest/artifacts/artifact-test-image.png create mode 100644 jest/artifacts/artifact-test-text.txt create mode 100644 mocha-ts-multi-reporters/artifacts/artifact-test-image.png create mode 100644 mocha-ts-multi-reporters/artifacts/artifact-test-text.txt create mode 100644 mocha-ts-multi-reporters/spec/artifact.spec.ts create mode 100644 mocha-ts-multi-reporters/spec/meta.spec.ts create mode 100644 playwright/artifacts/artifact-test-image.png create mode 100644 playwright/artifacts/artifact-test-text.txt create mode 100644 playwright/tests/artifacts.test.js diff --git a/codeceptJS/artifact-tests/artifact.test.js b/codeceptJS/artifact-tests/artifact.test.js new file mode 100644 index 00000000..0b994f05 --- /dev/null +++ b/codeceptJS/artifact-tests/artifact.test.js @@ -0,0 +1,13 @@ +const { testomat } = require('@testomatio/reporter'); + +Feature('Upload artifacts @Sc8396536'); + +Scenario('Upload file @T4d645caf', async () => { + testomat.artifact('artifacts/artifact-test-text.txt'); +}); + +Scenario('Upload image @T806d42e4', async () => { + testomat.artifact({ + path: 'artifacts/artifact-test-image.png', + }); +}); diff --git a/codeceptJS/artifacts/artifact-test-image.png b/codeceptJS/artifacts/artifact-test-image.png new file mode 100644 index 0000000000000000000000000000000000000000..f844619a0d4f53e71e81b05c8e5f0ff1279cab84 GIT binary patch literal 18453 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 DfU(8* literal 0 HcmV?d00001 diff --git a/codeceptJS/artifacts/artifact-test-text.txt b/codeceptJS/artifacts/artifact-test-text.txt new file mode 100644 index 00000000..a250a0b1 --- /dev/null +++ b/codeceptJS/artifacts/artifact-test-text.txt @@ -0,0 +1 @@ +this is test artifact content \ No newline at end of file diff --git a/codeceptJS/package.json b/codeceptJS/package.json index 4a11cd20..eb1d8c73 100644 --- a/codeceptJS/package.json +++ b/codeceptJS/package.json @@ -3,7 +3,8 @@ "version": "1.1.3", "main": "index.js", "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" + "test": "npx codeceptjs run", + "test:artifact": "npx codeceptjs run artifact-tests/**" }, "keywords": [], "author": "kaflan, davert, mihaylukvv", diff --git a/cucumber-new/artifacts/artifact-test-image.png b/cucumber-new/artifacts/artifact-test-image.png new file mode 100644 index 0000000000000000000000000000000000000000..f844619a0d4f53e71e81b05c8e5f0ff1279cab84 GIT binary patch literal 18453 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 DfU(8* literal 0 HcmV?d00001 diff --git a/cucumber-new/features/greeting.feature b/cucumber-new/features/greeting.feature new file mode 100644 index 00000000..194a2fbc --- /dev/null +++ b/cucumber-new/features/greeting.feature @@ -0,0 +1,7 @@ +@Sc0a5168e +Feature: Greeting + + @T4aad7217 + Scenario: Say hello + When the greeter says hello + Then I should have heard "hello" diff --git a/cucumber-new/features/step_definitions/steps.js b/cucumber-new/features/step_definitions/steps.js new file mode 100644 index 00000000..ba8591ed --- /dev/null +++ b/cucumber-new/features/step_definitions/steps.js @@ -0,0 +1,14 @@ +import assert from "assert"; +import { When, Then } from "@cucumber/cucumber"; +import { Greeter } from "../../src/index.js"; + +When("the greeter says hello", function () { + this.whatIHeard = new Greeter().sayHello(); +}); + +Then( + "I should have heard {string}", + function (expectedResponse) { + assert.equal(this.whatIHeard, expectedResponse); + } +); diff --git a/cucumber-new/package.json b/cucumber-new/package.json new file mode 100644 index 00000000..38aa83ac --- /dev/null +++ b/cucumber-new/package.json @@ -0,0 +1,16 @@ +{ + "name": "cucumber-new", + "version": "1.0.0", + "description": "", + "main": "index.js", + "type": "module", + "scripts": { + "test": "cucumber-js" + }, + "author": "", + "license": "ISC", + "dependencies": { + "@cucumber/cucumber": "^10.0.1", + "@testomatio/reporter": "file:../../reporter/testomatio-reporter-1.0.12.tgz" + } +} diff --git a/cucumber-new/src/index.js b/cucumber-new/src/index.js new file mode 100644 index 00000000..f1fa5582 --- /dev/null +++ b/cucumber-new/src/index.js @@ -0,0 +1,10 @@ +import { testomat } from '@testomatio/reporter'; + +export class Greeter { + sayHello() { + testomat.artifact({ + path: 'artifacts/artifact-test-image.png', + }); + return 'hello'; + } +} diff --git a/cucumber-new/testomatio_tmp/artifact/artifact_4aad7217 b/cucumber-new/testomatio_tmp/artifact/artifact_4aad7217 new file mode 100644 index 00000000..3d8846f1 --- /dev/null +++ b/cucumber-new/testomatio_tmp/artifact/artifact_4aad7217 @@ -0,0 +1 @@ +{"path":"artifacts/artifact-test-image.png"} \ No newline at end of file diff --git a/cucumber-new/testomatio_tmp/log/log_4aad7217 b/cucumber-new/testomatio_tmp/log/log_4aad7217 new file mode 100644 index 00000000..b2963dad --- /dev/null +++ b/cucumber-new/testomatio_tmp/log/log_4aad7217 @@ -0,0 +1,5 @@ +Say hello: PASSED  +  SUCCESS  | Total Scenarios: 1  +[TESTOMATIO] 📊 Report created. Report ID: c6e82531 +[TESTOMATIO] 📊 Report Saved. Report URL: https://beta.testomat.io/projects/cucumber-artifact/runs/c6e82531/report +[TESTOMATIO] 🗄️ 1 artifacts publicly uploaded to S3 bucket \ No newline at end of file diff --git a/cucumber/package.json b/cucumber/package.json index 4b2b730e..1a816506 100644 --- a/cucumber/package.json +++ b/cucumber/package.json @@ -9,9 +9,9 @@ "author": "", "license": "ISC", "dependencies": { + "@cucumber/cucumber": "^10.0.1", "@testomatio/reporter": "latest", "cucumber": "^6.0.5" }, - "devDependencies": {}, "description": "" } diff --git a/jest/__tests__/artifact.test.js b/jest/__tests__/artifact.test.js new file mode 100644 index 00000000..0cdf5ae8 --- /dev/null +++ b/jest/__tests__/artifact.test.js @@ -0,0 +1,15 @@ +import { testomat } from '@testomatio/reporter'; + + +describe('suite name @Sf862948e', () => { + it('artifact with path as string is uploaded @Tca2c7cc5', async function () { + testomat.artifact('artifacts/artifact-test-text.txt'); + }); + + it('artifact with path as object is uploaded @Tcf89ae3e', async function () { + testomat.artifact({ + path: 'artifacts/artifact-test-image.png', + }); + }); + +}) diff --git a/jest/artifacts/artifact-test-image.png b/jest/artifacts/artifact-test-image.png new file mode 100644 index 0000000000000000000000000000000000000000..f844619a0d4f53e71e81b05c8e5f0ff1279cab84 GIT binary patch literal 18453 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 DfU(8* literal 0 HcmV?d00001 diff --git a/jest/artifacts/artifact-test-text.txt b/jest/artifacts/artifact-test-text.txt new file mode 100644 index 00000000..a250a0b1 --- /dev/null +++ b/jest/artifacts/artifact-test-text.txt @@ -0,0 +1 @@ +this is test artifact content \ No newline at end of file diff --git a/jest/package.json b/jest/package.json index 9b5bdefe..0e56f7fb 100644 --- a/jest/package.json +++ b/jest/package.json @@ -6,8 +6,8 @@ "author": "Koushik Mohan ", "license": "MIT", "dependencies": { - "@testomatio/reporter": "^0.6.5", - "check-tests": "^0.7.3", + "@testomatio/reporter": "latest", + "check-tests": "latest", "jest-junit": "^13.2.0" }, "devDependencies": { diff --git a/mocha-ts-multi-reporters/artifacts/artifact-test-image.png b/mocha-ts-multi-reporters/artifacts/artifact-test-image.png new file mode 100644 index 0000000000000000000000000000000000000000..f844619a0d4f53e71e81b05c8e5f0ff1279cab84 GIT binary patch literal 18453 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 DfU(8* literal 0 HcmV?d00001 diff --git a/mocha-ts-multi-reporters/artifacts/artifact-test-text.txt b/mocha-ts-multi-reporters/artifacts/artifact-test-text.txt new file mode 100644 index 00000000..a250a0b1 --- /dev/null +++ b/mocha-ts-multi-reporters/artifacts/artifact-test-text.txt @@ -0,0 +1 @@ +this is test artifact content \ No newline at end of file diff --git a/mocha-ts-multi-reporters/package.json b/mocha-ts-multi-reporters/package.json index f927aec2..90243fdd 100644 --- a/mocha-ts-multi-reporters/package.json +++ b/mocha-ts-multi-reporters/package.json @@ -15,16 +15,16 @@ "author": "Vitalii Mykhailiuk (vmykhailiuk.chanel@gmail.com)", "license": "MIT", "dependencies": { - "mocha": "10.2.0", - "typescript": "4.9.4", - "ts-node": "10.9.1", - "dotenv": "16.0.3", - "mocha-multi-reporters": "1.5.1", - "chai": "4.3.7", + "@testomatio/reporter": "latest", + "@types/chai": "4.3.4", "@types/mocha": "10.0.1", "@types/node": "18.11.18", - "@types/chai": "4.3.4", - "@testomatio/reporter": "^0.7.4" + "chai": "4.3.7", + "dotenv": "16.0.3", + "mocha": "10.2.0", + "mocha-multi-reporters": "1.5.1", + "ts-node": "10.9.1", + "typescript": "4.9.4" }, "devDependencies": { "@typescript-eslint/eslint-plugin": "5.48.2", diff --git a/mocha-ts-multi-reporters/spec/artifact.spec.ts b/mocha-ts-multi-reporters/spec/artifact.spec.ts new file mode 100644 index 00000000..cd25cdc8 --- /dev/null +++ b/mocha-ts-multi-reporters/spec/artifact.spec.ts @@ -0,0 +1,11 @@ +import { testomat } from '@testomatio/reporter'; + +describe('Artifacts', () => { + it('attach image @Tc4cf0c7e', () => { + testomat.artifact('artifacts/artifact-test-image.png'); + }); + + it('attach text', () => { + testomat.artifact('artifacts/artifact-test-text.txt'); + }); +}); diff --git a/mocha-ts-multi-reporters/spec/meta.spec.ts b/mocha-ts-multi-reporters/spec/meta.spec.ts new file mode 100644 index 00000000..4ab4c78a --- /dev/null +++ b/mocha-ts-multi-reporters/spec/meta.spec.ts @@ -0,0 +1,11 @@ +import { testomat } from '@testomatio/reporter'; + +describe('Meta info @Saa571da4', () => { + it('attach meta (key:value) to testrun @T75633e91', () => { + testomat.meta({ + runType: 'smoke', + browser: 'chrome', + env: 'production', + }) + }); +}); diff --git a/playwright/artifacts/artifact-test-image.png b/playwright/artifacts/artifact-test-image.png new file mode 100644 index 0000000000000000000000000000000000000000..f844619a0d4f53e71e81b05c8e5f0ff1279cab84 GIT binary patch literal 18453 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 DfU(8* literal 0 HcmV?d00001 diff --git a/playwright/artifacts/artifact-test-text.txt b/playwright/artifacts/artifact-test-text.txt new file mode 100644 index 00000000..a250a0b1 --- /dev/null +++ b/playwright/artifacts/artifact-test-text.txt @@ -0,0 +1 @@ +this is test artifact content \ No newline at end of file diff --git a/playwright/tests/artifacts.test.js b/playwright/tests/artifacts.test.js new file mode 100644 index 00000000..a25411c4 --- /dev/null +++ b/playwright/tests/artifacts.test.js @@ -0,0 +1,12 @@ +import { test } from '@playwright/test'; +import { testomat } from '@testomatio/reporter'; + +test(`upload text file @T50c9c7d7`, async ({}, testInfo) => { + testomat.artifact('artifacts/artifact-test-text.txt'); +}); + +test(`upload image @Tc5c3fc3e`, async () => { + testomat.artifact({ + path: 'artifacts/artifact-test-image.png', + }); +}); From 7a5b418b29c95ed2c3f18843fa6ad464003dea56 Mon Sep 17 00:00:00 2001 From: Oleksandr Pelykh Date: Fri, 10 Nov 2023 21:08:40 +0200 Subject: [PATCH 54/64] update tests for: pw, codecept, cucumber-new, mocha, jest (#51) * update pw art tests * add examples for codeceptjs tests * add cucumber-new tests * add cucumber-new tests * rm ids from cucumber tests * upd playright tests * update tests for jest * add mocha examples * upd comment * rm logs --- codeceptJS/artifact-tests/artifact.test.js | 13 ------ .../tests-usage-examples/functions.test.js | 22 +++++++++ .../tests-usage-examples/logger.test.js | 20 +++++++++ cucumber-new/features/artifact.feature | 5 +++ cucumber-new/features/greeting.feature | 2 - cucumber-new/features/logger.feature | 5 +++ cucumber-new/features/step.feature | 5 +++ .../features/step_definitions/steps.js | 45 ++++++++++++++----- cucumber-new/package.json | 2 +- cucumber-new/src/index.js | 5 --- .../testomatio_tmp/artifact/artifact_4aad7217 | 1 - cucumber-new/testomatio_tmp/log/log_4aad7217 | 5 --- jest/__tests__/artifact.test.js | 15 ------- jest/__tests__/logger-parallel.test.js | 2 +- jest/__tests__/testomat-functions.test.js | 15 +++++++ .../spec/artifact.spec.ts | 11 ----- mocha-ts-multi-reporters/spec/logger.spec.ts | 9 ++++ mocha-ts-multi-reporters/spec/meta.spec.ts | 11 ----- .../spec/testomat-functions.spec.ts | 16 +++++++ .../e2e-tests/1-getting-started.spec.ts | 3 -- playwright/tests/artifacts.test.js | 12 ----- playwright/tests/image-artifact.test.js | 8 ++++ playwright/tests/logger.test.js | 20 +++++++++ playwright/tests/multiple-artifacts.test.js | 17 +++++++ playwright/tests/testomat-functions.test.js | 12 +++++ 25 files changed, 191 insertions(+), 90 deletions(-) delete mode 100644 codeceptJS/artifact-tests/artifact.test.js create mode 100644 codeceptJS/tests-usage-examples/functions.test.js create mode 100644 codeceptJS/tests-usage-examples/logger.test.js create mode 100644 cucumber-new/features/artifact.feature create mode 100644 cucumber-new/features/logger.feature create mode 100644 cucumber-new/features/step.feature delete mode 100644 cucumber-new/testomatio_tmp/artifact/artifact_4aad7217 delete mode 100644 cucumber-new/testomatio_tmp/log/log_4aad7217 delete mode 100644 jest/__tests__/artifact.test.js create mode 100644 jest/__tests__/testomat-functions.test.js delete mode 100644 mocha-ts-multi-reporters/spec/artifact.spec.ts create mode 100644 mocha-ts-multi-reporters/spec/logger.spec.ts delete mode 100644 mocha-ts-multi-reporters/spec/meta.spec.ts create mode 100644 mocha-ts-multi-reporters/spec/testomat-functions.spec.ts delete mode 100644 playwright/tests/artifacts.test.js create mode 100644 playwright/tests/image-artifact.test.js create mode 100644 playwright/tests/logger.test.js create mode 100644 playwright/tests/multiple-artifacts.test.js create mode 100644 playwright/tests/testomat-functions.test.js diff --git a/codeceptJS/artifact-tests/artifact.test.js b/codeceptJS/artifact-tests/artifact.test.js deleted file mode 100644 index 0b994f05..00000000 --- a/codeceptJS/artifact-tests/artifact.test.js +++ /dev/null @@ -1,13 +0,0 @@ -const { testomat } = require('@testomatio/reporter'); - -Feature('Upload artifacts @Sc8396536'); - -Scenario('Upload file @T4d645caf', async () => { - testomat.artifact('artifacts/artifact-test-text.txt'); -}); - -Scenario('Upload image @T806d42e4', async () => { - testomat.artifact({ - path: 'artifacts/artifact-test-image.png', - }); -}); diff --git a/codeceptJS/tests-usage-examples/functions.test.js b/codeceptJS/tests-usage-examples/functions.test.js new file mode 100644 index 00000000..fe151728 --- /dev/null +++ b/codeceptJS/tests-usage-examples/functions.test.js @@ -0,0 +1,22 @@ +const { testomat } = require('@testomatio/reporter'); + +Feature('Testomat functions'); + +Scenario('Upload file', async () => { + testomat.artifact('artifacts/artifact-test-text.txt'); +}); + +Scenario('Upload image', async () => { + testomat.artifact({ + path: 'artifacts/artifact-test-image.png', + }); +}); + +Scenario('Add Step to report', async () => { + testomat.step('This is step message'); +}); + +Scenario('Add log message to report', async () => { + testomat.log('This is log message'); + testomat.log`This is log message with template literal`; +}); diff --git a/codeceptJS/tests-usage-examples/logger.test.js b/codeceptJS/tests-usage-examples/logger.test.js new file mode 100644 index 00000000..e5600122 --- /dev/null +++ b/codeceptJS/tests-usage-examples/logger.test.js @@ -0,0 +1,20 @@ +const { logger, log } = require('@testomatio/reporter'); + +// console messages are added to report by default when import "logger" from reporter +// but to prevent unsused variable warning, you can call next code: +logger; + +Feature('Logger @Sebd746ef'); + +Scenario('Intercept console logs @Tebebb252', async () => { + console.warn('This is warning message'); +}); + +Scenario('Add own log message @T35d1887d', async () => { + const someObject = { + name: 'John', + surname: 'Doe', + age: 30, + }; + log('This is log message', someObject); +}); diff --git a/cucumber-new/features/artifact.feature b/cucumber-new/features/artifact.feature new file mode 100644 index 00000000..7acfd907 --- /dev/null +++ b/cucumber-new/features/artifact.feature @@ -0,0 +1,5 @@ +Feature: Artifact + + Scenario: Upload artifact + When I add the artifact + Then artifact should be attached to report diff --git a/cucumber-new/features/greeting.feature b/cucumber-new/features/greeting.feature index 194a2fbc..2a92e543 100644 --- a/cucumber-new/features/greeting.feature +++ b/cucumber-new/features/greeting.feature @@ -1,7 +1,5 @@ -@Sc0a5168e Feature: Greeting - @T4aad7217 Scenario: Say hello When the greeter says hello Then I should have heard "hello" diff --git a/cucumber-new/features/logger.feature b/cucumber-new/features/logger.feature new file mode 100644 index 00000000..b58ed98b --- /dev/null +++ b/cucumber-new/features/logger.feature @@ -0,0 +1,5 @@ +Feature: Logger + + Scenario: Intercept console message + When The logger intercepts console message + Then log message should be added to report diff --git a/cucumber-new/features/step.feature b/cucumber-new/features/step.feature new file mode 100644 index 00000000..ae4b325a --- /dev/null +++ b/cucumber-new/features/step.feature @@ -0,0 +1,5 @@ +Feature: Step + + Scenario: Add step to report + When I add the step + Then step should be added to report diff --git a/cucumber-new/features/step_definitions/steps.js b/cucumber-new/features/step_definitions/steps.js index ba8591ed..c82560f0 100644 --- a/cucumber-new/features/step_definitions/steps.js +++ b/cucumber-new/features/step_definitions/steps.js @@ -1,14 +1,39 @@ -import assert from "assert"; -import { When, Then } from "@cucumber/cucumber"; -import { Greeter } from "../../src/index.js"; +import assert from 'assert'; +import { When, Then } from '@cucumber/cucumber'; +import { Greeter } from '../../src/index.js'; +import { logger, testomat, log } from '@testomatio/reporter'; -When("the greeter says hello", function () { +logger; + +When('the greeter says hello', function () { this.whatIHeard = new Greeter().sayHello(); }); -Then( - "I should have heard {string}", - function (expectedResponse) { - assert.equal(this.whatIHeard, expectedResponse); - } -); +Then('I should have heard {string}', function (expectedResponse) { + assert.equal(this.whatIHeard, expectedResponse); +}); + +When('The logger intercepts console message', function () { + console.error('This is error message'); + log`This is log message`; +}); + +Then('log message should be added to report', function () { + assert.equal(true, true); +}); + +When('I add the step', function () { + testomat.step('This is step'); +}); + +Then('step should be added to report', function () { + assert.equal(true, true); +}); + +When('I add the artifact', function () { + testomat.artifact('artifacts/artifact-test-image.png'); +}); + +Then('artifact should be attached to report', function () { + assert.equal(true, true); +}); diff --git a/cucumber-new/package.json b/cucumber-new/package.json index 38aa83ac..a12469f5 100644 --- a/cucumber-new/package.json +++ b/cucumber-new/package.json @@ -11,6 +11,6 @@ "license": "ISC", "dependencies": { "@cucumber/cucumber": "^10.0.1", - "@testomatio/reporter": "file:../../reporter/testomatio-reporter-1.0.12.tgz" + "@testomatio/reporter": "latest" } } diff --git a/cucumber-new/src/index.js b/cucumber-new/src/index.js index f1fa5582..b0d37ce9 100644 --- a/cucumber-new/src/index.js +++ b/cucumber-new/src/index.js @@ -1,10 +1,5 @@ -import { testomat } from '@testomatio/reporter'; - export class Greeter { sayHello() { - testomat.artifact({ - path: 'artifacts/artifact-test-image.png', - }); return 'hello'; } } diff --git a/cucumber-new/testomatio_tmp/artifact/artifact_4aad7217 b/cucumber-new/testomatio_tmp/artifact/artifact_4aad7217 deleted file mode 100644 index 3d8846f1..00000000 --- a/cucumber-new/testomatio_tmp/artifact/artifact_4aad7217 +++ /dev/null @@ -1 +0,0 @@ -{"path":"artifacts/artifact-test-image.png"} \ No newline at end of file diff --git a/cucumber-new/testomatio_tmp/log/log_4aad7217 b/cucumber-new/testomatio_tmp/log/log_4aad7217 deleted file mode 100644 index b2963dad..00000000 --- a/cucumber-new/testomatio_tmp/log/log_4aad7217 +++ /dev/null @@ -1,5 +0,0 @@ -Say hello: PASSED  -  SUCCESS  | Total Scenarios: 1  -[TESTOMATIO] 📊 Report created. Report ID: c6e82531 -[TESTOMATIO] 📊 Report Saved. Report URL: https://beta.testomat.io/projects/cucumber-artifact/runs/c6e82531/report -[TESTOMATIO] 🗄️ 1 artifacts publicly uploaded to S3 bucket \ No newline at end of file diff --git a/jest/__tests__/artifact.test.js b/jest/__tests__/artifact.test.js deleted file mode 100644 index 0cdf5ae8..00000000 --- a/jest/__tests__/artifact.test.js +++ /dev/null @@ -1,15 +0,0 @@ -import { testomat } from '@testomatio/reporter'; - - -describe('suite name @Sf862948e', () => { - it('artifact with path as string is uploaded @Tca2c7cc5', async function () { - testomat.artifact('artifacts/artifact-test-text.txt'); - }); - - it('artifact with path as object is uploaded @Tcf89ae3e', async function () { - testomat.artifact({ - path: 'artifacts/artifact-test-image.png', - }); - }); - -}) diff --git a/jest/__tests__/logger-parallel.test.js b/jest/__tests__/logger-parallel.test.js index b0584bd8..472f3dad 100644 --- a/jest/__tests__/logger-parallel.test.js +++ b/jest/__tests__/logger-parallel.test.js @@ -1,7 +1,7 @@ import { logger } from '@testomatio/reporter'; describe('Logger test 2', function () { - it('logs should be added to the testomatio report 2 @Tf2418f0e', async function () { + it('logs should be added to the testomatio report 2', async function () { console.log('this is 2nd console log message from Jest'); logger.warn('this is 2nd logger warn message from Jest'); }); diff --git a/jest/__tests__/testomat-functions.test.js b/jest/__tests__/testomat-functions.test.js new file mode 100644 index 00000000..7e9c324c --- /dev/null +++ b/jest/__tests__/testomat-functions.test.js @@ -0,0 +1,15 @@ +import { testomat } from '@testomatio/reporter'; + +describe('suite name', () => { + it('artifact is uploaded', async function () { + testomat.artifact('artifacts/artifact-test-text.txt'); + }); + + it('log', async function () { + testomat.log`This is log message`; + }); + + it('step', async function () { + testomat.step('This is step'); + }); +}) diff --git a/mocha-ts-multi-reporters/spec/artifact.spec.ts b/mocha-ts-multi-reporters/spec/artifact.spec.ts deleted file mode 100644 index cd25cdc8..00000000 --- a/mocha-ts-multi-reporters/spec/artifact.spec.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { testomat } from '@testomatio/reporter'; - -describe('Artifacts', () => { - it('attach image @Tc4cf0c7e', () => { - testomat.artifact('artifacts/artifact-test-image.png'); - }); - - it('attach text', () => { - testomat.artifact('artifacts/artifact-test-text.txt'); - }); -}); diff --git a/mocha-ts-multi-reporters/spec/logger.spec.ts b/mocha-ts-multi-reporters/spec/logger.spec.ts new file mode 100644 index 00000000..74234156 --- /dev/null +++ b/mocha-ts-multi-reporters/spec/logger.spec.ts @@ -0,0 +1,9 @@ +import { logger, log } from '@testomatio/reporter'; + +describe('Logger test @S2f6e76f8', function () { + it('logs should be added to the testomatio report @T7f509392', async function () { + console.log('this is console log message'); + logger.warn('this is logger warn message'); + log`This is log message` + }); +}); diff --git a/mocha-ts-multi-reporters/spec/meta.spec.ts b/mocha-ts-multi-reporters/spec/meta.spec.ts deleted file mode 100644 index 4ab4c78a..00000000 --- a/mocha-ts-multi-reporters/spec/meta.spec.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { testomat } from '@testomatio/reporter'; - -describe('Meta info @Saa571da4', () => { - it('attach meta (key:value) to testrun @T75633e91', () => { - testomat.meta({ - runType: 'smoke', - browser: 'chrome', - env: 'production', - }) - }); -}); diff --git a/mocha-ts-multi-reporters/spec/testomat-functions.spec.ts b/mocha-ts-multi-reporters/spec/testomat-functions.spec.ts new file mode 100644 index 00000000..23b9b370 --- /dev/null +++ b/mocha-ts-multi-reporters/spec/testomat-functions.spec.ts @@ -0,0 +1,16 @@ +import { testomat, log, } from '@testomatio/reporter'; + +describe('Testomat functions @S43849225', () => { + it('attach image @T93dc3526', () => { + + testomat.artifact('artifacts/artifact-test-image.png'); + }); + + it('log message @T6fdfc777', () => { + log`This is log message`; + }); + + it('add step @T2fa88ab9', () => { + testomat.step('This is step message'); + }); +}); diff --git a/playwright/e2e-examples/e2e-tests/1-getting-started.spec.ts b/playwright/e2e-examples/e2e-tests/1-getting-started.spec.ts index 0de9aa56..7eb053a1 100644 --- a/playwright/e2e-examples/e2e-tests/1-getting-started.spec.ts +++ b/playwright/e2e-examples/e2e-tests/1-getting-started.spec.ts @@ -1,5 +1,4 @@ import { test, expect } from '@playwright/test'; -import { testomatioLogger } from '@testomatio/reporter'; /** * Inside every test you get a new isolated page instance. @@ -7,8 +6,6 @@ import { testomatioLogger } from '@testomatio/reporter'; * @see https://playwright.dev/docs/api/class-page */ test('basic test', async ({ page }) => { - console.log('console log message'); - testomatioLogger.warn('testomatio logger warn message'); await page.goto('https://todomvc.com/examples/vanilla-es6/'); const inputBox = page.locator('input.new-todo'); diff --git a/playwright/tests/artifacts.test.js b/playwright/tests/artifacts.test.js deleted file mode 100644 index a25411c4..00000000 --- a/playwright/tests/artifacts.test.js +++ /dev/null @@ -1,12 +0,0 @@ -import { test } from '@playwright/test'; -import { testomat } from '@testomatio/reporter'; - -test(`upload text file @T50c9c7d7`, async ({}, testInfo) => { - testomat.artifact('artifacts/artifact-test-text.txt'); -}); - -test(`upload image @Tc5c3fc3e`, async () => { - testomat.artifact({ - path: 'artifacts/artifact-test-image.png', - }); -}); diff --git a/playwright/tests/image-artifact.test.js b/playwright/tests/image-artifact.test.js new file mode 100644 index 00000000..2487a128 --- /dev/null +++ b/playwright/tests/image-artifact.test.js @@ -0,0 +1,8 @@ +import { test } from '@playwright/test'; +import { testomat } from '@testomatio/reporter'; + +test.describe('upload artifact 2', () => { + test(`upload image`, async () => { + testomat.artifact('artifacts/artifact-test-image.png'); + }); +}); diff --git a/playwright/tests/logger.test.js b/playwright/tests/logger.test.js new file mode 100644 index 00000000..79e7441c --- /dev/null +++ b/playwright/tests/logger.test.js @@ -0,0 +1,20 @@ +import { test } from '@playwright/test'; +import { logger, log } from '@testomatio/reporter'; + +logger.configure({ + level: 'info', + prettyObjects: true, +}); + +test.describe('Logger', () => { + test(`logger`, async () => { + logger.warn('testomatio logger warn message'); + console.log('console log message'); + + + }); + + test(`simple log`, async () => { + log`This is log message`; + }); +}); diff --git a/playwright/tests/multiple-artifacts.test.js b/playwright/tests/multiple-artifacts.test.js new file mode 100644 index 00000000..67dfd234 --- /dev/null +++ b/playwright/tests/multiple-artifacts.test.js @@ -0,0 +1,17 @@ +import { test } from '@playwright/test'; +import { testomat } from '@testomatio/reporter'; + +test.describe('upload artifacts 1', () => { + test.beforeEach(async () => { + console.log('beforeEach executed'); + }); + + test(`upload text file`, async () => { + testomat.artifact('artifacts/artifact-test-text.txt'); + }); + + test(`upload second text file`, async () => { + testomat.step('step 1 - login'); + testomat.artifact('artifacts/artifact-test-text2.txt'); + }); +}); diff --git a/playwright/tests/testomat-functions.test.js b/playwright/tests/testomat-functions.test.js new file mode 100644 index 00000000..d6344d89 --- /dev/null +++ b/playwright/tests/testomat-functions.test.js @@ -0,0 +1,12 @@ +import { test } from '@playwright/test'; +import { testomat } from '@testomatio/reporter'; + +test.describe('Testomat functions', () => { + test('artifact @T64346bdd', async () => { + testomat.artifact('artifacts/artifact-test-image.png'); + }); + + test('testomat log', async () => { + testomat.log`This is log message`; + }); +}); From 219cd7cdb61fac1cd05fb4f8070310cc2d94e38e Mon Sep 17 00:00:00 2001 From: Vitalii Mykhailiuk <82405549+mykhailiukVitalii@users.noreply.github.com> Date: Fri, 17 Nov 2023 10:54:19 +0200 Subject: [PATCH 55/64] [EXAMPLE CI] Playwright CI: updated number of the failed tests (#52) --- playwright/run-tests-and-check-results.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/playwright/run-tests-and-check-results.sh b/playwright/run-tests-and-check-results.sh index 21b5a509..4028431a 100644 --- a/playwright/run-tests-and-check-results.sh +++ b/playwright/run-tests-and-check-results.sh @@ -3,8 +3,8 @@ npx playwright test > test-playwright-results.txt fail_tests=$(grep -oE '\b[0-9]+\s+failed\b' test-playwright-results.txt | grep -oE '[0-9]+') echo "Number of failed tests: $fail_tests" -if [ "$fail_tests" -gt 2 ]; then - echo "More than 2 failed tests. Exiting with error." +if [ "$fail_tests" -gt 7 ]; then + echo "More than 7 failed tests. Exiting with error." exit 1 else exit 0 From fe1b09c96c65b8268b916dc160eca056231a5be6 Mon Sep 17 00:00:00 2001 From: Oleksandr Pelykh Date: Mon, 8 Jan 2024 08:31:02 +0200 Subject: [PATCH 56/64] add Codeceptjs-TYPESCRIPT project (#53) * rm codeceptjs-ts empty * init codeceptjs-typescript project * connect testomatio reporter * rm common package-json update --- codeceptJS-typescript/.gitignore | 2 ++ .../__tests__/open-google_test.ts | 6 ++++ codeceptJS-typescript/codecept.conf.ts | 30 +++++++++++++++++++ codeceptJS-typescript/package.json | 19 ++++++++++++ codeceptJS-typescript/steps.d.ts | 11 +++++++ codeceptJS-typescript/steps_file.ts | 10 +++++++ codeceptJS-typescript/tsconfig.json | 16 ++++++++++ codeceptjs-typescript | 1 - 8 files changed, 94 insertions(+), 1 deletion(-) create mode 100644 codeceptJS-typescript/.gitignore create mode 100644 codeceptJS-typescript/__tests__/open-google_test.ts create mode 100644 codeceptJS-typescript/codecept.conf.ts create mode 100644 codeceptJS-typescript/package.json create mode 100644 codeceptJS-typescript/steps.d.ts create mode 100644 codeceptJS-typescript/steps_file.ts create mode 100644 codeceptJS-typescript/tsconfig.json delete mode 160000 codeceptjs-typescript diff --git a/codeceptJS-typescript/.gitignore b/codeceptJS-typescript/.gitignore new file mode 100644 index 00000000..b56eea18 --- /dev/null +++ b/codeceptJS-typescript/.gitignore @@ -0,0 +1,2 @@ +node_modules/ +output/ \ No newline at end of file diff --git a/codeceptJS-typescript/__tests__/open-google_test.ts b/codeceptJS-typescript/__tests__/open-google_test.ts new file mode 100644 index 00000000..e21a5690 --- /dev/null +++ b/codeceptJS-typescript/__tests__/open-google_test.ts @@ -0,0 +1,6 @@ +Feature('Test feature name'); + +Scenario('test something', ({ I }) => { + I.amOnPage('https://www.google.com/'); + I.see('Google'); +}); diff --git a/codeceptJS-typescript/codecept.conf.ts b/codeceptJS-typescript/codecept.conf.ts new file mode 100644 index 00000000..181c188a --- /dev/null +++ b/codeceptJS-typescript/codecept.conf.ts @@ -0,0 +1,30 @@ +import { setHeadlessWhen, setCommonPlugins } from '@codeceptjs/configure'; +// turn on headless mode when running with HEADLESS=true environment variable +// export HEADLESS=true && npx codeceptjs run +setHeadlessWhen(process.env.HEADLESS); + +// enable all common plugins https://github.com/codeceptjs/configure#setcommonplugins +setCommonPlugins(); + +export const config: CodeceptJS.MainConfig = { + tests: './__tests__/**/*_test.ts', + output: './output', + helpers: { + Playwright: { + browser: 'chromium', + url: 'http://localhost', + show: false + } + }, + include: { + I: './steps_file' + }, + name: 'codeceptJS-typescript', + + plugins: { + testomatio: { + enabled: true, + require: '@testomatio/reporter/lib/adapter/codecept', + } + } +} \ No newline at end of file diff --git a/codeceptJS-typescript/package.json b/codeceptJS-typescript/package.json new file mode 100644 index 00000000..865ce9e8 --- /dev/null +++ b/codeceptJS-typescript/package.json @@ -0,0 +1,19 @@ +{ + "name": "codeceptjs-typescript", + "version": "1.0.0", + "description": "Example project for CodeceptJS + TypeScript", + "scripts": { + "test": "npx codeceptjs run --steps" + }, + "author": "Oleksandr Pelykh / Testomatio", + "license": "ISC", + "dependencies": { + "codeceptjs": "^3.5.11", + "playwright": "^1.40.1" + }, + "devDependencies": { + "@types/node": "^20.10.7", + "ts-node": "^10.9.2", + "typescript": "^5.3.3" + } +} diff --git a/codeceptJS-typescript/steps.d.ts b/codeceptJS-typescript/steps.d.ts new file mode 100644 index 00000000..49d8f0f8 --- /dev/null +++ b/codeceptJS-typescript/steps.d.ts @@ -0,0 +1,11 @@ +/// +type steps_file = typeof import('./steps_file'); + +declare namespace CodeceptJS { + interface SupportObject { I: I, current: any } + interface Methods extends Playwright {} + interface I extends ReturnType {} + namespace Translation { + interface Actions {} + } +} diff --git a/codeceptJS-typescript/steps_file.ts b/codeceptJS-typescript/steps_file.ts new file mode 100644 index 00000000..1083a858 --- /dev/null +++ b/codeceptJS-typescript/steps_file.ts @@ -0,0 +1,10 @@ +// in this file you can append custom step methods to 'I' object + +export = function() { + return actor({ + + // Define custom steps here, use 'this' to access default methods of I. + // It is recommended to place a general 'login' function here. + + }); +} diff --git a/codeceptJS-typescript/tsconfig.json b/codeceptJS-typescript/tsconfig.json new file mode 100644 index 00000000..a4a3e5f9 --- /dev/null +++ b/codeceptJS-typescript/tsconfig.json @@ -0,0 +1,16 @@ +{ + "ts-node": { + "files": true + }, + "compilerOptions": { + "target": "es2018", + "lib": ["es2018", "DOM"], + "esModuleInterop": true, + "module": "commonjs", + "strictNullChecks": false, + "types": ["codeceptjs", "node"], + "declaration": true, + "skipLibCheck": true + }, + "exclude": ["node_modules"] +} \ No newline at end of file diff --git a/codeceptjs-typescript b/codeceptjs-typescript deleted file mode 160000 index a6e0a0ee..00000000 --- a/codeceptjs-typescript +++ /dev/null @@ -1 +0,0 @@ -Subproject commit a6e0a0ee6a9a9813a4dcf6abeb5b0d6215b0db36 From 949b8606ce6d3638b22bc48f15051a4317d46b7b Mon Sep 17 00:00:00 2001 From: Oleksandr Pelykh Date: Wed, 10 Jan 2024 21:24:58 +0200 Subject: [PATCH 57/64] Upgrade jest version (#54) * rm codeceptjs-ts empty * init codeceptjs-typescript project * connect testomatio reporter * rm common package-json update * upgrade jest version --- jest/jest.config.js | 2 ++ jest/package.json | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/jest/jest.config.js b/jest/jest.config.js index 6ad480ba..17e9c53d 100644 --- a/jest/jest.config.js +++ b/jest/jest.config.js @@ -1,3 +1,5 @@ +require('dotenv').config(); + module.exports = { testEnvironment: "node", reporters: ['default', ['@testomatio/reporter/lib/adapter/jest.js', { apiKey: process.env.TESTOMATIO }]], diff --git a/jest/package.json b/jest/package.json index 0e56f7fb..86767195 100644 --- a/jest/package.json +++ b/jest/package.json @@ -8,6 +8,7 @@ "dependencies": { "@testomatio/reporter": "latest", "check-tests": "latest", + "jest": "^29.7.0", "jest-junit": "^13.2.0" }, "devDependencies": { @@ -17,8 +18,7 @@ "eslint": "^6.7.2", "eslint-config-airbnb": "^18.0.1", "eslint-config-airbnb-base": "^14.0.0", - "eslint-plugin-import": "^2.18.2", - "jest": "^24.9.0" + "eslint-plugin-import": "^2.18.2" }, "scripts": { "test": "jest" From 35b5c2c7899aaee89d06be2e141c7d24038cf1c1 Mon Sep 17 00:00:00 2001 From: Oleksandr Pelykh Date: Wed, 17 Jan 2024 21:33:41 +0200 Subject: [PATCH 58/64] Remove cypress plugin usage duplication (#56) * update testrun script for cypress * remove cypress plugin usage duplication --- cypress/cypress.config.js | 5 +++-- cypress/cypress/plugins/index.js | 4 ++-- cypress/package.json | 8 ++++++-- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/cypress/cypress.config.js b/cypress/cypress.config.js index 684bf070..3d0fb259 100644 --- a/cypress/cypress.config.js +++ b/cypress/cypress.config.js @@ -5,8 +5,9 @@ module.exports = defineConfig({ // We've imported your old cypress plugins here. // You may want to clean this up later by importing these. setupNodeEvents(on, config) { - require('@testomatio/reporter/lib/adapter/cypress-plugin')(on, config); - return require('./cypress/plugins/index.js')(on, config) + // require('@testomatio/reporter/lib/adapter/cypress-plugin')(on, config); + // return require('./cypress/plugins/index.js')(on, config) + return require('@testomatio/reporter/lib/adapter/cypress-plugin')(on, config) }, }, }) diff --git a/cypress/cypress/plugins/index.js b/cypress/cypress/plugins/index.js index c3ec1bc5..5601b0ce 100644 --- a/cypress/cypress/plugins/index.js +++ b/cypress/cypress/plugins/index.js @@ -12,7 +12,7 @@ // This function is called when a project is opened or re-opened (e.g. due to // the project's config changing) -const testomatioReporter = require('@testomatio/reporter/lib/adapter/cypress-plugin'); +// const testomatioReporter = require('@testomatio/reporter/lib/adapter/cypress-plugin'); /** * @type {Cypress.PluginConfig} @@ -21,7 +21,7 @@ module.exports = (on, config) => { // `on` is used to hook into various events Cypress emits // `config` is the resolved Cypress config - testomatioReporter(on, config); + // testomatioReporter(on, config); return config; }; diff --git a/cypress/package.json b/cypress/package.json index 4b8167d1..c875cc3d 100644 --- a/cypress/package.json +++ b/cypress/package.json @@ -4,9 +4,13 @@ "description": "Running and Monitoring Cypress tests with TESTOMATIO Reporter", "main": "index.js", "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" + "test": "npx cypress run" }, - "keywords": ["e2e", "testomatio", "cypress"], + "keywords": [ + "e2e", + "testomatio", + "cypress" + ], "author": "davert, mihaylukvv", "license": "ISC", "devDependencies": { From 209ba31605bf724a77ebc72bf58bcfa8ab996cbf Mon Sep 17 00:00:00 2001 From: Oleksandr Pelykh Date: Fri, 19 Jan 2024 17:01:26 +0200 Subject: [PATCH 59/64] update testomat object import for all projects (#57) --- codeceptJS/tests-usage-examples/functions.test.js | 2 +- cucumber-new/features/step_definitions/steps.js | 2 +- jest/__tests__/testomat-functions.test.js | 2 +- mocha-ts-multi-reporters/spec/testomat-functions.spec.ts | 2 +- playwright/tests/image-artifact.test.js | 2 +- playwright/tests/multiple-artifacts.test.js | 2 +- playwright/tests/testomat-functions.test.js | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/codeceptJS/tests-usage-examples/functions.test.js b/codeceptJS/tests-usage-examples/functions.test.js index fe151728..29e8a378 100644 --- a/codeceptJS/tests-usage-examples/functions.test.js +++ b/codeceptJS/tests-usage-examples/functions.test.js @@ -1,4 +1,4 @@ -const { testomat } = require('@testomatio/reporter'); +const testomat = require('@testomatio/reporter'); Feature('Testomat functions'); diff --git a/cucumber-new/features/step_definitions/steps.js b/cucumber-new/features/step_definitions/steps.js index c82560f0..7321d4a7 100644 --- a/cucumber-new/features/step_definitions/steps.js +++ b/cucumber-new/features/step_definitions/steps.js @@ -1,7 +1,7 @@ import assert from 'assert'; import { When, Then } from '@cucumber/cucumber'; import { Greeter } from '../../src/index.js'; -import { logger, testomat, log } from '@testomatio/reporter'; +import testomat, { logger, log } from '@testomatio/reporter'; logger; diff --git a/jest/__tests__/testomat-functions.test.js b/jest/__tests__/testomat-functions.test.js index 7e9c324c..f56ff52e 100644 --- a/jest/__tests__/testomat-functions.test.js +++ b/jest/__tests__/testomat-functions.test.js @@ -1,4 +1,4 @@ -import { testomat } from '@testomatio/reporter'; +import testomat from '@testomatio/reporter'; describe('suite name', () => { it('artifact is uploaded', async function () { diff --git a/mocha-ts-multi-reporters/spec/testomat-functions.spec.ts b/mocha-ts-multi-reporters/spec/testomat-functions.spec.ts index 23b9b370..2dfae297 100644 --- a/mocha-ts-multi-reporters/spec/testomat-functions.spec.ts +++ b/mocha-ts-multi-reporters/spec/testomat-functions.spec.ts @@ -1,4 +1,4 @@ -import { testomat, log, } from '@testomatio/reporter'; +import testomat, { log } from '@testomatio/reporter'; describe('Testomat functions @S43849225', () => { it('attach image @T93dc3526', () => { diff --git a/playwright/tests/image-artifact.test.js b/playwright/tests/image-artifact.test.js index 2487a128..ed67f351 100644 --- a/playwright/tests/image-artifact.test.js +++ b/playwright/tests/image-artifact.test.js @@ -1,5 +1,5 @@ import { test } from '@playwright/test'; -import { testomat } from '@testomatio/reporter'; +import testomat from '@testomatio/reporter'; test.describe('upload artifact 2', () => { test(`upload image`, async () => { diff --git a/playwright/tests/multiple-artifacts.test.js b/playwright/tests/multiple-artifacts.test.js index 67dfd234..d35521a1 100644 --- a/playwright/tests/multiple-artifacts.test.js +++ b/playwright/tests/multiple-artifacts.test.js @@ -1,5 +1,5 @@ import { test } from '@playwright/test'; -import { testomat } from '@testomatio/reporter'; +import testomat from '@testomatio/reporter'; test.describe('upload artifacts 1', () => { test.beforeEach(async () => { diff --git a/playwright/tests/testomat-functions.test.js b/playwright/tests/testomat-functions.test.js index d6344d89..7d63876d 100644 --- a/playwright/tests/testomat-functions.test.js +++ b/playwright/tests/testomat-functions.test.js @@ -1,5 +1,5 @@ import { test } from '@playwright/test'; -import { testomat } from '@testomatio/reporter'; +import testomat from '@testomatio/reporter'; test.describe('Testomat functions', () => { test('artifact @T64346bdd', async () => { From ceccb30d69417f8d936fe06358e4fbb49b13f7ff Mon Sep 17 00:00:00 2001 From: Vitalii Mykhailiuk <82405549+mykhailiukVitalii@users.noreply.github.com> Date: Mon, 22 Jan 2024 16:02:01 +0200 Subject: [PATCH 60/64] [ISSUE-299] fix codeceptjs goto() url (#59) * [ISSUE-299] fix basic url + small codeceptJS test updates * [ISSUE-299] Number of failed tests = 12 * [ISSUE-299] fix codeceptjs-cucmber tests too --- codeceptJS/todomvc-tests/pages/todos.page.js | 12 +++++++----- codeceptJS/todomvc-tests/todo-mvc_test.js | 2 +- .../todomvc-tests/pages/todos.page.js | 6 +++--- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/codeceptJS/todomvc-tests/pages/todos.page.js b/codeceptJS/todomvc-tests/pages/todos.page.js index d2377a11..717df9b0 100644 --- a/codeceptJS/todomvc-tests/pages/todos.page.js +++ b/codeceptJS/todomvc-tests/pages/todos.page.js @@ -8,13 +8,13 @@ const I = actor(); // const nthTodoItem = nth => `.todo-list li:nth-child(${nth})` // ({ xpath: `(//*[contains(@class,"todo-list")]/li)[${nth}]`}) const nthTodoCheckbox = nth => locate('div > input').inside(`.todo-list li:nth-child(${nth})`) -const nthTTodoDeleteButton = nth => locate('div > button').inside(`.todo-list li:nth-child(${nth})`).as(`${nth}nth delete button`) -const nthTodoEditField = nth => locate('form > input').inside(`.todo-list li:nth-child(${nth})`).as(`${nth}nth todo input`) +const nthTTodoDeleteButton = nth => locate('div > button.destroy').inside(`.todo-list li:nth-child(${nth})`).as(`${nth}nth delete button`) +const nthTodoEditField = nth => locate('input#edit-todo-input').inside(`.todo-list li:nth-child(${nth})`).as(`${nth}nth todo input`) const nthTodoItem = nth => locate('.todo-list li').at(nth).as(`${nth} todo item`) module.exports = { goto() { - I.amOnPage('http://todomvc.com/examples/angularjs/#/') + I.amOnPage('https://todomvc.com/examples/angular/dist/browser/#/all') I.refreshPage() I.executeScript(() => sessionStorage.clear()) I.executeScript(() => console.error('Boom!')) @@ -33,7 +33,9 @@ module.exports = { }, async markNthAsCompleted(nthTodo) { - const classNames = await I.grabAttributeFrom(nthTodoItem(nthTodo), 'class') + let classNames = await I.grabAttributeFrom(nthTodoItem(nthTodo), 'class') + if (!classNames) classNames = []; + assert(classNames.indexOf('completed') < 0, 'Expected todo to be not already marked as completed') I.click(nthTodoCheckbox(nthTodo)) }, @@ -86,7 +88,7 @@ module.exports = { todos = [todos] } - assert(todos[nthTodo - 1] === 1, `Expected "${todo}" but got "${todos[nthTodo - 1]}"`) + assert(todos[nthTodo - 1] === todo, `Expected "${todo}" but got "${todos[nthTodo - 1]}"`) return todos }, diff --git a/codeceptJS/todomvc-tests/todo-mvc_test.js b/codeceptJS/todomvc-tests/todo-mvc_test.js index 68b372d7..6061c0ee 100644 --- a/codeceptJS/todomvc-tests/todo-mvc_test.js +++ b/codeceptJS/todomvc-tests/todo-mvc_test.js @@ -1,7 +1,7 @@ Feature('codepress demo') Before(async ({ I }) => { - I.amOnPage('http://todomvc.com/examples/angularjs/#/') + I.amOnPage('https://todomvc.com/examples/angular/dist/browser/#/all') console.log("Test Before hoooks quick test"); diff --git a/codeceptjs-cucumber/todomvc-tests/pages/todos.page.js b/codeceptjs-cucumber/todomvc-tests/pages/todos.page.js index 740056fc..cbcb497c 100644 --- a/codeceptjs-cucumber/todomvc-tests/pages/todos.page.js +++ b/codeceptjs-cucumber/todomvc-tests/pages/todos.page.js @@ -8,13 +8,13 @@ const I = actor(); // const nthTodoItem = nth => `.todo-list li:nth-child(${nth})` // ({ xpath: `(//*[contains(@class,"todo-list")]/li)[${nth}]`}) const nthTodoCheckbox = nth => locate('div > input').inside(`.todo-list li:nth-child(${nth})`) -const nthTTodoDeleteButton = nth => locate('div > button').inside(`.todo-list li:nth-child(${nth})`).as(`${nth}nth delete button`) -const nthTodoEditField = nth => locate('form > input').inside(`.todo-list li:nth-child(${nth})`).as(`${nth}nth todo input`) +const nthTTodoDeleteButton = nth => locate('div > button.destroy').inside(`.todo-list li:nth-child(${nth})`).as(`${nth}nth delete button`) +const nthTodoEditField = nth => locate('input#edit-todo-input').inside(`.todo-list li:nth-child(${nth})`).as(`${nth}nth todo input`) const nthTodoItem = nth => locate('.todo-list li').at(nth).as(`${nth} todo item`) module.exports = { goto() { - I.amOnPage('http://todomvc.com/examples/angularjs/#/') + I.amOnPage('https://todomvc.com/examples/angular/dist/browser/#/all') I.refreshPage() I.executeScript(() => sessionStorage.clear()) I.executeScript(() => console.error('Boom!')) From 623ca5e33aa2bb1a24c9535e7901261b0b18bafa Mon Sep 17 00:00:00 2001 From: Oleksandr Pelykh Date: Wed, 31 Jan 2024 19:12:29 +0200 Subject: [PATCH 61/64] Revert "[EXAMPLES] remmove WDIO & protrator examples(old) (#42)" (#61) This reverts commit dd7f51bfba2d8c70cf23701095d21e7c780245b1. --- protractor/.gitignore | 5 + protractor/.jshintrc | 3 + protractor/.npmrc | 1 + protractor/README.md | 52 ++ protractor/conf.js | 61 +++ protractor/data/userData.js | 4 + protractor/flake | 23 + protractor/package.json | 39 ++ protractor/pages/basePage.js | 135 +++++ protractor/pages/friendPage.js | 63 +++ protractor/pages/githubPage.js | 14 + protractor/pages/nonAngularLoginPage.js | 39 ++ protractor/pages/qsHomePage.js | 62 +++ protractor/pages/searchPage.js | 28 + protractor/specs/friend_spec.js | 42 ++ protractor/specs/nonAngularLogin_spec.js | 27 + protractor/specs/qsHomepage_spec.js | 32 ++ protractor/specs/qsSearch_spec.js | 23 + webdriverio-mocha/.gitignore | 6 + webdriverio-mocha/README.md | 69 +++ webdriverio-mocha/package.json | 55 ++ webdriverio-mocha/support/utils/Utilities.js | 24 + .../support/utils/teamsReporter.js | 90 +++ webdriverio-mocha/test/pages/ajax.page.js | 20 + webdriverio-mocha/test/pages/click.page.js | 20 + .../pages/components/home_modal.component.js | 8 + webdriverio-mocha/test/pages/home.page.js | 15 + webdriverio-mocha/test/pages/page.js | 15 + webdriverio-mocha/test/pages/progress.page.js | 25 + .../test/pages/textInput.page.js | 23 + webdriverio-mocha/test/specs/ajax.spec.js | 12 + webdriverio-mocha/test/specs/click.spec.js | 11 + webdriverio-mocha/test/specs/home.spec.js | 14 + webdriverio-mocha/test/specs/progress.spec.js | 33 ++ webdriverio-mocha/test/specs/sample.spec.js | 17 + .../test/specs/textInput.spec.js | 14 + webdriverio-mocha/wdio.conf.js | 512 ++++++++++++++++++ 37 files changed, 1636 insertions(+) create mode 100644 protractor/.gitignore create mode 100644 protractor/.jshintrc create mode 100644 protractor/.npmrc create mode 100644 protractor/README.md create mode 100644 protractor/conf.js create mode 100644 protractor/data/userData.js create mode 100755 protractor/flake create mode 100644 protractor/package.json create mode 100644 protractor/pages/basePage.js create mode 100644 protractor/pages/friendPage.js create mode 100644 protractor/pages/githubPage.js create mode 100644 protractor/pages/nonAngularLoginPage.js create mode 100644 protractor/pages/qsHomePage.js create mode 100644 protractor/pages/searchPage.js create mode 100644 protractor/specs/friend_spec.js create mode 100644 protractor/specs/nonAngularLogin_spec.js create mode 100644 protractor/specs/qsHomepage_spec.js create mode 100644 protractor/specs/qsSearch_spec.js create mode 100644 webdriverio-mocha/.gitignore create mode 100644 webdriverio-mocha/README.md create mode 100644 webdriverio-mocha/package.json create mode 100644 webdriverio-mocha/support/utils/Utilities.js create mode 100644 webdriverio-mocha/support/utils/teamsReporter.js create mode 100644 webdriverio-mocha/test/pages/ajax.page.js create mode 100644 webdriverio-mocha/test/pages/click.page.js create mode 100644 webdriverio-mocha/test/pages/components/home_modal.component.js create mode 100644 webdriverio-mocha/test/pages/home.page.js create mode 100644 webdriverio-mocha/test/pages/page.js create mode 100644 webdriverio-mocha/test/pages/progress.page.js create mode 100644 webdriverio-mocha/test/pages/textInput.page.js create mode 100644 webdriverio-mocha/test/specs/ajax.spec.js create mode 100644 webdriverio-mocha/test/specs/click.spec.js create mode 100644 webdriverio-mocha/test/specs/home.spec.js create mode 100644 webdriverio-mocha/test/specs/progress.spec.js create mode 100644 webdriverio-mocha/test/specs/sample.spec.js create mode 100644 webdriverio-mocha/test/specs/textInput.spec.js create mode 100644 webdriverio-mocha/wdio.conf.js diff --git a/protractor/.gitignore b/protractor/.gitignore new file mode 100644 index 00000000..e4b4c243 --- /dev/null +++ b/protractor/.gitignore @@ -0,0 +1,5 @@ +/.idea +/node_modules +.DS_Store +specs/test* +package-lock.json \ No newline at end of file diff --git a/protractor/.jshintrc b/protractor/.jshintrc new file mode 100644 index 00000000..cade3f63 --- /dev/null +++ b/protractor/.jshintrc @@ -0,0 +1,3 @@ +{ + "esversion": 6 +} \ No newline at end of file diff --git a/protractor/.npmrc b/protractor/.npmrc new file mode 100644 index 00000000..9cf94950 --- /dev/null +++ b/protractor/.npmrc @@ -0,0 +1 @@ +package-lock=false \ No newline at end of file diff --git a/protractor/README.md b/protractor/README.md new file mode 100644 index 00000000..77f9b644 --- /dev/null +++ b/protractor/README.md @@ -0,0 +1,52 @@ +This repo contains tests for Protractor application with Testomat.io plugins + +# Installation + +This is a playground for your first steps in testing, so instead of installing it from NPM it is recommended to clone it from repo instead. + +1) Clone this repository + +``` +git clone git@github.com:testomatio/examples.git && cd examples/protractor +``` + +2) Install dependencies via npm: + +``` +npm i +``` + +This will install protractor & Testomat.io reporter + +## Loading Tests to Testomat.io + +1. Create empty project in Testomat.io +2. Obtain API key from Testomat.io +2. Run `npx check-tests` to upload tests data into testomat.io. Pass api key as `TESTOMATIO` environment variable: + +``` +TESTOMATIO={apiKey} npx check-tests@latest Protractor "**/*{.,_}{test,spec}.js +``` + +> **Environment variables** It is recommended to store Testomatio API Key as environment variable and never save it in the source code. Set them directly when running tests or use [dotenv](https://www.npmjs.com/package/dotenv) package to save environment variable in a file and load them for tests. + +## Publishing Test Results to Testomat.io + +Get API key from a project in Testomat.io and set it as environment variable `TESTOMATIO`: + +``` +TESTOMATIO={apiKey} npx @testomatio/reporter@latest -c 'npx protractor conf.js' +``` + +### Configuration + +Testomatio reporter is a plugin and should be enabled in `conf.js`: + +Do not hard code apiKey and always use it as environment variable. + +```js +onPrepare: () => { + const TestomatioReporter = require('@testomatio/reporter/lib/adapter/jasmine'); + jasmine.getEnv().addReporter(new TestomatioReporter({ apiKey: process.env.TESTOMATIO })); +}, +``` diff --git a/protractor/conf.js b/protractor/conf.js new file mode 100644 index 00000000..95940059 --- /dev/null +++ b/protractor/conf.js @@ -0,0 +1,61 @@ +// solves `SyntaxError: Unexpected token import` +require("babel-register")({ + presets: [ 'es2015' ] +}); + + + +exports.config = { + /** + * Uncomment ONE of the following to connect to: seleniumServerJar OR directConnect. Protractor + * will auto-start selenium if you uncomment the jar, or connect directly to chrome/firefox + * if you uncomment directConnect. + */ + //seleniumServerJar: "node_modules/protractor/node_modules/webdriver-manager/selenium/selenium-server-standalone-3.4.0.jar", + directConnect: true, + SELENIUM_PROMISE_MANAGER: false, + + specs: ['specs/*spec.js'], + baseUrl: 'https://qualityshepherd.com', + framework: 'jasmine', + + onPrepare: () => { + const SpecReporter = require('jasmine-spec-reporter').SpecReporter; + jasmine.getEnv().addReporter(new SpecReporter({ + spec: { + displayStacktrace: true + } + })); + const JasmineReporter = require('@testomatio/reporter/lib/adapter/jasmine'); + jasmine.getEnv().addReporter(new JasmineReporter({ apiKey: process.env.TESTOMATIO })); + }, + + capabilities: { + browserName: 'chrome', + shardTestFiles: true, + maxInstances: 2, + chromeOptions: { + args: [ + // disable chrome's wakiness + '--disable-infobars', + '--disable-extensions', + 'verbose', + 'log-path=/tmp/chromedriver.log' + ], + prefs: { + // disable chrome's annoying password manager + 'profile.password_manager_enabled': false, + 'credentials_enable_service': false, + 'password_manager_enabled': false + } + } + }, + + jasmineNodeOpts: { + showColors: true, + displaySpecDuration: true, + // overrides jasmine's print method to report dot syntax for custom reports + print: () => {}, + defaultTimeoutInterval: 50000 + } +}; \ No newline at end of file diff --git a/protractor/data/userData.js b/protractor/data/userData.js new file mode 100644 index 00000000..e0354ffb --- /dev/null +++ b/protractor/data/userData.js @@ -0,0 +1,4 @@ +// store user data in object for ease of use and readability... +export default { + testUser : {'username': 'test', 'password': 'test'}, +}; diff --git a/protractor/flake b/protractor/flake new file mode 100755 index 00000000..887fb2d7 --- /dev/null +++ b/protractor/flake @@ -0,0 +1,23 @@ +#!/usr/bin/env node + +/** + * flake is a node script that uses protractor-flake to re-run failed tests. Note + * that it reruns tests at the _file_ level, so if one test fails, it will rerun all + * the tests in that file. Still... awesome. + * + * usage: + * `./flake conf.js [other protractor args]` + */ + +const protractorFlake = require('protractor-flake'); +// skip first two passed args (node and self) +let protractorArgs = process.argv.splice(2); + +protractorFlake({ + protractorPath: 'node_modules/.bin/protractor', + maxAttempts: 2, + nodeBin: 'node', + protractorArgs: protractorArgs +}, (status, output) => { + process.exit(status); +}); \ No newline at end of file diff --git a/protractor/package.json b/protractor/package.json new file mode 100644 index 00000000..9f156042 --- /dev/null +++ b/protractor/package.json @@ -0,0 +1,39 @@ +{ + "name": "protractor_example", + "version": "2.0.0", + "description": "An example Protractor project that makes use of page objects...", + "engines": { + "node": "8.12.0" + }, + "scripts": { + "test": "npx @testomatio/reporter@latest -c 'npx protractor conf.js'" + }, + "repository": { + "type": "git", + "url": "https://github.com/qualityshepherd/protractor_example.git" + }, + "keywords": [ + "protractor", + "page objects", + "tests", + "automation", + "webdriver" + ], + "author": "Brine", + "license": "ISC", + "bugs": { + "url": "https://github.com/qualityshepherd/protractor_example/issues" + }, + "homepage": "https://github.com/qualityshepherd/protractor_example", + "dependencies": { + "@testomatio/reporter": "latest", + "chance": "1.0.16", + "jasmine-spec-reporter": "4.2.1", + "protractor": "7.0.0", + "protractor-flake": "4.0.0" + }, + "devDependencies": { + "babel-preset-es2015": "^6.24.1", + "babel-register": "^6.24.1" + } +} diff --git a/protractor/pages/basePage.js b/protractor/pages/basePage.js new file mode 100644 index 00000000..97355b6d --- /dev/null +++ b/protractor/pages/basePage.js @@ -0,0 +1,135 @@ + +export default class BasePage { + constructor() { + /** + * wrap this.timeout. (ms) in t-shirt sizes + */ + this.timeout = { + 'xs': 420, + 's' : 1000, + 'm' : 2000, + 'l' : 5000, + 'xl': 9000, + 'xxl': 15000 + }; + + /** + * get an element's width + * extends protractor's ElementFinder + * @return {int} - the width of the element + */ + protractor.ElementFinder.prototype.getWidth = async function() { + return await this.getSize().then(size => { + return size.width; + }); + }; + } + + /** + * wait and verify that a page is loaded + * @returns {promise} + * @requires a page to include `pageLoaded` method + */ + async loaded() { + return browser.wait(async () => { + return await this.pageLoaded(); + }, this.timeout.xl, 'timeout: waiting for page to load. The url is: ' + this.url); + } + + /** + * navigate to a page via it's `url` var + * and verify/wait via loaded() + * @requires page have both `url` and `pageLoaded` properties + */ + async goto() { + await browser.get(this.url, this.timeout.xl); + return await this.loaded(); + } + + /** + * wait and then click an element + * @param {obj} element + */ + async waitAndClick(element) { + await this.isClickable(element); + await element.click(); + } + + /** + * Wrappers for expected conditions + * I find ECs to be poorly named, so we wrap them in methods + * that are 9% more sexy, and allow us to add logging, etc... + * @returns {ExpectedCondition} + */ + isVisible(locator) { + return protractor.ExpectedConditions.visibilityOf(locator); + } + + isNotVisible(locator) { + return protractor.ExpectedConditions.invisibilityOf(locator); + } + + inDom(locator) { + return protractor.ExpectedConditions.presenceOf(locator); + } + + notInDom(locator) { + return protractor.ExpectedConditions.stalenessOf(locator); + } + + isClickable(locator) { + return protractor.ExpectedConditions.elementToBeClickable(locator); + } + + hasText(locator, text) { + return protractor.ExpectedConditions.textToBePresentInElement(locator, text); + } + + and(arrayOfFunctions) { + return protractor.ExpectedConditions.and(arrayOfFunctions); + } + + titleIs(title) { + return protractor.ExpectedConditions.titleIs(title); + } + + /** + * test if an element has a class + * @param {elementFinder} locator - eg. $('div#myId') + * @param {string} klass - class name + * @return {Boolean} - does the element have the class? + */ + hasClass(locator, klass) { + return locator.getAttribute('class').then(classes => { + return classes.split(' ').indexOf(klass) !== -1; + }); + } + + /** + * Webdriver equivalent to hitting Enter/Return key. + */ + async hitEnter() { + await browser.actions().sendKeys(protractor.Key.ENTER).perform(); + } + + /** + * switches focus to a new (last) window + */ + async switchToNewWindow() { + await browser.getAllWindowHandles().then(handles => { + browser.switchTo().window(handles[handles.length - 1]); + }); + } + + /** + * close the current window and switch to its parent window + * @param {obj} parentPage - the parent page object we want to load + */ + async closeNewWindow() { + await browser.getAllWindowHandles().then(handles => { + browser.close(); + // the parent should be 2 less than the length of all found window handlers + browser.switchTo().window(handles.length - 2); + }); + } +} \ No newline at end of file diff --git a/protractor/pages/friendPage.js b/protractor/pages/friendPage.js new file mode 100644 index 00000000..1b5ad1ce --- /dev/null +++ b/protractor/pages/friendPage.js @@ -0,0 +1,63 @@ +import BasePage from './basePage'; + +class FriendsPage extends BasePage { + constructor() { + super(); + this.searchBox = element(by.model('search')); + this.addnameBox = element(by.model('addName')); + this.addButton = element(by.buttonText('+ add')); + this.actualCount = $('em.ng-binding'); + this.deleteButton = $('i.icon-trash'); + this.deleteButtons = $$('i.icon-trash'); + this.friendName = text => { return element.all(by.cssContainingText('td.ng-binding', text)); }; + // results... + this.rows = element.all(by.repeater('row in rows')); + this.names = element.all(by.repeater('row in rows').column('{{row}}')); + + this.url = 'angular/friends/'; + this.pageLoaded = this.isClickable($('h2.ng-binding')); + } + + /** + * search for a friend + * @param {string} string + * @return {promise} + */ + searchFor(string) { + return this.searchBox.sendKeys(string); + } + + /** + * add a friend + * @param {string} name + * @return {promise} + */ + addFriend(name) { + this.addnameBox.sendKeys(name); + return this.addButton.click(); + } + + /** + * find a friend in search results + * @param {string} name - name to find + * @return {bool} + */ + inResults(name) { + return this.friendName(name).then(found => { + return found.length > 0; + }); + } + + /** + * delete all friends + */ + async deleteAllFriends() { + await this.deleteButtons.count().then(async count => { + while(count > 0) { + await this.deleteButtons.get(0).click(); + count--; + } + }); + } +} +export default new FriendsPage(); \ No newline at end of file diff --git a/protractor/pages/githubPage.js b/protractor/pages/githubPage.js new file mode 100644 index 00000000..b215b085 --- /dev/null +++ b/protractor/pages/githubPage.js @@ -0,0 +1,14 @@ +// page is non-angular +browser.ignoreSynchronization = true; +import BasePage from './basePage'; + +class GithubPage extends BasePage { + constructor() { + super(); + this.username = $('.vcard-names'); + + this.url = 'https://github.com/qualityshepherd'; + this.pageLoaded = this.isVisible(this.username); + } +} +export default new GithubPage(); \ No newline at end of file diff --git a/protractor/pages/nonAngularLoginPage.js b/protractor/pages/nonAngularLoginPage.js new file mode 100644 index 00000000..18a8247d --- /dev/null +++ b/protractor/pages/nonAngularLoginPage.js @@ -0,0 +1,39 @@ +// page is non-angular +browser.ignoreSynchronization = true; +import BasePage from './basePage'; + +class LoginPage extends BasePage { + constructor() { + super(); + this.userInput = element(by.name('user')); + this.passInput = element(by.name('pass')); + this.loginButton = $('.login'); + this.errorMessage = $('div#errorMessage'); + + this.url = 'angular'; + this.pageLoaded = this.inDom($('#page')); + } + + /** + * convenience wrapper for login that allows you to login via + * role object. eg. loginAs(admin) + * @param {obj} userObj - user data object + * @return {promise} + */ + loginAs(userObj) { + return this.login(userObj.username, userObj.password); + } + + /** + * non-angular login + * @param {string} user + * @param {string} pass + * @return {promise} + */ + login(user, pass) { + this.userInput.sendKeys(user); + this.passInput.sendKeys(pass); + return this.loginButton.click(); + } +} +export default new LoginPage(); \ No newline at end of file diff --git a/protractor/pages/qsHomePage.js b/protractor/pages/qsHomePage.js new file mode 100644 index 00000000..2334cbec --- /dev/null +++ b/protractor/pages/qsHomePage.js @@ -0,0 +1,62 @@ +// page is non-angular +browser.ignoreSynchronization = true; +import BasePage from './basePage'; + +class QsHomePage extends BasePage { + constructor() { + super(); + + this.posts = $$('div.post'); + this.postTitleLinks = $$('h2 a'); + this.siteTitle = $('h1'); + // social media links.... + this.githubLink = $('#github-social'); + // pagination + this.prevPageLink = $('.button.next'); + + this.url = 'http://qualityshepherd.com'; + // pageLoaded is used by `.loaded()` to test that we're on a page + this.pageLoaded = this.and( + this.hasText(this.siteTitle, 'Quality Shepherd'), + this.isClickable(this.postTitleLinks.first()) + ); + } + + /** + * check if a post title exists + * @param {string} postTitle + * @return {bool} + */ + postTitleExists(postTitle) { + return element(by.cssContainingText('a', postTitle)).isPresent(); + } + + /** + * Page back till we find the post title + * or run out of previous posts + * @param {string} postTitle + * @return {bool} + */ + async findPostByPaging(postTitle) { + return await this.postTitleExists(postTitle).then(found => { + if(found) { + // found it! + return true; + } else { + // prevPageLink not displayed on first page + return this.prevPageLink.isPresent().then(async yup => { + if(yup) { + await this.prevPageLink.click(); + await this.findPostByPaging(postTitle); // call recursively till found... + // wait for page to load... + await this.loaded(); + } else { + // post not found + return false; + } + }); + } + }); + } +} +export default new QsHomePage(); \ No newline at end of file diff --git a/protractor/pages/searchPage.js b/protractor/pages/searchPage.js new file mode 100644 index 00000000..e4805935 --- /dev/null +++ b/protractor/pages/searchPage.js @@ -0,0 +1,28 @@ +import BasePage from './basePage'; + +class SearchPage extends BasePage { + constructor() { + super(); + this.siteTitle = $('h2 a') + this.searchInput = $('#search_input'); + this.results = $$('.search-result'); + this.noResultsMsg = $('#no-results'); + + this.url = '?search'; + this.pageLoaded = this.and( + this.hasText(this.siteTitle, 'Search'), + this.isClickable(this.searchInput) + ); + } + + /** + * Search blog posts + * @param {string} + * @return {promise} + */ + async searchFor(text) { + await this.searchInput.sendKeys(text); + this.hitEnter(); + } +} +export default new SearchPage(); \ No newline at end of file diff --git a/protractor/specs/friend_spec.js b/protractor/specs/friend_spec.js new file mode 100644 index 00000000..0e58b159 --- /dev/null +++ b/protractor/specs/friend_spec.js @@ -0,0 +1,42 @@ +import friendPage from '../pages/friendPage'; +import Chance from 'chance'; +const chance = new Chance(); +const EXISTING_NAME = 'Paul'; + +describe ('Angular App', () => { + beforeEach(async () => { + await friendPage.goto(); + }); + + it('should add a new friend', async () => { + const FRIEND_NAME = chance.first(); // random first name + await friendPage.addFriend(FRIEND_NAME); + + expect(await friendPage.inResults(FRIEND_NAME)).toBe(true); + }); + + it('should not display non-found search terms', async () => { + await friendPage.searchFor('poo!!!'); + + expect(await friendPage.rows.count()).toBe(0); + }); + + it('should display found search terms', async () => { + await friendPage.searchFor(EXISTING_NAME); + + expect(await friendPage.inResults(EXISTING_NAME)).toBe(true); + }); + + it('should display no rows when all friends deleted', async () => { + await friendPage.deleteAllFriends(); + await friendPage.loaded(); // protect against false positives... + + expect(await friendPage.rows.count()).toBe(0); + }); + + it('should display actual count before saving new friend', async () => { + await friendPage.addnameBox.sendKeys('Some text...'); + + expect(await friendPage.actualCount.getText()).toEqual('(only 3 actually....)'); + }); +}); diff --git a/protractor/specs/nonAngularLogin_spec.js b/protractor/specs/nonAngularLogin_spec.js new file mode 100644 index 00000000..93aec040 --- /dev/null +++ b/protractor/specs/nonAngularLogin_spec.js @@ -0,0 +1,27 @@ +import loginPage from '../pages/nonAngularLoginPage'; +import friendPage from '../pages/friendPage'; +import userData from '../data/userData'; + +describe ('Non-Angular Login', () => { + beforeEach(async () => { + await loginPage.goto(); + }); + + it('should display message for invalid credentials', async () => { + await loginPage.login('invalid_user', 'invalid_password'); + + expect(await loginPage.errorMessage.isDisplayed()).toBe(true); + }); + + it('should display message for empty credentials', async () => { + await loginPage.login('', ''); + + expect(await loginPage.errorMessage.isDisplayed()).toBe(true); + }); + + it('should goto friend pages on successful login', async () => { + await loginPage.loginAs(userData.testUser); + + expect(await friendPage.loaded()).toBe(true); + }); +}); \ No newline at end of file diff --git a/protractor/specs/qsHomepage_spec.js b/protractor/specs/qsHomepage_spec.js new file mode 100644 index 00000000..ad3987a4 --- /dev/null +++ b/protractor/specs/qsHomepage_spec.js @@ -0,0 +1,32 @@ +/** + * Exammple tests of non-angular site... + */ +import qsHomePage from '../pages/qsHomePage'; +import githubPage from '../pages/githubPage'; + +describe('QS Homepage', () => { + beforeEach(async () => { + await qsHomePage.goto(); + }); + + it('should display 7 posts per page', async () => { + expect(await qsHomePage.posts.count()).toBe(7); + }); + + it('should find an older post by paging', async () => { + const POSTTITLE = 'When To Automate'; + await qsHomePage.findPostByPaging(POSTTITLE); + + expect(await qsHomePage.postTitleExists(POSTTITLE)).toBe(true); + }); + + it('should open social media link in new window', async () => { + await qsHomePage.waitAndClick(qsHomePage.githubLink); + await qsHomePage.switchToNewWindow(); + + expect(await githubPage.loaded()).toBe(true); + + // cleanup: close new window and switch back to original window... + await qsHomePage.closeNewWindow(); + }); +}); \ No newline at end of file diff --git a/protractor/specs/qsSearch_spec.js b/protractor/specs/qsSearch_spec.js new file mode 100644 index 00000000..4035c288 --- /dev/null +++ b/protractor/specs/qsSearch_spec.js @@ -0,0 +1,23 @@ +/** + * Exammple tests of non-angular site... + */ +import searchPage from '../pages/searchPage'; +import githubPage from '../pages/githubPage'; + +describe('QS Search', () => { + beforeEach(async () => { + await searchPage.goto(); + }); + + it('should return search results', async () => { + await searchPage.searchFor('protractor'); + + expect(await searchPage.results.count()).toBeGreaterThan(0); + }); + + it('unfound search term should return no results', async () => { + await searchPage.searchFor('sfdslkjsfkjslkdf'); + + expect(await searchPage.noResultsMsg.isDisplayed()).toBe(true); + }); +}); \ No newline at end of file diff --git a/webdriverio-mocha/.gitignore b/webdriverio-mocha/.gitignore new file mode 100644 index 00000000..1fa86c57 --- /dev/null +++ b/webdriverio-mocha/.gitignore @@ -0,0 +1,6 @@ +/report/* +/node_modules/* +/allure-report/* +/junit/* +report.* +.idea/* \ No newline at end of file diff --git a/webdriverio-mocha/README.md b/webdriverio-mocha/README.md new file mode 100644 index 00000000..22087964 --- /dev/null +++ b/webdriverio-mocha/README.md @@ -0,0 +1,69 @@ +This repo contains tests for Webdriverio + mocha application with Testomat.io plugins + +# Installation + +This is a playground for your first steps in testing, so instead of installing it from NPM it is recommended to clone it from repo instead. + +1) Clone this repository + +``` +git clone git@github.com:testomatio/examples.git && cd examples/webdriver-mocha +``` + +2) Install dependencies via npm: + +``` +npm i +``` + +This will install wdio cli & Testomat.io reporter + +## Loading Tests to Testomat.io + +1. Create empty project in Testomat.io +2. Obtain API key from Testomat.io +2. Run `npx check-tests` to upload tests data into testomat.io. Pass api key as `TESTOMATIO` environment variable: + +``` +TESTOMATIO={apiKey} npx check-tests webdriver-mocha "**/*{.,_}test.js" -d test +``` + +> **Environment variables** It is recommended to store Testomatio API Key as environment variable and never save it in the source code. Set them directly when running tests or use [dotenv](https://www.npmjs.com/package/dotenv) package to save environment variable in a file and load them for tests. + +## Publishing Test Results to Testomat.io + +Get API key from a project in Testomat.io and set it as environment variable `TESTOMATIO`: + +``` +TESTOMATIO={apiKey} npx @testomatio/reporter -c "npx wdio wdio.conf.js" +``` + +### Configuration + +Testomatio reporter is a plugin and should be enabled in `wdio.conf.js`: + +Do not hard code apiKey and always use it as environment variable. + +```js +const testomatio = require('@testomatio/reporter/lib/adapter/webdriver'); + +reporters: [ + ..., + [testomatio, { + apiKey: ${process.env.TESTOMATIO} + }] +], +``` + +### Attaching Test Artifacts + +Test artifacts will be uploaded automatically. +For uploading screenshots on failed tests to your report add the following hook to `wdio.conf.js`: + +```js + afterTest: function (test, context, { error, result, duration, passed, retries }) { + if (error) { + browser.takeScreenshot() + } + }, +``` diff --git a/webdriverio-mocha/package.json b/webdriverio-mocha/package.json new file mode 100644 index 00000000..edb41800 --- /dev/null +++ b/webdriverio-mocha/package.json @@ -0,0 +1,55 @@ +{ + "name": "webdriver-mocha", + "version": "1.0.0", + "description": "Example testomatio project with Webdriverio + mocha", + "main": "./node_modules/.bin/wdio", + "scripts": { + "test": "wdio wdio.conf.js" + }, + "keywords": [ + "mocha", + "uitesting", + "webdriver", + "webdriverio", + "e2e", + "selenium", + "test automation", + "javascript" + ], + "author": "Serhat Bolsu ", + "license": "MIT", + "dependencies": { + "@testomatio/reporter": "^0.6.3", + "@wdio/cli": "^6.12.1", + "@wdio/reporter": "^7.8.0", + "@wdio/sync": "^6.0.12", + "chai": "^4.2.0", + "superagent": "^5.2.2" + }, + "devDependencies": { + "@babel/cli": "^7.8.4", + "@babel/core": "^7.9.0", + "@babel/plugin-proposal-class-properties": "^7.8.3", + "@babel/preset-env": "^7.9.0", + "@babel/register": "^7.9.0", + "@wdio/allure-reporter": "^6.0.12", + "@wdio/junit-reporter": "^6.0.12", + "@wdio/local-runner": "^6.0.13", + "@wdio/mocha-framework": "^6.0.13", + "@wdio/sauce-service": "^6.0.13", + "@wdio/selenium-standalone-service": "^6.12.1", + "@wdio/spec-reporter": "^6.0.12", + "chromedriver": "^80.0.2", + "wdio-chromedriver-service": "^7.2.0" + }, + "babel": { + "presets": [ + [ + "@babel/preset-env" + ] + ], + "plugins": [ + "@babel/plugin-proposal-class-properties" + ] + } +} diff --git a/webdriverio-mocha/support/utils/Utilities.js b/webdriverio-mocha/support/utils/Utilities.js new file mode 100644 index 00000000..01a45d32 --- /dev/null +++ b/webdriverio-mocha/support/utils/Utilities.js @@ -0,0 +1,24 @@ +const fs = require('fs'); + +class Utilities { + static getRandomInt(max) { + return Math.floor(Math.random() * Math.floor(max)); + } + + static takeScreenshot(name, failure=false) { + const path = './report/screenshot/'; + if (!fs.existsSync(path)) { + fs.mkdirSync(path, { recursive: true }); + } + + if (failure) { + name = name + '_fail'; + } + name = name.replace(/ /g, '_') + '.png'; + browser.saveScreenshot( path + name); + const data = fs.readFileSync(`${path}/${name}`); + allure.addAttachment(name, data, 'image/png'); + } +} + +module.exports = Utilities; diff --git a/webdriverio-mocha/support/utils/teamsReporter.js b/webdriverio-mocha/support/utils/teamsReporter.js new file mode 100644 index 00000000..0d4bf5d5 --- /dev/null +++ b/webdriverio-mocha/support/utils/teamsReporter.js @@ -0,0 +1,90 @@ +const request = require('superagent'); + +function main() { + if (process.env.TEST_FRAMEWORK === undefined) { + throw new Error('TEST_FRAMEWORK should be defined either as "jest" or "wdio"'); + } else if (process.env.HOOK_URL === undefined) { + throw new Error('HOOK_URL should be defined as Microsoft teams channel hook url'); + } + const file = process.argv[2]; + if (!file) throw new Error('File path is not given'); + + const platform = process.env.TEST_FRAMEWORK; + const hookUrl = process.env.HOOK_URL; + const project = process.env.PROJECT_NAME; + let environment = process.env.PROJECT_ENVIRONMENT; + let type = process.env.PROJECT_TEST_TYPE; + const buildUrl = process.env.BUILD_URL; + + let total = 0; + let passed = 0; + let failed = 0; + let skipped = 0; + + // logic to get results + if (platform === 'jest') { + const testData = require(file); + total = testData['numTotalTests']; + passed= testData['numPassedTests']; + failed = testData['numFailedTests']; + skipped = total - passed - failed; + } else if (platform === 'wdio') { + const testData = require(file); + environment = testData['capabilities'][0]['browserName']; + type = 'UI'; + passed = testData['state']['passed']; + failed = testData['state']['failed']; + skipped = testData['state']['skipped']; + total = passed + failed + skipped; + } + + let results = `**${total} Tests**: ✔${passed} passed`; + if (failed > 0) results += `, ❗${failed} failed`; + if (skipped > 0) results += `, ➖${skipped} skipped`; + + request.post(hookUrl) + .send( + { + "@type": "MessageCard", + "@context": "http://schema.org/extensions", + "themeColor": "33F0FF", + "summary": `Test report for ${project}`, + "sections": [ + { + + "activityTitle": `## ${project}, ${environment}, ${type}`, + // "activitySubtitle": "Status of build #90", + "facts": [ + { + "name": "Environment", + "value": environment, + }, + // { + // "name": "Type", + // "value": "UI", + // }, + { + "name": "Status", + "value": results, + }, + ], + "markdown": true, + }, + ], + "potentialAction": [ + { + "@type": "OpenUri", + "name": "View Report", + "targets": [ + { "os": "default", "uri": buildUrl }, + ], + }, + ], + }, + ) + .then( (res) => { + console.log(`Post results to Microsoft Teams with status: ${res.status}`); + }); +} + +main(); diff --git a/webdriverio-mocha/test/pages/ajax.page.js b/webdriverio-mocha/test/pages/ajax.page.js new file mode 100644 index 00000000..3b45198f --- /dev/null +++ b/webdriverio-mocha/test/pages/ajax.page.js @@ -0,0 +1,20 @@ +import Page from './page'; + +class AjaxPage extends Page { + get ajaxButton() {return $('#ajaxButton');} + get ajaxContent() {return $('#content > p');} + + open() { + super.open('ajax'); + // `return this;` in order to be able to chain in methods in test case. + return this; + } + + getContent() { + this.ajaxContent.waitForExist({ timeout: 20000 }); + return this.ajaxContent.getText(); + return this; + } +} + +export default new AjaxPage(); diff --git a/webdriverio-mocha/test/pages/click.page.js b/webdriverio-mocha/test/pages/click.page.js new file mode 100644 index 00000000..9f2f02fb --- /dev/null +++ b/webdriverio-mocha/test/pages/click.page.js @@ -0,0 +1,20 @@ +import Page from './page'; + +class ClickPage extends Page { + get clickButton() { return $('#badButton'); } + get ajaxContent() {return $('#content > p');} + + open() { + super.open('click'); + // `return this;` in order to be able to chain in methods in test case. + return this; + } + + getContent() { + this.ajaxContent.waitForExist({ timeout: 20000 }); + return this.ajaxContent.getText(); + return this; + } +} + +export default new ClickPage(); diff --git a/webdriverio-mocha/test/pages/components/home_modal.component.js b/webdriverio-mocha/test/pages/components/home_modal.component.js new file mode 100644 index 00000000..a02b1923 --- /dev/null +++ b/webdriverio-mocha/test/pages/components/home_modal.component.js @@ -0,0 +1,8 @@ +export default class ModalComponent { + constructor(element) { + this.element = element; + } + + get title() {return this.element.$('h3').getText();} + get content() {return this.element.$('p').getText();} +} diff --git a/webdriverio-mocha/test/pages/home.page.js b/webdriverio-mocha/test/pages/home.page.js new file mode 100644 index 00000000..eb7150d2 --- /dev/null +++ b/webdriverio-mocha/test/pages/home.page.js @@ -0,0 +1,15 @@ +import Page from './page'; +import ModalComponent from './components/home_modal.component'; + +class HomePage extends Page { + get pageModals() { + return $$('#overview .col-sm').map((modal) => new ModalComponent(modal)); + } + + open() { + super.open('home'); + return this; + } +} + +export default new HomePage(); diff --git a/webdriverio-mocha/test/pages/page.js b/webdriverio-mocha/test/pages/page.js new file mode 100644 index 00000000..a27777fa --- /dev/null +++ b/webdriverio-mocha/test/pages/page.js @@ -0,0 +1,15 @@ +export default class Page { + constructor() { + this.title = 'my Page'; + } + + open(path) { + browser.url(path); + } + + verifyTextInPage(text) { + const pageText = $('body').getText(); + const position = pageText.search(text); + chai.expect(position).to.be.above(0); + } +} diff --git a/webdriverio-mocha/test/pages/progress.page.js b/webdriverio-mocha/test/pages/progress.page.js new file mode 100644 index 00000000..25edf690 --- /dev/null +++ b/webdriverio-mocha/test/pages/progress.page.js @@ -0,0 +1,25 @@ +import Page from './page'; + +class ProgressPage extends Page { + get startButton() {return $('#startButton');} + get stopButton() { return $('#stopButton'); } + get progressBar() { return $('#progressBar'); } + + open() { + super.open('progressbar'); + // `return this;` in order to be able to chain in methods in test case. + return this; + } + + getProgress() { + return this.progressBar.getText(); + } + + waitForProgress(progress, timeout) { + browser.waitUntil( + () => this.getProgress() === `${progress}%`, { timeout: timeout || 10000, timeoutMsg: `Expected ${progress} but got ${this.getProgress()}` } + ); + } +} + +export default new ProgressPage(); diff --git a/webdriverio-mocha/test/pages/textInput.page.js b/webdriverio-mocha/test/pages/textInput.page.js new file mode 100644 index 00000000..2c848a6c --- /dev/null +++ b/webdriverio-mocha/test/pages/textInput.page.js @@ -0,0 +1,23 @@ +import Page from './page'; + +class TextInput extends Page { + get nameField() {return $('#newButtonName');} + get button() {return $('#updatingButton');} + + open() { + super.open('textinput'); + return this; + } + + updateButtonText(text) { + this.nameField.setValue(text); + this.button.click(); + return this; + } + + getButtonText() { + return this.button.getText(); + } +} + +export default new TextInput(); diff --git a/webdriverio-mocha/test/specs/ajax.spec.js b/webdriverio-mocha/test/specs/ajax.spec.js new file mode 100644 index 00000000..a3309ace --- /dev/null +++ b/webdriverio-mocha/test/specs/ajax.spec.js @@ -0,0 +1,12 @@ +import ajaxPage from '../pages/ajax.page'; + +describe('Ajax Page Suite', function() { + it('should retrieve Ajax response', function() { + ajaxPage + .open() + .ajaxButton.click(); + expect(ajaxPage.ajaxContent).toHaveText( + 'Data loaded with AJAX get request.', { wait: 20000 }); + utilities.takeScreenshot('ajax_with_request_capture'); + }); +}); diff --git a/webdriverio-mocha/test/specs/click.spec.js b/webdriverio-mocha/test/specs/click.spec.js new file mode 100644 index 00000000..76560f85 --- /dev/null +++ b/webdriverio-mocha/test/specs/click.spec.js @@ -0,0 +1,11 @@ +import clickPage from '../pages/click.page'; + +describe('Click page', function() { + it('Should able to click the button', function() { + clickPage + .open() + .clickButton.click(); + + expect(clickPage.clickButton.getText()).toEqual('Button That Ignores DOM Click Event', { wait: 2000 }); + }); +}); diff --git a/webdriverio-mocha/test/specs/home.spec.js b/webdriverio-mocha/test/specs/home.spec.js new file mode 100644 index 00000000..eba940d3 --- /dev/null +++ b/webdriverio-mocha/test/specs/home.spec.js @@ -0,0 +1,14 @@ +import homePage from '../pages/home.page'; + +describe('Home Page Suite', function() { + it('should have Load Delay section', function() { + // Using component to capture repeating html part + homePage + .open() + .verifyTextInPage('UI Test Automation'); + expect(homePage.pageModals[3].title).toEqual('Load Delay'); + expect(homePage.pageModals[3].content).toEqual( + 'Ensure that a test is capable of waiting for a page to load'); + utilities.takeScreenshot('homepage'); + }); +}); diff --git a/webdriverio-mocha/test/specs/progress.spec.js b/webdriverio-mocha/test/specs/progress.spec.js new file mode 100644 index 00000000..73e8b8a5 --- /dev/null +++ b/webdriverio-mocha/test/specs/progress.spec.js @@ -0,0 +1,33 @@ +import progressPage from '../pages/progress.page'; + +describe('Progress page', function () { + + it('Should open the progress page in browser', () => { + progressPage.open() + }); + + it('Should able to click start button', () => { + progressPage + .open() + .startButton.click() + + expect(progressPage.getProgress()).toEqual('25%', { wait: 2000 }); + }) + + it('Should progress change', () => { + progressPage + .open() + .startButton.click() + + progressPage.waitForProgress(27, 20000); + }) + + it('Should wait for 75%', () => { + progressPage + .open() + .startButton.click() + + progressPage.waitForProgress(75, 20000); + }) + +}); diff --git a/webdriverio-mocha/test/specs/sample.spec.js b/webdriverio-mocha/test/specs/sample.spec.js new file mode 100644 index 00000000..e184b9e7 --- /dev/null +++ b/webdriverio-mocha/test/specs/sample.spec.js @@ -0,0 +1,17 @@ +import Page from '../pages/page'; + +describe('Basic tests', function () { + const page = new Page(); + + it('Should open the page in browser', () => { + page.open('home'); + }); + + it('Should able open the home page and see test', () => { + page.open('home'); + + const pageText = $('body').getText(); + const position = pageText.search('UI Test Automation'); + chai.expect(position).to.be.above(0); + }) +}); diff --git a/webdriverio-mocha/test/specs/textInput.spec.js b/webdriverio-mocha/test/specs/textInput.spec.js new file mode 100644 index 00000000..01a5a35a --- /dev/null +++ b/webdriverio-mocha/test/specs/textInput.spec.js @@ -0,0 +1,14 @@ +import textInputPage from '../pages/textInput.page'; + +describe('Text Input Spec', function() { + before(function() { + textInputPage.open(); + }); + + it('Change button text with chai', function() { + console.log(textInputPage.getButtonText()); + textInputPage.updateButtonText('My Special Name'); + chai.expect(textInputPage.getButtonText()).to.be.eql('My Special Name'); + }); +}); + diff --git a/webdriverio-mocha/wdio.conf.js b/webdriverio-mocha/wdio.conf.js new file mode 100644 index 00000000..d398cba8 --- /dev/null +++ b/webdriverio-mocha/wdio.conf.js @@ -0,0 +1,512 @@ +const utilities = require("./support/utils/Utilities"); +const chai = require('chai'); +const allure = require('@wdio/allure-reporter').default; +const testomatio = require('@testomatio/reporter/lib/adapter/webdriver'); +// Max time for single test case execution +let timeout = process.env.DEBUG ? 99999999 : 120000; +let elementTimeout = 10000; + +exports.config = { + + // ================================== + // Where should your test be launched + // ================================== + // + runner: 'local', + // + // ===================== + // Server Configurations + // ===================== + // Host address of the running Selenium server. This information is usually obsolete, as + // WebdriverIO automatically connects to localhost. Also if you are using one of the + // supported cloud services like Sauce Labs, Browserstack, Testing Bot or LambdaTest, you also don't + // need to define host and port information (because WebdriverIO can figure that out + // from your user and key information). However, if you are using a private Selenium + // backend, you should define the `hostname`, `port`, and `path` here. + // + // hostname: 'localhost', + // port: 4444, + // path: '/', + // Protocol: http | https + // protocol: 'http', + // + // ================= + // Service Providers + // ================= + // WebdriverIO supports Sauce Labs, Browserstack, Testing Bot and LambdaTest. (Other cloud providers + // should work, too.) These services define specific `user` and `key` (or access key) + // values you must put here, in order to connect to these services. + // + // user: 'webdriverio', + // key: 'xxxxxxxxxxxxxxxx-xxxxxx-xxxxx-xxxxxxxxx', + + // If you run your tests on Sauce Labs you can specify the region you want to run your tests + // in via the `region` property. Available short handles for regions are `us` (default) and `eu`. + // These regions are used for the Sauce Labs VM cloud and the Sauce Labs Real Device Cloud. + // If you don't provide the region, it defaults to `us`. + // region: 'us', + // + // Sauce Labs provides a [headless offering](https://saucelabs.com/products/web-testing/sauce-headless-testing) + // that allows you to run Chrome and Firefox tests headless. + // + // headless: false, + // + // ================== + // Specify Test Files + // ================== + // Define which test specs should run. The pattern is relative to the directory + // from which `wdio` was called. + // + // The specs are defined as an array of spec files (optionally using wildcards + // that will be expanded). The test for each spec file will be run in a separate + // worker process. In order to have a group of spec files run in the same worker + // process simply enclose them in an array within the specs array. + // + // If you are calling `wdio` from an NPM script (see https://docs.npmjs.com/cli/run-script), + // then the current working directory is where your `package.json` resides, so `wdio` + // will be called from there. + // + specs: [ + './test/specs/**/*.spec.js' + ], + // Patterns to exclude. + exclude: [ + 'path/to/excluded/files' + ], + // + // ============ + // Capabilities + // ============ + // Define your capabilities here. WebdriverIO can run multiple capabilities at the same + // time. Depending on the number of capabilities, WebdriverIO launches several test + // sessions. Within your `capabilities`, you can overwrite the `spec` and `exclude` + // options in order to group specific specs to a specific capability. + // + // First, you can define how many instances should be started at the same time. Let's + // say you have 3 different capabilities (Chrome, Firefox, and Safari) and you have + // set `maxInstances` to 1. wdio will spawn 3 processes. + // + // Therefore, if you have 10 spec files and you set `maxInstances` to 10, all spec files + // will be tested at the same time and 30 processes will be spawned. + // + // The property basically handles how many capabilities from the same test should run tests. + // + maxInstances: 10, + // + // Or set a limit to run tests with a specific capability. + // maxInstancesPerCapability: 10, + // + // If you have trouble getting all important capabilities together, check out the + // Sauce Labs platform configurator - a great tool to configure your capabilities: + // https://docs.saucelabs.com/reference/platforms-configurator + // + capabilities: [{ + maxInstances: 3, + browserName: 'chrome', + // 'goog:chromeOptions': { + // // to run chrome headless the following flags are required + // // (see https://developers.google.com/web/updates/2017/04/headless-chrome) + // // args: ['--headless', '--disable-gpu'], + // } + // + // Parameter to ignore some or all default flags + // - if value is true: ignore all DevTools 'default flags' and Puppeteer 'default arguments' + // - if value is an array: DevTools filters given default arguments + // 'wdio:devtoolsOptions': { + // ignoreDefaultArgs: true, + // ignoreDefaultArgs: ['--disable-sync', '--disable-extensions'], + // } + }, + // { + // // maxInstances can get overwritten per capability. So if you have an in house Selenium + // // grid with only 5 firefox instance available you can make sure that not more than + // // 5 instance gets started at a time. + // maxInstances: 5, + // browserName: 'firefox', + // specs: [ + // 'test/ffOnly/*' + // ], + // 'moz:firefoxOptions': { + // // flag to activate Firefox headless mode (see https://github.com/mozilla/geckodriver/blob/master/README.md#firefox-capabilities for more details about moz:firefoxOptions) + // // args: ['-headless'] + // }, + // // If outputDir is provided WebdriverIO can capture driver session logs + // // it is possible to configure which logTypes to exclude. + // // excludeDriverLogs: ['*'], // pass '*' to exclude all driver session logs + // excludeDriverLogs: ['bugreport', 'server'], + // // + // // Parameter to ignore some or all Puppeteer default arguments + // // ignoreDefaultArgs: ['-foreground'], // set value to true to ignore all default arguments + // } + ], + // + // Additional list of node arguments to use when starting child processes + // execArgv: [], + // + // =================== + // Test Configurations + // =================== + // Define all options that are relevant for the WebdriverIO instance here + // + // Level of logging verbosity: trace | debug | info | warn | error | silent + logLevel: 'info', + // + // Set specific log levels per logger + // use 'silent' level to disable logger + // logLevels: { + // webdriver: 'info', + // '@wdio/appium-service': 'info' + // }, + // + // Set directory to store all logs into + // outputDir: __dirname, + // + // If you only want to run your tests until a specific amount of tests have failed use + // bail (default is 0 - don't bail, run all tests). + bail: 0, + // + // Set a base URL in order to shorten `url()` command calls. If your `url` parameter starts + // with `/`, the `baseUrl` is prepended, not including the path portion of `baseUrl`. + // + // If your `url` parameter starts without a scheme or `/` (like `some/path`), the `baseUrl` + // gets prepended directly. + baseUrl: 'http://uitestingplayground.com', + // + // Default timeout for all waitForXXX commands. + waitforTimeout: elementTimeout, + // + // Default timeout in milliseconds for request + // if browser driver or grid doesn't send response + connectionRetryTimeout: 90000, + // + // Default request retries count + connectionRetryCount: 3, + // + // Test runner services + // Services take over a specific job you don't want to take care of. They enhance + // your test setup with almost no effort. Unlike plugins, they don't add new + // commands. Instead, they hook themselves up into the test process. + services: [['selenium-standalone', { drivers: { chrome: true } }]], + // + // Add files to watch (e.g. application code or page objects) when running `wdio` command + // with `--watch` flag. Globbing is supported. + // filesToWatch: [ + // // e.g. rerun tests if I change my application code + // // './app/**/*.js' + // ], + // + // Framework you want to run your specs with. + // The following are supported: 'mocha', 'jasmine', and 'cucumber' + // See also: https://webdriver.io/docs/frameworks.html + // + // Make sure you have the wdio adapter package for the specific framework installed before running any tests. + framework: 'mocha', + // + // The number of times to retry the entire specfile when it fails as a whole + // specFileRetries: 1, + // Delay in seconds between the spec file retry attempts + // specFileRetriesDelay: 0, + // Whether or not retried specfiles should be retried immediately or deferred to the end of the queue + // specFileRetriesDeferred: false, + // + // Test reporter for stdout. + // The only one supported by default is 'dot' + // See also: https://webdriver.io/docs/dot-reporter.html , and click on "Reporters" in left column + reporters: [ + 'spec', + [testomatio, { + apiKey: `${process.env.TESTOMATIO}` + }] + ], + // + // Options to be passed to Mocha. + // See the full list at: http://mochajs.org + mochaOpts: { + ui: 'bdd', + timeout: timeout, + require: ['@babel/register'] + }, + // + // Options to be passed to Jasmine. + // See also: https://github.com/webdriverio/webdriverio/tree/main/packages/wdio-jasmine-framework#jasmineopts-options + // jasmineOpts: { + // // + // // Jasmine default timeout + // defaultTimeoutInterval: 5000, + // // + // // The Jasmine framework allows it to intercept each assertion in order to log the state of the application + // // or website depending on the result. For example, it is pretty handy to take a screenshot every time + // // an assertion fails. + // expectationResultHandler: function(passed, assertion) { + // // do something + // }, + // // + // // Make use of Jasmine-specific grep functionality + // grep: null, + // invertGrep: null + // }, + // + // If you are using Cucumber you need to specify where your step definitions are located. + // See also: https://github.com/webdriverio/webdriverio/tree/main/packages/wdio-cucumber-framework#cucumberopts-options + // cucumberOpts: { + // require: [], // (file/dir) require files before executing features + // backtrace: false, // show full backtrace for errors + // compiler: [], // ("extension:module") require files with the given EXTENSION after requiring MODULE (repeatable) + // dryRun: false, // invoke formatters without executing steps + // failFast: false, // abort the run on first failure + // snippets: true, // hide step definition snippets for pending steps + // source: true, // hide source URIs + // strict: false, // fail if there are any undefined or pending steps + // tagExpression: '', // (expression) only execute the features or scenarios with tags matching the expression + // timeout: 20000, // timeout for step definitions + // ignoreUndefinedDefinitions: false, // Enable this config to treat undefined definitions as warnings. + // scenarioLevelReporter: false // Enable this to make webdriver.io behave as if scenarios and not steps were the tests. + // }, + // For convenience, if ts-node or @babel/register modules are detected + // they are automatically loaded for config parsing so that TypeScript and + // future ES features can be used in wdio configs, and are also + // automatically loaded for test running so that tests can be written + // using TypeScript and future ES features. + // Because this may not be ideal in every situation, the following options + // may be used to customize the loading for test running, incase it has + // other requirements. + // autoCompileOpts: { + // // + // // To disable auto-loading entirely set this to false. + // autoCompile: true, // Disable this to turn off autoloading. Note: When disabling, you will need to handle calling any such libraries yourself. + // // + // // If you have ts-node installed, you can customize how options are passed to it here: + // // Any valid ts-node config option is allowed. Alternatively the ENV Vars could also be used instead of this. + // // See also: https://github.com/TypeStrong/ts-node#cli-and-programmatic-options + // // See also RegisterOptions in https://github.com/TypeStrong/ts-node/blob/master/src/index.ts + // tsNodeOpts: { + // transpileOnly: true, + // project: 'tsconfig.json' + // }, + // // If you have tsconfig-paths installed and provide a tsConfigPathsOpts + // // option, it will be automatically registered during bootstrap. + // tsConfigPathsOpts: { + // baseUrl: './' + // }, + // // + // // If @babel/register is installed, you can customize how options are passed to it here: + // // Any valid @babel/register config option is allowed. + // // https://babeljs.io/docs/en/babel-register#specifying-options + // babelOpts: { + // ignore: [] + // }, + // }, + // + // ===== + // Hooks + // ===== + // WebdriverIO provides a several hooks you can use to interfere the test process in order to enhance + // it and build services around it. You can either apply a single function to it or an array of + // methods. If one of them returns with a promise, WebdriverIO will wait until that promise is + // resolved to continue. + // + /** + * Gets executed once before all workers get launched. + * @param {Object} config wdio configuration object + * @param {Array.} capabilities list of capabilities details + */ + // onPrepare: function (config, capabilities) { + // }, + /** + * Gets executed before a worker process is spawned and can be used to initialize specific service + * for that worker as well as modify runtime environments in an async fashion. + * @param {String} cid capability id (e.g 0-0) + * @param {[type]} caps object containing capabilities for session that will be spawn in the worker + * @param {[type]} specs specs to be run in the worker process + * @param {[type]} args object that will be merged with the main configuration once worker is initialized + * @param {[type]} execArgv list of string arguments passed to the worker process + */ + // onWorkerStart: function (cid, caps, specs, args, execArgv) { + // }, + /** + * Gets executed just after a worker process has exited. + * @param {String} cid capability id (e.g 0-0) + * @param {Number} exitCode 0 - success, 1 - fail + * @param {[type]} specs specs to be run in the worker process + * @param {Number} retries number of retries used + */ + // onWorkerEnd: function (cid, exitCode, specs, retries) { + // }, + /** + * Gets executed just before initializing the webdriver session and test framework. It allows you + * to manipulate configurations depending on the capability or spec. + * @param {Object} config wdio configuration object + * @param {Array.} capabilities list of capabilities details + * @param {Array.} specs List of spec file paths that are to be run + */ + // beforeSession: function (config, capabilities, specs) { + // }, + /** + * Gets executed before test execution begins. At this point you can access to all global + * variables like `browser`. It is the perfect place to define custom commands. + * @param {Array.} capabilities list of capabilities details + * @param {Array.} specs List of spec file paths that are to be run + * @param {Object} browser instance of created browser/device session + */ + before: function (capabilities, specs, browser) { + global.allure = allure; + global.chai = chai; + global.utilities = utilities; + }, + /** + * Gets executed before the suite starts. + * @param {Object} suite suite details + */ + // beforeSuite: function (suite) { + // }, + /** + * This hook gets executed _before_ every hook within the suite starts. + * (For example, this runs before calling `before`, `beforeEach`, `after`, `afterEach` in Mocha.). In Cucumber `context` is the World object. + * + */ + // beforeHook: function (test, context) { + // }, + /** + * Hook that gets executed _after_ every hook within the suite ends. + * (For example, this runs after calling `before`, `beforeEach`, `after`, `afterEach` in Mocha.). In Cucumber `context` is the World object. + */ + // afterHook: function (test, context, { error, result, duration, passed, retries }) { + // }, + /** + * Function to be executed before a test (in Mocha/Jasmine only) + * @param {Object} test test object + * @param {Object} context scope object the test was executed with + */ + // beforeTest: function (test, context) { + // }, + /** + * Runs before a WebdriverIO command is executed. + * @param {String} commandName hook command name + * @param {Array} args arguments that the command would receive + */ + // beforeCommand: function (commandName, args) { + // }, + /** + * Runs after a WebdriverIO command gets executed + * @param {String} commandName hook command name + * @param {Array} args arguments that command would receive + * @param {Number} result 0 - command success, 1 - command error + * @param {Object} error error object, if any + */ + // afterCommand: function (commandName, args, result, error) { + // }, + /** + * Function to be executed after a test (in Mocha/Jasmine only) + * @param {Object} test test object + * @param {Object} context scope object the test was executed with + * @param {Error} result.error error object in case the test fails, otherwise `undefined` + * @param {Any} result.result return object of test function + * @param {Number} result.duration duration of test + * @param {Boolean} result.passed true if test has passed, otherwise false + * @param {Object} result.retries informations to spec related retries, e.g. `{ attempts: 0, limit: 0 }` + */ + afterTest: function (test, context, { error, result, duration, passed, retries }) { + if (error) { + browser.takeScreenshot() + } + }, + /** + * Hook that gets executed after the suite has ended. + * @param {Object} suite suite details + */ + // afterSuite: function (suite) { + // }, + /** + * Gets executed after all tests are done. You still have access to all global variables from + * the test. + * @param {Number} result 0 - test pass, 1 - test fail + * @param {Array.} capabilities list of capabilities details + * @param {Array.} specs List of spec file paths that ran + */ + // after: function (result, capabilities, specs) { + // }, + /** + * Gets executed right after terminating the webdriver session. + * @param {Object} config wdio configuration object + * @param {Array.} capabilities list of capabilities details + * @param {Array.} specs List of spec file paths that ran + */ + // afterSession: function (config, capabilities, specs) { + // }, + /** + * Gets executed after all workers have shut down and the process is about to exit. + * An error thrown in the `onComplete` hook will result in the test run failing. + * @param {Object} exitCode 0 - success, 1 - fail + * @param {Object} config wdio configuration object + * @param {Array.} capabilities list of capabilities details + * @param {} results object containing test results + */ + // onComplete: function (exitCode, config, capabilities, results) { + // }, + /** + * Gets executed when a refresh happens. + * @param {String} oldSessionId session ID of the old session + * @param {String} newSessionId session ID of the new session + */ + // onReload: function(oldSessionId, newSessionId) { + // }, + /** + * Cucumber Hooks + * + * Runs before a Cucumber Feature. + * @param {String} uri path to feature file + * @param {GherkinDocument.IFeature} feature Cucumber feature object + */ + // beforeFeature: function (uri, feature) { + // }, + /** + * + * Runs before a Cucumber Scenario. + * @param {ITestCaseHookParameter} world world object containing information on pickle and test step + * @param {Object} context Cucumber World object + */ + // beforeScenario: function (world, context) { + // }, + /** + * + * Runs before a Cucumber Step. + * @param {Pickle.IPickleStep} step step data + * @param {IPickle} scenario scenario pickle + * @param {Object} context Cucumber World object + */ + // beforeStep: function (step, scenario, context) { + // }, + /** + * + * Runs after a Cucumber Step. + * @param {Pickle.IPickleStep} step step data + * @param {IPickle} scenario scenario pickle + * @param {Object} result results object containing scenario results + * @param {boolean} result.passed true if scenario has passed + * @param {string} result.error error stack if scenario failed + * @param {number} result.duration duration of scenario in milliseconds + * @param {Object} context Cucumber World object + */ + // afterStep: function (step, scenario, result, context) { + // }, + /** + * + * Runs after a Cucumber Scenario. + * @param {ITestCaseHookParameter} world world object containing information on pickle and test step + * @param {Object} result results object containing scenario results `{passed: boolean, error: string, duration: number}` + * @param {boolean} result.passed true if scenario has passed + * @param {string} result.error error stack if scenario failed + * @param {number} result.duration duration of scenario in milliseconds + * @param {Object} context Cucumber World object + */ + // afterScenario: function (world, result, context) { + // }, + /** + * + * Runs after a Cucumber Feature. + * @param {String} uri path to feature file + * @param {GherkinDocument.IFeature} feature Cucumber feature object + */ + // afterFeature: function (uri, feature) { + // } +} From 9b624ecffc53c61e4efc598f6135bc58a2e4b275 Mon Sep 17 00:00:00 2001 From: Oleksandr Pelykh Date: Sat, 24 Feb 2024 08:05:48 +0200 Subject: [PATCH 62/64] add wdio test projects (#62) --- wdio/v7/package.json | 25 ++ wdio/v7/test/pageobjects/login.page.ts | 42 +++ wdio/v7/test/pageobjects/page.ts | 13 + wdio/v7/test/pageobjects/secure.page.ts | 17 ++ wdio/v7/test/specs/example.e2e.ts | 15 ++ wdio/v7/test/tsconfig.json | 12 + wdio/v7/test/wdio.conf.ts | 325 ++++++++++++++++++++++++ wdio/v7/tsconfig.json | 106 ++++++++ wdio/v8/package.json | 24 ++ wdio/v8/test/pageobjects/login.page.ts | 41 +++ wdio/v8/test/pageobjects/page.ts | 15 ++ wdio/v8/test/pageobjects/secure.page.ts | 16 ++ wdio/v8/test/specs/test.e2e.ts | 15 ++ wdio/v8/tsconfig.json | 30 +++ wdio/v8/wdio.conf.ts | 309 ++++++++++++++++++++++ 15 files changed, 1005 insertions(+) create mode 100644 wdio/v7/package.json create mode 100644 wdio/v7/test/pageobjects/login.page.ts create mode 100644 wdio/v7/test/pageobjects/page.ts create mode 100644 wdio/v7/test/pageobjects/secure.page.ts create mode 100644 wdio/v7/test/specs/example.e2e.ts create mode 100644 wdio/v7/test/tsconfig.json create mode 100644 wdio/v7/test/wdio.conf.ts create mode 100644 wdio/v7/tsconfig.json create mode 100644 wdio/v8/package.json create mode 100644 wdio/v8/test/pageobjects/login.page.ts create mode 100644 wdio/v8/test/pageobjects/page.ts create mode 100644 wdio/v8/test/pageobjects/secure.page.ts create mode 100644 wdio/v8/test/specs/test.e2e.ts create mode 100644 wdio/v8/tsconfig.json create mode 100644 wdio/v8/wdio.conf.ts diff --git a/wdio/v7/package.json b/wdio/v7/package.json new file mode 100644 index 00000000..7d780306 --- /dev/null +++ b/wdio/v7/package.json @@ -0,0 +1,25 @@ +{ + "name": "testomat-example-wdio-v7", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "npx start-test-run -c 'wdio run ./test/wdio.conf.ts' --env-file .env" + }, + "author": "", + "license": "ISC", + "devDependencies": { + "@wdio/cli": "^7.34.0", + "@wdio/local-runner": "^7.34.0", + "@wdio/mocha-framework": "^7.33.0", + "@wdio/spec-reporter": "^7.33.0", + "chromedriver": "^121.0.0", + "ts-node": "^10.9.2", + "typescript": "^5.3.3", + "wdio-chromedriver-service": "^8.1.1" + }, + "dependencies": { + "@testomatio/reporter": "file:../../../reporter/testomatio-reporter-1.1.1.tgz", + "webdriverio": "^7.34.0" + } +} diff --git a/wdio/v7/test/pageobjects/login.page.ts b/wdio/v7/test/pageobjects/login.page.ts new file mode 100644 index 00000000..b117fb0d --- /dev/null +++ b/wdio/v7/test/pageobjects/login.page.ts @@ -0,0 +1,42 @@ +import { ChainablePromiseElement } from 'webdriverio'; + +import Page from './page'; + +/** + * sub page containing specific selectors and methods for a specific page + */ +class LoginPage extends Page { + /** + * define selectors using getter methods + */ + public get inputUsername () { + return $('#username'); + } + + public get inputPassword () { + return $('#password'); + } + + public get btnSubmit () { + return $('button[type="submit"]'); + } + + /** + * a method to encapsule automation code to interact with the page + * e.g. to login using username and password + */ + public async login (username: string, password: string) { + await this.inputUsername.setValue(username); + await this.inputPassword.setValue(password); + await this.btnSubmit.click(); + } + + /** + * overwrite specific options to adapt it to page object + */ + public open () { + return super.open('login'); + } +} + +export default new LoginPage(); diff --git a/wdio/v7/test/pageobjects/page.ts b/wdio/v7/test/pageobjects/page.ts new file mode 100644 index 00000000..fc8be333 --- /dev/null +++ b/wdio/v7/test/pageobjects/page.ts @@ -0,0 +1,13 @@ +/** +* main page object containing all methods, selectors and functionality +* that is shared across all page objects +*/ +export default class Page { + /** + * Opens a sub page of the page + * @param path path of the sub page (e.g. /path/to/page.html) + */ + public open (path: string) { + return browser.url(`https://the-internet.herokuapp.com/${path}`) + } +} diff --git a/wdio/v7/test/pageobjects/secure.page.ts b/wdio/v7/test/pageobjects/secure.page.ts new file mode 100644 index 00000000..35b7dca1 --- /dev/null +++ b/wdio/v7/test/pageobjects/secure.page.ts @@ -0,0 +1,17 @@ +import { ChainablePromiseElement } from 'webdriverio'; + +import Page from './page'; + +/** + * sub page containing specific selectors and methods for a specific page + */ +class SecurePage extends Page { + /** + * define selectors using getter methods + */ + public get flashAlert () { + return $('#flash'); + } +} + +export default new SecurePage(); diff --git a/wdio/v7/test/specs/example.e2e.ts b/wdio/v7/test/specs/example.e2e.ts new file mode 100644 index 00000000..c382b88f --- /dev/null +++ b/wdio/v7/test/specs/example.e2e.ts @@ -0,0 +1,15 @@ +import LoginPage from '../pageobjects/login.page'; +import SecurePage from '../pageobjects/secure.page'; + +describe('My Login application @S1806fa9f', () => { + it('should login with valid credentials @T6980b031', async () => { + await LoginPage.open(); + + await LoginPage.login('tomsmith', 'SuperSecretPassword!'); + await expect(SecurePage.flashAlert).toBeExisting(); + await expect(SecurePage.flashAlert).toHaveTextContaining( + 'You logged into a secure area!'); + }); +}); + + diff --git a/wdio/v7/test/tsconfig.json b/wdio/v7/test/tsconfig.json new file mode 100644 index 00000000..c53bd32d --- /dev/null +++ b/wdio/v7/test/tsconfig.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "moduleResolution": "node", + "types": [ + "node", + "webdriverio/async", + "@wdio/mocha-framework", + "expect-webdriverio" + ], + "target": "es2019" + } +} \ No newline at end of file diff --git a/wdio/v7/test/wdio.conf.ts b/wdio/v7/test/wdio.conf.ts new file mode 100644 index 00000000..237c1df4 --- /dev/null +++ b/wdio/v7/test/wdio.conf.ts @@ -0,0 +1,325 @@ +import type { Options } from '@wdio/types' +const testomatio = require('@testomatio/reporter/lib/adapter/webdriver'); + +require('dotenv').config({ path: './.env' }); + +export const config: Options.Testrunner = { + // + // ==================== + // Runner Configuration + // ==================== + // + // + // ===================== + // ts-node Configurations + // ===================== + // + // You can write tests using TypeScript to get autocompletion and type safety. + // You will need typescript and ts-node installed as devDependencies. + // WebdriverIO will automatically detect if these dependencies are installed + // and will compile your config and tests for you. + // If you need to configure how ts-node runs please use the + // environment variables for ts-node or use wdio config's autoCompileOpts section. + // + + autoCompileOpts: { + autoCompile: true, + // see https://github.com/TypeStrong/ts-node#cli-and-programmatic-options + // for all available options + tsNodeOpts: { + transpileOnly: true, + project: 'test/tsconfig.json' + } + // tsconfig-paths is only used if "tsConfigPathsOpts" are provided, if you + // do please make sure "tsconfig-paths" is installed as dependency + // tsConfigPathsOpts: { + // baseUrl: './' + // } + }, + // + // ================== + // Specify Test Files + // ================== + // Define which test specs should run. The pattern is relative to the directory + // from which `wdio` was called. + // + // The specs are defined as an array of spec files (optionally using wildcards + // that will be expanded). The test for each spec file will be run in a separate + // worker process. In order to have a group of spec files run in the same worker + // process simply enclose them in an array within the specs array. + // + // If you are calling `wdio` from an NPM script (see https://docs.npmjs.com/cli/run-script), + // then the current working directory is where your `package.json` resides, so `wdio` + // will be called from there. + // + specs: [ + './test/specs/**/*.ts' + ], + // Patterns to exclude. + exclude: [ + // 'path/to/excluded/files' + ], + // + // ============ + // Capabilities + // ============ + // Define your capabilities here. WebdriverIO can run multiple capabilities at the same + // time. Depending on the number of capabilities, WebdriverIO launches several test + // sessions. Within your capabilities you can overwrite the spec and exclude options in + // order to group specific specs to a specific capability. + // + // First, you can define how many instances should be started at the same time. Let's + // say you have 3 different capabilities (Chrome, Firefox, and Safari) and you have + // set maxInstances to 1; wdio will spawn 3 processes. Therefore, if you have 10 spec + // files and you set maxInstances to 10, all spec files will get tested at the same time + // and 30 processes will get spawned. The property handles how many capabilities + // from the same test should run tests. + // + maxInstances: 10, + // + // If you have trouble getting all important capabilities together, check out the + // Sauce Labs platform configurator - a great tool to configure your capabilities: + // https://saucelabs.com/platform/platform-configurator + // + capabilities: [{ + + // maxInstances can get overwritten per capability. So if you have an in-house Selenium + // grid with only 5 firefox instances available you can make sure that not more than + // 5 instances get started at a time. + maxInstances: 5, + // + browserName: 'chrome', + acceptInsecureCerts: true + // If outputDir is provided WebdriverIO can capture driver session logs + // it is possible to configure which logTypes to include/exclude. + // excludeDriverLogs: ['*'], // pass '*' to exclude all driver session logs + // excludeDriverLogs: ['bugreport', 'server'], + }], + // + // =================== + // Test Configurations + // =================== + // Define all options that are relevant for the WebdriverIO instance here + // + // Level of logging verbosity: trace | debug | info | warn | error | silent + logLevel: 'warn', + // + // Set specific log levels per logger + // loggers: + // - webdriver, webdriverio + // - @wdio/browserstack-service, @wdio/devtools-service, @wdio/sauce-service + // - @wdio/mocha-framework, @wdio/jasmine-framework + // - @wdio/local-runner + // - @wdio/sumologic-reporter + // - @wdio/cli, @wdio/config, @wdio/utils + // Level of logging verbosity: trace | debug | info | warn | error | silent + // logLevels: { + // webdriver: 'info', + // '@wdio/appium-service': 'info' + // }, + // + // If you only want to run your tests until a specific amount of tests have failed use + // bail (default is 0 - don't bail, run all tests). + bail: 0, + // + // Set a base URL in order to shorten url command calls. If your `url` parameter starts + // with `/`, the base url gets prepended, not including the path portion of your baseUrl. + // If your `url` parameter starts without a scheme or `/` (like `some/path`), the base url + // gets prepended directly. + baseUrl: 'http://localhost', + // + // Default timeout for all waitFor* commands. + waitforTimeout: 10000, + // + // Default timeout in milliseconds for request + // if browser driver or grid doesn't send response + connectionRetryTimeout: 120000, + // + // Default request retries count + connectionRetryCount: 3, + // + // Test runner services + // Services take over a specific job you don't want to take care of. They enhance + // your test setup with almost no effort. Unlike plugins, they don't add new + // commands. Instead, they hook themselves up into the test process. + services: ['chromedriver'], + + // Framework you want to run your specs with. + // The following are supported: Mocha, Jasmine, and Cucumber + // see also: https://webdriver.io/docs/frameworks + // + // Make sure you have the wdio adapter package for the specific framework installed + // before running any tests. + framework: 'mocha', + // + // The number of times to retry the entire specfile when it fails as a whole + // specFileRetries: 1, + // + // Delay in seconds between the spec file retry attempts + // specFileRetriesDelay: 0, + // + // Whether or not retried specfiles should be retried immediately or deferred to the end of the queue + // specFileRetriesDeferred: false, + // + // Test reporter for stdout. + // The only one supported by default is 'dot' + // see also: https://webdriver.io/docs/dot-reporter + reporters: ['spec', + [testomatio, { apiKey: process.env.TESTOMATIO }] + ], + + // + // Options to be passed to Mocha. + // See the full list at http://mochajs.org/ + mochaOpts: { + ui: 'bdd', + timeout: 60000 + }, + // + // ===== + // Hooks + // ===== + // WebdriverIO provides several hooks you can use to interfere with the test process in order to enhance + // it and to build services around it. You can either apply a single function or an array of + // methods to it. If one of them returns with a promise, WebdriverIO will wait until that promise got + // resolved to continue. + /** + * Gets executed once before all workers get launched. + * @param {Object} config wdio configuration object + * @param {Array.} capabilities list of capabilities details + */ + // onPrepare: function (config, capabilities) { + // }, + /** + * Gets executed before a worker process is spawned and can be used to initialise specific service + * for that worker as well as modify runtime environments in an async fashion. + * @param {String} cid capability id (e.g 0-0) + * @param {[type]} caps object containing capabilities for session that will be spawn in the worker + * @param {[type]} specs specs to be run in the worker process + * @param {[type]} args object that will be merged with the main configuration once worker is initialized + * @param {[type]} execArgv list of string arguments passed to the worker process + */ + // onWorkerStart: function (cid, caps, specs, args, execArgv) { + // }, + /** + * Gets executed just after a worker process has exited. + * @param {String} cid capability id (e.g 0-0) + * @param {Number} exitCode 0 - success, 1 - fail + * @param {[type]} specs specs to be run in the worker process + * @param {Number} retries number of retries used + */ + // onWorkerEnd: function (cid, exitCode, specs, retries) { + // }, + /** + * Gets executed just before initialising the webdriver session and test framework. It allows you + * to manipulate configurations depending on the capability or spec. + * @param {Object} config wdio configuration object + * @param {Array.} capabilities list of capabilities details + * @param {Array.} specs List of spec file paths that are to be run + * @param {String} cid worker id (e.g. 0-0) + */ + // beforeSession: function (config, capabilities, specs, cid) { + // }, + /** + * Gets executed before test execution begins. At this point you can access to all global + * variables like `browser`. It is the perfect place to define custom commands. + * @param {Array.} capabilities list of capabilities details + * @param {Array.} specs List of spec file paths that are to be run + * @param {Object} browser instance of created browser/device session + */ + // before: function (capabilities, specs) { + // }, + /** + * Runs before a WebdriverIO command gets executed. + * @param {String} commandName hook command name + * @param {Array} args arguments that command would receive + */ + // beforeCommand: function (commandName, args) { + // }, + /** + * Hook that gets executed before the suite starts + * @param {Object} suite suite details + */ + // beforeSuite: function (suite) { + // }, + /** + * Function to be executed before a test (in Mocha/Jasmine) starts. + */ + // beforeTest: function (test, context) { + // }, + /** + * Hook that gets executed _before_ a hook within the suite starts (e.g. runs before calling + * beforeEach in Mocha) + */ + // beforeHook: function (test, context) { + // }, + /** + * Hook that gets executed _after_ a hook within the suite starts (e.g. runs after calling + * afterEach in Mocha) + */ + // afterHook: function (test, context, { error, result, duration, passed, retries }) { + // }, + /** + * Function to be executed after a test (in Mocha/Jasmine only) + * @param {Object} test test object + * @param {Object} context scope object the test was executed with + * @param {Error} result.error error object in case the test fails, otherwise `undefined` + * @param {Any} result.result return object of test function + * @param {Number} result.duration duration of test + * @param {Boolean} result.passed true if test has passed, otherwise false + * @param {Object} result.retries informations to spec related retries, e.g. `{ attempts: 0, limit: 0 }` + */ + // afterTest: function(test, context, { error, result, duration, passed, retries }) { + // }, + + + /** + * Hook that gets executed after the suite has ended + * @param {Object} suite suite details + */ + // afterSuite: function (suite) { + // }, + /** + * Runs after a WebdriverIO command gets executed + * @param {String} commandName hook command name + * @param {Array} args arguments that command would receive + * @param {Number} result 0 - command success, 1 - command error + * @param {Object} error error object if any + */ + // afterCommand: function (commandName, args, result, error) { + // }, + /** + * Gets executed after all tests are done. You still have access to all global variables from + * the test. + * @param {Number} result 0 - test pass, 1 - test fail + * @param {Array.} capabilities list of capabilities details + * @param {Array.} specs List of spec file paths that ran + */ + // after: function (result, capabilities, specs) { + // }, + /** + * Gets executed right after terminating the webdriver session. + * @param {Object} config wdio configuration object + * @param {Array.} capabilities list of capabilities details + * @param {Array.} specs List of spec file paths that ran + */ + // afterSession: function (config, capabilities, specs) { + // }, + /** + * Gets executed after all workers got shut down and the process is about to exit. An error + * thrown in the onComplete hook will result in the test run failing. + * @param {Object} exitCode 0 - success, 1 - fail + * @param {Object} config wdio configuration object + * @param {Array.} capabilities list of capabilities details + * @param {} results object containing test results + */ + // onComplete: function(exitCode, config, capabilities, results) { + // }, + /** + * Gets executed when a refresh happens. + * @param {String} oldSessionId session ID of the old session + * @param {String} newSessionId session ID of the new session + */ + // onReload: function(oldSessionId, newSessionId) { + // } +} diff --git a/wdio/v7/tsconfig.json b/wdio/v7/tsconfig.json new file mode 100644 index 00000000..312a48e1 --- /dev/null +++ b/wdio/v7/tsconfig.json @@ -0,0 +1,106 @@ +{ + "compilerOptions": { + /* Visit https://aka.ms/tsconfig to read more about this file */ + /* Projects */ + // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ + // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ + // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ + // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ + // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ + // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ + /* Language and Environment */ + "target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ + // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ + // "jsx": "preserve", /* Specify what JSX code is generated. */ + // "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */ + // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ + // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ + // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ + // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ + // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ + // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ + // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ + // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ + /* Modules */ + "module": "commonjs", /* Specify what module code is generated. */ + // "rootDir": "./", /* Specify the root folder within your source files. */ + // "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */ + // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ + // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ + // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ + // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ + "types": [ + "node", + "webdriverio/async", + "@wdio/mocha-framework", + "expect-webdriverio" + ], + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ + // "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */ + // "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */ + // "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */ + // "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */ + // "resolveJsonModule": true, /* Enable importing .json files. */ + // "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */ + // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ + /* JavaScript Support */ + // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ + // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ + // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ + /* Emit */ + // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ + // "declarationMap": true, /* Create sourcemaps for d.ts files. */ + // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ + // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ + // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ + // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ + // "outDir": "./", /* Specify an output folder for all emitted files. */ + // "removeComments": true, /* Disable emitting comments. */ + // "noEmit": true, /* Disable emitting files from a compilation. */ + // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ + // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */ + // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ + // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ + // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ + // "newLine": "crlf", /* Set the newline character for emitting files. */ + // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ + // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ + // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ + // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ + // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ + // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ + /* Interop Constraints */ + // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ + // "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */ + // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ + "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ + // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ + "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ + /* Type Checking */ + "strict": true, + // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ + // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ + // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ + // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ + // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ + // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ + // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ + // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ + // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ + // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ + // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ + // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ + // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ + // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ + // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ + // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ + // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ + /* Completeness */ + // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ + "skipLibCheck": true /* Skip type checking all .d.ts files. */ + } +} \ No newline at end of file diff --git a/wdio/v8/package.json b/wdio/v8/package.json new file mode 100644 index 00000000..cb72e607 --- /dev/null +++ b/wdio/v8/package.json @@ -0,0 +1,24 @@ +{ + "name": "testomat-example-wdio-v8", + "version": "1.0.0", + "description": "Example project for Testomat.io with WebdriverIO v8", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "wdio": "wdio run ./wdio.conf.ts" + }, + "author": "Oleksandr Pelykh / Testomat.io", + "license": "ISC", + "devDependencies": { + "@wdio/cli": "^8.32.1", + "@wdio/local-runner": "^8.32.1", + "@wdio/mocha-framework": "^8.31.1", + "@wdio/spec-reporter": "^8.31.1", + "ts-node": "^10.9.2", + "typescript": "^5.3.3", + "wdio-html-nice-reporter": "^8.1.4" + }, + "dependencies": { + "@testomatio/reporter": "^1.2.0" + } +} diff --git a/wdio/v8/test/pageobjects/login.page.ts b/wdio/v8/test/pageobjects/login.page.ts new file mode 100644 index 00000000..4b548287 --- /dev/null +++ b/wdio/v8/test/pageobjects/login.page.ts @@ -0,0 +1,41 @@ +import { $ } from '@wdio/globals' +import Page from './page'; + +/** + * sub page containing specific selectors and methods for a specific page + */ +class LoginPage extends Page { + /** + * define selectors using getter methods + */ + public get inputUsername () { + return $('#username'); + } + + public get inputPassword () { + return $('#password'); + } + + public get btnSubmit () { + return $('button[type="submit"]'); + } + + /** + * a method to encapsule automation code to interact with the page + * e.g. to login using username and password + */ + public async login (username: string, password: string) { + await this.inputUsername.setValue(username); + await this.inputPassword.setValue(password); + await this.btnSubmit.click(); + } + + /** + * overwrite specific options to adapt it to page object + */ + public open () { + return super.open('login'); + } +} + +export default new LoginPage(); diff --git a/wdio/v8/test/pageobjects/page.ts b/wdio/v8/test/pageobjects/page.ts new file mode 100644 index 00000000..6a20f1ce --- /dev/null +++ b/wdio/v8/test/pageobjects/page.ts @@ -0,0 +1,15 @@ +import { browser } from '@wdio/globals' + +/** +* main page object containing all methods, selectors and functionality +* that is shared across all page objects +*/ +export default class Page { + /** + * Opens a sub page of the page + * @param path path of the sub page (e.g. /path/to/page.html) + */ + public open (path: string) { + return browser.url(`https://the-internet.herokuapp.com/${path}`) + } +} diff --git a/wdio/v8/test/pageobjects/secure.page.ts b/wdio/v8/test/pageobjects/secure.page.ts new file mode 100644 index 00000000..b8d60f89 --- /dev/null +++ b/wdio/v8/test/pageobjects/secure.page.ts @@ -0,0 +1,16 @@ +import { $ } from '@wdio/globals' +import Page from './page'; + +/** + * sub page containing specific selectors and methods for a specific page + */ +class SecurePage extends Page { + /** + * define selectors using getter methods + */ + public get flashAlert () { + return $('#flash'); + } +} + +export default new SecurePage(); diff --git a/wdio/v8/test/specs/test.e2e.ts b/wdio/v8/test/specs/test.e2e.ts new file mode 100644 index 00000000..c002a5ed --- /dev/null +++ b/wdio/v8/test/specs/test.e2e.ts @@ -0,0 +1,15 @@ +import { expect } from '@wdio/globals' +import LoginPage from '../pageobjects/login.page' +import SecurePage from '../pageobjects/secure.page' + +describe('My Login application', () => { + it('should login with valid credentials', async () => { + await LoginPage.open() + + await LoginPage.login('tomsmith', 'SuperSecretPassword!') + await expect(SecurePage.flashAlert).toBeExisting() + await expect(SecurePage.flashAlert).toHaveTextContaining( + 'You logged into a secure area!') + }) +}) + diff --git a/wdio/v8/tsconfig.json b/wdio/v8/tsconfig.json new file mode 100644 index 00000000..910b6790 --- /dev/null +++ b/wdio/v8/tsconfig.json @@ -0,0 +1,30 @@ +{ + "compilerOptions": { + "moduleResolution": "node", + "module": "commonjs", + "target": "es2022", + "lib": [ + "es2022", + "dom" + ], + "types": [ + "node", + "@wdio/globals/types", + "expect-webdriverio", + "@wdio/mocha-framework" + ], + "skipLibCheck": true, + "noEmit": true, + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + }, + "include": [ + "test", + "wdio.conf.ts" + ] +} \ No newline at end of file diff --git a/wdio/v8/wdio.conf.ts b/wdio/v8/wdio.conf.ts new file mode 100644 index 00000000..12d4fbb7 --- /dev/null +++ b/wdio/v8/wdio.conf.ts @@ -0,0 +1,309 @@ +import type { Options } from '@wdio/types' +const testomatio = require('@testomatio/reporter/lib/adapter/webdriver'); + +export const config: Options.Testrunner = { + // + // ==================== + // Runner Configuration + // ==================== + // WebdriverIO supports running e2e tests as well as unit and component tests. + runner: 'local', + autoCompileOpts: { + autoCompile: true, + tsNodeOpts: { + project: './tsconfig.json', + transpileOnly: true + } + }, + + // + // ================== + // Specify Test Files + // ================== + // Define which test specs should run. The pattern is relative to the directory + // of the configuration file being run. + // + // The specs are defined as an array of spec files (optionally using wildcards + // that will be expanded). The test for each spec file will be run in a separate + // worker process. In order to have a group of spec files run in the same worker + // process simply enclose them in an array within the specs array. + // + // The path of the spec files will be resolved relative from the directory of + // of the config file unless it's absolute. + // + specs: [ + './test/specs/**/*.ts' + ], + // Patterns to exclude. + exclude: [ + // 'path/to/excluded/files' + ], + // + // ============ + // Capabilities + // ============ + // Define your capabilities here. WebdriverIO can run multiple capabilities at the same + // time. Depending on the number of capabilities, WebdriverIO launches several test + // sessions. Within your capabilities you can overwrite the spec and exclude options in + // order to group specific specs to a specific capability. + // + // First, you can define how many instances should be started at the same time. Let's + // say you have 3 different capabilities (Chrome, Firefox, and Safari) and you have + // set maxInstances to 1; wdio will spawn 3 processes. Therefore, if you have 10 spec + // files and you set maxInstances to 10, all spec files will get tested at the same time + // and 30 processes will get spawned. The property handles how many capabilities + // from the same test should run tests. + // + maxInstances: 10, + // + // If you have trouble getting all important capabilities together, check out the + // Sauce Labs platform configurator - a great tool to configure your capabilities: + // https://saucelabs.com/platform/platform-configurator + // + capabilities: [{ + browserName: 'chrome' + }], + + // + // =================== + // Test Configurations + // =================== + // Define all options that are relevant for the WebdriverIO instance here + // + // Level of logging verbosity: trace | debug | info | warn | error | silent + logLevel: 'warn', + // + // Set specific log levels per logger + // loggers: + // - webdriver, webdriverio + // - @wdio/browserstack-service, @wdio/devtools-service, @wdio/sauce-service + // - @wdio/mocha-framework, @wdio/jasmine-framework + // - @wdio/local-runner + // - @wdio/sumologic-reporter + // - @wdio/cli, @wdio/config, @wdio/utils + // Level of logging verbosity: trace | debug | info | warn | error | silent + // logLevels: { + // webdriver: 'info', + // '@wdio/appium-service': 'info' + // }, + // + // If you only want to run your tests until a specific amount of tests have failed use + // bail (default is 0 - don't bail, run all tests). + bail: 0, + // + // Set a base URL in order to shorten url command calls. If your `url` parameter starts + // with `/`, the base url gets prepended, not including the path portion of your baseUrl. + // If your `url` parameter starts without a scheme or `/` (like `some/path`), the base url + // gets prepended directly. + // baseUrl: 'http://localhost:8080', + // + // Default timeout for all waitFor* commands. + waitforTimeout: 10000, + // + // Default timeout in milliseconds for request + // if browser driver or grid doesn't send response + connectionRetryTimeout: 120000, + // + // Default request retries count + connectionRetryCount: 3, + // + // Test runner services + // Services take over a specific job you don't want to take care of. They enhance + // your test setup with almost no effort. Unlike plugins, they don't add new + // commands. Instead, they hook themselves up into the test process. + // services: [], + // + // Framework you want to run your specs with. + // The following are supported: Mocha, Jasmine, and Cucumber + // see also: https://webdriver.io/docs/frameworks + // + // Make sure you have the wdio adapter package for the specific framework installed + // before running any tests. + framework: 'mocha', + + // + // The number of times to retry the entire specfile when it fails as a whole + // specFileRetries: 1, + // + // Delay in seconds between the spec file retry attempts + // specFileRetriesDelay: 0, + // + // Whether or not retried spec files should be retried immediately or deferred to the end of the queue + // specFileRetriesDeferred: false, + // + // Test reporter for stdout. + // The only one supported by default is 'dot' + // see also: https://webdriver.io/docs/dot-reporter + reporters: ['spec', + [testomatio, { + apiKey: `${process.env.TESTOMATIO}` + }] + ], + + // Options to be passed to Mocha. + // See the full list at http://mochajs.org/ + mochaOpts: { + ui: 'bdd', + timeout: 60000 + }, + + // + // ===== + // Hooks + // ===== + // WebdriverIO provides several hooks you can use to interfere with the test process in order to enhance + // it and to build services around it. You can either apply a single function or an array of + // methods to it. If one of them returns with a promise, WebdriverIO will wait until that promise got + // resolved to continue. + /** + * Gets executed once before all workers get launched. + * @param {object} config wdio configuration object + * @param {Array.} capabilities list of capabilities details + */ + // onPrepare: function (config, capabilities) { + // }, + /** + * Gets executed before a worker process is spawned and can be used to initialize specific service + * for that worker as well as modify runtime environments in an async fashion. + * @param {string} cid capability id (e.g 0-0) + * @param {object} caps object containing capabilities for session that will be spawn in the worker + * @param {object} specs specs to be run in the worker process + * @param {object} args object that will be merged with the main configuration once worker is initialized + * @param {object} execArgv list of string arguments passed to the worker process + */ + // onWorkerStart: function (cid, caps, specs, args, execArgv) { + // }, + /** + * Gets executed just after a worker process has exited. + * @param {string} cid capability id (e.g 0-0) + * @param {number} exitCode 0 - success, 1 - fail + * @param {object} specs specs to be run in the worker process + * @param {number} retries number of retries used + */ + // onWorkerEnd: function (cid, exitCode, specs, retries) { + // }, + /** + * Gets executed just before initialising the webdriver session and test framework. It allows you + * to manipulate configurations depending on the capability or spec. + * @param {object} config wdio configuration object + * @param {Array.} capabilities list of capabilities details + * @param {Array.} specs List of spec file paths that are to be run + * @param {string} cid worker id (e.g. 0-0) + */ + // beforeSession: function (config, capabilities, specs, cid) { + // }, + /** + * Gets executed before test execution begins. At this point you can access to all global + * variables like `browser`. It is the perfect place to define custom commands. + * @param {Array.} capabilities list of capabilities details + * @param {Array.} specs List of spec file paths that are to be run + * @param {object} browser instance of created browser/device session + */ + // before: function (capabilities, specs) { + // }, + /** + * Runs before a WebdriverIO command gets executed. + * @param {string} commandName hook command name + * @param {Array} args arguments that command would receive + */ + // beforeCommand: function (commandName, args) { + // }, + /** + * Hook that gets executed before the suite starts + * @param {object} suite suite details + */ + // beforeSuite: function (suite) { + // }, + /** + * Function to be executed before a test (in Mocha/Jasmine) starts. + */ + // beforeTest: function (test, context) { + // }, + /** + * Hook that gets executed _before_ a hook within the suite starts (e.g. runs before calling + * beforeEach in Mocha) + */ + // beforeHook: function (test, context, hookName) { + // }, + /** + * Hook that gets executed _after_ a hook within the suite starts (e.g. runs after calling + * afterEach in Mocha) + */ + // afterHook: function (test, context, { error, result, duration, passed, retries }, hookName) { + // }, + /** + * Function to be executed after a test (in Mocha/Jasmine only) + * @param {object} test test object + * @param {object} context scope object the test was executed with + * @param {Error} result.error error object in case the test fails, otherwise `undefined` + * @param {*} result.result return object of test function + * @param {number} result.duration duration of test + * @param {boolean} result.passed true if test has passed, otherwise false + * @param {object} result.retries information about spec related retries, e.g. `{ attempts: 0, limit: 0 }` + */ + // afterTest: function(test, context, { error, result, duration, passed, retries }) { + // }, + + + /** + * Hook that gets executed after the suite has ended + * @param {object} suite suite details + */ + // afterSuite: function (suite) { + // }, + /** + * Runs after a WebdriverIO command gets executed + * @param {string} commandName hook command name + * @param {Array} args arguments that command would receive + * @param {number} result 0 - command success, 1 - command error + * @param {object} error error object if any + */ + // afterCommand: function (commandName, args, result, error) { + // }, + /** + * Gets executed after all tests are done. You still have access to all global variables from + * the test. + * @param {number} result 0 - test pass, 1 - test fail + * @param {Array.} capabilities list of capabilities details + * @param {Array.} specs List of spec file paths that ran + */ + // after: function (result, capabilities, specs) { + // }, + /** + * Gets executed right after terminating the webdriver session. + * @param {object} config wdio configuration object + * @param {Array.} capabilities list of capabilities details + * @param {Array.} specs List of spec file paths that ran + */ + // afterSession: function (config, capabilities, specs) { + // }, + /** + * Gets executed after all workers got shut down and the process is about to exit. An error + * thrown in the onComplete hook will result in the test run failing. + * @param {object} exitCode 0 - success, 1 - fail + * @param {object} config wdio configuration object + * @param {Array.} capabilities list of capabilities details + * @param {} results object containing test results + */ + // onComplete: function(exitCode, config, capabilities, results) { + // }, + /** + * Gets executed when a refresh happens. + * @param {string} oldSessionId session ID of the old session + * @param {string} newSessionId session ID of the new session + */ + // onReload: function(oldSessionId, newSessionId) { + // } + /** + * Hook that gets executed before a WebdriverIO assertion happens. + * @param {object} params information about the assertion to be executed + */ + // beforeAssertion: function(params) { + // } + /** + * Hook that gets executed after a WebdriverIO assertion happened. + * @param {object} params information about the assertion that was executed, including its results + */ + // afterAssertion: function(params) { + // } +} From 1c5e931cd28a92675957627cf6cdff3bef838f85 Mon Sep 17 00:00:00 2001 From: Oleksandr Pelykh Date: Wed, 20 Mar 2024 20:33:00 +0200 Subject: [PATCH 63/64] fix wdio 8 (#64) --- wdio/v8/test/specs/test.e2e.ts | 15 --------------- wdio/v8/test/specs/test.test.ts | 20 ++++++++++++++++++++ wdio/v8/test/specs/test2.test.ts | 19 +++++++++++++++++++ 3 files changed, 39 insertions(+), 15 deletions(-) delete mode 100644 wdio/v8/test/specs/test.e2e.ts create mode 100644 wdio/v8/test/specs/test.test.ts create mode 100644 wdio/v8/test/specs/test2.test.ts diff --git a/wdio/v8/test/specs/test.e2e.ts b/wdio/v8/test/specs/test.e2e.ts deleted file mode 100644 index c002a5ed..00000000 --- a/wdio/v8/test/specs/test.e2e.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { expect } from '@wdio/globals' -import LoginPage from '../pageobjects/login.page' -import SecurePage from '../pageobjects/secure.page' - -describe('My Login application', () => { - it('should login with valid credentials', async () => { - await LoginPage.open() - - await LoginPage.login('tomsmith', 'SuperSecretPassword!') - await expect(SecurePage.flashAlert).toBeExisting() - await expect(SecurePage.flashAlert).toHaveTextContaining( - 'You logged into a secure area!') - }) -}) - diff --git a/wdio/v8/test/specs/test.test.ts b/wdio/v8/test/specs/test.test.ts new file mode 100644 index 00000000..358c57c8 --- /dev/null +++ b/wdio/v8/test/specs/test.test.ts @@ -0,0 +1,20 @@ +import { expect } from '@wdio/globals' +import LoginPage from '../pageobjects/login.page' +import SecurePage from '../pageobjects/secure.page' + +describe('My Login application', () => { + it('username input should be visible', async () => { + await LoginPage.open() + + await expect(LoginPage.inputUsername).toBeExisting() + }); + + it('should login with valid credentials', async () => { + await LoginPage.open() + + await LoginPage.login('tomsmith', 'SuperSecretPassword!') + await expect(SecurePage.flashAlert).toBeExisting() + await expect(SecurePage.flashAlert).toHaveTextContaining( + 'You logged into a secure area!') + }); +}) diff --git a/wdio/v8/test/specs/test2.test.ts b/wdio/v8/test/specs/test2.test.ts new file mode 100644 index 00000000..4427f1e0 --- /dev/null +++ b/wdio/v8/test/specs/test2.test.ts @@ -0,0 +1,19 @@ +import { expect } from '@wdio/globals' + +function sum(a: number, b: number): number { + return a + b; +} + +describe('Function test', () => { + it('1+2', async () => { + expect(sum(1, 2)).toBe(3); + }); + + it('3+4', async () => { + expect(sum(3, 4)).toBe(7); + }); + + it('5+6', async () => { + expect(sum(5, 6)).toBe(11); + }); +}) From 7d34bd008ed52e9989095db95222804eec998590 Mon Sep 17 00:00:00 2001 From: Oleksandr Pelykh Date: Wed, 10 Apr 2024 10:14:46 +0300 Subject: [PATCH 64/64] Wdio cucumber (#65) * upd wdio test script * add wdio-cucumber * add wdio cucumber * upd wdio cucumber --- wdio/cucumber/.gitignore | 1 + wdio/cucumber/README.md | 5 + wdio/cucumber/features/addNumbers.feature | 13 ++ wdio/cucumber/package.json | 34 +++++ wdio/cucumber/src/steps/script.ts | 3 + wdio/cucumber/src/steps/then.ts | 8 + wdio/cucumber/src/steps/when.ts | 7 + wdio/cucumber/tsconfig.json | 75 +++++++++ wdio/cucumber/wdio.conf.ts | 176 ++++++++++++++++++++++ wdio/v8/package.json | 3 +- 10 files changed, 323 insertions(+), 2 deletions(-) create mode 100644 wdio/cucumber/.gitignore create mode 100644 wdio/cucumber/README.md create mode 100644 wdio/cucumber/features/addNumbers.feature create mode 100644 wdio/cucumber/package.json create mode 100644 wdio/cucumber/src/steps/script.ts create mode 100644 wdio/cucumber/src/steps/then.ts create mode 100644 wdio/cucumber/src/steps/when.ts create mode 100644 wdio/cucumber/tsconfig.json create mode 100644 wdio/cucumber/wdio.conf.ts diff --git a/wdio/cucumber/.gitignore b/wdio/cucumber/.gitignore new file mode 100644 index 00000000..3c3629e6 --- /dev/null +++ b/wdio/cucumber/.gitignore @@ -0,0 +1 @@ +node_modules diff --git a/wdio/cucumber/README.md b/wdio/cucumber/README.md new file mode 100644 index 00000000..05adc039 --- /dev/null +++ b/wdio/cucumber/README.md @@ -0,0 +1,5 @@ +### Prepare +`npm i` + +### Run +`npm test` \ No newline at end of file diff --git a/wdio/cucumber/features/addNumbers.feature b/wdio/cucumber/features/addNumbers.feature new file mode 100644 index 00000000..3207c7f5 --- /dev/null +++ b/wdio/cucumber/features/addNumbers.feature @@ -0,0 +1,13 @@ +@Sad520f3c +Feature: Add number + I want to check if two numbers can be added + + @some-tag1 @Tf37e0731 + Scenario: Add two positive numbers + When I add two numbers 2 and 3 + Then return the sum of 2 and 3 as 5 + + @some-tag2 @T6e4a0318 + Scenario: Should fail + When I add two numbers 2 and 3 + Then return the sum of 2 and 3 as 6 diff --git a/wdio/cucumber/package.json b/wdio/cucumber/package.json new file mode 100644 index 00000000..2c2e6343 --- /dev/null +++ b/wdio/cucumber/package.json @@ -0,0 +1,34 @@ +{ + "author": "Oleksandr Pelykh", + "license": "MIT", + "name": "wdio-cucumber-example", + "version": "0.0.1", + "description": "Boilerplate project to run WebdriverIO tests with Cucumber and report to Testomat.io", + "scripts": { + "test": "wdio run wdio.conf.ts" + }, + "devDependencies": { + "@testomatio/reporter": "file:../../../reporter/testomatio-reporter-1.3.0.tgz", + "@types/chai": "^4.3.14", + "@typescript-eslint/eslint-plugin": "^7.0.1", + "@typescript-eslint/parser": "^7.0.1", + "@wdio/cli": "^8.32.2", + "@wdio/config": "^8.32.2", + "@wdio/cucumber-framework": "^8.32.2", + "@wdio/globals": "^8.32.2", + "@wdio/local-runner": "^8.32.2", + "@wdio/spec-reporter": "^8.32.2", + "@wdio/static-server-service": "^8.32.2", + "eslint": "^8.56.0", + "eslint-config-airbnb-base": "^15.0.0", + "eslint-plugin-import": "^2.29.1", + "eslint-plugin-wdio": "^8.24.12", + "npm-run-all": "^4.1.5", + "ts-node": "^10.9.2", + "typescript": "^5.3.3", + "webdriverio": "^8.32.2" + }, + "dependencies": { + "chai": "^5.1.0" + } +} diff --git a/wdio/cucumber/src/steps/script.ts b/wdio/cucumber/src/steps/script.ts new file mode 100644 index 00000000..089f6946 --- /dev/null +++ b/wdio/cucumber/src/steps/script.ts @@ -0,0 +1,3 @@ +export function addTwoNumbers(a: number, b: number) { + return a + b; +} diff --git a/wdio/cucumber/src/steps/then.ts b/wdio/cucumber/src/steps/then.ts new file mode 100644 index 00000000..9e69d38b --- /dev/null +++ b/wdio/cucumber/src/steps/then.ts @@ -0,0 +1,8 @@ +import { Then } from '@cucumber/cucumber'; +import { addTwoNumbers } from './script'; +import assert from 'assert'; + +// result should correspond to the sum of the two numbers +Then(/^return the sum of (\d+) and (\d+) as (\d+)$/, (num1: number, num2: number, result: number) => { + assert(addTwoNumbers(num1, num2) === result, 'The result is not correct'); +}); diff --git a/wdio/cucumber/src/steps/when.ts b/wdio/cucumber/src/steps/when.ts new file mode 100644 index 00000000..adc1b8fe --- /dev/null +++ b/wdio/cucumber/src/steps/when.ts @@ -0,0 +1,7 @@ +import { When } from '@cucumber/cucumber'; +import { addTwoNumbers } from './script'; + +When( + /I add two numbers (\d+) and (\d+)/, + addTwoNumbers +); diff --git a/wdio/cucumber/tsconfig.json b/wdio/cucumber/tsconfig.json new file mode 100644 index 00000000..f3f81fed --- /dev/null +++ b/wdio/cucumber/tsconfig.json @@ -0,0 +1,75 @@ +{ + "compilerOptions": { + /* Visit https://aka.ms/tsconfig.json to read more about this file */ + + /* Basic Options */ + // "incremental": true, /* Enable incremental compilation */ + "module": "CommonJS", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */ + "target": "es2022", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */ + // "lib": [], /* Specify library files to be included in the compilation. */ + "allowJs": true, /* Allow javascript files to be compiled. */ + // "checkJs": true, /* Report errors in .js files. */ + // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ + // "declaration": true, /* Generates corresponding '.d.ts' file. */ + // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ + // "sourceMap": true, /* Generates corresponding '.map' file. */ + // "outFile": "./", /* Concatenate and emit output to single file. */ + // "outDir": "./", /* Redirect output structure to the directory. */ + // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ + // "composite": true, /* Enable project compilation */ + // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ + // "removeComments": true, /* Do not emit comments to output. */ + // "noEmit": true, /* Do not emit outputs. */ + // "importHelpers": true, /* Import emit helpers from 'tslib'. */ + // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ + "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ + + /* Strict Type-Checking Options */ + "strict": true, /* Enable all strict type-checking options. */ + // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* Enable strict null checks. */ + // "strictFunctionTypes": true, /* Enable strict checking of function types. */ + // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ + // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ + // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ + // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ + + /* Additional Checks */ + // "noUnusedLocals": true, /* Report errors on unused locals. */ + // "noUnusedParameters": true, /* Report errors on unused parameters. */ + // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ + // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ + // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ + + /* Module Resolution Options */ + "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ + // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ + // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ + // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ + // "typeRoots": [], /* List of folders to include type definitions from. */ + "types": [ + "node", + "@wdio/globals/types", + "@wdio/cucumber-framework", + "expect-webdriverio" + ], /* Type declaration files to be included in compilation. */ + "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ + "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ + // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + + /* Source Map Options */ + // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ + // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ + + /* Experimental Options */ + // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ + // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ + + /* Advanced Options */ + "skipLibCheck": true, /* Skip type checking of declaration files. */ + "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ + } +} diff --git a/wdio/cucumber/wdio.conf.ts b/wdio/cucumber/wdio.conf.ts new file mode 100644 index 00000000..facf733d --- /dev/null +++ b/wdio/cucumber/wdio.conf.ts @@ -0,0 +1,176 @@ +import type { Options } from '@wdio/types' +// const testomatio = require('@testomatio/reporter/lib/adapter/wdio-cucumber'); +const testomatio = require('@testomatio/reporter/lib/adapter/webdriver'); + +export const config: Options.Testrunner = { + // + // ==================== + // Runner Configuration + // ==================== + // WebdriverIO supports running e2e tests as well as unit and component tests. + runner: 'local', + autoCompileOpts: { + autoCompile: true, + tsNodeOpts: { + project: './tsconfig.json', + transpileOnly: true + } + }, + + // + // ================== + // Specify Test Files + // ================== + // Define which test specs should run. The pattern is relative to the directory + // of the configuration file being run. + // + // The specs are defined as an array of spec files (optionally using wildcards + // that will be expanded). The test for each spec file will be run in a separate + // worker process. In order to have a group of spec files run in the same worker + // process simply enclose them in an array within the specs array. + // + // The path of the spec files will be resolved relative from the directory of + // of the config file unless it's absolute. + // + specs: [ + './features/**/*.feature', + ], + // Patterns to exclude. + exclude: [ + // 'path/to/excluded/files' + ], + // + // ============ + // Capabilities + // ============ + // Define your capabilities here. WebdriverIO can run multiple capabilities at the same + // time. Depending on the number of capabilities, WebdriverIO launches several test + // sessions. Within your capabilities you can overwrite the spec and exclude options in + // order to group specific specs to a specific capability. + // + // First, you can define how many instances should be started at the same time. Let's + // say you have 3 different capabilities (Chrome, Firefox, and Safari) and you have + // set maxInstances to 1; wdio will spawn 3 processes. Therefore, if you have 10 spec + // files and you set maxInstances to 10, all spec files will get tested at the same time + // and 30 processes will get spawned. The property handles how many capabilities + // from the same test should run tests. + // + maxInstances: 5, + // + // If you have trouble getting all important capabilities together, check out the + // Sauce Labs platform configurator - a great tool to configure your capabilities: + // https://saucelabs.com/platform/platform-configurator + // + capabilities: [{ + browserName: 'chrome', + 'goog:chromeOptions': { + args: ['headless', 'disable-gpu'] + } + }], + + // + // =================== + // Test Configurations + // =================== + // Define all options that are relevant for the WebdriverIO instance here + // + // Level of logging verbosity: trace | debug | info | warn | error | silent + logLevel: 'warn', + // + // Set specific log levels per logger + // loggers: + // - webdriver, webdriverio + // - @wdio/browserstack-service, @wdio/devtools-service, @wdio/sauce-service + // - @wdio/mocha-framework, @wdio/jasmine-framework + // - @wdio/local-runner + // - @wdio/sumologic-reporter + // - @wdio/cli, @wdio/config, @wdio/utils + // Level of logging verbosity: trace | debug | info | warn | error | silent + // logLevels: { + // webdriver: 'info', + // '@wdio/appium-service': 'info' + // }, + // + // If you only want to run your tests until a specific amount of tests have failed use + // bail (default is 0 - don't bail, run all tests). + bail: 0, + // + // Set a base URL in order to shorten url command calls. If your `url` parameter starts + // with `/`, the base url gets prepended, not including the path portion of your baseUrl. + // If your `url` parameter starts without a scheme or `/` (like `some/path`), the base url + // gets prepended directly. + // baseUrl: 'http://localhost:8080', + // + // Default timeout for all waitFor* commands. + waitforTimeout: 10000, + // + // Default timeout in milliseconds for request + // if browser driver or grid doesn't send response + connectionRetryTimeout: 120000, + // + // Default request retries count + connectionRetryCount: 3, + // + // Test runner services + // Services take over a specific job you don't want to take care of. They enhance + // your test setup with almost no effort. Unlike plugins, they don't add new + // commands. Instead, they hook themselves up into the test process. + // services: [], + // + // Framework you want to run your specs with. + // The following are supported: Mocha, Jasmine, and Cucumber + // see also: https://webdriver.io/docs/frameworks + // + // Make sure you have the wdio adapter package for the specific framework installed + // before running any tests. + framework: 'cucumber', + + cucumberOpts: { + backtrace: false, + requireModule: [], + failAmbiguousDefinitions: true, + failFast: false, + ignoreUndefinedDefinitions: false, + names: [], + snippets: true, + source: true, + profile: [], + require: [ + './src/steps/then.ts', + './src/steps/when.ts', + ], + scenarioLevelReporter: false, + order: 'defined', + snippetSyntax: undefined, + strict: true, + tagExpression: 'not @Pending', + tagsInTitle: false, + timeout: 20000, + } as WebdriverIO.CucumberOpts, + + // + // The number of times to retry the entire specfile when it fails as a whole + // specFileRetries: 1, + // + // Delay in seconds between the spec file retry attempts + // specFileRetriesDelay: 0, + // + // Whether or not retried spec files should be retried immediately or deferred to the end of the queue + // specFileRetriesDeferred: false, + // + // Test reporter for stdout. + // The only one supported by default is 'dot' + // see also: https://webdriver.io/docs/dot-reporter + reporters: ['spec', + [testomatio, { + apiKey: `${process.env.TESTOMATIO}` + }] + ], + + // Options to be passed to Mocha. + // See the full list at http://mochajs.org/ + mochaOpts: { + ui: 'bdd', + timeout: 60000 + }, +} diff --git a/wdio/v8/package.json b/wdio/v8/package.json index cb72e607..5b4dcfb8 100644 --- a/wdio/v8/package.json +++ b/wdio/v8/package.json @@ -4,8 +4,7 @@ "description": "Example project for Testomat.io with WebdriverIO v8", "main": "index.js", "scripts": { - "test": "echo \"Error: no test specified\" && exit 1", - "wdio": "wdio run ./wdio.conf.ts" + "test": "npx start-test-run -c 'wdio run ./wdio.conf.ts'" }, "author": "Oleksandr Pelykh / Testomat.io", "license": "ISC",