Merge pull request #1344 from jakemcdermott/e2e-updates

e2e / nightwatch updates
This commit is contained in:
Jake McDermott
2018-02-26 11:58:29 -05:00
committed by GitHub
36 changed files with 276 additions and 107 deletions

View File

@@ -33,13 +33,14 @@
}, },
"devDependencies": { "devDependencies": {
"angular-mocks": "~1.6.6", "angular-mocks": "~1.6.6",
"archiver": "^2.1.1",
"axios": "^0.16.2", "axios": "^0.16.2",
"babel-core": "^6.26.0", "babel-core": "^6.26.0",
"babel-istanbul": "^0.12.2", "babel-istanbul": "^0.12.2",
"babel-loader": "^7.1.2", "babel-loader": "^7.1.2",
"babel-plugin-istanbul": "^4.1.5", "babel-plugin-istanbul": "^4.1.5",
"babel-preset-env": "^1.6.0", "babel-preset-env": "^1.6.0",
"chromedriver": "^2.31.0", "chromedriver": "^2.35.0",
"clean-webpack-plugin": "^0.1.16", "clean-webpack-plugin": "^0.1.16",
"copy-webpack-plugin": "^4.0.1", "copy-webpack-plugin": "^4.0.1",
"css-loader": "^0.28.5", "css-loader": "^0.28.5",
@@ -80,7 +81,7 @@
"load-grunt-configs": "^1.0.0", "load-grunt-configs": "^1.0.0",
"load-grunt-tasks": "^3.5.0", "load-grunt-tasks": "^3.5.0",
"ngtemplate-loader": "^2.0.1", "ngtemplate-loader": "^2.0.1",
"nightwatch": "^0.9.16", "nightwatch": "^0.9.19",
"node-object-hash": "^1.3.0", "node-object-hash": "^1.3.0",
"phantomjs-prebuilt": "^2.1.12", "phantomjs-prebuilt": "^2.1.12",
"time-grunt": "^1.4.0", "time-grunt": "^1.4.0",

View File

@@ -4,7 +4,6 @@
docker exec -i tools_awx_1 sh <<-EOSH docker exec -i tools_awx_1 sh <<-EOSH
awx-manage createsuperuser --noinput --username=awx-e2e --email=null@ansible.com awx-manage createsuperuser --noinput --username=awx-e2e --email=null@ansible.com
awx-manage update_password --username=awx-e2e --password=password awx-manage update_password --username=awx-e2e --password=password
make --directory=/awx_devel DATA_GEN_PRESET=e2e bulk_data
EOSH EOSH
# run all of the tests with a live browser # run all of the tests with a live browser

View File

@@ -2,13 +2,13 @@
version: '2' version: '2'
services: services:
hub: hub:
image: selenium/hub:3.8.1-erbium image: selenium/hub
ports: ports:
- 4444:4444 - 4444:4444
chrome: chrome:
image: selenium/node-chrome:3.8.1-erbium image: selenium/node-chrome
# uncomment the two lines below to make tests watchable at vnc://localhost:secret@localhost:5900 # uncomment the two lines below to make tests watchable at vnc://localhost:secret@localhost:5900
# image: selenium/node-chrome-debug:3.8.1-erbium # image: selenium/node-chrome-debug
# ports: ['5900:5900'] # ports: ['5900:5900']
links: links:
- hub - hub
@@ -18,7 +18,7 @@ services:
HUB_PORT_4444_TCP_ADDR: hub HUB_PORT_4444_TCP_ADDR: hub
HUB_PORT_4444_TCP_PORT: 4444 HUB_PORT_4444_TCP_PORT: 4444
firefox: firefox:
image: selenium/node-firefox:3.8.1-erbium image: selenium/node-firefox
links: links:
- hub - hub
environment: environment:

View File

@@ -1,8 +1,13 @@
exports.command = function navigateTo (url) { const spinny = 'div.spinny';
exports.command = function navigateTo (url, expectSpinny = true) {
this.url('data:,'); // https://github.com/nightwatchjs/nightwatch/issues/1724
this.url(url); this.url(url);
this.waitForElementVisible('div.spinny'); if (expectSpinny) {
this.waitForElementNotVisible('div.spinny'); this.waitForElementVisible(spinny);
this.waitForElementNotVisible(spinny);
}
return this; return this;
}; };

View File

@@ -0,0 +1,57 @@
import { basename } from 'path';
import { EventEmitter } from 'events';
import { inherits } from 'util';
import archiver from 'archiver';
function pushFileToWorker (localFilePath, callback) {
const name = basename(localFilePath);
const push = handler => {
const archive = archiver('zip');
const buffers = [];
archive
.on('data', data => buffers.push(data))
.on('error', err => { throw err; })
.on('finish', () => {
const file = Buffer.concat(buffers).toString('base64');
this.api.session(session => {
const params = {
path: `/session/${session.sessionId}/file`,
method: 'POST',
data: { file },
};
this.client.runProtocolAction(params, handler).send();
});
});
archive.file(localFilePath, { name });
archive.finalize();
};
push(({ status, value }) => {
if (status !== 0) {
throw new Error(value.message);
}
if (typeof callback === 'function') {
callback.call(this, value);
}
this.emit('complete');
});
return this;
}
function PushFileToWorker () { EventEmitter.call(this); }
inherits(PushFileToWorker, EventEmitter);
PushFileToWorker.prototype.command = pushFileToWorker;
module.exports = PushFileToWorker;

View File

@@ -10,24 +10,17 @@ import {
const session = `e2e-${uuid().substr(0, 8)}`; const session = `e2e-${uuid().substr(0, 8)}`;
const store = {}; const store = {};
const getOrCreate = (endpoint, data, unique = ['name', 'username', 'id']) => { const getOrCreate = (endpoint, data, unique = ['name']) => {
const identifier = Object.keys(data).find(key => unique.includes(key)); const identifiers = Object.keys(data).filter(key => unique.indexOf(key) > -1);
if (identifier === undefined) { if (identifiers.length < 1) {
throw new Error('A unique key value must be provided.'); throw new Error('A unique key value must be provided.');
} }
const identity = data[identifier]; const lookup = `${endpoint}/${identifiers.map(key => data[key]).join('-')}`;
const params = Object.assign(...identifiers.map(key => ({ [key]: data[key] })));
store[endpoint] = store[endpoint] || {}; store[lookup] = store[lookup] || get(endpoint, { params })
if (store[endpoint][identity]) {
return store[endpoint][identity].then(created => created.data);
}
const query = { params: { [identifier]: identity } };
store[endpoint][identity] = get(endpoint, query)
.then(res => { .then(res => {
if (res.data.results.length > 1) { if (res.data.results.length > 1) {
return Promise.reject(new Error('More than one matching result.')); return Promise.reject(new Error('More than one matching result.'));
@@ -44,7 +37,7 @@ const getOrCreate = (endpoint, data, unique = ['name', 'username', 'id']) => {
return Promise.reject(new Error(`unexpected response: ${res}`)); return Promise.reject(new Error(`unexpected response: ${res}`));
}); });
return store[endpoint][identity].then(created => created.data); return store[lookup].then(created => created.data);
}; };
const getOrganization = (namespace = session) => getOrCreate('/organizations/', { const getOrganization = (namespace = session) => getOrCreate('/organizations/', {
@@ -57,7 +50,12 @@ const getInventory = (namespace = session) => getOrganization(namespace)
name: `${namespace}-inventory`, name: `${namespace}-inventory`,
description: namespace, description: namespace,
organization: organization.id organization: organization.id
})); }).then(inventory => getOrCreate('/hosts/', {
name: `${namespace}-host`,
description: namespace,
inventory: inventory.id,
variables: JSON.stringify({ ansible_connection: 'local' }),
}, ['name', 'inventory']).then(() => inventory)));
const getHost = (namespace = session) => getInventory(namespace) const getHost = (namespace = session) => getInventory(namespace)
.then(inventory => getOrCreate('/hosts/', { .then(inventory => getOrCreate('/hosts/', {
@@ -65,7 +63,7 @@ const getHost = (namespace = session) => getInventory(namespace)
description: namespace, description: namespace,
inventory: inventory.id, inventory: inventory.id,
variables: JSON.stringify({ ansible_connection: 'local' }), variables: JSON.stringify({ ansible_connection: 'local' }),
})); }, ['name', 'inventory']));
const getInventoryScript = (namespace = session) => getOrganization(namespace) const getInventoryScript = (namespace = session) => getOrganization(namespace)
.then(organization => getOrCreate('/inventory_scripts/', { .then(organization => getOrCreate('/inventory_scripts/', {
@@ -265,8 +263,8 @@ const getWorkflowTemplate = (namespace = session) => {
]; ];
const createSuccessNodes = ([projectNode, jobNode, sourceNode]) => Promise.all([ const createSuccessNodes = ([projectNode, jobNode, sourceNode]) => Promise.all([
getOrCreate(projectNode.related.success_nodes, { id: jobNode.id }), getOrCreate(projectNode.related.success_nodes, { id: jobNode.id }, ['id']),
getOrCreate(jobNode.related.success_nodes, { id: sourceNode.id }), getOrCreate(jobNode.related.success_nodes, { id: sourceNode.id }, ['id']),
]); ]);
return Promise.all(nodes) return Promise.all(nodes)
@@ -287,7 +285,7 @@ const getAuditor = (namespace = session) => getOrganization(namespace)
is_superuser: false, is_superuser: false,
is_system_auditor: true, is_system_auditor: true,
password: AWX_E2E_PASSWORD password: AWX_E2E_PASSWORD
})); }, ['username']));
const getUser = (namespace = session) => getOrganization(namespace) const getUser = (namespace = session) => getOrganization(namespace)
.then(organization => getOrCreate('/users/', { .then(organization => getOrCreate('/users/', {
@@ -299,7 +297,7 @@ const getUser = (namespace = session) => getOrganization(namespace)
is_superuser: false, is_superuser: false,
is_system_auditor: false, is_system_auditor: false,
password: AWX_E2E_PASSWORD password: AWX_E2E_PASSWORD
})); }, ['username']));
const getJobTemplateAdmin = (namespace = session) => { const getJobTemplateAdmin = (namespace = session) => {
const rolePromise = getJobTemplate(namespace) const rolePromise = getJobTemplate(namespace)
@@ -315,7 +313,7 @@ const getJobTemplateAdmin = (namespace = session) => {
is_superuser: false, is_superuser: false,
is_system_auditor: false, is_system_auditor: false,
password: AWX_E2E_PASSWORD password: AWX_E2E_PASSWORD
})); }, ['username']));
const assignRolePromise = Promise.all([userPromise, rolePromise]) const assignRolePromise = Promise.all([userPromise, rolePromise])
.then(([user, role]) => post(`/api/v2/roles/${role.id}/users/`, { id: user.id })); .then(([user, role]) => post(`/api/v2/roles/${role.id}/users/`, { id: user.id }));
@@ -338,7 +336,7 @@ const getProjectAdmin = (namespace = session) => {
is_superuser: false, is_superuser: false,
is_system_auditor: false, is_system_auditor: false,
password: AWX_E2E_PASSWORD password: AWX_E2E_PASSWORD
})); }, ['username']));
const assignRolePromise = Promise.all([userPromise, rolePromise]) const assignRolePromise = Promise.all([userPromise, rolePromise])
.then(([user, role]) => post(`/api/v2/roles/${role.id}/users/`, { id: user.id })); .then(([user, role]) => post(`/api/v2/roles/${role.id}/users/`, { id: user.id }));

