From 1554a645d4a36b2318e24b41d54f5b74c8bd338e Mon Sep 17 00:00:00 2001 From: muhme Date: Sun, 22 Dec 2024 06:31:28 +0100 Subject: [PATCH] [Cypress] Remove Joomla command line dependency MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With the `SefPlugin.cy.js` test specification, the Joomla command line client tool was added to the Joomla system tests for the first time. This is an additional complexity and dependency. Especially if the test environment is containerised and Cypress container has no PHP installed. And PHP version needs to be 8.1 and additional modules like `php-simplexml` are needed. And with the Docker access rights, `configuration.php` must be opened for writing (`chmod 644`). And then there is the locally running Cypress GUI to be configured... Therefore switched from using the Joomla command line client tool to using the existing `config_setParameter` Cypress command. The original creator of [44253](https://github.com/joomla/joomla-cms/pull/44253) could not know as it is not documented (will be solved with another PR and together with enabling `mod_rewrite` and the `AllowOverride All` needed configuration). Since sometimes the first SEF test failed when the entire Joomla System Tests was performed, the following was implemented: * The operations of the `writeRelativeFile` Cypress task are wrapped in a Promise to ensure that they are only resolved when everything is done * The `config_setParameter` Cypress custom command to return a Cypress chainable * Chaining has been implemented everywhere in `SefPlugin.cy.js` For this test spec the Cypress best practice to use `beforeEach` to ensure tests is added to run independently from one another (and needed for other places in the Joomla System Tests – one more PR). As a small side effect the SEF test spec with 9 tests runs faster, e.g. 3 instead of 18 seconds before. Tested with own target Joomla 5.2-dev three times the single test spec on: * Intel macOS 15.2 using Apache/MariaDB * Apple Silicon macOS 15.2 using Apache/MariaDB * Windows 11 24H2 using Laragon/Apache/MySQL ``` npx cypress run --spec tests/System/integration/plugins/system/sef/SefPlugin.cy.js ``` And once the overall test suite: ``` npm run cypress:run ``` --- .../plugins/system/sef/SefPlugin.cy.js | 184 ++++++++++-------- tests/System/plugins/fs.mjs | 41 ++-- tests/System/support/commands/config.mjs | 11 +- 3 files changed, 137 insertions(+), 99 deletions(-) diff --git a/tests/System/integration/plugins/system/sef/SefPlugin.cy.js b/tests/System/integration/plugins/system/sef/SefPlugin.cy.js index 35b27448284b0..b4a81da32df4e 100644 --- a/tests/System/integration/plugins/system/sef/SefPlugin.cy.js +++ b/tests/System/integration/plugins/system/sef/SefPlugin.cy.js @@ -1,77 +1,99 @@ describe('Test that the sef system plugin', () => { - afterEach(() => { - cy.task('deleteRelativePath', '.htaccess'); - cy.exec(`php ${Cypress.env('cmsPath')}/cli/joomla.php config:set sef=true sef_suffix=false sef_rewrite=false`); - cy.db_updateExtensionParameter('enforcesuffix', '1', 'plg_system_sef'); - cy.db_updateExtensionParameter('indexphp', '1', 'plg_system_sef'); - cy.db_updateExtensionParameter('trailingslash', '0', 'plg_system_sef'); - cy.db_updateExtensionParameter('strictrouting', '1', 'plg_system_sef'); - }); + const setSefDefaults = () => cy.task('deleteRelativePath', '.htaccess') + .then(() => cy.config_setParameter('sef', true)) + .then(() => cy.config_setParameter('sef_suffix', false)) + .then(() => cy.config_setParameter('sef_rewrite', false)) + .then(() => cy.db_updateExtensionParameter('enforcesuffix', '1', 'plg_system_sef')) + .then(() => cy.db_updateExtensionParameter('indexphp', '1', 'plg_system_sef')) + .then(() => cy.db_updateExtensionParameter('trailingslash', '0', 'plg_system_sef')) + .then(() => cy.db_updateExtensionParameter('strictrouting', '1', 'plg_system_sef')); + + // Ensure that we always start with a clean SEF default state + beforeEach(() => setSefDefaults()); + + // Return to the clean SEF default state for subsequent Joomla System Tests + afterEach(() => setSefDefaults()); it('can process if option \'sef\' disabled', () => { - cy.exec(`php ${Cypress.env('cmsPath')}/cli/joomla.php config:set sef=false`); - cy.request({ url: '/index.php?option=com_users&view=login', followRedirect: false }).then((response) => { - expect(response.status).to.eq(200); - }); - cy.request({ url: '/index.php/component/users/login', failOnStatusCode: false, followRedirect: false }).then((response) => { - expect(response.status).to.eq(404); - }); + cy.config_setParameter('sef', false) + .then(() => { + cy.request({ url: '/index.php?option=com_users&view=login', followRedirect: false }) + .then((response) => { + expect(response.status).to.eq(200); + }); + cy.request({ url: '/index.php/component/users/login', failOnStatusCode: false, followRedirect: false }) + .then((response) => { + expect(response.status).to.eq(404); + }); + }); }); it('can process if option \'enforcesuffix\' enabled', () => { - cy.exec(`php ${Cypress.env('cmsPath')}/cli/joomla.php config:set sef_suffix=true`); - cy.request({ url: '/index.php/component/users/login', followRedirect: false }).then((response) => { - expect(response.status).to.eq(301); - expect(response.redirectedToUrl).to.match(/\/index\.php\/component\/users\/login\.html$/); - }); - cy.request({ url: '/index.php/component/users/login.html', followRedirect: false }).then((response) => { - expect(response.status).to.eq(200); - }); + cy.config_setParameter('sef_suffix', true) + .then(() => { + cy.request({ url: '/index.php/component/users/login', followRedirect: false }) + .then((response) => { + expect(response.status).to.eq(301); + expect(response.redirectedToUrl).to.match(/\/index\.php\/component\/users\/login\.html$/); + }); + cy.request({ url: '/index.php/component/users/login.html', followRedirect: false }) + .then((response) => { + expect(response.status).to.eq(200); + }); + }); }); it('can process if option \'enforcesuffix\' disabled', () => { - cy.exec(`php ${Cypress.env('cmsPath')}/cli/joomla.php config:set sef_suffix=true`); - cy.db_updateExtensionParameter('enforcesuffix', '0', 'plg_system_sef'); - cy.request({ url: '/index.php/component/users/login', followRedirect: false }).then((response) => { - expect(response.status).to.eq(200); - }); - cy.request({ url: '/index.php/component/users/login.html', followRedirect: false }).then((response) => { - expect(response.status).to.eq(200); - }); + cy.config_setParameter('sef_suffix', true) + .then(() => cy.db_updateExtensionParameter('enforcesuffix', '0', 'plg_system_sef')) + .then(() => cy.request({ url: '/index.php/component/users/login', followRedirect: false })) + .then((response) => { + expect(response.status).to.eq(200); + }) + .then(() => cy.request({ url: '/index.php/component/users/login.html', followRedirect: false })) + .then((response) => { + expect(response.status).to.eq(200); + }); }); it('can process if option \'indexphp\' enabled', () => { - cy.exec(`php ${Cypress.env('cmsPath')}/cli/joomla.php config:set sef_rewrite=true`); - cy.task('copyRelativeFile', { source: 'htaccess.txt', destination: '.htaccess' }); - cy.request({ url: '/index.php/component/users/login', followRedirect: false }).then((response) => { - expect(response.status).to.eq(301); - expect(response.redirectedToUrl).to.match(/(? { - expect(response.status).to.eq(200); - }); + cy.config_setParameter('sef_rewrite', true) + .then(() => cy.task('copyRelativeFile', { source: 'htaccess.txt', destination: '.htaccess' })) + .then(() => cy.request({ url: '/index.php/component/users/login', followRedirect: false })) + .then((response) => { + expect(response.status).to.eq(301); + expect(response.redirectedToUrl).to.match(/(? cy.request({ url: '/component/users/login', followRedirect: false })) + .then((response) => { + expect(response.status).to.eq(200); + }); }); it('can process if option \'indexphp\' disabled', () => { - cy.exec(`php ${Cypress.env('cmsPath')}/cli/joomla.php config:set sef_rewrite=true`); - cy.task('copyRelativeFile', { source: 'htaccess.txt', destination: '.htaccess' }); - cy.db_updateExtensionParameter('indexphp', '0', 'plg_system_sef'); - cy.request({ url: '/index.php/component/users/login', followRedirect: false }).then((response) => { - expect(response.status).to.eq(200); - }); - cy.request({ url: '/component/users/login', followRedirect: false }).then((response) => { - expect(response.status).to.eq(200); - }); + cy.config_setParameter('sef_rewrite', true) + .then(() => cy.task('copyRelativeFile', { source: 'htaccess.txt', destination: '.htaccess' })) + .then(() => cy.db_updateExtensionParameter('indexphp', '0', 'plg_system_sef')) + .then(() => cy.request({ url: '/index.php/component/users/login', followRedirect: false })) + .then((response) => { + expect(response.status).to.eq(200); + }) + .then(() => cy.request({ url: '/component/users/login', followRedirect: false })) + .then((response) => { + expect(response.status).to.eq(200); + }); }); it('can process if option \'trailingslash\' disabled', () => { - cy.request({ url: '/index.php/component/users/login/', followRedirect: false }).then((response) => { - expect(response.status).to.eq(301); - expect(response.redirectedToUrl).to.match(/\/index\.php\/component\/users\/login$/); - }); - cy.request({ url: '/index.php/component/users/login', followRedirect: false }).then((response) => { - expect(response.status).to.eq(200); - }); + cy.request({ url: '/index.php/component/users/login/', followRedirect: false }) + .then((response) => { + expect(response.status).to.eq(301); + expect(response.redirectedToUrl).to.match(/\/index\.php\/component\/users\/login$/); + }); + cy.request({ url: '/index.php/component/users/login', followRedirect: false }) + .then((response) => { + expect(response.status).to.eq(200); + }); cy.visit('/'); cy.get('li.nav-item').contains('Home') .should('have.attr', 'href') @@ -79,14 +101,16 @@ describe('Test that the sef system plugin', () => { }); it('can process if option \'trailingslash\' enabled', () => { - cy.db_updateExtensionParameter('trailingslash', '1', 'plg_system_sef'); - cy.request({ url: '/index.php/component/users/login', followRedirect: false }).then((response) => { - expect(response.status).to.eq(301); - expect(response.redirectedToUrl).to.match(/\/index\.php\/component\/users\/login\/$/); - }); - cy.request({ url: '/index.php/component/users/login/', followRedirect: false }).then((response) => { - expect(response.status).to.eq(200); - }); + cy.db_updateExtensionParameter('trailingslash', '1', 'plg_system_sef') + .then(() => cy.request({ url: '/index.php/component/users/login', followRedirect: false })) + .then((response) => { + expect(response.status).to.eq(301); + expect(response.redirectedToUrl).to.match(/\/index\.php\/component\/users\/login\/$/); + }) + .then(() => cy.request({ url: '/index.php/component/users/login/', followRedirect: false })) + .then((response) => { + expect(response.status).to.eq(200); + }); cy.visit('/'); cy.get('li.nav-item').contains('Home') .should('have.attr', 'href') @@ -94,22 +118,26 @@ describe('Test that the sef system plugin', () => { }); it('can process if option \'strictrouting\' enabled', () => { - cy.request({ url: '/index.php?option=com_users&view=login', followRedirect: false }).then((response) => { - expect(response.status).to.eq(301); - expect(response.redirectedToUrl).to.match(/\/index\.php\/component\/users\/login$/); - }); - cy.request({ url: '/index.php/component/users/login', followRedirect: false }).then((response) => { - expect(response.status).to.eq(200); - }); + cy.request({ url: '/index.php?option=com_users&view=login', followRedirect: false }) + .then((response) => { + expect(response.status).to.eq(301); + expect(response.redirectedToUrl).to.match(/\/index\.php\/component\/users\/login$/); + }); + cy.request({ url: '/index.php/component/users/login', followRedirect: false }) + .then((response) => { + expect(response.status).to.eq(200); + }); }); it('can process if option \'strictrouting\' disabled', () => { - cy.db_updateExtensionParameter('strictrouting', '0', 'plg_system_sef'); - cy.request({ url: '/index.php?option=com_users&view=login', followRedirect: false }).then((response) => { - expect(response.status).to.eq(200); - }); - cy.request({ url: '/index.php/component/users/login', followRedirect: false }).then((response) => { - expect(response.status).to.eq(200); - }); + cy.db_updateExtensionParameter('strictrouting', '0', 'plg_system_sef') + .then(() => cy.request({ url: '/index.php?option=com_users&view=login', followRedirect: false })) + .then((response) => { + expect(response.status).to.eq(200); + }) + .then(() => cy.request({ url: '/index.php/component/users/login', followRedirect: false })) + .then((response) => { + expect(response.status).to.eq(200); + }); }); }); diff --git a/tests/System/plugins/fs.mjs b/tests/System/plugins/fs.mjs index 5dd1a23e3a59c..9bbd65be11321 100644 --- a/tests/System/plugins/fs.mjs +++ b/tests/System/plugins/fs.mjs @@ -36,24 +36,29 @@ function deleteRelativePath(relativePath, config) { * @returns null */ function writeRelativeFile(relativePath, content, config, mode = 0o444) { - const fullPath = join(config.env.cmsPath, relativePath); - // Prologue: Reset process file mode creation mask to ensure the umask value is not subtracted - const oldmask = umask(0); - // Create missing parent directories with 'rwxrwxrwx' - mkdirSync(dirname(fullPath), { recursive: true, mode: 0o777 }); - // Check if the file exists - if (existsSync(fullPath)) { - // Set 'rw-rw-rw-' to be able to overwrite the file - chmodSync(fullPath, 0o666); - } - // Write or overwrite the file on relative path with given content - writeFileSync(fullPath, content); - // Finally set given file mode or default 'r--r--r--' - chmodSync(fullPath, mode); - // Epilogue: Restore process file mode creation mask - umask(oldmask); - - return null; + return new Promise((resolve, reject) => { + try { + const fullPath = join(config.env.cmsPath, relativePath); + // Prologue: Reset process file mode creation mask to ensure the umask value is not subtracted + const oldmask = umask(0); + // Create missing parent directories with 'rwxrwxrwx' + mkdirSync(dirname(fullPath), { recursive: true, mode: 0o777 }); + // Check if the file exists + if (existsSync(fullPath)) { + // Set 'rw-rw-rw-' to be able to overwrite the file + chmodSync(fullPath, 0o666); + } + // Write or overwrite the file on relative path with given content + writeFileSync(fullPath, content); + // Finally set given file mode or default 'r--r--r--' + chmodSync(fullPath, mode); + // Epilogue: Restore process file mode creation mask + umask(oldmask); + resolve(`File successfully written: ${fullPath}`); + } catch (error) { + reject(new Error(`Failed to write file: ${error.message}`)); + } + }); } /** diff --git a/tests/System/support/commands/config.mjs b/tests/System/support/commands/config.mjs index 4987273e8ee49..b044974663cae 100644 --- a/tests/System/support/commands/config.mjs +++ b/tests/System/support/commands/config.mjs @@ -1,7 +1,8 @@ Cypress.Commands.add('config_setParameter', (parameter, value) => { const configPath = `${Cypress.env('cmsPath')}/configuration.php`; - cy.readFile(configPath).then((fileContent) => { + // Return a Cypress chainable for chaining + return cy.readFile(configPath).then((fileContent) => { // Setup the new value const newValue = typeof value === 'string' ? `'${value}'` : value; @@ -11,7 +12,11 @@ Cypress.Commands.add('config_setParameter', (parameter, value) => { // Replace the whole line with the new value const content = fileContent.replace(regex, `public $${parameter} = ${newValue};`); - // Write the modified content back to the configuration file relative to the CMS root folder - cy.task('writeRelativeFile', { path: 'configuration.php', content }); + /* Write the modified content back to the configuration file relative to the CMS root folder and + * wait for the task to complete and chain it. + */ + return cy.task('writeRelativeFile', { path: 'configuration.php', content }).then((result) => { + cy.log(result); // Log success message for debugging + }); }); });