diff --git a/administrator/components/com_templates/src/Model/TemplateModel.php b/administrator/components/com_templates/src/Model/TemplateModel.php index c6f45b68c7a4d..1ca54667aadfe 100644 --- a/administrator/components/com_templates/src/Model/TemplateModel.php +++ b/administrator/components/com_templates/src/Model/TemplateModel.php @@ -1702,14 +1702,17 @@ public function getFont() $explodeArray = explode('/', $relPath); $fileName = end($explodeArray); $path = $this->getBasePath() . base64_decode($app->getInput()->get('file')); + $isModern = $template->xmldata->inheritable || !empty($template->xmldata->parent); if (stristr($client->path, 'administrator') === false) { - $folder = '/templates/'; + $folder = $isModern ? '/media/templates/site/' : '/templates/'; } else { - $folder = '/administrator/templates/'; + $folder = $isModern ? '/media/templates/administrator/' : '/administrator/templates/'; } - $uri = Uri::root(true) . $folder . $template->element; + $uri = $isModern + ? (str_replace('/administrator/', '/', Uri::root(true))) . $folder . $template->element + : Uri::root(true) . $folder . $template->element; if (file_exists(Path::clean($path))) { $font['address'] = $uri . $relPath; diff --git a/administrator/components/com_workflow/src/Controller/DisplayController.php b/administrator/components/com_workflow/src/Controller/DisplayController.php index 030e0aad3ccaf..308fe25b19fe1 100644 --- a/administrator/components/com_workflow/src/Controller/DisplayController.php +++ b/administrator/components/com_workflow/src/Controller/DisplayController.php @@ -70,7 +70,7 @@ public function __construct($config = [], ?MVCFactoryInterface $factory = null, // If extension is not set try to get it from input or throw an exception if (empty($this->extension)) { - $extension = $this->input->getCmd('extension'); + $extension = $this->input->getCmd('extension', ''); $parts = explode('.', $extension); diff --git a/administrator/components/com_workflow/src/Controller/StageController.php b/administrator/components/com_workflow/src/Controller/StageController.php index b113d32619212..9b10299813577 100644 --- a/administrator/components/com_workflow/src/Controller/StageController.php +++ b/administrator/components/com_workflow/src/Controller/StageController.php @@ -77,7 +77,7 @@ public function __construct($config = [], ?MVCFactoryInterface $factory = null, // If extension is not set try to get it from input or throw an exception if (empty($this->extension)) { - $extension = $this->input->getCmd('extension'); + $extension = $this->input->getCmd('extension', ''); $parts = explode('.', $extension); @@ -154,7 +154,6 @@ protected function allowEdit($data = [], $key = 'id') protected function getRedirectToItemAppend($recordId = null, $urlVar = 'id') { $append = parent::getRedirectToItemAppend($recordId); - $append .= '&workflow_id=' . $this->workflowId . '&extension=' . $this->extension . ($this->section ? '.' . $this->section : ''); return $append; diff --git a/administrator/components/com_workflow/src/Controller/StagesController.php b/administrator/components/com_workflow/src/Controller/StagesController.php index bd5c25f62b935..8661fb3cabe66 100644 --- a/administrator/components/com_workflow/src/Controller/StagesController.php +++ b/administrator/components/com_workflow/src/Controller/StagesController.php @@ -87,7 +87,7 @@ public function __construct(array $config = [], ?MVCFactoryInterface $factory = // If extension is not set try to get it from input or throw an exception if (empty($this->extension)) { - $extension = $this->input->getCmd('extension'); + $extension = $this->input->getCmd('extension', ''); $parts = explode('.', $extension); diff --git a/administrator/components/com_workflow/src/Controller/TransitionController.php b/administrator/components/com_workflow/src/Controller/TransitionController.php index e78227550dab3..a7ebc2ad90fd9 100644 --- a/administrator/components/com_workflow/src/Controller/TransitionController.php +++ b/administrator/components/com_workflow/src/Controller/TransitionController.php @@ -77,7 +77,7 @@ public function __construct($config = [], ?MVCFactoryInterface $factory = null, // If extension is not set try to get it from input or throw an exception if (empty($this->extension)) { - $extension = $this->input->getCmd('extension'); + $extension = $this->input->getCmd('extension', ''); $parts = explode('.', $extension); @@ -156,7 +156,7 @@ protected function allowEdit($data = [], $key = 'id') protected function getRedirectToItemAppend($recordId = null, $urlVar = 'id') { $append = parent::getRedirectToItemAppend($recordId); - $append .= '&workflow_id=' . $this->workflowId . '&extension=' . $this->extension; + $append .= '&workflow_id=' . $this->workflowId . '&extension=' . $this->extension . ($this->section ? '.' . $this->section : ''); return $append; } @@ -171,7 +171,7 @@ protected function getRedirectToItemAppend($recordId = null, $urlVar = 'id') protected function getRedirectToListAppend() { $append = parent::getRedirectToListAppend(); - $append .= '&workflow_id=' . $this->workflowId . '&extension=' . $this->extension; + $append .= '&workflow_id=' . $this->workflowId . '&extension=' . $this->extension . ($this->section ? '.' . $this->section : ''); return $append; } diff --git a/administrator/components/com_workflow/src/Controller/TransitionsController.php b/administrator/components/com_workflow/src/Controller/TransitionsController.php index 823be6773f2aa..ac0eee84e2e52 100644 --- a/administrator/components/com_workflow/src/Controller/TransitionsController.php +++ b/administrator/components/com_workflow/src/Controller/TransitionsController.php @@ -85,7 +85,7 @@ public function __construct($config = [], ?MVCFactoryInterface $factory = null, // If extension is not set try to get it from input or throw an exception if (empty($this->extension)) { - $extension = $this->input->getCmd('extension'); + $extension = $this->input->getCmd('extension', ''); $parts = explode('.', $extension); diff --git a/administrator/components/com_workflow/src/Controller/WorkflowController.php b/administrator/components/com_workflow/src/Controller/WorkflowController.php index af0c52bfdfb12..be92f9f72d83e 100644 --- a/administrator/components/com_workflow/src/Controller/WorkflowController.php +++ b/administrator/components/com_workflow/src/Controller/WorkflowController.php @@ -62,7 +62,7 @@ public function __construct($config = [], ?MVCFactoryInterface $factory = null, // If extension is not set try to get it from input or throw an exception if (empty($this->extension)) { - $extension = $this->input->getCmd('extension'); + $extension = $this->input->getCmd('extension', ''); $parts = explode('.', $extension); diff --git a/administrator/components/com_workflow/src/Controller/WorkflowsController.php b/administrator/components/com_workflow/src/Controller/WorkflowsController.php index 3d4f59877b21f..11670c5c241ff 100644 --- a/administrator/components/com_workflow/src/Controller/WorkflowsController.php +++ b/administrator/components/com_workflow/src/Controller/WorkflowsController.php @@ -62,7 +62,7 @@ public function __construct($config = [], ?MVCFactoryInterface $factory = null, // If extension is not set try to get it from input or throw an exception if (empty($this->extension)) { - $extension = $this->input->getCmd('extension'); + $extension = $this->input->getCmd('extension', ''); $parts = explode('.', $extension); diff --git a/administrator/components/com_workflow/src/Dispatcher/Dispatcher.php b/administrator/components/com_workflow/src/Dispatcher/Dispatcher.php index 5633363111185..418f68468407c 100644 --- a/administrator/components/com_workflow/src/Dispatcher/Dispatcher.php +++ b/administrator/components/com_workflow/src/Dispatcher/Dispatcher.php @@ -31,9 +31,8 @@ class Dispatcher extends ComponentDispatcher */ protected function checkAccess() { - $extension = $this->getApplication()->getInput()->getCmd('extension'); - - $parts = explode('.', $extension); + $extension = $this->getApplication()->getInput()->getCmd('extension', ''); + $parts = explode('.', $extension); // Check the user has permission to access this component if in the backend if ($this->app->isClient('administrator') && !$this->app->getIdentity()->authorise('core.manage.workflow', $parts[0])) { diff --git a/administrator/components/com_workflow/src/Model/StageModel.php b/administrator/components/com_workflow/src/Model/StageModel.php index bda6664101ab6..429b8c95eb4d6 100644 --- a/administrator/components/com_workflow/src/Model/StageModel.php +++ b/administrator/components/com_workflow/src/Model/StageModel.php @@ -356,7 +356,7 @@ public function publish(&$pks, $value = 1) */ protected function preprocessForm(Form $form, $data, $group = 'content') { - $extension = Factory::getApplication()->getInput()->get('extension'); + $extension = Factory::getApplication()->getInput()->get('extension', ''); $parts = explode('.', $extension); diff --git a/administrator/components/com_workflow/src/Model/TransitionModel.php b/administrator/components/com_workflow/src/Model/TransitionModel.php index 448625d4dc658..d5c8d7bd8c211 100644 --- a/administrator/components/com_workflow/src/Model/TransitionModel.php +++ b/administrator/components/com_workflow/src/Model/TransitionModel.php @@ -316,7 +316,7 @@ public function getWorkflow() */ protected function preprocessForm(Form $form, $data, $group = 'content') { - $extension = Factory::getApplication()->getInput()->get('extension'); + $extension = Factory::getApplication()->getInput()->get('extension', ''); $parts = explode('.', $extension); diff --git a/administrator/components/com_workflow/src/Model/WorkflowModel.php b/administrator/components/com_workflow/src/Model/WorkflowModel.php index 9ca07c065f60f..1fbbaa8b27ac6 100644 --- a/administrator/components/com_workflow/src/Model/WorkflowModel.php +++ b/administrator/components/com_workflow/src/Model/WorkflowModel.php @@ -223,7 +223,7 @@ protected function loadFormData() */ protected function preprocessForm(Form $form, $data, $group = 'content') { - $extension = Factory::getApplication()->getInput()->get('extension'); + $extension = Factory::getApplication()->getInput()->get('extension', ''); $parts = explode('.', $extension); diff --git a/administrator/components/com_workflow/src/Table/TransitionTable.php b/administrator/components/com_workflow/src/Table/TransitionTable.php index 14045f382f0e4..6e76116b93e19 100644 --- a/administrator/components/com_workflow/src/Table/TransitionTable.php +++ b/administrator/components/com_workflow/src/Table/TransitionTable.php @@ -58,6 +58,27 @@ public function __construct(DatabaseInterface $db, ?DispatcherInterface $dispatc parent::__construct('#__workflow_transitions', 'id', $db, $dispatcher); } + /** + * Overloaded check function + * + * @return boolean True on success + * + * @see Table::check() + * @since __DEPLOY_VERSION__ + */ + public function check(): bool + { + $return = parent::check(); + + if ($return) { + if (empty($this->options)) { + $this->options = '{}'; + } + } + + return $return; + } + /** * Method to bind an associative array or object to the Table instance. * This method only binds properties that are publicly accessible and optionally diff --git a/administrator/components/com_workflow/tmpl/stage/edit.php b/administrator/components/com_workflow/tmpl/stage/edit.php index 83eae9b3757a6..1dcc5638e079d 100644 --- a/administrator/components/com_workflow/tmpl/stage/edit.php +++ b/administrator/components/com_workflow/tmpl/stage/edit.php @@ -40,7 +40,7 @@ - + item->id != 0) : ?>
diff --git a/administrator/components/com_workflow/tmpl/stages/default.php b/administrator/components/com_workflow/tmpl/stages/default.php index 22ba1d112bf35..6d28cbcec98d2 100644 --- a/administrator/components/com_workflow/tmpl/stages/default.php +++ b/administrator/components/com_workflow/tmpl/stages/default.php @@ -61,7 +61,7 @@
- +
, , diff --git a/administrator/components/com_workflow/tmpl/transition/edit.php b/administrator/components/com_workflow/tmpl/transition/edit.php index 10b3e7f6db664..fd33fa9816dea 100644 --- a/administrator/components/com_workflow/tmpl/transition/edit.php +++ b/administrator/components/com_workflow/tmpl/transition/edit.php @@ -30,14 +30,31 @@ $this->useCoreUI = true; // In case of modal -$isModal = $this->input->get('layout') === 'modal'; -$layout = $isModal ? 'modal' : 'edit'; -$tmpl = $isModal || $this->input->get('tmpl', '', 'cmd') === 'component' ? '&tmpl=component' : ''; +$isModal = $this->input->get('layout') === 'modal'; +$layout = $isModal ? 'modal' : 'edit'; +$tmpl = $isModal || $this->input->get('tmpl', '', 'cmd') === 'component' ? '&tmpl=component' : ''; +$clientId = $this->state->get('item.client_id', 0); +$lang = $this->getLanguage()->getTag(); ?>
+ + item->id != 0) : ?> +
+
+
+
+ +
+
+ +
+
+
+
+
'details', 'recall' => true, 'breakpoint' => 768]); ?> diff --git a/administrator/components/com_workflow/tmpl/transitions/default.php b/administrator/components/com_workflow/tmpl/transitions/default.php index c416ff7e2acfc..f691c101f52f6 100644 --- a/administrator/components/com_workflow/tmpl/transitions/default.php +++ b/administrator/components/com_workflow/tmpl/transitions/default.php @@ -60,7 +60,7 @@
- +
, , diff --git a/administrator/components/com_workflow/tmpl/workflows/default.php b/administrator/components/com_workflow/tmpl/workflows/default.php index 1412a90d2c9fb..304d94c6f4540 100644 --- a/administrator/components/com_workflow/tmpl/workflows/default.php +++ b/administrator/components/com_workflow/tmpl/workflows/default.php @@ -69,7 +69,7 @@ - +
, , diff --git a/api/components/com_users/src/Controller/UsersController.php b/api/components/com_users/src/Controller/UsersController.php index 21b75eb2e0ac2..91a8b5d6d19ec 100644 --- a/api/components/com_users/src/Controller/UsersController.php +++ b/api/components/com_users/src/Controller/UsersController.php @@ -14,6 +14,7 @@ use Joomla\CMS\Filter\InputFilter; use Joomla\CMS\Language\Text; use Joomla\CMS\MVC\Controller\ApiController; +use Joomla\CMS\User\UserHelper; use Joomla\Component\Fields\Administrator\Helper\FieldsHelper; use Tobscure\JsonApi\Exception\InvalidParameterException; @@ -70,6 +71,10 @@ protected function preprocessSaveData(array $data): array if (!\array_key_exists('password', $body)) { unset($data['password']); } + + if (!isset($data['groups'])) { + $data['groups'] = UserHelper::getUserGroups($data['id']); + } } if ($this->input->getMethod() === 'POST') { diff --git a/tests/System/README.md b/tests/System/README.md index ff9002aebbcd8..c63f1709f0780 100644 --- a/tests/System/README.md +++ b/tests/System/README.md @@ -232,6 +232,9 @@ The following commands are available and are served by the file [tests/System/su - **db_createUser** – Creates a user entry and returns the id - **db_createUserGroup** – Creates a user group and returns the id - **db_createUserLevel** – Creates a user access level and returns the id +- **db_createWorkflow** – Creates a workflow and returns the inserted record +- **db_createWorkflowStage** – Creates a workflow stage and returns the inserted record +- **db_createWorkflowTransition** – Creates a workflow transition and returns the inserted record - **db_enableExtension** – Sets the enabled status for the given extension - **db_getUserId** – Returns the id of the currently logged in user - **db_updateExtensionParameter** – Sets the parameter for the given extension diff --git a/tests/System/integration/administrator/components/com_content/Workflow.cy.js b/tests/System/integration/administrator/components/com_content/Workflow.cy.js new file mode 100644 index 0000000000000..598cffa2ce9b7 --- /dev/null +++ b/tests/System/integration/administrator/components/com_content/Workflow.cy.js @@ -0,0 +1,112 @@ +describe('Test in backend that the content component', () => { + beforeEach(() => cy.doAdministratorLogin()); + afterEach(() => { + cy.task('queryDB', "DELETE FROM #__content_frontpage WHERE content_id IN (SELECT id FROM #__content WHERE title = 'Test article')"); + cy.task('queryDB', "DELETE FROM #__workflow_associations WHERE item_id IN (SELECT id FROM #__content WHERE title = 'Test article') AND extension = 'com_content.article'"); + cy.task('queryDB', "DELETE FROM #__content WHERE title = 'Test article'"); + }); + after(() => cy.db_updateExtensionParameter('workflow_enabled', '0', 'com_content')); + + it('can enable workflow integration', () => { + cy.visit('/administrator/index.php?option=com_config&view=component&component=com_content'); + cy.get('#configTabs div[role="tablist"] button[aria-controls="integration"]').click(); + cy.get('#jform_workflow_enabled1').check(); + cy.intercept('index.php?option=com_config*').as('config_save'); + cy.clickToolbarButton('Save'); + cy.wait('@config_save'); + cy.checkForSystemMessage('Configuration saved.'); + }); + + it('can run transition (unpublish)', () => { + cy.db_createArticle({ title: 'Test article', state: 1 }).then(() => { + cy.visit('/administrator/index.php?option=com_content&view=articles&filter[search]=Test%20article'); + cy.checkAllResults(); + cy.get('#toolbar-status-group').click().within(() => { + cy.get('joomla-toolbar-button').contains('Unpublish').click(); + }); + cy.checkForSystemMessage('New state saved.'); + cy.get('#articleList tbody td.article-status span.icon-unpublish').should('exist'); + }); + }); + + it('can run transition (publish)', () => { + cy.db_createArticle({ title: 'Test article', state: 0 }).then(() => { + cy.visit('/administrator/index.php?option=com_content&view=articles&filter[search]=Test%20article'); + cy.get('#cb0').check(); + cy.get('#toolbar-status-group').click().within(() => { + cy.get('joomla-toolbar-button').contains('Publish').click(); + }); + cy.checkForSystemMessage('New state saved.'); + cy.get('#articleList tbody td.article-status span.icon-publish').should('exist'); + }); + }); + + it('can run transition (trash)', () => { + cy.db_createArticle({ title: 'Test article', state: 0 }).then(() => { + cy.visit('/administrator/index.php?option=com_content&view=articles&filter[search]=Test%20article&filter[published]=*'); + cy.get('#cb0').check(); + cy.get('#toolbar-status-group').click().within(() => { + cy.get('joomla-toolbar-button').contains('Trash').click(); + }); + cy.checkForSystemMessage('New state saved.'); + cy.get('#articleList tbody td.article-status span.icon-trash').should('exist'); + }); + }); + + it('can run transition (archive)', () => { + cy.db_createArticle({ title: 'Test article', state: 1 }).then(() => { + cy.visit('/administrator/index.php?option=com_content&view=articles&filter[search]=Test%20article&filter[published]=*'); + cy.get('#articleList thead th').contains('Stage'); + cy.get('#articleList tbody td.article-status span.icon-publish').should('exist'); + cy.get('td.article-stage button').click(); + cy.get('td.article-stage select').select('Archive'); + cy.checkForSystemMessage('New state saved.'); + cy.get('#articleList tbody td.article-status span.icon-archive').should('exist'); + }); + }); + + it('can run transition (feature)', () => { + cy.db_createArticle({ title: 'Test article', state: 1 }).then(() => { + cy.visit('/administrator/index.php?option=com_content&view=articles&filter[search]=Test%20article'); + cy.get('#articleList thead th').contains('Stage'); + cy.get('#articleList tbody td span.icon-unfeatured').should('exist'); + cy.get('td.article-stage button').click(); + cy.get('td.article-stage select').select('Feature'); + cy.checkForSystemMessage('New state saved.'); + cy.get('#articleList tbody td span.icon-color-featured.icon-star').should('exist'); + }); + }); + + it('can run transition (publish & feature)', () => { + cy.db_createArticle({ title: 'Test article', state: 0 }).then(() => { + cy.visit('/administrator/index.php?option=com_content&view=articles&filter[search]=Test%20article'); + cy.get('#articleList thead th').contains('Stage'); + cy.get('#articleList tbody td.article-status span.icon-unpublish').should('exist'); + cy.get('#articleList tbody td span.icon-unfeatured').should('exist'); + cy.get('td.article-stage button').click(); + cy.get('td.article-stage select').select('Publish & Feature'); + cy.checkForSystemMessage('New state saved.'); + cy.get('#articleList tbody td.article-status span.icon-publish').should('exist'); + cy.get('#articleList tbody td span.icon-color-featured.icon-star').should('exist'); + }); + }); + + it('can create new article and run transitions', () => { + cy.visit('/administrator/index.php?option=com_content&view=articles&filter='); + cy.clickToolbarButton('New'); + cy.get('#jform_title').type('Test article'); + cy.get('#jform_state-lbl').contains('Unpublished'); + cy.get('#jform_transition').select('Publish'); + cy.clickToolbarButton('Save'); + + cy.checkForSystemMessage('Article saved.'); + cy.get('#jform_state-lbl').contains('Published'); + cy.get('#jform_featured-lbl').contains('No'); + cy.get('#jform_transition').select('Feature'); + cy.clickToolbarButton('Save'); + + cy.checkForSystemMessage('Article saved.'); + cy.get('#jform_state-lbl').contains('Published'); + cy.get('#jform_featured-lbl').contains('Yes'); + }); +}); diff --git a/tests/System/integration/administrator/components/com_workflow/Stage.cy.js b/tests/System/integration/administrator/components/com_workflow/Stage.cy.js new file mode 100644 index 0000000000000..127139653abe7 --- /dev/null +++ b/tests/System/integration/administrator/components/com_workflow/Stage.cy.js @@ -0,0 +1,67 @@ +describe('Test in backend that the stage form', () => { + before(() => cy.task('writeRelativeFile', { path: 'administrator/language/overrides/en-GB.override.ini', content: 'AUTOMATED_TEST_STAGE="Test stage translated"' })); + beforeEach(() => cy.doAdministratorLogin()); + afterEach(() => cy.task('queryDB', "DELETE FROM #__workflow_stages WHERE title = 'AUTOMATED_TEST_STAGE'")); + after(() => cy.task('deleteRelativePath', 'administrator/language/overrides/en-GB.override.ini')); + + it('can create a stage', () => { + cy.visit('/administrator/index.php?option=com_workflow&task=stage.add&workflow_id=1&extension=com_content.article'); + cy.title().should('contain', 'Add Stage'); + cy.get('h1.page-title').should('contain', 'Add Stage'); + cy.get('#jform_title').clear().type('AUTOMATED_TEST_STAGE'); + cy.get('#jform_description').clear().type('automated test stage'); + cy.clickToolbarButton('Save'); + + cy.checkForSystemMessage('Item saved.'); + cy.title().should('contain', 'Edit Stage'); + cy.get('h1.page-title').should('contain', 'Edit Stage'); + cy.get('#jform_title').should('have.value', 'AUTOMATED_TEST_STAGE'); + cy.get('#stage_title_translation').should('have.value', 'Test stage translated'); + + cy.clickToolbarButton('Cancel'); + cy.get('h1.page-title').should('contain', 'Stages: Basic Workflow'); + + cy.get('table#stageList').contains('Test stage translated'); + cy.get('table#stageList').contains('automated test stage'); + }); + + it('can edit a stage', () => { + cy.db_createWorkflowStage({ title: 'Test stage', published: 0 }).then((stage) => { + cy.visit(`/administrator/index.php?option=com_workflow&task=stage.edit&workflow_id=1&extension=com_content.article&id=${stage.id}`); + cy.get('h1.page-title').should('contain', 'Edit Stage'); + cy.get('#jform_published').find(':selected').should('contain', 'Disabled'); + cy.get('#jform_published').select('Enabled'); + cy.get('#jform_description').clear().type('edited stage'); + cy.clickToolbarButton('Save'); + + cy.checkForSystemMessage('Item saved.'); + cy.get('#jform_published').find(':selected').should('contain', 'Enabled'); + cy.clickToolbarButton('Save & Close'); + + cy.checkForSystemMessage('Item saved.'); + cy.get('table#stageList').contains('edited stage'); + }); + }); + + it('can edit a stage (list view)', () => { + cy.db_createWorkflowStage({ title: 'Test stage' }); + cy.visit('/administrator/index.php?option=com_workflow&view=stages&workflow_id=1&extension=com_content.article'); + cy.get('table#stageList a').contains('Test stage').click(); + cy.get('h1.page-title').should('contain', 'Edit Stage'); + cy.get('#jform_title').clear().type('stage title'); + cy.get('#toolbar-dropdown-save-group .btn.btn-success.dropdown-toggle-split').click(); + cy.clickToolbarButton('Save & New'); + + cy.checkForSystemMessage('Item saved.'); + cy.get('h1.page-title').should('contain', 'Add Stage'); + }); + + it('redirects to the correct list view', () => { + cy.visit('/administrator/index.php?option=com_workflow&task=stage.add&workflow_id=1&extension=com_content.article'); + cy.intercept('**/administrator/index.php?option=com_workflow&view=stages&workflow_id=1&extension=com_content.article').as('listview'); + cy.clickToolbarButton('Cancel'); + + cy.wait('@listview'); + cy.title().should('contain', 'Stages: Basic Workflow'); + }); +}); diff --git a/tests/System/integration/administrator/components/com_workflow/Stages.cy.js b/tests/System/integration/administrator/components/com_workflow/Stages.cy.js new file mode 100644 index 0000000000000..a77b90bc94eb7 --- /dev/null +++ b/tests/System/integration/administrator/components/com_workflow/Stages.cy.js @@ -0,0 +1,154 @@ +describe('Test in backend that the stages list', () => { + beforeEach(() => { + cy.doAdministratorLogin(); + cy.visit('/administrator/index.php?option=com_workflow&view=stages&workflow_id=1&extension=com_content.article&filter='); + }); + after(() => cy.task('queryDB', 'UPDATE #__workflow_stages SET `default` = 1 WHERE id = 1')); + + it('can display a list of stages', () => { + cy.title().should('contain', 'Stages: Basic Workflow'); + cy.get('h1.page-title').should('contain', 'Stages: Basic Workflow'); + cy.contains('Basic Stage'); + }); + + it('can open the stage form', () => { + cy.clickToolbarButton('New'); + + cy.title().should('contain', 'Add Stage'); + cy.contains('Add Stage'); + }); + + it('can enable the stage', () => { + cy.db_createWorkflowStage({ title: 'Test stage', published: 0 }); + cy.reload(); + cy.setFilter('published', 'Unpublished'); + cy.searchForItem('Test stage'); + cy.checkAllResults(); + cy.clickToolbarButton('Action'); + cy.clickToolbarButton('Publish'); + + cy.checkForSystemMessage('Stage enabled.'); + }); + + it('can disable the stage', () => { + cy.db_createWorkflowStage({ title: 'Test stage', published: 1 }); + cy.reload(); + cy.setFilter('published', 'Published'); + cy.searchForItem('Test stage'); + cy.checkAllResults(); + cy.clickToolbarButton('Action'); + cy.clickToolbarButton('Unpublish'); + + cy.checkForSystemMessage('Stage disabled.'); + }); + + it('can trash the stage', () => { + cy.db_createWorkflowStage({ title: 'Test stage', published: 1 }); + cy.reload(); + cy.setFilter('published', 'Published'); + cy.searchForItem('Test stage'); + cy.checkAllResults(); + cy.clickToolbarButton('Action'); + cy.clickToolbarButton('Trash'); + + cy.checkForSystemMessage('Stage trashed.'); + }); + + it('can delete the stage', () => { + cy.db_createWorkflowStage({ title: 'Test stage', published: -2 }); + cy.reload(); + cy.setFilter('published', 'Trashed'); + cy.searchForItem('Test stage'); + cy.checkAllResults(); + cy.clickToolbarButton('Empty Trash'); + cy.clickDialogConfirm(true); + + cy.checkForSystemMessage('Stage deleted.'); + }); + + it('can checkin the stage', () => { + cy.db_getUserId(Cypress.env('username')).then((uid) => { + cy.db_createWorkflowStage({ title: 'Test stage', checked_out: uid, checked_out_time: '2025-01-01 00:00:00' }); + cy.reload(); + cy.searchForItem('Test stage'); + cy.checkAllResults(); + cy.clickToolbarButton('Action'); + cy.clickToolbarButton('Check-In'); + + cy.checkForSystemMessage('Stage checked in.'); + }); + }); + + it('can enable the stage (grid button)', () => { + cy.db_createWorkflowStage({ title: 'Test stage', published: 0, description: 'stage1' }); + cy.reload(); + cy.setFilter('published', 'Unpublished'); + cy.searchForItem('stage1'); + cy.get('table#stageList .icon-unpublish').click(); + + cy.checkForSystemMessage('Stage enabled.'); + }); + + it('can disable the stage (grid button)', () => { + cy.db_createWorkflowStage({ title: 'Test stage', published: 1, description: 'stage2' }); + cy.reload(); + cy.setFilter('published', 'Published'); + cy.searchForItem('stage2'); + cy.get('table#stageList .icon-publish').click(); + + cy.checkForSystemMessage('Stage disabled.'); + }); + + it('can checkin the stage (grid button)', () => { + cy.db_getUserId(Cypress.env('username')).then((uid) => { + cy.db_createWorkflowStage({ title: 'Test stage', checked_out: uid, checked_out_time: '2025-01-01 00:00:00' }); + cy.reload(); + cy.searchForItem('Test stage'); + cy.get('table#stageList .icon-checkedout').click(); + + cy.checkForSystemMessage('Stage checked in.'); + }); + }); + + it('can set stage as default (grid button)', () => { + cy.db_createWorkflowStage({ title: 'Test stage', default: 0 }); + cy.reload(); + cy.searchForItem('Test stage'); + cy.get('table#stageList .icon-unfeatured').click(); + + cy.checkForSystemMessage('Stage set as default.'); + }); + + it('can filter state', () => { + cy.db_createWorkflowStage({ title: 'Test stage 1', published: 1 }); + cy.db_createWorkflowStage({ title: 'Test stage 2', published: 0 }); + cy.db_createWorkflowStage({ title: 'Test stage 3', published: -2 }); + cy.reload(); + + cy.get('#stageList') + .should('contain', 'Test stage 1') + .should('contain', 'Test stage 2') + .should('not.contain', 'Test stage 3'); + + cy.setFilter('published', 'Published'); + + cy.get('#stageList') + .should('contain', 'Test stage 1') + .should('not.contain', 'Test stage 2') + .should('not.contain', 'Test stage 3'); + + cy.setFilter('published', 'Unpublished'); + + cy.get('#stageList') + .should('not.contain', 'Test stage 1') + .should('contain', 'Test stage 2') + .should('not.contain', 'Test stage 3'); + + cy.setFilter('published', 'Trashed'); + + cy.get('#stageList') + .should('not.contain', 'Test stage 1') + .should('not.contain', 'Test stage 2') + .should('contain', 'Test stage 3'); + }); +}); diff --git a/tests/System/integration/administrator/components/com_workflow/Transition.cy.js b/tests/System/integration/administrator/components/com_workflow/Transition.cy.js new file mode 100644 index 0000000000000..5c6ce25f983af --- /dev/null +++ b/tests/System/integration/administrator/components/com_workflow/Transition.cy.js @@ -0,0 +1,67 @@ +describe('Test in backend that the transition form', () => { + before(() => cy.task('writeRelativeFile', { path: 'administrator/language/overrides/en-GB.override.ini', content: 'AUTOMATED_TEST_TRANSITION="Test transition translated"' })); + beforeEach(() => cy.doAdministratorLogin()); + afterEach(() => cy.task('queryDB', "DELETE FROM #__workflow_transitions WHERE title = 'AUTOMATED_TEST_TRANSITION'")); + after(() => cy.task('deleteRelativePath', 'administrator/language/overrides/en-GB.override.ini')); + + it('can create a transition', () => { + cy.visit('/administrator/index.php?option=com_workflow&task=transition.add&workflow_id=1&extension=com_content.article'); + cy.title().should('contain', 'Add Transition'); + cy.get('h1.page-title').should('contain', 'Add Transition'); + cy.get('#jform_title').clear().type('AUTOMATED_TEST_TRANSITION'); + cy.get('#jform_description').clear().type('automated test transition'); + cy.clickToolbarButton('Save'); + + cy.checkForSystemMessage('Item saved.'); + cy.title().should('contain', 'Edit Transition'); + cy.get('h1.page-title').should('contain', 'Edit Transition'); + cy.get('#jform_title').should('have.value', 'AUTOMATED_TEST_TRANSITION'); + cy.get('#transition_title_translation').should('have.value', 'Test transition translated'); + + cy.clickToolbarButton('Cancel'); + cy.get('h1.page-title').should('contain', 'Transitions: Basic Workflow'); + + cy.get('table#transitionList').contains('Test transition translated'); + cy.get('table#transitionList').contains('automated test transition'); + }); + + it('can edit a transition', () => { + cy.db_createWorkflowTransition({ title: 'Test transition', published: 0 }).then((transition) => { + cy.visit(`/administrator/index.php?option=com_workflow&task=transition.edit&workflow_id=1&extension=com_content.article&id=${transition.id}`); + cy.get('h1.page-title').should('contain', 'Edit Transition'); + cy.get('#jform_published').find(':selected').should('contain', 'Disabled'); + cy.get('#jform_published').select('Enabled'); + cy.get('#jform_description').clear().type('edited transition'); + cy.clickToolbarButton('Save'); + + cy.checkForSystemMessage('Item saved.'); + cy.get('#jform_published').find(':selected').should('contain', 'Enabled'); + cy.clickToolbarButton('Save & Close'); + + cy.checkForSystemMessage('Item saved.'); + cy.get('table#transitionList').contains('edited transition'); + }); + }); + + it('can edit a transition (list view)', () => { + cy.db_createWorkflowTransition({ title: 'Test transition' }); + cy.visit('/administrator/index.php?option=com_workflow&view=transitions&workflow_id=1&extension=com_content.article'); + cy.get('table#transitionList a').contains('Test transition').click(); + cy.get('h1.page-title').should('contain', 'Edit Transition'); + cy.get('#jform_title').clear().type('transition title'); + cy.get('#toolbar-dropdown-save-group .btn.btn-success.dropdown-toggle-split').click(); + cy.clickToolbarButton('Save & New'); + + cy.checkForSystemMessage('Item saved.'); + cy.get('h1.page-title').should('contain', 'Add Transition'); + }); + + it('redirects to the correct list view', () => { + cy.visit('/administrator/index.php?option=com_workflow&task=transition.add&workflow_id=1&extension=com_content.article'); + cy.intercept('**/administrator/index.php?option=com_workflow&view=transitions&workflow_id=1&extension=com_content.article').as('listview'); + cy.clickToolbarButton('Cancel'); + + cy.wait('@listview'); + cy.title().should('contain', 'Transitions: Basic Workflow'); + }); +}); diff --git a/tests/System/integration/administrator/components/com_workflow/Transitions.cy.js b/tests/System/integration/administrator/components/com_workflow/Transitions.cy.js new file mode 100644 index 0000000000000..38108e6be58a7 --- /dev/null +++ b/tests/System/integration/administrator/components/com_workflow/Transitions.cy.js @@ -0,0 +1,196 @@ +describe('Test in backend that the transitions list', () => { + beforeEach(() => { + cy.doAdministratorLogin(); + cy.visit('/administrator/index.php?option=com_workflow&view=transitions&workflow_id=1&extension=com_content.article&filter='); + }); + + it('can display a list of transitions', () => { + cy.title().should('contain', 'Transitions: Basic Workflow'); + cy.get('h1.page-title').should('contain', 'Transitions: Basic Workflow'); + cy.contains('Publish'); + }); + + it('can open the transition form', () => { + cy.clickToolbarButton('New'); + + cy.title().should('contain', 'Add Transition'); + cy.contains('Add Transition'); + }); + + it('can enable the transition', () => { + cy.db_createWorkflowTransition({ title: 'Test transition', published: 0 }); + cy.reload(); + cy.setFilter('published', 'Unpublished'); + cy.searchForItem('Test transition'); + cy.checkAllResults(); + cy.clickToolbarButton('Action'); + cy.clickToolbarButton('Publish'); + + cy.checkForSystemMessage('Transition enabled.'); + }); + + it('can disable the transition', () => { + cy.db_createWorkflowTransition({ title: 'Test transition', published: 1 }); + cy.reload(); + cy.setFilter('published', 'Published'); + cy.searchForItem('Test transition'); + cy.checkAllResults(); + cy.clickToolbarButton('Action'); + cy.clickToolbarButton('Unpublish'); + + cy.checkForSystemMessage('Transition disabled.'); + }); + + it('can trash the transition', () => { + cy.db_createWorkflowTransition({ title: 'Test transition', published: 1 }); + cy.reload(); + cy.setFilter('published', 'Published'); + cy.searchForItem('Test transition'); + cy.checkAllResults(); + cy.clickToolbarButton('Action'); + cy.clickToolbarButton('Trash'); + + cy.checkForSystemMessage('Transition trashed.'); + }); + + it('can delete the transition', () => { + cy.db_createWorkflowTransition({ title: 'Test transition', published: -2 }); + cy.reload(); + cy.setFilter('published', 'Trashed'); + cy.searchForItem('Test transition'); + cy.checkAllResults(); + cy.clickToolbarButton('Empty Trash'); + cy.clickDialogConfirm(true); + + cy.checkForSystemMessage('Transition deleted.'); + }); + + it('can checkin the transition', () => { + cy.db_getUserId(Cypress.env('username')).then((uid) => { + cy.db_createWorkflowTransition({ title: 'Test transition', checked_out: uid, checked_out_time: '2025-01-01 00:00:00' }); + cy.reload(); + cy.searchForItem('Test transition'); + cy.checkAllResults(); + cy.clickToolbarButton('Action'); + cy.clickToolbarButton('Check-In'); + + cy.checkForSystemMessage('Transition checked in.'); + }); + }); + + it('can enable the transition (grid button)', () => { + cy.db_createWorkflowTransition({ title: 'Test transition', published: 0, description: 'transition1' }); + cy.reload(); + cy.setFilter('published', 'Unpublished'); + cy.searchForItem('transition1'); + cy.get('table#transitionList .icon-unpublish').click(); + + cy.checkForSystemMessage('Transition enabled.'); + }); + + it('can disable the transition (grid button)', () => { + cy.db_createWorkflowTransition({ title: 'Test transition', published: 1, description: 'transition2' }); + cy.reload(); + cy.setFilter('published', 'Published'); + cy.searchForItem('transition2'); + cy.get('table#transitionList .icon-publish').click(); + + cy.checkForSystemMessage('Transition disabled.'); + }); + + it('can checkin the transition (grid button)', () => { + cy.db_getUserId(Cypress.env('username')).then((uid) => { + cy.db_createWorkflowTransition({ title: 'Test transition', checked_out: uid, checked_out_time: '2025-01-01 00:00:00' }); + cy.reload(); + cy.searchForItem('Test transition'); + cy.get('table#transitionList .icon-checkedout').click(); + + cy.checkForSystemMessage('Transition checked in.'); + }); + }); + + it('can filter state', () => { + cy.db_createWorkflowTransition({ title: 'Test transition 1', published: 1 }); + cy.db_createWorkflowTransition({ title: 'Test transition 2', published: 0 }); + cy.db_createWorkflowTransition({ title: 'Test transition 3', published: -2 }); + cy.reload(); + + cy.get('#transitionList') + .should('contain', 'Test transition 1') + .should('contain', 'Test transition 2') + .should('not.contain', 'Test transition 3'); + + cy.setFilter('published', 'Published'); + + cy.get('#transitionList') + .should('contain', 'Test transition 1') + .should('not.contain', 'Test transition 2') + .should('not.contain', 'Test transition 3'); + + cy.setFilter('published', 'Unpublished'); + + cy.get('#transitionList') + .should('not.contain', 'Test transition 1') + .should('contain', 'Test transition 2') + .should('not.contain', 'Test transition 3'); + + cy.setFilter('published', 'Trashed'); + + cy.get('#transitionList') + .should('not.contain', 'Test transition 1') + .should('not.contain', 'Test transition 2') + .should('contain', 'Test transition 3'); + }); + + it('can filter current stage', () => { + cy.db_createWorkflowTransition({ title: 'Test transition 1' }); + cy.db_createWorkflowStage({ title: 'Test stage 2' }).then((stage) => cy.db_createWorkflowTransition({ title: 'Test transition 2', from_stage_id: stage.id })); + cy.db_createWorkflowStage({ title: 'Test stage 3' }).then((stage) => cy.db_createWorkflowTransition({ title: 'Test transition 3', from_stage_id: stage.id })); + cy.reload(); + + cy.get('#transitionList') + .should('contain', 'Test transition 1') + .should('contain', 'Test transition 2') + .should('contain', 'Test transition 3'); + + cy.setFilter('from_stage', 'Test stage 2'); + + cy.get('#transitionList') + .should('not.contain', 'Test transition 1') + .should('contain', 'Test transition 2') + .should('not.contain', 'Test transition 3'); + + cy.setFilter('from_stage', 'Test stage 3'); + + cy.get('#transitionList') + .should('not.contain', 'Test transition 1') + .should('not.contain', 'Test transition 2') + .should('contain', 'Test transition 3'); + }); + + it('can filter target stage', () => { + cy.db_createWorkflowTransition({ title: 'Test transition 1' }); + cy.db_createWorkflowStage({ title: 'Test stage 2' }).then((stage) => cy.db_createWorkflowTransition({ title: 'Test transition 2', to_stage_id: stage.id })); + cy.db_createWorkflowStage({ title: 'Test stage 3' }).then((stage) => cy.db_createWorkflowTransition({ title: 'Test transition 3', to_stage_id: stage.id })); + cy.reload(); + + cy.get('#transitionList') + .should('contain', 'Test transition 1') + .should('contain', 'Test transition 2') + .should('contain', 'Test transition 3'); + + cy.setFilter('to_stage', 'Test stage 2'); + + cy.get('#transitionList') + .should('not.contain', 'Test transition 1') + .should('contain', 'Test transition 2') + .should('not.contain', 'Test transition 3'); + + cy.setFilter('to_stage', 'Test stage 3'); + + cy.get('#transitionList') + .should('not.contain', 'Test transition 1') + .should('not.contain', 'Test transition 2') + .should('contain', 'Test transition 3'); + }); +}); diff --git a/tests/System/integration/administrator/components/com_workflow/Workflow.cy.js b/tests/System/integration/administrator/components/com_workflow/Workflow.cy.js new file mode 100644 index 0000000000000..e4d2ad1813252 --- /dev/null +++ b/tests/System/integration/administrator/components/com_workflow/Workflow.cy.js @@ -0,0 +1,70 @@ +describe('Test in backend that the workflow form', () => { + before(() => cy.task('writeRelativeFile', { path: 'administrator/language/overrides/en-GB.override.ini', content: 'AUTOMATED_TEST_WORKFLOW="Test workflow translated"' })); + beforeEach(() => cy.doAdministratorLogin()); + afterEach(() => { + cy.task('queryDB', "DELETE FROM #__workflow_stages WHERE workflow_id IN (SELECT id FROM #__workflows WHERE title = 'AUTOMATED_TEST_WORKFLOW')"); + cy.task('queryDB', "DELETE FROM #__workflows WHERE title = 'AUTOMATED_TEST_WORKFLOW'"); + }); + after(() => cy.task('deleteRelativePath', 'administrator/language/overrides/en-GB.override.ini')); + + it('can create a workflow', () => { + cy.visit('/administrator/index.php?option=com_workflow&task=workflow.add&extension=com_content.article'); + cy.title().should('contain', 'Add Workflow'); + cy.get('h1.page-title').should('contain', 'Add Workflow'); + cy.get('#jform_title').clear().type('AUTOMATED_TEST_WORKFLOW'); + cy.get('#jform_description').clear().type('automated test workflow'); + cy.clickToolbarButton('Save'); + + cy.checkForSystemMessage('Item saved.'); + cy.title().should('contain', 'Edit Workflow'); + cy.get('h1.page-title').should('contain', 'Edit Workflow'); + cy.get('#jform_title').should('have.value', 'AUTOMATED_TEST_WORKFLOW'); + cy.get('#workflow_title_translation').should('have.value', 'Test workflow translated'); + + cy.clickToolbarButton('Cancel'); + cy.get('h1.page-title').should('contain', 'Workflows'); + + cy.get('table#workflowList').contains('Test workflow translated'); + cy.get('table#workflowList').contains('automated test workflow'); + }); + + it('can edit a workflow', () => { + cy.db_createWorkflow({ title: 'Test workflow', extension: 'com_content.article', published: 0 }).then((workflow) => { + cy.visit(`/administrator/index.php?option=com_workflow&task=workflow.edit&extension=com_content.article&id=${workflow.id}`); + cy.get('h1.page-title').should('contain', 'Edit Workflow'); + cy.get('#jform_published').find(':selected').should('contain', 'Disabled'); + cy.get('#jform_published').select('Enabled'); + cy.get('#jform_description').clear().type('edited workflow'); + cy.clickToolbarButton('Save'); + + cy.checkForSystemMessage('Item saved.'); + cy.get('#jform_published').find(':selected').should('contain', 'Enabled'); + cy.clickToolbarButton('Save & Close'); + + cy.checkForSystemMessage('Item saved.'); + cy.get('table#workflowList').contains('edited workflow'); + }); + }); + + it('can edit a workflow (list view)', () => { + cy.db_createWorkflow({ title: 'Test workflow', extension: 'com_content.article' }); + cy.visit('/administrator/index.php?option=com_workflow&view=workflows&extension=com_content.article'); + cy.get('table#workflowList a').contains('Test workflow').click(); + cy.get('h1.page-title').should('contain', 'Edit Workflow'); + cy.get('#jform_title').clear().type('workflow title'); + cy.get('#toolbar-dropdown-save-group .btn.btn-success.dropdown-toggle-split').click(); + cy.clickToolbarButton('Save & New'); + + cy.checkForSystemMessage('Item saved.'); + cy.get('h1.page-title').should('contain', 'Add Workflow'); + }); + + it('redirects to the correct list view', () => { + cy.visit('/administrator/index.php?option=com_workflow&task=workflow.add&extension=com_content.article'); + cy.intercept('**/administrator/index.php?option=com_workflow&view=workflows&extension=com_content.article').as('listview'); + cy.clickToolbarButton('Cancel'); + + cy.wait('@listview'); + cy.title().should('contain', 'Workflows'); + }); +}); diff --git a/tests/System/integration/administrator/components/com_workflow/Workflows.cy.js b/tests/System/integration/administrator/components/com_workflow/Workflows.cy.js new file mode 100644 index 0000000000000..89af7c3bc56ca --- /dev/null +++ b/tests/System/integration/administrator/components/com_workflow/Workflows.cy.js @@ -0,0 +1,215 @@ +describe('Test in backend that the workflows list', () => { + beforeEach(() => { + cy.doAdministratorLogin(); + cy.visit('/administrator/index.php?option=com_workflow&view=workflows&extension=com_content.article&filter='); + }); + after(() => cy.task('queryDB', 'UPDATE #__workflows SET `default` = 1 WHERE id = 1')); + + it('can display a list of workflows', () => { + cy.title().should('contain', 'Workflows'); + cy.get('h1.page-title').should('contain', 'Workflows'); + cy.contains('Basic Workflow'); + }); + + it('can open the workflow form', () => { + cy.clickToolbarButton('New'); + + cy.title().should('contain', 'Add Workflow'); + cy.contains('Add Workflow'); + }); + + it('can enable the workflow', () => { + cy.db_createWorkflow({ title: 'Test workflow', extension: 'com_content.article', published: 0 }); + cy.reload(); + cy.setFilter('published', 'Unpublished'); + cy.searchForItem('Test workflow'); + cy.checkAllResults(); + cy.clickToolbarButton('Action'); + cy.clickToolbarButton('Publish'); + + cy.checkForSystemMessage('Workflow enabled.'); + }); + + it('can disable the workflow', () => { + cy.db_createWorkflow({ title: 'Test workflow', extension: 'com_content.article', published: 1 }); + cy.reload(); + cy.setFilter('published', 'Published'); + cy.searchForItem('Test workflow'); + cy.checkAllResults(); + cy.clickToolbarButton('Action'); + cy.clickToolbarButton('Unpublish'); + + cy.checkForSystemMessage('Workflow disabled.'); + }); + + it('can trash the workflow', () => { + cy.db_createWorkflow({ title: 'Test workflow', extension: 'com_content.article', published: 1 }); + cy.reload(); + cy.setFilter('published', 'Published'); + cy.searchForItem('Test workflow'); + cy.checkAllResults(); + cy.clickToolbarButton('Action'); + cy.clickToolbarButton('Trash'); + + cy.checkForSystemMessage('Workflow trashed.'); + }); + + it('can delete the workflow', () => { + cy.db_createWorkflow({ title: 'Test workflow', extension: 'com_content.article', published: -2 }); + cy.reload(); + cy.setFilter('published', 'Trashed'); + cy.searchForItem('Test workflow'); + cy.checkAllResults(); + cy.clickToolbarButton('Empty Trash'); + cy.clickDialogConfirm(true); + + cy.checkForSystemMessage('Workflow deleted.'); + }); + + it('can checkin the workflow', () => { + cy.db_getUserId(Cypress.env('username')).then((uid) => { + cy.db_createWorkflow({ + title: 'Test workflow', + extension: 'com_content.article', + checked_out: uid, + checked_out_time: '2025-01-01 00:00:00', + }); + cy.reload(); + cy.searchForItem('Test workflow'); + cy.checkAllResults(); + cy.clickToolbarButton('Action'); + cy.clickToolbarButton('Check-In'); + + cy.checkForSystemMessage('Workflow checked in.'); + }); + }); + + it('can enable the workflow (grid button)', () => { + cy.db_createWorkflow({ + title: 'Test workflow', + extension: 'com_content.article', + published: 0, + description: 'workflow1', + }); + cy.reload(); + cy.setFilter('published', 'Unpublished'); + cy.searchForItem('workflow1'); + cy.get('table#workflowList .icon-unpublish').click(); + + cy.checkForSystemMessage('Workflow enabled.'); + }); + + it('can disable the workflow (grid button)', () => { + cy.db_createWorkflow({ + title: 'Test workflow', + extension: 'com_content.article', + published: 1, + description: 'workflow2', + }); + cy.reload(); + cy.setFilter('published', 'Published'); + cy.searchForItem('workflow2'); + cy.get('table#workflowList .icon-publish').click(); + + cy.checkForSystemMessage('Workflow disabled.'); + }); + + it('can checkin the workflow (grid button)', () => { + cy.db_getUserId(Cypress.env('username')).then((uid) => { + cy.db_createWorkflow({ + title: 'Test workflow', + extension: 'com_content.article', + checked_out: uid, + checked_out_time: '2025-01-01 00:00:00', + }); + cy.reload(); + cy.searchForItem('Test workflow'); + cy.get('table#workflowList .icon-checkedout').click(); + + cy.checkForSystemMessage('Workflow checked in.'); + }); + }); + + it('can set workflow as default (grid button)', () => { + cy.db_createWorkflow({ title: 'Test workflow', extension: 'com_content.article', default: 0 }); + cy.reload(); + cy.searchForItem('Test workflow'); + cy.get('table#workflowList .icon-unfeatured').click(); + + cy.checkForSystemMessage('Workflow set as default.'); + }); + + it('can filter state', () => { + cy.db_createWorkflow({ title: 'Test workflow 1', extension: 'com_content.article', published: 1 }); + cy.db_createWorkflow({ title: 'Test workflow 2', extension: 'com_content.article', published: 0 }); + cy.db_createWorkflow({ title: 'Test workflow 3', extension: 'com_content.article', published: -2 }); + cy.reload(); + + cy.get('#workflowList') + .should('contain', 'Test workflow 1') + .should('contain', 'Test workflow 2') + .should('not.contain', 'Test workflow 3'); + + cy.setFilter('published', 'Published'); + + cy.get('#workflowList') + .should('contain', 'Test workflow 1') + .should('not.contain', 'Test workflow 2') + .should('not.contain', 'Test workflow 3'); + + cy.setFilter('published', 'Unpublished'); + + cy.get('#workflowList') + .should('not.contain', 'Test workflow 1') + .should('contain', 'Test workflow 2') + .should('not.contain', 'Test workflow 3'); + + cy.setFilter('published', 'Trashed'); + + cy.get('#workflowList') + .should('not.contain', 'Test workflow 1') + .should('not.contain', 'Test workflow 2') + .should('contain', 'Test workflow 3'); + }); + + it('can open list of stages', () => { + cy.searchForItem('BASIC WORKFLOW'); + cy.intercept('**/administrator/index.php?option=com_workflow&view=workflow*').as('workflows'); + cy.intercept('**/administrator/index.php?option=com_workflow&view=stages*').as('stages'); + cy.get('table#workflowList a.btn.btn-warning').click(); + + cy.wait('@stages'); + cy.get('h1.page-title').should('contain', 'Stages: Basic Workflow'); + cy.get('#toolbar-link').click(); + + cy.wait('@workflows'); + cy.get('h1.page-title').should('contain', 'Workflows'); + }); + + it('can open list of transitions', () => { + cy.searchForItem('BASIC WORKFLOW'); + cy.intercept('**/administrator/index.php?option=com_workflow&view=workflow*').as('workflows'); + cy.intercept('**/administrator/index.php?option=com_workflow&view=transitions*').as('transitions'); + cy.get('table#workflowList a.btn.btn-primary').click(); + + cy.wait('@transitions'); + cy.get('h1.page-title').should('contain', 'Transitions: Basic Workflow'); + cy.get('#toolbar-link').click(); + + cy.wait('@workflows'); + cy.get('h1.page-title').should('contain', 'Workflows'); + }); + + it('can visit options', () => { + cy.intercept('**/administrator/index.php?option=com_config&view=component&component=com_content*').as('options'); + cy.intercept('**/administrator/index.php?option=com_workflow&view=workflow*').as('listview'); + + cy.clickToolbarButton('Options'); + cy.wait('@options'); + cy.get('h1.page-title').should('contain', 'Articles: Options'); + + cy.clickToolbarButton('Cancel'); + cy.wait('@listview'); + cy.get('h1.page-title').should('contain', 'Workflows'); + }); +}); diff --git a/tests/System/integration/cli/CheckExtensionsUpdates.cy.js b/tests/System/integration/cli/CheckExtensionsUpdates.cy.js new file mode 100644 index 0000000000000..cb7dd1ea91f78 --- /dev/null +++ b/tests/System/integration/cli/CheckExtensionsUpdates.cy.js @@ -0,0 +1,7 @@ +describe('Test that console command check extensions update', () => { + it('can check update', () => { + cy.exec(`php ${Cypress.env('cmsPath')}/cli/joomla.php update:extensions:check`) + .its('stdout') + .should('contain', 'There are no updates available'); + }); +}); diff --git a/tests/System/support/commands/db.mjs b/tests/System/support/commands/db.mjs index 0b96faf32a93c..f0ecf9bd6c6c8 100644 --- a/tests/System/support/commands/db.mjs +++ b/tests/System/support/commands/db.mjs @@ -622,6 +622,103 @@ Cypress.Commands.add('db_createSchedulerTask', (taskData) => { }); }); +/** + * Creates a workflow in the database with the given data. The workflow contains some default values when + * not all required fields are passed in the given data. The data of the inserted workflow is returned. + * + * @param {Object} workflowData The workflow data to insert + * + * @returns Object + */ +Cypress.Commands.add('db_createWorkflow', (workflowData) => { + const defaultWorkflowOptions = { + published: 1, + title: 'test workflow', + description: '', + extension: 'com_content.article', + default: 0, + created: '2026-01-15 18:00:00', + modified: '2026-01-15 18:00:00', + }; + const workflow = { ...defaultWorkflowOptions, ...workflowData }; + + return cy.task('queryDB', createInsertQuery('workflows', workflow)) + .then((info) => { + workflow.id = info.insertId; + + if (workflow.default === 1) { + return cy.task('queryDB', `UPDATE #__workflows SET \`default\` = 0 WHERE id <> '${workflow.id}'`) + .then(() => workflow); + } + + return workflow; + }); +}); + +/** + * Creates a stage of a workflow in the database with the given data. The stage contains some default values when + * not all required fields are passed in the given data. The data of the inserted stage is returned. + * + * @param {Object} stageData The stage data to insert + * + * @returns Object + */ +Cypress.Commands.add('db_createWorkflowStage', (stageData) => { + const defaultStageOptions = { + workflow_id: 1, + published: 1, + title: '', + description: '', + default: 0, + }; + const stage = { ...defaultStageOptions, ...stageData }; + + return cy.task('queryDB', createInsertQuery('workflow_stages', stage)) + .then((info) => { + stage.id = info.insertId; + + if (stage.default === 1) { + return cy.task('queryDB', `UPDATE #__workflow_stages SET \`default\` = 0 WHERE id <> '${stage.id}' AND workflow_id = '${stage.workflow_id}'`) + .then(() => stage); + } + + return stage; + }); +}); + +/** + * Creates a transition of a workflow in the database with the given data. The transition contains some default values when + * not all required fields are passed in the given data. The data of the inserted transition is returned. + * + * @param {Object} transitionData The transition data to insert + * + * @returns Object + */ +Cypress.Commands.add('db_createWorkflowTransition', (transitionData) => { + const defaultTransitionOptions = { + workflow_id: 1, + published: 1, + title: '', + description: '', + from_stage_id: -1, + to_stage_id: 1, + options: '{}', + }; + const transition = { ...defaultTransitionOptions, ...transitionData }; + ['options'].forEach((key) => { + if (typeof transition[key] === 'object') { + transition[key] = JSON.stringify(transition[key]); + } + }); + + return cy.task('queryDB', createInsertQuery('workflow_transitions', transition)) + .then((info) => { + transition.id = info.insertId; + + return transition; + }); +}); + /** * Sets the parameter for the given extension. *