View File

@@ -2,6 +2,12 @@ module.exports = {
url () { url () {
return `${this.api.globals.launch_url}/#/activity_stream`; return `${this.api.globals.launch_url}/#/activity_stream`;
}, },
commands: [{
load () {
this.api.url('data:,'); // https://github.com/nightwatchjs/nightwatch/issues/1724
return this.navigate();
},
}],
elements: { elements: {
title: '.List-titleText', title: '.List-titleText',
subtitle: '.List-titleLockup', subtitle: '.List-titleLockup',

View File

@@ -52,6 +52,12 @@ module.exports = {
url () { url () {
return `${this.api.globals.launch_url}/#/credential_types`; return `${this.api.globals.launch_url}/#/credential_types`;
}, },
commands: [{
load () {
this.api.url('data:,'); // https://github.com/nightwatchjs/nightwatch/issues/1724
return this.navigate();
},
}],
sections: { sections: {
header, header,
breadcrumb, breadcrumb,

View File

@@ -216,6 +216,12 @@ module.exports = {
url () { url () {
return `${this.api.globals.launch_url}/#/credentials`; return `${this.api.globals.launch_url}/#/credentials`;
}, },
commands: [{
load () {
this.api.url('data:,'); // https://github.com/nightwatchjs/nightwatch/issues/1724
return this.navigate();
},
}],
sections: { sections: {
header, header,
navigation, navigation,

View File

@@ -116,6 +116,10 @@ module.exports = {
save: 'button[class*="Form-saveButton"]' save: 'button[class*="Form-saveButton"]'
}, },
commands: [{ commands: [{
load () {
this.api.url('data:,'); // https://github.com/nightwatchjs/nightwatch/issues/1724
return this.navigate();
},
selectAdd (name) { selectAdd (name) {
this.api.waitForElementVisible('button span[class="List-dropdownCarat"]'); this.api.waitForElementVisible('button span[class="List-dropdownCarat"]');
this.expect.element('button span[class="List-dropdownCarat"]').enabled; this.expect.element('button span[class="List-dropdownCarat"]').enabled;

View File

@@ -24,6 +24,12 @@ module.exports = {
url () { url () {
return `${this.api.globals.launch_url}/#/inventory_scripts`; return `${this.api.globals.launch_url}/#/inventory_scripts`;
}, },
commands: [{
load () {
this.api.url('data:,'); // https://github.com/nightwatchjs/nightwatch/issues/1724
return this.navigate();
},
}],
sections: { sections: {
header, header,
navigation, navigation,

View File

@@ -6,5 +6,10 @@ module.exports = {
}, },
sections: {}, // TODO: Fill this out sections: {}, // TODO: Fill this out
elements: {}, // TODO: Fill this out elements: {}, // TODO: Fill this out
commands: [], // TODO: Fill this out as needed commands: [{
load () {
this.api.url('data:,'); // https://github.com/nightwatchjs/nightwatch/issues/1724
return this.navigate();
},
}],
}; };

View File

@@ -2,6 +2,12 @@ module.exports = {
url () { url () {
return `${this.api.globals.launch_url}/#/login`; return `${this.api.globals.launch_url}/#/login`;
}, },
commands: [{
load () {
this.api.url('data:,'); // https://github.com/nightwatchjs/nightwatch/issues/1724
return this.navigate();
},
}],
elements: { elements: {
username: '#login-username', username: '#login-username',
password: '#login-password', password: '#login-password',

View File

@@ -29,6 +29,12 @@ module.exports = {
url () { url () {
return `${this.api.globals.launch_url}/#/notification_templates`; return `${this.api.globals.launch_url}/#/notification_templates`;
}, },
commands: [{
load () {
this.api.url('data:,'); // https://github.com/nightwatchjs/nightwatch/issues/1724
return this.navigate();
},
}],
sections: { sections: {
header, header,
navigation, navigation,

View File

@@ -26,6 +26,12 @@ module.exports = {
url () { url () {
return `${this.api.globals.launch_url}/#/organizations`; return `${this.api.globals.launch_url}/#/organizations`;
}, },
commands: [{
load () {
this.api.url('data:,'); // https://github.com/nightwatchjs/nightwatch/issues/1724
return this.navigate();
},
}],
sections: { sections: {
header, header,
navigation, navigation,

View File

@@ -25,6 +25,12 @@ module.exports = {
url () { url () {
return `${this.api.globals.launch_url}/#/projects`; return `${this.api.globals.launch_url}/#/projects`;
}, },
commands: [{
load () {
this.api.url('data:,'); // https://github.com/nightwatchjs/nightwatch/issues/1724
return this.navigate();
},
}],
sections: { sections: {
header, header,
navigation, navigation,

View File

@@ -23,6 +23,12 @@ module.exports = {
url () { url () {
return `${this.api.globals.launch_url}/#/teams`; return `${this.api.globals.launch_url}/#/teams`;
}, },
commands: [{
load () {
this.api.url('data:,'); // https://github.com/nightwatchjs/nightwatch/issues/1724
return this.navigate();
},
}],
sections: { sections: {
header, header,
navigation, navigation,

View File

@@ -116,6 +116,10 @@ module.exports = {
save: 'button[class*="Form-saveButton"]' save: 'button[class*="Form-saveButton"]'
}, },
commands: [{ commands: [{
load () {
this.api.url('data:,'); // https://github.com/nightwatchjs/nightwatch/issues/1724
return this.navigate();
},
clickWhenEnabled (selector) { clickWhenEnabled (selector) {
this.api.waitForElementVisible(selector); this.api.waitForElementVisible(selector);
this.expect.element(selector).enabled; this.expect.element(selector).enabled;

View File

@@ -23,6 +23,12 @@ module.exports = {
url () { url () {
return `${this.api.globals.launch_url}/#/users`; return `${this.api.globals.launch_url}/#/users`;
}, },
commands: [{
load () {
this.api.url('data:,'); // https://github.com/nightwatchjs/nightwatch/issues/1724
return this.navigate();
},
}],
sections: { sections: {
header, header,
navigation, navigation,

View File

@@ -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_PASSWORD = process.env.AWX_E2E_PASSWORD || 'password';
const AWX_E2E_URL = process.env.AWX_E2E_URL || 'https://localhost:8043'; 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_USERNAME = process.env.AWX_E2E_USERNAME || 'awx-e2e';
const AWX_E2E_TIMEOUT_ASYNC = process.env.AWX_E2E_TIMEOUT_ASYNC || 30000; const AWX_E2E_TIMEOUT_ASYNC = process.env.AWX_E2E_TIMEOUT_ASYNC || 90000;
const AWX_E2E_TIMEOUT_LONG = process.env.AWX_E2E_TIMEOUT_LONG || 10000; 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_MEDIUM = process.env.AWX_E2E_TIMEOUT_MEDIUM || 5000;
const AWX_E2E_TIMEOUT_SHORT = process.env.AWX_E2E_TIMEOUT_SHORT || 1000; const AWX_E2E_TIMEOUT_SHORT = process.env.AWX_E2E_TIMEOUT_SHORT || 1000;

View File

@@ -58,7 +58,8 @@ module.exports = {
projects.section.list.waitForElementVisible('@add'); projects.section.list.waitForElementVisible('@add');
projects.section.list.expect.element('@add').enabled; projects.section.list.expect.element('@add').enabled;
projects.section.list.click('@add');
client.pause(1000); projects.section.list.click('@add');
projects.waitForElementVisible('label[for="name"] + div input'); projects.waitForElementVisible('label[for="name"] + div input');
projects.waitForElementVisible('label[for="organization"] + div input'); projects.waitForElementVisible('label[for="organization"] + div input');
@@ -269,6 +270,7 @@ module.exports = {
templates.waitForElementVisible('smart-search input'); templates.waitForElementVisible('smart-search input');
templates.expect.element('smart-search input').enabled; templates.expect.element('smart-search input').enabled;
client.pause(1000).waitForElementNotVisible('div.spinny');
templates.sendKeys('smart-search input', `${TEMPLATE_NAME}${client.Keys.ENTER}`); templates.sendKeys('smart-search input', `${TEMPLATE_NAME}${client.Keys.ENTER}`);
templates.waitForElementVisible('div.spinny'); templates.waitForElementVisible('div.spinny');
templates.waitForElementNotVisible('div.spinny'); templates.waitForElementNotVisible('div.spinny');

View File

@@ -24,13 +24,6 @@ let users;
let inventories; let inventories;
let teams; let teams;
function navigateAndWaitForSpinner (client, url) {
client
.url(url)
.waitForElementVisible('div.spinny')
.waitForElementNotVisible('div.spinny');
}
module.exports = { module.exports = {
before: (client, done) => { before: (client, done) => {
const promises = [ const promises = [
@@ -68,7 +61,9 @@ module.exports = {
}); });
}, },
'verify an auditor\'s credentials inputs are read-only': client => { 'verify an auditor\'s credentials inputs are read-only': client => {
navigateAndWaitForSpinner(client, `${credentials.url()}/${data.adminAWSCredential.id}/`); const url = `${credentials.url()}/${data.adminAWSCredential.id}/`;
client.navigateTo(url);
credentials.section.edit credentials.section.edit
.expect.element('@title').text.contain(data.adminAWSCredential.name); .expect.element('@title').text.contain(data.adminAWSCredential.name);
@@ -76,7 +71,9 @@ module.exports = {
credentials.section.edit.section.details.checkAllFieldsDisabled(); credentials.section.edit.section.details.checkAllFieldsDisabled();
}, },
'verify an auditor\'s inventory scripts inputs are read-only': client => { 'verify an auditor\'s inventory scripts inputs are read-only': client => {
navigateAndWaitForSpinner(client, `${inventoryScripts.url()}/${data.inventoryScript.id}/`); const url = `${inventoryScripts.url()}/${data.inventoryScript.id}/`;
client.navigateTo(url);
inventoryScripts.section.edit inventoryScripts.section.edit
.expect.element('@title').text.contain(data.inventoryScript.name); .expect.element('@title').text.contain(data.inventoryScript.name);
@@ -92,7 +89,7 @@ module.exports = {
// //
// 'verify an auditor\'s job template inputs are read-only': function (client) { // 'verify an auditor\'s job template inputs are read-only': function (client) {
// const url = `${templates.url()}/job_template/${data.jobTemplate.id}/`; // const url = `${templates.url()}/job_template/${data.jobTemplate.id}/`;
// navigateAndWaitForSpinner(client, url); // client.navigateTo(url)''
// //
// templates.section.editJobTemplate // templates.section.editJobTemplate
// .expect.element('@title').text.contain(data.jobTemplate.name); // .expect.element('@title').text.contain(data.jobTemplate.name);
@@ -103,7 +100,9 @@ module.exports = {
// templates.expect.element('@save').to.not.be.visible; // templates.expect.element('@save').to.not.be.visible;
// }, // },
'verify an auditor\'s notification templates inputs are read-only': client => { 'verify an auditor\'s notification templates inputs are read-only': client => {
navigateAndWaitForSpinner(client, `${notificationTemplates.url()}/${data.notificationTemplate.id}/`); const url = `${notificationTemplates.url()}/${data.notificationTemplate.id}/`;
client.navigateTo(url);
notificationTemplates.section.edit notificationTemplates.section.edit
.expect.element('@title').text.contain(data.notificationTemplate.name); .expect.element('@title').text.contain(data.notificationTemplate.name);
@@ -114,7 +113,9 @@ module.exports = {
notificationTemplates.expect.element('@save').to.not.be.visible; notificationTemplates.expect.element('@save').to.not.be.visible;
}, },
'verify an auditor\'s organizations inputs are read-only': client => { 'verify an auditor\'s organizations inputs are read-only': client => {
navigateAndWaitForSpinner(client, `${organizations.url()}/${data.organization.id}/`); const url = `${organizations.url()}/${data.organization.id}/`;
client.navigateTo(url);
organizations.section.edit organizations.section.edit
.expect.element('@title').text.contain(data.organization.name); .expect.element('@title').text.contain(data.organization.name);
@@ -125,7 +126,9 @@ module.exports = {
organizations.expect.element('@save').to.not.be.visible; organizations.expect.element('@save').to.not.be.visible;
}, },
'verify an auditor\'s smart inventory inputs are read-only': client => { 'verify an auditor\'s smart inventory inputs are read-only': client => {
navigateAndWaitForSpinner(client, `${inventories.url()}/smart/${data.smartInventory.id}/`); const url = `${inventories.url()}/smart/${data.smartInventory.id}/`;
client.navigateTo(url);
inventories.section.editSmartInventory inventories.section.editSmartInventory
.expect.element('@title').text.contain(data.smartInventory.name); .expect.element('@title').text.contain(data.smartInventory.name);
@@ -136,7 +139,9 @@ module.exports = {
inventories.expect.element('@save').to.not.be.visible; inventories.expect.element('@save').to.not.be.visible;
}, },
'verify an auditor\'s project inputs are read-only': client => { 'verify an auditor\'s project inputs are read-only': client => {
navigateAndWaitForSpinner(client, `${projects.url()}/${data.project.id}/`); const url = `${projects.url()}/${data.project.id}/`;
client.navigateTo(url);
projects.section.edit projects.section.edit
.expect.element('@title').text.contain(data.project.name); .expect.element('@title').text.contain(data.project.name);
@@ -147,7 +152,9 @@ module.exports = {
projects.expect.element('@save').to.not.be.visible; projects.expect.element('@save').to.not.be.visible;
}, },
'verify an auditor\'s standard inventory inputs are read-only': client => { 'verify an auditor\'s standard inventory inputs are read-only': client => {
navigateAndWaitForSpinner(client, `${inventories.url()}/inventory/${data.inventory.id}/`); const url = `${inventories.url()}/inventory/${data.inventory.id}/`;
client.navigateTo(url);
inventories.section.editStandardInventory inventories.section.editStandardInventory
.expect.element('@title').text.contain(data.inventory.name); .expect.element('@title').text.contain(data.inventory.name);
@@ -159,7 +166,9 @@ module.exports = {
inventories.expect.element('@save').to.not.be.visible; inventories.expect.element('@save').to.not.be.visible;
}, },
'verify an auditor\'s teams inputs are read-only': client => { 'verify an auditor\'s teams inputs are read-only': client => {
navigateAndWaitForSpinner(client, `${teams.url()}/${data.team.id}/`); const url = `${teams.url()}/${data.team.id}/`;
client.navigateTo(url);
teams.section.edit teams.section.edit
.expect.element('@title').text.contain(data.team.name); .expect.element('@title').text.contain(data.team.name);
@@ -170,7 +179,9 @@ module.exports = {
teams.expect.element('@save').to.not.be.visible; teams.expect.element('@save').to.not.be.visible;
}, },
'verify an auditor\'s user inputs are read-only': client => { 'verify an auditor\'s user inputs are read-only': client => {
navigateAndWaitForSpinner(client, `${users.url()}/${data.user.id}/`); const url = `${users.url()}/${data.user.id}/`;
client.navigateTo(url);
users.section.edit users.section.edit
.expect.element('@title').text.contain(data.user.username); .expect.element('@title').text.contain(data.user.username);

View File

@@ -5,10 +5,7 @@ module.exports = {
client.login(); client.login();
client.waitForAngular(); client.waitForAngular();
credentialTypes credentialTypes.navigateTo(`${credentialTypes.url()}/add/`);
.navigate(`${credentialTypes.url()}/add/`)
.waitForElementVisible('div.spinny')
.waitForElementNotVisible('div.spinny');
credentialTypes.section.add credentialTypes.section.add
.waitForElementVisible('@title', done); .waitForElementVisible('@title', done);

View File

@@ -64,7 +64,9 @@ module.exports = {
const { details } = credentials.section.add.section; const { details } = credentials.section.add.section;
const { gce } = details.section; const { gce } = details.section;
gce.section.serviceAccountFile.setValue('form input[type="file"]', GCE_SERVICE_ACCOUNT_FILE); client.pushFileToWorker(GCE_SERVICE_ACCOUNT_FILE, file => {
gce.section.serviceAccountFile.setValue('form input[type="file"]', file);
});
gce.expect.element('@email').not.enabled; gce.expect.element('@email').not.enabled;
gce.expect.element('@sshKeyData').not.enabled; gce.expect.element('@sshKeyData').not.enabled;
@@ -100,7 +102,9 @@ module.exports = {
const { details } = credentials.section.add.section; const { details } = credentials.section.add.section;
const { gce } = details.section; const { gce } = details.section;
gce.section.serviceAccountFile.setValue('form input[type="file"]', GCE_SERVICE_ACCOUNT_FILE_MISSING); client.pushFileToWorker(GCE_SERVICE_ACCOUNT_FILE_MISSING, file => {
gce.section.serviceAccountFile.setValue('form input[type="file"]', file);
});
gce.expect.element('@email').not.enabled; gce.expect.element('@email').not.enabled;
gce.expect.element('@sshKeyData').not.enabled; gce.expect.element('@sshKeyData').not.enabled;
@@ -142,7 +146,9 @@ module.exports = {
const { details } = credentials.section.add.section; const { details } = credentials.section.add.section;
const { gce } = details.section; const { gce } = details.section;
gce.section.serviceAccountFile.setValue('form input[type="file"]', GCE_SERVICE_ACCOUNT_FILE_INVALID); client.pushFileToWorker(GCE_SERVICE_ACCOUNT_FILE_INVALID, file => {
gce.section.serviceAccountFile.setValue('form input[type="file"]', file);
});
gce.expect.element('@email').not.enabled; gce.expect.element('@email').not.enabled;
gce.expect.element('@sshKeyData').not.enabled; gce.expect.element('@sshKeyData').not.enabled;
@@ -184,7 +190,9 @@ module.exports = {
const add = credentials.section.add.section.details; const add = credentials.section.add.section.details;
const edit = credentials.section.edit.section.details; const edit = credentials.section.edit.section.details;
add.section.gce.section.serviceAccountFile.setValue('form input[type="file"]', GCE_SERVICE_ACCOUNT_FILE); client.pushFileToWorker(GCE_SERVICE_ACCOUNT_FILE, file => {
add.section.gce.section.serviceAccountFile.setValue('form input[type="file"]', file);
});
add.section.gce.expect.element('@email').not.enabled; add.section.gce.expect.element('@email').not.enabled;
add.section.gce.expect.element('@sshKeyData').not.enabled; add.section.gce.expect.element('@sshKeyData').not.enabled;
@@ -224,7 +232,9 @@ module.exports = {
gce.section.project.expect.element('@error').not.present; gce.section.project.expect.element('@error').not.present;
gce.section.serviceAccountFile.expect.element('@error').not.present; gce.section.serviceAccountFile.expect.element('@error').not.present;
gce.section.serviceAccountFile.setValue('form input[type="file"]', GCE_SERVICE_ACCOUNT_FILE_ALT); client.pushFileToWorker(GCE_SERVICE_ACCOUNT_FILE_ALT, file => {
gce.section.serviceAccountFile.setValue('form input[type="file"]', file);
});
gce.expect.element('@serviceAccountFile').enabled; gce.expect.element('@serviceAccountFile').enabled;

View File

@@ -166,18 +166,6 @@ module.exports = {
credentials.section.edit.expect.section('@details').visible; credentials.section.edit.expect.section('@details').visible;
}, },
'credential is searchable after saving': client => {
const credentials = client.page.credentials();
const row = '#credentials_table tbody tr';
credentials.section.list.section.search
.waitForElementVisible('@input', AWX_E2E_TIMEOUT_LONG)
.setValue('@input', `name:${store.credential.name}`)
.click('@searchButton');
credentials.waitForElementNotPresent(`${row}:nth-of-type(2)`);
credentials.expect.element(row).text.contain(store.credential.name);
},
'change the password after saving': client => { 'change the password after saving': client => {
const credentials = client.page.credentials(); const credentials = client.page.credentials();
const { edit } = credentials.section; const { edit } = credentials.section;
@@ -190,6 +178,7 @@ module.exports = {
machine.expect.element('@password').not.enabled; machine.expect.element('@password').not.enabled;
machine.section.password.click('@replace'); machine.section.password.click('@replace');
machine.section.password.expect.element('@replace').not.present; machine.section.password.expect.element('@replace').not.present;
machine.section.password.expect.element('@revert').visible; machine.section.password.expect.element('@revert').visible;
@@ -201,7 +190,19 @@ module.exports = {
credentials credentials
.waitForElementVisible('div.spinny') .waitForElementVisible('div.spinny')
.waitForElementNotVisible('div.spinny'); .waitForElementNotVisible('div.spinny');
},
'credential is searchable after saving': client => {
const credentials = client.page.credentials();
const row = '#credentials_table tbody tr';
credentials.section.list.section.search
.waitForElementVisible('@input', AWX_E2E_TIMEOUT_LONG)
.setValue('@input', `name:${store.credential.name}`)
.click('@searchButton');
credentials.waitForElementNotPresent(`${row}:nth-of-type(2)`);
credentials.expect.element(row).text.contain(store.credential.name);
client.end(); client.end();
} },
}; };

View File

@@ -16,7 +16,7 @@ module.exports = {
client.login(); client.login();
client.waitForAngular(); client.waitForAngular();
credentials.navigate(); credentials.load();
credentials.waitForElementVisible('div.spinny'); credentials.waitForElementVisible('div.spinny');
credentials.waitForElementNotVisible('div.spinny'); credentials.waitForElementNotVisible('div.spinny');

View File

@@ -1,25 +1,32 @@
import { range } from 'lodash';
import { getOrganization } from '../fixtures';
module.exports = { module.exports = {
before: (client, done) => { before: (client, done) => {
const credentials = client.page.credentials(); const resources = range(100).map(n => getOrganization(`test-lookup-${n}`));
const { details } = credentials.section.add.section;
client.login(); Promise.all(resources)
client.waitForAngular(); .then(() => {
const credentials = client.page.credentials();
const { details } = credentials.section.add.section;
credentials.section.navigation client.login();
.waitForElementVisible('@credentials') client.waitForAngular();
.click('@credentials');
credentials credentials.section.navigation.waitForElementVisible('@credentials');
.waitForElementVisible('div.spinny') credentials.section.navigation.click('@credentials');
.waitForElementNotVisible('div.spinny');
credentials.section.list credentials.waitForElementVisible('div.spinny');
.waitForElementVisible('@add') credentials.waitForElementNotVisible('div.spinny');
.click('@add');
details credentials.section.list.waitForElementVisible('@add');
.waitForElementVisible('@save', done); credentials.section.list.click('@add');
details.waitForElementVisible('@save');
done();
});
}, },
'open the lookup modal': client => { 'open the lookup modal': client => {
const credentials = client.page.credentials(); const credentials = client.page.credentials();

View File

@@ -6,7 +6,7 @@ module.exports = {
client.waitForAngular(); client.waitForAngular();
credentials credentials
.navigate() .load()
.waitForElementVisible('div.spinny') .waitForElementVisible('div.spinny')
.waitForElementNotVisible('div.spinny', done); .waitForElementNotVisible('div.spinny', done);
}, },

View File

@@ -10,7 +10,7 @@ module.exports = {
client.waitForAngular(); client.waitForAngular();
credentials credentials
.navigate() .load()
.waitForElementVisible('div.spinny') .waitForElementVisible('div.spinny')
.waitForElementNotVisible('div.spinny'); .waitForElementNotVisible('div.spinny');

View File

@@ -16,7 +16,7 @@ module.exports = {
client.login(); client.login();
client.waitForAngular(); client.waitForAngular();
inventories.navigate(); inventories.load();
inventories.waitForElementVisible('div.spinny'); inventories.waitForElementVisible('div.spinny');
inventories.waitForElementNotVisible('div.spinny'); inventories.waitForElementNotVisible('div.spinny');

View File

@@ -16,7 +16,7 @@ module.exports = {
client.login(); client.login();
client.waitForAngular(); client.waitForAngular();
inventoryScripts.navigate(); inventoryScripts.load();
inventoryScripts.waitForElementVisible('div.spinny'); inventoryScripts.waitForElementVisible('div.spinny');
inventoryScripts.waitForElementNotVisible('div.spinny'); inventoryScripts.waitForElementNotVisible('div.spinny');

View File

@@ -16,7 +16,7 @@ module.exports = {
client.login(); client.login();
client.waitForAngular(); client.waitForAngular();
notifications.navigate(); notifications.load();
notifications.waitForElementVisible('div.spinny'); notifications.waitForElementVisible('div.spinny');
notifications.waitForElementNotVisible('div.spinny'); notifications.waitForElementNotVisible('div.spinny');

View File

@@ -1,10 +1,10 @@
import { getProject } from '../fixtures'; import { getUpdatedProject } from '../fixtures';
const data = {}; const data = {};
module.exports = { module.exports = {
before: (client, done) => { before: (client, done) => {
getProject('test-actions') getUpdatedProject('test-actions')
.then(obj => { data.project = obj; }) .then(obj => { data.project = obj; })
.then(done); .then(done);
}, },
@@ -16,7 +16,7 @@ module.exports = {
client.login(); client.login();
client.waitForAngular(); client.waitForAngular();
projects.navigate(); projects.load();
projects.waitForElementVisible('div.spinny'); projects.waitForElementVisible('div.spinny');
projects.waitForElementNotVisible('div.spinny'); projects.waitForElementNotVisible('div.spinny');

View File

@@ -65,7 +65,7 @@ module.exports = {
client.login(); client.login();
client.waitForAngular(); client.waitForAngular();
templates.navigate(); templates.load();
templates.waitForElementVisible('div.spinny'); templates.waitForElementVisible('div.spinny');
templates.waitForElementNotVisible('div.spinny'); templates.waitForElementNotVisible('div.spinny');
@@ -120,7 +120,7 @@ module.exports = {
client.login(); client.login();
client.waitForAngular(); client.waitForAngular();
templates.navigate(); templates.load();
templates.waitForElementVisible('div.spinny'); templates.waitForElementVisible('div.spinny');
templates.waitForElementNotVisible('div.spinny'); templates.waitForElementNotVisible('div.spinny');
@@ -170,7 +170,7 @@ module.exports = {
client.login(data.user.username); client.login(data.user.username);
client.waitForAngular(); client.waitForAngular();
templates.navigate(); templates.load();
templates.waitForElementVisible('div.spinny'); templates.waitForElementVisible('div.spinny');
templates.waitForElementNotVisible('div.spinny'); templates.waitForElementNotVisible('div.spinny');

View File

@@ -30,7 +30,7 @@ module.exports = {
client.login(); client.login();
client.waitForAngular(); client.waitForAngular();
templates.navigate(); templates.load();
templates.waitForElementVisible('div.spinny'); templates.waitForElementVisible('div.spinny');
templates.waitForElementNotVisible('div.spinny'); templates.waitForElementNotVisible('div.spinny');
@@ -70,7 +70,7 @@ module.exports = {
client.login(); client.login();
client.waitForAngular(); client.waitForAngular();
templates.navigate(); templates.load();
templates.waitForElementVisible('div.spinny'); templates.waitForElementVisible('div.spinny');
templates.waitForElementNotVisible('div.spinny'); templates.waitForElementNotVisible('div.spinny');

View File

@@ -40,7 +40,7 @@ module.exports = {
getTeam(namespace).then(obj => { data.team = obj; }), getTeam(namespace).then(obj => { data.team = obj; }),
getProjectAdmin(namespace).then(obj => { data.user = obj; }), getProjectAdmin(namespace).then(obj => { data.user = obj; }),
getNotificationTemplate(namespace).then(obj => { data.notification = obj; }), getNotificationTemplate(namespace).then(obj => { data.notification = obj; }),
getJob(namespaceShort).then(obj => { data.job = obj; }), getJob(namespace).then(obj => { data.job = obj; }),
]; ];
Promise.all(resources) Promise.all(resources)
@@ -58,7 +58,6 @@ module.exports = {
urls.organization = `${pages.organizations.url()}/${data.organization.id}`; urls.organization = `${pages.organizations.url()}/${data.organization.id}`;
urls.inventory = `${pages.inventories.url()}/inventory/${data.inventory.id}`; urls.inventory = `${pages.inventories.url()}/inventory/${data.inventory.id}`;
urls.inventoryHosts = `${urls.inventory}/hosts`;
urls.inventoryScript = `${pages.inventoryScripts.url()}/${data.inventoryScript.id}`; urls.inventoryScript = `${pages.inventoryScripts.url()}/${data.inventoryScript.id}`;
urls.inventorySource = `${urls.inventory}/inventory_sources/edit/${data.inventorySource.id}`; urls.inventorySource = `${urls.inventory}/inventory_sources/edit/${data.inventorySource.id}`;
urls.sourceSchedule = `${urls.inventorySource}/schedules/${data.sourceSchedule.id}`; urls.sourceSchedule = `${urls.inventorySource}/schedules/${data.sourceSchedule.id}`;
@@ -72,6 +71,7 @@ module.exports = {
urls.notification = `${pages.notificationTemplates.url()}/${data.notification.id}`; urls.notification = `${pages.notificationTemplates.url()}/${data.notification.id}`;
urls.jobs = `${pages.jobs.url()}`; urls.jobs = `${pages.jobs.url()}`;
urls.jobsSchedules = `${pages.jobs.url()}/schedules`; urls.jobsSchedules = `${pages.jobs.url()}/schedules`;
urls.inventoryHosts = `${pages.inventories.url()}/inventory/${data.host.summary_fields.inventory.id}/hosts`;
client.useCss(); client.useCss();
client.login(); client.login();
@@ -85,7 +85,7 @@ module.exports = {
const multiCredentialOpen = 'multi-credential button i[class*="search"]'; const multiCredentialOpen = 'multi-credential button i[class*="search"]';
const multiCredentialExit = 'multi-credential-modal button[class*="exit"]'; const multiCredentialExit = 'multi-credential-modal button[class*="exit"]';
client.url(urls.jobTemplate); client.navigateTo(urls.jobTemplate, false);
client.expect.element('#job_template_form').visible; client.expect.element('#job_template_form').visible;
client.expect.element('#xss').not.present; client.expect.element('#xss').not.present;
@@ -371,7 +371,7 @@ module.exports = {
client.expect.element('#prompt-header').not.visible; client.expect.element('#prompt-header').not.visible;
}, },
'check smart inventory form for unsanitized content': client => { 'check smart inventory form for unsanitized content': client => {
client.url(urls.smartInventory); client.navigateTo(urls.smartInventory, false);
client.expect.element('#smartinventory_form').visible; client.expect.element('#smartinventory_form').visible;
@@ -691,6 +691,14 @@ module.exports = {
const popOver = `${itemRow} td[class*="active_failures-"] div[class*="popover"]`; const popOver = `${itemRow} td[class*="active_failures-"] div[class*="popover"]`;
client.navigateTo(urls.inventoryHosts); client.navigateTo(urls.inventoryHosts);
client.expect.element('div[class^="Panel"] smart-search').visible;
client.expect.element('div[class^="Panel"] smart-search input').enabled;
client.sendKeys('div[class^="Panel"] smart-search input', `id:>${data.host.id - 1} id:<${data.host.id + 1}`);
client.sendKeys('div[class^="Panel"] smart-search input', client.Keys.ENTER);
client.expect.element('div.spinny').visible;
client.expect.element('div.spinny').not.visible;
client.click(itemName); client.click(itemName);
client.expect.element(popOver).present; client.expect.element(popOver).present;