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
commit 7430856ac9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
36 changed files with 276 additions and 107 deletions

View File

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

View File

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

View File

@ -2,13 +2,13 @@
version: '2'
services:
hub:
image: selenium/hub:3.8.1-erbium
image: selenium/hub
ports:
- 4444:4444
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
# image: selenium/node-chrome-debug:3.8.1-erbium
# image: selenium/node-chrome-debug
# ports: ['5900:5900']
links:
- hub
@ -18,7 +18,7 @@ services:
HUB_PORT_4444_TCP_ADDR: hub
HUB_PORT_4444_TCP_PORT: 4444
firefox:
image: selenium/node-firefox:3.8.1-erbium
image: selenium/node-firefox
links:
- hub
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.waitForElementVisible('div.spinny');
this.waitForElementNotVisible('div.spinny');
if (expectSpinny) {
this.waitForElementVisible(spinny);
this.waitForElementNotVisible(spinny);
}
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 store = {};
const getOrCreate = (endpoint, data, unique = ['name', 'username', 'id']) => {
const identifier = Object.keys(data).find(key => unique.includes(key));
const getOrCreate = (endpoint, data, unique = ['name']) => {
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.');
}
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] || {};
if (store[endpoint][identity]) {
return store[endpoint][identity].then(created => created.data);
}
const query = { params: { [identifier]: identity } };
store[endpoint][identity] = get(endpoint, query)
store[lookup] = store[lookup] || get(endpoint, { params })
.then(res => {
if (res.data.results.length > 1) {
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 store[endpoint][identity].then(created => created.data);
return store[lookup].then(created => created.data);
};
const getOrganization = (namespace = session) => getOrCreate('/organizations/', {
@ -57,7 +50,12 @@ const getInventory = (namespace = session) => getOrganization(namespace)
name: `${namespace}-inventory`,
description: namespace,
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)
.then(inventory => getOrCreate('/hosts/', {
@ -65,7 +63,7 @@ const getHost = (namespace = session) => getInventory(namespace)
description: namespace,
inventory: inventory.id,
variables: JSON.stringify({ ansible_connection: 'local' }),
}));
}, ['name', 'inventory']));
const getInventoryScript = (namespace = session) => getOrganization(namespace)
.then(organization => getOrCreate('/inventory_scripts/', {
@ -265,8 +263,8 @@ const getWorkflowTemplate = (namespace = session) => {
];
const createSuccessNodes = ([projectNode, jobNode, sourceNode]) => Promise.all([
getOrCreate(projectNode.related.success_nodes, { id: jobNode.id }),
getOrCreate(jobNode.related.success_nodes, { id: sourceNode.id }),
getOrCreate(projectNode.related.success_nodes, { id: jobNode.id }, ['id']),
getOrCreate(jobNode.related.success_nodes, { id: sourceNode.id }, ['id']),
]);
return Promise.all(nodes)
@ -287,7 +285,7 @@ const getAuditor = (namespace = session) => getOrganization(namespace)
is_superuser: false,
is_system_auditor: true,
password: AWX_E2E_PASSWORD
}));
}, ['username']));
const getUser = (namespace = session) => getOrganization(namespace)
.then(organization => getOrCreate('/users/', {
@ -299,7 +297,7 @@ const getUser = (namespace = session) => getOrganization(namespace)
is_superuser: false,
is_system_auditor: false,
password: AWX_E2E_PASSWORD
}));
}, ['username']));
const getJobTemplateAdmin = (namespace = session) => {
const rolePromise = getJobTemplate(namespace)
@ -315,7 +313,7 @@ const getJobTemplateAdmin = (namespace = session) => {
is_superuser: false,
is_system_auditor: false,
password: AWX_E2E_PASSWORD
}));
}, ['username']));
const assignRolePromise = Promise.all([userPromise, rolePromise])
.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_system_auditor: false,
password: AWX_E2E_PASSWORD
}));
}, ['username']));
const assignRolePromise = Promise.all([userPromise, rolePromise])
.then(([user, role]) => post(`/api/v2/roles/${role.id}/users/`, { id: user.id }));

View File

