diff --git a/.gitignore b/.gitignore index a03b29c..407dd4e 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,8 @@ .vscode .docker /docker-compose.yml +cypress.config.mjs +tests/screenshots # NPM node_modules diff --git a/README.md b/README.md index 120aa1a..902fe99 100644 --- a/README.md +++ b/README.md @@ -4,9 +4,13 @@ [![version][version-badge]][package] -This is a support package that helps in writing end-to-end tests for the [Joomla CMS](https://joomla.org) and its extensions with the frontend testing tool [Cypress](/https://www.cypress.io/). +This is a support package that helps in writing end-to-end tests for the [Joomla CMS](https://joomla.org) and its extensions with the frontend testing tool [Cypress](https://www.cypress.io/). The Joomla default branch and all active development [branches](https://github.com/joomla/joomla-cms/branches) are supported. +Instructions for testing the package are provided and documented in the +[tests](tests) sub-folder. +The test suite also shows the use of the Cypress custom commands. + ## Installation `joomla-cypress` is distributed with [npm](https://npmjs.com/) and works with the latest version of Cypress. diff --git a/images/test-run.png b/images/test-run.png new file mode 100644 index 0000000..4dff121 Binary files /dev/null and b/images/test-run.png differ diff --git a/images/test-user.png b/images/test-user.png new file mode 100644 index 0000000..6c534a8 Binary files /dev/null and b/images/test-user.png differ diff --git a/package-lock.json b/package-lock.json index da60d22..b5afb83 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "1.2.0", "license": "GPL-2.0+", "dependencies": { - "cypress": "^13.15.0", + "cypress": "^13.16.0", "cypress-file-upload": "^5.0.8" } }, @@ -24,9 +24,9 @@ } }, "node_modules/@cypress/request": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@cypress/request/-/request-3.0.5.tgz", - "integrity": "sha512-v+XHd9XmWbufxF1/bTaVm2yhbxY+TB4YtWRqF2zaXBlDNMkls34KiATz0AVDLavL3iB6bQk9/7n3oY1EoLSWGA==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@cypress/request/-/request-3.0.6.tgz", + "integrity": "sha512-fi0eVdCOtKu5Ed6+E8mYxUF6ZTFJDZvHogCBelM0xVXmrDEkyM22gRArQzq1YcHPm1V47Vf/iAD+WgVdUlJCGg==", "license": "Apache-2.0", "dependencies": { "aws-sign2": "~0.7.0", @@ -44,7 +44,7 @@ "performance-now": "^2.1.0", "qs": "6.13.0", "safe-buffer": "^5.1.2", - "tough-cookie": "^4.1.3", + "tough-cookie": "^5.0.0", "tunnel-agent": "^0.6.0", "uuid": "^8.3.2" }, @@ -331,16 +331,15 @@ } }, "node_modules/call-bind": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", - "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", + "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", "license": "MIT", "dependencies": { + "call-bind-apply-helpers": "^1.0.0", "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", "get-intrinsic": "^1.2.4", - "set-function-length": "^1.2.1" + "set-function-length": "^1.2.2" }, "engines": { "node": ">= 0.4" @@ -349,6 +348,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.0.tgz", + "integrity": "sha512-CCKAP2tkPau7D3GE8+V8R6sQubA9R5foIzGp+85EXCVSCivuxBNAWqcpn72PKYiIcqoViv/kcUDpaEIMBVi1lQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/caseless": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", @@ -393,9 +405,9 @@ } }, "node_modules/ci-info": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", - "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.1.0.tgz", + "integrity": "sha512-HutrvTNsF48wnxkzERIXOe5/mlcfFcbfCmwcg6CJnizbSue78AbDt+1cgl26zwn61WFxhcPykPfZrbqjGmBb4A==", "funding": [ { "type": "github", @@ -534,13 +546,13 @@ } }, "node_modules/cypress": { - "version": "13.15.0", - "resolved": "https://registry.npmjs.org/cypress/-/cypress-13.15.0.tgz", - "integrity": "sha512-53aO7PwOfi604qzOkCSzNlWquCynLlKE/rmmpSPcziRH6LNfaDUAklQT6WJIsD8ywxlIy+uVZsnTMCCQVd2kTw==", + "version": "13.16.1", + "resolved": "https://registry.npmjs.org/cypress/-/cypress-13.16.1.tgz", + "integrity": "sha512-17FtCaz0cx7ssWYKXzGB0Vub8xHwpVPr+iPt2fHhLMDhVAPVrplD+rTQsZUsfb19LVBn5iwkEUFjQ1yVVJXsLA==", "hasInstallScript": true, "license": "MIT", "dependencies": { - "@cypress/request": "^3.0.4", + "@cypress/request": "^3.0.6", "@cypress/xvfb": "^1.2.4", "@types/sinonjs__fake-timers": "8.1.1", "@types/sizzle": "^2.3.2", @@ -551,6 +563,7 @@ "cachedir": "^2.3.0", "chalk": "^4.1.0", "check-more-types": "^2.24.0", + "ci-info": "^4.0.0", "cli-cursor": "^3.1.0", "cli-table3": "~0.6.1", "commander": "^6.2.1", @@ -565,7 +578,6 @@ "figures": "^3.2.0", "fs-extra": "^9.1.0", "getos": "^3.2.1", - "is-ci": "^3.0.1", "is-installed-globally": "~0.4.0", "lazy-ass": "^1.6.0", "listr2": "^3.8.3", @@ -580,6 +592,7 @@ "semver": "^7.5.3", "supports-color": "^8.1.1", "tmp": "~0.2.3", + "tree-kill": "1.2.2", "untildify": "^4.0.0", "yauzl": "^2.10.0" }, @@ -663,6 +676,20 @@ "node": ">=0.4.0" } }, + "node_modules/dunder-proto": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.0.tgz", + "integrity": "sha512-9+Sj30DIu+4KvHqMfLUGLFYL2PkURSYMVXJyXe92nFRvlYq5hBjLEhblKB+vkd/WVlUYMWigiY07T91Fkk0+4A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/ecc-jsbn": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", @@ -702,13 +729,10 @@ } }, "node_modules/es-define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", - "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", "license": "MIT", - "dependencies": { - "get-intrinsic": "^1.2.4" - }, "engines": { "node": ">= 0.4" } @@ -879,16 +903,19 @@ } }, "node_modules/get-intrinsic": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", - "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.5.tgz", + "integrity": "sha512-Y4+pKa7XeRUPWFNvOOYHkRYrfzW07oraURSvjDmRVOJ748OrVmeXtpE4+GCEHncjCjkTxPNRt8kEbxDhsn6VTg==", "license": "MIT", "dependencies": { + "call-bind-apply-helpers": "^1.0.0", + "dunder-proto": "^1.0.0", + "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2" }, "engines": { "node": ">= 0.4" @@ -946,12 +973,12 @@ } }, "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", "license": "MIT", - "dependencies": { - "get-intrinsic": "^1.1.3" + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -984,22 +1011,10 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/has-proto": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", - "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", "license": "MIT", "engines": { "node": ">= 0.4" @@ -1081,18 +1096,6 @@ "node": ">=10" } }, - "node_modules/is-ci": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-3.0.1.tgz", - "integrity": "sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==", - "license": "MIT", - "dependencies": { - "ci-info": "^3.2.0" - }, - "bin": { - "is-ci": "bin.js" - } - }, "node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", @@ -1391,9 +1394,9 @@ } }, "node_modules/object-inspect": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", - "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==", + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.3.tgz", + "integrity": "sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==", "license": "MIT", "engines": { "node": ">= 0.4" @@ -1504,12 +1507,6 @@ "integrity": "sha512-F2JHgJQ1iqwnHDcQjVBsq3n/uoaFL+iPW/eAeL7kVxy/2RrWaN4WroKjjvbsoRtv0ftelNyC01bjRhn/bhcf4A==", "license": "MIT" }, - "node_modules/psl": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", - "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", - "license": "MIT" - }, "node_modules/pump": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz", @@ -1520,15 +1517,6 @@ "once": "^1.3.1" } }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/qs": { "version": "6.13.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", @@ -1544,12 +1532,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/querystringify": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", - "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", - "license": "MIT" - }, "node_modules/request-progress": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/request-progress/-/request-progress-3.0.0.tgz", @@ -1559,12 +1541,6 @@ "throttleit": "^1.0.0" } }, - "node_modules/requires-port": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", - "license": "MIT" - }, "node_modules/restore-cursor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", @@ -1797,6 +1773,24 @@ "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", "license": "MIT" }, + "node_modules/tldts": { + "version": "6.1.66", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.66.tgz", + "integrity": "sha512-l3ciXsYFel/jSRfESbyKYud1nOw7WfhrBEF9I3UiarYk/qEaOOwu3qXNECHw4fHGHGTEOuhf/VdKgoDX5M/dhQ==", + "license": "MIT", + "dependencies": { + "tldts-core": "^6.1.66" + }, + "bin": { + "tldts": "bin/cli.js" + } + }, + "node_modules/tldts-core": { + "version": "6.1.66", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.66.tgz", + "integrity": "sha512-s07jJruSwndD2X8bVjwioPfqpIc1pDTzszPe9pL1Skbh4bjytL85KNQ3tolqLbCvpQHawIsGfFi9dgerWjqW4g==", + "license": "MIT" + }, "node_modules/tmp": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz", @@ -1807,27 +1801,24 @@ } }, "node_modules/tough-cookie": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz", - "integrity": "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.0.0.tgz", + "integrity": "sha512-FRKsF7cz96xIIeMZ82ehjC3xW2E+O2+v11udrDYewUbszngYhsGa8z6YUMMzO9QJZzzyd0nGGXnML/TReX6W8Q==", "license": "BSD-3-Clause", "dependencies": { - "psl": "^1.1.33", - "punycode": "^2.1.1", - "universalify": "^0.2.0", - "url-parse": "^1.5.3" + "tldts": "^6.1.32" }, "engines": { - "node": ">=6" + "node": ">=16" } }, - "node_modules/tough-cookie/node_modules/universalify": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", - "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", + "node_modules/tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", "license": "MIT", - "engines": { - "node": ">= 4.0.0" + "bin": { + "tree-kill": "cli.js" } }, "node_modules/tslib": { @@ -1891,16 +1882,6 @@ "node": ">=8" } }, - "node_modules/url-parse": { - "version": "1.5.10", - "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", - "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", - "license": "MIT", - "dependencies": { - "querystringify": "^2.1.1", - "requires-port": "^1.0.0" - } - }, "node_modules/uuid": { "version": "8.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", diff --git a/package.json b/package.json index d73f320..97821e0 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,10 @@ "description": "Helpers for using Cypress with Joomla for testing", "main": "src/index.js", "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" + "clean": "rm -rf node_modules tests/cypress.config.mjs tests/screenshots", + "test": "npx cypress run --config-file tests/cypress.config.mjs", + "open": "npx cypress open --config-file tests/cypress.config.mjs", + "cypress:install": "cypress install" }, "repository": { "type": "git", @@ -24,7 +27,7 @@ "src" ], "dependencies": { - "cypress": "^13.15.0", + "cypress": "^13.16.0", "cypress-file-upload": "^5.0.8" } } diff --git a/tests/README.md b/tests/README.md new file mode 100644 index 0000000..94fb7aa --- /dev/null +++ b/tests/README.md @@ -0,0 +1,160 @@ +# Cypress tests + +The testing of the NPM module `joomla-cypress` is implemented using Cypress as well. +Each Cypress custom command is executed at least once and the result is checked. + +## Running the tests + +Requirements: +* PHP (a Joomla-supported version) +* Composer +* NPM +* Cypress +* Web Server +* Joomla sources +* A database and PHP extensions for the database + +Ensure Firefox is installed system-wide (check with `npx cypress info`). + +For Joomla sources the following commands are sufficient: +``` +git clone https://github.com/joomla/joomla-cms --depth 1 +cd joomla-cms +composer install +npm ci +``` +No additional configuration for Joomla is necessary, as the parameters +are defined in the `joomla-cypress/tests/cypress.config.mjs` Cypress configuration file. +A Joomla installation is not required +if you start by running the entire test suite or the `tests/e2e/joomla.cy.js` test spec. + +> [!NOTE] +> The custom command `installJoomlaMultilingualSite` deletes the Joomla `installation` folder. +> You have to restore the `installation` folder after each test run, +> from saved copy or from GitHub and with recreating the `installation/template/css` folder: +> ``` +> git checkout installation +> npm ci +> ``` + +### Installation + +Install the JavaScript dependencies: +``` +git clone https://github.com/joomla-projects/joomla-cypress +cd joomla-cypress +npm ci +``` + +### Configuration + +Create the Cypress configuration file from the distribution template: +``` +cp tests/cypress.config.dist.mjs tests/cypress.config.mjs +``` + +Adapt the environment variables in the file `cypress.config.mjs`, +they should point to the site, user data and database environment. +Most configurations are the same as with Joomla System Tests `cypress.config.mjs` file. +Additional variables are +* `installationPath` contains the file system path to your Joomla installation + (used to delete the `configuration.php` file before installing Joomla) +* `instance` specifies the Joomla major and minor version number, e.g. `53` for Joomla 5.3. + +### Tests + +Running all tests headless: +``` +npm test +``` + +Running the test suite without the `installLanguage` and `installJoomlaMultilingualSite` tests, +if the language package is not yet available, on Windows PowerShell: +``` +$env:CYPRESS_SKIP_INSTALL_LANGUAGES=1 +npm test +``` + +Running only `user.cy.js` test spec file: +``` +npx cypress run --spec tests/e2e/user.cy.js --config-file tests/cypress.config.mjs +``` + +Running tests with local Cypress GUI: +``` +npm run open +``` + +## Running the tests using JBT + +JBT has integrated the testing of the NPM module `joomla-cypress`. +In the `installation/joomla-cypress` directory the latest main branch version is already installed. +Furthermore Joomla, databases, Cypress and sample module are already provided with JBT. +Restoring the `installation` folder, running `npm ci` and handing over the +sample module for installation are covered for you. +This is implemented in the scripts `tests`, `cypress` and `patch`. + +Patch `installation/joomla-cypress` e.g. with pull request `#37 Fixing installJoomlaMultilingualSite()`: +``` +scripts/patch installation joomla-cypress-37 +``` + +Running the test suite headless for Joomla 5.2: +``` +scripts/test 52 joomla-cypress +``` + +![Cypress test run](../images/test-run.png) + +Running only `user.cy.js` test spec file and watching the progress with NoVNC: +``` +scripts/test 52 joomla-cypress tests/e2e/user.cy.js novnc +``` + +![Cypress test user.cy.js file](../images/test-user.png) + +Running the test suite for Joomla 5.3 and 6.0 +without the `installLanguage` and `installJoomlaMultilingualSite` tests, +if the language package is not yet available, on Unix-based systems: + +``` +CYPRESS_SKIP_INSTALL_LANGUAGES=1 scripts/test 53 60 joomla-cypress +``` + +Running tests with local Cypress GUI, e.g. on macOS or Linux: +``` +scripts/cypress 52 joomla-cypress local +``` + +Running on Joomla 6.0 without the tests they install languages, +with the Cypress GUI, e.g. on Windows 11 WSL2 Ubuntu: +``` +CYPRESS_SKIP_INSTALL_LANGUAGES=1 scripts/cypress 60 joomla-cypress +``` + +# Troubleshooting + +1. Ensure that you have installed the version of `joomla-cypress` to be tested + and patched if necessary. +2. If one test step fails, the target Joomla installation may reach a state + causes subsequent tests to fail. Dependecies include + deleting Joomla `installation` folder, deleting the `configuration.php` file, + cancelling guided tours and disabling Joomla statistics. + In such cases open the target Joomla installation administration + to investigate or reinstall the target Joomla sources. +3. For failed tests you can inspect the screenshots in `tests/screenshots` folder or + run Cypresss GUI to observe the issue. +4. If the `installExtensionFromFolder()` test fails as the Joomla web server cannot + find the `mod_hello_world` package folder, set the folder with the enviroment + variable `CYPRESS_SERVER_UPLOAD_FOLDER`. For example on Unix-based systems: + ``` + export CYPRESS_SERVER_UPLOAD_FOLDER=/users/alice/joomla-cypress/tests/fixtures/mod_hello_world + ``` +5. For Joomla development versions that are not yet been released, + the tests `installLanguage` and `installJoomlaMultilingualSite` + fail with the error message `Unable to detect manifest file` + because the language packages are not yet available. + Exclude these tests by setting an environment variable on Unix-based systems: + ``` + export CYPRESS_SKIP_INSTALL_LANGUAGES=1 + ``` diff --git a/tests/cypress.config.dist.mjs b/tests/cypress.config.dist.mjs new file mode 100644 index 0000000..bfd6657 --- /dev/null +++ b/tests/cypress.config.dist.mjs @@ -0,0 +1,50 @@ +/** + * To run tests on your own installation, copy this file as 'cypress.config.mjs'. + * Then, review and adjust the configuration values to suit your setup. + */ +import { defineConfig } from 'cypress'; + +export default defineConfig({ + env: { + // As from Joomla System Tests + sitename: 'Joomla CMS Test', + name: 'jane doe', + email: 'admin@example.com', + username: 'ci-admin', + password: 'joomla-17082005', + db_type: 'MySQLi', + db_host: 'localhost', + db_port: '', + db_name: 'test_joomla', + db_user: 'joomla', + db_password: 'joomla', + db_prefix: 'jos_', + smtp_host: 'localhost', + smtp_port: '1025', + cmsPath: '.', + // Added for joomla-cypress tests + instance: 52, // Joomla major and minor version number + installationPath: 'C:/laragon/www/joomla52' // Joomla installation path + }, + e2e: { + baseUrl: 'http://localhost:9500', + supportFile: false, + // Just in case we are coming from a failed installation test, start with the Joomla installation + specPattern: ['tests/e2e/joomla.cy.js', 'tests/e2e/*.cy.js'], + screenshotsFolder: 'tests/screenshots', + fixturesFolder: 'tests/fixtures', + // Use Firefox as Joomla default and to prevent useless macOS Electron font warnings + // (Cypress 13.16.0 added 'defaultBrowser' option) + defaultBrowser: 'firefox', + setupNodeEvents(on, config) { + // For example, in a German environment, force the use of Firefox with British English. + on("before:browser:launch", (browser, launchOptions) => { + if (browser.family === "firefox") { + launchOptions.preferences["intl.accept_languages"] = "en-GB"; + } + return launchOptions; + }); + return config; + }, + }, +}); diff --git a/tests/e2e/common.cy.js b/tests/e2e/common.cy.js new file mode 100644 index 0000000..9237f6e --- /dev/null +++ b/tests/e2e/common.cy.js @@ -0,0 +1,76 @@ +/** + * common.cy.js – Cypress test suite for ../src/common.js custom commands + */ + +import { registerCommands } from '../support/registerCommands'; +import { config, caughtJavaScriptExceptions } from '../support/setup'; + +registerCommands(); + +beforeEach(() => { + caughtJavaScriptExceptions(); + cy.doAdministratorLogin(config.username, config.password, false); +}); + +describe("Test the Cypress custom commands from 'common.js' file", () => { + /* + * Stupid waits are inserted as a work-around for the error: + * 'Expected to find element: `#jform_title`, but never found it.'. + * These delays allow enough time for the iframe content to fully load, + * Increased from one to two second for slow Intel N95 miniPC. + * This approach works consistently across headless, noVNC, and GUI modes. + * + * If you have a more reliable solution, you are very welcome to improve this implementation. + */ + it('isIframeLoaded()', () => { + // Visit admin's Home Dashboard + cy.visit('/administrator/index.php?option=com_cpanel'); + // Click 'Add module to the dashboard' + cy.get('button.cpanel-add-module').click(); + // Now working with the iFrame + cy.wait(2000); + cy.get('iframe').iframe().then(($body) => { + cy.wrap($body).within(() => { + cy.contains('Frontend Link').click(); + }); + }); + // Open following iFrame + cy.wait(2000); + cy.get('iframe').iframe().then(($body) => { + cy.wrap($body).within(() => { + cy.get('#jform_title').clear().type('test module'); + if (config.instance > 44) { + // Button 'Save & Close' inside iFrame + cy.clickToolbarButton('save & close'); + } + }); + }); + if (config.instance <= 44) { + // Button 'Save & Close' inside iFrame + cy.clickToolbarButton('save & close'); + } + // Delete the module from the Home Dashboard again + // On slow machines (e.g. Intel N95 miniPC) we need to wait that the module is on the page. + cy.wait(2000); + cy.visit('/administrator/index.php?option=com_cpanel'); + // Find the

