diff --git a/awx/ui/package.json b/awx/ui/package.json index c152d7be52..335d033aa5 100644 --- a/awx/ui/package.json +++ b/awx/ui/package.json @@ -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", diff --git a/awx/ui/test/e2e/README.md b/awx/ui/test/e2e/README.md index 86ff61f36b..3f31335edb 100644 --- a/awx/ui/test/e2e/README.md +++ b/awx/ui/test/e2e/README.md @@ -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 diff --git a/awx/ui/test/e2e/cluster/docker-compose.yml b/awx/ui/test/e2e/cluster/docker-compose.yml index 583090f555..de4a7f920b 100644 --- a/awx/ui/test/e2e/cluster/docker-compose.yml +++ b/awx/ui/test/e2e/cluster/docker-compose.yml @@ -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: diff --git a/awx/ui/test/e2e/commands/navigateTo.js b/awx/ui/test/e2e/commands/navigateTo.js index 4b8bd3048a..4c4509cf61 100644 --- a/awx/ui/test/e2e/commands/navigateTo.js +++ b/awx/ui/test/e2e/commands/navigateTo.js @@ -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; }; diff --git a/awx/ui/test/e2e/commands/pushFileToWorker.js b/awx/ui/test/e2e/commands/pushFileToWorker.js new file mode 100644 index 0000000000..191164f778 --- /dev/null +++ b/awx/ui/test/e2e/commands/pushFileToWorker.js @@ -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; diff --git a/awx/ui/test/e2e/fixtures.js b/awx/ui/test/e2e/fixtures.js index ecb45db9dc..ddc705cdf7 100644 --- a/awx/ui/test/e2e/fixtures.js +++ b/awx/ui/test/e2e/fixtures.js @@ -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 })); diff --git a/awx/ui/test/e2e/objects/activityStream.js b/awx/ui/test/e2e/objects/activityStream.js index 896fa32886..961ef6056e 100644 --- a/awx/ui/test/e2e/objects/activityStream.js +++ b/awx/ui/test/e2e/objects/activityStream.js @@ -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', diff --git a/awx/ui/test/e2e/objects/credentialTypes.js b/awx/ui/test/e2e/objects/credentialTypes.js index 6c98d1e631..302bbd61ae 100644 --- a/awx/ui/test/e2e/objects/credentialTypes.js +++ b/awx/ui/test/e2e/objects/credentialTypes.js @@ -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, diff --git a/awx/ui/test/e2e/objects/credentials.js b/awx/ui/test/e2e/objects/credentials.js index d271eb953a..a4225f2873 100644 --- a/awx/ui/test/e2e/objects/credentials.js +++ b/awx/ui/test/e2e/objects/credentials.js @@ -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, diff --git a/awx/ui/test/e2e/objects/inventories.js b/awx/ui/test/e2e/objects/inventories.js index 9fe5761ef5..794a72f760 100644 --- a/awx/ui/test/e2e/objects/inventories.js +++ b/awx/ui/test/e2e/objects/inventories.js @@ -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; diff --git a/awx/ui/test/e2e/objects/inventoryScripts.js b/awx/ui/test/e2e/objects/inventoryScripts.js index 4bea0c55e9..4555ac11ff 100644 --- a/awx/ui/test/e2e/objects/inventoryScripts.js +++ b/awx/ui/test/e2e/objects/inventoryScripts.js @@ -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, diff --git a/awx/ui/test/e2e/objects/jobs.js b/awx/ui/test/e2e/objects/jobs.js index 93c43caccd..8e1b3fe82e 100644 --- a/awx/ui/test/e2e/objects/jobs.js +++ b/awx/ui/test/e2e/objects/jobs.js @@ -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(); + }, + }], }; diff --git a/awx/ui/test/e2e/objects/login.js b/awx/ui/test/e2e/objects/login.js index 7fa8e5d8d4..75623f1300 100644 --- a/awx/ui/test/e2e/objects/login.js +++ b/awx/ui/test/e2e/objects/login.js @@ -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', diff --git a/awx/ui/test/e2e/objects/notificationTemplates.js b/awx/ui/test/e2e/objects/notificationTemplates.js index f98fb4bd2e..dabe450cbb 100644 --- a/awx/ui/test/e2e/objects/notificationTemplates.js +++ b/awx/ui/test/e2e/objects/notificationTemplates.js @@ -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, diff --git a/awx/ui/test/e2e/objects/organizations.js b/awx/ui/test/e2e/objects/organizations.js index e6f8b36398..f5eda6580b 100644 --- a/awx/ui/test/e2e/objects/organizations.js +++ b/awx/ui/test/e2e/objects/organizations.js @@ -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, diff --git a/awx/ui/test/e2e/objects/projects.js b/awx/ui/test/e2e/objects/projects.js index fcdfeb35bb..d40bcffb65 100644 --- a/awx/ui/test/e2e/objects/projects.js +++ b/awx/ui/test/e2e/objects/projects.js @@ -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, diff --git a/awx/ui/test/e2e/objects/teams.js b/awx/ui/test/e2e/objects/teams.js index bc1a4bd940..64d33b2b1a 100644 --- a/awx/ui/test/e2e/objects/teams.js +++ b/awx/ui/test/e2e/objects/teams.js @@ -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, diff --git a/awx/ui/test/e2e/objects/templates.js b/awx/ui/test/e2e/objects/templates.js index 86a5478d3e..cb59cdaaf2 100644 --- a/awx/ui/test/e2e/objects/templates.js +++ b/awx/ui/test/e2e/objects/templates.js @@ -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; diff --git a/awx/ui/test/e2e/objects/users.js b/awx/ui/test/e2e/objects/users.js index 784d7f57fd..d808dd5103 100644 --- a/awx/ui/test/e2e/objects/users.js +++ b/awx/ui/test/e2e/objects/users.js @@ -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, diff --git a/awx/ui/test/e2e/settings.js b/awx/ui/test/e2e/settings.js index a68f9fcc22..acee4dca4d 100644 --- a/awx/ui/test/e2e/settings.js +++ b/awx/ui/test/e2e/settings.js @@ -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; diff --git a/awx/ui/test/e2e/tests/smoke.js b/awx/ui/test/e2e/tests/smoke.js index 8d27ec4f39..5ef4a4ebfd 100644 --- a/awx/ui/test/e2e/tests/smoke.js +++ b/awx/ui/test/e2e/tests/smoke.js @@ -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'); diff --git a/awx/ui/test/e2e/tests/test-auditor-read-only-forms.js b/awx/ui/test/e2e/tests/test-auditor-read-only-forms.js index 9a9fc07225..ede45a1a8e 100644 --- a/awx/ui/test/e2e/tests/test-auditor-read-only-forms.js +++ b/awx/ui/test/e2e/tests/test-auditor-read-only-forms.js @@ -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); diff --git a/awx/ui/test/e2e/tests/test-credential-types-add-edit.js b/awx/ui/test/e2e/tests/test-credential-types-add-edit.js index 5b343ad0c5..6108db871c 100644 --- a/awx/ui/test/e2e/tests/test-credential-types-add-edit.js +++ b/awx/ui/test/e2e/tests/test-credential-types-add-edit.js @@ -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); diff --git a/awx/ui/test/e2e/tests/test-credentials-add-edit-gce-file.js b/awx/ui/test/e2e/tests/test-credentials-add-edit-gce-file.js index 458b735630..cdf871c36a 100644 --- a/awx/ui/test/e2e/tests/test-credentials-add-edit-gce-file.js +++ b/awx/ui/test/e2e/tests/test-credentials-add-edit-gce-file.js @@ -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; diff --git a/awx/ui/test/e2e/tests/test-credentials-add-edit-machine.js b/awx/ui/test/e2e/tests/test-credentials-add-edit-machine.js index e21e8c02f5..68507171b0 100644 --- a/awx/ui/test/e2e/tests/test-credentials-add-edit-machine.js +++ b/awx/ui/test/e2e/tests/test-credentials-add-edit-machine.js @@ -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(); - } + }, }; diff --git a/awx/ui/test/e2e/tests/test-credentials-list-actions.js b/awx/ui/test/e2e/tests/test-credentials-list-actions.js index 11f075dc79..dd5c274496 100644 --- a/awx/ui/test/e2e/tests/test-credentials-list-actions.js +++ b/awx/ui/test/e2e/tests/test-credentials-list-actions.js @@ -16,7 +16,7 @@ module.exports = { client.login(); client.waitForAngular(); - credentials.navigate(); + credentials.load(); credentials.waitForElementVisible('div.spinny'); credentials.waitForElementNotVisible('div.spinny'); diff --git a/awx/ui/test/e2e/tests/test-credentials-lookup-organization.js b/awx/ui/test/e2e/tests/test-credentials-lookup-organization.js index 5b62fac9ac..948c6693de 100644 --- a/awx/ui/test/e2e/tests/test-credentials-lookup-organization.js +++ b/awx/ui/test/e2e/tests/test-credentials-lookup-organization.js @@ -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(); diff --git a/awx/ui/test/e2e/tests/test-credentials-navigation-click-through.js b/awx/ui/test/e2e/tests/test-credentials-navigation-click-through.js index 19eae2dd9d..9fe4a7c79b 100644 --- a/awx/ui/test/e2e/tests/test-credentials-navigation-click-through.js +++ b/awx/ui/test/e2e/tests/test-credentials-navigation-click-through.js @@ -6,7 +6,7 @@ module.exports = { client.waitForAngular(); credentials - .navigate() + .load() .waitForElementVisible('div.spinny') .waitForElementNotVisible('div.spinny', done); }, diff --git a/awx/ui/test/e2e/tests/test-credentials-search-sort.js b/awx/ui/test/e2e/tests/test-credentials-search-sort.js index 525901b4ae..cd46775aae 100644 --- a/awx/ui/test/e2e/tests/test-credentials-search-sort.js +++ b/awx/ui/test/e2e/tests/test-credentials-search-sort.js @@ -10,7 +10,7 @@ module.exports = { client.waitForAngular(); credentials - .navigate() + .load() .waitForElementVisible('div.spinny') .waitForElementNotVisible('div.spinny'); diff --git a/awx/ui/test/e2e/tests/test-inventories-list-actions.js b/awx/ui/test/e2e/tests/test-inventories-list-actions.js index 280608b197..e705daeacf 100644 --- a/awx/ui/test/e2e/tests/test-inventories-list-actions.js +++ b/awx/ui/test/e2e/tests/test-inventories-list-actions.js @@ -16,7 +16,7 @@ module.exports = { client.login(); client.waitForAngular(); - inventories.navigate(); + inventories.load(); inventories.waitForElementVisible('div.spinny'); inventories.waitForElementNotVisible('div.spinny'); diff --git a/awx/ui/test/e2e/tests/test-inventory-scripts-list-actions.js b/awx/ui/test/e2e/tests/test-inventory-scripts-list-actions.js index 51c52798ea..b75bb73516 100644 --- a/awx/ui/test/e2e/tests/test-inventory-scripts-list-actions.js +++ b/awx/ui/test/e2e/tests/test-inventory-scripts-list-actions.js @@ -16,7 +16,7 @@ module.exports = { client.login(); client.waitForAngular(); - inventoryScripts.navigate(); + inventoryScripts.load(); inventoryScripts.waitForElementVisible('div.spinny'); inventoryScripts.waitForElementNotVisible('div.spinny'); diff --git a/awx/ui/test/e2e/tests/test-notifications-list-actions.js b/awx/ui/test/e2e/tests/test-notifications-list-actions.js index fc32d1553b..8e126e075d 100644 --- a/awx/ui/test/e2e/tests/test-notifications-list-actions.js +++ b/awx/ui/test/e2e/tests/test-notifications-list-actions.js @@ -16,7 +16,7 @@ module.exports = { client.login(); client.waitForAngular(); - notifications.navigate(); + notifications.load(); notifications.waitForElementVisible('div.spinny'); notifications.waitForElementNotVisible('div.spinny'); diff --git a/awx/ui/test/e2e/tests/test-projects-list-actions.js b/awx/ui/test/e2e/tests/test-projects-list-actions.js index e8bd6c0bc3..17881ed906 100644 --- a/awx/ui/test/e2e/tests/test-projects-list-actions.js +++ b/awx/ui/test/e2e/tests/test-projects-list-actions.js @@ -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'); diff --git a/awx/ui/test/e2e/tests/test-templates-copy-delete-warnings.js b/awx/ui/test/e2e/tests/test-templates-copy-delete-warnings.js index 4feb1eb346..b6544ff2da 100644 --- a/awx/ui/test/e2e/tests/test-templates-copy-delete-warnings.js +++ b/awx/ui/test/e2e/tests/test-templates-copy-delete-warnings.js @@ -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'); diff --git a/awx/ui/test/e2e/tests/test-templates-list-actions.js b/awx/ui/test/e2e/tests/test-templates-list-actions.js index 000338126e..ae551e7768 100644 --- a/awx/ui/test/e2e/tests/test-templates-list-actions.js +++ b/awx/ui/test/e2e/tests/test-templates-list-actions.js @@ -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'); diff --git a/awx/ui/test/e2e/tests/test-xss.js b/awx/ui/test/e2e/tests/test-xss.js index 5d15bdfc99..6383926709 100644 --- a/awx/ui/test/e2e/tests/test-xss.js +++ b/awx/ui/test/e2e/tests/test-xss.js @@ -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;