Initial Upload
This commit is contained in:
79
cypress/e2e/Administration/applications.cy.js
Normal file
79
cypress/e2e/Administration/applications.cy.js
Normal file
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Xibo Signage Ltd
|
||||
*
|
||||
* Xibo - Digital Signage - https://xibosignage.com
|
||||
*
|
||||
* This file is part of Xibo.
|
||||
*
|
||||
* Xibo is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* Xibo is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* eslint-disable max-len */
|
||||
describe('Applications', function() {
|
||||
let testRun = '';
|
||||
|
||||
beforeEach(function() {
|
||||
cy.login();
|
||||
|
||||
testRun = Cypress._.random(0, 1e9);
|
||||
});
|
||||
|
||||
it('should add edit an application', function() {
|
||||
// Intercept the PUT request
|
||||
cy.intercept({
|
||||
method: 'PUT',
|
||||
url: '/application/*',
|
||||
}).as('putRequest');
|
||||
|
||||
cy.visit('/application/view');
|
||||
|
||||
// Click on the Add Application button
|
||||
cy.contains('Add Application').click();
|
||||
|
||||
cy.get('.modal input#name')
|
||||
.type('Cypress Test Application ' + testRun);
|
||||
|
||||
// Add first by clicking next
|
||||
cy.get('.modal .save-button').click();
|
||||
|
||||
// Check if application is added in toast message
|
||||
cy.contains('Edit Application');
|
||||
|
||||
cy.get('.modal input#name').clear()
|
||||
.type('Cypress Test Application Edited ' + testRun);
|
||||
|
||||
// edit test application
|
||||
cy.get('.bootbox .save-button').click();
|
||||
|
||||
// Wait for the intercepted PUT request and check the form data
|
||||
cy.wait('@putRequest').then((interception) => {
|
||||
// Get the request body (form data)
|
||||
const response = interception.response;
|
||||
const responseData = response.body.data;
|
||||
|
||||
// assertion on the "application" value
|
||||
expect(responseData.name).to.eq('Cypress Test Application Edited ' + testRun);
|
||||
// Return appKey as a Cypress.Promise to ensure proper scoping
|
||||
return Cypress.Promise.resolve(responseData.key);
|
||||
}).then((appKey) => {
|
||||
if (appKey) {
|
||||
// TODO cannot be deleted via cypress
|
||||
// Delete the application and assert success
|
||||
// cy.deleteApplication(appKey).then((res) => {
|
||||
// expect(res.status).to.equal(200);
|
||||
// });
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
198
cypress/e2e/Administration/folder.cy.js
Normal file
198
cypress/e2e/Administration/folder.cy.js
Normal file
@@ -0,0 +1,198 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Xibo Signage Ltd
|
||||
*
|
||||
* Xibo - Digital Signage - https://xibosignage.com
|
||||
*
|
||||
* This file is part of Xibo.
|
||||
*
|
||||
* Xibo is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* Xibo is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* eslint-disable max-len */
|
||||
describe('Folders', function() {
|
||||
beforeEach(function() {
|
||||
cy.login();
|
||||
});
|
||||
|
||||
it('creating a new folder and rename it', () => {
|
||||
cy.visit('/folders/view');
|
||||
cy.contains('Root Folder').rightclick();
|
||||
cy.contains('Create').should('be.visible').click();
|
||||
|
||||
cy.visit('/folders/view');
|
||||
cy.contains('New Folder').should('be.visible').rightclick();
|
||||
cy.contains('Rename').type('Folder123{enter}');
|
||||
});
|
||||
|
||||
it('Moving an image from Root Folder to another folder', () => {
|
||||
cy.intercept({
|
||||
url: '/library?*',
|
||||
query: {media: 'child_folder_media'},
|
||||
}).as('mediaGridLoadAfterSearch');
|
||||
|
||||
// Go to library
|
||||
cy.visit('/library/view');
|
||||
|
||||
cy.get('#media').type('child_folder_media');
|
||||
|
||||
// Wait for the search to complete
|
||||
cy.wait('@mediaGridLoadAfterSearch');
|
||||
|
||||
cy.get('#libraryItems tbody tr').should('have.length', 1);
|
||||
cy.get('#datatable-container').should('contain', 'child_folder_media');
|
||||
|
||||
// Click the dropdown menu and choose a folder to move the image to
|
||||
cy.get('#libraryItems tr:first-child .dropdown-toggle').click({force: true});
|
||||
cy.get('#libraryItems tr:first-child .library_button_selectfolder').click({force: true});
|
||||
|
||||
// Expand the folder tree and select ChildFolder
|
||||
cy.get('#container-folder-form-tree>ul>li>i').click();
|
||||
cy.get('#container-folder-form-tree>ul>li:not(.jstree-loading)>i').click();
|
||||
cy.contains('ChildFolder').click();
|
||||
|
||||
// Click the save button
|
||||
cy.get('.save-button').click();
|
||||
});
|
||||
|
||||
it('Sharing', () => {
|
||||
// Create and alias for load user permissions for folders
|
||||
cy.intercept({
|
||||
url: '/user/permissions/Folder/*',
|
||||
query: {name: 'folder_user'},
|
||||
}).as('permissionsFoldersAfterSearch');
|
||||
|
||||
cy.visit('/folders/view');
|
||||
|
||||
cy.contains('ShareFolder').should('be.visible').rightclick();
|
||||
cy.get('ul.jstree-contextmenu >li:nth-child(6) > a').click(); // Click on Share Link
|
||||
cy.get('#name').type('folder_user');
|
||||
|
||||
cy.wait('@permissionsFoldersAfterSearch');
|
||||
|
||||
cy.get('#permissionsTable tbody tr').should('have.length', 1);
|
||||
cy.get('#permissionsTable tbody tr:nth-child(1) td:nth-child(1)').contains('folder_user');
|
||||
cy.get('#permissionsTable tbody tr:nth-child(1) td:nth-child(2)> input').click();
|
||||
cy.get('.save-button').click();
|
||||
});
|
||||
|
||||
it('Set Home Folders for a user', () => {
|
||||
// Create and alias for load users
|
||||
cy.intercept({
|
||||
url: '/user*',
|
||||
query: {userName: 'folder_user'},
|
||||
}).as('userGridLoadAfterSearch');
|
||||
|
||||
cy.visit('/user/view');
|
||||
cy.get('#userName').type('folder_user');
|
||||
|
||||
cy.wait('@userGridLoadAfterSearch');
|
||||
cy.get('#users tbody tr').should('have.length', 1);
|
||||
cy.get('#users tr:first-child .dropdown-toggle').click({force: true});
|
||||
cy.get('#users tr:first-child .user_button_set_home').click({force: true});
|
||||
cy.get('#home-folder').should('be.visible');
|
||||
cy.get('.jstree-anchor:contains(\'FolderHome\')').should('be.visible').click();
|
||||
cy.get('.save-button').click();
|
||||
|
||||
// Check
|
||||
cy.visit('/user/view');
|
||||
cy.get('#userName').clear();
|
||||
cy.get('#userName').type('folder_user');
|
||||
cy.wait('@userGridLoadAfterSearch');
|
||||
|
||||
cy.get('#users tbody tr').should('have.length', 1);
|
||||
cy.get('#users tbody tr:nth-child(1) td:nth-child(1)').contains('folder_user');
|
||||
cy.get('#users tbody tr:nth-child(1) td:nth-child(3)').contains('FolderHome');
|
||||
});
|
||||
|
||||
it('Remove an empty folder', () => {
|
||||
cy.visit('/folders/view');
|
||||
// Find the EmptyFolder element and right-click on it
|
||||
cy.get('.jstree-anchor:contains(\'EmptyFolder\')')
|
||||
.rightclick()
|
||||
.should('have.class', 'jstree-hovered'); // Ensure the right-click effect
|
||||
|
||||
// Find the context menu item with "Remove" text and click on it
|
||||
cy.contains('Remove').click();
|
||||
|
||||
// Validate
|
||||
cy.visit('/folders/view');
|
||||
cy.get('.jstree-anchor:contains(\'EmptyFolder\')').should('not.exist');
|
||||
});
|
||||
|
||||
it('cannot remove a folder with content', () => {
|
||||
cy.visit('/folders/view');
|
||||
cy.get('.jstree-anchor:contains(\'FolderWithContent\')')
|
||||
.rightclick();
|
||||
|
||||
// Find the context menu item with "Remove" text and click on it
|
||||
cy.contains('Remove').click();
|
||||
|
||||
// Check folder still exists
|
||||
cy.visit('/folders/view');
|
||||
cy.get('.jstree-anchor:contains(\'FolderWithContent\')').should('exist');
|
||||
});
|
||||
|
||||
it('search a media in a folder', () => {
|
||||
// Go to library
|
||||
cy.visit('/library/view');
|
||||
cy.get('.jstree-anchor:contains(\'Root Folder\')')
|
||||
.should('be.visible') // Ensure the element is visible
|
||||
.parent()
|
||||
.find('.jstree-icon.jstree-ocl')
|
||||
.click();
|
||||
|
||||
cy.get('.jstree-anchor:contains(\'FolderWithImage\')').click();
|
||||
cy.get('#libraryItems tbody tr').should('have.length', 1);
|
||||
cy.get('#libraryItems tbody').contains('media_for_search_in_folder')
|
||||
.should('be.visible');
|
||||
});
|
||||
|
||||
it('Hide Folder tree', () => {
|
||||
// Go to library
|
||||
cy.visit('/library/view');
|
||||
// The Folder tree is open by default on a grid
|
||||
cy.get('#folder-tree-select-folder-button').click();
|
||||
// clicking on the folder icon hides it
|
||||
cy.get('#grid-folder-filter').should('have.css', 'display', 'none');
|
||||
});
|
||||
|
||||
it('Move folders and Merge', () => {
|
||||
// Go to folders
|
||||
cy.visit('/folders/view');
|
||||
cy.get('.jstree-anchor:contains(\'MoveFromFolder\')').rightclick();
|
||||
cy.contains('Move Folder').click();
|
||||
cy.get('#container-folder-form-tree').within(() => {
|
||||
// Find the "MoveToFolder" link and click it
|
||||
cy.contains('MoveToFolder').click();
|
||||
});
|
||||
cy.get('#merge').should('be.visible').check();
|
||||
cy.get('.save-button').click();
|
||||
|
||||
// Validate test34 image exist in MoveToFolder
|
||||
cy.visit('/folders/view');
|
||||
cy.get('.jstree-anchor:contains(\'MoveFromFolder\')').should('not.exist');
|
||||
|
||||
// Validate test34 image exist in MoveToFolder
|
||||
// Go to library
|
||||
cy.visit('/library/view');
|
||||
|
||||
cy.get('.jstree-anchor:contains(\'Root Folder\')')
|
||||
.should('be.visible') // Ensure the element is visible
|
||||
.parent()
|
||||
.find('.jstree-icon.jstree-ocl')
|
||||
.click();
|
||||
cy.get('.jstree-anchor:contains(\'MoveToFolder\')').click();
|
||||
cy.get('#libraryItems tbody').contains('test34');
|
||||
});
|
||||
});
|
||||
38
cypress/e2e/Administration/modules.cy.js
Normal file
38
cypress/e2e/Administration/modules.cy.js
Normal file
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Xibo Signage Ltd
|
||||
*
|
||||
* Xibo - Digital Signage - https://xibosignage.com
|
||||
*
|
||||
* This file is part of Xibo.
|
||||
*
|
||||
* Xibo is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* Xibo is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
describe('Modules Page', function () {
|
||||
beforeEach(function () {
|
||||
cy.login();
|
||||
});
|
||||
|
||||
it.skip('should load the modules page and show a complete table of modules', function () {
|
||||
|
||||
cy.visit('/module/view');
|
||||
|
||||
cy.contains('Modules');
|
||||
|
||||
// Click on the first page of the pagination
|
||||
cy.get('.pagination > :nth-child(2) > a').click();
|
||||
|
||||
cy.contains('Showing 1 to');
|
||||
});
|
||||
});
|
||||
157
cypress/e2e/Administration/tags.cy.js
Normal file
157
cypress/e2e/Administration/tags.cy.js
Normal file
@@ -0,0 +1,157 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Xibo Signage Ltd
|
||||
*
|
||||
* Xibo - Digital Signage - https://xibosignage.com
|
||||
*
|
||||
* This file is part of Xibo.
|
||||
*
|
||||
* Xibo is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* Xibo is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* eslint-disable max-len */
|
||||
describe('Tags', function() {
|
||||
let testRun = '';
|
||||
|
||||
beforeEach(function() {
|
||||
cy.login();
|
||||
|
||||
testRun = Cypress._.random(0, 1e9);
|
||||
});
|
||||
|
||||
it('should add a tag', function() {
|
||||
cy.visit('/tag/view');
|
||||
|
||||
// Click on the Add Tag button
|
||||
cy.contains('Add Tag').click();
|
||||
|
||||
cy.get('.modal input#name')
|
||||
.type('Cypress Test Tag ' + testRun + '_1');
|
||||
|
||||
// Add first by clicking next
|
||||
cy.get('.modal .save-button').click();
|
||||
|
||||
// Check if tag is added in toast message
|
||||
cy.contains('Added Cypress Test Tag ' + testRun + '_1');
|
||||
});
|
||||
|
||||
it('searches and edit existing tag', function() {
|
||||
// Create a new tag and then search for it and delete it
|
||||
cy.createTag('Cypress Test Tag ' + testRun).then((res) => {
|
||||
cy.intercept({
|
||||
url: '/tag?*',
|
||||
query: {tag: 'Cypress Test Tag ' + testRun},
|
||||
}).as('loadGridAfterSearch');
|
||||
|
||||
// Intercept the PUT request
|
||||
cy.intercept({
|
||||
method: 'PUT',
|
||||
url: '/tag/*',
|
||||
}).as('putRequest');
|
||||
|
||||
cy.visit('/tag/view');
|
||||
|
||||
// Filter for the created tag
|
||||
cy.get('#Filter input[name="tag"]')
|
||||
.type('Cypress Test Tag ' + testRun);
|
||||
|
||||
// Wait for the grid reload
|
||||
cy.wait('@loadGridAfterSearch');
|
||||
cy.get('#tags tbody tr').should('have.length', 1);
|
||||
|
||||
// Click on the first row element to open the delete modal
|
||||
cy.get('#tags tr:first-child .dropdown-toggle').click({force: true});
|
||||
cy.get('#tags tr:first-child .tag_button_edit').click({force: true});
|
||||
|
||||
cy.get('.modal input#name').clear()
|
||||
.type('Cypress Test Tag Edited ' + testRun);
|
||||
|
||||
// edit test tag
|
||||
cy.get('.bootbox .save-button').click();
|
||||
|
||||
// Wait for the intercepted PUT request and check the form data
|
||||
cy.wait('@putRequest').then((interception) => {
|
||||
// Get the request body (form data)
|
||||
const response = interception.response;
|
||||
const responseData = response.body.data;
|
||||
const tag = responseData.tag;
|
||||
|
||||
// assertion on the "tag" value
|
||||
expect(tag).to.eq('Cypress Test Tag Edited ' + testRun);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('searches and delete existing tag', function() {
|
||||
// Create a new tag and then search for it and delete it
|
||||
cy.createTag('Cypress Test Tag ' + testRun).then((res) => {
|
||||
cy.intercept({
|
||||
url: '/tag?*',
|
||||
query: {tag: 'Cypress Test Tag ' + testRun},
|
||||
}).as('loadGridAfterSearch');
|
||||
|
||||
cy.visit('/tag/view');
|
||||
|
||||
// Filter for the created tag
|
||||
cy.get('#Filter input[name="tag"]')
|
||||
.type('Cypress Test Tag ' + testRun);
|
||||
|
||||
// Wait for the grid reload
|
||||
cy.wait('@loadGridAfterSearch');
|
||||
cy.get('#tags tbody tr').should('have.length', 1);
|
||||
|
||||
// Click on the first row element to open the delete modal
|
||||
cy.get('#tags tr:first-child .dropdown-toggle').click({force: true});
|
||||
cy.get('#tags tr:first-child .tag_button_delete').click({force: true});
|
||||
|
||||
// Delete test tag
|
||||
cy.get('.bootbox .save-button').click();
|
||||
|
||||
// Check if tag is deleted in toast message
|
||||
cy.get('.toast').contains('Deleted Cypress Test Tag');
|
||||
});
|
||||
});
|
||||
|
||||
it('selects multiple tags and delete them', function() {
|
||||
// Create a new tag and then search for it and delete it
|
||||
cy.createTag('Cypress Test Tag ' + testRun).then((res) => {
|
||||
cy.intercept({
|
||||
url: '/tag?*',
|
||||
query: {tag: 'Cypress Test Tag'},
|
||||
}).as('loadGridAfterSearch');
|
||||
|
||||
// Delete all test tags
|
||||
cy.visit('/tag/view');
|
||||
|
||||
// Clear filter
|
||||
cy.get('.clear-filter-btn').click();
|
||||
cy.get('#Filter input[name="tag"]')
|
||||
.type('Cypress Test Tag');
|
||||
|
||||
// Wait for the grid reload
|
||||
cy.wait('@loadGridAfterSearch');
|
||||
|
||||
// Select all
|
||||
cy.get('button[data-toggle="selectAll"]').click();
|
||||
|
||||
// Delete all
|
||||
cy.get('.dataTables_info button[data-toggle="dropdown"]').click();
|
||||
cy.get('.dataTables_info a[data-button-id="tag_button_delete"]').click();
|
||||
|
||||
cy.get('button.save-button').click();
|
||||
|
||||
// Modal should contain one successful delete at least
|
||||
cy.get('.modal-body').contains(': Success');
|
||||
});
|
||||
});
|
||||
});
|
||||
64
cypress/e2e/Administration/tasks.cy.js
Normal file
64
cypress/e2e/Administration/tasks.cy.js
Normal file
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Xibo Signage Ltd
|
||||
*
|
||||
* Xibo - Digital Signage - https://xibosignage.com
|
||||
*
|
||||
* This file is part of Xibo.
|
||||
*
|
||||
* Xibo is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* Xibo is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* eslint-disable max-len */
|
||||
describe('Tasks', function() {
|
||||
beforeEach(function() {
|
||||
cy.login();
|
||||
});
|
||||
|
||||
it('should edit a task', function() {
|
||||
// Intercept the PUT request
|
||||
cy.intercept({
|
||||
method: 'PUT',
|
||||
url: '/task/*',
|
||||
}).as('putRequest');
|
||||
|
||||
cy.visit('/task/view');
|
||||
|
||||
// Click on the first row element to open the delete modal
|
||||
cy.get('#tasks tr:first-child .dropdown-toggle').click({force: true});
|
||||
cy.get('#tasks tr:first-child .task_button_edit').click({force: true});
|
||||
|
||||
// Assuming you have an input field with the id 'myInputField'
|
||||
cy.get('.modal input#name').invoke('val').then((value) => {
|
||||
return Cypress.Promise.resolve(value);
|
||||
}).then((value) => {
|
||||
if (value) {
|
||||
cy.get('.modal input#name').clear()
|
||||
.type(value + ' Edited');
|
||||
|
||||
// edit test tag
|
||||
cy.get('.bootbox .save-button').click();
|
||||
|
||||
// Wait for the intercepted PUT request and check the form data
|
||||
cy.wait('@putRequest').then((interception) => {
|
||||
// Get the request body (form data)
|
||||
const response = interception.response;
|
||||
const responseData = response.body.data;
|
||||
|
||||
// assertion on the "task" value
|
||||
expect(responseData.name).to.eq(value + ' Edited');
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
64
cypress/e2e/Administration/transitions.cy.js
Normal file
64
cypress/e2e/Administration/transitions.cy.js
Normal file
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Xibo Signage Ltd
|
||||
*
|
||||
* Xibo - Digital Signage - https://xibosignage.com
|
||||
*
|
||||
* This file is part of Xibo.
|
||||
*
|
||||
* Xibo is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* Xibo is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* eslint-disable max-len */
|
||||
describe('Transitions', function() {
|
||||
beforeEach(function() {
|
||||
cy.login();
|
||||
});
|
||||
|
||||
it('should edit an transition', function() {
|
||||
// Intercept the PUT request
|
||||
cy.intercept({
|
||||
method: 'PUT',
|
||||
url: '/transition/*',
|
||||
}).as('putRequest');
|
||||
|
||||
cy.visit('/transition/view');
|
||||
cy.get('#transitions tbody tr').should('have.length', 3);
|
||||
|
||||
// Click on the first row element to open the delete modal
|
||||
cy.get('#transitions tr:first-child .dropdown-toggle').click({force: true});
|
||||
cy.get('#transitions tr:first-child .transition_button_edit').click({force: true});
|
||||
|
||||
cy.get('.modal #availableAsIn').then(($checkbox) => {
|
||||
const isChecked = $checkbox.prop('checked');
|
||||
cy.get('#availableAsIn').should('be.visible').click(); // Click to check/uncheck
|
||||
|
||||
// edit
|
||||
cy.get('.bootbox .save-button').click();
|
||||
|
||||
// Wait for the intercepted PUT request and check the form data
|
||||
cy.wait('@putRequest').then((interception) => {
|
||||
// Get the request body (form data)
|
||||
const response = interception.response;
|
||||
const responseData = response.body.data;
|
||||
|
||||
// assertion on the "task" value
|
||||
if (isChecked) {
|
||||
expect(responseData.availableAsIn).to.eq(0);
|
||||
} else {
|
||||
expect(responseData.availableAsIn).to.eq(1);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
128
cypress/e2e/Administration/usergroups.cy.js
Normal file
128
cypress/e2e/Administration/usergroups.cy.js
Normal file
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Xibo Signage Ltd
|
||||
*
|
||||
* Xibo - Digital Signage - https://xibosignage.com
|
||||
*
|
||||
* This file is part of Xibo.
|
||||
*
|
||||
* Xibo is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* Xibo is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* eslint-disable max-len */
|
||||
describe('Usergroups', function() {
|
||||
let testRun = '';
|
||||
|
||||
beforeEach(function() {
|
||||
cy.login();
|
||||
|
||||
testRun = Cypress._.random(0, 1e9);
|
||||
});
|
||||
|
||||
it('should add a usergroup', function() {
|
||||
cy.visit('/group/view');
|
||||
|
||||
// Click on the Add Usergroup button
|
||||
cy.contains('Add User Group').click();
|
||||
|
||||
cy.get('.modal input#group')
|
||||
.type('Cypress Test Usergroup ' + testRun + '_1');
|
||||
|
||||
// Add first by clicking next
|
||||
cy.get('.modal .save-button').click();
|
||||
|
||||
// Check if usergroup is added in toast message
|
||||
cy.contains('Added Cypress Test Usergroup');
|
||||
});
|
||||
|
||||
it('searches and edit existing usergroup', function() {
|
||||
// Create a new usergroup and then search for it and delete it
|
||||
cy.createUsergroup('Cypress Test Usergroup ' + testRun).then((groupId) => {
|
||||
cy.intercept({
|
||||
url: '/group?*',
|
||||
query: {userGroup: 'Cypress Test Usergroup ' + testRun},
|
||||
}).as('loadGridAfterSearch');
|
||||
|
||||
// Intercept the PUT request
|
||||
cy.intercept({
|
||||
method: 'PUT',
|
||||
url: '/group/*',
|
||||
}).as('putRequest');
|
||||
|
||||
cy.visit('/group/view');
|
||||
|
||||
// Filter for the created usergroup
|
||||
cy.get('#Filter input[name="userGroup"]')
|
||||
.type('Cypress Test Usergroup ' + testRun);
|
||||
|
||||
// Wait for the grid reload
|
||||
cy.wait('@loadGridAfterSearch');
|
||||
cy.get('#userGroups tbody tr').should('have.length', 1);
|
||||
|
||||
// Click on the first row element to open the delete modal
|
||||
cy.get('#userGroups tr:first-child .dropdown-toggle').click({force: true});
|
||||
cy.get('#userGroups tr:first-child .usergroup_button_edit').click({force: true});
|
||||
|
||||
cy.get('.modal input#group').clear()
|
||||
.type('Cypress Test Usergroup Edited ' + testRun);
|
||||
|
||||
// edit test usergroup
|
||||
cy.get('.bootbox .save-button').click();
|
||||
|
||||
// Wait for the intercepted PUT request and check the form data
|
||||
cy.wait('@putRequest').then((interception) => {
|
||||
// Get the request body (form data)
|
||||
const response = interception.response;
|
||||
const responseData = response.body.data;
|
||||
|
||||
// assertion on the "usergroup" value
|
||||
expect(responseData.group).to.eq('Cypress Test Usergroup Edited ' + testRun);
|
||||
|
||||
// Delete the usergroup and assert success
|
||||
cy.deleteUsergroup(groupId).then((response) => {
|
||||
expect(response.status).to.equal(200);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('searches and delete existing usergroup', function() {
|
||||
// Create a new usergroup and then search for it and delete it
|
||||
cy.createUsergroup('Cypress Test Usergroup ' + testRun).then((groupId) => {
|
||||
cy.intercept({
|
||||
url: '/group?*',
|
||||
query: {userGroup: 'Cypress Test Usergroup ' + testRun},
|
||||
}).as('loadGridAfterSearch');
|
||||
|
||||
cy.visit('/group/view');
|
||||
|
||||
// Filter for the created usergroup
|
||||
cy.get('#Filter input[name="userGroup"]')
|
||||
.type('Cypress Test Usergroup ' + testRun);
|
||||
|
||||
// Wait for the grid reload
|
||||
cy.wait('@loadGridAfterSearch');
|
||||
cy.get('#userGroups tbody tr').should('have.length', 1);
|
||||
|
||||
// Click on the first row element to open the delete modal
|
||||
cy.get('#userGroups tr:first-child .dropdown-toggle').click({force: true});
|
||||
cy.get('#userGroups tr:first-child .usergroup_button_delete').click({force: true});
|
||||
|
||||
// Delete test usergroup
|
||||
cy.get('.bootbox .save-button').click();
|
||||
|
||||
// Check if usergroup is deleted in toast message
|
||||
cy.get('.toast').contains('Deleted Cypress Test Usergroup');
|
||||
});
|
||||
});
|
||||
});
|
||||
166
cypress/e2e/Administration/users.cy.js
Normal file
166
cypress/e2e/Administration/users.cy.js
Normal file
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Xibo Signage Ltd
|
||||
*
|
||||
* Xibo - Digital Signage - https://xibosignage.com
|
||||
*
|
||||
* This file is part of Xibo.
|
||||
*
|
||||
* Xibo is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* Xibo is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* eslint-disable max-len */
|
||||
describe('Users', function() {
|
||||
let testRun = '';
|
||||
|
||||
beforeEach(function() {
|
||||
cy.login();
|
||||
|
||||
testRun = Cypress._.random(0, 1e9);
|
||||
});
|
||||
|
||||
it('should add a user', function() {
|
||||
cy.intercept({
|
||||
url: '/user/form/homepages?groupId=1&userTypeId=3*',
|
||||
query: {},
|
||||
}).as('loadHomepageAfterSearch');
|
||||
|
||||
cy.visit('/user/view');
|
||||
|
||||
// Click on the Add User button
|
||||
cy.contains('Add User').click();
|
||||
cy.get('.radio input[value="manual"]').click();
|
||||
|
||||
cy.get('#onboarding-steper-next-button').click();
|
||||
|
||||
cy.get('.modal input#userName')
|
||||
.type('CypressTestUser' + testRun);
|
||||
|
||||
cy.get('.modal input#password')
|
||||
.type('cypress');
|
||||
|
||||
// Error checking - for incorrect email format
|
||||
cy.get('.modal input#email').type('cypress');
|
||||
|
||||
cy.get('.select2-container--bootstrap').eq(1).click();
|
||||
cy.log('Before waiting for Icon Dashboard element');
|
||||
cy.wait('@loadHomepageAfterSearch');
|
||||
cy.get('.select2-results__option')
|
||||
.should('contain', 'Icon Dashboard')
|
||||
.click();
|
||||
|
||||
// Try saving
|
||||
cy.get('.modal .save-button').click();
|
||||
|
||||
cy.contains('Please enter a valid email address.');
|
||||
cy.get('.modal input#email').clear().type('cypress@test.com');
|
||||
|
||||
// Save
|
||||
cy.get('.modal .save-button').click();
|
||||
|
||||
// Check if user is added in toast message
|
||||
cy.contains('Added CypressTestUser');
|
||||
});
|
||||
|
||||
it('searches and edit existing user', function() {
|
||||
// Create a new user and then search for it and delete it
|
||||
cy.createUser('CypressTestUser' + testRun, 'password', 3, 1).then((id) => {
|
||||
cy.intercept({
|
||||
url: '/user?*',
|
||||
query: {userName: 'CypressTestUser' + testRun},
|
||||
}).as('loadGridAfterSearch');
|
||||
|
||||
// Intercept the PUT request
|
||||
cy.intercept({
|
||||
method: 'PUT',
|
||||
url: '/user/*',
|
||||
}).as('putRequest');
|
||||
|
||||
cy.visit('/user/view');
|
||||
|
||||
// Filter for the created user
|
||||
cy.get('#Filter input[name="userName"]')
|
||||
.type('CypressTestUser' + testRun);
|
||||
|
||||
// Wait for the grid reload
|
||||
cy.wait('@loadGridAfterSearch');
|
||||
cy.get('#users tbody tr').should('have.length', 1);
|
||||
|
||||
// Click on the first row element to open the delete modal
|
||||
cy.get('#users tr:first-child .dropdown-toggle').click({force: true});
|
||||
cy.get('#users tr:first-child .user_button_edit').click({force: true});
|
||||
|
||||
cy.get('.modal input#userName').clear()
|
||||
.type('CypressTestUserEdited' + testRun);
|
||||
|
||||
cy.get('.modal input#newPassword').clear().type('newPassword');
|
||||
cy.get('.modal input#retypeNewPassword').clear().type('wrongPassword');
|
||||
|
||||
// edit test user
|
||||
cy.get('.bootbox .save-button').click();
|
||||
cy.wait('@putRequest')
|
||||
|
||||
// Error checking - for password mismatch
|
||||
cy.contains('Passwords do not match');
|
||||
cy.get('.modal input#retypeNewPassword').clear().type('newPassword');
|
||||
|
||||
// edit test user
|
||||
cy.get('.bootbox .save-button').click();
|
||||
|
||||
// Wait for the intercepted PUT request and check the form data
|
||||
cy.wait('@putRequest').then((interception) => {
|
||||
// Get the request body (form data)
|
||||
const response = interception.response;
|
||||
const responseData = response.body.data;
|
||||
|
||||
// assertion on the "user" value
|
||||
expect(responseData.userName).to.eq('CypressTestUserEdited' + testRun);
|
||||
});
|
||||
|
||||
// Delete the user and assert success
|
||||
cy.deleteUser(id).then((res) => {
|
||||
expect(res.status).to.equal(200);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('searches and delete existing user', function() {
|
||||
// Create a new user and then search for it and delete it
|
||||
cy.createUser('CypressTestUser' + testRun, 'password', 3, 1).then((id) => {
|
||||
cy.intercept({
|
||||
url: '/user?*',
|
||||
query: {userName: 'CypressTestUser' + testRun},
|
||||
}).as('loadGridAfterSearch');
|
||||
|
||||
cy.visit('/user/view');
|
||||
|
||||
// Filter for the created user
|
||||
cy.get('#Filter input[name="userName"]')
|
||||
.type('CypressTestUser' + testRun);
|
||||
|
||||
// Wait for the grid reload
|
||||
cy.wait('@loadGridAfterSearch');
|
||||
cy.get('#users tbody tr').should('have.length', 1);
|
||||
|
||||
// Click on the first row element to open the delete modal
|
||||
cy.get('#users tr:first-child .dropdown-toggle').click({force: true});
|
||||
cy.get('#users tr:first-child .user_button_delete').click({force: true});
|
||||
|
||||
// Delete test User
|
||||
cy.get('.bootbox .save-button').click();
|
||||
|
||||
// Check if User is deleted in toast message
|
||||
cy.get('.toast').contains('Deleted CypressTestUser');
|
||||
});
|
||||
});
|
||||
});
|
||||
167
cypress/e2e/Campaign/campaigns.cy.js
Normal file
167
cypress/e2e/Campaign/campaigns.cy.js
Normal file
@@ -0,0 +1,167 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Xibo Signage Ltd
|
||||
*
|
||||
* Xibo - Digital Signage - https://xibosignage.com
|
||||
*
|
||||
* This file is part of Xibo.
|
||||
*
|
||||
* Xibo is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* Xibo is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* eslint-disable max-len */
|
||||
describe('Campaigns', function() {
|
||||
const testRun = Cypress._.random(0, 1e9);
|
||||
|
||||
beforeEach(function() {
|
||||
cy.login();
|
||||
});
|
||||
|
||||
// Create a list campaign
|
||||
// Assign layout to it
|
||||
// and add the id to the session
|
||||
it('should add a campaign and assign a layout', function() {
|
||||
cy.intercept('/campaign?draw=4&*').as('campaignGridLoad');
|
||||
|
||||
cy.intercept({
|
||||
url: '/campaign?*',
|
||||
query: {name: 'Cypress Test Campaign ' + testRun},
|
||||
}).as('campaignGridLoadAfterSearch');
|
||||
|
||||
cy.intercept({
|
||||
url: '/layout?*',
|
||||
query: {layout: 'List Campaign Layout'},
|
||||
}).as('layoutLoadAfterSearch');
|
||||
|
||||
// Intercept the POST request to get the campaign Id
|
||||
cy.intercept('/campaign').as('postCampaign');
|
||||
cy.intercept('/campaign/form/add?*').as('campaignFormAdd');
|
||||
|
||||
cy.visit('/campaign/view');
|
||||
|
||||
// Click on the Add Campaign button
|
||||
cy.contains('Add Campaign').click();
|
||||
|
||||
cy.get('.modal input#name')
|
||||
.type('Cypress Test Campaign ' + testRun);
|
||||
|
||||
cy.get('.modal .save-button').click();
|
||||
|
||||
// Wait for the edit form to pop open
|
||||
cy.contains('.modal .modal-title', testRun);
|
||||
|
||||
// Wait for the intercepted POST request to complete and the response to be received
|
||||
cy.wait('@postCampaign').then((interception) => {
|
||||
// Access the response body and extract the ID
|
||||
const id = interception.response.body.id;
|
||||
// Save the ID to the Cypress.env object
|
||||
Cypress.env('sessionCampaignId', id);
|
||||
});
|
||||
|
||||
// Switch to the layouts tab.
|
||||
cy.contains('.modal .nav-tabs .nav-link', 'Layouts').click();
|
||||
|
||||
// Should have no layouts assigned
|
||||
cy.get('.modal #LayoutAssignSortable').children()
|
||||
.should('have.length', 0);
|
||||
|
||||
// Search for 2 layouts names 'List Campaign Layout 1' and 'List Campaign Layout 2'
|
||||
cy.get('.form-inline input[name="layout"]')
|
||||
.type('List Campaign Layout').blur();
|
||||
|
||||
// Wait for the intercepted request and check the URL for the desired query parameter value
|
||||
cy.wait('@layoutLoadAfterSearch').then((interception) => {
|
||||
// Perform your desired actions or assertions here
|
||||
cy.log('Layout Loading');
|
||||
|
||||
cy.get('#layoutAssignments tbody tr').should('have.length', 2);
|
||||
|
||||
// Assign a layout
|
||||
cy.get('#layoutAssignments tr:nth-child(1) a.assignItem').click();
|
||||
cy.get('#layoutAssignments tr:nth-child(2) a.assignItem').click();
|
||||
|
||||
// Save
|
||||
cy.get('.bootbox .save-button').click();
|
||||
|
||||
// Wait for 4th campaign grid reload
|
||||
cy.wait('@campaignGridLoad');
|
||||
|
||||
// Filter for the created campaign
|
||||
cy.get('#Filter input[name="name"]')
|
||||
.type('Cypress Test Campaign ' + testRun);
|
||||
|
||||
cy.wait('@campaignGridLoadAfterSearch');
|
||||
|
||||
// Should have 2 layouts assigned
|
||||
cy.get('#campaigns tbody tr').should('have.length', 1);
|
||||
cy.get('#campaigns tbody tr:nth-child(1) td:nth-child(5)').contains('2');
|
||||
});
|
||||
});
|
||||
|
||||
it('should schedule a campaign and should set display status to green', function() {
|
||||
// At this point we know the campaignId
|
||||
const displayName = 'List Campaign Display 1';
|
||||
const sessionCampaignId = Cypress.env('sessionCampaignId');
|
||||
|
||||
// Schedule the campaign
|
||||
cy.scheduleCampaign(sessionCampaignId, displayName).then((res) => {
|
||||
cy.displaySetStatus(displayName, 1);
|
||||
|
||||
// Go to display grid
|
||||
cy.intercept('/display?draw=3&*').as('displayGridLoad');
|
||||
|
||||
cy.visit('/display/view');
|
||||
|
||||
// Filter for the created campaign
|
||||
cy.get('.FilterDiv input[name="display"]')
|
||||
.type(displayName);
|
||||
|
||||
// Should have the display
|
||||
cy.get('#displays tbody tr').should('have.length', 1);
|
||||
|
||||
// Check the display status is green
|
||||
cy.get('#displays tbody tr:nth-child(1)').should('have.class', 'table-success'); // For class "table-success"
|
||||
cy.get('#displays tbody tr:nth-child(1)').should('have.class', 'odd'); // For class "odd"
|
||||
});
|
||||
});
|
||||
|
||||
it('delete a campaign and check if the display status is pending', function() {
|
||||
cy.intercept('/campaign?draw=2&*').as('campaignGridLoad');
|
||||
cy.intercept('DELETE', '/campaign/*', (req) => {
|
||||
}).as('deleteCampaign');
|
||||
cy.visit('/campaign/view');
|
||||
|
||||
// Filter for the created campaign
|
||||
cy.get('#Filter input[name="name"]')
|
||||
.type('Cypress Test Campaign ' + testRun);
|
||||
|
||||
// Wait for 2nd campaign grid reload
|
||||
cy.wait('@campaignGridLoad');
|
||||
|
||||
cy.get('#campaigns tbody tr').should('have.length', 1);
|
||||
|
||||
cy.get('#campaigns tr:first-child .dropdown-toggle').click({force: true});
|
||||
cy.get('#campaigns tr:first-child .campaign_button_delete').click({force: true});
|
||||
|
||||
// Delete the campaign
|
||||
cy.get('.bootbox .save-button').click();
|
||||
|
||||
// Wait for the intercepted DELETE request to complete with status 200
|
||||
cy.wait('@deleteCampaign').its('response.statusCode').should('eq', 200);
|
||||
|
||||
// check the display status
|
||||
cy.displayStatusEquals('List Campaign Display 1', 3).then((res) => {
|
||||
expect(res.body).to.be.true;
|
||||
});
|
||||
});
|
||||
});
|
||||
318
cypress/e2e/Display/displaygroups.cy.js
Normal file
318
cypress/e2e/Display/displaygroups.cy.js
Normal file
@@ -0,0 +1,318 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Xibo Signage Ltd
|
||||
*
|
||||
* Xibo - Digital Signage - https://xibosignage.com
|
||||
*
|
||||
* This file is part of Xibo.
|
||||
*
|
||||
* Xibo is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* Xibo is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* eslint-disable max-len */
|
||||
describe('Display Groups', function() {
|
||||
let testRun = '';
|
||||
|
||||
beforeEach(function() {
|
||||
cy.login();
|
||||
|
||||
testRun = Cypress._.random(0, 1e9);
|
||||
});
|
||||
|
||||
it('should add one empty and one filled display groups', function() {
|
||||
cy.visit('/displaygroup/view');
|
||||
|
||||
// Click on the Add Displaygroup button
|
||||
cy.contains('Add Display Group').click();
|
||||
|
||||
cy.get('.modal input#displayGroup')
|
||||
.type('Cypress Test Displaygroup ' + testRun + '_1');
|
||||
|
||||
// Add first by clicking next
|
||||
cy.get('.modal').contains('Next').click();
|
||||
|
||||
// Check if displaygroup is added in toast message
|
||||
cy.contains('Added Cypress Test Displaygroup ' + testRun + '_1');
|
||||
|
||||
cy.get('.modal input#displayGroup')
|
||||
.type('Cypress Test Displaygroup ' + testRun + '_2');
|
||||
|
||||
cy.get('.modal input#description')
|
||||
.type('Description');
|
||||
|
||||
cy.get('.modal input#isDynamic').check();
|
||||
|
||||
cy.get('.modal input#dynamicCriteria')
|
||||
.type('testLayoutId');
|
||||
|
||||
// Add first by clicking next
|
||||
cy.get('.modal .save-button').click();
|
||||
|
||||
// Check if displaygroup is added in toast message
|
||||
cy.contains('Added Cypress Test Displaygroup ' + testRun + '_2');
|
||||
});
|
||||
|
||||
it('copy an existing displaygroup', function() {
|
||||
// Create a new displaygroup and then search for it and delete it
|
||||
cy.createDisplaygroup('Cypress Test Displaygroup ' + testRun).then((res) => {
|
||||
cy.intercept({
|
||||
url: '/displaygroup?*',
|
||||
query: {displayGroup: 'Cypress Test Displaygroup ' + testRun},
|
||||
}).as('loadGridAfterSearch');
|
||||
|
||||
// Intercept the POST request
|
||||
cy.intercept({
|
||||
method: 'POST',
|
||||
url: /\/displaygroup\/\d+\/copy$/,
|
||||
}).as('postRequest');
|
||||
|
||||
cy.visit('/displaygroup/view');
|
||||
|
||||
// Filter for the created displaygroup
|
||||
cy.get('#Filter input[name="displayGroup"]')
|
||||
.type('Cypress Test Displaygroup ' + testRun);
|
||||
|
||||
// Wait for the grid reload
|
||||
cy.wait('@loadGridAfterSearch');
|
||||
cy.get('#displaygroups tbody tr').should('have.length', 1);
|
||||
|
||||
// Click on the first row element to open the delete modal
|
||||
cy.get('#displaygroups tr:first-child .dropdown-toggle').click({force: true});
|
||||
cy.get('#displaygroups tr:first-child .displaygroup_button_copy').click({force: true});
|
||||
|
||||
// Delete test displaygroup
|
||||
cy.get('.bootbox .save-button').click();
|
||||
|
||||
// Wait for the intercepted POST request and check the form data
|
||||
cy.wait('@postRequest').then((interception) => {
|
||||
// Get the request body (form data)
|
||||
const response = interception.response;
|
||||
const responseData = response.body.data;
|
||||
expect(responseData.displayGroup).to.include('Cypress Test Displaygroup ' + testRun + ' 2');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('searches and delete existing displaygroup', function() {
|
||||
// Create a new displaygroup and then search for it and delete it
|
||||
cy.createDisplaygroup('Cypress Test Displaygroup ' + testRun).then((res) => {
|
||||
cy.intercept({
|
||||
url: '/displaygroup?*',
|
||||
query: {displayGroup: 'Cypress Test Displaygroup ' + testRun},
|
||||
}).as('loadGridAfterSearch');
|
||||
|
||||
cy.visit('/displaygroup/view');
|
||||
|
||||
// Filter for the created displaygroup
|
||||
cy.get('#Filter input[name="displayGroup"]')
|
||||
.type('Cypress Test Displaygroup ' + testRun);
|
||||
|
||||
// Wait for the grid reload
|
||||
cy.wait('@loadGridAfterSearch');
|
||||
cy.get('#displaygroups tbody tr').should('have.length', 1);
|
||||
|
||||
// Click on the first row element to open the delete modal
|
||||
cy.get('#displaygroups tr:first-child .dropdown-toggle').click({force: true});
|
||||
cy.get('#displaygroups tr:first-child .displaygroup_button_delete').click({force: true});
|
||||
|
||||
// Delete test displaygroup
|
||||
cy.get('.bootbox .save-button').click();
|
||||
|
||||
// Check if displaygroup is deleted in toast message
|
||||
cy.get('.toast').contains('Deleted Cypress Test Displaygroup');
|
||||
});
|
||||
});
|
||||
|
||||
// Seeded displays: dispgrp_disp1, dispgrp_disp2
|
||||
it('manage membership for a displaygroup', function() {
|
||||
cy.createDisplaygroup('Cypress Test Displaygroup ' + testRun).then((res) => {
|
||||
// assign displays to display group
|
||||
cy.intercept({
|
||||
url: '/displaygroup?*',
|
||||
query: {displayGroup: 'Cypress Test Displaygroup ' + testRun},
|
||||
}).as('loadGridAfterSearch');
|
||||
|
||||
// Intercept the PUT request
|
||||
cy.intercept({
|
||||
method: 'POST',
|
||||
url: /\/displaygroup\/\d+\/display\/assign$/,
|
||||
}).as('postRequest');
|
||||
|
||||
cy.intercept({
|
||||
url: '/display*',
|
||||
query: {display: 'dispgrp_disp1'},
|
||||
}).as('loadDisplayAfterSearch');
|
||||
|
||||
cy.visit('/displaygroup/view');
|
||||
|
||||
// Filter for the created displaygroup
|
||||
cy.get('#Filter input[name="displayGroup"]')
|
||||
.type('Cypress Test Displaygroup ' + testRun);
|
||||
|
||||
// Wait for the grid reload
|
||||
cy.wait('@loadGridAfterSearch');
|
||||
cy.get('#displaygroups tbody tr').should('have.length', 1);
|
||||
|
||||
// Click on the first row element to open the delete modal
|
||||
cy.get('#displaygroups tr:first-child .dropdown-toggle').click({force: true});
|
||||
cy.get('#displaygroups tr:first-child .displaygroup_button_group_members').click({force: true});
|
||||
|
||||
cy.get('.modal #display').type('dispgrp_disp1');
|
||||
|
||||
cy.wait('@loadDisplayAfterSearch');
|
||||
cy.get('#displaysMembersTable').within(() => {
|
||||
// count the rows within table
|
||||
cy.get('tbody').find('tr').should('have.length', 1);
|
||||
cy.get('tbody tr:first-child input[type="checkbox"]').check();
|
||||
});
|
||||
|
||||
// Save assignments
|
||||
cy.get('.bootbox .save-button').click();
|
||||
|
||||
// Wait for the intercepted POST request and check the form data
|
||||
cy.wait('@postRequest').then((interception) => {
|
||||
// Get the request body (form data)
|
||||
const response = interception.response;
|
||||
const body = response.body;
|
||||
expect(body.success).to.eq(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// -------
|
||||
// Seeded displays: dispgrp_disp_dynamic1, dispgrp_disp_dynamic2
|
||||
it('should add a dynamic display group', function() {
|
||||
cy.intercept({
|
||||
url: '/display?*',
|
||||
query: {display: 'dynamic'},
|
||||
}).as('loadDisplayGridAfterSearch');
|
||||
|
||||
cy.visit('/displaygroup/view');
|
||||
|
||||
// Click on the Add Displaygroup button
|
||||
cy.contains('Add Display Group').click();
|
||||
|
||||
cy.get('.modal input#displayGroup')
|
||||
.type('Cypress Test Displaygroup ' + testRun);
|
||||
|
||||
// Add first by clicking next
|
||||
cy.get('.modal #isDynamic').check();
|
||||
// Type "dynamic" into the input field with the name "dynamicCriteria"
|
||||
cy.get('.modal input[name="dynamicCriteria"]').type('dynamic');
|
||||
cy.wait('@loadDisplayGridAfterSearch');
|
||||
|
||||
// Add first by clicking next
|
||||
cy.get('.modal .save-button').click();
|
||||
|
||||
// Check if displaygroup is added in toast message
|
||||
cy.contains('Added Cypress Test Displaygroup ' + testRun);
|
||||
});
|
||||
|
||||
it('should edit the criteria of a dynamic display group', function() {
|
||||
// Create a new displaygroup with dynamic criteria
|
||||
cy.createDisplaygroup('Cypress Test Displaygroup Dynamic ' + testRun, true, 'dynamic').then((res) => {
|
||||
cy.intercept({
|
||||
url: '/displaygroup?*',
|
||||
query: {displayGroup: 'Cypress Test Displaygroup Dynamic ' + testRun},
|
||||
}).as('loadGridAfterSearch');
|
||||
|
||||
// Intercept the PUT request
|
||||
cy.intercept({
|
||||
method: 'PUT',
|
||||
url: '/displaygroup/*',
|
||||
}).as('putRequest');
|
||||
|
||||
cy.visit('/displaygroup/view');
|
||||
|
||||
// Filter for the created displaygroup
|
||||
cy.get('#Filter input[name="displayGroup"]')
|
||||
.type('Cypress Test Displaygroup Dynamic ' + testRun);
|
||||
|
||||
// Wait for the grid reload
|
||||
cy.wait('@loadGridAfterSearch');
|
||||
cy.get('#displaygroups tbody tr').should('have.length', 1);
|
||||
|
||||
// Click on the first row element to open the delete modal
|
||||
cy.get('#displaygroups tr:first-child .dropdown-toggle').click({force: true});
|
||||
cy.get('#displaygroups tr:first-child .displaygroup_button_edit').click({force: true});
|
||||
|
||||
cy.get('.modal input[name="dynamicCriteria"]').clear().type('dynamic_edited');
|
||||
|
||||
// Delete test displaygroup
|
||||
cy.get('.bootbox .save-button').click();
|
||||
|
||||
// Wait for the intercepted PUT request and check the form data
|
||||
cy.wait('@putRequest').then((interception) => {
|
||||
// Get the request body (form data)
|
||||
const response = interception.response;
|
||||
const responseData = response.body.data;
|
||||
|
||||
// assertion on the "display" value
|
||||
expect(responseData.dynamicCriteria).to.eq('dynamic_edited');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// -------
|
||||
// -- Delete Many
|
||||
it('selects multiple display groups and delete them', function() {
|
||||
// Create a new displaygroup and then search for it and delete it
|
||||
cy.createDisplaygroup('Cypress Test Displaygroup ' + testRun).then((res) => {
|
||||
cy.intercept('GET', '/displaygroup?draw=2&*').as('displaygroupGridLoad');
|
||||
|
||||
// Delete all test displaygroups
|
||||
cy.visit('/displaygroup/view');
|
||||
|
||||
// Clear filter
|
||||
cy.get('#Filter input[name="displayGroup"]')
|
||||
.clear()
|
||||
.type('Cypress Test Displaygroup');
|
||||
|
||||
// Wait for the grid reload
|
||||
cy.wait('@displaygroupGridLoad');
|
||||
|
||||
// Select all
|
||||
cy.get('button[data-toggle="selectAll"]').click();
|
||||
|
||||
// Delete all
|
||||
cy.get('.dataTables_info button[data-toggle="dropdown"]').click();
|
||||
cy.get('.dataTables_info a[data-button-id="displaygroup_button_delete"]').click();
|
||||
|
||||
cy.get('input#checkbox-confirmDelete').check();
|
||||
cy.get('button.save-button').click();
|
||||
|
||||
// Modal should contain one successful delete at least
|
||||
cy.get('.modal-body').contains(': Success');
|
||||
});
|
||||
});
|
||||
|
||||
// ---------
|
||||
// Tests - Error handling
|
||||
it('should not add a displaygroup without dynamic criteria', function() {
|
||||
cy.visit('/displaygroup/view');
|
||||
|
||||
// Click on the Add Displaygroup button
|
||||
cy.contains('Add Display Group').click();
|
||||
|
||||
cy.get('.modal input#displayGroup')
|
||||
.type('Cypress Test Displaygroup ' + testRun + '_1');
|
||||
|
||||
cy.get('.modal input#isDynamic').check();
|
||||
|
||||
// Add first by clicking next
|
||||
cy.get('.modal .save-button').click();
|
||||
|
||||
// Check toast message
|
||||
cy.contains('Dynamic Display Groups must have at least one Criteria specified.');
|
||||
});
|
||||
});
|
||||
334
cypress/e2e/Display/displays.cy.js
Normal file
334
cypress/e2e/Display/displays.cy.js
Normal file
@@ -0,0 +1,334 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Xibo Signage Ltd
|
||||
*
|
||||
* Xibo - Digital Signage - https://xibosignage.com
|
||||
*
|
||||
* This file is part of Xibo.
|
||||
*
|
||||
* Xibo is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* Xibo is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* eslint-disable max-len */
|
||||
describe('Displays', function() {
|
||||
let testRun = '';
|
||||
|
||||
beforeEach(function() {
|
||||
cy.login();
|
||||
|
||||
testRun = Cypress._.random(0, 1e9);
|
||||
});
|
||||
|
||||
// Seeded displays: disp1, disp2, disp3, disp4, disp5
|
||||
// Seeded display Groups: disp5_dispgrp
|
||||
// Seeded layouts: disp4_default_layout
|
||||
it('searches and edit existing display', function() {
|
||||
// search for a display disp1 and edit
|
||||
cy.intercept({
|
||||
url: '/display?*',
|
||||
query: {display: 'dis_disp1'},
|
||||
}).as('loadGridAfterSearch');
|
||||
|
||||
// Intercept the PUT request
|
||||
cy.intercept({
|
||||
method: 'PUT',
|
||||
url: '/display/*',
|
||||
}).as('putRequest');
|
||||
|
||||
cy.visit('/display/view');
|
||||
|
||||
// Filter for the created display
|
||||
cy.get('#Filter input[name="display"]')
|
||||
.type('dis_disp1');
|
||||
|
||||
// Wait for the grid reload
|
||||
cy.wait('@loadGridAfterSearch');
|
||||
cy.get('#displays tbody tr').should('have.length', 1);
|
||||
|
||||
// Click on the first row element to open the delete modal
|
||||
cy.get('#displays tr:first-child .dropdown-toggle').click({force: true});
|
||||
cy.get('#displays tr:first-child .display_button_edit').click({force: true});
|
||||
|
||||
cy.get('.modal input#display').clear()
|
||||
.type('dis_disp1 Edited');
|
||||
|
||||
cy.get('.modal input#license').clear()
|
||||
.type('dis_disp1_license');
|
||||
|
||||
cy.get('.modal input#description').clear()
|
||||
.type('description');
|
||||
|
||||
// edit test display
|
||||
cy.get('.bootbox .save-button').click();
|
||||
|
||||
// Wait for the intercepted PUT request and check the form data
|
||||
cy.wait('@putRequest').then((interception) => {
|
||||
// Get the request body (form data)
|
||||
const response = interception.response;
|
||||
const responseData = response.body.data;
|
||||
|
||||
// assertion on the "display" value
|
||||
expect(responseData.display).to.eq('dis_disp1 Edited');
|
||||
expect(responseData.description).to.eq('description');
|
||||
expect(responseData.license).to.eq('dis_disp1_license');
|
||||
});
|
||||
});
|
||||
|
||||
// Display: disp2
|
||||
it('searches and delete existing display', function() {
|
||||
cy.intercept({
|
||||
url: '/display?*',
|
||||
query: {display: 'dis_disp2'},
|
||||
}).as('loadGridAfterSearch');
|
||||
|
||||
cy.visit('/display/view');
|
||||
|
||||
// Filter for the created display
|
||||
cy.get('#Filter input[name="display"]')
|
||||
.type('dis_disp2');
|
||||
|
||||
// Wait for the grid reload
|
||||
cy.wait('@loadGridAfterSearch');
|
||||
cy.get('#displays tbody tr').should('have.length', 1);
|
||||
|
||||
// Click on the first row element to open the delete modal
|
||||
cy.get('#displays tr:first-child .dropdown-toggle').click({force: true});
|
||||
cy.get('#displays tr:first-child .display_button_delete').click({force: true});
|
||||
|
||||
// Delete test display
|
||||
cy.get('.bootbox .save-button').click();
|
||||
|
||||
// Check if display is deleted in toast message
|
||||
cy.get('.toast').contains('Deleted dis_disp2');
|
||||
});
|
||||
|
||||
// Display: disp3
|
||||
it('searches and authorise an unauthorised display', function() {
|
||||
// search for a display disp1 and edit
|
||||
cy.intercept({
|
||||
url: '/display?*',
|
||||
query: {display: 'dis_disp3'},
|
||||
}).as('loadGridAfterSearch');
|
||||
|
||||
// Intercept the PUT request
|
||||
cy.intercept({
|
||||
method: 'PUT',
|
||||
url: '/display/authorise/*',
|
||||
}).as('putRequest');
|
||||
|
||||
cy.visit('/display/view');
|
||||
|
||||
// Filter for the created display
|
||||
cy.get('#Filter input[name="display"]')
|
||||
.type('dis_disp3');
|
||||
|
||||
// Wait for the grid reload
|
||||
cy.wait('@loadGridAfterSearch');
|
||||
cy.get('#displays tbody tr').should('have.length', 1);
|
||||
|
||||
// Click on the first row element to open the delete modal
|
||||
cy.get('#displays tr:first-child .dropdown-toggle').click({force: true});
|
||||
cy.get('#displays tr:first-child .display_button_authorise').click({force: true});
|
||||
|
||||
// edit test display
|
||||
cy.get('.bootbox .save-button').click();
|
||||
|
||||
// Wait for the intercepted PUT request and check the form data
|
||||
cy.wait('@putRequest').then((interception) => {
|
||||
// Get the request body (form data)
|
||||
const response = interception.response;
|
||||
// assertion
|
||||
expect(response.body.message).to.eq('Authorised set to 1 for dis_disp3');
|
||||
});
|
||||
});
|
||||
|
||||
// Display: disp4
|
||||
it('set a default layout', function() {
|
||||
cy.intercept({
|
||||
url: '/display?*',
|
||||
query: {display: 'dis_disp4'},
|
||||
}).as('loadGridAfterSearch');
|
||||
|
||||
// Intercept the PUT request
|
||||
cy.intercept({
|
||||
method: 'PUT',
|
||||
url: '/display/defaultlayout/*',
|
||||
}).as('putRequest');
|
||||
|
||||
cy.intercept({
|
||||
url: '/layout*',
|
||||
query: {
|
||||
layout: 'disp4_default_layout',
|
||||
},
|
||||
}).as('loadLayoutAfterSearch');
|
||||
|
||||
cy.visit('/display/view');
|
||||
|
||||
// Filter for the created display
|
||||
cy.get('#Filter input[name="display"]')
|
||||
.type('dis_disp4');
|
||||
|
||||
// Wait for the grid reload
|
||||
cy.wait('@loadGridAfterSearch');
|
||||
cy.get('#displays tbody tr').should('have.length', 1);
|
||||
|
||||
// Click on the first row element to open the delete modal
|
||||
cy.get('#displays tr:first-child .dropdown-toggle').click({force: true});
|
||||
cy.get('#displays tr:first-child .display_button_defaultlayout').click({force: true});
|
||||
|
||||
// Set the default layout
|
||||
cy.get('.modal .select2-container--bootstrap').click();
|
||||
cy.get('.select2-search__field').type('disp4_default_layout');
|
||||
|
||||
cy.wait('@loadLayoutAfterSearch');
|
||||
cy.get('.select2-results__option').contains('disp4_default_layout').click();
|
||||
|
||||
// edit test display
|
||||
cy.get('.bootbox .save-button').click();
|
||||
|
||||
// Wait for the intercepted PUT request and check the form data
|
||||
cy.wait('@putRequest').then((interception) => {
|
||||
// Get the request body (form data)
|
||||
const response = interception.response;
|
||||
const body = response.body;
|
||||
expect(body.success).to.eq(true);
|
||||
});
|
||||
});
|
||||
|
||||
// Display: disp5
|
||||
it('manage membership for disp5', function() {
|
||||
cy.intercept({
|
||||
url: '/display?*',
|
||||
query: {display: 'dis_disp5'},
|
||||
}).as('loadGridAfterSearch');
|
||||
|
||||
// Intercept the PUT request
|
||||
cy.intercept({
|
||||
method: 'POST',
|
||||
url: /\/display\/\d+\/displaygroup\/assign$/,
|
||||
}).as('postRequest');
|
||||
|
||||
cy.intercept({
|
||||
url: '/displaygroup*',
|
||||
query: {
|
||||
displayGroup: 'disp5_dispgrp',
|
||||
},
|
||||
}).as('loadDisplaypGroupAfterSearch');
|
||||
|
||||
cy.visit('/display/view');
|
||||
|
||||
// Filter for the created display
|
||||
cy.get('#Filter input[name="display"]')
|
||||
.type('dis_disp5');
|
||||
|
||||
// Wait for the grid reload
|
||||
cy.wait('@loadGridAfterSearch');
|
||||
cy.get('#displays tbody tr').should('have.length', 1);
|
||||
|
||||
// Click on the first row element to open the delete modal
|
||||
cy.get('#displays tr:first-child .dropdown-toggle').click({force: true});
|
||||
cy.get('#displays tr:first-child .display_button_group_membership').click({force: true});
|
||||
|
||||
cy.get('.modal #displayGroup').type('disp5_dispgrp');
|
||||
|
||||
cy.wait('@loadDisplaypGroupAfterSearch');
|
||||
cy.get('#displaysGroupsMembersTable').within(() => {
|
||||
// count the rows within table
|
||||
cy.get('tbody').find('tr')
|
||||
.should('have.length', 1)
|
||||
.and('contain', 'disp5_dispgrp');
|
||||
cy.get('tbody tr:first-child input[type="checkbox"]')
|
||||
.should('not.be.checked')
|
||||
.check();
|
||||
});
|
||||
|
||||
// Save assignments
|
||||
cy.get('.bootbox .save-button').click();
|
||||
|
||||
// Wait for the intercepted POST request and check the form data
|
||||
cy.wait('@postRequest').then((interception) => {
|
||||
// Get the request body (form data)
|
||||
const response = interception.response;
|
||||
const body = response.body;
|
||||
expect(body.success).to.eq(true);
|
||||
});
|
||||
});
|
||||
|
||||
it('should display map and revert back to table', function() {
|
||||
cy.intercept('GET', '/user/pref?preference=displayGrid').as('displayPrefsLoad');
|
||||
cy.intercept('GET', '/display?draw=2*').as('displayLoad');
|
||||
cy.intercept('POST', '/user/pref').as('userPrefPost');
|
||||
|
||||
cy.visit('/display/view');
|
||||
|
||||
cy.wait('@displayPrefsLoad');
|
||||
cy.wait('@displayLoad');
|
||||
cy.wait('@userPrefPost');
|
||||
|
||||
cy.get('#map_button').click();
|
||||
|
||||
cy.get('#display-map.leaflet-container').should('be.visible');
|
||||
|
||||
cy.get('#list_button').click();
|
||||
|
||||
cy.get('#displays_wrapper.dataTables_wrapper').should('be.visible');
|
||||
});
|
||||
|
||||
// ---------
|
||||
// Tests - Error handling
|
||||
it('should not be able to save while editing existing display with incorrect latitude/longitude', function() {
|
||||
// search for a display disp1 and edit
|
||||
cy.intercept({
|
||||
url: '/display?*',
|
||||
query: {display: 'dis_disp1'},
|
||||
}).as('loadGridAfterSearch');
|
||||
|
||||
// Intercept the PUT request
|
||||
cy.intercept({
|
||||
method: 'PUT',
|
||||
url: '/display/*',
|
||||
}).as('putRequest');
|
||||
|
||||
cy.visit('/display/view');
|
||||
|
||||
// Filter for the created display
|
||||
cy.get('#Filter input[name="display"]')
|
||||
.type('dis_disp1');
|
||||
|
||||
// Wait for the grid reload
|
||||
cy.wait('@loadGridAfterSearch');
|
||||
cy.get('#displays tbody tr').should('have.length', 1);
|
||||
|
||||
// Click on the first row element to open the delete modal
|
||||
cy.get('#displays tr:first-child .dropdown-toggle').click({force: true});
|
||||
cy.get('#displays tr:first-child .display_button_edit').click({force: true});
|
||||
cy.contains('Details').click();
|
||||
|
||||
cy.get('.modal input#latitude').type('1234');
|
||||
|
||||
// edit test display
|
||||
cy.get('.bootbox .save-button').click();
|
||||
|
||||
// Check error message
|
||||
cy.contains('The latitude entered is not valid.');
|
||||
|
||||
cy.get('.modal input#latitude').clear();
|
||||
cy.get('.modal input#longitude').type('1234');
|
||||
|
||||
// edit test display
|
||||
cy.get('.bootbox .save-button').click();
|
||||
|
||||
// Check error message
|
||||
cy.contains('The longitude entered is not valid.');
|
||||
});
|
||||
});
|
||||
166
cypress/e2e/Display/displaysettings.cy.js
Normal file
166
cypress/e2e/Display/displaysettings.cy.js
Normal file
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Xibo Signage Ltd
|
||||
*
|
||||
* Xibo - Digital Signage - https://xibosignage.com
|
||||
*
|
||||
* This file is part of Xibo.
|
||||
*
|
||||
* Xibo is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* Xibo is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* eslint-disable max-len */
|
||||
describe('Display Settings', function() {
|
||||
let testRun = '';
|
||||
|
||||
beforeEach(function() {
|
||||
cy.login();
|
||||
|
||||
testRun = Cypress._.random(0, 1e9);
|
||||
});
|
||||
|
||||
it('should and edit a display setting', function() {
|
||||
// Intercept the POST request
|
||||
cy.intercept({
|
||||
method: 'POST',
|
||||
url: '/displayprofile',
|
||||
}).as('postRequest');
|
||||
|
||||
// Intercept the PUT request
|
||||
cy.intercept({
|
||||
method: 'PUT',
|
||||
url: '/displayprofile/*',
|
||||
}).as('putRequest');
|
||||
|
||||
cy.visit('/displayprofile/view');
|
||||
|
||||
// Click on the Add Display Setting button
|
||||
cy.contains('Add Profile').click();
|
||||
|
||||
cy.get('.modal input#name')
|
||||
.type('Cypress Test Display Setting ' + testRun);
|
||||
|
||||
// Add first by clicking next
|
||||
cy.get('.modal .save-button').click();
|
||||
|
||||
// Wait for the intercepted PUT request and check the form data
|
||||
cy.wait('@postRequest').then((interception) => {
|
||||
// Get the request body (form data)
|
||||
const response = interception.response;
|
||||
const responseData = response.body.data;
|
||||
|
||||
// assertion on the "tag" value
|
||||
expect(responseData.name).to.eq('Cypress Test Display Setting ' + testRun);
|
||||
|
||||
cy.get('.modal input#name').clear()
|
||||
.type('Cypress Test Display Setting Edited ' + testRun);
|
||||
|
||||
// Select the option with the value "10 minutes"
|
||||
cy.get('.modal #collectInterval').select('600');
|
||||
|
||||
// Add first by clicking next
|
||||
cy.get('.modal .save-button').click();
|
||||
|
||||
// Wait for the intercepted PUT request and check the form data
|
||||
cy.wait('@putRequest').then((interception) => {
|
||||
// Get the request body (form data)
|
||||
const response = interception.response;
|
||||
const responseData = response.body.data;
|
||||
|
||||
// assertion on the "tag" value
|
||||
expect(responseData.name).to.eq('Cypress Test Display Setting Edited ' + testRun);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('searches and edit existing display setting', function() {
|
||||
// Create a new tag and then search for it and delete it
|
||||
cy.createDisplayProfile('Cypress Test Display Setting ' + testRun, 'android').then((id) => {
|
||||
cy.intercept({
|
||||
url: '/displayprofile?*',
|
||||
query: {displayProfile: 'Cypress Test Display Setting ' + testRun},
|
||||
}).as('loadGridAfterSearch');
|
||||
|
||||
// Intercept the PUT request
|
||||
cy.intercept({
|
||||
method: 'PUT',
|
||||
url: '/displayprofile/*',
|
||||
}).as('putRequest');
|
||||
|
||||
cy.visit('/displayprofile/view');
|
||||
|
||||
// Filter for the created tag
|
||||
cy.get('#Filter input[name="displayProfile"]')
|
||||
.type('Cypress Test Display Setting ' + testRun);
|
||||
|
||||
// Wait for the grid reload
|
||||
cy.wait('@loadGridAfterSearch');
|
||||
cy.get('#displayProfiles tbody tr').should('have.length', 1);
|
||||
|
||||
// Click on the first row element to open the delete modal
|
||||
cy.get('#displayProfiles tr:first-child .dropdown-toggle').click({force: true});
|
||||
cy.get('#displayProfiles tr:first-child .displayprofile_button_edit').click({force: true});
|
||||
|
||||
cy.get('.modal input#name').clear()
|
||||
.type('Cypress Test Display Setting Edited ' + testRun);
|
||||
|
||||
// edit test tag
|
||||
cy.get('.bootbox .save-button').click();
|
||||
|
||||
// Wait for the intercepted PUT request and check the form data
|
||||
cy.wait('@putRequest').then((interception) => {
|
||||
// Get the request body (form data)
|
||||
const response = interception.response;
|
||||
const responseData = response.body.data;
|
||||
|
||||
// assertion on the "tag" value
|
||||
expect(responseData.name).to.eq('Cypress Test Display Setting Edited ' + testRun);
|
||||
});
|
||||
|
||||
// Delete the user and assert success
|
||||
cy.deleteDisplayProfile(id).then((res) => {
|
||||
expect(res.status).to.equal(204);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('searches and delete existing display setting', function() {
|
||||
// Create a new tag and then search for it and delete it
|
||||
cy.createDisplayProfile('Cypress Test Display Setting ' + testRun, 'android').then((id) => {
|
||||
cy.intercept({
|
||||
url: '/displayprofile?*',
|
||||
query: {displayProfile: 'Cypress Test Display Setting ' + testRun},
|
||||
}).as('loadGridAfterSearch');
|
||||
|
||||
cy.visit('/displayprofile/view');
|
||||
|
||||
// Filter for the created tag
|
||||
cy.get('#Filter input[name="displayProfile"]')
|
||||
.type('Cypress Test Display Setting ' + testRun);
|
||||
|
||||
// Wait for the grid reload
|
||||
cy.wait('@loadGridAfterSearch');
|
||||
cy.get('#displayProfiles tbody tr').should('have.length', 1);
|
||||
|
||||
// Click on the first row element to open the delete modal
|
||||
cy.get('#displayProfiles tr:first-child .dropdown-toggle').click({force: true});
|
||||
cy.get('#displayProfiles tr:first-child .displayprofile_button_delete').click({force: true});
|
||||
|
||||
// Delete test tag
|
||||
cy.get('.bootbox .save-button').click();
|
||||
|
||||
// Check if tag is deleted in toast message
|
||||
cy.get('.toast').contains('Deleted Cypress Test Display Setting');
|
||||
});
|
||||
});
|
||||
});
|
||||
97
cypress/e2e/Display/syncgroups.cy.js
Normal file
97
cypress/e2e/Display/syncgroups.cy.js
Normal file
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Xibo Signage Ltd
|
||||
*
|
||||
* Xibo - Digital Signage - https://xibosignage.com
|
||||
*
|
||||
* This file is part of Xibo.
|
||||
*
|
||||
* Xibo is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* Xibo is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* eslint-disable max-len */
|
||||
describe('Sync Groups', function() {
|
||||
let testRun = '';
|
||||
|
||||
beforeEach(function() {
|
||||
cy.login();
|
||||
|
||||
testRun = Cypress._.random(0, 1e9);
|
||||
});
|
||||
|
||||
it('should add one empty syncgroups', function() {
|
||||
cy.visit('/syncgroup/view');
|
||||
|
||||
// Click on the Add Sync Group button
|
||||
cy.contains('Add Sync Group').click();
|
||||
|
||||
cy.get('.modal input#name')
|
||||
.type('Cypress Test Sync Group ' + testRun);
|
||||
|
||||
// Add first by clicking next
|
||||
cy.get('.modal .save-button').click();
|
||||
|
||||
// Check if syncgroup is added in toast message
|
||||
cy.contains('Added Cypress Test Sync Group ' + testRun);
|
||||
});
|
||||
|
||||
it('searches and delete existing syncgroup', function() {
|
||||
// Create a new syncgroup and then search for it and delete it
|
||||
cy.createSyncGroup('Cypress Test Sync Group ' + testRun).then((res) => {
|
||||
cy.intercept({
|
||||
url: '/syncgroup?*',
|
||||
query: {name: 'Cypress Test Sync Group ' + testRun},
|
||||
}).as('loadGridAfterSearch');
|
||||
|
||||
cy.visit('/syncgroup/view');
|
||||
|
||||
// Filter for the created syncgroup
|
||||
cy.get('#Filter input[name="name"]')
|
||||
.type('Cypress Test Sync Group ' + testRun);
|
||||
|
||||
// Wait for the grid reload
|
||||
cy.wait('@loadGridAfterSearch');
|
||||
cy.get('#syncgroups tbody tr').should('have.length', 1);
|
||||
|
||||
// Click on the first row element to open the delete modal
|
||||
cy.get('#syncgroups tr:first-child .dropdown-toggle').click({force: true});
|
||||
cy.get('#syncgroups tr:first-child .syncgroup_button_group_delete').click({force: true});
|
||||
|
||||
// Delete test syncgroup
|
||||
cy.get('.bootbox .save-button').click();
|
||||
|
||||
// Check if syncgroup is deleted in toast message
|
||||
cy.get('.toast').contains('Deleted Cypress Test Sync Group');
|
||||
});
|
||||
});
|
||||
|
||||
// ---------
|
||||
// Tests - Error handling
|
||||
it.only('should not add a syncgroup without publisher port', function() {
|
||||
cy.visit('/syncgroup/view');
|
||||
|
||||
// Click on the Add Sync Group button
|
||||
cy.contains('Add Sync Group').click();
|
||||
|
||||
cy.get('.modal input#name')
|
||||
.type('Cypress Test Sync Group ' + testRun);
|
||||
|
||||
cy.get('#syncPublisherPort').clear();
|
||||
|
||||
// Add first by clicking next
|
||||
cy.get('.modal .save-button').click();
|
||||
|
||||
// Check if syncgroup is added in toast message
|
||||
cy.contains('Sync Publisher Port cannot be empty');
|
||||
});
|
||||
});
|
||||
134
cypress/e2e/Layout/Editor/layout-action-menu.cy.js
Normal file
134
cypress/e2e/Layout/Editor/layout-action-menu.cy.js
Normal file
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
* Copyright (C) 2025 Xibo Signage Ltd
|
||||
*
|
||||
* Xibo - Digital Signage - https://xibosignage.com
|
||||
*
|
||||
* This file is part of Xibo.
|
||||
*
|
||||
* Xibo is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* Xibo is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* eslint-disable max-len */
|
||||
describe('Layout Editor Toolbar (Back button, Interactive Mode, Layout jump list)', () => {
|
||||
beforeEach(function() {
|
||||
cy.login();
|
||||
|
||||
cy.intercept('GET', '/user/pref?preference=toolbar').as('toolbarPrefsLoad');
|
||||
cy.intercept('GET', '/user/pref?preference=editor').as('editorPrefsLoad');
|
||||
|
||||
cy.visit('/layout/view');
|
||||
cy.get('button.layout-add-button').click();
|
||||
cy.get('#layout-viewer').should('be.visible');
|
||||
cy.wait('@toolbarPrefsLoad');
|
||||
cy.wait('@editorPrefsLoad');
|
||||
});
|
||||
|
||||
it('Back button should be present and navigate correctly', () => {
|
||||
cy.get('#backBtn')
|
||||
.should('have.class', 'btn btn-lg')
|
||||
.and('have.attr', 'href', '/layout/view')
|
||||
.click({force: true});
|
||||
cy.url().should('include', '/layout/view');
|
||||
});
|
||||
|
||||
it('should display Interactive Mode with OFF status initially', () => { // done
|
||||
cy.get('li.interactive-control')
|
||||
.should('have.attr', 'data-status', 'off')
|
||||
.within(() => {
|
||||
cy.contains('.interactive-control-label', 'Interactive Mode');
|
||||
cy.get('.interactive-control-status-off').should('be.visible').and('contain.text', 'OFF');
|
||||
cy.get('.interactive-control-status-on').should('not.be.visible');
|
||||
});
|
||||
});
|
||||
|
||||
it('should toggle Interactive Mode status on click', () => { // done
|
||||
cy.get('li.nav-item.interactive-control[data-status="off"]')
|
||||
.should(($el) => {
|
||||
expect($el).to.be.visible;
|
||||
})
|
||||
.click({force: true});
|
||||
cy.get('.interactive-control-status-off').should('not.be.visible');
|
||||
});
|
||||
|
||||
it.only('should open and close the layout jump list dropdown safely', () => {
|
||||
cy.intercept('GET', '/layout?onlyMyLayouts=*').as('onlyMyLayouts');
|
||||
|
||||
const layoutName = 'Audio-Video-PDF';
|
||||
|
||||
cy.get('#select2-layoutJumpList-container')
|
||||
.should('be.visible');
|
||||
|
||||
// Force click because the element intermittently detaches in CI environment
|
||||
cy.get('#layoutJumpListContainer .select2-selection')
|
||||
.should('be.visible')
|
||||
.click({force: true});
|
||||
|
||||
// Check for status
|
||||
cy.wait('@onlyMyLayouts').then((interception) => {
|
||||
const result = interception.response.body.data[0];
|
||||
cy.log('result:', result.layoutId);
|
||||
});
|
||||
|
||||
// Type into the search input
|
||||
cy.get('.select2-search__field')
|
||||
.should('be.visible')
|
||||
.clear()
|
||||
.type(layoutName, {delay: 100});
|
||||
|
||||
// Click the matching option
|
||||
cy.get('.select2-results__option')
|
||||
.contains(layoutName)
|
||||
.click();
|
||||
});
|
||||
|
||||
it('Options dropdown menu toggles and contains expected items', () => {
|
||||
cy.get('#optionsContainerTop').should('be.visible');
|
||||
cy.get('#optionsContainerTop').click({force: true});
|
||||
|
||||
cy.get('.navbar-submenu-options-container')
|
||||
.should('be.visible')
|
||||
.within(() => {
|
||||
cy.get('#publishLayout').should('be.visible');
|
||||
cy.get('#checkoutLayout').should('have.class', 'd-none');
|
||||
cy.get('#discardLayout').should('be.visible');
|
||||
cy.get('#newLayout').should('be.visible');
|
||||
cy.get('#deleteLayout').should('have.class', 'd-none');
|
||||
cy.get('#saveTemplate').should('have.class', 'd-none');
|
||||
cy.get('#scheduleLayout').should('have.class', 'd-none');
|
||||
cy.get('#clearLayout').should('be.visible');
|
||||
cy.get('#displayTooltips').should('be.checked');
|
||||
cy.get('#deleteConfirmation').should('be.checked');
|
||||
});
|
||||
});
|
||||
|
||||
it('Tooltips and popovers appear on hover', () => {
|
||||
// Tooltip
|
||||
cy.get('.layout-info-name')
|
||||
.should('be.visible')
|
||||
.trigger('mouseover');
|
||||
cy.get('.tooltip').should('be.visible');
|
||||
cy.get('.layout-info-name')
|
||||
.should('be.visible')
|
||||
.trigger('mouseout');
|
||||
|
||||
// Popover
|
||||
cy.get('#layout-info-status')
|
||||
.should('be.visible')
|
||||
.trigger('mouseover');
|
||||
cy.get('.popover').should('be.visible');
|
||||
cy.get('#layout-info-status')
|
||||
.should('be.visible')
|
||||
.trigger('mouseout');
|
||||
});
|
||||
});
|
||||
85
cypress/e2e/Layout/Editor/layout_editor_background.cy.js
Normal file
85
cypress/e2e/Layout/Editor/layout_editor_background.cy.js
Normal file
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright (C) 2025 Xibo Signage Ltd
|
||||
*
|
||||
* Xibo - Digital Signage - https://xibosignage.com
|
||||
*
|
||||
* This file is part of Xibo.
|
||||
*
|
||||
* Xibo is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* Xibo is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* eslint-disable max-len */
|
||||
describe('Layout Editor Background', function() {
|
||||
const SELECTORS = {
|
||||
layoutAddButton: 'button.layout-add-button',
|
||||
layoutViewer: '#layout-viewer',
|
||||
propertiesPanel: '#properties-panel',
|
||||
colorPickerTrigger: '.input-group-prepend',
|
||||
colorPickerSaturation: '.colorpicker-saturation',
|
||||
backgroundColorInput: '#input_backgroundColor',
|
||||
backgroundzIndex: '#input_backgroundzIndex',
|
||||
resolutionDropdown: '#input_resolutionId',
|
||||
select2Selection: '.select2-selection',
|
||||
select2SearchInput: '.select2-container--open input[type="search"]',
|
||||
layoutInfoDimensions: '.layout-info-dimensions span',
|
||||
};
|
||||
|
||||
beforeEach(function() {
|
||||
cy.login();
|
||||
cy.visit('/layout/view');
|
||||
cy.get(SELECTORS.layoutAddButton).click();
|
||||
cy.get(SELECTORS.layoutViewer).should('be.visible'); // Assert that the URL has changed to the layout editor
|
||||
});
|
||||
|
||||
it('should update the background according to the colour set via colour picker', function() {
|
||||
cy.get(SELECTORS.propertiesPanel).should('be.visible'); // Verify properties panel is present
|
||||
cy.get(SELECTORS.colorPickerTrigger).click(); // Open colour picker
|
||||
cy.get(SELECTORS.colorPickerSaturation).click(68, 28); // Select on a specific saturation
|
||||
cy.get(SELECTORS.propertiesPanel).click(30, 60); // Click outside color picker to close
|
||||
|
||||
// Verify the selected color is applied to the background
|
||||
cy.get(SELECTORS.layoutViewer).should('have.css', 'background-color', 'rgb(243, 248, 255)');
|
||||
});
|
||||
|
||||
it('should update the background according to the colour set via hex input', function() {
|
||||
cy.get(SELECTORS.propertiesPanel).should('be.visible');
|
||||
cy.get(SELECTORS.backgroundColorInput).clear().type('#b53939{enter}');
|
||||
|
||||
// Verify the selected color is applied to the background
|
||||
cy.get(SELECTORS.layoutViewer).should('have.css', 'background-color', 'rgb(243, 248, 255)');
|
||||
});
|
||||
|
||||
it('should update the layer according to the input', function() {
|
||||
cy.get(SELECTORS.propertiesPanel).should('be.visible');
|
||||
cy.get(SELECTORS.backgroundzIndex).clear().type('1{enter}');
|
||||
|
||||
// Verify the selected number is applied to the layer
|
||||
cy.get(SELECTORS.backgroundzIndex).should('have.value', '1');
|
||||
});
|
||||
|
||||
// This is failing and a bug reported
|
||||
it.skip('should update the layout resolution', function() {
|
||||
cy.get(SELECTORS.propertiesPanel).should('be.visible');
|
||||
const resName = 'cinema';
|
||||
|
||||
cy.get(SELECTORS.resolutionDropdown).parent().find(SELECTORS.select2Selection).click();
|
||||
cy.get(SELECTORS.select2SearchInput).type(resName);
|
||||
cy.selectOption(resName);
|
||||
|
||||
cy.get(SELECTORS.layoutInfoDimensions)
|
||||
.should('be.visible')
|
||||
.and('contain', '4096x2304');
|
||||
});
|
||||
});
|
||||
|
||||
164
cypress/e2e/Layout/Editor/layout_editor_empty.cy.js
Normal file
164
cypress/e2e/Layout/Editor/layout_editor_empty.cy.js
Normal file
@@ -0,0 +1,164 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Xibo Signage Ltd
|
||||
*
|
||||
* Xibo - Digital Signage - https://xibosignage.com
|
||||
*
|
||||
* This file is part of Xibo.
|
||||
*
|
||||
* Xibo is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* Xibo is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* eslint-disable max-len */
|
||||
describe('Layout Designer (Empty)', function() {
|
||||
beforeEach(function() {
|
||||
cy.login();
|
||||
});
|
||||
|
||||
context('Unexisting Layout', function() {
|
||||
it('show layout not found if layout does not exist', function() {
|
||||
// Use a huge id to test a layout not found
|
||||
cy.visit({
|
||||
url: '/layout/designer/111111111111',
|
||||
failOnStatusCode: false,
|
||||
});
|
||||
|
||||
// See page not found message
|
||||
cy.contains('Layout not found');
|
||||
});
|
||||
});
|
||||
|
||||
context('Empty layout (published)', function() {
|
||||
const layoutTempName = '';
|
||||
|
||||
beforeEach(function() {
|
||||
// Import a layout and go to the Layout's designer page - we need a Layout in a Published state
|
||||
cy.importLayout('../assets/export_test_layout.zip').as('testLayoutId').then((res) => {
|
||||
cy.goToLayoutAndLoadPrefs(res);
|
||||
});
|
||||
});
|
||||
|
||||
it.skip('goes into draft mode when checked out', function() {
|
||||
// Get the done button from the checkout modal
|
||||
cy.get('[data-test="welcomeModal"] button.btn-bb-checkout').click();
|
||||
|
||||
// Check if campaign is deleted in toast message
|
||||
cy.contains('Checked out ' + layoutTempName);
|
||||
});
|
||||
|
||||
it.skip('should prevent a layout edit action, and show a toast message', function() {
|
||||
// Should contain widget options form
|
||||
cy.get('#properties-panel-form-container').contains('Edit Layout');
|
||||
|
||||
// The save button should not be visible
|
||||
cy.get('#properties-panel-form-container [data-action="save"]').should('not.exist');
|
||||
});
|
||||
});
|
||||
|
||||
context('Empty layout (draft)', function() {
|
||||
beforeEach(function() {
|
||||
// Create random name
|
||||
const uuid = Cypress._.random(0, 1e9);
|
||||
|
||||
// Create a new layout and go to the layout's designer page, then load toolbar prefs
|
||||
cy.createLayout(uuid).as('testLayoutId').then((res) => {
|
||||
cy.goToLayoutAndLoadPrefs(res);
|
||||
});
|
||||
});
|
||||
|
||||
it.skip('should create a new region from within the navigator edit', () => {
|
||||
// Open navigator edit
|
||||
cy.get('.editor-bottom-bar #navigator-edit-btn').click();
|
||||
|
||||
// Click on add region button
|
||||
cy.get('.editor-bottom-bar #add-btn').click();
|
||||
|
||||
// Check if there are 2 regions in the timeline ( there was 1 by default )
|
||||
cy.get('#layout-timeline [data-type="region"]').should('have.length', 2);
|
||||
});
|
||||
|
||||
it.skip('should delete a region using the toolbar bin', () => {
|
||||
cy.intercept('GET', '/layout?layoutId=*').as('reloadLayout');
|
||||
|
||||
// Open navigator edit
|
||||
cy.get('.editor-bottom-bar #navigator-edit-btn').click();
|
||||
|
||||
// Select a region from the navigator
|
||||
cy.get('#layout-navigator-content [data-type="region"]:first-child').click().then(($el) => {
|
||||
const regionId = $el.attr('id');
|
||||
|
||||
// Click trash container
|
||||
cy.get('.editor-bottom-bar #delete-btn').click();
|
||||
|
||||
// Confirm delete on modal
|
||||
cy.get('[data-test="deleteObjectModal"] button.btn-bb-confirm').click();
|
||||
|
||||
// Check toast message
|
||||
cy.get('.toast-success').contains('Deleted');
|
||||
|
||||
// Wait for the layout to reload
|
||||
cy.wait('@reloadLayout');
|
||||
|
||||
// Check that region is not on timeline
|
||||
cy.get('#layout-timeline [data-type="region"]#' + regionId).should('not.exist');
|
||||
});
|
||||
});
|
||||
|
||||
it.skip('creates a new widget by selecting a searched media from the toolbar to layout-navigator region', () => {
|
||||
cy.populateLibraryWithMedia();
|
||||
|
||||
// Create and alias for reload Layout
|
||||
cy.intercept('GET', '/layout?layoutId=*').as('reloadLayout');
|
||||
cy.intercept('GET', '/library/search?*').as('mediaLoad');
|
||||
|
||||
// Open library search tab
|
||||
cy.get('.editor-main-toolbar #btn-menu-0').should('be.visible').click({force: true});
|
||||
cy.get('.editor-main-toolbar #btn-menu-1').should('be.visible').click({force: true});
|
||||
|
||||
cy.wait('@mediaLoad');
|
||||
|
||||
cy.get('.editor-bottom-bar #navigator-edit-btn').click({force: true});
|
||||
|
||||
cy.get('.editor-main-toolbar #media-content-1 .toolbar-card:nth-of-type(2)').find('img').should('be.visible');
|
||||
|
||||
// Get a table row, select it and add to the region
|
||||
cy.get('.editor-main-toolbar #media-content-1 .toolbar-card:nth-of-type(2) .select-button').click({force: true}).then(() => {
|
||||
cy.get('#layout-navigator [data-type="region"]:first-child').click().then(() => {
|
||||
// Wait for the layout to reload
|
||||
cy.wait('@reloadLayout');
|
||||
|
||||
// Check if there is just one widget in the timeline
|
||||
cy.get('#layout-timeline [data-type="region"] [data-type="widget"]').then(($widgets) => {
|
||||
expect($widgets.length).to.eq(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it.skip('shows the file upload form by adding a uploadable media from the toolbar to layout-navigator region', () => {
|
||||
cy.populateLibraryWithMedia();
|
||||
|
||||
// Open toolbar Widgets tab
|
||||
cy.get('.editor-main-toolbar #btn-menu-1').should('be.visible').click({force: true});
|
||||
cy.get('.editor-main-toolbar #btn-menu-2').should('be.visible').click({force: true});
|
||||
|
||||
cy.get('.editor-bottom-bar #navigator-edit-btn').click();
|
||||
|
||||
cy.get('.editor-main-toolbar #content-2 .toolbar-pane-content .toolbar-card.upload-card').should('be.visible').then(() => {
|
||||
cy.get('.editor-main-toolbar #content-2 .toolbar-pane-content .toolbar-card.upload-card .select-upload').click({force: true});
|
||||
cy.get('#layout-navigator [data-type="region"]:first-child').click({force: true});
|
||||
cy.get('[data-test="uploadFormModal"]').contains('Upload media');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
348
cypress/e2e/Layout/Editor/layout_editor_options.cy.js
Normal file
348
cypress/e2e/Layout/Editor/layout_editor_options.cy.js
Normal file
@@ -0,0 +1,348 @@
|
||||
/*
|
||||
* Copyright (C) 2025 Xibo Signage Ltd
|
||||
*
|
||||
* Xibo - Digital Signage - https://xibosignage.com
|
||||
*
|
||||
* This file is part of Xibo.
|
||||
*
|
||||
* Xibo is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* Xibo is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* eslint-disable max-len */
|
||||
describe('Layout Editor Options', function() {
|
||||
beforeEach(function() {
|
||||
cy.login();
|
||||
cy.visit('/layout/view');
|
||||
});
|
||||
|
||||
it.skip('should be able to publish, checkout and discard layout', function() {
|
||||
let layoutName;
|
||||
|
||||
cy.intercept('GET', '/layout?layoutId=*').as('layoutStatus');
|
||||
cy.intercept('PUT', '/layout/discard/*').as('discardLayout');
|
||||
|
||||
cy.get('button.layout-add-button').click();
|
||||
cy.get('#layout-viewer').should('be.visible');
|
||||
|
||||
// Publish layout
|
||||
cy.openOptionsMenu();
|
||||
cy.get('#publishLayout').click();
|
||||
cy.get('button.btn-bb-Publish').click();
|
||||
|
||||
cy.wait('@layoutStatus').then((interception) => {
|
||||
expect(interception.response.statusCode).to.eq(200);
|
||||
// Check if the publishedStatus is "Published"
|
||||
const layoutData = interception.response.body.data[0];
|
||||
expect(layoutData).to.have.property('publishedStatus', 'Published');
|
||||
});
|
||||
|
||||
// Checkout published layout
|
||||
cy.openOptionsMenu();
|
||||
cy.get('#checkoutLayout').click();
|
||||
|
||||
cy.wait('@layoutStatus').then((interception) => {
|
||||
expect(interception.response.statusCode).to.eq(200);
|
||||
// Check if the publishedStatus is back to "Draft"
|
||||
const layoutData = interception.response.body.data[0];
|
||||
expect(layoutData).to.have.property('publishedStatus', 'Draft');
|
||||
});
|
||||
|
||||
// Capture layout name before discarding draft layout
|
||||
cy.get('.layout-info-name span')
|
||||
.invoke('text')
|
||||
.then((name) => {
|
||||
layoutName = name.trim().replace(/^"|"$/g, ''); // Remove double quotes
|
||||
cy.log(`Layout Name: ${layoutName}`);
|
||||
|
||||
cy.openOptionsMenu();
|
||||
cy.get('#discardLayout').click();
|
||||
cy.get('button.btn-bb-Discard').click();
|
||||
|
||||
// Verify that the layout has been discarded
|
||||
cy.wait('@discardLayout').then((interception) => {
|
||||
expect(interception.response.statusCode).to.equal(200);
|
||||
});
|
||||
|
||||
// Check if the user is redirected to the layouts page
|
||||
cy.url().should('include', '/layout/view');
|
||||
|
||||
// Search for the layout name
|
||||
cy.get('input[name="layout"]').clear().type(`${layoutName}{enter}`);
|
||||
|
||||
// Check status of the layout with matching layout name
|
||||
cy.get('#layouts tbody')
|
||||
.find('tr')
|
||||
.should('contain', layoutName)
|
||||
.should('contain', 'Published');
|
||||
});
|
||||
});
|
||||
|
||||
it.skip('should display an error when publishing an invalid layout', function() {
|
||||
cy.intercept('GET', '/playlist/widget/form/edit/*').as('addElement');
|
||||
cy.intercept('PUT', '/layout/publish/*').as('publishLayout');
|
||||
|
||||
cy.get('button.layout-add-button').click();
|
||||
cy.get('#layout-viewer').should('be.visible');
|
||||
|
||||
// Open widgets toolbox
|
||||
cy.openToolbarMenu(0, false);
|
||||
cy.get('[data-sub-type="ics-calendar"]').click();
|
||||
cy.get('[data-template-id="daily_light"]').click();
|
||||
cy.get('.viewer-object').click();
|
||||
|
||||
// Wait for element to be loaded on layout
|
||||
cy.wait('@addElement').then((interception) => {
|
||||
expect(interception.response.statusCode).to.eq(200);
|
||||
});
|
||||
|
||||
// Publish layout
|
||||
cy.openOptionsMenu();
|
||||
cy.get('#publishLayout').click();
|
||||
cy.get('button.btn-bb-Publish').click();
|
||||
|
||||
// Verify response
|
||||
cy.wait('@publishLayout').then((interception) => {
|
||||
expect(interception.response.statusCode).to.eq(200);
|
||||
expect(interception.response.body).to.have.property('message', 'There is an error with this Layout: Missing required property Feed URL');
|
||||
});
|
||||
|
||||
// Verify that a toast message is displayed
|
||||
cy.get('.toast-message')
|
||||
.should('be.visible')
|
||||
.and('contain.text', 'There is an error with this Layout');
|
||||
});
|
||||
|
||||
it.skip('should be able to create new layout', function() {
|
||||
cy.intercept('GET', '/layout?layoutId=*').as('newLayout');
|
||||
|
||||
cy.get('button.layout-add-button').click();
|
||||
cy.get('#layout-viewer').should('be.visible');
|
||||
|
||||
// Capture the layout ID of the initial layout loaded
|
||||
cy.get('#layout-editor')
|
||||
.invoke('attr', 'data-layout-id')
|
||||
.then((initialLayoutId) => {
|
||||
// Create new layout
|
||||
cy.wait(1000);
|
||||
cy.openOptionsMenu();
|
||||
cy.get('#newLayout').click();
|
||||
|
||||
cy.wait('@newLayout').then((interception) => {
|
||||
expect(interception.response.statusCode).to.eq(200); // Check if the request was successful
|
||||
|
||||
// Get the new layout ID
|
||||
cy.get('#layout-editor')
|
||||
.invoke('attr', 'data-layout-id')
|
||||
.then((newLayoutId) => {
|
||||
// Assert that the new layout ID is different from the initial layout ID
|
||||
expect(newLayoutId).to.not.eq(initialLayoutId);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it.skip('should be able to unlock layout', function() {
|
||||
let layoutName;
|
||||
|
||||
cy.intercept('GET', '/layout?layoutId=*').as('checkLockStatus');
|
||||
cy.intercept('GET', '/playlist/widget/form/edit/*').as('addElement');
|
||||
|
||||
cy.get('button.layout-add-button').click();
|
||||
cy.get('#layout-viewer').should('be.visible');
|
||||
|
||||
// Capture layout name to navigate back to it after unlocking
|
||||
cy.get('.layout-info-name span')
|
||||
.invoke('text')
|
||||
.then((name) => {
|
||||
layoutName = name.trim().replace(/^"|"$/g, '');
|
||||
cy.log(`Layout Name: ${layoutName}`);
|
||||
|
||||
// Open global elements toolbox
|
||||
cy.openToolbarMenu(1, false);
|
||||
cy.get('[data-template-id="text"]').click();
|
||||
cy.get('.viewer-object').click();
|
||||
|
||||
// Wait for element to be loaded on layout
|
||||
cy.wait('@addElement').then((interception) => {
|
||||
expect(interception.response.statusCode).to.eq(200);
|
||||
});
|
||||
|
||||
// Check for lock status
|
||||
cy.wait('@checkLockStatus').then((interception) => {
|
||||
const isLocked = interception.response.body.data[0].isLocked;
|
||||
expect(isLocked).to.not.be.empty;
|
||||
cy.log('isLocked:', isLocked);
|
||||
});
|
||||
|
||||
cy.intercept('PUT', '/layout/lock/release/*').as('unlock');
|
||||
|
||||
// Unlock layout
|
||||
cy.wait(1000);
|
||||
cy.openOptionsMenu();
|
||||
cy.get('#unlockLayout').should('be.visible').click();
|
||||
cy.get('button.btn-bb-unlock').click();
|
||||
|
||||
// Wait for the release lock request to complete
|
||||
cy.wait('@unlock').then((interception) => {
|
||||
expect(interception.response.statusCode).to.equal(200);
|
||||
});
|
||||
|
||||
// Check if the user is redirected to the /layout/view page
|
||||
cy.url().should('include', '/layout/view');
|
||||
|
||||
// Search for the layout name
|
||||
cy.get('input[name="layout"]').clear().type(`${layoutName}{enter}`);
|
||||
cy.get('#layouts tbody tr').should('contain.text', layoutName);
|
||||
cy.get('#layouts tbody tr').should('have.length', 1);
|
||||
|
||||
cy.openRowMenu();
|
||||
cy.get('#layout_button_design').click();
|
||||
cy.get('#layout-viewer').should('be.visible');
|
||||
|
||||
// Check for lock status
|
||||
cy.wait('@checkLockStatus').then((interception) => {
|
||||
const isLocked = interception.response.body.data[0].isLocked;
|
||||
expect(isLocked).be.empty;
|
||||
cy.log('isLocked:', isLocked);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it.skip('should enable tooltips', function() {
|
||||
cy.intercept('POST', '/user/pref').as('updatePreferences');
|
||||
|
||||
cy.get('button.layout-add-button').click();
|
||||
cy.get('#layout-viewer').should('be.visible');
|
||||
cy.openOptionsMenu();
|
||||
|
||||
// Enable tooltips
|
||||
// Check the current state of the tooltips checkbox
|
||||
cy.get('#displayTooltips').then(($checkbox) => {
|
||||
if (!$checkbox.is(':checked')) {
|
||||
// Check the checkbox if it is currently unchecked
|
||||
cy.wrap($checkbox).click();
|
||||
cy.wait('@updatePreferences');
|
||||
|
||||
// Confirm the checkbox is checked
|
||||
cy.get('#displayTooltips').should('be.checked');
|
||||
}
|
||||
});
|
||||
|
||||
// Verify that tooltips are present
|
||||
cy.get('.navbar-nav .btn-menu-option[data-toggle="tooltip"]').each(($element) => {
|
||||
// Trigger hover to show tooltip
|
||||
cy.wrap($element).trigger('mouseover');
|
||||
|
||||
// Check that the tooltip is visible for each button
|
||||
cy.get('.tooltip').should('be.visible'); // Expect tooltip to be present
|
||||
});
|
||||
});
|
||||
|
||||
it.skip('should disable tooltips', function() {
|
||||
cy.intercept('POST', '/user/pref').as('updatePreferences');
|
||||
|
||||
cy.get('button.layout-add-button').click();
|
||||
cy.get('#layout-viewer').should('be.visible');
|
||||
cy.openOptionsMenu();
|
||||
|
||||
// Disable tooltips
|
||||
// Check the current state of the tooltips checkbox
|
||||
cy.get('#displayTooltips').then(($checkbox) => {
|
||||
if ($checkbox.is(':checked')) {
|
||||
// Uncheck the checkbox if it is currently checked
|
||||
cy.wrap($checkbox).click();
|
||||
cy.wait('@updatePreferences');
|
||||
|
||||
// Confirm the checkbox is now unchecked
|
||||
cy.get('#displayTooltips').should('not.be.checked');
|
||||
}
|
||||
});
|
||||
|
||||
// Verify that tooltips are gone
|
||||
cy.get('.navbar-nav .btn-menu-option[data-toggle="tooltip"]').each(($element) => {
|
||||
cy.wrap($element).trigger('mouseover'); // Trigger hover to show tooltip
|
||||
cy.get('.tooltip').should('not.exist'); // Check if tooltip is gone for each button on the toolbox
|
||||
});
|
||||
});
|
||||
|
||||
it.skip('should enable delete confirmation', function() {
|
||||
cy.intercept('POST', '/user/pref').as('updatePreferences');
|
||||
|
||||
cy.get('button.layout-add-button').click();
|
||||
cy.get('#layout-viewer').should('be.visible');
|
||||
cy.openOptionsMenu();
|
||||
|
||||
// Check the current state of the delete confirmation checkbox
|
||||
cy.get('#deleteConfirmation').then(($checkbox) => {
|
||||
if (!$checkbox.is(':checked')) {
|
||||
// Check the checkbox if it is currently unchecked
|
||||
cy.wrap($checkbox).click();
|
||||
cy.wait('@updatePreferences');
|
||||
|
||||
// Confirm the checkbox is checked
|
||||
cy.get('#deleteConfirmation').should('be.checked');
|
||||
}
|
||||
});
|
||||
|
||||
// Add an element then attempt to delete
|
||||
cy.openToolbarMenu(0, false);
|
||||
cy.get('[data-sub-type="clock"]').click();
|
||||
cy.get('[data-sub-type="clock-analogue"]').click();
|
||||
cy.get('.viewer-object').click();
|
||||
cy.get('#delete-btn').click();
|
||||
|
||||
// Verify that delete confirmation modal appears
|
||||
cy.get('.modal-content')
|
||||
.should('be.visible')
|
||||
.and('contain.text', 'Delete Widget');
|
||||
});
|
||||
|
||||
it.skip('should disable delete confirmation', function() {
|
||||
cy.intercept('POST', '/user/pref').as('updatePreferences');
|
||||
|
||||
cy.get('button.layout-add-button').click();
|
||||
cy.get('#layout-viewer').should('be.visible');
|
||||
cy.openOptionsMenu();
|
||||
|
||||
// Check the current state of the delete confirmation checkbox
|
||||
cy.get('#deleteConfirmation').then(($checkbox) => {
|
||||
if ($checkbox.is(':checked')) {
|
||||
// Uncheck the checkbox if it is currently checked
|
||||
cy.wrap($checkbox).click();
|
||||
cy.wait('@updatePreferences');
|
||||
|
||||
// Confirm the checkbox is now unchecked
|
||||
cy.get('#displayTooltips').should('not.be.checked');
|
||||
}
|
||||
});
|
||||
|
||||
cy.intercept('DELETE', '/region/*').as('deleteElement');
|
||||
|
||||
// Add an element then attempt to delete
|
||||
cy.openToolbarMenu(0, false);
|
||||
cy.get('[data-sub-type="clock"]').click();
|
||||
cy.get('[data-sub-type="clock-analogue"]').click();
|
||||
cy.get('.viewer-object').click();
|
||||
cy.get('#delete-btn').click();
|
||||
|
||||
// Verify that the widget is immediately deleted without confirmation
|
||||
cy.wait('@deleteElement').then((interception) => {
|
||||
expect(interception.response.statusCode).to.equal(200);
|
||||
});
|
||||
|
||||
cy.get('.viewer-object').within(() => {
|
||||
cy.get('[data-type="region"]').should('not.exist');
|
||||
});
|
||||
});
|
||||
});
|
||||
240
cypress/e2e/Layout/Editor/layout_editor_populated.cy.js
Normal file
240
cypress/e2e/Layout/Editor/layout_editor_populated.cy.js
Normal file
@@ -0,0 +1,240 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Xibo Signage Ltd
|
||||
*
|
||||
* Xibo - Digital Signage - https://xibosignage.com
|
||||
*
|
||||
* This file is part of Xibo.
|
||||
*
|
||||
* Xibo is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* Xibo is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* eslint-disable max-len */
|
||||
describe('Layout Designer (Populated)', function() {
|
||||
beforeEach(function() {
|
||||
cy.login();
|
||||
|
||||
// Import existing
|
||||
cy.importLayout('../assets/export_test_layout.zip').as('testLayoutId').then((res) => {
|
||||
cy.checkoutLayout(res);
|
||||
|
||||
cy.goToLayoutAndLoadPrefs(res);
|
||||
});
|
||||
});
|
||||
|
||||
// Open widget form, change the name and duration, save, and see the name change result
|
||||
it.skip('changes and saves widget properties', () => {
|
||||
// Create and alias for reload widget
|
||||
cy.intercept('GET', '/playlist/widget/form/edit/*').as('reloadWidget');
|
||||
|
||||
// Select the first widget from the first region on timeline ( image )
|
||||
cy.get('#layout-timeline .designer-region:first [data-type="widget"]:first-child').click();
|
||||
|
||||
// Type the new name in the input
|
||||
cy.get('#properties-panel input[name="name"]').clear().type('newName');
|
||||
|
||||
// Set a duration
|
||||
cy.get('#properties-panel #useDuration').check();
|
||||
cy.get('#properties-panel input[name="duration"]').clear().type(12);
|
||||
|
||||
// Save form
|
||||
cy.get('#properties-panel button[data-action="save"]').click();
|
||||
|
||||
// Should show a notification for the name change
|
||||
cy.get('.toast-success').contains('newName');
|
||||
|
||||
// Check if the values are the same entered after reload
|
||||
cy.wait('@reloadWidget').then(() => {
|
||||
cy.get('#properties-panel input[name="name"]').should('have.attr', 'value').and('equal', 'newName');
|
||||
cy.get('#properties-panel input[name="duration"]').should('have.attr', 'value').and('equal', '12');
|
||||
});
|
||||
});
|
||||
|
||||
// On layout edit form, change background color and layer, save and check the changes
|
||||
it.skip('changes and saves layout properties', () => {
|
||||
// Create and alias for reload layout
|
||||
|
||||
cy.intercept('GET', '/layout?layoutId=*').as('reloadLayout');
|
||||
|
||||
// Change background color
|
||||
cy.get('#properties-panel input[name="backgroundColor"]').clear().type('#ccc');
|
||||
|
||||
// Change layer
|
||||
cy.get('#properties-panel input[name="backgroundzIndex"]').clear().type(1);
|
||||
|
||||
// Save form
|
||||
cy.get('#properties-panel button[data-action="save"]').click();
|
||||
|
||||
// Should show a notification for the successful save
|
||||
cy.get('.toast-success').contains('Edited');
|
||||
|
||||
// Check if the values are the same entered after reload
|
||||
cy.wait('@reloadLayout').then(() => {
|
||||
cy.get('#properties-panel input[name="backgroundColor"]').should('have.attr', 'value').and('equal', '#cccccc');
|
||||
cy.get('#properties-panel input[name="backgroundzIndex"]').should('have.value', '1');
|
||||
});
|
||||
});
|
||||
|
||||
// On layout edit form, change background image check the changes
|
||||
it.skip('should change layout´s background image', () => {
|
||||
// Create and alias for reload layout
|
||||
|
||||
cy.intercept('GET', '/layout?layoutId=*').as('reloadLayout');
|
||||
cy.intercept('GET', '/library/search?*').as('mediaLoad');
|
||||
|
||||
cy.get('#properties-panel #backgroundRemoveButton').click();
|
||||
|
||||
// Open library search tab
|
||||
cy.get('.editor-main-toolbar #btn-menu-0').click();
|
||||
cy.get('.editor-main-toolbar #btn-menu-1').click();
|
||||
|
||||
cy.wait('@mediaLoad');
|
||||
|
||||
cy.get('.editor-bottom-bar #navigator-edit-btn').click();
|
||||
|
||||
cy.get('.editor-main-toolbar #media-content-1 .toolbar-card:nth-of-type(2)').find('img').should('be.visible');
|
||||
|
||||
// Get a table row, select it and add to the region
|
||||
cy.get('.editor-main-toolbar #media-content-1 .toolbar-card:nth-of-type(2) .select-button').click({force: true}).then(() => {
|
||||
cy.get('#properties-panel-form-container .background-image-drop').click().then(() => {
|
||||
// Save form
|
||||
cy.get('#properties-panel button[data-action="save"]').click();
|
||||
|
||||
// Should show a notification for the successful save
|
||||
cy.get('.toast-success').contains('Edited');
|
||||
|
||||
// Check if the background field has an image
|
||||
cy.get('#properties-panel .background-image-add img#bg_image_image').should('be.visible');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Navigator
|
||||
it.skip('should change and save the region´s position', () => {
|
||||
// Create and alias for position save and reload layout
|
||||
|
||||
cy.intercept('GET', '/layout?layoutId=*').as('reloadLayout');
|
||||
cy.intercept('GET', '/region/form/edit/*').as('reloadRegion');
|
||||
cy.intercept('GET', '**/region/preview/*').as('regionPreview');
|
||||
|
||||
// Open navigator edit
|
||||
cy.get('.editor-bottom-bar #navigator-edit-btn').click();
|
||||
|
||||
// Wait for the region to preview
|
||||
cy.wait('@regionPreview');
|
||||
|
||||
cy.get('#layout-navigator [data-type="region"]:first').then(($originalRegion) => {
|
||||
const regionId = $originalRegion.attr('id');
|
||||
|
||||
// Select region
|
||||
cy.get('#layout-navigator-content #' + regionId).click();
|
||||
|
||||
// Move region 50px for each dimension
|
||||
cy.get('#layout-navigator-content #' + regionId).then(($movedRegion) => {
|
||||
const regionOriginalPosition = {
|
||||
top: Math.round($movedRegion.position().top),
|
||||
left: Math.round($movedRegion.position().left),
|
||||
};
|
||||
|
||||
const offsetToAdd = 50;
|
||||
|
||||
// Move the region
|
||||
cy.get('#layout-navigator-content #' + regionId)
|
||||
.trigger('mousedown', {
|
||||
which: 1,
|
||||
})
|
||||
.trigger('mousemove', {
|
||||
which: 1,
|
||||
pageX: $movedRegion.width() / 2 + $movedRegion.offset().left + offsetToAdd,
|
||||
pageY: $movedRegion.height() / 2 + $movedRegion.offset().top + offsetToAdd,
|
||||
})
|
||||
.trigger('mouseup');
|
||||
|
||||
// Close the navigator edit
|
||||
cy.wait('@reloadRegion');
|
||||
|
||||
// Save
|
||||
cy.get('#properties-panel button#save').click();
|
||||
|
||||
// Wait for the layout to reload
|
||||
cy.wait('@reloadLayout');
|
||||
|
||||
// Check if the region´s position are not the original
|
||||
cy.get('#layout-navigator-content #' + regionId).then(($changedRegion) => {
|
||||
expect(Math.round($changedRegion.position().top)).to.not.eq(regionOriginalPosition.top);
|
||||
expect(Math.round($changedRegion.position().left)).to.not.eq(regionOriginalPosition.left);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it.skip('should delete a widget using the toolbar bin', () => {
|
||||
cy.intercept('GET', '/layout?layoutId=*').as('reloadLayout');
|
||||
cy.intercept('GET', '/region/preview/*').as('regionPreview');
|
||||
|
||||
// Select a widget from the timeline
|
||||
cy.get('#layout-timeline .designer-region:first [data-type="widget"]:first-child').click().then(($el) => {
|
||||
const widgetId = $el.attr('id');
|
||||
|
||||
// Wait for the widget to be loaded
|
||||
cy.wait('@regionPreview');
|
||||
|
||||
// Click trash container
|
||||
cy.get('.editor-bottom-bar button#delete-btn').click({force: true});
|
||||
|
||||
// Confirm delete on modal
|
||||
cy.get('[data-test="deleteObjectModal"] button.btn-bb-confirm').click();
|
||||
|
||||
// Check toast message
|
||||
cy.get('.toast-success').contains('Deleted');
|
||||
|
||||
// Wait for the layout to reload
|
||||
cy.wait('@reloadLayout');
|
||||
|
||||
// Check that widget is not on timeline
|
||||
cy.get('#layout-timeline [data-type="widget"]#' + widgetId).should('not.exist');
|
||||
});
|
||||
});
|
||||
|
||||
it.skip('saves the widgets order when sorting by dragging', () => {
|
||||
cy.intercept('GET', 'POST', '**/playlist/order/*').as('saveOrder');
|
||||
cy.intercept('GET', '/layout?layoutId=*').as('reloadLayout');
|
||||
|
||||
cy.get('#layout-timeline .designer-region:first [data-type="widget"]:first-child').then(($oldWidget) => {
|
||||
const offsetX = 50;
|
||||
|
||||
// Move to the second widget position ( plus offset )
|
||||
cy.wrap($oldWidget)
|
||||
.trigger('mousedown', {
|
||||
which: 1,
|
||||
})
|
||||
.trigger('mousemove', {
|
||||
which: 1,
|
||||
pageX: $oldWidget.offset().left + $oldWidget.width() * 1.5 + offsetX,
|
||||
})
|
||||
.trigger('mouseup', {force: true});
|
||||
|
||||
cy.wait('@saveOrder');
|
||||
|
||||
// Should show a notification for the order change
|
||||
cy.get('.toast-success').contains('Order Changed');
|
||||
|
||||
// Reload layout and check if the new first widget has a different Id
|
||||
cy.wait('@reloadLayout');
|
||||
|
||||
cy.get('#layout-timeline .designer-region:first [data-type="widget"]:first-child').then(($newWidget) => {
|
||||
expect($oldWidget.attr('id')).not.to.eq($newWidget.attr('id'));
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
70
cypress/e2e/Layout/Editor/layout_editor_status_bar.cy.js
Normal file
70
cypress/e2e/Layout/Editor/layout_editor_status_bar.cy.js
Normal file
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright (C) 2025 Xibo Signage Ltd
|
||||
*
|
||||
* Xibo - Digital Signage - https://xibosignage.com
|
||||
*
|
||||
* This file is part of Xibo.
|
||||
*
|
||||
* Xibo is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* Xibo is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* eslint-disable max-len */
|
||||
describe('Layout Editor Status Bar', function() {
|
||||
const layoutStatusSelector = '#layout-info-status';
|
||||
const layoutNameSelector = '.layout-info-name span';
|
||||
const layoutDurationSelector = '.layout-info-duration .layout-info-duration-value';
|
||||
const layoutDimensionsSelector = '.layout-info-dimensions span';
|
||||
const tooltipSelector = '.popover';
|
||||
|
||||
beforeEach(function() {
|
||||
cy.login();
|
||||
cy.visit('/layout/view');
|
||||
cy.get('button.layout-add-button').click();
|
||||
cy.get('#layout-viewer').should('be.visible');
|
||||
});
|
||||
|
||||
it('should display the correct Layout status icon and tooltip', function() {
|
||||
cy.get(layoutStatusSelector)
|
||||
.should('be.visible')
|
||||
.and('have.class', 'badge-danger')
|
||||
.trigger('mouseover');
|
||||
|
||||
cy.get(tooltipSelector)
|
||||
.should('be.visible')
|
||||
.and('contain', 'This Layout is invalid');
|
||||
|
||||
cy.get(layoutStatusSelector).trigger('mouseout');
|
||||
});
|
||||
|
||||
it('should display the correct Layout name', () => {
|
||||
// Verify the Layout name text
|
||||
cy.get(layoutNameSelector)
|
||||
.should('be.visible')
|
||||
.and('contain', 'Untitled');
|
||||
});
|
||||
|
||||
it('should display the correct Layout duration', () => {
|
||||
// Verify the duration is correctly displayed
|
||||
cy.get(layoutDurationSelector)
|
||||
.should('be.visible')
|
||||
.and('contain', '00:00');
|
||||
});
|
||||
|
||||
it('should display the correct Layout dimensions', () => {
|
||||
// Verify the dimensions are correctly displayed
|
||||
cy.get(layoutDimensionsSelector)
|
||||
.should('be.visible')
|
||||
.and('contain', '1920x1080');
|
||||
});
|
||||
});
|
||||
181
cypress/e2e/Layout/Editor/layout_editor_toolbar.cy.js
Normal file
181
cypress/e2e/Layout/Editor/layout_editor_toolbar.cy.js
Normal file
@@ -0,0 +1,181 @@
|
||||
/*
|
||||
* Copyright (C) 2025 Xibo Signage Ltd
|
||||
*
|
||||
* Xibo - Digital Signage - https://xibosignage.com
|
||||
*
|
||||
* This file is part of Xibo.
|
||||
*
|
||||
* Xibo is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* Xibo is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* eslint-disable max-len */
|
||||
describe('Layout Editor Toolbar', function() {
|
||||
beforeEach(function() {
|
||||
cy.login();
|
||||
});
|
||||
|
||||
it.skip('should expand and close the toolbox', function() {
|
||||
cy.visit('/layout/view');
|
||||
cy.get('button[href="/layout"]').click();
|
||||
|
||||
cy.openToolbarMenu(0);
|
||||
cy.get('.close-content').filter(':visible').click();
|
||||
});
|
||||
|
||||
const setZoomLevel = (level) => {
|
||||
cy.intercept('POST', '/user/pref').as('updatePreferences');
|
||||
|
||||
cy.openToolbarMenu(0);
|
||||
|
||||
cy.get('.toolbar-level-control-menu').click();
|
||||
cy.get('nav.navbar').then(($toolbar) => {
|
||||
if ($toolbar.hasClass(`toolbar-level-${level}`)) return;
|
||||
cy.get(`i[data-level="${level}"]`).click();
|
||||
cy.wait('@updatePreferences');
|
||||
});
|
||||
cy.get('nav.navbar').should('have.class', `toolbar-level-${level}`);
|
||||
};
|
||||
|
||||
it.skip('should be able to set zoom level to 1', function() {
|
||||
cy.visit('/layout/view');
|
||||
cy.get('button[href="/layout"]').click();
|
||||
setZoomLevel(1);
|
||||
});
|
||||
|
||||
it.skip('should be able to set zoom level to 2', function() {
|
||||
cy.visit('/layout/view');
|
||||
cy.get('button[href="/layout"]').click();
|
||||
setZoomLevel(2);
|
||||
});
|
||||
|
||||
function searchAndAddElement(tabIndex, keyword, elementSelector, subTypeSelector, paneSelector) {
|
||||
cy.intercept('POST', '/region/*').as('addRegion');
|
||||
|
||||
// Search for the element
|
||||
cy.toolbarSearch(keyword);
|
||||
cy.get(paneSelector + '.active')
|
||||
.find('.toolbar-pane-content')
|
||||
.find('.toolbar-card')
|
||||
.should('have.length.greaterThan', 0)
|
||||
.each(($card) => {
|
||||
cy.wrap($card)
|
||||
.find('.card-title')
|
||||
.should('include.text', keyword);
|
||||
});
|
||||
|
||||
// Add the widget to layout
|
||||
cy.get(elementSelector).click();
|
||||
if (subTypeSelector) {
|
||||
cy.get(subTypeSelector).click();
|
||||
}
|
||||
cy.get('.viewer-object').click();
|
||||
cy.wait('@addRegion').then((interception) => { // todo: error here
|
||||
expect(interception.response.statusCode).to.eq(200);
|
||||
});
|
||||
}
|
||||
|
||||
it.skip('should navigate to Widgets tab, search and add a widget', function() {
|
||||
|
||||
cy.visit('/layout/view');
|
||||
cy.get('button[href="/layout"]').click();
|
||||
|
||||
// Open the respective toolbar tab
|
||||
cy.openToolbarMenu(0);
|
||||
|
||||
searchAndAddElement(0, 'Clock', '[data-sub-type="clock"]', '[data-sub-type="clock-analogue"]', '.toolbar-widgets-pane');
|
||||
});
|
||||
|
||||
it.skip('should navigate to Global Elements tab, search and add an element', function() {
|
||||
|
||||
cy.visit('/layout/view');
|
||||
cy.get('button[href="/layout"]').click();
|
||||
|
||||
searchAndAddElement(1, 'Text', '[data-template-id="text"]', '', '.toolbar-global-pane');
|
||||
});
|
||||
|
||||
function testLibrarySearchAndAddMedia(mediaType, tabIndex, keyword, folderName, mediaTitle) {
|
||||
cy.intercept('POST', '/user/pref').as('updatePreferences');
|
||||
cy.intercept('GET', '/folders?start=0&length=10').as('loadFolders');
|
||||
cy.intercept('GET', '/library/search*').as('librarySearch');
|
||||
cy.intercept('POST', '/region/*').as('addRegion');
|
||||
|
||||
cy.openToolbarMenu(tabIndex);
|
||||
|
||||
// Conditionally filter media by Folder if folderName is provided
|
||||
if (folderName) {
|
||||
cy.get(`.toolbar-pane.toolbar-${mediaType}-pane.active`)
|
||||
.find('#input-folder')
|
||||
.parent()
|
||||
.find('.select2-selection')
|
||||
.click();
|
||||
cy.wait('@loadFolders');
|
||||
cy.get('.select2-container--open')
|
||||
.contains(folderName)
|
||||
.click();
|
||||
cy.wait('@updatePreferences');
|
||||
cy.wait('@librarySearch');
|
||||
}
|
||||
|
||||
// Search for a media
|
||||
cy.toolbarSearchWithActiveFilter(keyword);
|
||||
cy.get(`.toolbar-pane.toolbar-${mediaType}-pane.active`)
|
||||
.find('.toolbar-pane-content')
|
||||
.find('.toolbar-card[data-type="media"]')
|
||||
.each(($card) => {
|
||||
cy.wrap($card)
|
||||
.find('span.media-title')
|
||||
.should('include.text', keyword);
|
||||
});
|
||||
|
||||
cy.wait('@librarySearch');
|
||||
cy.wait('@updatePreferences');
|
||||
|
||||
// Add media to layout
|
||||
cy.get(`.toolbar-pane.toolbar-${mediaType}-pane.active`)
|
||||
.find(`[data-card-title="${mediaTitle}"]`)
|
||||
.should('exist')
|
||||
.click();
|
||||
cy.get('.viewer-object').click();
|
||||
cy.wait('@addRegion');
|
||||
}
|
||||
|
||||
// Test cases
|
||||
it.skip('should navigate to Library Image Search tab, filter, search and add media', function() {
|
||||
testLibrarySearchAndAddMedia('image', 2, 'media_for_search', 'FolderWithImage', 'media_for_search_in_folder');
|
||||
});
|
||||
|
||||
it.skip('should navigate to Library Audio Search tab, filter, search and add media', function() {
|
||||
testLibrarySearchAndAddMedia('audio', 3, 'test-audio', null, 'test-audio.mp3');
|
||||
});
|
||||
|
||||
it.skip('should navigate to Library Video Search tab, filter, search and add media', function() {
|
||||
testLibrarySearchAndAddMedia('video', 4, 'test-video', null, 'test-video.mp4');
|
||||
});
|
||||
|
||||
it.skip('should navigate to Interactive Actions tab and search for actions', function() {
|
||||
const keyword = 'Next';
|
||||
|
||||
cy.visit('/layout/view');
|
||||
cy.get('button[href="/layout"]').click();
|
||||
|
||||
cy.openToolbarMenu(7, false);
|
||||
cy.toolbarSearch(keyword);
|
||||
cy.get('.toolbar-pane.toolbar-actions-pane.active')
|
||||
.find('.toolbar-pane-content .toolbar-card')
|
||||
.each(($card) => {
|
||||
cy.wrap($card).find('.card-title').should('include.text', keyword);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
168
cypress/e2e/Layout/Editor/layout_editor_unchanged.cy.js
Normal file
168
cypress/e2e/Layout/Editor/layout_editor_unchanged.cy.js
Normal file
@@ -0,0 +1,168 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Xibo Signage Ltd
|
||||
*
|
||||
* Xibo - Digital Signage - https://xibosignage.com
|
||||
*
|
||||
* This file is part of Xibo.
|
||||
*
|
||||
* Xibo is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* Xibo is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
describe('Layout Designer (Populated/Unchanged)', function() {
|
||||
before(function() {
|
||||
// Import existing
|
||||
// cy.importLayout('../assets/export_test_layout.zip').as('testLayoutId').then((res) => {
|
||||
// cy.checkoutLayout(res);
|
||||
// });
|
||||
});
|
||||
|
||||
beforeEach(function() {
|
||||
cy.login();
|
||||
// cy.goToLayoutAndLoadPrefs(this.testLayoutId);
|
||||
});
|
||||
|
||||
it.skip('should load all the layout designer elements', function() {
|
||||
// Check if the basic elements of the designer loaded
|
||||
cy.get('#layout-editor').should('be.visible');
|
||||
cy.get('.timeline-panel').should('be.visible');
|
||||
cy.get('#layout-viewer-container').should('be.visible');
|
||||
cy.get('#properties-panel').should('be.visible');
|
||||
});
|
||||
|
||||
it.skip('shows widget properties in the properties panel when clicking on a widget in the timeline', function() {
|
||||
// Select the first widget from the first region on timeline ( image )
|
||||
cy.get('#layout-timeline .designer-region:first [data-type="widget"]:first-child').click();
|
||||
|
||||
// Check if the properties panel title is Edit Image
|
||||
cy.get('#properties-panel').contains('Edit Image');
|
||||
});
|
||||
|
||||
it.skip('should open the playlist editor and be able to show modals', function() {
|
||||
cy.intercept('GET', '/playlist/widget/form/edit/*').as('reloadWidget');
|
||||
|
||||
// Open the playlist editor
|
||||
cy.get('#layout-timeline .designer-region-info:first .open-playlist-editor').click();
|
||||
|
||||
// Wait for the widget to load
|
||||
cy.wait('@reloadWidget');
|
||||
|
||||
// Right click on the first widget in the playlist editor
|
||||
cy.get('.editor-modal #timeline-container .playlist-widget:first').rightclick();
|
||||
|
||||
// Open the delete modal for the first widget
|
||||
cy.get('.context-menu-overlay .context-menu-widget .deleteBtn').should('be.visible').click();
|
||||
|
||||
// Modal should be visible
|
||||
cy.get('[data-test="deleteObjectModal"]').should('be.visible');
|
||||
});
|
||||
|
||||
it.skip('should revert a saved form to a previous state', () => {
|
||||
let oldName;
|
||||
|
||||
// Create and alias for reload widget
|
||||
|
||||
cy.intercept('GET', '/playlist/widget/form/edit/*').as('reloadWidget');
|
||||
cy.intercept('PUT', '/playlist/widget/*').as('saveWidget');
|
||||
|
||||
// Select the first widget on timeline ( image )
|
||||
cy.get('#layout-timeline .designer-region:first [data-type="widget"]:first-child').click();
|
||||
|
||||
// Wait for the widget to load
|
||||
cy.wait('@reloadWidget');
|
||||
|
||||
// Get the input field
|
||||
cy.get('#properties-panel input[name="name"]').then(($input) => {
|
||||
// Save old name
|
||||
oldName = $input.val();
|
||||
|
||||
// Type the new name in the input
|
||||
cy.get('#properties-panel input[name="name"]').clear().type('newName');
|
||||
|
||||
// Save form
|
||||
cy.get('#properties-panel button[data-action="save"]').click();
|
||||
|
||||
// Should show a notification for the name change
|
||||
cy.get('.toast-success');
|
||||
|
||||
// Wait for the widget to save
|
||||
cy.wait('@reloadWidget');
|
||||
|
||||
// Click the revert button
|
||||
cy.get('.editor-bottom-bar #undo-btn').click();
|
||||
|
||||
// Wait for the widget to save
|
||||
cy.wait('@saveWidget');
|
||||
|
||||
// Test if the revert made the name go back to the old name
|
||||
cy.get('#properties-panel input[name="name"]').should('have.attr', 'value').and('equal', oldName);
|
||||
});
|
||||
});
|
||||
|
||||
it.skip('should revert the widgets order when using the undo feature', () => {
|
||||
cy.intercept('POST', '**/playlist/order/*').as('saveOrder');
|
||||
cy.intercept('GET', '/layout?layoutId=*').as('reloadLayout');
|
||||
|
||||
cy.get('#layout-timeline .designer-region:first [data-type="widget"]:first-child').then(($oldWidget) => {
|
||||
const offsetX = 50;
|
||||
|
||||
// Move to the second widget position ( plus offset )
|
||||
cy.wrap($oldWidget)
|
||||
.trigger('mousedown', {
|
||||
which: 1,
|
||||
})
|
||||
.trigger('mousemove', {
|
||||
which: 1,
|
||||
pageX: $oldWidget.offset().left + $oldWidget.width() * 1.5 + offsetX,
|
||||
})
|
||||
.trigger('mouseup', {force: true});
|
||||
|
||||
cy.wait('@saveOrder');
|
||||
|
||||
// Should show a notification for the order change
|
||||
cy.get('.toast-success').contains('Order Changed');
|
||||
|
||||
// Reload layout and check if the new first widget has a different Id
|
||||
cy.wait('@reloadLayout');
|
||||
|
||||
cy.get('#layout-timeline .designer-region:first [data-type="widget"]:first-child').then(($newWidget) => {
|
||||
expect($oldWidget.attr('id')).not.to.eq($newWidget.attr('id'));
|
||||
});
|
||||
|
||||
// Click the revert button
|
||||
cy.get('.editor-bottom-bar #undo-btn').click();
|
||||
|
||||
// Wait for the order to save
|
||||
cy.wait('@saveOrder');
|
||||
cy.wait('@reloadLayout');
|
||||
|
||||
// Test if the revert made the name go back to the first widget
|
||||
cy.get('#layout-timeline .designer-region:first [data-type="widget"]:first-child').then(($newWidget) => {
|
||||
expect($oldWidget.attr('id')).to.eq($newWidget.attr('id'));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it.skip('should play a preview in the viewer', () => {
|
||||
cy.intercept('GET', '**/region/preview/*').as('loadRegion');
|
||||
// Wait for the viewer and region to load
|
||||
cy.get('#layout-viewer-container .viewer-object.layout-player').should('be.visible');
|
||||
cy.wait('@loadRegion');
|
||||
|
||||
// Click play
|
||||
cy.get('.editor-bottom-bar #play-btn').click();
|
||||
|
||||
// Check if the fullscreen iframe has loaded
|
||||
cy.get('#layout-viewer-container #layout-viewer .viewer-object > iframe').should('be.visible');
|
||||
});
|
||||
});
|
||||
56
cypress/e2e/Layout/IA/toggle_mode_on_off.cy.js
Normal file
56
cypress/e2e/Layout/IA/toggle_mode_on_off.cy.js
Normal file
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright (C) 2025 Xibo Signage Ltd
|
||||
*
|
||||
* Xibo - Digital Signage - https://xibosignage.com
|
||||
*
|
||||
* This file is part of Xibo.
|
||||
*
|
||||
* Xibo is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* Xibo is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
describe('Test IA: Toggle Mode ON/OFF', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
cy.login();
|
||||
|
||||
// Navigate to Layouts page
|
||||
cy.visit('/layout/view');
|
||||
|
||||
//Click the Add Layout button
|
||||
cy.get('button.layout-add-button').click();
|
||||
cy.get('#layout-viewer').should('be.visible');
|
||||
});
|
||||
|
||||
it('should verify default status = OFF and checks the status of IA Mode when toggled to ON or OFF', () => {
|
||||
|
||||
//check default IA Mode = OFF
|
||||
cy.get('li.nav-item.interactive-control')
|
||||
.should('have.attr', 'data-status', 'off')
|
||||
.then(($el) => {
|
||||
cy.wrap($el).click({ force: true })
|
||||
})
|
||||
|
||||
//Toggle Mode = ON
|
||||
cy.get('li.nav-item.interactive-control')
|
||||
.should('have.attr', 'data-status', 'on')
|
||||
.and('contain.text', 'ON')
|
||||
|
||||
//Toggle OFF back to Layout Editor
|
||||
cy.get('li.nav-item.interactive-control').click({ force: true })
|
||||
cy.get('li.nav-item.interactive-control')
|
||||
.should('have.attr', 'data-status', 'off')
|
||||
.and('contain.text', 'OFF')
|
||||
});
|
||||
});
|
||||
97
cypress/e2e/Layout/Widget/layout_editor_clock.cy.js
Normal file
97
cypress/e2e/Layout/Widget/layout_editor_clock.cy.js
Normal file
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright (C) 2025 Xibo Signage Ltd
|
||||
*
|
||||
* Xibo - Digital Signage - https://xibosignage.com
|
||||
*
|
||||
* This file is part of Xibo.
|
||||
*
|
||||
* Xibo is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* Xibo is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* eslint-disable max-len */
|
||||
describe('Clock Analogue Widget', function() {
|
||||
beforeEach(function() {
|
||||
cy.login();
|
||||
});
|
||||
|
||||
it('should create a new layout and be redirected to the layout designer, add/delete analogue clock', function() {
|
||||
cy.intercept('/playlist/widget/*').as('saveWidget');
|
||||
cy.intercept('DELETE', '**/region/**').as('deleteWidget');
|
||||
cy.intercept('POST', '/user/pref').as('userPref');
|
||||
|
||||
cy.visit('/layout/view');
|
||||
cy.get('button[href="/layout"]').click();
|
||||
|
||||
// Open widget menu
|
||||
cy.openToolbarMenu(0);
|
||||
|
||||
cy.get('[data-sub-type="clock"]')
|
||||
.should('be.visible')
|
||||
.click();
|
||||
cy.wait('@userPref');
|
||||
|
||||
cy.get('[data-sub-type="clock-analogue"] > .toolbar-card-thumb')
|
||||
.should('be.visible')
|
||||
.click();
|
||||
cy.wait('@userPref');
|
||||
|
||||
cy.get('.viewer-object.layout.ui-droppable-active')
|
||||
.should('be.visible')
|
||||
.click();
|
||||
|
||||
// Check if the widget is in the viewer
|
||||
cy.get('#layout-viewer .designer-region .widget-preview[data-type="widget_clock-analogue"]').should('exist');
|
||||
|
||||
cy.get('[name="themeId"]').select('Dark', {force: true});
|
||||
cy.get('[name="offset"]').clear().type('1').trigger('change');
|
||||
cy.wait('@saveWidget');
|
||||
|
||||
cy.get('.widget-form .nav-link[href="#advancedTab"]').click();
|
||||
|
||||
// Type the new name in the input
|
||||
cy.get('#advancedTab input[name="name"]').clear().type('newName');
|
||||
cy.wait('@saveWidget');
|
||||
|
||||
// Set a duration
|
||||
cy.get('#advancedTab input[name="useDuration"]').check();
|
||||
cy.wait('@saveWidget');
|
||||
cy.get('#advancedTab input[name="duration"]').clear().type('12').trigger('change');
|
||||
cy.wait('@saveWidget');
|
||||
|
||||
// Change the background of the layout
|
||||
cy.get('.viewer-object').click({force: true});
|
||||
cy.get('[name="backgroundColor"]').clear().type('#ffffff').trigger('change');
|
||||
|
||||
// Validate background color changed wo white
|
||||
cy.get('.viewer-object').should('have.css', 'background-color', 'rgb(255, 255, 255)');
|
||||
|
||||
// Check if the name and duration values are the same entered
|
||||
cy.get('#layout-viewer .designer-region .widget-preview[data-type="widget_clock-analogue"]').parents('.designer-region').click();
|
||||
cy.get('.widget-form .nav-link[href="#advancedTab"]').click();
|
||||
cy.get('#advancedTab input[name="name"]').should('have.attr', 'value').and('equal', 'newName');
|
||||
cy.get('#advancedTab input[name="duration"]').should('have.attr', 'value').and('equal', '12');
|
||||
|
||||
// Delete
|
||||
cy.get('#layout-viewer .designer-region .widget-preview[data-type="widget_clock-analogue"]')
|
||||
.parents('.designer-region')
|
||||
.rightclick();
|
||||
|
||||
// todo -investigate further why this is not working in ci/cdk mode
|
||||
// cy.get('[data-title="Delete"]').click().then(() => {
|
||||
// cy.wait('@deleteWidget').its('response.statusCode').should('eq', 200);
|
||||
// cy.get('#layout-viewer .designer-region .widget-preview[data-type="widget_clock-analogue"]')
|
||||
// .should('not.exist');
|
||||
// });
|
||||
});
|
||||
});
|
||||
103
cypress/e2e/Layout/Widget/layout_editor_dataset.cy.js
Normal file
103
cypress/e2e/Layout/Widget/layout_editor_dataset.cy.js
Normal file
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright (C) 2025 Xibo Signage Ltd
|
||||
*
|
||||
* Xibo - Digital Signage - https://xibosignage.com
|
||||
*
|
||||
* This file is part of Xibo.
|
||||
*
|
||||
* Xibo is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* Xibo is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* eslint-disable max-len */
|
||||
describe('Dataset', function() {
|
||||
beforeEach(function() {
|
||||
cy.login();
|
||||
});
|
||||
|
||||
it('should create a new layout, add/delete dataset widget', function() {
|
||||
cy.intercept('/dataset?start=*').as('loadDatasets');
|
||||
cy.intercept('DELETE', '**/region/**').as('deleteWidget');
|
||||
cy.intercept('POST', '/user/pref').as('userPref');
|
||||
|
||||
cy.visit('/layout/view');
|
||||
cy.get('button[href="/layout"]').click();
|
||||
|
||||
// Open widget menu and add dataset widget
|
||||
cy.openToolbarMenu(0);
|
||||
|
||||
cy.get('[data-sub-type="dataset"]')
|
||||
.should('be.visible')
|
||||
.click();
|
||||
cy.wait('@userPref');
|
||||
|
||||
cy.get('[data-template-id="dataset_table_1"]')
|
||||
.should('be.visible')
|
||||
.click();
|
||||
cy.wait('@userPref');
|
||||
|
||||
cy.get('.viewer-object.layout.ui-droppable-active')
|
||||
.should('be.visible')
|
||||
.click();
|
||||
|
||||
// Verify widget exists in the layout viewer
|
||||
cy.get('#layout-viewer .designer-region .widget-preview[data-type="widget_dataset"]').should('exist');
|
||||
|
||||
// Select and configure the dataset
|
||||
cy.get('#configureTab .select2-selection').click();
|
||||
cy.wait('@loadDatasets');
|
||||
cy.get('.select2-container--open input[type="search"]').type('8 items');
|
||||
cy.get('.select2-container--open').contains('8 items').click();
|
||||
|
||||
cy.get('[name="lowerLimit"]').clear().type('1');
|
||||
cy.get('[name="upperLimit"]').clear().type('10');
|
||||
cy.get('.order-clause-row > :nth-child(2) > .form-control').select('Col1', {force: true});
|
||||
cy.get('.order-clause-row > .btn').click();
|
||||
cy.get(':nth-child(2) > :nth-child(2) > .form-control').select('Col2', {force: true});
|
||||
|
||||
// Open Appearance Tab
|
||||
cy.get('.nav-link[href="#appearanceTab"]').click();
|
||||
|
||||
// Ensure dataset has exactly two columns
|
||||
cy.get('#columnsOut li').should('have.length', 2);
|
||||
|
||||
// Move columns to "Columns Selected"
|
||||
cy.get('#columnsOut li:first').trigger('mousedown', {which: 1}).trigger('mousemove', {which: 1, pageX: 583, pageY: 440});
|
||||
cy.get('#columnsIn').click();
|
||||
cy.get('#columnsOut li:first').trigger('mousedown', {which: 1}).trigger('mousemove', {which: 1, pageX: 583, pageY: 440});
|
||||
cy.get('#columnsIn').click();
|
||||
|
||||
// Customize appearance settings
|
||||
cy.get('[name="showHeadings"]').check();
|
||||
cy.get('[name="rowsPerPage"]').clear().type('5');
|
||||
cy.get('[name="fontSize"]').clear().type('48');
|
||||
cy.get('[name="backgroundColor"]').clear().type('#333333');
|
||||
|
||||
// Delete widget
|
||||
// The .moveable-control-box overlay obstructing the right-click interaction on the designer region, causing the test to fail.
|
||||
// By invoking .hide(), we remove the overlay temporarily to allow uninterrupted interaction with the underlying elements.
|
||||
cy.get('.moveable-control-box').invoke('hide');
|
||||
|
||||
cy.get('#layout-viewer .designer-region .widget-preview[data-type="widget_dataset"]')
|
||||
.parents('.designer-region')
|
||||
.scrollIntoView()
|
||||
.should('be.visible')
|
||||
.rightclick();
|
||||
// Wait until the widget has been deleted
|
||||
// cy.get('[data-title="Delete"]').click().then(() => {
|
||||
// cy.wait('@deleteWidget').its('response.statusCode').should('eq', 200);
|
||||
// cy.get('#layout-viewer .designer-region .widget-preview[data-type="widget_dataset"]')
|
||||
// .should('not.exist');
|
||||
// });
|
||||
});
|
||||
});
|
||||
91
cypress/e2e/Layout/Widget/layout_editor_mastodon.cy.js
Normal file
91
cypress/e2e/Layout/Widget/layout_editor_mastodon.cy.js
Normal file
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright (C) 2025 Xibo Signage Ltd
|
||||
*
|
||||
* Xibo - Digital Signage - https://xibosignage.com
|
||||
*
|
||||
* This file is part of Xibo.
|
||||
*
|
||||
* Xibo is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* Xibo is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* eslint-disable max-len */
|
||||
describe('Mastodon', function() {
|
||||
beforeEach(function() {
|
||||
cy.login();
|
||||
});
|
||||
|
||||
it('should create a new layout and be redirected to the layout designer, add/delete Mastodon widget', function() {
|
||||
cy.intercept('DELETE', '**/region/**').as('deleteWidget');
|
||||
cy.intercept('POST', '/user/pref').as('userPref');
|
||||
|
||||
cy.visit('/layout/view');
|
||||
cy.get('button[href="/layout"]').click();
|
||||
|
||||
// Open widget menu
|
||||
cy.openToolbarMenu(0);
|
||||
|
||||
cy.get('[data-sub-type="mastodon"]')
|
||||
.should('be.visible')
|
||||
.click();
|
||||
cy.wait('@userPref');
|
||||
|
||||
cy.get('[data-template-id="social_media_static_1"] > .toolbar-card-thumb')
|
||||
.should('be.visible')
|
||||
.click();
|
||||
cy.wait('@userPref');
|
||||
|
||||
cy.get('.viewer-object.layout.ui-droppable-active')
|
||||
.should('be.visible')
|
||||
.click();
|
||||
|
||||
// Check if the widget is in the viewer
|
||||
cy.get('#layout-viewer .designer-region .widget-preview[data-type="widget_mastodon"]')
|
||||
.should('exist');
|
||||
|
||||
cy.get('[name="hashtag"]').clear();
|
||||
cy.get('[name="hashtag"]').type('#cat');
|
||||
cy.get('[name="searchOn"]').select('local', {force: true});
|
||||
cy.get('[name="numItems"]').clear().type('10').trigger('change');
|
||||
cy.get('[name="onlyMedia"]').check();
|
||||
|
||||
// Click on Appearance Tab
|
||||
cy.get('.nav-link[href="#appearanceTab"]').click();
|
||||
cy.get('[name="itemsPerPage"]').clear().type('2').trigger('change');
|
||||
|
||||
// Vertical/Fade/100/Right/Bottom
|
||||
cy.get('[name="displayDirection"]').select('Vertical', {force: true});
|
||||
cy.get('[name="effect"]').select('Fade', {force: true});
|
||||
cy.get('[name="speed"]').clear().type('100').trigger('change');
|
||||
cy.get('[name="alignmentH"]').select('Right', {force: true});
|
||||
cy.get('[name="alignmentV"]').select('Bottom', {force: true});
|
||||
|
||||
// Delete widget
|
||||
// The .moveable-control-box overlay obstructing the right-click interaction on the designer region, causing the test to fail.
|
||||
// By invoking .hide(), we remove the overlay temporarily to allow uninterrupted interaction with the underlying elements.
|
||||
cy.get('.moveable-control-box').invoke('hide');
|
||||
|
||||
cy.get('#layout-viewer .designer-region .widget-preview[data-type="widget_mastodon"]')
|
||||
.parents('.designer-region')
|
||||
.scrollIntoView()
|
||||
.should('be.visible')
|
||||
.rightclick();
|
||||
|
||||
// Wait until the widget has been deleted
|
||||
// cy.get('[data-title="Delete"]').click().then(() => {
|
||||
// cy.wait('@deleteWidget').its('response.statusCode').should('eq', 200);
|
||||
// cy.get('#layout-viewer .designer-region .widget-preview[data-type="widget_mastodon"]')
|
||||
// .should('not.exist');
|
||||
// });
|
||||
});
|
||||
});
|
||||
101
cypress/e2e/Layout/Widget/layout_editor_rss_ticker.cy.js
Normal file
101
cypress/e2e/Layout/Widget/layout_editor_rss_ticker.cy.js
Normal file
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright (C) 2025 Xibo Signage Ltd
|
||||
*
|
||||
* Xibo - Digital Signage - https://xibosignage.com
|
||||
*
|
||||
* This file is part of Xibo.
|
||||
*
|
||||
* Xibo is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* Xibo is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* eslint-disable max-len */
|
||||
describe('RSS Ticker', function() {
|
||||
beforeEach(function() {
|
||||
cy.login();
|
||||
});
|
||||
|
||||
it('should create a new layout and be redirected to the layout designer, add/delete RSS ticker widget', function() {
|
||||
cy.intercept('DELETE', '**/region/**').as('deleteWidget');
|
||||
cy.intercept('POST', '/user/pref').as('userPref');
|
||||
|
||||
cy.visit('/layout/view');
|
||||
cy.get('button[href="/layout"]').click();
|
||||
|
||||
// Open widget menu
|
||||
cy.openToolbarMenu(0);
|
||||
|
||||
cy.get('[data-sub-type="rss-ticker"]')
|
||||
.scrollIntoView()
|
||||
.should('be.visible')
|
||||
.click();
|
||||
cy.wait('@userPref');
|
||||
|
||||
cy.get('[data-template-id="article_image_only"] > .toolbar-card-thumb')
|
||||
.scrollIntoView()
|
||||
.should('be.visible')
|
||||
.click();
|
||||
cy.wait('@userPref');
|
||||
|
||||
cy.get('.viewer-object.layout.ui-droppable-active')
|
||||
.should('be.visible')
|
||||
.click();
|
||||
|
||||
// Check if the widget is in the viewer
|
||||
cy.get('#layout-viewer .designer-region .widget-preview[data-type="widget_rss-ticker"]').should('exist');
|
||||
cy.get('#layout-viewer .designer-region .widget-preview[data-type="widget_rss-ticker"]').parents('.designer-region').click();
|
||||
|
||||
// Validate if uri is not provide we show an error message
|
||||
cy.get('[name="numItems"]').clear().type('10').trigger('change');
|
||||
cy.get('.form-container').contains('Missing required property Feed URL');
|
||||
|
||||
cy.get('[name="uri"]').clear();
|
||||
cy.get('[name="uri"]').type('http://xibo.org.uk/feed');
|
||||
cy.get('[name="numItems"]').clear().type('10').trigger('change');
|
||||
cy.get('[name="durationIsPerItem"]').check();
|
||||
cy.get('[name="takeItemsFrom"]').select('End of the Feed', {force: true});
|
||||
cy.get('[name="reverseOrder"]').check();
|
||||
cy.get('[name="randomiseItems"]').check();
|
||||
|
||||
cy.get('[name="userAgent"]').clear().type('Mozilla/5.0');
|
||||
cy.get('[name="updateInterval"]').clear().type('10').trigger('change');
|
||||
|
||||
// Click on Appearance Tab
|
||||
cy.get('.nav-link[href="#appearanceTab"]').click();
|
||||
cy.get('[name="backgroundColor"]').clear().type('#dddddd');
|
||||
cy.get('[name="itemImageFit"]').select('Fill', {force: true});
|
||||
cy.get('[name="effect"]').select('Fade', {force: true});
|
||||
cy.get('[name="speed"]').clear().type('500');
|
||||
// Update CKEditor value
|
||||
cy.updateCKEditor('noDataMessage', 'No data to show');
|
||||
cy.get('[name="copyright"]').clear().type('Xibo').trigger('change');
|
||||
|
||||
// Delete widget
|
||||
// The .moveable-control-box overlay obstructing the right-click interaction on the designer region, causing the test to fail.
|
||||
// By invoking .hide(), we remove the overlay temporarily to allow uninterrupted interaction with the underlying elements.
|
||||
cy.get('.moveable-control-box').invoke('hide');
|
||||
|
||||
cy.get('#layout-viewer .designer-region .widget-preview[data-type="widget_rss-ticker"]')
|
||||
.parents('.designer-region')
|
||||
.scrollIntoView()
|
||||
.should('be.visible')
|
||||
.rightclick();
|
||||
|
||||
// Wait until the widget has been deleted
|
||||
// cy.get('[data-title="Delete"]').click().then(() => {
|
||||
// cy.wait('@deleteWidget').its('response.statusCode').should('eq', 200);
|
||||
// cy.get('#layout-viewer .designer-region .widget-preview[data-type="widget_rss-ticker"]')
|
||||
// .should('not.exist');
|
||||
// });
|
||||
});
|
||||
});
|
||||
56
cypress/e2e/Layout/layout_view.cy.js
Normal file
56
cypress/e2e/Layout/layout_view.cy.js
Normal file
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Xibo Signage Ltd
|
||||
*
|
||||
* Xibo - Digital Signage - https://xibosignage.com
|
||||
*
|
||||
* This file is part of Xibo.
|
||||
*
|
||||
* Xibo is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* Xibo is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* eslint-disable max-len */
|
||||
describe('Layout View', function() {
|
||||
beforeEach(function() {
|
||||
cy.login();
|
||||
});
|
||||
|
||||
it('searches and delete existing layout', function() {
|
||||
// Create random name
|
||||
const uuid = Cypress._.random(0, 1e10);
|
||||
|
||||
// Create a new layout and go to the layout's designer page, then load toolbar prefs
|
||||
cy.createLayout(uuid).as('testLayoutId').then((res) => {
|
||||
cy.intercept('GET', '/layout?draw=2&*').as('layoutGridLoad');
|
||||
|
||||
cy.visit('/layout/view');
|
||||
|
||||
// Filter for the created layout
|
||||
cy.get('#Filter input[name="layout"]')
|
||||
.type(uuid);
|
||||
|
||||
// Wait for the layout grid reload
|
||||
cy.wait('@layoutGridLoad');
|
||||
|
||||
// Click on the first row element to open the designer
|
||||
cy.get('#layouts tr:first-child .dropdown-toggle').click({force: true});
|
||||
cy.get('#layouts tr:first-child .layout_button_delete').click({force: true});
|
||||
|
||||
// Delete test layout
|
||||
cy.get('.bootbox .save-button').click();
|
||||
|
||||
// Check if layout is deleted in toast message
|
||||
cy.get('.toast').contains('Deleted ' + uuid);
|
||||
});
|
||||
});
|
||||
});
|
||||
322
cypress/e2e/Library/datasets.cy.js
Normal file
322
cypress/e2e/Library/datasets.cy.js
Normal file
@@ -0,0 +1,322 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Xibo Signage Ltd
|
||||
*
|
||||
* Xibo - Digital Signage - https://xibosignage.com
|
||||
*
|
||||
* This file is part of Xibo.
|
||||
*
|
||||
* Xibo is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* Xibo is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* eslint-disable max-len */
|
||||
describe('Datasets', function() {
|
||||
let testRun = '';
|
||||
|
||||
beforeEach(function() {
|
||||
cy.login();
|
||||
|
||||
testRun = Cypress._.random(0, 1e9);
|
||||
});
|
||||
|
||||
it('should add one empty dataset', function() {
|
||||
cy.visit('/dataset/view');
|
||||
|
||||
// Click on the Add Dataset button
|
||||
cy.contains('Add DataSet').click();
|
||||
|
||||
cy.get('.modal input#dataSet')
|
||||
.type('Cypress Test Dataset ' + testRun + '_1');
|
||||
|
||||
// Add first by clicking next
|
||||
cy.get('.modal .save-button').click();
|
||||
|
||||
// Check if dataset is added in toast message
|
||||
cy.contains('Added Cypress Test Dataset ' + testRun + '_1');
|
||||
});
|
||||
|
||||
it('should be able to cancel creating dataset', function() {
|
||||
cy.visit('/dataset/view');
|
||||
|
||||
// Click on the Add Dataset button
|
||||
cy.contains('Add DataSet').click();
|
||||
|
||||
cy.get('.modal input#dataSet')
|
||||
.type('Cypress Test Dataset ' + testRun + '_1');
|
||||
|
||||
// Click cancel
|
||||
cy.get('.modal #dialog_btn_1').click();
|
||||
|
||||
// Check if you are back to the view page
|
||||
cy.url().should('include', '/dataset/view');
|
||||
});
|
||||
|
||||
it('searches and edit existing dataset', function() {
|
||||
// Create a new dataset and then search for it and delete it
|
||||
cy.createDataset('Cypress Test Dataset ' + testRun).then((id) => {
|
||||
cy.intercept({
|
||||
url: '/dataset?*',
|
||||
query: {dataSet: 'Cypress Test Dataset ' + testRun},
|
||||
}).as('loadGridAfterSearch');
|
||||
|
||||
// Intercept the PUT request
|
||||
cy.intercept({
|
||||
method: 'PUT',
|
||||
url: '/dataset/*',
|
||||
}).as('putRequest');
|
||||
|
||||
cy.visit('/dataset/view');
|
||||
|
||||
// Filter for the created dataset
|
||||
cy.get('#Filter input[name="dataSet"]')
|
||||
.type('Cypress Test Dataset ' + testRun);
|
||||
|
||||
// Wait for the grid reload
|
||||
cy.wait('@loadGridAfterSearch');
|
||||
cy.get('#datasets tbody tr').should('have.length', 1);
|
||||
|
||||
// Click on the first row element to open the delete modal
|
||||
cy.get('#datasets tr:first-child .dropdown-toggle').click({force: true});
|
||||
cy.get('#datasets tr:first-child .dataset_button_edit').click({force: true});
|
||||
|
||||
cy.get('.modal input#dataSet').clear()
|
||||
.type('Cypress Test Dataset Edited ' + testRun);
|
||||
|
||||
// edit test dataset
|
||||
cy.get('.bootbox .save-button').click();
|
||||
|
||||
// Wait for the intercepted PUT request and check the form data
|
||||
cy.wait('@putRequest').then((interception) => {
|
||||
// Get the request body (form data)
|
||||
const response = interception.response;
|
||||
const responseData = response.body.data;
|
||||
|
||||
// assertion on the "dataset" value
|
||||
expect(responseData.dataSet).to.eq('Cypress Test Dataset Edited ' + testRun);
|
||||
});
|
||||
|
||||
// Delete the dataset and assert success
|
||||
cy.deleteDataset(id).then((res) => {
|
||||
expect(res.status).to.equal(204);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('add row/column to an existing dataset', function() {
|
||||
// Create a new dataset and then search for it and delete it
|
||||
cy.createDataset('Cypress Test Dataset ' + testRun).then((id) => {
|
||||
cy.intercept({
|
||||
url: '/dataset?*',
|
||||
query: {dataSet: 'Cypress Test Dataset ' + testRun},
|
||||
}).as('loadGridAfterSearch');
|
||||
|
||||
// Intercept the PUT request
|
||||
cy.intercept({
|
||||
method: 'POST',
|
||||
url: /\/dataset\/\d+\/column$/,
|
||||
}).as('postRequestAddColumn');
|
||||
|
||||
cy.intercept({
|
||||
method: 'POST',
|
||||
url: /\/dataset\/data\/\d+/,
|
||||
}).as('postRequestAddRow');
|
||||
|
||||
cy.visit('/dataset/view');
|
||||
|
||||
// Filter for the created dataset
|
||||
cy.get('#Filter input[name="dataSet"]')
|
||||
.type('Cypress Test Dataset ' + testRun);
|
||||
|
||||
// Wait for the grid reload
|
||||
cy.wait('@loadGridAfterSearch');
|
||||
cy.get('#datasets tbody tr').should('have.length', 1);
|
||||
|
||||
// Click on the first row element to open the View data
|
||||
cy.get('#datasets tr:first-child .dropdown-toggle').click({force: true});
|
||||
cy.get('#datasets tr:first-child .dataset_button_viewcolumns').click({force: true});
|
||||
|
||||
cy.get('#datasets').contains('No data available in table');
|
||||
|
||||
// Add data row to dataset
|
||||
cy.contains('Add Column').click();
|
||||
cy.get('.modal input#heading').type('Col1');
|
||||
|
||||
// Save
|
||||
cy.get('.bootbox .save-button').click();
|
||||
|
||||
// Wait for the intercepted PUT request and check the form data
|
||||
cy.wait('@postRequestAddColumn').then((interception) => {
|
||||
// Get the request body (form data)
|
||||
const response = interception.response;
|
||||
const responseData = response.body.data;
|
||||
|
||||
// assertion on the "dataset" value
|
||||
expect(responseData.heading).to.eq('Col1');
|
||||
|
||||
cy.contains('View Data').click();
|
||||
cy.get('#datasets').contains('No data available in table');
|
||||
|
||||
// Add data row to dataset
|
||||
cy.contains('Add Row').click();
|
||||
cy.get('#dataSetDataAdd').within(() => {
|
||||
cy.get('input:first').type('Your text goes here');
|
||||
});
|
||||
|
||||
// Save
|
||||
cy.get('.bootbox .save-button').click();
|
||||
|
||||
// Wait for the intercepted request and check data
|
||||
cy.wait('@postRequestAddRow').then((interception) => {
|
||||
cy.contains('Added Row');
|
||||
});
|
||||
});
|
||||
|
||||
// Now try to delete the dataset
|
||||
cy.visit('/dataset/view');
|
||||
|
||||
// Filter for the created dataset
|
||||
cy.get('#Filter input[name="dataSet"]')
|
||||
.type('Cypress Test Dataset ' + testRun);
|
||||
|
||||
// Wait for the grid reload
|
||||
cy.wait('@loadGridAfterSearch');
|
||||
cy.get('#datasets tbody tr').should('have.length', 1);
|
||||
|
||||
// Click on the first row element to open the View data
|
||||
cy.get('#datasets tr:first-child .dropdown-toggle').click({force: true});
|
||||
cy.get('#datasets tr:first-child .dataset_button_delete').click({force: true});
|
||||
});
|
||||
});
|
||||
|
||||
it('copy an existing dataset', function() {
|
||||
// Create a new dataset and then search for it and copy it
|
||||
cy.createDataset('Cypress Test Dataset ' + testRun).then((res) => {
|
||||
cy.intercept({
|
||||
url: '/dataset?*',
|
||||
query: {dataSet: 'Cypress Test Dataset ' + testRun},
|
||||
}).as('loadGridAfterSearch');
|
||||
|
||||
// Intercept the POST request
|
||||
cy.intercept({
|
||||
method: 'POST',
|
||||
url: /\/dataset\/copy\/\d+/,
|
||||
}).as('postRequest');
|
||||
|
||||
cy.visit('/dataset/view');
|
||||
|
||||
// Filter for the created dataset
|
||||
cy.get('#Filter input[name="dataSet"]')
|
||||
.type('Cypress Test Dataset ' + testRun);
|
||||
|
||||
// Wait for the grid reload
|
||||
cy.wait('@loadGridAfterSearch');
|
||||
cy.get('#datasets tbody tr').should('have.length', 1);
|
||||
|
||||
// Click on the first row element to open the delete modal
|
||||
cy.get('#datasets tr:first-child .dropdown-toggle').click({force: true});
|
||||
cy.get('#datasets tr:first-child .dataset_button_copy').click({force: true});
|
||||
|
||||
// save
|
||||
cy.get('.bootbox .save-button').click();
|
||||
|
||||
// Wait for the intercepted POST request and check the form data
|
||||
cy.wait('@postRequest').then((interception) => {
|
||||
// Get the request body (form data)
|
||||
const response = interception.response;
|
||||
const responseData = response.body.data;
|
||||
expect(responseData.dataSet).to.include('Cypress Test Dataset ' + testRun + ' 2');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('searches and delete existing dataset', function() {
|
||||
// Create a new dataset and then search for it and delete it
|
||||
cy.createDataset('Cypress Test Dataset ' + testRun).then((res) => {
|
||||
cy.intercept('GET', '/dataset?draw=2&*').as('datasetGridLoad');
|
||||
|
||||
cy.visit('/dataset/view');
|
||||
|
||||
// Filter for the created dataset
|
||||
cy.get('#Filter input[name="dataSet"]')
|
||||
.type('Cypress Test Dataset ' + testRun);
|
||||
|
||||
// Wait for the grid reload
|
||||
cy.wait('@datasetGridLoad');
|
||||
cy.get('#datasets tbody tr').should('have.length', 1);
|
||||
|
||||
// Click on the first row element to open the delete modal
|
||||
cy.get('#datasets tr:first-child .dropdown-toggle').click({force: true});
|
||||
cy.get('#datasets tr:first-child .dataset_button_delete').click({force: true});
|
||||
|
||||
// Delete test dataset
|
||||
cy.get('.bootbox .save-button').click();
|
||||
|
||||
// Check if dataset is deleted in toast message
|
||||
cy.get('.toast').contains('Deleted Cypress Test Dataset');
|
||||
});
|
||||
});
|
||||
|
||||
it('selects multiple datasets and delete them', function() {
|
||||
// Create a new dataset and then search for it and delete it
|
||||
cy.createDataset('Cypress Test Dataset ' + testRun).then((res) => {
|
||||
cy.intercept('GET', '/dataset?draw=2&*').as('datasetGridLoad');
|
||||
|
||||
// Delete all test datasets
|
||||
cy.visit('/dataset/view');
|
||||
|
||||
// Clear filter
|
||||
cy.get('#Filter input[name="dataSet"]')
|
||||
.clear()
|
||||
.type('Cypress Test Dataset');
|
||||
|
||||
// Wait for the grid reload
|
||||
cy.wait('@datasetGridLoad');
|
||||
|
||||
// Select all
|
||||
cy.get('button[data-toggle="selectAll"]').click();
|
||||
|
||||
// Delete all
|
||||
cy.get('.dataTables_info button[data-toggle="dropdown"]').click();
|
||||
cy.get('.dataTables_info a[data-button-id="dataset_button_delete"]').click();
|
||||
|
||||
cy.get('input#deleteData').check();
|
||||
cy.get('button.save-button').click();
|
||||
|
||||
// Modal should contain one successful delete at least
|
||||
cy.get('.modal-body').contains(': Success');
|
||||
});
|
||||
});
|
||||
|
||||
// ---------
|
||||
// Tests - Error handling
|
||||
it('should not add a remote dataset without URI', function() {
|
||||
cy.visit('/dataset/view');
|
||||
|
||||
// Click on the Add Dataset button
|
||||
cy.contains('Add DataSet').click();
|
||||
|
||||
cy.get('.modal input#dataSet')
|
||||
.type('Cypress Test Dataset ' + testRun);
|
||||
|
||||
cy.get('.modal input#isRemote').check();
|
||||
|
||||
// Add first by clicking next
|
||||
cy.get('.modal .save-button').click();
|
||||
|
||||
// Click on the "Remote" tab
|
||||
cy.get(':nth-child(2) > .nav-link').should('be.visible').click();
|
||||
|
||||
// Check that the error message is displayed for the missing URI field
|
||||
cy.get('#uri-error').should('have.text', 'This field is required.');
|
||||
});
|
||||
});
|
||||
111
cypress/e2e/Library/media.cy.js
Normal file
111
cypress/e2e/Library/media.cy.js
Normal file
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Xibo Signage Ltd
|
||||
*
|
||||
* Xibo - Digital Signage - https://xibosignage.com
|
||||
*
|
||||
* This file is part of Xibo.
|
||||
*
|
||||
* Xibo is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* Xibo is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* eslint-disable max-len */
|
||||
describe('Media Admin', function() {
|
||||
let testRun;
|
||||
|
||||
beforeEach(function() {
|
||||
cy.login();
|
||||
|
||||
testRun = Cypress._.random(0, 1e9);
|
||||
});
|
||||
|
||||
it('should add a media via url', function() {
|
||||
cy.visit('/library/view');
|
||||
|
||||
// Click on the Add Playlist button
|
||||
cy.contains('Add media (URL)').click();
|
||||
|
||||
cy.get('#url')
|
||||
.type('https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4');
|
||||
|
||||
cy.get('#optionalName')
|
||||
.type('Cypress Test Media ' + testRun);
|
||||
|
||||
cy.get('.modal .save-button').click();
|
||||
cy.wait(24000);
|
||||
|
||||
// Filter for the created playlist
|
||||
cy.get('#media')
|
||||
.type('Cypress Test Media ' + testRun);
|
||||
|
||||
// Should have the added playlist
|
||||
cy.get('#libraryItems tbody tr').should('have.length', 1);
|
||||
cy.get('#libraryItems tbody tr:nth-child(1) td:nth-child(2)').contains('Cypress Test Media ' + testRun);
|
||||
});
|
||||
|
||||
it('should cancel adding a media', function() {
|
||||
cy.visit('/library/view');
|
||||
|
||||
// Click on the Add Playlist button
|
||||
cy.contains('Add media (URL)').click();
|
||||
|
||||
cy.get('#url')
|
||||
.type('https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4');
|
||||
|
||||
cy.get('#optionalName')
|
||||
.type('Cypress Test Media ' + testRun);
|
||||
|
||||
// Click cancel
|
||||
cy.get('#dialog_btn_1').click();
|
||||
|
||||
// Check if you are back to the view page
|
||||
cy.url().should('include', '/library/view');
|
||||
});
|
||||
|
||||
it('should show a list of Media', function() {
|
||||
// Wait for playlist grid reload
|
||||
cy.intercept('/library?draw=1&*').as('mediaGridLoad');
|
||||
|
||||
cy.visit('/library/view').then(function() {
|
||||
cy.wait('@mediaGridLoad');
|
||||
cy.get('#libraryItems');
|
||||
});
|
||||
});
|
||||
|
||||
it.skip('selects media and delete them', function() {
|
||||
// Create a new playlist and then search for it and delete it
|
||||
cy.intercept('/library?draw=1&*').as('mediaGridLoad');
|
||||
|
||||
// Delete all test playlists
|
||||
cy.visit('/library/view');
|
||||
|
||||
// Clear filter and search for text playlists
|
||||
cy.get('#media')
|
||||
.clear()
|
||||
.type('Cypress Test Media');
|
||||
|
||||
// Wait for 1st playlist grid reload
|
||||
cy.wait('@mediaGridLoad');
|
||||
|
||||
// Select first entry
|
||||
cy.get('table#libraryItems').contains('Cypress Test Media').parents('tr.odd').should('be.visible').click();
|
||||
cy.get('button[data-toggle="dropdown"]').first().click();
|
||||
|
||||
// Click Delete
|
||||
cy.contains('Delete').click();
|
||||
cy.get('button.save-button').click();
|
||||
|
||||
// Modal should contain one successful delete at least
|
||||
cy.get('div[class="toast-message"]').should('contain', 'Deleted');
|
||||
});
|
||||
});
|
||||
439
cypress/e2e/Library/menuboards.cy.js
Normal file
439
cypress/e2e/Library/menuboards.cy.js
Normal file
@@ -0,0 +1,439 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Xibo Signage Ltd
|
||||
*
|
||||
* Xibo - Digital Signage - https://xibosignage.com
|
||||
*
|
||||
* This file is part of Xibo.
|
||||
*
|
||||
* Xibo is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* Xibo is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* eslint-disable max-len */
|
||||
describe('Menuboards', function() {
|
||||
let testRun = '';
|
||||
|
||||
beforeEach(function() {
|
||||
cy.login();
|
||||
|
||||
testRun = Cypress._.random(0, 1e9);
|
||||
});
|
||||
|
||||
it('should add a menuboard', function() {
|
||||
cy.visit('/menuboard/view');
|
||||
|
||||
// Click on the Add Menuboard button
|
||||
cy.contains('Add Menu Board').click();
|
||||
|
||||
cy.get('.modal input#name')
|
||||
.type('Cypress Test Menuboard ' + testRun + '_1');
|
||||
cy.get('.modal input#code')
|
||||
.type('MENUBOARD');
|
||||
cy.get('.modal textarea#description')
|
||||
.type('Menuboard Description');
|
||||
|
||||
// Add first by clicking next
|
||||
cy.get('.modal .save-button').click();
|
||||
|
||||
// Check if menuboard is added in toast message
|
||||
cy.contains('Added Menu Board');
|
||||
});
|
||||
|
||||
it('searches and edit existing menuboard', function() {
|
||||
// Create a new menuboard and then search for it and delete it
|
||||
cy.createMenuboard('Cypress Test Menuboard ' + testRun).then((res) => {
|
||||
cy.intercept({
|
||||
url: '/menuboard?*',
|
||||
query: {name: 'Cypress Test Menuboard ' + testRun},
|
||||
}).as('loadGridAfterSearch');
|
||||
|
||||
// Intercept the PUT request
|
||||
cy.intercept({
|
||||
method: 'PUT',
|
||||
url: '/menuboard/*',
|
||||
}).as('putRequest');
|
||||
|
||||
cy.visit('/menuboard/view');
|
||||
|
||||
// Filter for the created menuboard
|
||||
cy.get('#Filter input[name="name"]')
|
||||
.type('Cypress Test Menuboard ' + testRun);
|
||||
|
||||
// Wait for the grid reload
|
||||
cy.wait('@loadGridAfterSearch');
|
||||
cy.get('#menuBoards tbody tr').should('have.length', 1);
|
||||
|
||||
// Click on the first row element to open the delete modal
|
||||
cy.get('#menuBoards tr:first-child .dropdown-toggle').click({force: true});
|
||||
cy.get('#menuBoards tr:first-child .menuBoard_edit_button').click({force: true});
|
||||
|
||||
cy.get('.modal input#name').clear()
|
||||
.type('Cypress Test Menuboard Edited ' + testRun);
|
||||
|
||||
// edit test menuboard
|
||||
cy.get('.bootbox .save-button').click();
|
||||
|
||||
// Wait for the intercepted PUT request and check the form data
|
||||
cy.wait('@putRequest').then((interception) => {
|
||||
// Get the request body (form data)
|
||||
const response = interception.response;
|
||||
const responseData = response.body.data;
|
||||
|
||||
// assertion on the "menuboard" value
|
||||
expect(responseData.name).to.eq('Cypress Test Menuboard Edited ' + testRun);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('searches and delete existing menuboard', function() {
|
||||
// Create a new menuboard and then search for it and delete it
|
||||
cy.createMenuboard('Cypress Test Menuboard ' + testRun).then((res) => {
|
||||
cy.intercept({
|
||||
url: '/menuboard?*',
|
||||
query: {name: 'Cypress Test Menuboard ' + testRun},
|
||||
}).as('loadGridAfterSearch');
|
||||
|
||||
cy.visit('/menuboard/view');
|
||||
|
||||
// Filter for the created menuboard
|
||||
cy.get('#Filter input[name="name"]')
|
||||
.type('Cypress Test Menuboard ' + testRun);
|
||||
|
||||
// Wait for the grid reload
|
||||
cy.wait('@loadGridAfterSearch');
|
||||
cy.get('#menuBoards tbody tr').should('have.length', 1);
|
||||
|
||||
// Click on the first row element to open the delete modal
|
||||
cy.get('#menuBoards tr:first-child .dropdown-toggle').click({force: true});
|
||||
cy.get('#menuBoards tr:first-child .menuBoard_delete_button').click({force: true});
|
||||
|
||||
// Delete test menuboard
|
||||
cy.get('.bootbox .save-button').click();
|
||||
|
||||
// Check if menuboard is deleted in toast message
|
||||
cy.get('.toast').contains('Deleted Cypress Test Menuboard');
|
||||
});
|
||||
});
|
||||
|
||||
// -------------------
|
||||
it('should add categories and products to a menuboard', function() {
|
||||
// Create a new menuboard and then search for it and delete it
|
||||
cy.createMenuboard('Cypress Test Menuboard ' + testRun).then((menuId) => {
|
||||
cy.intercept({
|
||||
url: '/menuboard?*',
|
||||
query: {name: 'Cypress Test Menuboard ' + testRun},
|
||||
}).as('loadGridAfterSearch');
|
||||
|
||||
cy.visit('/menuboard/view');
|
||||
|
||||
// Filter for the created menuboard
|
||||
cy.get('#Filter input[name="name"]')
|
||||
.type('Cypress Test Menuboard ' + testRun);
|
||||
|
||||
// Wait for the grid reload
|
||||
cy.wait('@loadGridAfterSearch');
|
||||
cy.get('#menuBoards tbody tr').should('have.length', 1);
|
||||
|
||||
// Click on the first row element to open the delete modal
|
||||
cy.get('#menuBoards tr:first-child .dropdown-toggle').click({force: true});
|
||||
cy.get('#menuBoards tr:first-child .menuBoard_button_viewcategories').click({force: true});
|
||||
|
||||
// Click on the Add Category button
|
||||
cy.contains('Add Category').click();
|
||||
|
||||
cy.get('.modal input#name')
|
||||
.type('Cypress Test Category ' + testRun + '_1');
|
||||
cy.get('.modal input#code')
|
||||
.type('MENUBOARDCAT');
|
||||
|
||||
// Add first by clicking next
|
||||
cy.get('.modal .save-button').click();
|
||||
|
||||
// Check if menuboard is added in toast message
|
||||
cy.contains('Added Menu Board Category');
|
||||
|
||||
// Wait for the grid reload
|
||||
// cy.wait('@loadCategoryGridAfterSearch');
|
||||
|
||||
// Click on the first row element to open the delete modal
|
||||
cy.get('#menuBoardCategories tr:first-child .dropdown-toggle').click({force: true});
|
||||
cy.get('#menuBoardCategories tr:first-child .menuBoardCategory_button_viewproducts').click({force: true});
|
||||
|
||||
// Click on the Add Product button
|
||||
cy.contains('Add Product').click();
|
||||
|
||||
cy.get('.modal input#name')
|
||||
.type('Cypress Test Product ' + testRun + '_1');
|
||||
cy.get('.modal input#code')
|
||||
.type('MENUBOARDPROD');
|
||||
|
||||
// Add first by clicking next
|
||||
cy.get('.modal .save-button').click();
|
||||
|
||||
// Check if menuboard is added in toast message
|
||||
cy.contains('Added Menu Board Product');
|
||||
});
|
||||
});
|
||||
|
||||
// -------------------
|
||||
// Categories
|
||||
it('should add a category', function() {
|
||||
// Create a new menuboard and then search for it and delete it
|
||||
cy.createMenuboard('Cypress Test Menuboard ' + testRun).then((menuId) => {
|
||||
// GO to products page
|
||||
cy.visit('/menuboard/' + menuId + '/categories/view');
|
||||
// Click on the Add Category button
|
||||
cy.contains('Add Category').click();
|
||||
|
||||
cy.get('.modal input#name')
|
||||
.type('Cypress Test Category ' + testRun + '_1');
|
||||
cy.get('.modal input#code')
|
||||
.type('MENUBOARDCAT');
|
||||
|
||||
// Add first by clicking next
|
||||
cy.get('.modal .save-button').click();
|
||||
|
||||
// Check toast message
|
||||
cy.contains('Added Menu Board Category');
|
||||
|
||||
// Delete the menuboard and assert success
|
||||
cy.deleteMenuboard(menuId).then((response) => {
|
||||
expect(response.status).to.equal(204);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('searches and edit existing category', function() {
|
||||
// Create a new menuboard and then search for it and delete it
|
||||
cy.createMenuboard('Cypress Test Menuboard ' + testRun).then((menuId) => {
|
||||
cy.createMenuboardCat('Cypress Test Category ' + testRun, menuId).then((menuCatId) => {
|
||||
cy.intercept({
|
||||
url: '/menuboard/' + menuId + '/categories?*',
|
||||
query: {name: 'Cypress Test Category ' + testRun},
|
||||
}).as('loadGridAfterSearch');
|
||||
|
||||
// Intercept the PUT request
|
||||
cy.intercept({
|
||||
method: 'PUT',
|
||||
url: '/menuboard/' + menuCatId + '/category',
|
||||
}).as('putRequest');
|
||||
|
||||
// GO to products page
|
||||
cy.visit('/menuboard/' + menuId + '/categories/view');
|
||||
// Filter for the created menuboard
|
||||
cy.get('#Filter input[name="name"]')
|
||||
.type('Cypress Test Category ' + testRun);
|
||||
|
||||
// Wait for the grid reload
|
||||
cy.wait('@loadGridAfterSearch');
|
||||
cy.get('#menuBoardCategories tbody tr').should('have.length', 1);
|
||||
|
||||
// Click on the first row element to open the delete modal
|
||||
cy.get('#menuBoardCategories tr:first-child .dropdown-toggle').click({force: true});
|
||||
cy.get('#menuBoardCategories tr:first-child .menuBoardCategory_edit_button').click({force: true});
|
||||
|
||||
// EDIT
|
||||
cy.get('.modal input#name').clear()
|
||||
.type('Cypress Test Category Edited ' + testRun);
|
||||
|
||||
cy.get('.bootbox .save-button').click();
|
||||
|
||||
// Wait for the intercepted PUT request and check the form data
|
||||
cy.wait('@putRequest').then((interception) => {
|
||||
// Get the request body (form data)
|
||||
const response = interception.response;
|
||||
const responseData = response.body.data;
|
||||
|
||||
// assertion on the "menuboard" value
|
||||
expect(responseData.name).to.eq('Cypress Test Category Edited ' + testRun);
|
||||
});
|
||||
|
||||
// Delete the menuboard and assert success
|
||||
cy.deleteMenuboard(menuId).then((response) => {
|
||||
expect(response.status).to.equal(204);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('searches and delete existing category', function() {
|
||||
// Create a new menuboard and then search for it and delete it
|
||||
cy.createMenuboard('Cypress Test Menuboard ' + testRun).then((menuId) => {
|
||||
cy.createMenuboardCat('Cypress Test Category ' + testRun, menuId).then((menuCatId) => {
|
||||
cy.intercept({
|
||||
url: '/menuboard/' + menuId + '/categories?*',
|
||||
query: {name: 'Cypress Test Category ' + testRun},
|
||||
}).as('loadGridAfterSearch');
|
||||
|
||||
// Intercept the PUT request
|
||||
cy.intercept({
|
||||
method: 'PUT',
|
||||
url: '/menuboard/' + menuCatId + '/category',
|
||||
}).as('putRequest');
|
||||
|
||||
// GO to products page
|
||||
cy.visit('/menuboard/' + menuId + '/categories/view');
|
||||
// Filter for the created menuboard
|
||||
cy.get('#Filter input[name="name"]')
|
||||
.type('Cypress Test Category ' + testRun);
|
||||
|
||||
// Wait for the grid reload
|
||||
cy.wait('@loadGridAfterSearch');
|
||||
cy.get('#menuBoardCategories tbody tr').should('have.length', 1);
|
||||
|
||||
// Click on the first row element to open the delete modal
|
||||
cy.get('#menuBoardCategories tr:first-child .dropdown-toggle').click({force: true});
|
||||
cy.get('#menuBoardCategories tr:first-child .menuBoardCategory_delete_button').click({force: true});
|
||||
|
||||
// Delete test category
|
||||
cy.get('.bootbox .save-button').click();
|
||||
|
||||
// Check toast message
|
||||
cy.get('.toast').contains('Deleted Cypress Test Category');
|
||||
|
||||
// Delete the menuboard and assert success
|
||||
cy.deleteMenuboard(menuId).then((response) => {
|
||||
expect(response.status).to.equal(204);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// -------------------
|
||||
// Products
|
||||
it('should add a product', function() {
|
||||
// Create a new menuboard and then search for it and delete it
|
||||
cy.createMenuboard('Cypress Test Menuboard ' + testRun).then((menuId) => {
|
||||
cy.createMenuboardCat('Cypress Test Category ' + testRun, menuId).then((menuCatId) => {
|
||||
// GO to products page
|
||||
cy.visit('/menuboard/' + menuCatId + '/products/view');
|
||||
// Click on the Add Product button
|
||||
cy.contains('Add Product').click();
|
||||
|
||||
cy.get('.modal input#name')
|
||||
.type('Cypress Test Product ' + testRun);
|
||||
cy.get('.modal input#code')
|
||||
.type('MENUBOARDPROD');
|
||||
|
||||
// Add first by clicking next
|
||||
cy.get('.modal .save-button').click();
|
||||
|
||||
// Check if menuboard is added in toast message
|
||||
cy.contains('Added Menu Board Product');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('searches and edit existing product', function() {
|
||||
// Create a new menuboard and then search for it and delete it
|
||||
cy.createMenuboard('Cypress Test Menuboard ' + testRun).then((menuId) => {
|
||||
cy.log(menuId);
|
||||
cy.createMenuboardCat('Cypress Test Category ' + testRun, menuId).then((menuCatId) => {
|
||||
cy.log(menuCatId);
|
||||
cy.createMenuboardCatProd('Cypress Test Product ' + testRun, menuCatId).then((menuProdId) => {
|
||||
cy.log(menuProdId);
|
||||
cy.intercept({
|
||||
url: '/menuboard/' + menuCatId + '/products?*',
|
||||
query: {name: 'Cypress Test Product ' + testRun},
|
||||
}).as('loadGridAfterSearch');
|
||||
|
||||
// Intercept the PUT request
|
||||
cy.intercept({
|
||||
method: 'PUT',
|
||||
url: '/menuboard/' + menuProdId + '/product',
|
||||
}).as('putRequest');
|
||||
|
||||
// GO to products page
|
||||
cy.visit('/menuboard/' + menuCatId + '/products/view');
|
||||
// Filter for the created menuboard
|
||||
cy.get('#Filter input[name="name"]')
|
||||
.type('Cypress Test Product ' + testRun);
|
||||
|
||||
// Wait for the grid reload
|
||||
cy.wait('@loadGridAfterSearch');
|
||||
cy.get('#menuBoardProducts tbody tr').should('have.length', 1);
|
||||
|
||||
// Click on the first row element to open the delete modal
|
||||
cy.get('#menuBoardProducts tr:first-child .dropdown-toggle').click({force: true});
|
||||
cy.get('#menuBoardProducts tr:first-child .menuBoardProduct_edit_button').click({force: true});
|
||||
|
||||
// EDIT
|
||||
cy.get('.modal input#name').clear()
|
||||
.type('Cypress Test Product Edited ' + testRun);
|
||||
|
||||
cy.get('.bootbox .save-button').click();
|
||||
|
||||
// Wait for the intercepted PUT request and check the form data
|
||||
cy.wait('@putRequest').then((interception) => {
|
||||
// Get the request body (form data)
|
||||
const response = interception.response;
|
||||
const responseData = response.body.data;
|
||||
|
||||
// assertion on the "menuboard" value
|
||||
expect(responseData.name).to.eq('Cypress Test Product Edited ' + testRun);
|
||||
});
|
||||
|
||||
// Delete the menuboard and assert success
|
||||
cy.deleteMenuboard(menuId).then((response) => {
|
||||
expect(response.status).to.equal(204);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('searches and delete existing product', function() {
|
||||
// Create a new menuboard and then search for it and delete it
|
||||
cy.createMenuboard('Cypress Test Menuboard ' + testRun).then((menuId) => {
|
||||
cy.createMenuboardCat('Cypress Test Category ' + testRun, menuId).then((menuCatId) => {
|
||||
cy.createMenuboardCatProd('Cypress Test Product ' + testRun, menuCatId).then((menuProdId) => {
|
||||
cy.intercept({
|
||||
url: '/menuboard/' + menuCatId + '/products?*',
|
||||
query: {name: 'Cypress Test Product ' + testRun},
|
||||
}).as('loadGridAfterSearch');
|
||||
|
||||
// Intercept the PUT request
|
||||
cy.intercept({
|
||||
method: 'PUT',
|
||||
url: '/menuboard/' + menuProdId + '/product',
|
||||
}).as('putRequest');
|
||||
|
||||
// GO to products page
|
||||
cy.visit('/menuboard/' + menuCatId + '/products/view');
|
||||
// Filter for the created menuboard
|
||||
cy.get('#Filter input[name="name"]')
|
||||
.type('Cypress Test Product ' + testRun);
|
||||
|
||||
// Wait for the grid reload
|
||||
cy.wait('@loadGridAfterSearch');
|
||||
cy.get('#menuBoardProducts tbody tr').should('have.length', 1);
|
||||
|
||||
// Click on the first row element to open the delete modal
|
||||
cy.get('#menuBoardProducts tr:first-child .dropdown-toggle').click({force: true});
|
||||
cy.get('#menuBoardProducts tr:first-child .menuBoardProduct_delete_button').click({force: true});
|
||||
|
||||
// Delete test menuboard
|
||||
cy.get('.bootbox .save-button').click();
|
||||
|
||||
// Check toast message
|
||||
cy.get('.toast').contains('Deleted Cypress Test Product');
|
||||
|
||||
// Delete the menuboard and assert success
|
||||
cy.deleteMenuboard(menuId).then((response) => {
|
||||
expect(response.status).to.equal(204);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
41
cypress/e2e/Library/playlist_editor_empty.cy.js
Normal file
41
cypress/e2e/Library/playlist_editor_empty.cy.js
Normal file
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Xibo Signage Ltd
|
||||
*
|
||||
* Xibo - Digital Signage - https://xibosignage.com
|
||||
*
|
||||
* This file is part of Xibo.
|
||||
*
|
||||
* Xibo is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* Xibo is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
describe('Playlist Editor (Empty)', function() {
|
||||
|
||||
beforeEach(function() {
|
||||
cy.login();
|
||||
|
||||
// Create random name
|
||||
let uuid = Cypress._.random(0, 1e9);
|
||||
|
||||
// Create a new layout and go to the layout's designer page
|
||||
cy.createNonDynamicPlaylist(uuid).as('testPlaylistId').then((res) => {
|
||||
cy.openPlaylistEditorAndLoadPrefs(res);
|
||||
});
|
||||
});
|
||||
|
||||
it('should show the droppable zone and toolbar', function() {
|
||||
|
||||
cy.get('#playlist-editor-container').should('be.visible');
|
||||
cy.get('div[class="container-toolbar container-fluid flex-column flex-column justify-content-between"]').should('be.visible');
|
||||
});
|
||||
});
|
||||
208
cypress/e2e/Library/playlist_editor_populated.cy.js
Normal file
208
cypress/e2e/Library/playlist_editor_populated.cy.js
Normal file
@@ -0,0 +1,208 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Xibo Signage Ltd
|
||||
*
|
||||
* Xibo - Digital Signage - https://xibosignage.com
|
||||
*
|
||||
* This file is part of Xibo.
|
||||
*
|
||||
* Xibo is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* Xibo is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
describe('Playlist Editor (Populated)', function() {
|
||||
|
||||
beforeEach(function() {
|
||||
cy.login();
|
||||
|
||||
// Create random name
|
||||
let uuid = Cypress._.random(0, 1e9);
|
||||
|
||||
// Create a new layout and go to the layout's designer page
|
||||
cy.createNonDynamicPlaylist(uuid).as('testPlaylistId').then((res) => {
|
||||
|
||||
// Populate playlist with some widgets and media
|
||||
cy.addWidgetToPlaylist(res, 'embedded', {
|
||||
name: 'Embedded Widget'
|
||||
});
|
||||
|
||||
cy.addMediaToLibrary("file/example.zip");
|
||||
|
||||
cy.addWidgetToPlaylist(res, 'clock', {
|
||||
name: 'Clock Widget'
|
||||
});
|
||||
|
||||
cy.openPlaylistEditorAndLoadPrefs(res);
|
||||
});
|
||||
});
|
||||
|
||||
it('changes and saves widget properties', () => {
|
||||
// Create and alias for reload widget
|
||||
// cy.intercept('GET','/playlist/widget/form/edit/*').as('reloadWidget');
|
||||
|
||||
// Select the first widget on timeline ( image )
|
||||
cy.get('#timeline-container [data-type="widget"]').first().click();
|
||||
|
||||
// Wait for the widget to load
|
||||
// cy.wait('@reloadWidget');
|
||||
|
||||
// Type the new name in the input
|
||||
cy.get('a[href="#advancedTab"]').click();
|
||||
cy.get('#properties-panel-form-container input[name="name"]').clear().type('newName');
|
||||
|
||||
// Set a duration
|
||||
cy.get('#properties-panel-form-container input[name="useDuration"]').check();
|
||||
cy.get('#properties-panel-form-container input[name="duration"]').clear().type(12);
|
||||
|
||||
// Save form
|
||||
cy.get('#properties-panel-form-container button[data-action="save"]').click();
|
||||
|
||||
// Should show a notification for the name change
|
||||
// cy.get('.toast-success');
|
||||
|
||||
// Wait for the widget to reload
|
||||
// cy.wait('@reloadWidget');
|
||||
|
||||
// Check if the values are the same entered after reload
|
||||
cy.get('#properties-panel-form-container input[name="name"]').should('have.prop', 'value').and('equal', 'newName');
|
||||
cy.get('#properties-panel-form-container input[name="duration"]').should('have.prop', 'value').and('equal', '12');
|
||||
|
||||
});
|
||||
|
||||
it.skip('should revert a saved form to a previous state', () => {
|
||||
|
||||
let oldName;
|
||||
|
||||
// Create and alias for reload widget
|
||||
// cy.intercept('GET', '/playlist/widget/form/edit/*').as('reloadWidget');
|
||||
// cy.intercept('PUT', '/playlist/widget/*').as('saveWidget');
|
||||
|
||||
// Select the first widget on timeline ( image )
|
||||
cy.get('#timeline-container [data-type="widget"]').first().click();
|
||||
|
||||
// Wait for the widget to load
|
||||
// cy.wait('@reloadWidget');
|
||||
|
||||
// Get the input field
|
||||
cy.get('a[href="#advancedTab"]').click();
|
||||
cy.get('#properties-panel-form-container input[name="name"]').then(($input) => {
|
||||
|
||||
// Save old name
|
||||
oldName = $input.val();
|
||||
|
||||
//Type the new name in the input
|
||||
cy.get('#properties-panel-form-container input[name="name"]').clear().type('newName');
|
||||
|
||||
// Save form
|
||||
cy.get('#properties-panel-form-container button[data-action="save"]').click();
|
||||
|
||||
// Should show a notification for the name change
|
||||
// cy.get('.toast-success');
|
||||
|
||||
// Wait for the widget to save
|
||||
// cy.wait('@reloadWidget');
|
||||
|
||||
// Click the revert button
|
||||
cy.get('#playlist-editor-toolbar #undoContainer').click();
|
||||
|
||||
// Wait for the widget to save
|
||||
// cy.wait('@saveWidget');
|
||||
|
||||
// Test if the revert made the name go back to the old name
|
||||
cy.get('#properties-panel-form-container input[name="name"]').should('have.prop', 'value').and('equal', oldName);
|
||||
});
|
||||
});
|
||||
|
||||
it('should delete a widget using the toolbar bin', () => {
|
||||
// cy.intercept('/playlist?playlistId=*').as('reloadPlaylist');
|
||||
|
||||
// Select a widget from the navigator
|
||||
cy.get('#playlist-timeline [data-type="widget"]').first().click().then(($el) => {
|
||||
|
||||
const widgetId = $el.attr('id');
|
||||
|
||||
// Click trash container
|
||||
cy.get('div[class="widgetDelete"]').first().click({force: true});
|
||||
|
||||
// Confirm delete on modal
|
||||
cy.get('button[class*="btn-bb-confirm"]').click();
|
||||
|
||||
// Check toast message
|
||||
// cy.get('.toast-success').contains('Deleted');
|
||||
|
||||
// Wait for the layout to reload
|
||||
// cy.wait('@reloadPlaylist');
|
||||
|
||||
// Check that widget is not on timeline
|
||||
cy.get('#playlist-timeline [data-type="widget"]#' + widgetId).should('not.exist');
|
||||
});
|
||||
});
|
||||
|
||||
it.skip('should add an audio clip to a widget by the context menu, and adds a link to open the form in the timeline', () => {
|
||||
|
||||
cy.populateLibraryWithMedia();
|
||||
|
||||
// Create and alias for reload playlist
|
||||
cy.intercept('/playlist?playlistId=*').as('reloadPlaylist');
|
||||
|
||||
// Right click to open the context menu and select add audio
|
||||
cy.get('#timeline-container [data-type="widget"]').first().should('be.visible').rightclick();
|
||||
cy.get('.context-menu-btn[data-property="Audio"]').should('be.visible').click();
|
||||
|
||||
// Select the 1st option
|
||||
cy.get('[data-test="widgetPropertiesForm"] #mediaId > option').eq(1).then(($el) => {
|
||||
cy.get('[data-test="widgetPropertiesForm"] #mediaId').select($el.val());
|
||||
});
|
||||
|
||||
// Save and close the form
|
||||
cy.get('[data-test="widgetPropertiesForm"] .btn-bb-done').click();
|
||||
|
||||
// Check if the widget has the audio icon
|
||||
// cy.wait('@reloadPlaylist');
|
||||
cy.get('#timeline-container [data-type="widget"]:first-child')
|
||||
.find('i[data-property="Audio"]').should('exist').click({force: true});
|
||||
|
||||
cy.get('[data-test="widgetPropertiesForm"]').contains('Audio for');
|
||||
});
|
||||
|
||||
// Skip test for now ( it's failing in the test suite and being tested already in layout designer spec )
|
||||
it.skip('attaches expiry dates to a widget by the context menu, and adds a link to open the form in the timeline', () => {
|
||||
// Create and alias for reload playlist
|
||||
// cy.intercept('/playlist?playlistId=*').as('reloadPlaylist');
|
||||
|
||||
// Right click to open the context menu and select add audio
|
||||
cy.get('#timeline-container [data-type="widget"]').first().should('be.visible').rightclick();
|
||||
cy.get('.context-menu-btn[data-property="Expiry"]').should('be.visible').click();
|
||||
|
||||
// Add dates
|
||||
cy.get('[data-test="widgetPropertiesForm"] .starttime-control .date-clear-button').click();
|
||||
// cy.get('[data-test="widgetPropertiesForm"] #fromDt').find('input[class="datePickerHelper form-control dateControl dateTime active"]').click();
|
||||
cy.get('div[class="flatpickr-wrapper"]').first().click();
|
||||
cy.get('.flatpickr-calendar.open .dayContainer .flatpickr-day:first').click();
|
||||
|
||||
cy.get('[data-test="widgetPropertiesForm"] .endtime-control .date-clear-button').click();
|
||||
// cy.get('[data-test="widgetPropertiesForm"] #toDt').find('input[class="datePickerHelper form-control dateControl dateTime active"]').click();
|
||||
cy.get('div[class="flatpickr-wrapper"]').last().click();
|
||||
cy.get('.flatpickr-calendar.open .dayContainer .flatpickr-day:first').click();
|
||||
|
||||
|
||||
// Save and close the form
|
||||
cy.get('[data-test="widgetPropertiesForm"] .btn-bb-done').click();
|
||||
|
||||
// Check if the widget has the expiry dates icon
|
||||
// cy.wait('@reloadPlaylist');
|
||||
cy.get('#timeline-container [data-type="widget"]:first-child')
|
||||
.find('i[data-property="Expiry"]').should('exist').click({force: true});
|
||||
|
||||
cy.get('[data-test="widgetPropertiesForm"]').contains('Expiry for');
|
||||
});
|
||||
});
|
||||
110
cypress/e2e/Library/playlist_editor_populated_unchanged.cy.js
Normal file
110
cypress/e2e/Library/playlist_editor_populated_unchanged.cy.js
Normal file
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Xibo Signage Ltd
|
||||
*
|
||||
* Xibo - Digital Signage - https://xibosignage.com
|
||||
*
|
||||
* This file is part of Xibo.
|
||||
*
|
||||
* Xibo is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* Xibo is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
describe('Playlist Editor (Populated/Unchanged)', function() {
|
||||
|
||||
before(function() {
|
||||
cy.login();
|
||||
|
||||
// Create random name
|
||||
let uuid = Cypress._.random(0, 1e9);
|
||||
|
||||
// Create a new layout and go to the layout's designer page
|
||||
cy.createNonDynamicPlaylist(uuid).as('testPlaylistId').then((res) => {
|
||||
|
||||
// Populate playlist with some widgets and media
|
||||
cy.addWidgetToPlaylist(res, 'embedded', {
|
||||
name: 'Embedded Widget'
|
||||
});
|
||||
|
||||
// TODO skip so that the test success
|
||||
// cy.addRandomMediaToPlaylist(res);
|
||||
|
||||
cy.addWidgetToPlaylist(res, 'clock', {
|
||||
name: 'Clock Widget'
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
beforeEach(function() {
|
||||
cy.login();
|
||||
cy.openPlaylistEditorAndLoadPrefs(this.testPlaylistId);
|
||||
});
|
||||
|
||||
it('opens a media tab in the toolbar and searches for items', () => {
|
||||
|
||||
// cy.intercept('/library/search?*').as('mediaLoad');
|
||||
|
||||
cy.populateLibraryWithMedia();
|
||||
|
||||
// Open audio tool tab
|
||||
cy.get('a[id="btn-menu-3"]').should('be.visible').click();
|
||||
|
||||
// cy.wait('@mediaLoad');
|
||||
|
||||
// Check if there are audio items in the search content
|
||||
cy.get('div[class="toolbar-card-preview"]').last().should('be.visible');
|
||||
});
|
||||
|
||||
it('creates a new widget by selecting a searched media from the toolbar to the editor, and then reverts the change', () => {
|
||||
cy.populateLibraryWithMedia();
|
||||
|
||||
// Create and alias for reload playlist
|
||||
// cy.intercept('/playlist?playlistId=*').as('reloadPlaylist');
|
||||
// cy.intercept('DELETE', '/playlist/widget/*').as('deleteWidget');
|
||||
// cy.intercept('/library/search?*').as('mediaLoad');
|
||||
|
||||
// Open library search tab
|
||||
cy.get('a[id="btn-menu-0"]').should('be.visible').click();
|
||||
|
||||
// cy.wait('@mediaLoad');
|
||||
cy.wait(1000);
|
||||
|
||||
// Get a table row, select it and add to the dropzone
|
||||
cy.get('div[class="toolbar-card ui-draggable ui-draggable-handle"]').eq(2).should('be.visible').click({force: true}).then(() => {
|
||||
cy.get('#timeline-overlay-container').click({force: true}).then(() => {
|
||||
|
||||
// Wait for the layout to reload
|
||||
// cy.wait('@reloadPlaylist');
|
||||
cy.wait(3000);
|
||||
|
||||
// Check if there is just one widget in the timeline
|
||||
cy.get('#timeline-container [data-type="widget"]').then(($widgets) => {
|
||||
expect($widgets.length).to.eq(3);
|
||||
});
|
||||
|
||||
// Click the revert button
|
||||
cy.get('#timeline-container [id^="widget_"]').last().click();
|
||||
cy.get('button[data-action="undo"]').click({force: true});
|
||||
|
||||
// Wait for the widget to be deleted and for the playlist to reload
|
||||
// cy.wait('@deleteWidget');
|
||||
// cy.wait('@reloadPlaylist');
|
||||
cy.wait(3000);
|
||||
|
||||
// Check if there is just one widget in the timeline
|
||||
cy.get('#timeline-container [data-type="widget"]').then(($widgets) => {
|
||||
expect($widgets.length).to.eq(2);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
107
cypress/e2e/Library/playlists.cy.js
Normal file
107
cypress/e2e/Library/playlists.cy.js
Normal file
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Xibo Signage Ltd
|
||||
*
|
||||
* Xibo - Digital Signage - https://xibosignage.com
|
||||
*
|
||||
* This file is part of Xibo.
|
||||
*
|
||||
* Xibo is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* Xibo is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* eslint-disable max-len */
|
||||
describe('Playlists Admin', function() {
|
||||
let testRun;
|
||||
|
||||
beforeEach(function() {
|
||||
cy.login();
|
||||
|
||||
testRun = Cypress._.random(0, 1e9);
|
||||
});
|
||||
|
||||
it('should add a non-dynamic playlist', function() {
|
||||
cy.visit('/playlist/view');
|
||||
|
||||
// Click on the Add Playlist button
|
||||
cy.contains('Add Playlist').click();
|
||||
|
||||
cy.get('.modal input#name')
|
||||
.type('Cypress Test Playlist ' + testRun);
|
||||
|
||||
cy.get('.modal .save-button').click();
|
||||
|
||||
// Filter for the created playlist
|
||||
cy.get('#Filter input[name="name"]')
|
||||
.type('Cypress Test Playlist ' + testRun);
|
||||
|
||||
// Should have the added playlist
|
||||
cy.get('#playlists tbody tr').should('have.length', 1);
|
||||
cy.get('#playlists tbody tr:nth-child(1) td:nth-child(2)').contains('Cypress Test Playlist ' + testRun);
|
||||
});
|
||||
|
||||
it('should cancel adding a non-dynamic playlist', function() {
|
||||
cy.visit('/playlist/view');
|
||||
|
||||
// Click on the Add Playlist button
|
||||
cy.contains('Add Playlist').click();
|
||||
|
||||
cy.get('.modal input#name')
|
||||
.type('Cypress Test Playlist ' + testRun);
|
||||
|
||||
// Click cancel
|
||||
cy.get('#dialog_btn_1').click();
|
||||
|
||||
// Check if you are back to the view page
|
||||
cy.url().should('include', '/playlist/view');
|
||||
});
|
||||
|
||||
it('should show a list of Playlists', function() {
|
||||
// Wait for playlist grid reload
|
||||
cy.intercept('/playlist?draw=1&*').as('playlistGridLoad');
|
||||
|
||||
cy.visit('/playlist/view').then(function() {
|
||||
cy.wait('@playlistGridLoad');
|
||||
cy.get('#playlists');
|
||||
});
|
||||
});
|
||||
|
||||
it('selects multiple playlists and delete them', function() {
|
||||
// Create a new playlist and then search for it and delete it
|
||||
cy.createNonDynamicPlaylist('Cypress Test Playlist ' + testRun).then(() => {
|
||||
cy.intercept('/playlist?draw=2&*').as('playlistGridLoad');
|
||||
|
||||
// Delete all test playlists
|
||||
cy.visit('/playlist/view');
|
||||
|
||||
// Clear filter and search for text playlists
|
||||
cy.get('#Filter input[name="name"]')
|
||||
.clear()
|
||||
.type('Cypress Test Playlist');
|
||||
|
||||
// Wait for 2nd playlist grid reload
|
||||
cy.wait('@playlistGridLoad');
|
||||
|
||||
// Select all
|
||||
cy.get('button[data-toggle="selectAll"]').click();
|
||||
|
||||
// Delete all
|
||||
cy.get('.dataTables_info button[data-toggle="dropdown"]').click({force: true});
|
||||
cy.get('.dataTables_info a[data-button-id="playlist_button_delete"]').click({force: true});
|
||||
|
||||
cy.get('button.save-button').click();
|
||||
|
||||
// Modal should contain one successful delete at least
|
||||
cy.get('.modal-body').contains(': Success');
|
||||
});
|
||||
});
|
||||
});
|
||||
61
cypress/e2e/Reporting/report_bandwidth.cy.js
Normal file
61
cypress/e2e/Reporting/report_bandwidth.cy.js
Normal file
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Xibo Signage Ltd
|
||||
*
|
||||
* Xibo - Digital Signage - https://xibosignage.com
|
||||
*
|
||||
* This file is part of Xibo.
|
||||
*
|
||||
* Xibo is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* Xibo is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* eslint-disable max-len */
|
||||
describe('Bandwidth', function() {
|
||||
const display1 = 'POP Display 1';
|
||||
|
||||
beforeEach(function() {
|
||||
cy.login();
|
||||
});
|
||||
|
||||
it('should load tabular data and charts', () => {
|
||||
// Create and alias for load Display
|
||||
cy.intercept({
|
||||
url: '/display?start=*',
|
||||
query: {display: display1},
|
||||
}).as('loadDisplayAfterSearch');
|
||||
|
||||
cy.intercept('/report/data/bandwidth?*').as('reportData');
|
||||
|
||||
cy.visit('/report/form/bandwidth');
|
||||
|
||||
// Click on the select2 selection
|
||||
cy.get('#displayId + span .select2-selection').click();
|
||||
cy.get('.select2-container--open input[type="search"]').type(display1);
|
||||
cy.wait('@loadDisplayAfterSearch');
|
||||
cy.selectOption(display1);
|
||||
|
||||
// Click on the Apply button
|
||||
cy.contains('Apply').should('be.visible').click();
|
||||
|
||||
cy.get('.chart-container').should('be.visible');
|
||||
|
||||
// Click on Tabular
|
||||
cy.contains('Tabular').should('be.visible').click();
|
||||
cy.wait('@reportData');
|
||||
|
||||
// Should have media stats
|
||||
cy.get('#bandwidthTbl tbody tr:nth-child(1) td:nth-child(1)').contains('Submit Stats');
|
||||
cy.get('#bandwidthTbl tbody tr:nth-child(1) td:nth-child(2)').contains(200); // Bandwidth
|
||||
cy.get('#bandwidthTbl tbody tr:nth-child(1) td:nth-child(3)').contains('bytes'); // Unit
|
||||
});
|
||||
});
|
||||
130
cypress/e2e/Reporting/report_distribution.cy.js
Normal file
130
cypress/e2e/Reporting/report_distribution.cy.js
Normal file
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Xibo Signage Ltd
|
||||
*
|
||||
* Xibo - Digital Signage - https://xibosignage.com
|
||||
*
|
||||
* This file is part of Xibo.
|
||||
*
|
||||
* Xibo is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* Xibo is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* eslint-disable max-len */
|
||||
describe('Distribution by Layout, Media or Event', function() {
|
||||
const display1 = 'POP Display 1';
|
||||
const layout1 = 'POP Layout 1';
|
||||
|
||||
beforeEach(function() {
|
||||
cy.login();
|
||||
});
|
||||
|
||||
it('Range: Today, Checks duration and count of a layout stat', () => {
|
||||
// Create and alias for load layout
|
||||
cy.intercept({
|
||||
url: '/display?start=*',
|
||||
query: {display: display1},
|
||||
}).as('loadDisplayAfterSearch');
|
||||
|
||||
cy.intercept({
|
||||
url: '/layout?start=*',
|
||||
query: {layout: layout1},
|
||||
}).as('loadLayoutAfterSearch');
|
||||
|
||||
cy.intercept('/report/data/distributionReport?*').as('reportData');
|
||||
|
||||
cy.visit('/report/form/distributionReport');
|
||||
|
||||
// Click on the select2 selection
|
||||
cy.get('#displayId + span .select2-selection').click();
|
||||
cy.get('.select2-container--open input[type="search"]').type(display1);
|
||||
cy.wait('@loadDisplayAfterSearch');
|
||||
cy.selectOption(display1);
|
||||
|
||||
// Click on the select2 selection
|
||||
cy.get('#layoutId + span .select2-selection').click();
|
||||
cy.get('.select2-container--open input[type="search"]').type(layout1);
|
||||
cy.wait('@loadLayoutAfterSearch');
|
||||
cy.selectOption(layout1);
|
||||
|
||||
// Click on the Apply button
|
||||
cy.contains('Apply').should('be.visible').click();
|
||||
|
||||
cy.get('.chart-container').should('be.visible');
|
||||
|
||||
// Click on Tabular
|
||||
cy.contains('Tabular').should('be.visible').click();
|
||||
cy.contains('Next').should('be.visible').click();
|
||||
cy.wait('@reportData');
|
||||
|
||||
// Should have media stats
|
||||
cy.get('#distributionTbl tbody tr:nth-child(3) td:nth-child(1)').contains('12:00 PM'); // Period
|
||||
cy.get('#distributionTbl tbody tr:nth-child(3) td:nth-child(2)').contains(60); // Duration
|
||||
cy.get('#distributionTbl tbody tr:nth-child(3) td:nth-child(3)').contains(1); // Count
|
||||
});
|
||||
|
||||
it.skip('Create/Delete a Daily Distribution Report Schedule', () => {
|
||||
const reportschedule = 'Daily Distribution by Layout 1 and Display 1';
|
||||
|
||||
// Create and alias for load layout
|
||||
cy.intercept({
|
||||
url: '/display?start=*',
|
||||
query: {display: display1},
|
||||
}).as('loadDisplayAfterSearch');
|
||||
|
||||
cy.intercept({
|
||||
url: '/layout?start=*',
|
||||
query: {layout: layout1},
|
||||
}).as('loadLayoutAfterSearch');
|
||||
|
||||
cy.intercept({
|
||||
url: '/report/reportschedule?*',
|
||||
query: {name: reportschedule},
|
||||
}).as('loadReportScheduleAfterSearch');
|
||||
|
||||
cy.visit('/report/form/distributionReport');
|
||||
|
||||
// Click on the select2 selection
|
||||
cy.get('#layoutId + span .select2-selection').click();
|
||||
cy.get('.select2-container--open input[type="search"]').type(layout1);
|
||||
cy.wait('@loadLayoutAfterSearch');
|
||||
cy.selectOption(layout1);
|
||||
|
||||
// ------
|
||||
// ------
|
||||
// Create a Daily Distribution Report Schedule
|
||||
cy.get('#reportAddBtn').click();
|
||||
cy.get('#reportScheduleAddForm #name ').type(reportschedule);
|
||||
|
||||
// Click on the select2 selection
|
||||
cy.get('#reportScheduleAddForm #displayId + span .select2-selection').click();
|
||||
cy.get('.select2-container--open input[type="search"]').type(display1);
|
||||
cy.wait('@loadDisplayAfterSearch');
|
||||
cy.selectOption(display1);
|
||||
|
||||
cy.get('#dialog_btn_2').should('be.visible').click();
|
||||
|
||||
cy.visit('/report/reportschedule/view');
|
||||
cy.get('#name').type(reportschedule);
|
||||
cy.wait('@loadReportScheduleAfterSearch');
|
||||
|
||||
// Click on the first row element to open the designer
|
||||
cy.get('#reportschedules_wrapper tr:first-child .dropdown-toggle').click({force: true});
|
||||
cy.get('#reportschedules_wrapper tr:first-child .reportschedule_button_delete').click({force: true});
|
||||
|
||||
// Delete test campaign
|
||||
cy.get('.bootbox .save-button').click();
|
||||
|
||||
// Check if layout is deleted in toast message
|
||||
cy.get('.toast').contains('Deleted ' + reportschedule);
|
||||
});
|
||||
});
|
||||
35
cypress/e2e/Reporting/report_library_usage.cy.js
Normal file
35
cypress/e2e/Reporting/report_library_usage.cy.js
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Xibo Signage Ltd
|
||||
*
|
||||
* Xibo - Digital Signage - https://xibosignage.com
|
||||
*
|
||||
* This file is part of Xibo.
|
||||
*
|
||||
* Xibo is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* Xibo is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* eslint-disable max-len */
|
||||
describe('Library Usage', function() {
|
||||
beforeEach(function() {
|
||||
cy.login();
|
||||
});
|
||||
|
||||
it('should load tabular data and charts', () => {
|
||||
cy.visit('/report/form/libraryusage');
|
||||
|
||||
cy.get('#libraryUsage_wrapper').should('be.visible');
|
||||
cy.get('#libraryChart').should('be.visible');
|
||||
cy.get('#userChart').should('be.visible');
|
||||
});
|
||||
});
|
||||
183
cypress/e2e/Reporting/report_proofofplay.cy.js
Normal file
183
cypress/e2e/Reporting/report_proofofplay.cy.js
Normal file
@@ -0,0 +1,183 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Xibo Signage Ltd
|
||||
*
|
||||
* Xibo - Digital Signage - https://xibosignage.com
|
||||
*
|
||||
* This file is part of Xibo.
|
||||
*
|
||||
* Xibo is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* Xibo is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* eslint-disable max-len */
|
||||
describe('Proof of Play', function() {
|
||||
const display1 = 'POP Display 1';
|
||||
beforeEach(function() {
|
||||
cy.login();
|
||||
});
|
||||
|
||||
it('Range: Test export', function() {
|
||||
// Create and alias for load displays
|
||||
cy.intercept({
|
||||
url: '/display?start=*',
|
||||
query: {display: display1},
|
||||
}).as('loadDisplayAfterSearch');
|
||||
|
||||
cy.visit('/report/view');
|
||||
cy.contains('Export').click();
|
||||
|
||||
cy.get(':nth-child(1) > .col-sm-10 > .input-group > .flatpickr-wrapper > .datePickerHelper').click();
|
||||
cy.get('.open > .flatpickr-innerContainer > .flatpickr-rContainer > .flatpickr-days > .dayContainer > .today').click();
|
||||
cy.get(':nth-child(2) > .col-sm-10 > .input-group > .flatpickr-wrapper > .datePickerHelper').click();
|
||||
cy.get('.open > .flatpickr-innerContainer > .flatpickr-rContainer > .flatpickr-days > .dayContainer > .today').next().click();
|
||||
|
||||
// Click on the select2 selection
|
||||
cy.get('#displayId + span .select2-selection').click();
|
||||
cy.get('.select2-container--open input[type="search"]').type(display1);
|
||||
cy.wait('@loadDisplayAfterSearch');
|
||||
cy.selectOption(display1);
|
||||
|
||||
cy.get('.total-stat').contains('Total number of records to be exported 5');
|
||||
});
|
||||
|
||||
it('Range: Today - Test layout/media stats for a layout and a display', function() {
|
||||
cy.intercept('/report/data/proofofplayReport?*').as('reportData');
|
||||
|
||||
cy.visit('/report/form/proofofplayReport');
|
||||
|
||||
cy.get('#type').select('media');
|
||||
|
||||
// Click on the Apply button
|
||||
cy.contains('Apply').click();
|
||||
|
||||
// Wait for
|
||||
cy.wait('@reportData');
|
||||
cy.get('#stats tbody').contains('media');
|
||||
|
||||
// Should have media stats - Test media stats for a layout and a display
|
||||
cy.get('#stats tbody tr:nth-child(1) td:nth-child(1)').contains('media'); // stat type
|
||||
cy.get('#stats tbody tr:nth-child(1) td:nth-child(3)').contains('POP Display 1'); // display
|
||||
cy.get('#stats tbody tr:nth-child(1) td:nth-child(6)').contains('POP Layout 1'); // layout
|
||||
cy.get('#stats tbody tr:nth-child(1) td:nth-child(8)').contains('child_folder_media'); // media
|
||||
cy.get('#stats tbody tr:nth-child(1) td:nth-child(10)').contains(2); // number of plays
|
||||
cy.get('#stats tbody tr:nth-child(1) td:nth-child(12)').contains(120); // total duration
|
||||
|
||||
|
||||
cy.get('#type').select('layout');
|
||||
|
||||
// Click on the Apply button
|
||||
cy.contains('Apply').click();
|
||||
|
||||
// Wait for
|
||||
cy.wait('@reportData');
|
||||
cy.contains('Tabular').should('be.visible').click();
|
||||
|
||||
cy.get('#stats tbody').contains('layout');
|
||||
|
||||
// Should have layout stat - Test a layout stat for an ad campaign, a layout and a display
|
||||
cy.get('#stats tbody tr:nth-child(1) td:nth-child(1)').contains('layout'); // stat type
|
||||
cy.get('#stats tbody tr:nth-child(1) td:nth-child(3)').contains('POP Display 1'); // display
|
||||
cy.get('#stats tbody tr:nth-child(1) td:nth-child(4)').contains('POP Ad Campaign 1'); // ad campaign
|
||||
cy.get('#stats tbody tr:nth-child(1) td:nth-child(6)').contains('POP Layout 1'); // layout
|
||||
cy.get('#stats tbody tr:nth-child(1) td:nth-child(10)').contains(1); // number of plays
|
||||
cy.get('#stats tbody tr:nth-child(1) td:nth-child(12)').contains(60); // total duration
|
||||
});
|
||||
|
||||
it('Range: Lastweek - Test media stats for a layout and a display', function() {
|
||||
cy.intercept('/report/data/proofofplayReport?*').as('reportData');
|
||||
|
||||
cy.visit('/report/form/proofofplayReport');
|
||||
|
||||
// Range: Lastweek
|
||||
cy.get('#reportFilter').select('lastweek');
|
||||
|
||||
cy.get('#type').select('media');
|
||||
|
||||
// Click on the Apply button
|
||||
cy.contains('Apply').click();
|
||||
|
||||
// Wait for
|
||||
cy.wait('@reportData');
|
||||
|
||||
cy.get('#stats').within(() => {
|
||||
// Check if the "No data available in table" message is not present
|
||||
cy.contains('No data available in table').should('not.exist');
|
||||
cy.get('tbody tr').should('have.length', 1);
|
||||
// Should have media stats
|
||||
cy.get('tbody td').eq(0).should('contain', 'media'); // stat type
|
||||
cy.get('tbody td').eq(2).contains('POP Display 1'); // display
|
||||
cy.get('tbody td').eq(5).contains('POP Layout 1'); // layout
|
||||
cy.get('tbody td').eq(7).contains('child_folder_media'); // media
|
||||
cy.get('tbody td').eq(9).contains(2); // number of plays
|
||||
cy.get('tbody td').eq(11).contains(120); // total duration
|
||||
});
|
||||
|
||||
cy.get('#type').select('layout');
|
||||
|
||||
// Click on the Apply button
|
||||
cy.contains('Apply').click();
|
||||
|
||||
// Wait for
|
||||
cy.wait('@reportData');
|
||||
|
||||
cy.get('#stats').within(() => {
|
||||
// Check if the "No data available in table" message is not present
|
||||
cy.contains('No data available in table').should('not.exist');
|
||||
cy.get('tbody tr').should('have.length', 1);
|
||||
// Should have layout stat - Test a layout stat for an ad campaign, a layout and a display
|
||||
cy.get('tbody td').eq(0).contains('layout'); // stat type
|
||||
cy.get('tbody td').eq(2).contains('POP Display 1'); // display
|
||||
cy.get('tbody td').eq(3).contains('POP Ad Campaign 1'); // ad campaign
|
||||
cy.get('tbody td').eq(5).contains('POP Layout 1'); // layout
|
||||
cy.get('tbody td').eq(9).contains(1); // number of plays
|
||||
cy.get('tbody td').eq(11).contains(60); // total duration
|
||||
});
|
||||
});
|
||||
|
||||
it('Range: Today - Test event/widget stats for a layout and a display', function() {
|
||||
cy.intercept('/report/data/proofofplayReport?*').as('reportData');
|
||||
|
||||
cy.visit('/report/form/proofofplayReport');
|
||||
|
||||
cy.get('#type').select('event');
|
||||
|
||||
// Click on the Apply button
|
||||
cy.contains('Apply').click();
|
||||
|
||||
// Wait for
|
||||
cy.wait('@reportData');
|
||||
|
||||
// Should have media stats - Test media stats for a layout and a display
|
||||
cy.get('#stats tbody tr:nth-child(1) td:nth-child(1)').contains('event'); // stat type
|
||||
cy.get('#stats tbody tr:nth-child(1) td:nth-child(3)').contains('POP Display 1'); // display
|
||||
cy.get('#stats tbody tr:nth-child(1) td:nth-child(9)').contains('Event123'); // tag/eventname
|
||||
cy.get('#stats tbody tr:nth-child(1) td:nth-child(10)').contains(1); // number of plays
|
||||
cy.get('#stats tbody tr:nth-child(1) td:nth-child(12)').contains(60); // total duration
|
||||
|
||||
cy.get('#type').select('widget');
|
||||
|
||||
// Click on the Apply button
|
||||
cy.contains('Apply').click();
|
||||
|
||||
// Wait for
|
||||
cy.wait('@reportData');
|
||||
cy.contains('Tabular').should('be.visible').click();
|
||||
|
||||
// Should have layout stat - Test a layout stat for an ad campaign, a layout and a display
|
||||
cy.get('#stats tbody tr:nth-child(1) td:nth-child(1)').contains('widget'); // stat type
|
||||
cy.get('#stats tbody tr:nth-child(1) td:nth-child(3)').contains('POP Display 1'); // display
|
||||
cy.get('#stats tbody tr:nth-child(1) td:nth-child(6)').contains('POP Layout 1'); // layout
|
||||
cy.get('#stats tbody tr:nth-child(1) td:nth-child(10)').contains(1); // number of plays
|
||||
cy.get('#stats tbody tr:nth-child(1) td:nth-child(12)').contains(60); // total duration
|
||||
});
|
||||
});
|
||||
134
cypress/e2e/Reporting/report_summary.cy.js
Normal file
134
cypress/e2e/Reporting/report_summary.cy.js
Normal file
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Xibo Signage Ltd
|
||||
*
|
||||
* Xibo - Digital Signage - https://xibosignage.com
|
||||
*
|
||||
* This file is part of Xibo.
|
||||
*
|
||||
* Xibo is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* Xibo is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* eslint-disable max-len */
|
||||
describe('Summary by Layout, Media or Event', function() {
|
||||
const display1 = 'POP Display 1';
|
||||
const layout1 = 'POP Layout 1';
|
||||
beforeEach(function() {
|
||||
cy.login();
|
||||
});
|
||||
|
||||
it('Range: Today, Checks duration and count of a layout stat', () => {
|
||||
// Create alias
|
||||
cy.intercept({
|
||||
url: '/display?start=*',
|
||||
query: {display: display1},
|
||||
}).as('loadDisplayAfterSearch');
|
||||
|
||||
cy.intercept({
|
||||
url: '/layout?start=*',
|
||||
query: {layout: layout1},
|
||||
}).as('loadLayoutAfterSearch');
|
||||
|
||||
cy.intercept('/report/data/summaryReport?*').as('reportData');
|
||||
|
||||
cy.visit('/report/form/summaryReport');
|
||||
|
||||
// Click on the select2 selection
|
||||
cy.get('#displayId + span .select2-selection').click();
|
||||
cy.get('.select2-container--open input[type="search"]').type(display1);
|
||||
cy.wait('@loadDisplayAfterSearch');
|
||||
cy.selectOption(display1);
|
||||
|
||||
// Click on the select2 selection
|
||||
cy.get('#layoutId + span .select2-selection').click();
|
||||
cy.get('.select2-container--open input[type="search"]').type(layout1);
|
||||
cy.wait('@loadLayoutAfterSearch');
|
||||
cy.selectOption(layout1);
|
||||
|
||||
// Click on the Apply button
|
||||
cy.contains('Apply').should('be.visible').click();
|
||||
// Wait for report data
|
||||
cy.wait('@reportData');
|
||||
|
||||
cy.get('.chart-container').should('be.visible');
|
||||
|
||||
// Click on Tabular
|
||||
cy.contains('Tabular').should('be.visible').click();
|
||||
cy.contains('Next').should('be.visible').click();
|
||||
|
||||
// Should have media stats
|
||||
cy.get('#summaryTbl tbody tr:nth-child(3) td:nth-child(1)').contains('12:00 PM'); // Period
|
||||
cy.get('#summaryTbl tbody tr:nth-child(3) td:nth-child(2)').contains(60); // Duration
|
||||
cy.get('#summaryTbl tbody tr:nth-child(3) td:nth-child(3)').contains(1); // Count
|
||||
});
|
||||
|
||||
it('Create/Delete a Daily Summary Report Schedule', () => {
|
||||
const reportschedule = 'Daily Summary by Layout 1 and Display 1';
|
||||
|
||||
// Create and alias for load layout
|
||||
cy.intercept('/report/reportschedule/form/add*').as('reportScheduleAddForm');
|
||||
cy.intercept({
|
||||
url: '/display?start=*',
|
||||
query: {display: display1},
|
||||
}).as('loadDisplayAfterSearch');
|
||||
|
||||
cy.intercept({
|
||||
url: '/layout?start=*',
|
||||
query: {layout: layout1},
|
||||
}).as('loadLayoutAfterSearch');
|
||||
|
||||
cy.intercept({
|
||||
url: '/report/reportschedule?*',
|
||||
query: {name: reportschedule},
|
||||
}).as('loadReportScheduleAfterSearch');
|
||||
|
||||
cy.visit('/report/form/summaryReport');
|
||||
|
||||
// Click on the select2 selection
|
||||
cy.get('#layoutId + span .select2-selection').click();
|
||||
cy.get('.select2-container--open input[type="search"]').type(layout1);
|
||||
cy.wait('@loadLayoutAfterSearch');
|
||||
cy.selectOption(layout1);
|
||||
|
||||
// ------
|
||||
// ------
|
||||
// Create a Daily Summary Report Schedule
|
||||
cy.get('#reportAddBtn').click();
|
||||
cy.wait('@reportScheduleAddForm');
|
||||
cy.get('#reportScheduleAddForm #name ').type(reportschedule);
|
||||
|
||||
// Click on the select2 selection
|
||||
cy.get('#reportScheduleAddForm #displayId + span .select2-selection').click();
|
||||
cy.get('.select2-container--open input[type="search"]').type(display1);
|
||||
cy.wait('@loadDisplayAfterSearch');
|
||||
cy.selectOption(display1);
|
||||
|
||||
|
||||
cy.get('#dialog_btn_2').should('be.visible').click();
|
||||
|
||||
cy.visit('/report/reportschedule/view');
|
||||
cy.get('#name').type(reportschedule);
|
||||
cy.wait('@loadReportScheduleAfterSearch');
|
||||
|
||||
// Click on the first row element to open the designer
|
||||
cy.get('#reportschedules_wrapper tr:first-child .dropdown-toggle').click({force: true});
|
||||
|
||||
cy.get('#reportschedules_wrapper tr:first-child .reportschedule_button_delete').click({force: true});
|
||||
|
||||
// Delete test campaign
|
||||
cy.get('.bootbox .save-button').click();
|
||||
|
||||
// Check if layout is deleted in toast message
|
||||
cy.get('.toast').contains('Deleted ' + reportschedule);
|
||||
});
|
||||
});
|
||||
45
cypress/e2e/Reporting/report_timeconnected.cy.js
Normal file
45
cypress/e2e/Reporting/report_timeconnected.cy.js
Normal file
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Xibo Signage Ltd
|
||||
*
|
||||
* Xibo - Digital Signage - https://xibosignage.com
|
||||
*
|
||||
* This file is part of Xibo.
|
||||
*
|
||||
* Xibo is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* Xibo is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* eslint-disable max-len */
|
||||
describe('Time Connected', function() {
|
||||
beforeEach(function() {
|
||||
cy.login();
|
||||
});
|
||||
|
||||
it('should load time connected data of displays', () => {
|
||||
cy.visit('/report/form/timeconnected');
|
||||
|
||||
// Click on the select2 selection
|
||||
cy.get('.select2-search__field').click();
|
||||
|
||||
// Type the display name
|
||||
cy.get('.select2-container--open textarea[type="search"]').type('POP Display Group');
|
||||
cy.get('.select2-container--open .select2-results > ul').contains('POP Display Group').click();
|
||||
|
||||
// Click on the Apply button
|
||||
cy.contains('Apply').should('be.visible').click();
|
||||
|
||||
// Should have media stats
|
||||
cy.get('#records_table tr:nth-child(1) th:nth-child(1)').contains('POP Display 1');
|
||||
cy.get('#records_table tr:nth-child(2) td:nth-child(2)').contains('100%');
|
||||
});
|
||||
});
|
||||
59
cypress/e2e/Reporting/report_timeconnectedsummary.cy.js
Normal file
59
cypress/e2e/Reporting/report_timeconnectedsummary.cy.js
Normal file
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Xibo Signage Ltd
|
||||
*
|
||||
* Xibo - Digital Signage - https://xibosignage.com
|
||||
*
|
||||
* This file is part of Xibo.
|
||||
*
|
||||
* Xibo is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* Xibo is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* eslint-disable max-len */
|
||||
describe('Time Connected', function() {
|
||||
const display1 = 'POP Display 1';
|
||||
beforeEach(function() {
|
||||
cy.login();
|
||||
});
|
||||
|
||||
it('should load time connected data of displays', () => {
|
||||
// Create and alias for load display
|
||||
cy.intercept({
|
||||
url: '/display?start=*',
|
||||
query: {display: display1},
|
||||
}).as('loadDisplayAfterSearch');
|
||||
|
||||
cy.visit('/report/form/timedisconnectedsummary');
|
||||
|
||||
// Click on the select2 selection
|
||||
cy.get('#displayId + span .select2-selection').click();
|
||||
cy.get('.select2-container--open input[type="search"]').type(display1);
|
||||
cy.wait('@loadDisplayAfterSearch');
|
||||
cy.selectOption(display1);
|
||||
|
||||
// Select "Yesterday" from the dropdown
|
||||
cy.get('#reportFilter').select('yesterday');
|
||||
|
||||
// Click on the Apply button
|
||||
cy.contains('Apply').should('be.visible').click();
|
||||
|
||||
cy.get('.chart-container').should('be.visible');
|
||||
|
||||
// Click on Tabular
|
||||
cy.contains('Tabular').should('be.visible').click();
|
||||
|
||||
// Should have media stats
|
||||
cy.get('#timeDisconnectedTbl tr:nth-child(1) td:nth-child(2)').contains('POP Display 1');
|
||||
cy.get('#timeDisconnectedTbl tr:nth-child(1) td:nth-child(3)').contains('10');
|
||||
});
|
||||
});
|
||||
220
cypress/e2e/Schedule/dayparts.cy.js
Normal file
220
cypress/e2e/Schedule/dayparts.cy.js
Normal file
@@ -0,0 +1,220 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Xibo Signage Ltd
|
||||
*
|
||||
* Xibo - Digital Signage - https://xibosignage.com
|
||||
*
|
||||
* This file is part of Xibo.
|
||||
*
|
||||
* Xibo is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* Xibo is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* eslint-disable max-len */
|
||||
describe('Dayparts', function() {
|
||||
let testRun = '';
|
||||
|
||||
beforeEach(function() {
|
||||
cy.login();
|
||||
|
||||
testRun = Cypress._.random(0, 1e9);
|
||||
});
|
||||
|
||||
it('should add a daypart', function() {
|
||||
cy.visit('/daypart/view');
|
||||
|
||||
// Click on the Add Daypart button
|
||||
cy.contains('Add Daypart').click();
|
||||
|
||||
cy.get('.modal input#name')
|
||||
.type('Cypress Test Daypart ' + testRun + '_1');
|
||||
|
||||
cy.get(':nth-child(3) > .col-sm-10 > .input-group > .flatpickr-wrapper > .datePickerHelper').click();
|
||||
// cy.get('.open > .flatpickr-time > :nth-child(1) > .arrowUp').click();
|
||||
cy.get('.open > .flatpickr-time > :nth-child(1) > .numInput').type('8');
|
||||
cy.get(':nth-child(4) > .col-sm-10 > .input-group > .flatpickr-wrapper > .datePickerHelper').click();
|
||||
cy.get('.open > .flatpickr-time > :nth-child(1) > .numInput').type('17');
|
||||
|
||||
// Add first by clicking next
|
||||
cy.get('.modal .save-button').click();
|
||||
|
||||
// Check if daypart is added in toast message
|
||||
cy.contains('Added Cypress Test Daypart ' + testRun + '_1');
|
||||
});
|
||||
|
||||
it('searches and edit existing daypart', function() {
|
||||
// Create a new daypart and then search for it and edit it
|
||||
cy.createDayPart('Cypress Test Daypart ' + testRun).then((id) => {
|
||||
cy.intercept({
|
||||
url: '/daypart?*',
|
||||
query: {name: 'Cypress Test Daypart ' + testRun},
|
||||
}).as('loadGridAfterSearch');
|
||||
|
||||
// Intercept the PUT request
|
||||
cy.intercept({
|
||||
method: 'PUT',
|
||||
url: '/daypart/*',
|
||||
}).as('putRequest');
|
||||
|
||||
cy.visit('/daypart/view');
|
||||
|
||||
// Filter for the created daypart
|
||||
cy.get('#Filter input[name="name"]')
|
||||
.type('Cypress Test Daypart ' + testRun);
|
||||
|
||||
// Wait for the grid reload
|
||||
cy.wait('@loadGridAfterSearch');
|
||||
cy.get('#dayparts tbody tr').should('have.length', 1);
|
||||
|
||||
// Click on the first row element to open the delete modal
|
||||
cy.get('#dayparts tr:first-child .dropdown-toggle').click({force: true});
|
||||
cy.get('#dayparts tr:first-child .daypart_button_edit').click({force: true});
|
||||
|
||||
cy.get('.modal input#name').clear()
|
||||
.type('Cypress Test Daypart Edited ' + testRun);
|
||||
|
||||
// edit test daypart
|
||||
cy.get('.bootbox .save-button').click();
|
||||
|
||||
// Wait for the intercepted PUT request and check the form data
|
||||
cy.wait('@putRequest').then((interception) => {
|
||||
// Get the request body (form data)
|
||||
const response = interception.response;
|
||||
const responseData = response.body.data;
|
||||
|
||||
// assertion on the "daypart" value
|
||||
expect(responseData.name).to.eq('Cypress Test Daypart Edited ' + testRun);
|
||||
});
|
||||
|
||||
// Delete the daypart and assert success
|
||||
cy.deleteDayPart(id).then((res) => {
|
||||
expect(res.status).to.equal(204);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('searches and delete existing daypart', function() {
|
||||
// Create a new daypart and then search for it and delete it
|
||||
cy.createDayPart('Cypress Test Daypart ' + testRun).then((res) => {
|
||||
cy.intercept({
|
||||
url: '/daypart?*',
|
||||
query: {name: 'Cypress Test Daypart ' + testRun},
|
||||
}).as('loadGridAfterSearch');
|
||||
|
||||
cy.visit('/daypart/view');
|
||||
|
||||
// Filter for the created daypart
|
||||
cy.get('#Filter input[name="name"]')
|
||||
.type('Cypress Test Daypart ' + testRun);
|
||||
|
||||
// Wait for the grid reload
|
||||
cy.wait('@loadGridAfterSearch');
|
||||
cy.get('#dayparts tbody tr').should('have.length', 1);
|
||||
|
||||
// Click on the first row element to open the delete modal
|
||||
cy.get('#dayparts tr:first-child .dropdown-toggle').click({force: true});
|
||||
cy.get('#dayparts tr:first-child .daypart_button_delete').click({force: true});
|
||||
|
||||
// Delete test daypart
|
||||
cy.get('.bootbox .save-button').click();
|
||||
|
||||
// Check if daypart is deleted in toast message
|
||||
cy.get('.toast').contains('Deleted Cypress Test Daypart');
|
||||
});
|
||||
});
|
||||
|
||||
it('searches and share existing daypart', function() {
|
||||
// Create a new daypart and then search for it and share it
|
||||
cy.createDayPart('Cypress Test Daypart ' + testRun).then((res) => {
|
||||
cy.intercept({
|
||||
url: '/daypart?*',
|
||||
query: {name: 'Cypress Test Daypart ' + testRun},
|
||||
}).as('loadGridAfterSearch');
|
||||
|
||||
cy.intercept({
|
||||
query: {name: 'Everyone'},
|
||||
url: /\/user\/permissions\/DayPart\/\d+\?draw=2/,
|
||||
}).as('draw2');
|
||||
|
||||
cy.intercept({
|
||||
query: {name: 'Everyone'},
|
||||
url: /\/user\/permissions\/DayPart\/\d+\?draw=3/,
|
||||
}).as('draw3');
|
||||
|
||||
cy.visit('/daypart/view');
|
||||
|
||||
// Filter for the created daypart
|
||||
cy.get('#Filter input[name="name"]')
|
||||
.type('Cypress Test Daypart ' + testRun);
|
||||
|
||||
// Wait for the grid reload
|
||||
cy.wait('@loadGridAfterSearch');
|
||||
cy.get('#dayparts tbody tr').should('have.length', 1);
|
||||
|
||||
// Click on the first row element to open the delete modal
|
||||
cy.get('#dayparts tr:first-child .dropdown-toggle').click({force: true});
|
||||
cy.get('#dayparts tr:first-child .daypart_button_permissions').click({force: true});
|
||||
|
||||
cy.get('.modal #name').type('Everyone');
|
||||
cy.wait('@draw2');
|
||||
cy.get('#permissionsTable tbody tr').should('have.length', 1);
|
||||
|
||||
cy.get('#permissionsTable').within(() => {
|
||||
cy.get('input[type="checkbox"][data-permission="view"]').should('be.visible').check();
|
||||
|
||||
// DOM is refreshed at this point, so wait for it
|
||||
cy.wait('@draw3');
|
||||
// We have no other option but to put {force: true} here
|
||||
cy.get('input[type="checkbox"][data-permission="edit"]').check();
|
||||
});
|
||||
|
||||
// Save
|
||||
cy.get('.bootbox .save-button').click();
|
||||
|
||||
// Check if daypart is deleted in toast message
|
||||
cy.get('.toast').contains('Share option Updated');
|
||||
});
|
||||
});
|
||||
|
||||
it('selects multiple dayparts and delete them', function() {
|
||||
// Create a new daypart and then search for it and delete it
|
||||
cy.createDayPart('Cypress Test Daypart ' + testRun).then((res) => {
|
||||
cy.intercept({
|
||||
url: '/daypart?*',
|
||||
query: {name: 'Cypress Test Daypart'},
|
||||
}).as('loadGridAfterSearch');
|
||||
|
||||
// Delete all test dayparts
|
||||
cy.visit('/daypart/view');
|
||||
|
||||
// Clear filter
|
||||
cy.get('#Filter input[name="name"]')
|
||||
.clear()
|
||||
.type('Cypress Test Daypart');
|
||||
|
||||
// Wait for the grid reload
|
||||
cy.wait('@loadGridAfterSearch');
|
||||
|
||||
// Select all
|
||||
cy.get('button[data-toggle="selectAll"]').click();
|
||||
|
||||
// Delete all
|
||||
cy.get('.dataTables_info button[data-toggle="dropdown"]').click();
|
||||
cy.get('.dataTables_info a[data-button-id="daypart_button_delete"]').click();
|
||||
|
||||
cy.get('button.save-button').click();
|
||||
|
||||
// Modal should contain one successful delete at least
|
||||
cy.get('.modal-body').contains(': Success');
|
||||
});
|
||||
});
|
||||
});
|
||||
392
cypress/e2e/Schedule/schedule.cy.js
Normal file
392
cypress/e2e/Schedule/schedule.cy.js
Normal file
@@ -0,0 +1,392 @@
|
||||
/*
|
||||
* Copyright (C) 2025 Xibo Signage Ltd
|
||||
*
|
||||
* Xibo - Digital Signage - https://xibosignage.com
|
||||
*
|
||||
* This file is part of Xibo.
|
||||
*
|
||||
* Xibo is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* Xibo is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* eslint-disable max-len */
|
||||
describe('Schedule Events', function() {
|
||||
// Seeded Data
|
||||
const campaignSchedule1 = 'Campaign for Schedule 1';
|
||||
const layoutSchedule1 = 'Layout for Schedule 1';
|
||||
|
||||
const display1 = 'List Campaign Display 1';
|
||||
const display2 = 'List Campaign Display 2';
|
||||
const command1 = 'Set Timezone';
|
||||
|
||||
beforeEach(function() {
|
||||
cy.login();
|
||||
});
|
||||
|
||||
it('should list all scheduled events', function() {
|
||||
// Make a GET request to the API endpoint '/schedule/data/events'??
|
||||
cy.request({
|
||||
method: 'GET',
|
||||
url: '/schedule/data/events',
|
||||
}).then((response) => {
|
||||
// Assertions on the response
|
||||
expect(response.status).to.equal(200);
|
||||
expect(response.body).to.have.property('result');
|
||||
});
|
||||
});
|
||||
|
||||
// TC-01
|
||||
it('should schedule an event layout that has no priority, no recurrence', function() {
|
||||
cy.intercept('GET', '/schedule/form/add?*').as('scheduleAddForm');
|
||||
|
||||
// Set up intercepts with aliases
|
||||
cy.intercept({
|
||||
url: '/display?start=*',
|
||||
query: {display: display1},
|
||||
}).as('loadDisplayAfterSearch');
|
||||
|
||||
cy.intercept({
|
||||
url: '/displaygroup?*',
|
||||
query: {displayGroup: display1},
|
||||
}).as('loadDisplaygroupAfterSearch');
|
||||
|
||||
cy.intercept({
|
||||
url: '/campaign?type=list*',
|
||||
query: {name: layoutSchedule1},
|
||||
}).as('loadListCampaignsAfterSearch');
|
||||
|
||||
// Click on the Add Event button
|
||||
cy.visit('/schedule/view');
|
||||
|
||||
cy.contains('Clear Filters').should('be.visible').click();
|
||||
cy.contains('Add Event').click();
|
||||
|
||||
cy.get('.bootbox.modal')
|
||||
.should('be.visible') // essential: Ensure the modal is visible
|
||||
.then(() => {
|
||||
cy.get('.modal-content #eventTypeId').select('Layout');
|
||||
// Select layout
|
||||
cy.selectFromDropdown('.layout-control .select2-selection', layoutSchedule1, layoutSchedule1, '@loadListCampaignsAfterSearch');
|
||||
|
||||
// Click Next and check toast message
|
||||
cy.get('.modal .modal-footer').contains('Next').click();
|
||||
|
||||
// Select display
|
||||
cy.selectFromDropdown('.display-group-control .select2-selection', display1, display1, '@loadDisplaygroupAfterSearch', 1);
|
||||
|
||||
// Click Next and check toast message
|
||||
cy.get('.modal .modal-footer').contains('Next').click();
|
||||
|
||||
// Select day part and set name
|
||||
cy.get('.modal-content [name="dayPartId"]').select('Always');
|
||||
|
||||
// Click Next and check toast message
|
||||
cy.get('.modal .modal-footer').contains('Next').click();
|
||||
cy.get('.modal-content [name="name"]').type('Always - Layout Event');
|
||||
|
||||
cy.get('.modal .modal-footer').contains('Finish').click();
|
||||
|
||||
cy.contains('Added Event');
|
||||
});
|
||||
|
||||
// Validate - schedule creation should be successful
|
||||
cy.visit('/schedule/view');
|
||||
cy.contains('Clear Filters').should('be.visible').click();
|
||||
|
||||
cy.get('#DisplayList + span .select2-selection').click();
|
||||
|
||||
// Type the display name
|
||||
cy.get('.select2-container--open textarea[type="search"]').type(display1);
|
||||
|
||||
// Wait for Display to load
|
||||
cy.wait('@loadDisplayAfterSearch');
|
||||
cy.get('.select2-container--open').contains(display1);
|
||||
cy.get('.select2-container--open .select2-results > ul > li').should('have.length', 1);
|
||||
|
||||
// Select the display from the dropdown
|
||||
cy.get('.select2-container--open .select2-results > ul > li:first').contains(display1).click();
|
||||
|
||||
// Verify that the schedule is successfully created and listed in the grid
|
||||
cy.get('#schedule-grid').contains(layoutSchedule1);
|
||||
|
||||
// Should have 1
|
||||
cy.get('#schedule-grid tbody tr').should('have.length', 1);
|
||||
});
|
||||
|
||||
// relies on TC-01
|
||||
it('should edit a scheduled event', function() {
|
||||
cy.intercept('GET', '/schedule/form/add?*').as('scheduleAddForm');
|
||||
|
||||
// Set up intercepts with aliases
|
||||
cy.intercept({
|
||||
url: '/display?start=*',
|
||||
query: {display: display1},
|
||||
}).as('loadDisplayAfterSearch');
|
||||
|
||||
cy.intercept({
|
||||
url: '/displaygroup?*',
|
||||
query: {displayGroup: display2},
|
||||
}).as('loadDisplaygroupAfterSearch');
|
||||
|
||||
cy.intercept({
|
||||
url: '/campaign?type=list*',
|
||||
query: {name: layoutSchedule1},
|
||||
}).as('loadListCampaignsAfterSearch');
|
||||
|
||||
// Click on the Add Event button
|
||||
cy.visit('/schedule/view');
|
||||
|
||||
cy.contains('Clear Filters').should('be.visible').click();
|
||||
|
||||
cy.get('#DisplayList + span .select2-selection').click();
|
||||
|
||||
// Type the display name
|
||||
cy.get('.select2-container--open textarea[type="search"]').type(display1);
|
||||
|
||||
// Wait for Display to load
|
||||
cy.wait('@loadDisplayAfterSearch');
|
||||
cy.get('.select2-container--open').contains(display1);
|
||||
cy.get('.select2-container--open .select2-results > ul > li').should('have.length', 1);
|
||||
|
||||
// Select the display from the dropdown
|
||||
cy.get('.select2-container--open .select2-results > ul > li:first').contains(display1).click();
|
||||
|
||||
// Verify that the schedule is successfully created and listed in the grid
|
||||
cy.get('#schedule-grid').contains(layoutSchedule1);
|
||||
|
||||
// Should have 1
|
||||
cy.get('#schedule-grid tbody tr').should('have.length', 1);
|
||||
cy.get('#schedule-grid tr:first-child .dropdown-toggle').click({force: true});
|
||||
cy.get('#schedule-grid tr:first-child .schedule_button_edit').click({force: true});
|
||||
|
||||
cy.contains('.stepwizard-step', 'Displays')
|
||||
.find('a')
|
||||
.click();
|
||||
|
||||
// Select display
|
||||
cy.get('.display-group-control > .col-sm-10 > .select2 > .selection > .select2-selection').type(display2);
|
||||
// Wait for the display group to load after search
|
||||
cy.wait('@loadDisplaygroupAfterSearch');
|
||||
cy.get('.select2-container--open .select2-dropdown .select2-results > ul')
|
||||
.should('contain', display2);
|
||||
cy.get('.select2-container--open .select2-dropdown .select2-results > ul > li')
|
||||
.should('have.length', 2)
|
||||
.last()
|
||||
.click();
|
||||
|
||||
cy.contains('.stepwizard-step', 'Optional')
|
||||
.find('a')
|
||||
.click();
|
||||
|
||||
cy.get('.modal-content [name="name"]').clear().type('Always - Layout Event Edited');
|
||||
|
||||
// Click Next and check toast message
|
||||
cy.get('.modal .modal-footer').contains('Save').click();
|
||||
cy.contains('Edited Event');
|
||||
});
|
||||
|
||||
it('should schedule an event campaign that has no priority, no recurrence', function() {
|
||||
cy.intercept('GET', '/schedule/form/add?*').as('scheduleAddForm');
|
||||
|
||||
// Set up intercepts with aliases
|
||||
cy.intercept({
|
||||
url: '/display?start=*',
|
||||
query: {display: display1},
|
||||
}).as('loadDisplayAfterSearch');
|
||||
|
||||
cy.intercept({
|
||||
url: '/displaygroup?*',
|
||||
query: {displayGroup: display1},
|
||||
}).as('loadDisplaygroupAfterSearch');
|
||||
|
||||
cy.intercept({
|
||||
url: '/campaign?type=list*',
|
||||
query: {name: campaignSchedule1},
|
||||
}).as('loadListCampaignsAfterSearch');
|
||||
|
||||
// Visit the page and click on the Add Event button
|
||||
cy.visit('/schedule/view');
|
||||
|
||||
cy.contains('Clear Filters').should('be.visible').click();
|
||||
cy.contains('Add Event').click();
|
||||
|
||||
cy.get('.bootbox.modal')
|
||||
.should('be.visible') // essential: Ensure the modal is visible
|
||||
.then(() => {
|
||||
cy.get('.modal-content #eventTypeId').select('Campaign');
|
||||
// Select campaign
|
||||
cy.selectFromDropdown('.layout-control .select2-selection', campaignSchedule1, campaignSchedule1, '@loadListCampaignsAfterSearch');
|
||||
|
||||
// Click Next and check toast message
|
||||
cy.get('.modal .modal-footer').contains('Next').click();
|
||||
|
||||
// Select display
|
||||
cy.selectFromDropdown('.display-group-control .select2-selection', display1, display1, '@loadDisplaygroupAfterSearch', 1);
|
||||
|
||||
// Click Next and check toast message
|
||||
cy.get('.modal .modal-footer').contains('Next').click();
|
||||
|
||||
// Select day part and campaign
|
||||
cy.get('.modal-content [name="dayPartId"]').select('Always');
|
||||
|
||||
// Click Next and check toast message
|
||||
cy.get('.modal .modal-footer').contains('Next').click();
|
||||
cy.get('.modal-content [name="name"]').type('Always - Campaign Event');
|
||||
|
||||
cy.get('.modal .modal-footer').contains('Finish').click();
|
||||
|
||||
cy.contains('Added Event');
|
||||
});
|
||||
|
||||
// Validate - schedule creation should be successful
|
||||
cy.visit('/schedule/view');
|
||||
cy.contains('Clear Filters').should('be.visible').click();
|
||||
|
||||
cy.get('#DisplayList + span .select2-selection').click();
|
||||
|
||||
// Type the display name
|
||||
cy.get('.select2-container--open textarea[type="search"]').type(display1);
|
||||
|
||||
// Wait for Display to load
|
||||
cy.wait('@loadDisplayAfterSearch');
|
||||
cy.get('.select2-container--open').contains(display1);
|
||||
cy.get('.select2-container--open .select2-results > ul > li').should('have.length', 1);
|
||||
|
||||
// Select the display from the dropdown
|
||||
cy.get('.select2-container--open .select2-results > ul > li:first').contains(display1).click();
|
||||
|
||||
// Verify that the schedule is successfully created and listed in the grid
|
||||
cy.get('#schedule-grid').contains(campaignSchedule1);
|
||||
});
|
||||
|
||||
it('should schedule an event command layout that has no priority, no recurrence', function() {
|
||||
cy.intercept('GET', '/schedule/form/add?*').as('scheduleAddForm');
|
||||
cy.intercept({
|
||||
url: '/displaygroup?*',
|
||||
query: {displayGroup: display1},
|
||||
}).as('loadDisplaygroupAfterSearch');
|
||||
|
||||
cy.intercept({
|
||||
url: '/command?*',
|
||||
query: {command: command1},
|
||||
}).as('loadCommandAfterSearch');
|
||||
|
||||
// Click on the Add Event button
|
||||
cy.visit('/schedule/view');
|
||||
|
||||
cy.contains('Clear Filters').should('be.visible').click();
|
||||
cy.contains('Add Event').click();
|
||||
|
||||
cy.get('.bootbox.modal')
|
||||
.should('be.visible') // essential: Ensure the modal is visible
|
||||
.then(() => {
|
||||
cy.get('.modal-content #eventTypeId').select('Command');
|
||||
// Select command
|
||||
cy.selectFromDropdown('.command-control .select2-selection', command1, command1, '@loadCommandAfterSearch');
|
||||
|
||||
// Click Next and check toast message
|
||||
cy.get('.modal .modal-footer').contains('Next').click();
|
||||
|
||||
// Select display
|
||||
cy.selectFromDropdown('.display-group-control .select2-selection', display1, display1, '@loadDisplaygroupAfterSearch', 1);
|
||||
|
||||
// Click Next and check toast message
|
||||
cy.get('.modal .modal-footer').contains('Next').click();
|
||||
|
||||
cy.get('.starttime-control > .col-sm-10 > .input-group > .flatpickr-wrapper > .datePickerHelper').click();
|
||||
cy.get('.open > .flatpickr-innerContainer > .flatpickr-rContainer > .flatpickr-days > .dayContainer > .today').click();
|
||||
cy.get('.open > .flatpickr-time > :nth-child(3) > .arrowUp').click();
|
||||
|
||||
cy.get('.modal .modal-footer').contains('Next').click();
|
||||
cy.get('.modal-content [name="name"]').type('Custom - Command Event');
|
||||
|
||||
cy.get('.modal .modal-footer').contains('Finish').click();
|
||||
});
|
||||
});
|
||||
|
||||
it('should schedule an event overlay layout that has no priority, no recurrence', function() {
|
||||
cy.intercept('GET', '/schedule/form/add?*').as('scheduleAddForm');
|
||||
cy.intercept({
|
||||
url: '/displaygroup?*',
|
||||
query: {displayGroup: display1},
|
||||
}).as('loadDisplaygroupAfterSearch');
|
||||
|
||||
cy.intercept({
|
||||
url: '/campaign?type=list*',
|
||||
query: {name: layoutSchedule1},
|
||||
}).as('loadListCampaignsAfterSearch');
|
||||
|
||||
// Click on the Add Event button
|
||||
cy.visit('/schedule/view');
|
||||
|
||||
cy.contains('Clear Filters').should('be.visible').click();
|
||||
cy.contains('Add Event').click();
|
||||
|
||||
cy.get('.bootbox.modal')
|
||||
.should('be.visible') // essential: Ensure the modal is visible
|
||||
.then(() => {
|
||||
cy.get('.modal-content #eventTypeId').select('Overlay Layout');
|
||||
// Select layout
|
||||
cy.selectFromDropdown('.layout-control .select2-selection', layoutSchedule1, layoutSchedule1, '@loadListCampaignsAfterSearch');
|
||||
|
||||
// Click Next and check toast message
|
||||
cy.get('.modal .modal-footer').contains('Next').click();
|
||||
|
||||
// Select display
|
||||
cy.selectFromDropdown('.display-group-control .select2-selection', display1, display1, '@loadDisplaygroupAfterSearch', 1);
|
||||
|
||||
// Click Next and check toast message
|
||||
cy.get('.modal .modal-footer').contains('Next').click();
|
||||
|
||||
// Select daypart - custom
|
||||
cy.get('#dayPartId').select('Custom');
|
||||
|
||||
|
||||
cy.get('.starttime-control > .col-sm-10 > .input-group > .flatpickr-wrapper > .datePickerHelper')
|
||||
.click() // Open the picker
|
||||
.then(() => {
|
||||
// Select today's date
|
||||
cy.get('.flatpickr-calendar.open .flatpickr-days .dayContainer .today')
|
||||
.click();
|
||||
|
||||
// Increment the hour (adjust time)
|
||||
cy.get('.flatpickr-calendar.open .flatpickr-time :nth-child(3) .arrowUp')
|
||||
.click();
|
||||
|
||||
// Close the picker by clicking outside
|
||||
cy.get('body').click(0, 0);
|
||||
});
|
||||
|
||||
cy.get('.endtime-control > .col-sm-10 > .input-group > .flatpickr-wrapper > .datePickerHelper')
|
||||
.click() // Open the picker
|
||||
.then(() => {
|
||||
// Select today's date
|
||||
cy.get('.flatpickr-calendar.open .flatpickr-days .dayContainer .today')
|
||||
.click();
|
||||
|
||||
// Increment the hour (adjust time)
|
||||
cy.get('.flatpickr-calendar.open .flatpickr-time :nth-child(3) .arrowUp')
|
||||
.click()
|
||||
.click();
|
||||
|
||||
// Close the picker by clicking outside
|
||||
cy.get('body').click(0, 0);
|
||||
});
|
||||
|
||||
cy.get('.modal .modal-footer').contains('Next').click();
|
||||
cy.get('.modal-content [name="name"]').type('Custom - Overlay Event');
|
||||
|
||||
cy.get('.modal .modal-footer').contains('Finish').click();
|
||||
});
|
||||
});
|
||||
});
|
||||
136
cypress/e2e/Templates/templates.cy.js
Normal file
136
cypress/e2e/Templates/templates.cy.js
Normal file
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
* Copyright (C) 2025 Xibo Signage Ltd
|
||||
*
|
||||
* Xibo - Digital Signage - https://xibosignage.com
|
||||
*
|
||||
* This file is part of Xibo.
|
||||
*
|
||||
* Xibo is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* Xibo is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
describe('Template Test Suite', function () {
|
||||
|
||||
let templateName = '';
|
||||
|
||||
// create template flow
|
||||
function createTemplate(name) {
|
||||
cy.visit('/template/view');
|
||||
cy.contains('Add Template').click();
|
||||
cy.get('#name').clear().type(name);
|
||||
cy.get('#dialog_btn_2').should('be.visible').click();
|
||||
cy.get('#layout-editor').should('be.visible');
|
||||
cy.get('#backBtn').click({ force: true });
|
||||
}
|
||||
|
||||
// delete template flow
|
||||
function deleteATemplate(name) {
|
||||
cy.get('div[title="Row Menu"] button.dropdown-toggle').click({ force: true });
|
||||
cy.get('a.layout_button_delete[data-commit-method="delete"]').click({ force: true });
|
||||
|
||||
cy.get('#layoutDeleteForm').should('be.visible');
|
||||
cy.contains('p', 'Are you sure you want to delete this item?').should('be.visible');
|
||||
}
|
||||
|
||||
beforeEach(function () {
|
||||
cy.login();
|
||||
templateName = 'Template No. ' + Cypress._.random(0, 1e9);
|
||||
});
|
||||
|
||||
// Display Template List
|
||||
it('should display the template list', function () {
|
||||
cy.intercept('GET', '**/template*').as('templateList');
|
||||
cy.visit('/template/view');
|
||||
cy.wait('@templateList').its('response.statusCode').should('eq', 200);
|
||||
});
|
||||
|
||||
// Save Incomplete Form
|
||||
it('should prevent saving incomplete template', function () {
|
||||
cy.visit('/template/view');
|
||||
cy.contains('Add Template').click();
|
||||
cy.get('#dialog_btn_2').should('be.visible').click();
|
||||
cy.contains('Layout Name must be between 1 and 100 characters').should('be.visible');
|
||||
});
|
||||
|
||||
// Create a Template
|
||||
it('should create a template', function () {
|
||||
createTemplate(templateName);
|
||||
cy.contains('td', templateName).should('be.visible');
|
||||
});
|
||||
|
||||
// Duplicate Template
|
||||
it('should not allow duplicate template name', function () {
|
||||
createTemplate(templateName);
|
||||
|
||||
cy.contains('Add Template').click();
|
||||
cy.get('#name').clear().type(templateName);
|
||||
cy.get('#dialog_btn_2').should('be.visible').click();
|
||||
|
||||
cy.get('.modal-footer .form-error')
|
||||
.contains(`You already own a Layout called '${templateName}'. Please choose another name.`)
|
||||
.should('be.visible');
|
||||
});
|
||||
|
||||
// Search and Delete a template
|
||||
it('should search template and delete it', function () {
|
||||
cy.intercept({
|
||||
url: '/template?*',
|
||||
query: { template: templateName },
|
||||
}).as('displayTemplateAfterSearch');
|
||||
|
||||
createTemplate(templateName);
|
||||
|
||||
cy.get('#template').clear().type(templateName);
|
||||
cy.wait('@displayTemplateAfterSearch');
|
||||
cy.get('table tbody tr').should('have.length', 1);
|
||||
cy.get('#templates tbody tr:nth-child(1) td:nth-child(1)').contains(templateName);
|
||||
|
||||
cy.get('#templates tbody tr')
|
||||
.should('have.length', 1)
|
||||
.first()
|
||||
.should('contain.text', templateName);
|
||||
|
||||
// delete template = no
|
||||
deleteATemplate(templateName);
|
||||
cy.get('#dialog_btn_2').click({ force: true });
|
||||
cy.contains(templateName).should('be.visible');
|
||||
|
||||
// delete template = yes
|
||||
deleteATemplate(templateName);
|
||||
cy.get('#dialog_btn_3').click({ force: true });
|
||||
cy.get('#toast-container .toast-message').contains(`Deleted ${templateName}`).should('be.visible');
|
||||
cy.contains(templateName).should('not.exist');
|
||||
});
|
||||
|
||||
// Multiple deleting of templates
|
||||
it('should delete multiple templates', function () {
|
||||
createTemplate(templateName);
|
||||
|
||||
cy.get('button[data-toggle="selectAll"]').click();
|
||||
cy.get('.dataTables_info button[data-toggle="dropdown"]').click();
|
||||
cy.get('a[data-button-id="layout_button_delete"]').click();
|
||||
|
||||
cy.get('.modal-footer').contains('Save').click();
|
||||
cy.get('.modal-body').contains(': Success');
|
||||
cy.get('.modal-footer').contains('Close').click();
|
||||
cy.contains('.dataTables_empty', 'No data available in table').should('be.visible');
|
||||
});
|
||||
|
||||
// Search for non-existing template
|
||||
it('should not return any entry for non-existing template', function () {
|
||||
cy.visit('/template/view');
|
||||
cy.get('#template').clear().type('This is a hardcoded template name just to make sure it doesnt exist in the record');
|
||||
cy.contains('.dataTables_empty', 'No data available in table').should('be.visible');
|
||||
});
|
||||
|
||||
});
|
||||
76
cypress/e2e/UserAccount/user_account.cy.js
Normal file
76
cypress/e2e/UserAccount/user_account.cy.js
Normal file
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright (C) 2025 Xibo Signage Ltd
|
||||
*
|
||||
* Xibo - Digital Signage - https://xibosignage.com
|
||||
*
|
||||
* This file is part of Xibo.
|
||||
*
|
||||
* Xibo is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* Xibo is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
describe('User Account Test Suite', function() {
|
||||
|
||||
beforeEach(function () {
|
||||
cy.login();
|
||||
cy.visit('/statusdashboard');
|
||||
});
|
||||
|
||||
it('navigates to edit profile', function() {
|
||||
cy.url().should('include', 'dashboard');
|
||||
cy.get('img.nav-avatar').should('be.visible');
|
||||
cy.get('#navbarUserMenu').click();
|
||||
cy.get('div[aria-labelledby="navbarUserMenu"]')
|
||||
.should('be.visible')
|
||||
.contains('Edit Profile');
|
||||
});
|
||||
|
||||
it('verifies all menu items are present and in order', function() {
|
||||
cy.get('#navbarUserMenu').click();
|
||||
cy.get('div[aria-labelledby="navbarUserMenu"] a')
|
||||
.should('have.length', 6)
|
||||
.then($items => {
|
||||
const texts = [...$items].map(el => el.innerText.trim());
|
||||
expect(texts).to.deep.equal([
|
||||
'Preferences',
|
||||
'Edit Profile',
|
||||
'My Applications',
|
||||
'Reshow welcome',
|
||||
'About',
|
||||
'Logout'
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
it('validates edit profile', function() {
|
||||
cy.get('#navbarUserMenu').click();
|
||||
cy.get('div[aria-labelledby="navbarUserMenu"]')
|
||||
.contains('Edit Profile')
|
||||
.click();
|
||||
|
||||
cy.get('.modal-content').should('be.visible');
|
||||
cy.contains('label', 'User Name').should('be.visible');
|
||||
cy.contains('label', 'Password').should('be.visible');
|
||||
cy.contains('label', 'New Password').should('be.visible');
|
||||
cy.contains('label', 'Retype New Password').should('be.visible');
|
||||
cy.contains('label', 'Email').should('be.visible');
|
||||
cy.contains('label', 'Two Factor Authentication').should('be.visible');
|
||||
|
||||
// Ensure 2FA defaults to Off
|
||||
cy.get('#twoFactorTypeId')
|
||||
.should('be.visible')
|
||||
.find('option:selected')
|
||||
.should('have.text', 'Off');
|
||||
});
|
||||
|
||||
});
|
||||
76
cypress/e2e/User_Account/user_account.cy.js
Normal file
76
cypress/e2e/User_Account/user_account.cy.js
Normal file
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright (C) 2025 Xibo Signage Ltd
|
||||
*
|
||||
* Xibo - Digital Signage - https://xibosignage.com
|
||||
*
|
||||
* This file is part of Xibo.
|
||||
*
|
||||
* Xibo is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* Xibo is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
describe('User Account Test Suite', function() {
|
||||
|
||||
beforeEach(function () {
|
||||
cy.login();
|
||||
cy.visit('/statusdashboard');
|
||||
});
|
||||
|
||||
it('navigates to edit profile', function() {
|
||||
cy.url().should('include', 'dashboard');
|
||||
cy.get('img.nav-avatar').should('be.visible');
|
||||
cy.get('#navbarUserMenu').click();
|
||||
cy.get('div[aria-labelledby="navbarUserMenu"]')
|
||||
.should('be.visible')
|
||||
.contains('Edit Profile');
|
||||
});
|
||||
|
||||
it('verifies all menu items are present and in order', function() {
|
||||
cy.get('#navbarUserMenu').click();
|
||||
cy.get('div[aria-labelledby="navbarUserMenu"] a')
|
||||
.should('have.length', 6)
|
||||
.then($items => {
|
||||
const texts = [...$items].map(el => el.innerText.trim());
|
||||
expect(texts).to.deep.equal([
|
||||
'Preferences',
|
||||
'Edit Profile',
|
||||
'My Applications',
|
||||
'Reshow welcome',
|
||||
'About',
|
||||
'Logout'
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
it('validates edit profile', function() {
|
||||
cy.get('#navbarUserMenu').click();
|
||||
cy.get('div[aria-labelledby="navbarUserMenu"]')
|
||||
.contains('Edit Profile')
|
||||
.click();
|
||||
|
||||
cy.get('.modal-content').should('be.visible');
|
||||
cy.contains('label', 'User Name').should('be.visible');
|
||||
cy.contains('label', 'Password').should('be.visible');
|
||||
cy.contains('label', 'New Password').should('be.visible');
|
||||
cy.contains('label', 'Retype New Password').should('be.visible');
|
||||
cy.contains('label', 'Email').should('be.visible');
|
||||
cy.contains('label', 'Two Factor Authentication').should('be.visible');
|
||||
|
||||
// Ensure 2FA defaults to Off
|
||||
cy.get('#twoFactorTypeId')
|
||||
.should('be.visible')
|
||||
.find('option:selected')
|
||||
.should('have.text', 'Off');
|
||||
});
|
||||
|
||||
});
|
||||
39
cypress/e2e/dashboard.cy.js
Normal file
39
cypress/e2e/dashboard.cy.js
Normal file
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Xibo Signage Ltd
|
||||
*
|
||||
* Xibo - Digital Signage - https://xibosignage.com
|
||||
*
|
||||
* This file is part of Xibo.
|
||||
*
|
||||
* Xibo is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* Xibo is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
describe('Dashboard', function() {
|
||||
beforeEach(function() {
|
||||
cy.login();
|
||||
});
|
||||
|
||||
it('should be at the dashboard page', function() {
|
||||
cy.visit('/statusdashboard');
|
||||
|
||||
|
||||
cy.url().should('include', 'dashboard');
|
||||
|
||||
// Check for the dashboard elements
|
||||
cy.contains('Bandwidth Usage');
|
||||
cy.contains('Library Usage');
|
||||
cy.contains('Display Activity');
|
||||
cy.contains('Latest News');
|
||||
});
|
||||
});
|
||||
37
cypress/e2e/login.cy.js
Normal file
37
cypress/e2e/login.cy.js
Normal file
@@ -0,0 +1,37 @@
|
||||
describe('Login', function() {
|
||||
|
||||
it('should be able to login the default user', function () {
|
||||
|
||||
cy.visit('/login').then(() => {
|
||||
|
||||
cy.get('input#username')
|
||||
.type('xibo_admin');
|
||||
|
||||
cy.get('input#password')
|
||||
.type('password');
|
||||
|
||||
cy.get('button[type=submit]')
|
||||
.click();
|
||||
|
||||
cy.url().should('include', 'dashboard');
|
||||
|
||||
cy.contains('xibo_admin');
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail to login an invalid user', function () {
|
||||
|
||||
cy.visit('/login').then(() => {
|
||||
cy.get('input#username')
|
||||
.type('xibo_admin');
|
||||
|
||||
cy.get('input#password')
|
||||
.type('wrongpassword');
|
||||
|
||||
cy.get('button[type=submit]')
|
||||
.click();
|
||||
|
||||
cy.contains('Username or Password incorrect');
|
||||
});
|
||||
});
|
||||
});
|
||||
19
cypress/e2e/unauthed.cy.js
Normal file
19
cypress/e2e/unauthed.cy.js
Normal file
@@ -0,0 +1,19 @@
|
||||
describe('Unauthenticated CMS access', function () {
|
||||
it('should visit the login page and check the version', function () {
|
||||
|
||||
cy.visit('/login').then(() => {
|
||||
|
||||
cy.url().should('include', '/login');
|
||||
|
||||
cy.contains('Version 4.');
|
||||
});
|
||||
});
|
||||
|
||||
it('should redirect to login when an authenticated page is requested', function() {
|
||||
cy.visit('/logout').then(() => {
|
||||
cy.visit('/layout/view').then(() => {
|
||||
cy.url().should('include', '/login');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user