with the text "test module" + cy.contains('h3', 'test module').should('be.visible').then(($h3) => { + // Traverse to the parent container and find the dropdown button + cy.wrap($h3) + .closest('.card-header') + .within(() => { + // Click the dropdown button + cy.get('button[data-bs-toggle="dropdown"]').click(); + }); + // Now find the dropdown menu that is directly related to this button + cy.wrap($h3) + .closest('.card-header') + .find('.dropdown-menu') + .should('be.visible') + .within(() => { + // Click on the "Unpublish" button + cy.contains('button', 'Unpublish').click(); + }); + }); + }); +}); diff --git a/tests/e2e/extensions.cy.js b/tests/e2e/extensions.cy.js new file mode 100644 index 0000000..fcf1b14 --- /dev/null +++ b/tests/e2e/extensions.cy.js @@ -0,0 +1,124 @@ +/** + * extensions.cy.js – Cypress test suite for ../src/extensions.js custom commands + */ + +import { registerCommands } from '../support/registerCommands'; +// Add attachFile() Cypress custom command, which is used in installExtensionFromFileUpload() +import 'cypress-file-upload'; +import { config, caughtJavaScriptExceptions } from '../support/setup'; + +registerCommands(); + +beforeEach(() => { + caughtJavaScriptExceptions(); + cy.doAdministratorLogin(config.username, config.password, false); +}); + +// cypress/fixtures contains folder and packed as ZIP +const modHelloWorld = 'mod_hello_world'; + +describe("Test the Cypress custom commands from 'extensions.js' file", () => { + it('installExtensionFromFolder()', () => { + // Install the extension using 'CYPRESS_SERVER_UPLOAD_FOLDER' environment variable + let serverUploadFolder = Cypress.env('SERVER_UPLOAD_FOLDER'); + if (!serverUploadFolder) { + // Fallback to using the fixtures folder + serverUploadFolder = `${Cypress.config('fixturesFolder')}/${modHelloWorld}`; + } + cy.log(`Using upload folder: ${serverUploadFolder}`); + // Install the extension using absolute path + cy.installExtensionFromFolder(serverUploadFolder); + // Deleting the module, which confirms it was installed + cy.visit('administrator/index.php?option=com_installer&view=manage'); + cy.searchForItem(modHelloWorld); + cy.get('#cb0').click(); + if (config.instance > 44) { + // Click on 'Actions' first to dropdown-toggle show + cy.clickToolbarButton('action'); + } + cy.contains('Uninstall').click(); + if (config.instance > 44) { + // Confirm + cy.contains('Yes').click(); + } + cy.checkForSystemMessage('Uninstalling the module was successful.'); + }); + + it('installExtensionFromFileUpload()', () => { + // File is taken by attachFile() from 'cypress/fixtures' folder + cy.installExtensionFromFileUpload(`${modHelloWorld}.zip`); + // Deleting the module, which confirms it was installed + cy.visit('administrator/index.php?option=com_installer&view=manage'); + cy.searchForItem(modHelloWorld); + cy.get('#cb0').click(); + if (config.instance > 44) { + // Click on 'Actions' first to dropdown-toggle show + cy.clickToolbarButton('action'); + } + cy.contains('Uninstall').click(); + if (config.instance > 44) { + // Confirm + cy.contains('Yes').click(); + } + cy.checkForSystemMessage('Uninstalling the module was successful.'); + }); + + it('installExtensionFromUrl()', () => { + cy.installExtensionFromUrl('https://downloads.joomla.org/language-packs/translations-joomla5/downloads/joomla5-afrikaans/5-0-2-1/af-za_joomla_lang_full_5-0-2v1-zip?format=zip'); + cy.checkForSystemMessage('Installation of the package was successful.'); + }); + + it('uninstallExtension()', () => { + cy.installExtensionFromUrl('https://downloads.joomla.org/language-packs/translations-joomla5/downloads/joomla5-afrikaans/5-0-2-1/af-za_joomla_lang_full_5-0-2v1-zip?format=zip'); + cy.uninstallExtension('Afrikaans (af-ZA) Taalpakket'); + // includes the verification for 'was successful' message and searching extension and checking for 'No Matching Results' + }); + + /* + * For develpment branches with not yet released Joomla versions there are no + * language packages available and installation will fail with 'Unable to detect manifest file.'. + * Testing this spec can be prevented with 'export CYPRESS_SKIP_INSTALL_LANGUAGES=1'. + */ + if (!Cypress.env('SKIP_INSTALL_LANGUAGES')) { + it('installLanguage()', () => { + // Grüezi mitenand id Schwiiz + cy.installLanguage('de-CH'); + cy.checkForSystemMessage('Installation of the language pack was successful.'); + }); + } + + it('enablePlugin()', () => { + const pluginName = 'Authentication - LDAP'; + // Enabling only works once, so just in case we disbale first + cy.visit('/administrator/index.php?option=com_plugins'); + cy.searchForItem(pluginName); + cy.get('#cb0').click(); + cy.get('#toolbar-unpublish button').click(); + cy.enablePlugin(pluginName); + cy.checkForSystemMessage('Plugin enabled.'); + }); + + it('setModulePosition()', () => { + cy.setModulePosition('Login Form', 'sidebar-right'); + cy.checkForSystemMessage('Module saved.'); + cy.get('span.badge.bg-info').should('contain.text', 'sidebar-right'); + }); + + it('publishModule()', () => { + cy.publishModule('Login Form'); + cy.checkForSystemMessage('Module published.'); + // Check for icon-publish + cy.get('a.js-grid-item-action.tbody-icon.active') + .should('have.attr', 'data-item-id', 'cb0') + .within(() => { + cy.get('span.icon-publish').should('exist'); + }); + }); + + it('displayModuleOnAllPages()', () => { + cy.displayModuleOnAllPages('Breadcrumbs'); + cy.checkForSystemMessage('Module saved.'); + // Verify the 6th ('Pages' column) contains the text 'All' + cy.get('tr[data-draggable-group="breadcrumbs"] td').eq(5).should('contain.text', 'All'); + }); +}); diff --git a/tests/e2e/joomla.cy.js b/tests/e2e/joomla.cy.js new file mode 100644 index 0000000..e5bf654 --- /dev/null +++ b/tests/e2e/joomla.cy.js @@ -0,0 +1,82 @@ +/** + * joomla.cy.js – Cypress test suite for ../src/joomla.js custom commands + */ + +import { registerCommands } from '../support/registerCommands'; +import { config, deleteJoomlaConfigurationFile, caughtJavaScriptExceptions } from '../support/setup'; + +registerCommands(); + +beforeEach(() => { + caughtJavaScriptExceptions(); +}); + +describe("Test the Cypress custom commands from 'joomla.js' file", () => { + it('installJoomla()', () => { + deleteJoomlaConfigurationFile(config); + cy.installJoomla(config); + // Frontend and backend available? + cy.visit('/'); + cy.doAdministratorLogin(config.username, config.password, false); + }); + + if (config.instance >= 51) { + it('cancelTour()', () => { + const startButton = '.shepherd-button-primary'; // Guided Tour 'Start' button + + deleteJoomlaConfigurationFile(config); + cy.installJoomla(config); + cy.doAdministratorLogin(config.username, config.password, false); + cy.get(startButton).should('exist'); + cy.cancelTour(); + cy.get(startButton).should('not.exist'); + }); + } + + it('disableStatistics()', () => { + deleteJoomlaConfigurationFile(config); + cy.installJoomla(config); + cy.doAdministratorLogin(config.username, config.password, false); + if (config.instance >= 51) { + cy.cancelTour(); + } + cy.visit('/administrator'); + cy.contains('p', 'Enable Joomla Statistics?').should('be.visible'); + cy.disableStatistics(); + cy.visit('/administrator'); + cy.contains('p', 'Enable Joomla Statistics?').should('not.exist'); + }); + + it('setErrorReportingToDevelopment()', () => { + cy.doAdministratorLogin(config.username, config.password, false); + cy.setErrorReportingToDevelopment(); + cy.checkForSystemMessage('Configuration saved.'); + cy.get('select#jform_error_reporting').should('have.value', 'maximum'); + }); + + /* + * For develpment branches with not yet released Joomla versions there are no + * language packages available and installation will fail with 'Unable to detect manifest file.'. + * Testing this spec can be prevented with 'export CYPRESS_SKIP_INSTALL_LANGUAGES=1'. + */ + if (!Cypress.env('SKIP_INSTALL_LANGUAGES')) { + /* + * This must be the final Joomla installation test, as it deletes the installation folder. + * The Joomla installation folder must be restored before the next test run. + * (Alternatively, we could save and restore the installation directory with Node.js scripting). + */ + it('installJoomlaMultilingualSite()', () => { + deleteJoomlaConfigurationFile(config); + cy.installJoomlaMultilingualSite(config); + // Frontend and backend available? + cy.visit('/'); + cy.doAdministratorLogin(config.username, config.password, false); + // Cancel tour is needed to have next tests running + if (config.instance >= 51) { + cy.cancelTour(); + } + // Disable statistics is needed to have next tests running + cy.disableStatistics(); + }); + } +}); diff --git a/tests/e2e/support.cy.js b/tests/e2e/support.cy.js new file mode 100644 index 0000000..00d9bf2 --- /dev/null +++ b/tests/e2e/support.cy.js @@ -0,0 +1,168 @@ +/** + * support.cy.js – Cypress test suite for ../src/support.js custom commands + */ + +import { registerCommands } from '../support/registerCommands'; +import { config, caughtJavaScriptExceptions } from '../support/setup'; + +registerCommands(); + +beforeEach(() => { + caughtJavaScriptExceptions(); + cy.doAdministratorLogin(config.username, config.password, false); +}); + +describe("Test the Cypress custom commands from 'support.js' file", () => { + it('clickToolbarButton()', () => { + cy.visit('/administrator/index.php?option=com_banners&view=banners'); + cy.clickToolbarButton('new'); + cy.clickToolbarButton('cancel'); + }); + + it('checkForPhpNoticesOrWarnings()', () => { + // Set site offline with custom message containing faked PHP warning + cy.visit('/administrator/index.php?option=com_config'); + cy.contains('.page-title', 'Global Configuration').scrollIntoView(); + cy.get("div[role='tablist'] button[aria-controls='page-site']").click(); + cy.toggleSwitch('Site Offline', 'Yes'); + // Needs to be saved and reload to have 'Offline Message' field available + cy.intercept('index.php?option=com_config*').as('config_save'); + cy.clickToolbarButton('save'); + cy.wait('@config_save'); + cy.contains('.page-title', 'Global Configuration').should('exist'); + cy.checkForSystemMessage('Configuration saved.'); + // Now we can type our fake PHP warning + cy.get('#jform_offline_message') + .clear() + .type('Warning: Test PHP Warning
'); + cy.intercept('index.php?option=com_config*').as('config_save'); + cy.clickToolbarButton('save'); + cy.wait('@config_save'); + cy.contains('.page-title', 'Global Configuration').should('exist'); + cy.checkForSystemMessage('Configuration saved.'); + // Test custom command fails + cy.visit('/', { failOnStatusCode: false }); + let didFail = false; + cy.on('fail', (error) => { + didFail = true; + expect(error.message).to.include('Unwanted PHP Warning'); + // Return false to prevent Cypress from failing the test + return false; + }); + cy.checkForPhpNoticesOrWarnings().then(() => { + // If the command did not fail, explicitly fail the test + if (!didFail) { + throw new Error('Expected PHP warning, but none were found'); + } + }); + }); + + it('Set site online again', () => { + cy.visit('/administrator/index.php?option=com_config'); + cy.contains('.page-title', 'Global Configuration').scrollIntoView(); + cy.get("div[role='tablist'] button[aria-controls='page-site']").click(); + cy.toggleSwitch('Site Offline', 'No'); + cy.intercept('index.php?option=com_config*').as('config_save'); + cy.clickToolbarButton('save'); + cy.wait('@config_save'); + cy.contains('.page-title', 'Global Configuration').should('exist'); + cy.checkForSystemMessage('Configuration saved.'); + }); + + it('checkForSystemMessage()', () => { + cy.visit('/administrator/index.php?option=com_config'); + cy.clickToolbarButton('save'); + cy.checkForSystemMessage('Configuration saved.'); + }); + + it('searchForItem()', () => { + const plugin = 'Authentication - LDAP'; + cy.visit('/administrator/index.php?option=com_plugins'); + cy.searchForItem(plugin); + cy.get('tr.row0').should('contain.text', plugin); + }); + + it('setFilter()', () => { + cy.visit('/administrator/index.php?option=com_plugins'); + cy.setFilter('enabled', 'Enabled'); + cy.get('#pluginList tr[class^="row"]').each(($row) => { + cy.wrap($row).find('td a.active').should('exist'); + }); + }); + + it('checkAllResults()', () => { + // User Actions Log + cy.visit('/administrator/index.php?option=com_actionlogs&view=actionlogs'); + cy.checkAllResults(); + cy.clickToolbarButton('delete'); + if (config.instance > 44) { + cy.contains('Yes').click(); + } + cy.checkForSystemMessage('logs deleted.'); + }); + + it('createMenuItem()', () => { + const menuTitle = `test menu title ${Date.now()}`; + cy.createMenuItem(menuTitle, 'Articles', 'Featured Articles'); + // Delete the menu entry, which also confirms is was created + cy.visit('/administrator/index.php?option=com_menus&view=items'); + cy.searchForItem(menuTitle); + cy.get('#cb0').click(); + cy.clickToolbarButton('action'); + cy.contains('Trash').click(); + cy.setFilter('published', 'Trashed'); + cy.searchForItem(menuTitle); + cy.get('#cb0').click(); + cy.clickToolbarButton('delete'); + if (config.instance > 44) { + cy.contains('Yes').click(); + } + cy.checkForSystemMessage('Menu item deleted.'); + }); + + it('createCategory()', () => { + const category = `test category title ${Date.now()}`; + cy.createCategory(category, 'com_content'); + // Delete the category, which also confirms is was created + cy.visit('/administrator/index.php?option=com_categories&view=categories&extension=com_content'); + cy.searchForItem(category); + cy.get('#cb0').click(); + cy.clickToolbarButton('action'); + cy.contains('Trash').click(); + cy.setFilter('published', 'Trashed'); + cy.searchForItem(category); + cy.get('#cb0').click(); + cy.clickToolbarButton('delete'); + if (config.instance > 44) { + cy.contains('Yes').click(); + } + cy.checkForSystemMessage('Category deleted.'); + }); + + it('selectOptionInFancySelect()', () => { + cy.visit('/administrator/index.php?option=com_config'); + cy.get('button[role="tab"]:contains(Logging)').click(); + cy.selectOptionInFancySelect('#jform_log_priorities', 'Debug'); + cy.clickToolbarButton('save & close'); + // Delete 'Debug' again + cy.visit('/administrator/index.php?option=com_config'); + cy.get('button[role="tab"]:contains(Logging)').click(); + cy.get('.choices__item') + .filter('[data-value="debug"]') + .find('button') + .click(); + cy.clickToolbarButton('save & close'); + }); + + it('toggleSwitch()', () => { + cy.visit('/administrator/index.php?option=com_config'); + cy.get('button[role="tab"]:contains(Logging)').click(); + cy.toggleSwitch('Log Almost Everything', 'Yes'); + cy.clickToolbarButton('save & close'); + cy.visit('/administrator/index.php?option=com_config'); + cy.get('input#jform_log_everything1').should('be.checked'); + // Switch back + cy.toggleSwitch('Log Almost Everything', 'No'); + cy.clickToolbarButton('save & close'); + }); +}); diff --git a/tests/e2e/user.cy.js b/tests/e2e/user.cy.js new file mode 100644 index 0000000..5ac5e25 --- /dev/null +++ b/tests/e2e/user.cy.js @@ -0,0 +1,37 @@ +/** + * user.cy.js – Cypress test suite for ../src/user.js custom commands + */ + +import { registerCommands } from '../support/registerCommands'; +import { config, caughtJavaScriptExceptions } from '../support/setup'; + +registerCommands(); + +beforeEach(() => { + caughtJavaScriptExceptions(); +}); + +describe("Test the Cypress custom commands from 'user.js' file", () => { + it('doAdministratorLogin()', () => { + cy.doAdministratorLogin(config.username, config.password, false); + }); + + it('doAdministratorLogout()', () => { + cy.doAdministratorLogin(config.username, config.password, false); + cy.doAdministratorLogout(); + }); + + it('doFrontendLogin()', () => { + cy.doFrontendLogin(config.username, config.password, false); + }); + + it('doFrontendLogout()', () => { + cy.doFrontendLogin(config.username, config.password, false); + cy.doFrontendLogout(); + }); + + it('createUser()', () => { + cy.doAdministratorLogin(config.username, config.password, false); + cy.createUser('joomla-cypress/test name', `test_${Date.now()}`, config.password, `${Date.now()}@test.com`); + }); +}); diff --git a/tests/fixtures/mod_hello_world.zip b/tests/fixtures/mod_hello_world.zip new file mode 100644 index 0000000..aaa3199 Binary files /dev/null and b/tests/fixtures/mod_hello_world.zip differ diff --git a/tests/fixtures/mod_hello_world/helper.php b/tests/fixtures/mod_hello_world/helper.php new file mode 100644 index 0000000..8049675 --- /dev/null +++ b/tests/fixtures/mod_hello_world/helper.php @@ -0,0 +1,10 @@ +' . htmlspecialchars($helloMessage) . ''; diff --git a/tests/fixtures/mod_hello_world/mod_hello_world.xml b/tests/fixtures/mod_hello_world/mod_hello_world.xml new file mode 100644 index 0000000..7aa6fe1 --- /dev/null +++ b/tests/fixtures/mod_hello_world/mod_hello_world.xml @@ -0,0 +1,16 @@ + + + mod_hello_world + Joomla! project + 2024-11-19 + 1.0.0 + Hello World Joomla Module + + mod_hello_world.php + helper.php + language + + + language/en-GB/en-GB.mod_hello_world.ini + + diff --git a/tests/support/registerCommands.js b/tests/support/registerCommands.js new file mode 100644 index 0000000..b98506e --- /dev/null +++ b/tests/support/registerCommands.js @@ -0,0 +1,21 @@ +/** + * Custom implementation of registerCommands() for joomla-cypress testing. + * This version is based on joomla-cypress/src/index.js but directly + * imports the source files to ensure they are used in the test suite. + */ + +const { joomlaCommands } = require('../../src/joomla'); +const { extensionsCommands } = require('../../src/extensions'); +const { supportCommands } = require('../../src/support'); +const { userCommands } = require('../../src/user'); +const { commonCommands } = require('../../src/common'); + +const registerCommands = () => { + joomlaCommands(); + extensionsCommands(); + supportCommands(); + userCommands(); + commonCommands(); +}; + +module.exports = { registerCommands }; diff --git a/tests/support/setup.js b/tests/support/setup.js new file mode 100644 index 0000000..96f731c --- /dev/null +++ b/tests/support/setup.js @@ -0,0 +1,59 @@ +/** + * setup.js - Common setup for joomla-cypress tests + */ + +export const config = { + // As from Joomla System Tests + sitename: Cypress.env('sitename'), + name: Cypress.env('name'), + username: Cypress.env('username'), + password: Cypress.env('password'), + email: Cypress.env('email'), + db_type: Cypress.env('db_type'), + db_host: Cypress.env('db_host'), + db_port: Cypress.env('db_port'), + db_user: Cypress.env('db_user'), + db_password: Cypress.env('db_password'), + db_name: Cypress.env('db_name'), + db_prefix: Cypress.env('db_prefix'), + // Added for joomla-cypress tests + instance: Cypress.env('instance'), // major and minor, e.g. 52 + installationPath: Cypress.env('installationPath'), // Joomla instance path +}; + +export const caughtJavaScriptExceptions = () => { + /* + * Catch Joomla JavaScript exceptions; otherwise, Cypress will fail. + * Use console.log to view these exceptions, e.g. with JBT scripts/check. + */ + Cypress.on('uncaught:exception', (err, runnable) => { + console.log(`ERROR uncaught:exception err :${err}`); // eslint-disable-line no-console + console.log(`ERROR uncaught:exception runnable :${runnable}`); // eslint-disable-line no-console + return false; + }); +}; + +/* + * Delete Joomla configuration.php to force new installation. + * Delete the file as Node.js one-liner to work on Windows and Unix-based systems. + * Try out the variants one after the other and simply ignore errors. + */ +export const deleteJoomlaConfigurationFile = () => { + // Running 'npx cypress run' OR + cy.exec( + `node -e "require('fs').unlink('${config.installationPath}/configuration.php', (err) => {});"`, + { failOnNonZeroExit: false }, + ); + + // Running with JBT in jbt-cypress container OR + cy.exec( + `node -e "require('fs').unlink('/jbt/joomla-${config.instance}/configuration.php', (err) => {});"`, + { failOnNonZeroExit: false }, + ); + + // Or running JBT with Cypress GUI local from installation/joomla-cypress folder + cy.exec( + `node -e "require('fs').unlink('../../joomla-${config.instance}/configuration.php', (err) => {});"`, + { failOnNonZeroExit: false }, + ); +};