mirror of
https://github.com/ansible/awx.git
synced 2026-01-11 18:09:57 -03:30
Merge pull request #3035 from ansible/e2e-websockets
E2E tests for websockets (and other things) Reviewed-by: https://github.com/softwarefactory-project-zuul[bot]
This commit is contained in:
commit
cf9dffbaf8
@ -16,7 +16,7 @@ const store = {};
|
||||
*
|
||||
* @param endpoint - The REST API url suffix.
|
||||
* @param data - Attributes used to create a new endpoint.
|
||||
* @param [unique=['name']] - An array of keys used to uniquely identify previously
|
||||
* @param [unique] - An array of keys used to uniquely identify previously
|
||||
* created resources from the endpoint.
|
||||
*
|
||||
*/
|
||||
@ -52,7 +52,7 @@ const getOrCreate = (endpoint, data, unique = ['name']) => {
|
||||
|
||||
/* Retrieves an organization, and creates it if it does not exist.
|
||||
*
|
||||
* @param [namespace=session] - A unique name prefix for the organization.
|
||||
* @param [namespace] - A unique name prefix for the organization.
|
||||
*
|
||||
*/
|
||||
const getOrganization = (namespace = session) => getOrCreate('/organizations/', {
|
||||
@ -63,7 +63,7 @@ const getOrganization = (namespace = session) => getOrCreate('/organizations/',
|
||||
/* Retrieves an inventory, and creates it if it does not exist.
|
||||
* Also creates an organization with the same name prefix if needed.
|
||||
*
|
||||
* @param [namespace=session] - A unique name prefix for the inventory.
|
||||
* @param [namespace] - A unique name prefix for the inventory.
|
||||
*
|
||||
*/
|
||||
const getInventory = (namespace = session) => getOrganization(namespace)
|
||||
@ -81,7 +81,7 @@ const getInventory = (namespace = session) => getOrganization(namespace)
|
||||
/* Identical to getInventory except it provides a unique suffix,
|
||||
* "*-inventory-nosource".
|
||||
*
|
||||
* @param[namespace=session] - A unique name prefix for the inventory.
|
||||
* @param[namespace] - A unique name prefix for the inventory.
|
||||
*/
|
||||
const getInventoryNoSource = (namespace = session) => getOrganization(namespace)
|
||||
.then(organization => getOrCreate('/inventories/', {
|
||||
@ -98,7 +98,7 @@ const getInventoryNoSource = (namespace = session) => getOrganization(namespace)
|
||||
/* Retrieves a host with the given name prefix, and creates it if it does not exist.
|
||||
* If an inventory does not exist with the same prefix, it is created as well.
|
||||
*
|
||||
* @param[namespace=session] - A unique name prefix for the host.
|
||||
* @param[namespace] - A unique name prefix for the host.
|
||||
*/
|
||||
const getHost = (namespace = session) => getInventory(namespace)
|
||||
.then(inventory => getOrCreate('/hosts/', {
|
||||
@ -112,7 +112,7 @@ const getHost = (namespace = session) => getInventory(namespace)
|
||||
* does not exist. If an organization does not exist with the same prefix, it is
|
||||
* created as well.
|
||||
*
|
||||
* @param[namespace=session] - A unique name prefix for the host.
|
||||
* @param[namespace] - A unique name prefix for the host.
|
||||
*/
|
||||
const getInventoryScript = (namespace = session) => getOrganization(namespace)
|
||||
.then(organization => getOrCreate('/inventory_scripts/', {
|
||||
@ -126,7 +126,7 @@ const getInventoryScript = (namespace = session) => getOrganization(namespace)
|
||||
* required dependent inventory and inventory script do not exist, they are also
|
||||
* created.
|
||||
*
|
||||
* @param[namespace=session] - A unique name prefix for the inventory source.
|
||||
* @param[namespace] - A unique name prefix for the inventory source.
|
||||
*/
|
||||
const getInventorySource = (namespace = session) => {
|
||||
const promises = [
|
||||
@ -146,7 +146,7 @@ const getInventorySource = (namespace = session) => {
|
||||
|
||||
/* Retrieves an AWS credential, and creates it if it does not exist.
|
||||
*
|
||||
* @param[namespace=session] - A unique name prefix for the AWS credential.
|
||||
* @param[namespace] - A unique name prefix for the AWS credential.
|
||||
*/
|
||||
const getAdminAWSCredential = (namespace = session) => {
|
||||
const promises = [
|
||||
@ -176,7 +176,7 @@ const getAdminAWSCredential = (namespace = session) => {
|
||||
|
||||
/* Retrieves a machine credential, and creates it if it does not exist.
|
||||
*
|
||||
* @param[namespace=session] - A unique name prefix for the machine credential.
|
||||
* @param[namespace] - A unique name prefix for the machine credential.
|
||||
*/
|
||||
const getAdminMachineCredential = (namespace = session) => {
|
||||
const promises = [
|
||||
@ -200,7 +200,7 @@ const getAdminMachineCredential = (namespace = session) => {
|
||||
* If an organization does not exist with the same prefix, it is
|
||||
* created as well.
|
||||
*
|
||||
* @param[namespace=session] - A unique name prefix for the team.
|
||||
* @param[namespace] - A unique name prefix for the team.
|
||||
*/
|
||||
const getTeam = (namespace = session) => getOrganization(namespace)
|
||||
.then(organization => getOrCreate(`/organizations/${organization.id}/teams/`, {
|
||||
@ -213,7 +213,7 @@ const getTeam = (namespace = session) => getOrganization(namespace)
|
||||
* name prefix. If an organization does not exist with the same prefix, it is
|
||||
* created as well.
|
||||
*
|
||||
* @param[namespace=session] - A unique name prefix for the smart inventory.
|
||||
* @param[namespace] - A unique name prefix for the smart inventory.
|
||||
*/
|
||||
const getSmartInventory = (namespace = session) => getOrganization(namespace)
|
||||
.then(organization => getOrCreate('/inventories/', {
|
||||
@ -228,7 +228,7 @@ const getSmartInventory = (namespace = session) => getOrganization(namespace)
|
||||
* name prefix. If an organization does not exist with the same prefix, it is
|
||||
* created as well.
|
||||
*
|
||||
* @param[namespace=session] - A unique name prefix for the notification template.
|
||||
* @param[namespace] - A unique name prefix for the notification template.
|
||||
*/
|
||||
const getNotificationTemplate = (namespace = session) => getOrganization(namespace)
|
||||
.then(organization => getOrCreate(`/organizations/${organization.id}/notification_templates/`, {
|
||||
@ -246,15 +246,21 @@ const getNotificationTemplate = (namespace = session) => getOrganization(namespa
|
||||
* name prefix. If an organization does not exist with the same prefix, it is
|
||||
* created as well.
|
||||
*
|
||||
* @param[namespace=session] - A unique name prefix for the host.
|
||||
* @param[namespace] - A unique name prefix for the host.
|
||||
* @param[scmUrl] - The url of the repository.
|
||||
* @param[scmType] - The type of scm (git, etc.)
|
||||
*/
|
||||
const getProject = (namespace = session) => getOrganization(namespace)
|
||||
const getProject = (
|
||||
namespace = session,
|
||||
scmUrl = 'https://github.com/ansible/ansible-tower-samples',
|
||||
scmType = 'git'
|
||||
) => getOrganization(namespace)
|
||||
.then(organization => getOrCreate(`/organizations/${organization.id}/projects/`, {
|
||||
name: `${namespace}-project`,
|
||||
description: namespace,
|
||||
organization: organization.id,
|
||||
scm_url: 'https://github.com/ansible/ansible-tower-samples',
|
||||
scm_type: 'git'
|
||||
scm_url: `${scmUrl}`,
|
||||
scm_type: `${scmType}`
|
||||
}));
|
||||
|
||||
const waitForJob = endpoint => {
|
||||
@ -297,9 +303,15 @@ const getUpdatedProject = (namespace = session) => getProject(namespace)
|
||||
* name prefix. This function also runs getOrCreate for an inventory,
|
||||
* credential, and project with the same prefix.
|
||||
*
|
||||
* @param[namespace=session] - A unique name prefix for the job template.
|
||||
*/
|
||||
const getJobTemplate = (namespace = session) => {
|
||||
* @param [namespace] - Name prefix for associated dependencies.
|
||||
* @param [playbook] - Playbook for the job template.
|
||||
* @param [name] - Unique name prefix for the job template.
|
||||
* */
|
||||
const getJobTemplate = (
|
||||
namespace = session,
|
||||
playbook = 'hello_world.yml',
|
||||
name = `${namespace}-job-template`
|
||||
) => {
|
||||
const promises = [
|
||||
getInventory(namespace),
|
||||
getAdminMachineCredential(namespace),
|
||||
@ -308,20 +320,26 @@ const getJobTemplate = (namespace = session) => {
|
||||
|
||||
return Promise.all(promises)
|
||||
.then(([inventory, credential, project]) => getOrCreate('/job_templates/', {
|
||||
name: `${namespace}-job-template`,
|
||||
name: `${name}`,
|
||||
description: namespace,
|
||||
inventory: inventory.id,
|
||||
credential: credential.id,
|
||||
project: project.id,
|
||||
playbook: 'hello_world.yml',
|
||||
playbook: `${playbook}`,
|
||||
}));
|
||||
};
|
||||
|
||||
/* Similar to getJobTemplate, except that it also launches the job.
|
||||
*
|
||||
* @param[namespace=session] - A unique name prefix for the host.
|
||||
* @param[namespace] - A unique name prefix for the job and its dependencies.
|
||||
* @param[playbook] - The playbook file to be run by the job template.
|
||||
* @param[name] - A unique name for the job template.
|
||||
*/
|
||||
const getJob = (namespace = session) => getJobTemplate(namespace)
|
||||
const getJob = (
|
||||
namespace = session,
|
||||
playbook = 'hello_world.yml',
|
||||
name = `${namespace}-job-template`
|
||||
) => getJobTemplate(namespace, playbook, name)
|
||||
.then(template => {
|
||||
const launchURL = template.related.launch;
|
||||
return post(launchURL, {}).then(response => {
|
||||
@ -334,7 +352,7 @@ const getJob = (namespace = session) => getJobTemplate(namespace)
|
||||
* name prefix. If an organization does not exist with the same prefix, it is
|
||||
* created as well. A basic workflow node setup is also created.
|
||||
*
|
||||
* @param[namespace=session] - A unique name prefix for the workflow template.
|
||||
* @param[namespace] - A unique name prefix for the workflow template.
|
||||
*/
|
||||
const getWorkflowTemplate = (namespace = session) => {
|
||||
const workflowTemplatePromise = getOrganization(namespace)
|
||||
@ -380,7 +398,7 @@ const getWorkflowTemplate = (namespace = session) => {
|
||||
* name prefix. If an organization does not exist with the same prefix,
|
||||
* it is also created.
|
||||
*
|
||||
* @param[namespace=session] - A unique name prefix for the auditor.
|
||||
* @param[namespace] - A unique name prefix for the auditor.
|
||||
*/
|
||||
const getAuditor = (namespace = session) => getOrganization(namespace)
|
||||
.then(organization => getOrCreate(`/organizations/${organization.id}/users/`, {
|
||||
@ -398,23 +416,15 @@ const getAuditor = (namespace = session) => getOrganization(namespace)
|
||||
* name prefix. If an organization does not exist with the same prefix,
|
||||
* it is also created.
|
||||
*
|
||||
* @param[namespace=session] - A unique name prefix for the user.
|
||||
* @param[namespace] - A unique name prefix for the user's organization.
|
||||
* @param[username] - A unique name for the user.
|
||||
*/
|
||||
const getUser = (namespace = session) => getOrganization(namespace)
|
||||
const getUser = (
|
||||
namespace = session,
|
||||
username = `user-${uuid().substr(0, 8)}`
|
||||
) => getOrganization(namespace)
|
||||
.then(organization => getOrCreate(`/organizations/${organization.id}/users/`, {
|
||||
username: `user-${uuid().substr(0, 8)}`,
|
||||
organization: organization.id,
|
||||
first_name: 'firstname',
|
||||
last_name: 'lastname',
|
||||
email: 'null@ansible.com',
|
||||
is_superuser: false,
|
||||
is_system_auditor: false,
|
||||
password: AWX_E2E_PASSWORD
|
||||
}, ['username']));
|
||||
|
||||
const getUserExact = (namespace = session, name) => getOrganization(namespace)
|
||||
.then(organization => getOrCreate(`/organizations/${organization.id}/users/`, {
|
||||
username: `${name}`,
|
||||
username: `${username}`,
|
||||
organization: organization.id,
|
||||
first_name: 'firstname',
|
||||
last_name: 'lastname',
|
||||
@ -428,7 +438,7 @@ const getUserExact = (namespace = session, name) => getOrganization(namespace)
|
||||
* If a job template or organization does not exist with the same
|
||||
* prefix, they are also created.
|
||||
*
|
||||
* @param[namespace=session] - A unique name prefix for the template admin.
|
||||
* @param[namespace] - A unique name prefix for the template admin.
|
||||
*/
|
||||
const getJobTemplateAdmin = (namespace = session) => {
|
||||
const rolePromise = getJobTemplate(namespace)
|
||||
@ -457,7 +467,7 @@ const getJobTemplateAdmin = (namespace = session) => {
|
||||
* If a job template or organization does not exist with the same
|
||||
* prefix, they are also created.
|
||||
*
|
||||
* @param[namespace=session] - A unique name prefix for the project admin.
|
||||
* @param[namespace] - A unique name prefix for the project admin.
|
||||
*/
|
||||
const getProjectAdmin = (namespace = session) => {
|
||||
const rolePromise = getUpdatedProject(namespace)
|
||||
@ -485,7 +495,7 @@ const getProjectAdmin = (namespace = session) => {
|
||||
/* Retrieves a inventory source schedule, and creates it if it does not exist.
|
||||
* If an inventory source does not exist with the same prefix, it is also created.
|
||||
*
|
||||
* @param[namespace=session] - A unique name prefix for the schedule.
|
||||
* @param[namespace] - A unique name prefix for the schedule.
|
||||
*/
|
||||
const getInventorySourceSchedule = (namespace = session) => getInventorySource(namespace)
|
||||
.then(source => getOrCreate(source.related.schedules, {
|
||||
@ -497,7 +507,7 @@ const getInventorySourceSchedule = (namespace = session) => getInventorySource(n
|
||||
/* Retrieves a job template schedule, and creates it if it does not exist.
|
||||
* If an job template does not exist with the same prefix, it is also created.
|
||||
*
|
||||
* @param[namespace=session] - A unique name prefix for the schedule.
|
||||
* @param[namespace] - A unique name prefix for the schedule.
|
||||
*/
|
||||
const getJobTemplateSchedule = (namespace = session) => getJobTemplate(namespace)
|
||||
.then(template => getOrCreate(template.related.schedules, {
|
||||
@ -529,6 +539,5 @@ module.exports = {
|
||||
getTeam,
|
||||
getUpdatedProject,
|
||||
getUser,
|
||||
getUserExact,
|
||||
getWorkflowTemplate,
|
||||
};
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import {
|
||||
getOrganization,
|
||||
getUserExact,
|
||||
getUser,
|
||||
getTeam,
|
||||
} from '../fixtures';
|
||||
|
||||
@ -53,7 +53,7 @@ const readOrgPermissionResults = `//*[@id="permissions_table"]//*[text()="${name
|
||||
module.exports = {
|
||||
before: (client, done) => {
|
||||
const resources = [
|
||||
getUserExact(namespace, `${namespace}-user`),
|
||||
getUser(namespace, `${namespace}-user`),
|
||||
getOrganization(namespace),
|
||||
getTeam(namespace),
|
||||
];
|
||||
|
||||
78
awx/ui/test/e2e/tests/test-websockets.js
Normal file
78
awx/ui/test/e2e/tests/test-websockets.js
Normal file
@ -0,0 +1,78 @@
|
||||
/* Websocket tests. These tests verify that the sparkline (colored box rows which
|
||||
* display job status) update correctly as the jobs progress.
|
||||
*/
|
||||
|
||||
import {
|
||||
getInventorySource,
|
||||
getProject,
|
||||
getJob
|
||||
} from '../fixtures';
|
||||
|
||||
let data;
|
||||
const spinny = '//*[contains(@class, "spinny")]';
|
||||
const dashboard = '//at-side-nav-item[contains(@name, "DASHBOARD")]';
|
||||
|
||||
// UI elements for recently run job templates on the dashboard.
|
||||
const successfulJt = '//a[contains(text(), "test-websockets-successful")]/../..';
|
||||
const failedJt = '//a[contains(text(), "test-websockets-failed")]/../..';
|
||||
const sparklineIcon = '//div[contains(@class, "SmartStatus-iconContainer")]';
|
||||
|
||||
// Sparkline icon statuses.
|
||||
// Running is blinking green, successful is green, fail/error/cancellation is red.
|
||||
const running = '//div[@ng-show="job.status === \'running\'"]';
|
||||
const success = '//div[contains(@class, "SmartStatus-iconIndicator--success")]';
|
||||
const fail = '//div[contains(@class, "SmartStatus-iconIndicator--failed")]';
|
||||
|
||||
module.exports = {
|
||||
|
||||
before: (client, done) => {
|
||||
// Jobs only display on the dashboard if they have been run at least once.
|
||||
const resources = [
|
||||
getInventorySource('test-websockets'),
|
||||
getProject('test-websockets', 'https://github.com/ansible/test-playbooks'),
|
||||
// launch job templates once before running tests.
|
||||
getJob('test-websockets', 'debug.yml', 'test-websockets-successful', done),
|
||||
getJob('test-websockets', 'fail_unless.yml', 'test-websockets-failed', done)
|
||||
];
|
||||
|
||||
Promise.all(resources)
|
||||
.then(([inventory, project, jt1, jt2]) => {
|
||||
data = { inventory, project, jt1, jt2 };
|
||||
done();
|
||||
});
|
||||
|
||||
client
|
||||
.login()
|
||||
.waitForAngular()
|
||||
.resizeWindow(1200, 1000);
|
||||
},
|
||||
|
||||
'Test job template status updates for a successful job on dashboard': client => {
|
||||
client.useXpath().findThenClick(dashboard);
|
||||
getJob('test-websockets', 'debug.yml', 'test-websockets-successful');
|
||||
client.expect.element(spinny).to.not.be.visible.before(5000);
|
||||
client.expect.element(`${sparklineIcon}[1]${running}`)
|
||||
.to.be.visible.before(5000);
|
||||
|
||||
// Allow a maximum amount of 30 seconds for the job to complete.
|
||||
client.expect.element(`${successfulJt}${sparklineIcon}[1]${success}`)
|
||||
.to.be.present.after(30000);
|
||||
},
|
||||
|
||||
'Test job template status updates for a failed job on dashboard': client => {
|
||||
client.useXpath().findThenClick(dashboard);
|
||||
getJob('test-websockets', 'fail_unless.yml', 'test-websockets-failed');
|
||||
|
||||
client.expect.element(spinny).to.not.be.visible.before(5000);
|
||||
client.expect.element(`${sparklineIcon}[1]${running}`)
|
||||
.to.be.visible.before(5000);
|
||||
|
||||
// Allow a maximum amount of 30 seconds for the job to complete.
|
||||
client.expect.element(`${failedJt}${sparklineIcon}[1]${fail}`)
|
||||
.to.be.present.after(30000);
|
||||
},
|
||||
|
||||
after: client => {
|
||||
client.end();
|
||||
}
|
||||
};
|
||||
Loading…
x
Reference in New Issue
Block a user