feat(project): refactor e2e testing (#4341)
* feat(project): refactor e2e testing * feat(project): remove example text * feat(project): add missing newlines Co-authored-by: owner <owner@pop-os.localdomain>pull/4349/head
parent
466bd24648
commit
d4c4c4e895
|
@ -102,6 +102,8 @@
|
|||
"clean-webpack-plugin": "^0.1.19",
|
||||
"css-loader": "^1.0.0",
|
||||
"cssnano": "^3.10.0",
|
||||
"cypress": "^5.2.0",
|
||||
"cypress-wait-until": "^1.7.1",
|
||||
"eslint": "5.16.0",
|
||||
"eslint-config-prettier": "^6.10.1",
|
||||
"eslint-loader": "^2.1.2",
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
version: '3'
|
||||
services:
|
||||
agent:
|
||||
image: portainer/agent
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
- /var/lib/docker/volumes:/var/lib/docker/volumes
|
||||
ports:
|
||||
- 9001:9001
|
||||
networks:
|
||||
- agent_network
|
||||
deploy:
|
||||
mode: global
|
||||
placement:
|
||||
constraints: [node.platform.os == linux]
|
||||
networks:
|
||||
agent_network:
|
||||
driver: overlay
|
|
@ -1,4 +1,6 @@
|
|||
{
|
||||
"projectId": "cgz5j5",
|
||||
"video": false
|
||||
"baseUrl": "http://localhost:9000",
|
||||
"video": false,
|
||||
"experimentalNetworkStubbing": true
|
||||
}
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
{}
|
|
@ -0,0 +1,19 @@
|
|||
context('Init admin user test', () => {
|
||||
beforeEach(() => {
|
||||
cy.restoreLocalStorage();
|
||||
});
|
||||
describe('Initialise admin user and endpoint', function () {
|
||||
it('Create admin user and verify success', function () {
|
||||
cy.initAdmin('admin', 'portainer');
|
||||
cy.url().should('include', 'init/endpoint');
|
||||
cy.saveLocalStorage();
|
||||
});
|
||||
it('Select local docker endpoint and init', function () {
|
||||
cy.initEndpoint();
|
||||
cy.url().should('include', 'home');
|
||||
});
|
||||
it('Add docker swarm endpoint', function () {
|
||||
cy.addNewEndpoint('swarm', 'Agent', 'e2e-portainer:9001');
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,31 @@
|
|||
context('Resting of minimum viable functionality against docker standalone', () => {
|
||||
before(() => {
|
||||
cy.visit('/');
|
||||
});
|
||||
|
||||
after(() => {});
|
||||
|
||||
describe('Manipulating resources as admin', function () {
|
||||
beforeEach(() => {
|
||||
cy.visit('/');
|
||||
cy.auth('frontend', 'admin', 'portainer');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
// Clean Tokens
|
||||
cy.clearUserTokens();
|
||||
});
|
||||
|
||||
it('Login and create resources as admin', function () {
|
||||
cy.visit('/');
|
||||
cy.selectEndpoint('local');
|
||||
cy.modifyResources('frontend', 'create');
|
||||
});
|
||||
|
||||
it('Login and delete resources as admin', function () {
|
||||
cy.visit('/');
|
||||
cy.selectEndpoint('local');
|
||||
cy.modifyResources('frontend', 'delete');
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,31 @@
|
|||
context('Testing of minimum viable functionality against docker swarm', () => {
|
||||
before(() => {
|
||||
cy.visit('/');
|
||||
});
|
||||
|
||||
after(() => {});
|
||||
|
||||
describe('Manipulating resources as admin', function () {
|
||||
beforeEach(() => {
|
||||
cy.visit('/');
|
||||
cy.auth('frontend', 'admin', 'portainer');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
// Clean Tokens
|
||||
cy.clearUserTokens();
|
||||
});
|
||||
|
||||
it('Login and create resources as admin', function () {
|
||||
cy.visit('/');
|
||||
cy.selectEndpoint('swarm');
|
||||
cy.modifyResources('frontend', 'create');
|
||||
});
|
||||
|
||||
it('Login and delete resources as admin', function () {
|
||||
cy.visit('/');
|
||||
cy.selectEndpoint('swarm');
|
||||
cy.modifyResources('frontend', 'delete');
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,16 @@
|
|||
context('Init admin & local docker endpoint', () => {
|
||||
beforeEach(() => {
|
||||
cy.restoreLocalStorage();
|
||||
});
|
||||
describe('Initialise admin user and endpoint', function () {
|
||||
it('Create admin user and verify success', function () {
|
||||
cy.initAdmin('admin', 'portainer');
|
||||
cy.url().should('include', 'init/endpoint');
|
||||
cy.saveLocalStorage();
|
||||
});
|
||||
it('Select local docker endpoint and init', function () {
|
||||
cy.initEndpoint();
|
||||
cy.url().should('include', 'home');
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,40 @@
|
|||
context('Standard RBAC tests against docker standalone', () => {
|
||||
before(() => {
|
||||
cy.visit('/');
|
||||
});
|
||||
|
||||
after(() => {});
|
||||
|
||||
describe('Validate endpoint admin functionality', function () {
|
||||
beforeEach(() => {
|
||||
cy.visit('/');
|
||||
cy.auth('frontend', 'admin', 'portainer');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
// Cleanup remaining users and teams
|
||||
cy.deleteUsers();
|
||||
cy.deleteTeams();
|
||||
// Clean Tokens
|
||||
cy.clearUserTokens();
|
||||
});
|
||||
|
||||
it('User assigned as endpoint-admin against an endpoint', function () {
|
||||
// Create and assign user as the administrator
|
||||
cy.createUser('frontend', 'adam', 'portainer');
|
||||
cy.assignAccess('local', 'adam', 'user');
|
||||
cy.clearBrowserToken();
|
||||
|
||||
// Login and create, read, update, delete resources as user
|
||||
cy.visit('/');
|
||||
cy.auth('frontend', 'adam', 'portainer');
|
||||
cy.selectEndpoint('local');
|
||||
|
||||
// create resources
|
||||
cy.modifyResources('frontend', 'create');
|
||||
// modify resources
|
||||
// delete resources
|
||||
cy.modifyResources('frontend', 'delete');
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,40 @@
|
|||
context('Standard RBAC tests against docker swarm', () => {
|
||||
before(() => {
|
||||
cy.visit('/');
|
||||
});
|
||||
|
||||
after(() => {});
|
||||
|
||||
describe('Validate endpoint admin functionality', function () {
|
||||
beforeEach(() => {
|
||||
cy.visit('/');
|
||||
cy.auth('frontend', 'admin', 'portainer');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
// Cleanup remaining users and teams
|
||||
cy.deleteUsers();
|
||||
cy.deleteTeams();
|
||||
// Clean Tokens
|
||||
cy.clearUserTokens();
|
||||
});
|
||||
|
||||
it('User assigned as endpoint-admin against an endpoint', function () {
|
||||
// Create and assign user as the administrator
|
||||
cy.createUser('frontend', 'adam', 'portainer');
|
||||
cy.assignAccess('swarm', 'adam', 'user');
|
||||
cy.clearBrowserToken();
|
||||
|
||||
// Login and create, read, update, delete resources as user
|
||||
cy.visit('/');
|
||||
cy.auth('frontend', 'adam', 'portainer');
|
||||
cy.selectEndpoint('swarm');
|
||||
|
||||
// create resources
|
||||
cy.modifyResources('frontend', 'create');
|
||||
// modify resources
|
||||
// delete resources
|
||||
cy.modifyResources('frontend', 'delete');
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,16 +0,0 @@
|
|||
// Tests to run
|
||||
context('Tests to run', () => {
|
||||
//Browse to homepage before each test
|
||||
beforeEach(() => {
|
||||
cy.visit('/');
|
||||
});
|
||||
describe('Init admin', function () {
|
||||
it('Create user and verify success', function () {
|
||||
cy.get('#username').should('have.value', 'admin');
|
||||
cy.get('#password').type('portaineriscool').should('have.value', 'portaineriscool');
|
||||
cy.get('#confirm_password').type('portaineriscool').should('have.value', 'portaineriscool');
|
||||
cy.get('[type=submit]').click();
|
||||
cy.url().should('include', '/init/endpoint');
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,25 +1,575 @@
|
|||
// ***********************************************
|
||||
// This example commands.js shows you how to
|
||||
// create various custom commands and overwrite
|
||||
// existing commands.
|
||||
//
|
||||
// For more comprehensive examples of custom
|
||||
// commands please read more here:
|
||||
// https://on.cypress.io/custom-commands
|
||||
// ***********************************************
|
||||
//
|
||||
//
|
||||
// -- This is a parent command --
|
||||
// Cypress.Commands.add("login", (email, password) => { ... })
|
||||
//
|
||||
//
|
||||
// -- This is a child command --
|
||||
// Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... })
|
||||
//
|
||||
//
|
||||
// -- This is a dual command --
|
||||
// Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... })
|
||||
//
|
||||
//
|
||||
// -- This is will overwrite an existing command --
|
||||
// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... })
|
||||
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', () => {
|
||||
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');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,20 +1 @@
|
|||
// ***********************************************************
|
||||
// This example support/index.js is processed and
|
||||
// loaded automatically before your test files.
|
||||
//
|
||||
// This is a great place to put global configuration and
|
||||
// behavior that modifies Cypress.
|
||||
//
|
||||
// You can change the location of this file or turn off
|
||||
// automatically serving support files with the
|
||||
// 'supportFile' configuration option.
|
||||
//
|
||||
// You can read more here:
|
||||
// https://on.cypress.io/configuration
|
||||
// ***********************************************************
|
||||
|
||||
// Import commands.js using ES2015 syntax:
|
||||
import './commands';
|
||||
|
||||
// Alternatively you can use CommonJS syntax:
|
||||
// require('./commands')
|
||||
|
|
|
@ -1,17 +0,0 @@
|
|||
version: '3'
|
||||
services:
|
||||
cypress:
|
||||
image: cypress/included:3.5.0
|
||||
container_name: e2e-cypress
|
||||
command: --record --browser chrome
|
||||
depends_on:
|
||||
- portainer
|
||||
working_dir: /e2e
|
||||
environment:
|
||||
CYPRESS_baseUrl: http://e2e-portainer:9000
|
||||
CYPRESS_RECORD_KEY: ${CYPRESS_RECORD_KEY}
|
||||
volumes:
|
||||
- ./cypress:/e2e/cypress
|
||||
- ./cypress.json:/e2e/cypress.json
|
||||
networks:
|
||||
- e2e-ci
|
|
@ -1,34 +1,16 @@
|
|||
version: '3'
|
||||
services:
|
||||
portainer:
|
||||
image: portainerci/portainer-ee:$PORTAINER_TAG
|
||||
container_name: e2e-portainer
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
networks:
|
||||
- e2e-ci
|
||||
|
||||
cypress:
|
||||
image: cypress/included:3.5.0
|
||||
container_name: e2e-cypress
|
||||
command: --browser chrome
|
||||
depends_on:
|
||||
- portainer
|
||||
working_dir: /e2e
|
||||
environment:
|
||||
CYPRESS_baseUrl: http://e2e-portainer:9000
|
||||
volumes:
|
||||
- ./cypress:/e2e/cypress
|
||||
- ./cypress.json:/e2e/cypress.json
|
||||
networks:
|
||||
- e2e-ci
|
||||
|
||||
manager1:
|
||||
image: docker:dind
|
||||
privileged: true
|
||||
environment:
|
||||
DOCKER_TLS_CERTDIR:
|
||||
hostname: manager1
|
||||
ports:
|
||||
- 9001:9001
|
||||
volumes:
|
||||
- ${PWD}/agent-stack.yml:/agent-stack.yml
|
||||
|
||||
networks:
|
||||
- e2e-ci
|
||||
depends_on:
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
exec_in() { docker-compose exec -T $@; }
|
||||
|
||||
# Up all dinds nodes
|
||||
docker-compose up -d manager1 manager2 worker1 worker2
|
||||
docker-compose up -d
|
||||
|
||||
# Manager1 init
|
||||
exec_in manager1 docker swarm init
|
||||
|
@ -19,11 +19,5 @@ exec_in worker1 docker swarm join --token $TOKEN_WORKER manager1:2377
|
|||
# Worker2 join
|
||||
exec_in worker2 docker swarm join --token $TOKEN_WORKER manager1:2377
|
||||
|
||||
# Run portainer + cypress
|
||||
# Use export CI=1 to run in CI mode
|
||||
if [ -z "${CI}" ];
|
||||
then
|
||||
docker-compose up --exit-code-from cypress
|
||||
else
|
||||
docker-compose -f docker-compose.yml -f docker-compose.ci.yml up --exit-code-from cypress
|
||||
fi
|
||||
# Deploy agent within dind swarm
|
||||
exec_in manager1 docker stack deploy -c agent-stack.yml portainer-agent
|
Loading…
Reference in New Issue