import 'cypress-wait-until'; import _ from 'lodash-es'; let LOCAL_STORAGE_MEMORY = {}; let USER_TOKENS = []; let ACTIVE_ENDPOINT_ID = ''; let ACTIVE_ENDPOINT_TYPE = ''; Cypress.Commands.add('saveLocalStorage', () => { Object.keys(localStorage).forEach((key) => { LOCAL_STORAGE_MEMORY[key] = localStorage[key]; }); }); Cypress.Commands.add('restoreLocalStorage', () => { Object.keys(LOCAL_STORAGE_MEMORY).forEach((key) => { localStorage.setItem(key, LOCAL_STORAGE_MEMORY[key]); }); }); Cypress.Commands.add('saveUserToken', (username) => { USER_TOKENS[username] = localStorage.getItem('portainer.JWT').slice(1, -1); }); Cypress.Commands.add('deleteUserToken', (username) => { delete USER_TOKENS[username]; }); Cypress.Commands.add('setBrowserToken', (username) => { localStorage.setItem('portainer.JWT', USER_TOKENS[username]); }); Cypress.Commands.add('clearBrowserToken', () => { localStorage.removeItem('portainer.JWT'); }); Cypress.Commands.add('clearUserTokens', () => { USER_TOKENS = []; }); Cypress.Commands.add('initAdmin', (username, password) => { cy.visit('/#/init/admin'); // Wait text, meaning page has loaded cy.waitUntil(() => cy.contains('Please create the initial administrator user.')); if (username != 'admin') { cy.get('#username').clear().type(username); } cy.get('#password').type(password); cy.get('#confirm_password').type(password); cy.get('[type=submit]').click(); }); Cypress.Commands.add('initEndpoint', () => { cy.get('[for=1]').click(); cy.get('[type=submit]').click(); }); Cypress.Commands.add('addNewEndpoint', (endpointName, endpointType, endpointURL) => { const addEndpoint = (endpointName, endpointType, endpointURL) => { cy.contains(endpointType).click(); cy.get('input[name=container_name]').type(endpointName); cy.get('input[name=endpoint_url]').clear().type(endpointURL); cy.get('span').contains('Add endpoint').click(); cy.waitUntil(() => cy.contains('Endpoints')); }; cy.visit('/#!/endpoints/new'); cy.waitUntil(() => cy.contains('Create endpoint')); addEndpoint(endpointName, endpointType, endpointURL); }); Cypress.Commands.add('selectEndpoint', (endpointName) => { cy.visit('/#!/home'); cy.waitUntil(() => cy.contains(endpointName).click()); cy.waitUntil(() => cy.get('rd-header-title[title-text="Dashboard"]')); // Get info from active endpoint for building URL's cy.request({ method: 'GET', url: '/api/endpoints?limit=10&start=1', auth: { bearer: USER_TOKENS['admin'], }, }) .its('body') .then((body) => { let endpointOBJ = _.find(body, { Name: endpointName }); ACTIVE_ENDPOINT_ID = endpointOBJ.Id; ACTIVE_ENDPOINT_TYPE = endpointOBJ.Type; }); }); Cypress.Commands.add('auth', (location, username, password) => { if (location == 'frontend') { cy.visit('/#/auth'); cy.get('#username').click(); cy.get('#username').type(username); cy.get('#password').type(password); cy.waitUntil(() => cy.get('ng-transclude > .ng-scope:nth-child(1)')).click(); // Wait until you hit the home screen and get at least 1 endpoint item cy.waitUntil(() => cy.get('endpoint-item')).saveUserToken(username); } else { cy.request({ method: 'POST', url: '/api/auth', body: { username: username, password: password, }, }) .its('body') .then((body) => { USER_TOKENS[username] = body.jwt; }); } }); Cypress.Commands.add('createUser', (location, username, password) => { // Setup team route to wait for response cy.route2({ method: 'POST', path: '**/users' }).as('users'); if (location == 'frontend') { cy.visit('/#!/users'); cy.waitUntil(() => cy.get('#username')).click(); cy.get('#username').type(username); cy.get('#password').type(password); cy.get('#confirm_password').type(password); cy.get('.btn-primary').click(); cy.wait('@users'); } else { cy.request({ method: 'POST', url: '/api/users', failOnStatusCode: false, auth: { bearer: USER_TOKENS['admin'], }, body: { username: username, password: password, role: 2, }, }); } }); Cypress.Commands.add('deleteUser', (username) => { cy.request({ method: 'GET', url: '/api/users', auth: { bearer: USER_TOKENS['admin'], }, }) .its('body') .then((response) => { let users = response; for (var key in users) { if (users[key].Username == username) { cy.request({ method: 'DELETE', url: '/api/users/' + users[key].Id, auth: { bearer: USER_TOKENS['admin'], }, }); } } }); }); Cypress.Commands.add('deleteUsers', () => { cy.request({ method: 'GET', url: '/api/users', auth: { bearer: USER_TOKENS['admin'], }, }) .its('body') .then((response) => { let users = response; for (var key in users) { if (users[key].Id != 1) { cy.request({ method: 'DELETE', url: '/api/users/' + users[key].Id, auth: { bearer: USER_TOKENS['admin'], }, }); } } }); }); Cypress.Commands.add('createTeam', (location, teamName) => { if (location == 'frontend') { // Setup team route to wait for response cy.route2('POST', '**/teams').as('teams'); cy.visit('/#!/teams'); cy.get('#team_name').click().type(teamName); cy.get('.btn-primary').click(); cy.wait('@teams'); } else { cy.request({ method: 'POST', url: '/api/teams', failOnStatusCode: false, auth: { bearer: USER_TOKENS['admin'], }, body: { Name: teamName, }, }); } }); Cypress.Commands.add('deleteTeam', (teamName) => { cy.request({ method: 'GET', url: '/api/teams', auth: { bearer: USER_TOKENS['admin'], }, }) .its('body') .then((response) => { let teams = response; for (var key in teams) { if (teams[key].Name == teamName) { cy.request({ method: 'DELETE', url: '/api/teams/' + teams[key].Id, auth: { bearer: USER_TOKENS['admin'], }, }); } } }); }); Cypress.Commands.add('deleteTeams', () => { cy.request({ method: 'GET', url: '/api/teams', auth: { bearer: USER_TOKENS['admin'], }, }) .its('body') .then((response) => { let teams = response; for (var key in teams) { cy.request({ method: 'DELETE', url: '/api/teams/' + teams[key].Id, auth: { bearer: USER_TOKENS['admin'], }, }); } }); }); // Navigate to teams view and assign a user to a team Cypress.Commands.add('assignToTeam', (username, teamName) => { cy.visit('/#!/teams'); // Click team to browse to related team details view cy.clickLink(teamName); // Get users table and execute within cy.waitUntil(() => cy.contains('.widget', 'Users')).within(() => { cy.contains('td', ' ' + username + ' ') .children('span') .click(); }); }); // Navigate to the endpoints view and give the user/team access Cypress.Commands.add('assignAccess', (endpointName, entityName, entityType, role) => { cy.visit('/#!/endpoints'); cy.contains('tr', endpointName).within(() => { cy.clickLink('Manage access'); }); // Click user/team dropdown cy.waitUntil(() => cy.get('.multiSelect > .ng-binding')).click(); // Assign based on entity type var type; if (entityType == 'team') { type = 'fa-users'; } else { type = 'fa-user'; } cy.get('.' + type) .parent() .contains(entityName) .click(); cy.get('.multiSelect > .ng-binding').click(); // If a role is provided, click role dropdown and select role if (role) { cy.get('.form-control:nth-child(1)').select(role); } // Click Create access button cy.get('button[type=submit]').click(); }); Cypress.Commands.add('createStack', (location, resourceName, waitForRedirection = true) => { if (location == 'frontend') { cy.visit(`/#!/${ACTIVE_ENDPOINT_ID}/docker/stacks/newstack`); cy.waitUntil(() => cy.get('#stack_name')) .click() .type(resourceName); if (ACTIVE_ENDPOINT_TYPE == '1') { cy.get('.CodeMirror-scroll') .click({ force: true }) .type("version: '2'") .type('{enter}') .type('services:') .type('{enter}') .type(' test:') .type('{enter}') .type(' image: nginx'); } else { cy.get('.CodeMirror-scroll') .click({ force: true }) .type("version: '3'") .type('{enter}') .type('services:') .type('{enter}') .type(' test:') .type('{enter}') .type(' image: nginx'); } cy.contains('Deploy the stack').click(); // Wait for redirection to stacks view if (waitForRedirection) cy.waitUntil(() => cy.contains('Stacks list', { timeout: 60000 })); } }); Cypress.Commands.add('deleteStack', (location, resourceName) => { if (location == 'frontend') { cy.visit(`/#!/${ACTIVE_ENDPOINT_ID}/docker/stacks`); cy.waitUntil(() => cy.contains('Stacks list', { timeout: 120000 })).showAllResources(); cy.contains(new RegExp('^' + resourceName + '$', 'g')) .closest('tr') .within(() => { cy.get('input[type=checkbox]').click(); }); cy.contains('Remove').click(); cy.get('.modal-dialog').within(() => { cy.get('button[class="btn btn-danger bootbox-accept"]').click(); }); cy.waitUntil(() => cy.contains('Stack successfully removed')); } else { cy.log('Delete stack via API'); } }); Cypress.Commands.add('createService', (location, resourceName, waitForRedirection = true) => { if (location == 'frontend') { cy.visit(`/#!/${ACTIVE_ENDPOINT_ID}/docker/services/new`); cy.waitUntil(() => cy.get('#service_name')) .click() .type(resourceName); cy.get('input[name=image_name]').type('nginx:alpine'); cy.contains('Create the service').click(); // Wait for redirection to services view if (waitForRedirection) cy.waitUntil(() => cy.contains('Service list', { timeout: 120000 })); } }); Cypress.Commands.add('deleteService', (location, resourceName) => { if (location == 'frontend') { cy.visit(`/#!/${ACTIVE_ENDPOINT_ID}/docker/services`); cy.waitUntil(() => cy.contains('Service list', { timeout: 120000 })).showAllResources(); cy.contains(new RegExp('^' + resourceName + '$', 'g')) .closest('tr') .within(() => { cy.get('input[type=checkbox]').click(); }); cy.contains('Remove').click(); cy.get('.modal-dialog').within(() => { cy.get('button[class="btn btn-danger bootbox-accept"]').click(); }); cy.waitUntil(() => cy.contains('Service successfully removed')); } else { cy.log('Delete service via API'); } }); Cypress.Commands.add('createContainer', (location, resourceName, waitForRedirection = true) => { if (location == 'frontend') { cy.visit(`/#!/${ACTIVE_ENDPOINT_ID}/docker/containers/new`); cy.waitUntil(() => cy.get('#container_name')) .click() .type(resourceName); cy.get('input[name=image_name]').type('nginx:alpine'); cy.contains('Deploy the container').click(); // Wait for redirection to containers view if (waitForRedirection) cy.waitUntil(() => cy.contains('Container list', { timeout: 120000 })); } }); Cypress.Commands.add('deleteContainer', (location, resourceName) => { if (location == 'frontend') { cy.visit(`/#!/${ACTIVE_ENDPOINT_ID}/docker/containers`); cy.waitUntil(() => cy.contains('Container list', { timeout: 120000 })).showAllResources(); cy.contains(new RegExp('^' + resourceName + '$', 'g')) .closest('tr') .within(() => { cy.get('input[type=checkbox]').click(); }); cy.contains('Remove').click(); cy.get('.modal-dialog').within(() => { cy.get('button[class="btn btn-danger bootbox-accept"]').click(); }); cy.waitUntil(() => cy.contains('Container successfully removed')); } else { cy.log('Delete container via API'); } }); Cypress.Commands.add('createNetwork', (location, resourceName, waitForRedirection = true) => { if (location == 'frontend') { cy.visit(`/#!/${ACTIVE_ENDPOINT_ID}/docker/networks/new`); cy.waitUntil(() => cy.get('#network_name')) .click() .type(resourceName); cy.contains('Create the network').click(); // Wait for redirection to networks view if (waitForRedirection) cy.waitUntil(() => cy.contains('Network list', { timeout: 120000 })); } }); Cypress.Commands.add('deleteNetwork', (location, resourceName) => { if (location == 'frontend') { cy.visit(`/#!/${ACTIVE_ENDPOINT_ID}/docker/networks`); cy.waitUntil(() => cy.contains('Network list', { timeout: 120000 })).showAllResources(); cy.contains(new RegExp('^' + resourceName + '$', 'g')) .closest('tr') .within(() => { cy.get('input[type=checkbox]').click(); }); cy.contains('Remove').click(); cy.waitUntil(() => cy.contains('Network successfully removed')); } else { } }); Cypress.Commands.add('createVolume', (location, resourceName, waitForRedirection = true) => { if (location == 'frontend') { cy.visit(`/#!/${ACTIVE_ENDPOINT_ID}/docker/volumes/new`); cy.waitUntil(() => cy.get('#volume_name')) .click() .type(resourceName); cy.contains('Create the volume').click(); // Wait for redirection to volumes view if (waitForRedirection) cy.waitUntil(() => cy.contains('Volume list', { timeout: 120000 })); } }); Cypress.Commands.add('deleteVolume', (location, resourceName) => { if (location == 'frontend') { cy.visit(`/#!/${ACTIVE_ENDPOINT_ID}/docker/volumes`); cy.waitUntil(() => cy.contains('Volume list', { timeout: 120000 })).showAllResources(); cy.contains(new RegExp('^' + resourceName + '$', 'g')) .closest('tr') .within(() => { cy.get('input[type=checkbox]').click(); }); cy.contains('Remove').click(); cy.waitUntil(() => cy.contains('Volume successfully removed')); } else { } }); Cypress.Commands.add('createConfig', (location, resourceName, waitForRedirection = true) => { if (location == 'frontend') { cy.visit(`/#!/${ACTIVE_ENDPOINT_ID}/docker/configs/new`); cy.waitUntil(() => cy.get('#config_name')) .click() .type(resourceName); cy.waitUntil(() => cy.get('.CodeMirror-scroll')) .click() .type('This is a config'); cy.get('button').contains('Create config').click(); // Wait for redirection to configs view if (waitForRedirection) cy.waitUntil(() => cy.contains('Configs list', { timeout: 120000 })); } }); Cypress.Commands.add('deleteConfig', (location, resourceName) => { if (location == 'frontend') { cy.visit(`/#!/${ACTIVE_ENDPOINT_ID}/docker/configs`); cy.waitUntil(() => cy.contains('Configs list', { timeout: 120000 })).showAllResources(); cy.contains(new RegExp('^' + resourceName + '$', 'g')) .closest('tr') .within(() => { cy.get('input[type=checkbox]').click(); }); cy.contains('Remove').click(); cy.waitUntil(() => cy.contains('Config successfully removed')); } else { } }); Cypress.Commands.add('createSecret', (location, resourceName, waitForRedirection = true) => { if (location == 'frontend') { cy.visit(`/#!/${ACTIVE_ENDPOINT_ID}/docker/secrets/new`); cy.waitUntil(() => cy.get('#secret_name')) .click() .type(resourceName); cy.waitUntil(() => cy.get('textarea')) .click() .type('This is a secret'); cy.contains('Create the secret').click(); // Wait for redirection to secrets view if (waitForRedirection) cy.waitUntil(() => cy.contains('Secrets list', { timeout: 120000 })); } }); Cypress.Commands.add('deleteSecret', (location, resourceName) => { if (location == 'frontend') { cy.visit(`/#!/${ACTIVE_ENDPOINT_ID}/docker/secrets`); cy.waitUntil(() => cy.contains('Secrets list', { timeout: 120000 })).showAllResources(); cy.contains(new RegExp('^' + resourceName + '$', 'g')) .closest('tr') .within(() => { cy.get('input[type=checkbox]').click(); }); cy.contains('Remove').click(); cy.waitUntil(() => cy.contains('Secret successfully removed')); } else { } }); Cypress.Commands.add('modifyResource', (location, action, resourceType, resourceName) => { // Dynamically call a custom cypress method on a resource of type 'resourceType' cy[action + resourceType](location, resourceName); }); // Method for modifying all resources in an endpoint with the default names Cypress.Commands.add('modifyResources', (location, action) => { const associatedResources = { 1: ['Stack', 'Container', 'Network', 'Volume'], 2: ['Stack', 'Service', 'Container', 'Network', 'Volume', 'Config', 'Secret'], 3: ['Application'], }; for (var res in associatedResources[ACTIVE_ENDPOINT_TYPE]) { let resource = associatedResources[ACTIVE_ENDPOINT_TYPE][res]; cy.modifyResource(location, action, resource, resource.toLowerCase()); } }); Cypress.Commands.add('clickLink', (label) => { cy.waitUntil(() => cy.contains('a', label)).click(); }); Cypress.Commands.add('showAllResources', () => { cy.waitUntil(() => cy.contains('.limitSelector', 'Items per page')).within(() => { cy.get('select').select('All'); }); });