text: 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');
    });
  });
  