@ -2,6 +2,12 @@ module.exports = {
url () {
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: {
title: '.List-titleText',
subtitle: '.List-titleLockup',

View File

@ -52,6 +52,12 @@ module.exports = {
url () {
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: {
header,
breadcrumb,

View File

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

View File

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

View File

@ -24,6 +24,12 @@ module.exports = {
url () {
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: {
header,
navigation,

View File

@ -6,5 +6,10 @@ module.exports = {
},
sections: {}, // 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 () {
return `${this.api.globals.launch_url}/#/login`;
},
commands: [{
load () {
this.api.url('data:,'); // https://github.com/nightwatchjs/nightwatch/issues/1724
return this.navigate();
},
}],
elements: {
username: '#login-username',
password: '#login-password',

View File

@ -29,6 +29,12 @@ module.exports = {
url () {
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: {
header,
navigation,

View File

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

View File

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

View File

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

View File

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

View File

@ -23,6 +23,12 @@ module.exports = {
url () {
return `${this.api.globals.launch_url}/#/users`;
},
commands: [{
load () {
this.api.url('data:,'); // https://github.com/nightwatchjs/nightwatch/issues/1724
return this.navigate();
},
}],
sections: {
header,
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_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 || 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_MEDIUM = process.env.AWX_E2E_TIMEOUT_MEDIUM || 5000;
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.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="organization"] + div input');
@ -269,6 +270,7 @@ module.exports = {
templates.waitForElementVisible('smart-search input');
templates.expect.element('smart-search input').enabled;
client.pause(1000).waitForElementNotVisible('div.spinny');
templates.sendKeys('smart-search input', `${TEMPLATE_NAME}${client.Keys.ENTER}`);
templates.waitForElementVisible('div.spinny');
templates.waitForElementNotVisible('div.spinny');

View File

@ -24,13 +24,6 @@ let users;
let inventories;
let teams;
function navigateAndWaitForSpinner (client, url) {
client
.url(url)
.waitForElementVisible('div.spinny')
.waitForElementNotVisible('div.spinny');
}
module.exports = {
before: (client, done) => {
const promises = [
@ -68,7 +61,9 @@ module.exports = {
});
},
'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
.expect.element('@title').text.contain(data.adminAWSCredential.name);
@ -76,7 +71,9 @@ module.exports = {
credentials.section.edit.section.details.checkAllFieldsDisabled();
},
'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
.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) {
// const url = `${templates.url()}/job_template/${data.jobTemplate.id}/`;
// navigateAndWaitForSpinner(client, url);
// client.navigateTo(url)''
//
// templates.section.editJobTemplate
// .expect.element('@title').text.contain(data.jobTemplate.name);
@ -103,7 +100,9 @@ module.exports = {
// templates.expect.element('@save').to.not.be.visible;
// },
'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
.expect.element('@title').text.contain(data.notificationTemplate.name);
@ -114,7 +113,9 @@ module.exports = {
notificationTemplates.expect.element('@save').to.not.be.visible;
},
'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
.expect.element('@title').text.contain(data.organization.name);
@ -125,7 +126,9 @@ module.exports = {
organizations.expect.element('@save').to.not.be.visible;
},
'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
.expect.element('@title').text.contain(data.smartInventory.name);
@ -136,7 +139,9 @@ module.exports = {
inventories.expect.element('@save').to.not.be.visible;
},
'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
.expect.element('@title').text.contain(data.project.name);
@ -147,7 +152,9 @@ module.exports = {
projects.expect.element('@save').to.not.be.visible;
},
'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
.expect.element('@title').text.contain(data.inventory.name);
@ -159,7 +166,9 @@ module.exports = {
inventories.expect.element('@save').to.not.be.visible;
},
'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
.expect.element('@title').text.contain(data.team.name);
@ -170,7 +179,9 @@ module.exports = {
teams.expect.element('@save').to.not.be.visible;
},
'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
.expect.element('@title').text.contain(data.user.username);

View File

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

View File

@ -64,7 +64,9 @@ module.exports = {
const { details } = credentials.section.add.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('@sshKeyData').not.enabled;
@ -100,7 +102,9 @@ module.exports = {
const { details } = credentials.section.add.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('@sshKeyData').not.enabled;
@ -142,7 +146,9 @@ module.exports = {
const { details } = credentials.section.add.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('@sshKeyData').not.enabled;
@ -184,7 +190,9 @@ module.exports = {
const add = credentials.section.add.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('@sshKeyData').not.enabled;
@ -224,7 +232,9 @@ module.exports = {
gce.section.project.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;

View File

@ -166,18 +166,6 @@ module.exports = {
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 => {
const credentials = client.page.credentials();
const { edit } = credentials.section;
@ -190,6 +178,7 @@ module.exports = {
machine.expect.element('@password').not.enabled;
machine.section.password.click('@replace');
machine.section.password.expect.element('@replace').not.present;
machine.section.password.expect.element('@revert').visible;
@ -201,7 +190,19 @@ module.exports = {
credentials
.waitForElementVisible('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();
}
},
};

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -40,7 +40,7 @@ module.exports = {
getTeam(namespace).then(obj => { data.team = obj; }),
getProjectAdmin(namespace).then(obj => { data.user = 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)
@ -58,7 +58,6 @@ module.exports = {
urls.organization = `${pages.organizations.url()}/${data.organization.id}`;
urls.inventory = `${pages.inventories.url()}/inventory/${data.inventory.id}`;
urls.inventoryHosts = `${urls.inventory}/hosts`;
urls.inventoryScript = `${pages.inventoryScripts.url()}/${data.inventoryScript.id}`;
urls.inventorySource = `${urls.inventory}/inventory_sources/edit/${data.inventorySource.id}`;
urls.sourceSchedule = `${urls.inventorySource}/schedules/${data.sourceSchedule.id}`;
@ -72,6 +71,7 @@ module.exports = {
urls.notification = `${pages.notificationTemplates.url()}/${data.notification.id}`;
urls.jobs = `${pages.jobs.url()}`;
urls.jobsSchedules = `${pages.jobs.url()}/schedules`;
urls.inventoryHosts = `${pages.inventories.url()}/inventory/${data.host.summary_fields.inventory.id}/hosts`;
client.useCss();
client.login();
@ -85,7 +85,7 @@ module.exports = {
const multiCredentialOpen = 'multi-credential button i[class*="search"]';
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('#xss').not.present;
@ -371,7 +371,7 @@ module.exports = {
client.expect.element('#prompt-header').not.visible;
},
'check smart inventory form for unsanitized content': client => {
client.url(urls.smartInventory);
client.navigateTo(urls.smartInventory, false);
client.expect.element('#smartinventory_form').visible;
@ -691,6 +691,14 @@ module.exports = {
const popOver = `${itemRow} td[class*="active_failures-"] div[class*="popover"]`;
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.expect.element(popOver).present;