mirror of
https://github.com/ansible/awx.git
synced 2024-10-31 06:51:10 +03:00
prevent flake for user e2e
This commit is contained in:
parent
fbc7d1a9f2
commit
b084622c9e
@ -1,8 +1,10 @@
|
||||
/* Utility function to wait for the working spinner to disappear. */
|
||||
exports.command = function waitForSpinny () {
|
||||
const selector = 'div.spinny';
|
||||
this
|
||||
.waitForElementVisible(selector)
|
||||
.waitForElementNotVisible(selector);
|
||||
exports.command = function waitForSpinny (useXpath = false) {
|
||||
let selector = 'div.spinny';
|
||||
if (useXpath) {
|
||||
selector = '//*[contains(@class, "spinny")]';
|
||||
}
|
||||
this.waitForElementVisible(selector);
|
||||
this.waitForElementNotVisible(selector);
|
||||
return this;
|
||||
};
|
||||
|
@ -246,7 +246,7 @@ const waitForJob = endpoint => {
|
||||
const interval = 2000;
|
||||
const statuses = ['successful', 'failed', 'error', 'canceled'];
|
||||
|
||||
let attempts = 20;
|
||||
let attempts = 30;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
(function pollStatus () {
|
||||
@ -437,7 +437,8 @@ const getUser = (
|
||||
// unique substrings are needed to avoid the edge case
|
||||
// where a user and org both exist, but the user is not in the organization.
|
||||
// this ensures a new user is always created.
|
||||
username = `user-${uuid().substr(0, 8)}`
|
||||
username = `user-${uuid().substr(0, 8)}`,
|
||||
isSuperuser = false
|
||||
) => getOrganization(namespace)
|
||||
.then(organization => getOrCreate(`/organizations/${organization.id}/users/`, {
|
||||
username: `${username}-${uuid().substr(0, 8)}`,
|
||||
@ -445,7 +446,7 @@ const getUser = (
|
||||
first_name: 'firstname',
|
||||
last_name: 'lastname',
|
||||
email: 'null@ansible.com',
|
||||
is_superuser: false,
|
||||
is_superuser: `${isSuperuser}`,
|
||||
is_system_auditor: false,
|
||||
password: AWX_E2E_PASSWORD
|
||||
}, ['username']));
|
||||
|
@ -56,8 +56,9 @@ module.exports = {
|
||||
if (application.redirectUris) {
|
||||
this.section.add.setValue('@redirectUris', application.redirectUris);
|
||||
}
|
||||
this.section.add.click('@save'); // flake avoidance. triple click ensures it works.
|
||||
this.section.add.click('@save');
|
||||
this.section.add.click('@save');
|
||||
this.waitForSpinny();
|
||||
this
|
||||
.waitForElementVisible('#alert-modal-msg')
|
||||
.expect.element('#alert-modal-msg').text.contain(application.name);
|
||||
|
@ -4,7 +4,7 @@ const AWX_E2E_CLUSTER_WORKERS = process.env.AWX_E2E_CLUSTER_WORKERS || 0;
|
||||
const AWX_E2E_PASSWORD = process.env.AWX_E2E_PASSWORD || 'password';
|
||||
const AWX_E2E_URL = process.env.AWX_E2E_URL || 'https://localhost:8043';
|
||||
const AWX_E2E_USERNAME = process.env.AWX_E2E_USERNAME || 'awx-e2e';
|
||||
const AWX_E2E_TIMEOUT_ASYNC = process.env.AWX_E2E_TIMEOUT_ASYNC || 90000;
|
||||
const AWX_E2E_TIMEOUT_ASYNC = process.env.AWX_E2E_TIMEOUT_ASYNC || 120000;
|
||||
const AWX_E2E_TIMEOUT_LONG = process.env.AWX_E2E_TIMEOUT_LONG || 10000;
|
||||
const AWX_E2E_TIMEOUT_MEDIUM = process.env.AWX_E2E_TIMEOUT_MEDIUM || 5000;
|
||||
const AWX_E2E_TIMEOUT_SHORT = process.env.AWX_E2E_TIMEOUT_SHORT || 1000;
|
||||
|
@ -1,12 +1,14 @@
|
||||
/* Tests for applications. */
|
||||
import uuid from 'uuid';
|
||||
import { getOrganization } from '../fixtures';
|
||||
|
||||
const row = '.at-List-container .at-Row';
|
||||
const testID = uuid().substr(0, 8);
|
||||
const namespace = 'test-applications';
|
||||
|
||||
const store = {
|
||||
organization: {
|
||||
name: `org-${testID}`
|
||||
name: `${namespace}-organization`
|
||||
},
|
||||
application: {
|
||||
name: `name-${testID}`,
|
||||
@ -17,19 +19,19 @@ const store = {
|
||||
},
|
||||
};
|
||||
|
||||
let data;
|
||||
|
||||
module.exports = {
|
||||
before: (client, done) => {
|
||||
const resources = [getOrganization(namespace)];
|
||||
Promise.all(resources)
|
||||
.then(([org]) => {
|
||||
data = { org };
|
||||
done();
|
||||
});
|
||||
|
||||
client.login();
|
||||
client.waitForAngular();
|
||||
|
||||
client.inject(
|
||||
[store, 'OrganizationModel'],
|
||||
(_store_, Model) => new Model().http.post({ data: _store_.organization }),
|
||||
({ data }) => {
|
||||
store.organization = data;
|
||||
done();
|
||||
}
|
||||
);
|
||||
},
|
||||
'create an application': client => {
|
||||
const applications = client.page.applications();
|
||||
|
@ -4,6 +4,10 @@ import {
|
||||
getTeam,
|
||||
} from '../fixtures';
|
||||
|
||||
import {
|
||||
AWX_E2E_URL
|
||||
} from '../settings';
|
||||
|
||||
const namespace = 'test-org-permissions';
|
||||
|
||||
let data;
|
||||
@ -18,7 +22,6 @@ const modalOrgsSearchBar = '//smart-search[@django-model="organizations"]//input
|
||||
|
||||
const orgsNavTab = "//at-side-nav-item[contains(@name, 'ORGANIZATIONS')]";
|
||||
const teamsNavTab = "//at-side-nav-item[contains(@name, 'TEAMS')]";
|
||||
const usersNavTab = "//at-side-nav-item[contains(@name, 'USERS')]";
|
||||
|
||||
const orgTab = '//div[not(@ng-show="showSection2Container()")]/div[@class="Form-tabHolder"]/div[@ng-click="selectTab(\'organizations\')"]';
|
||||
const teamsTab = '//*[@id="teams_tab"]';
|
||||
@ -40,8 +43,6 @@ const teamsSearchBadgeCount = '//span[contains(@class, "List-titleBadge") and co
|
||||
const teamCheckbox = '//*[@item="team"]//input[@type="checkbox"]';
|
||||
const addUserToTeam = '//*[@aw-tool-tip="Add User"]';
|
||||
|
||||
const trashButton = '//i[contains(@class, "fa-trash")]';
|
||||
const deleteButton = '//*[text()="DELETE"]';
|
||||
const saveButton = '//*[text()="Save"]';
|
||||
|
||||
const addPermission = '//*[@aw-tool-tip="Grant Permission"]';
|
||||
@ -109,6 +110,8 @@ module.exports = {
|
||||
.findThenClick(saveButton)
|
||||
// add team-wide permissions to an organization
|
||||
.findThenClick(orgsNavTab)
|
||||
.navigateTo(`${AWX_E2E_URL}/#/organizations`, false)
|
||||
.waitForElementVisible(searchBar)
|
||||
.clearValue(searchBar)
|
||||
.setValue(searchBar, [orgsText, client.Keys.ENTER])
|
||||
.waitForElementNotVisible(spinny)
|
||||
@ -130,12 +133,6 @@ module.exports = {
|
||||
.waitForElementVisible(verifyTeamPermissions);
|
||||
},
|
||||
after: client => {
|
||||
client
|
||||
.findThenClick(usersNavTab)
|
||||
.setValue(searchBar, [`username.iexact:${data.user.username}`, client.Keys.ENTER])
|
||||
.waitForElementNotVisible(spinny)
|
||||
.findThenClick(trashButton)
|
||||
.findThenClick(deleteButton)
|
||||
.end();
|
||||
client.end();
|
||||
}
|
||||
};
|
||||
|
@ -53,10 +53,7 @@ module.exports = {
|
||||
);
|
||||
client.useXpath().expect.element('//a[text()="test-pagination-job-template-0"]')
|
||||
.to.be.visible.after(AWX_E2E_TIMEOUT_MEDIUM);
|
||||
client
|
||||
.useCss()
|
||||
.waitForSpinny()
|
||||
.findThenClick('.Paginate-controls--next', 'css');
|
||||
client.useCss().findThenClick('.Paginate-controls--next', 'css');
|
||||
|
||||
// Default search sort uses alphanumeric sorting, so template #9 is last
|
||||
client.useXpath().expect.element('//a[text()="test-pagination-job-template-9"]')
|
||||
|
@ -1,9 +1,15 @@
|
||||
/* Tests for the user CRUD operations. */
|
||||
import uuid from 'uuid';
|
||||
import {
|
||||
getAuditor,
|
||||
getOrganization,
|
||||
getUser
|
||||
} from '../fixtures';
|
||||
|
||||
const row = '#users_table .List-tableRow';
|
||||
const testID = uuid().substr(0, 8);
|
||||
|
||||
let data;
|
||||
const store = {
|
||||
organization: {
|
||||
name: `org-${testID}`
|
||||
@ -36,17 +42,21 @@ const store = {
|
||||
|
||||
module.exports = {
|
||||
before: (client, done) => {
|
||||
const resources = [
|
||||
getOrganization(store.organization.name),
|
||||
getAuditor(store.auditor.username),
|
||||
getUser(store.user.username),
|
||||
getUser(store.admin.username, true)
|
||||
];
|
||||
|
||||
Promise.all(resources)
|
||||
.then(([organization, auditor, user, admin]) => {
|
||||
store.organization.name = `${store.organization.name}-organization`;
|
||||
data = { organization, auditor, user, admin };
|
||||
done();
|
||||
});
|
||||
client.login();
|
||||
client.waitForAngular();
|
||||
|
||||
client.inject(
|
||||
[store, 'OrganizationModel'],
|
||||
(_store_, Model) => new Model().http.post({ data: _store_.organization }),
|
||||
({ data }) => {
|
||||
store.organization = data;
|
||||
done();
|
||||
}
|
||||
);
|
||||
},
|
||||
'create a system administrator': (client) => {
|
||||
client.login();
|
||||
|
@ -39,10 +39,10 @@ module.exports = {
|
||||
getProject('test-websockets', 'https://github.com/ansible/test-playbooks'),
|
||||
getOrganization('test-websockets'),
|
||||
// launch job templates once before running tests so that they appear on the dashboard.
|
||||
getJob('test-websockets', 'debug.yml', 'test-websockets-successful', done),
|
||||
getJob('test-websockets', 'fail_unless.yml', 'test-websockets-failed', done),
|
||||
getJob('test-websockets', 'debug.yml', 'test-websockets-successful', true, done),
|
||||
getJob('test-websockets', 'fail_unless.yml', 'test-websockets-failed', true, done),
|
||||
getJobTemplate('test-websockets', 'debug.yml', 'test-ws-split-job-template', true, '2'),
|
||||
getJob('test-websockets', 'debug.yml', 'test-ws-split-job-template', done)
|
||||
getJob('test-websockets', 'debug.yml', 'test-ws-split-job-template', false, done)
|
||||
];
|
||||
|
||||
Promise.all(resources)
|
||||
@ -152,7 +152,7 @@ module.exports = {
|
||||
},
|
||||
'Test job slicing sparkline behavior': client => {
|
||||
client.findThenClick('[ui-sref=dashboard]', 'css');
|
||||
getJob('test-ws-split-job-template', 'debug.yml', 'test-websockets-failed', false);
|
||||
getJob('test-websockets', 'debug.yml', 'test-ws-split-job-template', false);
|
||||
|
||||
client.useXpath().expect.element(`${sparklineIcon}[1]${running}`)
|
||||
.to.be.visible.before(AWX_E2E_TIMEOUT_ASYNC);
|
||||
|
@ -5,14 +5,17 @@ import {
|
||||
getWorkflowTemplate
|
||||
} from '../fixtures';
|
||||
|
||||
import {
|
||||
AWX_E2E_URL,
|
||||
AWX_E2E_TIMEOUT_LONG
|
||||
} from '../settings';
|
||||
|
||||
let data;
|
||||
const spinny = "//*[contains(@class, 'spinny')]";
|
||||
const workflowTemplateNavTab = "//at-side-nav-item[contains(@name, 'TEMPLATES')]";
|
||||
const workflowSelector = "//a[contains(text(), 'test-actions-workflow-template')]";
|
||||
const workflowSelector = "//a[text()='test-actions-workflow-template']";
|
||||
const workflowVisualizerBtn = "//button[contains(@id, 'workflow_job_template_workflow_visualizer_btn')]";
|
||||
const workflowSearchBar = "//input[contains(@class, 'SmartSearch-input')]";
|
||||
const workflowText = 'name.iexact:"test-actions-workflow-template"';
|
||||
const workflowSearchBadgeCount = '//span[contains(@class, "at-Panel-headingTitleBadge") and contains(text(), "1")]';
|
||||
|
||||
const startNodeId = '1';
|
||||
let initialJobNodeId;
|
||||
@ -65,17 +68,21 @@ module.exports = {
|
||||
.login()
|
||||
.waitForAngular()
|
||||
.resizeWindow(1200, 1000)
|
||||
.navigateTo(`${AWX_E2E_URL}/#/templates`, false)
|
||||
.useXpath()
|
||||
.findThenClick(workflowTemplateNavTab)
|
||||
.pause(1500)
|
||||
.waitForElementNotVisible(spinny)
|
||||
.clearValue(workflowSearchBar)
|
||||
.setValue(workflowSearchBar, [workflowText, client.Keys.ENTER])
|
||||
.waitForElementVisible(workflowSearchBadgeCount)
|
||||
.waitForElementNotVisible(spinny)
|
||||
.findThenClick(workflowSelector)
|
||||
.findThenClick(workflowVisualizerBtn)
|
||||
.waitForElementVisible('//*[contains(@class, "WorkflowChart-nameText") and contains(text(), "test-actions-job")]/..');
|
||||
.waitForElementVisible(workflowSearchBar)
|
||||
.setValue(workflowSearchBar, [workflowText])
|
||||
.click('//*[contains(@class, "SmartSearch-searchButton")]')
|
||||
.waitForSpinny(true)
|
||||
.click('//*[contains(@class, "SmartSearch-clearAll")]')
|
||||
.waitForSpinny(true)
|
||||
.setValue(workflowSearchBar, [workflowText])
|
||||
.click('//*[contains(@class, "SmartSearch-searchButton")]')
|
||||
.waitForSpinny(true)
|
||||
.click(workflowSelector)
|
||||
.waitForSpinny(true)
|
||||
.click(workflowVisualizerBtn);
|
||||
client.waitForElementVisible('//*[contains(@class, "WorkflowChart-nameText") and contains(text(), "test-actions-job")]/..');
|
||||
|
||||
// Grab the ids of the nodes
|
||||
client.getAttribute('//*[contains(@class, "WorkflowChart-nameText") and contains(text(), "test-actions-job")]/..', 'id', (res) => {
|
||||
|
@ -150,6 +150,9 @@ module.exports = {
|
||||
client.expect.element('[class=xss]').not.present;
|
||||
|
||||
client.click('#prompt_cancel_btn');
|
||||
if (client.isVisible('#prompt_cancel_btn')) {
|
||||
client.click('#prompt_cancel_btn');
|
||||
}
|
||||
|
||||
client.expect.element('#prompt-header').not.visible;
|
||||
},
|
||||
@ -168,10 +171,8 @@ module.exports = {
|
||||
client.expect.element('#permissions_tab').visible;
|
||||
client.expect.element('#permissions_tab').enabled;
|
||||
|
||||
client.click('#permissions_tab');
|
||||
|
||||
client.expect.element('div.spinny').visible;
|
||||
client.expect.element('div.spinny').not.visible;
|
||||
client.pause(2000);
|
||||
client.findThenClick('#permissions_tab', 'css');
|
||||
|
||||
client.expect.element('#xss').not.present;
|
||||
client.expect.element('[class=xss]').not.present;
|
||||
@ -237,6 +238,7 @@ module.exports = {
|
||||
client.sendKeys('div.at-Panel smart-search input', `id:>${data.notification.id - 1} id:<${data.notification.id + 1}`);
|
||||
client.waitForElementNotPresent('div.at-Panel smart-search .SmartSearch-searchButton--disabled');
|
||||
client.waitForElementNotVisible('.overlay');
|
||||
client.pause(2000);
|
||||
client.click('div.at-Panel smart-search .SmartSearch-searchButton');
|
||||
|
||||
client.expect.element('div.spinny').visible;
|
||||
@ -270,6 +272,9 @@ module.exports = {
|
||||
client.expect.element('[class=xss]').not.present;
|
||||
|
||||
client.click('#prompt_cancel_btn');
|
||||
if (client.isVisible('#prompt_cancel_btn')) {
|
||||
client.click('#prompt_cancel_btn');
|
||||
}
|
||||
|
||||
client.expect.element('#prompt-header').not.visible;
|
||||
},
|
||||
@ -322,6 +327,9 @@ module.exports = {
|
||||
client.expect.element('[class=xss]').not.present;
|
||||
|
||||
client.click('#prompt_cancel_btn');
|
||||
if (client.isVisible('#prompt_cancel_btn')) {
|
||||
client.click('#prompt_cancel_btn');
|
||||
}
|
||||
|
||||
client.expect.element('#prompt-header').not.visible;
|
||||
},
|
||||
@ -376,6 +384,9 @@ module.exports = {
|
||||
client.expect.element('[class=xss]').not.present;
|
||||
|
||||
client.click('#prompt_cancel_btn');
|
||||
if (client.isVisible('#prompt_cancel_btn')) {
|
||||
client.click('#prompt_cancel_btn');
|
||||
}
|
||||
|
||||
client.expect.element('#prompt-header').not.visible;
|
||||
},
|
||||
@ -438,6 +449,9 @@ module.exports = {
|
||||
client.expect.element('[class=xss]').not.present;
|
||||
|
||||
client.click('#prompt_cancel_btn');
|
||||
if (client.isVisible('#prompt_cancel_btn')) {
|
||||
client.click('#prompt_cancel_btn');
|
||||
}
|
||||
|
||||
client.expect.element('#prompt-header').not.visible;
|
||||
},
|
||||
@ -455,6 +469,8 @@ module.exports = {
|
||||
client.expect.element('#permissions_tab').visible;
|
||||
client.expect.element('#permissions_tab').enabled;
|
||||
|
||||
client.pause(1000);
|
||||
client.click('#permissions_tab');
|
||||
client.click('#permissions_tab');
|
||||
|
||||
client.expect.element('div.spinny').visible;
|
||||
@ -563,6 +579,9 @@ module.exports = {
|
||||
client.expect.element('[class=xss]').not.present;
|
||||
|
||||
client.click('#prompt_cancel_btn');
|
||||
if (client.isVisible('#prompt_cancel_btn')) {
|
||||
client.click('#prompt_cancel_btn');
|
||||
}
|
||||
|
||||
client.expect.element('#prompt-header').not.visible;
|
||||
},
|
||||
@ -617,6 +636,9 @@ module.exports = {
|
||||
client.expect.element('[class=xss]').not.present;
|
||||
|
||||
client.click('#prompt_cancel_btn');
|
||||
if (client.isVisible('#prompt_cancel_btn')) {
|
||||
client.click('#prompt_cancel_btn');
|
||||
}
|
||||
|
||||
client.expect.element('#prompt-header').not.visible;
|
||||
},
|
||||
@ -671,6 +693,9 @@ module.exports = {
|
||||
client.expect.element('[class=xss]').not.present;
|
||||
|
||||
client.click('#prompt_cancel_btn');
|
||||
if (client.isVisible('#prompt_cancel_btn')) {
|
||||
client.click('#prompt_cancel_btn');
|
||||
}
|
||||
|
||||
client.expect.element('#prompt-header').not.visible;
|
||||
},
|
||||
|
Loading…
Reference in New Issue
Block a user