From 75eace1b6725d9e093f94d798305d297a85ab112 Mon Sep 17 00:00:00 2001 From: mabashian Date: Fri, 22 Sep 2017 14:08:17 -0400 Subject: [PATCH 1/6] Added e2e tests for auditor read-only forms --- awx/ui/client/test/e2e/objects/inventories.js | 91 ++++ .../test/e2e/objects/inventoryScripts.js | 70 +++ .../test/e2e/objects/notificationTemplates.js | 70 +++ .../client/test/e2e/objects/organizations.js | 59 +++ awx/ui/client/test/e2e/objects/projects.js | 72 +++ awx/ui/client/test/e2e/objects/teams.js | 70 +++ awx/ui/client/test/e2e/objects/templates.js | 89 ++++ awx/ui/client/test/e2e/objects/users.js | 71 +++ .../e2e/tests/test-auditor-read-only-forms.js | 444 ++++++++++++++++++ 9 files changed, 1036 insertions(+) create mode 100644 awx/ui/client/test/e2e/objects/inventories.js create mode 100644 awx/ui/client/test/e2e/objects/inventoryScripts.js create mode 100644 awx/ui/client/test/e2e/objects/notificationTemplates.js create mode 100644 awx/ui/client/test/e2e/objects/organizations.js create mode 100644 awx/ui/client/test/e2e/objects/projects.js create mode 100644 awx/ui/client/test/e2e/objects/teams.js create mode 100644 awx/ui/client/test/e2e/objects/templates.js create mode 100644 awx/ui/client/test/e2e/objects/users.js create mode 100644 awx/ui/client/test/e2e/tests/test-auditor-read-only-forms.js diff --git a/awx/ui/client/test/e2e/objects/inventories.js b/awx/ui/client/test/e2e/objects/inventories.js new file mode 100644 index 0000000000..65f694a75b --- /dev/null +++ b/awx/ui/client/test/e2e/objects/inventories.js @@ -0,0 +1,91 @@ +import actions from './sections/actions.js'; +import breadcrumb from './sections/breadcrumb.js'; +import createFormSection from './sections/createFormSection.js'; +import createTableSection from './sections/createTableSection.js'; +import header from './sections/header.js'; +import lookupModal from './sections/lookupModal.js'; +import navigation from './sections/navigation.js'; +import pagination from './sections/pagination.js'; +import permissions from './sections/permissions.js'; +import search from './sections/search.js'; + +const details = createFormSection({ + selector: 'form' +}); + +module.exports = { + url() { + return `${this.api.globals.awxURL}/#/inventories`; + }, + sections: { + header, + navigation, + breadcrumb, + lookupModal, + addStandardInventory: { + selector: 'div[ui-view="form"]', + sections: { + details + }, + elements: { + title: 'div[class^="Form-title"]' + } + }, + editStandardInventory: { + selector: 'div[ui-view="form"]', + sections: { + details, + permissions + }, + elements: { + title: 'div[class^="Form-title"]' + } + }, + addSmartInventory: { + selector: 'div[ui-view="form"]', + sections: { + details + }, + elements: { + title: 'div[class^="Form-title"]' + } + }, + editSmartInventory: { + selector: 'div[ui-view="form"]', + sections: { + details, + permissions + }, + elements: { + title: 'div[class^="Form-title"]' + } + }, + list: { + selector: 'div[ui-view="list"]', + elements: { + badge: 'span[class~="badge"]', + title: 'div[class="List-titleText"]', + add: 'button[class~="List-dropdownButton"]' + }, + sections: { + search, + pagination, + table: createTableSection({ + elements: { + status: 'td[class~="status-column"]', + name: 'td[class~="name-column"]', + kind: 'td[class~="kind-column"]', + organization: 'td[class~="organization-column"]' + }, + sections: { + actions + } + }) + } + } + }, + elements: { + cancel: 'button[class*="Form-cancelButton"]', + save: 'button[class*="Form-saveButton"]' + } +}; diff --git a/awx/ui/client/test/e2e/objects/inventoryScripts.js b/awx/ui/client/test/e2e/objects/inventoryScripts.js new file mode 100644 index 0000000000..0b90ef3969 --- /dev/null +++ b/awx/ui/client/test/e2e/objects/inventoryScripts.js @@ -0,0 +1,70 @@ +import actions from './sections/actions.js'; +import breadcrumb from './sections/breadcrumb.js'; +import createFormSection from './sections/createFormSection.js'; +import createTableSection from './sections/createTableSection.js'; +import header from './sections/header.js'; +import lookupModal from './sections/lookupModal.js'; +import navigation from './sections/navigation.js'; +import pagination from './sections/pagination.js'; +import permissions from './sections/permissions.js'; +import search from './sections/search.js'; + +const details = createFormSection({ + selector: 'form' +}); + +module.exports = { + url() { + return `${this.api.globals.awxURL}/#/inventory_scripts`; + }, + sections: { + header, + navigation, + breadcrumb, + lookupModal, + add: { + selector: 'div[ui-view="form"]', + sections: { + details + }, + elements: { + title: 'div[class^="Form-title"]' + } + }, + edit: { + selector: 'div[ui-view="form"]', + sections: { + details, + permissions + }, + elements: { + title: 'div[class^="Form-title"]' + } + }, + list: { + selector: 'div[ui-view="list"]', + elements: { + badge: 'span[class~="badge"]', + title: 'div[class="List-titleText"]', + add: 'button[class~="List-buttonSubmit"]' + }, + sections: { + search, + pagination, + table: createTableSection({ + elements: { + name: 'td[class~="name-column"]', + organization: 'td[class~="organization-column"]', + }, + sections: { + actions + } + }) + } + } + }, + elements: { + cancel: 'button[class*="Form-cancelButton"]', + save: 'button[class*="Form-saveButton"]' + } +}; diff --git a/awx/ui/client/test/e2e/objects/notificationTemplates.js b/awx/ui/client/test/e2e/objects/notificationTemplates.js new file mode 100644 index 0000000000..dcdb5777e1 --- /dev/null +++ b/awx/ui/client/test/e2e/objects/notificationTemplates.js @@ -0,0 +1,70 @@ +import actions from './sections/actions.js'; +import breadcrumb from './sections/breadcrumb.js'; +import createFormSection from './sections/createFormSection.js'; +import createTableSection from './sections/createTableSection.js'; +import header from './sections/header.js'; +import lookupModal from './sections/lookupModal.js'; +import navigation from './sections/navigation.js'; +import pagination from './sections/pagination.js'; +import permissions from './sections/permissions.js'; +import search from './sections/search.js'; + +const details = createFormSection({ + selector: 'form' +}); + +module.exports = { + url() { + return `${this.api.globals.awxURL}/#/notification_templates`; + }, + sections: { + header, + navigation, + breadcrumb, + lookupModal, + add: { + selector: 'div[ui-view="form"]', + sections: { + details + }, + elements: { + title: 'div[class^="Form-title"]' + } + }, + edit: { + selector: 'div[ui-view="form"]', + sections: { + details, + permissions + }, + elements: { + title: 'div[class^="Form-title"]' + } + }, + list: { + selector: 'div[ui-view="list"]', + elements: { + badge: 'span[class~="badge"]', + title: 'div[class="List-titleText"]', + add: 'button[class~="List-buttonSubmit"]' + }, + sections: { + search, + pagination, + table: createTableSection({ + elements: { + name: 'td[class~="name-column"]', + organization: 'td[class~="organization-column"]', + }, + sections: { + actions + } + }) + } + } + }, + elements: { + cancel: 'button[class*="Form-cancelButton"]', + save: 'button[class*="Form-saveButton"]' + } +}; diff --git a/awx/ui/client/test/e2e/objects/organizations.js b/awx/ui/client/test/e2e/objects/organizations.js new file mode 100644 index 0000000000..55b398eb81 --- /dev/null +++ b/awx/ui/client/test/e2e/objects/organizations.js @@ -0,0 +1,59 @@ +import breadcrumb from './sections/breadcrumb.js'; +import createFormSection from './sections/createFormSection.js'; +import header from './sections/header.js'; +import lookupModal from './sections/lookupModal.js'; +import navigation from './sections/navigation.js'; +import pagination from './sections/pagination.js'; +import permissions from './sections/permissions.js'; +import search from './sections/search.js'; + +const details = createFormSection({ + selector: 'form' +}); + +module.exports = { + url() { + return `${this.api.globals.awxURL}/#/organizations`; + }, + sections: { + header, + navigation, + breadcrumb, + lookupModal, + add: { + selector: 'div[ui-view="form"]', + sections: { + details + }, + elements: { + title: 'div[class^="Form-title"]' + } + }, + edit: { + selector: 'div[ui-view="form"]', + sections: { + details, + permissions + }, + elements: { + title: 'div[class^="Form-title"]' + } + }, + list: { + selector: '#organizations', + elements: { + badge: 'span[class~="badge"]', + title: 'div[class="List-titleText"]', + add: 'button[class~="List-buttonSubmit"]' + }, + sections: { + search, + pagination + } + } + }, + elements: { + cancel: 'button[class*="Form-cancelButton"]', + save: 'button[class*="Form-saveButton"]' + } +}; diff --git a/awx/ui/client/test/e2e/objects/projects.js b/awx/ui/client/test/e2e/objects/projects.js new file mode 100644 index 0000000000..8e7ae7208d --- /dev/null +++ b/awx/ui/client/test/e2e/objects/projects.js @@ -0,0 +1,72 @@ +import actions from './sections/actions.js'; +import breadcrumb from './sections/breadcrumb.js'; +import createFormSection from './sections/createFormSection.js'; +import createTableSection from './sections/createTableSection.js'; +import header from './sections/header.js'; +import lookupModal from './sections/lookupModal.js'; +import navigation from './sections/navigation.js'; +import pagination from './sections/pagination.js'; +import permissions from './sections/permissions.js'; +import search from './sections/search.js'; + +const details = createFormSection({ + selector: 'form' +}); + +module.exports = { + url() { + return `${this.api.globals.awxURL}/#/projects`; + }, + sections: { + header, + navigation, + breadcrumb, + lookupModal, + add: { + selector: 'div[ui-view="form"]', + sections: { + details + }, + elements: { + title: 'div[class^="Form-title"]' + } + }, + edit: { + selector: 'div[ui-view="form"]', + sections: { + details, + permissions + }, + elements: { + title: 'div[class^="Form-title"]' + } + }, + list: { + selector: 'div[ui-view="list"]', + elements: { + badge: 'span[class~="badge"]', + title: 'div[class="List-titleText"]', + add: 'button[class~="List-buttonSubmit"]' + }, + sections: { + search, + pagination, + table: createTableSection({ + elements: { + status: 'td[class~="status-column"]', + name: 'td[class~="name-column"]', + scm_type: 'td[class~="scm_type-column"]', + last_updated: 'td[class~="last_updated-column"]' + }, + sections: { + actions + } + }) + } + } + }, + elements: { + cancel: 'button[class*="Form-cancelButton"]', + save: 'button[class*="Form-saveButton"]' + } +}; diff --git a/awx/ui/client/test/e2e/objects/teams.js b/awx/ui/client/test/e2e/objects/teams.js new file mode 100644 index 0000000000..a09daf07f7 --- /dev/null +++ b/awx/ui/client/test/e2e/objects/teams.js @@ -0,0 +1,70 @@ +import actions from './sections/actions.js'; +import breadcrumb from './sections/breadcrumb.js'; +import createFormSection from './sections/createFormSection.js'; +import createTableSection from './sections/createTableSection.js'; +import header from './sections/header.js'; +import lookupModal from './sections/lookupModal.js'; +import navigation from './sections/navigation.js'; +import pagination from './sections/pagination.js'; +import permissions from './sections/permissions.js'; +import search from './sections/search.js'; + +const details = createFormSection({ + selector: 'form' +}); + +module.exports = { + url() { + return `${this.api.globals.awxURL}/#/teams`; + }, + sections: { + header, + navigation, + breadcrumb, + lookupModal, + add: { + selector: 'div[ui-view="form"]', + sections: { + details + }, + elements: { + title: 'div[class^="Form-title"]' + } + }, + edit: { + selector: 'div[ui-view="form"]', + sections: { + details, + permissions + }, + elements: { + title: 'div[class^="Form-title"]' + } + }, + list: { + selector: 'div[ui-view="list"]', + elements: { + badge: 'span[class~="badge"]', + title: 'div[class="List-titleText"]', + add: 'button[class~="List-buttonSubmit"]' + }, + sections: { + search, + pagination, + table: createTableSection({ + elements: { + name: 'td[class~="name-column"]', + organization: 'td[class~="organization-column"]' + }, + sections: { + actions + } + }) + } + } + }, + elements: { + cancel: 'button[class*="Form-cancelButton"]', + save: 'button[class*="Form-saveButton"]' + } +}; diff --git a/awx/ui/client/test/e2e/objects/templates.js b/awx/ui/client/test/e2e/objects/templates.js new file mode 100644 index 0000000000..700ac2565c --- /dev/null +++ b/awx/ui/client/test/e2e/objects/templates.js @@ -0,0 +1,89 @@ +import actions from './sections/actions.js'; +import breadcrumb from './sections/breadcrumb.js'; +import createFormSection from './sections/createFormSection.js'; +import createTableSection from './sections/createTableSection.js'; +import header from './sections/header.js'; +import lookupModal from './sections/lookupModal.js'; +import navigation from './sections/navigation.js'; +import pagination from './sections/pagination.js'; +import permissions from './sections/permissions.js'; +import search from './sections/search.js'; + +const details = createFormSection({ + selector: 'form' +}); + +module.exports = { + url() { + return `${this.api.globals.awxURL}/#/templates`; + }, + sections: { + header, + navigation, + breadcrumb, + lookupModal, + addJobTemplate: { + selector: 'div[ui-view="form"]', + sections: { + details + }, + elements: { + title: 'div[class^="Form-title"]' + } + }, + editJobTemplate: { + selector: 'div[ui-view="form"]', + sections: { + details, + permissions + }, + elements: { + title: 'div[class^="Form-title"]' + } + }, + addWorkflowJobTemplate: { + selector: 'div[ui-view="form"]', + sections: { + details + }, + elements: { + title: 'div[class^="Form-title"]' + } + }, + editWorkflowJobTemplate: { + selector: 'div[ui-view="form"]', + sections: { + details, + permissions + }, + elements: { + title: 'div[class^="Form-title"]' + } + }, + list: { + selector: 'div[ui-view="list"]', + elements: { + badge: 'span[class~="badge"]', + title: 'div[class="List-titleText"]', + add: 'button[class~="List-buttonSubmit"]' + }, + sections: { + search, + pagination, + table: createTableSection({ + elements: { + name: 'td[class~="name-column"]', + kind: 'td[class~="type-column"]' + }, + sections: { + actions + } + }) + } + } + }, + elements: { + cancel: 'button[class*="Form-cancelButton"]', + save: 'button[class*="Form-saveButton"]' + } +}; diff --git a/awx/ui/client/test/e2e/objects/users.js b/awx/ui/client/test/e2e/objects/users.js new file mode 100644 index 0000000000..2add0e6c92 --- /dev/null +++ b/awx/ui/client/test/e2e/objects/users.js @@ -0,0 +1,71 @@ +import actions from './sections/actions.js'; +import breadcrumb from './sections/breadcrumb.js'; +import createFormSection from './sections/createFormSection.js'; +import createTableSection from './sections/createTableSection.js'; +import header from './sections/header.js'; +import lookupModal from './sections/lookupModal.js'; +import navigation from './sections/navigation.js'; +import pagination from './sections/pagination.js'; +import permissions from './sections/permissions.js'; +import search from './sections/search.js'; + +const details = createFormSection({ + selector: 'form' +}); + +module.exports = { + url() { + return `${this.api.globals.awxURL}/#/users`; + }, + sections: { + header, + navigation, + breadcrumb, + lookupModal, + add: { + selector: 'div[ui-view="form"]', + sections: { + details + }, + elements: { + title: 'div[class^="Form-title"]' + } + }, + edit: { + selector: 'div[ui-view="form"]', + sections: { + details, + permissions + }, + elements: { + title: 'div[class^="Form-title"]' + } + }, + list: { + selector: 'div[ui-view="list"]', + elements: { + badge: 'span[class~="badge"]', + title: 'div[class="List-titleText"]', + add: 'button[class~="List-buttonSubmit"]' + }, + sections: { + search, + pagination, + table: createTableSection({ + elements: { + username: 'td[class~="username-column"]', + first_name: 'td[class~="first_name-column"]', + last_name: 'td[class~="last_name-column"]' + }, + sections: { + actions + } + }) + } + } + }, + elements: { + cancel: 'button[class*="Form-cancelButton"]', + save: 'button[class*="Form-saveButton"]' + } +}; diff --git a/awx/ui/client/test/e2e/tests/test-auditor-read-only-forms.js b/awx/ui/client/test/e2e/tests/test-auditor-read-only-forms.js new file mode 100644 index 0000000000..99612822c2 --- /dev/null +++ b/awx/ui/client/test/e2e/tests/test-auditor-read-only-forms.js @@ -0,0 +1,444 @@ +import uuid from 'uuid'; + + +let testID = uuid().substr(0,8); + + +let store = { + auditor: { + username: `auditor-${testID}`, + first_name: 'auditor', + last_name: 'last', + email: 'null@ansible.com', + is_superuser: false, + is_system_auditor: true, + password: 'password' + }, + adminJobTemplate: { + name: `adminJobTemplate-${testID}`, + description: `adminJobTemplate-description-${testID}`, + project: 103, + playbook: 'check.yml' + }, + adminAWSCredential: { + name: `adminAWSCredential-${testID}`, + description: `adminAWSCredential-description-${testID}`, + inputs: { + username: 'username', + password: 'password', + security_token: 'AAAAAAAAAAAAAAAAAAAAAAAAAA' + } + }, + adminMachineCredential: { + name: `adminMachineCredential-${testID}` + }, + adminOrganization: { + name: `adminOrganization-${testID}`, + }, + adminInventoryScript: { + name: `adminInventoryScript-${testID}`, + script: '#!/usr/bin/env python' + }, + adminNotificationTemplate: { + name: `adminNotificationTemplate-${testID}`, + notification_configuration: {channels: ["awx-e2e"], token: "foobar"}, + notification_type: "slack" + }, + adminProject: { + name: `adminProject-${testID}`, + scm_type: "git", + scm_url: "https://github.com/ansible/tower-example.git" + }, + adminSmartInventory: { + name: `adminSmartInventory-${testID}`, + host_filter: 'search=localhost', + kind: 'smart' + }, + adminStandardInventory: { + name: `adminStandardInventory-${testID}` + }, + adminTeam: { + name: `adminTeam-${testID}` + }, + adminUser: { + username: `adminUser-${testID}`, + first_name: `adminUser-${testID}-first`, + last_name: `adminUser-${testID}-last`, + email: `null-${testID}@ansible.com`, + is_superuser: false, + is_system_auditor: false, + password: 'password' + }, + created: {} +}; + +let credentials, + inventoryScripts, + templates, + notificationTemplates, + organizations, + projects, + users, + inventories, + teams; + +function checkDisabledElements(client, selectors) { + selectors.forEach(function(selector) { + client.elements('css selector', selector, inputs => { + inputs.value.map(o => o.ELEMENT).forEach(id => { + client.elementIdAttribute(id, 'disabled', ({ value }) => { + client.assert.equal(value, 'true'); + }); + }); + }); + }); +} + +function navigateAndWaitForSpinner(client, url) { + client + .url(url) + .waitForElementVisible('div.spinny') + .waitForElementNotVisible('div.spinny'); +} + +module.exports = { + before: function (client, done) { + + credentials = client.useCss().page.credentials(); + inventoryScripts = client.useCss().page.inventoryScripts(); + templates = client.useCss().page.templates(); + notificationTemplates = client.useCss().page.notificationTemplates(); + organizations = client.useCss().page.organizations(); + projects = client.useCss().page.projects(); + users = client.useCss().page.users(); + inventories = client.useCss().page.inventories(); + teams = client.useCss().page.teams(); + + client.login(); + client.waitForAngular(); + + client.inject([store, '$http'], (store, $http) => { + + let { adminJobTemplate, + adminAWSCredential, + adminMachineCredential, + adminOrganization, + adminInventoryScript, + adminNotificationTemplate, + adminProject, + adminSmartInventory, + adminStandardInventory, + adminTeam, + adminUser, + auditor } = store; + + return $http.get('/api/v2/me') + .then(({ data }) => { + let resource = 'Amazon%20Web%20Services+cloud'; + adminAWSCredential.user = data.results[0].id; + + return $http.get(`/api/v2/credential_types/${resource}`); + }) + .then(({ data }) => { + adminAWSCredential.credential_type = data.id; + + return $http.post('/api/v2/credentials/', adminAWSCredential); + }) + .then(({ data }) => { + adminAWSCredential = data; + + return $http.post('/api/v2/organizations/', adminOrganization); + }) + .then(({ data }) => { + adminOrganization = data; + adminInventoryScript.organization = data.id; + adminNotificationTemplate.organization = data.id; + adminProject.organization = data.id; + adminSmartInventory.organization = data.id; + adminStandardInventory.organization = data.id; + adminTeam.organization = data.id; + adminUser.organization = data.id; + adminMachineCredential.organization = data.id; + + return $http.post('/api/v2/teams/', adminTeam); + }) + .then(({ data }) => { + adminTeam = data; + + return $http.get('/api/v2/credential_types/Machine+ssh'); + }) + .then(({ data }) => { + adminMachineCredential.credential_type = data.id; + + return $http.post('/api/v2/credentials/', adminMachineCredential); + }) + .then(({ data }) => { + adminMachineCredential = data; + adminJobTemplate.credential = data.id; + + return $http.post('/api/v2/users/', adminUser); + }) + .then(({ data }) => { + adminUser = data; + + return $http.post('/api/v2/notification_templates/', adminNotificationTemplate); + }) + .then(({ data }) => { + adminNotificationTemplate = data; + + return $http.post('/api/v2/inventory_scripts/', adminInventoryScript); + }) + .then(({ data }) => { + adminInventoryScript = data; + + return $http.post('/api/v2/projects/', adminProject); + }) + .then(({ data }) => { + adminProject = data; + + return $http.post('/api/v2/inventories/', adminSmartInventory); + }) + .then(({ data }) => { + adminSmartInventory = data; + + return $http.post('/api/v2/inventories/', adminStandardInventory); + }) + .then(({ data }) => { + adminStandardInventory = data; + adminJobTemplate.inventory = data.id; + + return $http.post('/api/v2/job_templates/', adminJobTemplate); + }) + .then(({ data }) => { + adminJobTemplate = data; + + return $http.post('/api/v2/users/', auditor); + }) + .then(({ data }) => { + auditor = data; + + return { + adminJobTemplate, + adminAWSCredential, + adminMachineCredential, + adminOrganization, + adminInventoryScript, + adminNotificationTemplate, + adminProject, + adminSmartInventory, + adminStandardInventory, + adminTeam, + adminUser, + auditor + }; + }); + }, + ({ adminJobTemplate, + adminAWSCredential, + adminMachineCredential, + adminOrganization, + adminInventoryScript, + adminNotificationTemplate, + adminProject, + adminSmartInventory, + adminStandardInventory, + adminTeam, + adminUser, + auditor }) => { + store.created = { + adminJobTemplate, + adminAWSCredential, + adminMachineCredential, + adminOrganization, + adminInventoryScript, + adminNotificationTemplate, + adminProject, + adminSmartInventory, + adminStandardInventory, + adminTeam, + adminUser, + auditor + }; + + client.login(store.auditor.username, store.auditor.password); + + done(); + }); + }, + 'verify an auditor\'s credentials inputs are read-only': function (client) { + navigateAndWaitForSpinner(client, `${credentials.url()}/${store.created.adminAWSCredential.id}/`); + + credentials.section.edit + .expect.element('@title').text.contain(store.created.adminAWSCredential.name); + + checkDisabledElements(client, ['.at-Input']); + }, + 'verify an auditor\'s inventory scripts inputs are read-only': function (client) { + navigateAndWaitForSpinner(client, `${inventoryScripts.url()}/${store.created.adminInventoryScript.id}/`); + + inventoryScripts.section.edit + .expect.element('@title').text.contain(store.created.adminInventoryScript.name); + + let selectors = [ + '#inventory_script_form .Form-textInput', + '#inventory_script_form .Form-textArea', + '#inventory_script_form .Form-lookupButton' + ]; + + checkDisabledElements(client, selectors); + }, + 'verify save button hidden from auditor on inventory scripts form': function () { + inventoryScripts.expect.element('@save').to.not.be.visible; + }, + 'verify an auditor\'s job template inputs are read-only': function (client) { + navigateAndWaitForSpinner(client, `${templates.url()}/job_template/${store.created.adminJobTemplate.id}/`); + + templates.section.editJobTemplate + .expect.element('@title').text.contain(store.created.adminJobTemplate.name); + + client.pause(2000); + + let selectors = [ + '#job_template_form .Form-textInput', + '#job_template_form select.Form-dropDown', + '#job_template_form .Form-textArea', + '#job_template_form input[type="checkbox"]', + '#job_template_form .ui-spinner-input', + '#job_template_form .ScheduleToggle-switch' + ]; + + checkDisabledElements(client, selectors); + }, + 'verify save button hidden from auditor on job templates form': function () { + templates.expect.element('@save').to.not.be.visible; + }, + 'verify an auditor\'s notification templates inputs are read-only': function (client) { + navigateAndWaitForSpinner(client, `${notificationTemplates.url()}/${store.created.adminNotificationTemplate.id}/`); + + notificationTemplates.section.edit + .expect.element('@title').text.contain(store.created.adminNotificationTemplate.name); + + let selectors = [ + '#notification_template_form .Form-textInput', + '#notification_template_form select.Form-dropDown', + '#notification_template_form input[type="checkbox"]', + '#notification_template_form input[type="radio"]', + '#notification_template_form .ui-spinner-input', + '#notification_template_form .Form-textArea', + '#notification_template_form .ScheduleToggle-switch', + '#notification_template_form .Form-lookupButton' + ]; + + checkDisabledElements(client, selectors); + }, + 'verify save button hidden from auditor on notification templates page': function () { + notificationTemplates.expect.element('@save').to.not.be.visible; + }, + 'verify an auditor\'s organizations inputs are read-only': function (client) { + navigateAndWaitForSpinner(client, `${organizations.url()}/${store.created.adminOrganization.id}/`); + + organizations.section.edit + .expect.element('@title').text.contain(store.created.adminOrganization.name); + + let selectors = [ + '#organization_form input.Form-textInput', + '#organization_form .Form-lookupButton', + '#organization_form #InstanceGroups' + ]; + + checkDisabledElements(client, selectors); + }, + 'verify save button hidden from auditor on organizations form': function () { + organizations.expect.element('@save').to.not.be.visible; + }, + 'verify an auditor\'s smart inventory inputs are read-only': function (client) { + navigateAndWaitForSpinner(client, `${inventories.url()}/smart/${store.created.adminSmartInventory.id}/`); + + inventories.section.editSmartInventory + .expect.element('@title').text.contain(store.created.adminSmartInventory.name); + + let selectors = [ + '#smartinventory_form input.Form-textInput', + '#smartinventory_form textarea.Form-textArea', + '#smartinventory_form .Form-lookupButton', + '#smartinventory_form #InstanceGroups' + ]; + + checkDisabledElements(client, selectors); + }, + 'verify save button hidden from auditor on smart inventories form': function () { + inventories.expect.element('@save').to.not.be.visible; + }, + 'verify an auditor\'s project inputs are read-only': function (client) { + navigateAndWaitForSpinner(client, `${projects.url()}/${store.created.adminProject.id}/`); + + projects.section.edit + .expect.element('@title').text.contain(store.created.adminProject.name); + + let selectors = [ + '#project_form .Form-textInput', + '#project_form select.Form-dropDown', + '#project_form input[type="checkbox"]', + '#project_form .ui-spinner-input', + ]; + + checkDisabledElements(client, selectors); + }, + 'verify save button hidden from auditor on projects form': function () { + projects.expect.element('@save').to.not.be.visible; + }, + 'verify an auditor\'s standard inventory inputs are read-only': function (client) { + navigateAndWaitForSpinner(client, `${inventories.url()}/inventory/${store.created.adminStandardInventory.id}/`); + + inventories.section.editStandardInventory + .expect.element('@title').text.contain(store.created.adminStandardInventory.name); + + let selectors = [ + '#inventory_form .Form-textInput', + '#inventory_form select.Form-dropDown', + '#inventory_form .Form-textArea', + '#inventory_form input[type="checkbox"]', + '#inventory_form .ui-spinner-input', + '#inventory_form .ScheduleToggle-switch' + ]; + + checkDisabledElements(client, selectors); + }, + 'verify save button hidden from auditor on standard inventory form': function () { + inventories.expect.element('@save').to.not.be.visible; + }, + 'verify an auditor\'s teams inputs are read-only': function (client) { + navigateAndWaitForSpinner(client, `${teams.url()}/${store.created.adminTeam.id}/`); + + teams.section.edit + .expect.element('@title').text.contain(store.created.adminTeam.name); + + let selectors = [ + '#team_form input.Form-textInput', + '#team_form .Form-lookupButton' + ]; + + checkDisabledElements(client, selectors); + }, + 'verify save button hidden from auditor on teams form': function () { + teams.expect.element('@save').to.not.be.visible; + }, + 'verify an auditor\'s user inputs are read-only': function (client) { + navigateAndWaitForSpinner(client, `${users.url()}/${store.created.adminUser.id}/`); + + users.section.edit + .expect.element('@title').text.contain(store.created.adminUser.username); + + let selectors = [ + '#user_form .Form-textInput', + '#user_form select.Form-dropDown' + ]; + + checkDisabledElements(client, selectors); + }, + 'verify save button hidden from auditor on users form': function (client) { + users.expect.element('@save').to.not.be.visible; + + client.end(); + } +}; From 101c1d7229604aebd1c005517d4fed4569e06e16 Mon Sep 17 00:00:00 2001 From: mabashian Date: Mon, 25 Sep 2017 10:41:28 -0400 Subject: [PATCH 2/6] removed test-credentials-read-only.js --- .../e2e/tests/test-credentials-read-only.js | 97 ------------------- 1 file changed, 97 deletions(-) delete mode 100644 awx/ui/client/test/e2e/tests/test-credentials-read-only.js diff --git a/awx/ui/client/test/e2e/tests/test-credentials-read-only.js b/awx/ui/client/test/e2e/tests/test-credentials-read-only.js deleted file mode 100644 index 93d07714a0..0000000000 --- a/awx/ui/client/test/e2e/tests/test-credentials-read-only.js +++ /dev/null @@ -1,97 +0,0 @@ -import uuid from 'uuid'; - - -let testID = uuid().substr(0,8); - - -let store = { - auditor: { - username: `auditor-${testID}`, - first_name: 'auditor', - last_name: 'last', - email: 'null@ansible.com', - is_superuser: false, - is_system_auditor: true, - password: 'password' - }, - adminCredential: { - name: `adminCredential-${testID}`, - description: `adminCredential-description-${testID}`, - inputs: { - username: 'username', - password: 'password', - security_token: 'AAAAAAAAAAAAAAAAAAAAAAAAAA' - } - }, - created: {} -}; - - -module.exports = { - before: function (client, done) { - const credentials = client.page.credentials(); - - client.login(); - client.waitForAngular(); - - client.inject([store, '$http'], (store, $http) => { - - let { adminCredential, auditor } = store; - - return $http.get('/api/v2/me') - .then(({ data }) => { - let resource = 'Amazon%20Web%20Services+cloud'; - adminCredential.user = data.results[0].id; - - return $http.get(`/api/v2/credential_types/${resource}`); - }) - .then(({ data }) => { - adminCredential.credential_type = data.id; - - return $http.post('/api/v2/credentials/', adminCredential); - }) - .then(({ data }) => { - adminCredential = data; - - return $http.post('/api/v2/users/', auditor); - }) - .then(({ data }) => { - auditor = data; - - return { adminCredential, auditor }; - }); - }, - ({ adminCredential, auditor }) => { - store.created = { adminCredential, auditor }; - done(); - }) - }, - beforeEach: function (client) { - const credentials = client.useCss().page.credentials(); - - credentials - .login(store.auditor.username, store.auditor.password) - .navigate(`${credentials.url()}/${store.created.adminCredential.id}/`) - .waitForElementVisible('div.spinny') - .waitForElementNotVisible('div.spinny'); - }, - 'verify an auditor\'s inputs are read-only': function (client) { - const credentials = client.useCss().page.credentials() - const details = credentials.section.edit.section.details; - - let expected = store.created.adminCredential.name; - - credentials.section.edit - .expect.element('@title').text.contain(expected); - - client.elements('css selector', '.at-Input', inputs => { - inputs.value.map(o => o.ELEMENT).forEach(id => { - client.elementIdAttribute(id, 'disabled', ({ value }) => { - client.assert.equal(value, 'true'); - }); - }); - }); - - client.end(); - } -}; From 5b29e51a2453fc77d4cca5e6d2ab715e04ba48fb Mon Sep 17 00:00:00 2001 From: mabashian Date: Tue, 26 Sep 2017 14:30:45 -0400 Subject: [PATCH 3/6] Moved form element selectors out into props in the form definition. Added checkAllFieldsDisabled to createFormSection. --- awx/ui/client/test/e2e/objects/inventories.js | 34 +++- .../test/e2e/objects/inventoryScripts.js | 9 +- .../test/e2e/objects/notificationTemplates.js | 14 +- .../client/test/e2e/objects/organizations.js | 9 +- awx/ui/client/test/e2e/objects/projects.js | 10 +- .../e2e/objects/sections/createFormSection.js | 41 ++++- awx/ui/client/test/e2e/objects/teams.js | 8 +- awx/ui/client/test/e2e/objects/templates.js | 12 +- awx/ui/client/test/e2e/objects/users.js | 8 +- .../e2e/tests/test-auditor-read-only-forms.js | 169 ++++-------------- 10 files changed, 164 insertions(+), 150 deletions(-) diff --git a/awx/ui/client/test/e2e/objects/inventories.js b/awx/ui/client/test/e2e/objects/inventories.js index 65f694a75b..1bbae1a1e9 100644 --- a/awx/ui/client/test/e2e/objects/inventories.js +++ b/awx/ui/client/test/e2e/objects/inventories.js @@ -9,8 +9,30 @@ import pagination from './sections/pagination.js'; import permissions from './sections/permissions.js'; import search from './sections/search.js'; -const details = createFormSection({ - selector: 'form' +const standardInvDetails = createFormSection({ + selector: 'form', + props: { + formElementSelectors: [ + '#inventory_form .Form-textInput', + '#inventory_form select.Form-dropDown', + '#inventory_form .Form-textArea', + '#inventory_form input[type="checkbox"]', + '#inventory_form .ui-spinner-input', + '#inventory_form .ScheduleToggle-switch' + ] + } +}); + +const smartInvDetails = createFormSection({ + selector: 'form', + props: { + formElementSelectors: [ + '#smartinventory_form input.Form-textInput', + '#smartinventory_form textarea.Form-textArea', + '#smartinventory_form .Form-lookupButton', + '#smartinventory_form #InstanceGroups' + ] + } }); module.exports = { @@ -25,7 +47,7 @@ module.exports = { addStandardInventory: { selector: 'div[ui-view="form"]', sections: { - details + standardInvDetails }, elements: { title: 'div[class^="Form-title"]' @@ -34,7 +56,7 @@ module.exports = { editStandardInventory: { selector: 'div[ui-view="form"]', sections: { - details, + standardInvDetails, permissions }, elements: { @@ -44,7 +66,7 @@ module.exports = { addSmartInventory: { selector: 'div[ui-view="form"]', sections: { - details + smartInvDetails }, elements: { title: 'div[class^="Form-title"]' @@ -53,7 +75,7 @@ module.exports = { editSmartInventory: { selector: 'div[ui-view="form"]', sections: { - details, + smartInvDetails, permissions }, elements: { diff --git a/awx/ui/client/test/e2e/objects/inventoryScripts.js b/awx/ui/client/test/e2e/objects/inventoryScripts.js index 0b90ef3969..a34cb6dca4 100644 --- a/awx/ui/client/test/e2e/objects/inventoryScripts.js +++ b/awx/ui/client/test/e2e/objects/inventoryScripts.js @@ -10,7 +10,14 @@ import permissions from './sections/permissions.js'; import search from './sections/search.js'; const details = createFormSection({ - selector: 'form' + selector: 'form', + props: { + formElementSelectors: [ + '#inventory_script_form .Form-textInput', + '#inventory_script_form .Form-textArea', + '#inventory_script_form .Form-lookupButton' + ] + } }); module.exports = { diff --git a/awx/ui/client/test/e2e/objects/notificationTemplates.js b/awx/ui/client/test/e2e/objects/notificationTemplates.js index dcdb5777e1..dc7371eaaf 100644 --- a/awx/ui/client/test/e2e/objects/notificationTemplates.js +++ b/awx/ui/client/test/e2e/objects/notificationTemplates.js @@ -10,7 +10,19 @@ import permissions from './sections/permissions.js'; import search from './sections/search.js'; const details = createFormSection({ - selector: 'form' + selector: 'form', + props: { + formElementSelectors: [ + '#notification_template_form .Form-textInput', + '#notification_template_form select.Form-dropDown', + '#notification_template_form input[type="checkbox"]', + '#notification_template_form input[type="radio"]', + '#notification_template_form .ui-spinner-input', + '#notification_template_form .Form-textArea', + '#notification_template_form .ScheduleToggle-switch', + '#notification_template_form .Form-lookupButton' + ] + } }); module.exports = { diff --git a/awx/ui/client/test/e2e/objects/organizations.js b/awx/ui/client/test/e2e/objects/organizations.js index 55b398eb81..ca945ff269 100644 --- a/awx/ui/client/test/e2e/objects/organizations.js +++ b/awx/ui/client/test/e2e/objects/organizations.js @@ -8,7 +8,14 @@ import permissions from './sections/permissions.js'; import search from './sections/search.js'; const details = createFormSection({ - selector: 'form' + selector: 'form', + props: { + formElementSelectors: [ + '#organization_form input.Form-textInput', + '#organization_form .Form-lookupButton', + '#organization_form #InstanceGroups' + ] + } }); module.exports = { diff --git a/awx/ui/client/test/e2e/objects/projects.js b/awx/ui/client/test/e2e/objects/projects.js index 8e7ae7208d..399468267a 100644 --- a/awx/ui/client/test/e2e/objects/projects.js +++ b/awx/ui/client/test/e2e/objects/projects.js @@ -10,7 +10,15 @@ import permissions from './sections/permissions.js'; import search from './sections/search.js'; const details = createFormSection({ - selector: 'form' + selector: 'form', + props: { + formElementSelectors: [ + '#project_form .Form-textInput', + '#project_form select.Form-dropDown', + '#project_form input[type="checkbox"]', + '#project_form .ui-spinner-input', + ] + } }); module.exports = { diff --git a/awx/ui/client/test/e2e/objects/sections/createFormSection.js b/awx/ui/client/test/e2e/objects/sections/createFormSection.js index 6d53215c1c..4b68328890 100644 --- a/awx/ui/client/test/e2e/objects/sections/createFormSection.js +++ b/awx/ui/client/test/e2e/objects/sections/createFormSection.js @@ -79,13 +79,48 @@ const generatorOptions = { }; -const createFormSection = function({ selector, labels, strategy }) { +const checkAllFieldsDisabled = function() { + let client = this.client.api; + + // let selectors = this.props.formElementSelectors ? this.props.formElementSelectors : [ + // '.at-Input', + // '.Form-textInput', + // 'select.Form-dropDown', + // 'input[type="checkbox"]', + // 'input[type="radio"]', + // '.ui-spinner-input', + // '.Form-textArea', + // '.ScheduleToggle-switch', + // '.Form-lookupButton' + // ]; + + let selectors = this.props.formElementSelectors ? this.props.formElementSelectors : [ + '.at-Input' + ]; + + selectors.forEach(function(selector) { + client.elements('css selector', selector, inputs => { + inputs.value.map(o => o.ELEMENT).forEach(id => { + client.elementIdAttribute(id, 'disabled', ({ value }) => { + client.assert.equal(value, 'true'); + }); + }); + }); + }); +}; + + +const createFormSection = function({ selector, labels, strategy, props }) { let options = generatorOptions[strategy || 'default']; let formSection = { selector, sections: {}, - elements: {} + elements: {}, + commands: [{ + checkAllFieldsDisabled: checkAllFieldsDisabled + }], + props: props }; for (let key in labels) { @@ -95,7 +130,7 @@ const createFormSection = function({ selector, labels, strategy }) { formSection.elements[key] = inputElement; formSection.sections[key] = inputContainer; - }; + } return formSection; }; diff --git a/awx/ui/client/test/e2e/objects/teams.js b/awx/ui/client/test/e2e/objects/teams.js index a09daf07f7..fe2eb7ee12 100644 --- a/awx/ui/client/test/e2e/objects/teams.js +++ b/awx/ui/client/test/e2e/objects/teams.js @@ -10,7 +10,13 @@ import permissions from './sections/permissions.js'; import search from './sections/search.js'; const details = createFormSection({ - selector: 'form' + selector: 'form', + props: { + formElementSelectors: [ + '#team_form input.Form-textInput', + '#team_form .Form-lookupButton' + ] + } }); module.exports = { diff --git a/awx/ui/client/test/e2e/objects/templates.js b/awx/ui/client/test/e2e/objects/templates.js index 700ac2565c..0ddcaeb978 100644 --- a/awx/ui/client/test/e2e/objects/templates.js +++ b/awx/ui/client/test/e2e/objects/templates.js @@ -10,7 +10,17 @@ import permissions from './sections/permissions.js'; import search from './sections/search.js'; const details = createFormSection({ - selector: 'form' + selector: 'form', + props: { + formElementSelectors: [ + '#job_template_form .Form-textInput', + '#job_template_form select.Form-dropDown', + '#job_template_form .Form-textArea', + '#job_template_form input[type="checkbox"]', + '#job_template_form .ui-spinner-input', + '#job_template_form .ScheduleToggle-switch' + ] + } }); module.exports = { diff --git a/awx/ui/client/test/e2e/objects/users.js b/awx/ui/client/test/e2e/objects/users.js index 2add0e6c92..997f1b003b 100644 --- a/awx/ui/client/test/e2e/objects/users.js +++ b/awx/ui/client/test/e2e/objects/users.js @@ -10,7 +10,13 @@ import permissions from './sections/permissions.js'; import search from './sections/search.js'; const details = createFormSection({ - selector: 'form' + selector: 'form', + props: { + formElementSelectors: [ + '#user_form .Form-textInput', + '#user_form select.Form-dropDown' + ] + } }); module.exports = { diff --git a/awx/ui/client/test/e2e/tests/test-auditor-read-only-forms.js b/awx/ui/client/test/e2e/tests/test-auditor-read-only-forms.js index 99612822c2..6de6d1591a 100644 --- a/awx/ui/client/test/e2e/tests/test-auditor-read-only-forms.js +++ b/awx/ui/client/test/e2e/tests/test-auditor-read-only-forms.js @@ -82,18 +82,6 @@ let credentials, inventories, teams; -function checkDisabledElements(client, selectors) { - selectors.forEach(function(selector) { - client.elements('css selector', selector, inputs => { - inputs.value.map(o => o.ELEMENT).forEach(id => { - client.elementIdAttribute(id, 'disabled', ({ value }) => { - client.assert.equal(value, 'true'); - }); - }); - }); - }); -} - function navigateAndWaitForSpinner(client, url) { client .url(url) @@ -104,15 +92,17 @@ function navigateAndWaitForSpinner(client, url) { module.exports = { before: function (client, done) { - credentials = client.useCss().page.credentials(); - inventoryScripts = client.useCss().page.inventoryScripts(); - templates = client.useCss().page.templates(); - notificationTemplates = client.useCss().page.notificationTemplates(); - organizations = client.useCss().page.organizations(); - projects = client.useCss().page.projects(); - users = client.useCss().page.users(); - inventories = client.useCss().page.inventories(); - teams = client.useCss().page.teams(); + client.useCss(); + + credentials = client.page.credentials(); + inventoryScripts = client.page.inventoryScripts(); + templates = client.page.templates(); + notificationTemplates = client.page.notificationTemplates(); + organizations = client.page.organizations(); + projects = client.page.projects(); + users = client.page.users(); + inventories = client.page.inventories(); + teams = client.page.teams(); client.login(); client.waitForAngular(); @@ -233,32 +223,8 @@ module.exports = { }; }); }, - ({ adminJobTemplate, - adminAWSCredential, - adminMachineCredential, - adminOrganization, - adminInventoryScript, - adminNotificationTemplate, - adminProject, - adminSmartInventory, - adminStandardInventory, - adminTeam, - adminUser, - auditor }) => { - store.created = { - adminJobTemplate, - adminAWSCredential, - adminMachineCredential, - adminOrganization, - adminInventoryScript, - adminNotificationTemplate, - adminProject, - adminSmartInventory, - adminStandardInventory, - adminTeam, - adminUser, - auditor - }; + created => { + store.created = created; client.login(store.auditor.username, store.auditor.password); @@ -271,7 +237,7 @@ module.exports = { credentials.section.edit .expect.element('@title').text.contain(store.created.adminAWSCredential.name); - checkDisabledElements(client, ['.at-Input']); + credentials.section.edit.section.details.checkAllFieldsDisabled(); }, 'verify an auditor\'s inventory scripts inputs are read-only': function (client) { navigateAndWaitForSpinner(client, `${inventoryScripts.url()}/${store.created.adminInventoryScript.id}/`); @@ -279,57 +245,31 @@ module.exports = { inventoryScripts.section.edit .expect.element('@title').text.contain(store.created.adminInventoryScript.name); - let selectors = [ - '#inventory_script_form .Form-textInput', - '#inventory_script_form .Form-textArea', - '#inventory_script_form .Form-lookupButton' - ]; - - checkDisabledElements(client, selectors); + inventoryScripts.section.edit.section.details.checkAllFieldsDisabled(); }, 'verify save button hidden from auditor on inventory scripts form': function () { inventoryScripts.expect.element('@save').to.not.be.visible; }, - 'verify an auditor\'s job template inputs are read-only': function (client) { - navigateAndWaitForSpinner(client, `${templates.url()}/job_template/${store.created.adminJobTemplate.id}/`); - - templates.section.editJobTemplate - .expect.element('@title').text.contain(store.created.adminJobTemplate.name); - - client.pause(2000); - - let selectors = [ - '#job_template_form .Form-textInput', - '#job_template_form select.Form-dropDown', - '#job_template_form .Form-textArea', - '#job_template_form input[type="checkbox"]', - '#job_template_form .ui-spinner-input', - '#job_template_form .ScheduleToggle-switch' - ]; - - checkDisabledElements(client, selectors); - }, - 'verify save button hidden from auditor on job templates form': function () { - templates.expect.element('@save').to.not.be.visible; - }, + // TODO: re-enable these tests when JT edit has been re-factored to reliably show/remove the loading spinner + // only one time. Without this, we can't tell when all the requisite data is available. + // 'verify an auditor\'s job template inputs are read-only': function (client) { + // navigateAndWaitForSpinner(client, `${templates.url()}/job_template/${store.created.adminJobTemplate.id}/`); + // + // templates.section.editJobTemplate + // .expect.element('@title').text.contain(store.created.adminJobTemplate.name); + // + // templates.section.edit.section.details.checkAllFieldsDisabled(); + // }, + // 'verify save button hidden from auditor on job templates form': function () { + // templates.expect.element('@save').to.not.be.visible; + // }, 'verify an auditor\'s notification templates inputs are read-only': function (client) { navigateAndWaitForSpinner(client, `${notificationTemplates.url()}/${store.created.adminNotificationTemplate.id}/`); notificationTemplates.section.edit .expect.element('@title').text.contain(store.created.adminNotificationTemplate.name); - let selectors = [ - '#notification_template_form .Form-textInput', - '#notification_template_form select.Form-dropDown', - '#notification_template_form input[type="checkbox"]', - '#notification_template_form input[type="radio"]', - '#notification_template_form .ui-spinner-input', - '#notification_template_form .Form-textArea', - '#notification_template_form .ScheduleToggle-switch', - '#notification_template_form .Form-lookupButton' - ]; - - checkDisabledElements(client, selectors); + notificationTemplates.section.edit.section.details.checkAllFieldsDisabled(); }, 'verify save button hidden from auditor on notification templates page': function () { notificationTemplates.expect.element('@save').to.not.be.visible; @@ -340,13 +280,7 @@ module.exports = { organizations.section.edit .expect.element('@title').text.contain(store.created.adminOrganization.name); - let selectors = [ - '#organization_form input.Form-textInput', - '#organization_form .Form-lookupButton', - '#organization_form #InstanceGroups' - ]; - - checkDisabledElements(client, selectors); + organizations.section.edit.section.details.checkAllFieldsDisabled(); }, 'verify save button hidden from auditor on organizations form': function () { organizations.expect.element('@save').to.not.be.visible; @@ -357,14 +291,7 @@ module.exports = { inventories.section.editSmartInventory .expect.element('@title').text.contain(store.created.adminSmartInventory.name); - let selectors = [ - '#smartinventory_form input.Form-textInput', - '#smartinventory_form textarea.Form-textArea', - '#smartinventory_form .Form-lookupButton', - '#smartinventory_form #InstanceGroups' - ]; - - checkDisabledElements(client, selectors); + inventories.section.editSmartInventory.section.smartInvDetails.checkAllFieldsDisabled(); }, 'verify save button hidden from auditor on smart inventories form': function () { inventories.expect.element('@save').to.not.be.visible; @@ -375,14 +302,7 @@ module.exports = { projects.section.edit .expect.element('@title').text.contain(store.created.adminProject.name); - let selectors = [ - '#project_form .Form-textInput', - '#project_form select.Form-dropDown', - '#project_form input[type="checkbox"]', - '#project_form .ui-spinner-input', - ]; - - checkDisabledElements(client, selectors); + projects.section.edit.section.details.checkAllFieldsDisabled(); }, 'verify save button hidden from auditor on projects form': function () { projects.expect.element('@save').to.not.be.visible; @@ -393,16 +313,7 @@ module.exports = { inventories.section.editStandardInventory .expect.element('@title').text.contain(store.created.adminStandardInventory.name); - let selectors = [ - '#inventory_form .Form-textInput', - '#inventory_form select.Form-dropDown', - '#inventory_form .Form-textArea', - '#inventory_form input[type="checkbox"]', - '#inventory_form .ui-spinner-input', - '#inventory_form .ScheduleToggle-switch' - ]; - - checkDisabledElements(client, selectors); + inventories.section.editStandardInventory.section.standardInvDetails.checkAllFieldsDisabled(); }, 'verify save button hidden from auditor on standard inventory form': function () { inventories.expect.element('@save').to.not.be.visible; @@ -413,12 +324,7 @@ module.exports = { teams.section.edit .expect.element('@title').text.contain(store.created.adminTeam.name); - let selectors = [ - '#team_form input.Form-textInput', - '#team_form .Form-lookupButton' - ]; - - checkDisabledElements(client, selectors); + teams.section.edit.section.details.checkAllFieldsDisabled(); }, 'verify save button hidden from auditor on teams form': function () { teams.expect.element('@save').to.not.be.visible; @@ -429,12 +335,7 @@ module.exports = { users.section.edit .expect.element('@title').text.contain(store.created.adminUser.username); - let selectors = [ - '#user_form .Form-textInput', - '#user_form select.Form-dropDown' - ]; - - checkDisabledElements(client, selectors); + users.section.edit.section.details.checkAllFieldsDisabled(); }, 'verify save button hidden from auditor on users form': function (client) { users.expect.element('@save').to.not.be.visible; From 069ca1c7557bc53919a253c57c059176949f3442 Mon Sep 17 00:00:00 2001 From: Jake McDermott Date: Tue, 26 Sep 2017 17:59:24 -0400 Subject: [PATCH 4/6] add utils for bootstrapping test fixtures --- awx/ui/client/test/e2e/api.js | 104 ++++++++++++ awx/ui/client/test/e2e/fixtures.js | 263 +++++++++++++++++++++++++++++ awx/ui/package.json | 1 + 3 files changed, 368 insertions(+) create mode 100644 awx/ui/client/test/e2e/api.js create mode 100644 awx/ui/client/test/e2e/fixtures.js diff --git a/awx/ui/client/test/e2e/api.js b/awx/ui/client/test/e2e/api.js new file mode 100644 index 0000000000..464401cb24 --- /dev/null +++ b/awx/ui/client/test/e2e/api.js @@ -0,0 +1,104 @@ +import https from 'https'; + +import axios from 'axios'; + +import { + awxURL, + awxUsername, + awxPassword +} from './settings.js'; + + +let authenticated; + +const session = axios.create({ + baseURL: awxURL, + xsrfHeaderName: 'X-CSRFToken', + xsrfCookieName: 'csrftoken', + httpsAgent: new https.Agent({ + rejectUnauthorized: false + }) +}); + + +const endpoint = function(location) { + + if (location.indexOf('/api/v') === 0) { + return location; + } + + if (location.indexOf('://') > 0) { + return location; + } + + return `${awxURL}/api/v2${location}`; +}; + + +const authenticate = function() { + if (authenticated) { + return Promise.resolve(); + } + + let uri = endpoint('/authtoken/'); + + let credentials = { + username: awxUsername, + password: awxPassword + }; + + return session.post(uri, credentials).then(res => { + session.defaults.headers.Authorization = `Token ${res.data.token}`; + authenticated = true; + return res + }); +}; + + +const request = function(method, location, data) { + let uri = endpoint(location); + let action = session[method.toLowerCase()]; + + return authenticate().then(() => action(uri, data)).then(res => { + console.log([ + res.config.method.toUpperCase(), + uri, + res.status, + res.statusText + ].join(' ')); + + return res; + }); +}; + + +const get = function(endpoint, data) { + return request('GET', endpoint, data); +}; + +const options = function(endpoint) { + return request('OPTIONS', endpoint); +}; + +const post = function(endpoint, data) { + return request('POST', endpoint, data); +}; + +const patch = function(endpoint, data) { + return request('PATCH', endpoint, data) +}; + +const put = function(endpoint, data) { + return request('PUT', endpoint, data); +}; + + +module.exports = { + get, + options, + post, + patch, + put, + all: axios.all, + spread: axios.spread +}; diff --git a/awx/ui/client/test/e2e/fixtures.js b/awx/ui/client/test/e2e/fixtures.js new file mode 100644 index 0000000000..0ef3a080f6 --- /dev/null +++ b/awx/ui/client/test/e2e/fixtures.js @@ -0,0 +1,263 @@ +import uuid from 'uuid'; + +import { + all, + get, + post, + spread +} from './api.js'; + + +const sid = uuid().substr(0,8); + +let store = {}; + + +const getOrCreate = function(endpoint, data) { + let identifier = Object.keys(data).find(key => ['name', 'username'].includes(key)); + + if (identifier === undefined) { + throw new Error('A unique key value must be provided.'); + } + + let identity = data[identifier]; + + if (store[endpoint] && store[endpoint][identity]) { + return store[endpoint][identity].then(created => created.data); + } + + if (!store[endpoint]) { + store[endpoint] = {}; + } + + let query = { params: { [identifier]: identity } }; + + store[endpoint][identity] = get(endpoint, query).then(res => { + + if (res.data.results.length > 1) { + return Promise.reject(new Error('More than one matching result.')); + } + + if (res.data.results.length === 1) { + return get(res.data.results[0].url); + } + + if (res.data.results.length === 0) { + return post(endpoint, data); + } + + return Promise.reject(new Error(`unexpected response: ${res}`)); + }); + + return store[endpoint][identity].then(created => created.data); +}; + + +const getOrganization = function() { + return getOrCreate('/organizations/', { + name: `e2e-organization-${sid}` + }); +}; + + +const getInventory = function() { + return getOrganization().then(organization => { + return getOrCreate('/inventories/', { + name: `e2e-inventory-${sid}`, + organization: organization.id + }); + }); +}; + + +const getInventoryScript = function() { + return getOrganization().then(organization => { + return getOrCreate('/inventory_scripts/', { + name: `e2e-inventory-script-${sid}`, + organization: organization.id, + script: '#!/usr/bin/env python' + }); + }); +}; + + +const getAdminAWSCredential = function() { + return all([ + get('/me/'), + getOrCreate('/credential_types/', { + name: "Amazon Web Services" + }) + ]) + .then(spread((me, credentialType) => { + let admin = me.data.results[0]; + return getOrCreate('/credentials/', { + name: `e2e-aws-credential-${sid}`, + credential_type: credentialType.id, + user: admin.id, + inputs: { + username: 'admin', + password: 'password', + security_token: 'AAAAAAAAAAAAAAAA' + } + }); + })); +}; + + +const getAdminMachineCredential = function() { + return all([ + get('/me/'), + getOrCreate('/credential_types/', { name: "Machine" }) + ]) + .then(spread((me, credentialType) => { + let admin = me.data.results[0]; + return getOrCreate('/credentials/', { + name: `e2e-machine-credential-${sid}`, + credential_type: credentialType.id, + user: admin.id + }); + })); +}; + + +const getTeam = function() { + return getOrganization().then(organization => { + return getOrCreate('/teams/', { + name: `e2e-team-${sid}`, + organization: organization.id, + }); + }); +}; + + +const getSmartInventory = function() { + return getOrganization().then(organization => { + return getOrCreate('/inventories/', { + name: `e2e-smart-inventory-${sid}`, + organization: organization.id, + host_filter: 'search=localhost', + kind: 'smart' + }); + }); +}; + + +const getNotificationTemplate = function() { + return getOrganization().then(organization => { + return getOrCreate('/notification_templates/', { + name: `e2e-notification-template-${sid}`, + organization: organization.id, + notification_type: 'slack', + notification_configuration: { + token: '54321GFEDCBAABCDEFG12345', + channels: ['awx-e2e'] + } + }); + }); +}; + + +const getProject = function() { + return getOrganization().then(organization => { + return getOrCreate('/projects/', { + name: `e2e-project-${sid}`, + organization: organization.id, + scm_url: 'https://github.com/ansible/ansible-tower-samples', + scm_type: 'git' + }); + }); +}; + + +const waitForJob = function(endpoint) { + const interval = 2000; + const statuses = ['successful', 'failed', 'error', 'canceled']; + + let attempts = 20; + + return new Promise((resolve, reject) => { + (function pollStatus() { + get(endpoint).then(update => { + let completed = statuses.indexOf(update.data.status) > -1; + if (completed) return resolve(); + if (--attempts <= 0) return reject('Retry limit exceeded.'); + setTimeout(pollStatus, interval); + }); + })(); + }); +}; + + +const getUpdatedProject = function() { + return getProject().then(project => { + let updateURL = project.related.current_update; + if (updateURL) { + return waitForJob(updateURL).then(() => project); + } + return project; + }); +}; + + +const getJobTemplate = function() { + return all([ + getInventory(), + getAdminMachineCredential(), + getUpdatedProject() + ]) + .then(spread((inventory, credential, project) => { + return getOrCreate('/job_templates', { + name: `e2e-job-template-${sid}`, + inventory: inventory.id, + credential: credential.id, + project: project.id, + playbook: 'hello_world.yml' + }); + })); +}; + + +const getAuditor = function() { + return getOrganization().then(organization => { + return getOrCreate('/users/', { + organization: organization.id, + username: `e2e-auditor-${sid}`, + first_name: 'auditor', + last_name: 'last', + email: 'null@ansible.com', + is_superuser: false, + is_system_auditor: true, + password: 'password' + }) + }); +}; + + +const getUser = function() { + return getOrCreate('/users/', { + username: `e2e-user-${sid}`, + first_name: `user-${sid}-first`, + last_name: `user-${sid}-last`, + email: `null-${sid}@ansible.com`, + is_superuser: false, + is_system_auditor: false, + password: 'password' + }); +}; + + +module.exports = { + getAdminAWSCredential, + getAdminMachineCredential, + getAuditor, + getInventory, + getInventoryScript, + getJobTemplate, + getNotificationTemplate, + getOrCreate, + getOrganization, + getSmartInventory, + getTeam, + getUpdatedProject, + getUser +}; diff --git a/awx/ui/package.json b/awx/ui/package.json index f79ca4aa32..bf6880e8bc 100644 --- a/awx/ui/package.json +++ b/awx/ui/package.json @@ -33,6 +33,7 @@ }, "devDependencies": { "angular-mocks": "~1.4.14", + "axios": "^0.16.2", "babel-core": "^6.26.0", "babel-istanbul": "^0.12.2", "babel-loader": "^7.1.2", From 09a6a326d178d7d729a44584ae5b733d1672ab2d Mon Sep 17 00:00:00 2001 From: Jake McDermott Date: Tue, 26 Sep 2017 20:04:51 -0400 Subject: [PATCH 5/6] use fixture data in read-only form test --- awx/ui/client/test/e2e/commands/login.js | 2 +- .../e2e/objects/sections/createFormSection.js | 24 +- .../e2e/tests/test-auditor-read-only-forms.js | 291 ++++-------------- 3 files changed, 73 insertions(+), 244 deletions(-) diff --git a/awx/ui/client/test/e2e/commands/login.js b/awx/ui/client/test/e2e/commands/login.js index 8ae7a4d1ca..ddf753c496 100644 --- a/awx/ui/client/test/e2e/commands/login.js +++ b/awx/ui/client/test/e2e/commands/login.js @@ -26,7 +26,7 @@ Login.prototype.command = function(username, password) { .waitForElementVisible('div.spinny') .waitForElementNotVisible('div.spinny'); - // tempoary hack while login issue is resolved + // temporary hack while login issue is resolved this.api.elements('css selector', '.LoginModal-alert', result => { let alertVisible = false; result.value.map(i => i.ELEMENT).forEach(id => { diff --git a/awx/ui/client/test/e2e/objects/sections/createFormSection.js b/awx/ui/client/test/e2e/objects/sections/createFormSection.js index 4b68328890..1bdeeb0c54 100644 --- a/awx/ui/client/test/e2e/objects/sections/createFormSection.js +++ b/awx/ui/client/test/e2e/objects/sections/createFormSection.js @@ -73,27 +73,9 @@ const generateInputSelectors = function(label, containerElements) { }; -const generatorOptions = { - default: inputContainerElements, - legacy: legacyContainerElements -}; - - const checkAllFieldsDisabled = function() { let client = this.client.api; - // let selectors = this.props.formElementSelectors ? this.props.formElementSelectors : [ - // '.at-Input', - // '.Form-textInput', - // 'select.Form-dropDown', - // 'input[type="checkbox"]', - // 'input[type="radio"]', - // '.ui-spinner-input', - // '.Form-textArea', - // '.ScheduleToggle-switch', - // '.Form-lookupButton' - // ]; - let selectors = this.props.formElementSelectors ? this.props.formElementSelectors : [ '.at-Input' ]; @@ -110,6 +92,12 @@ const checkAllFieldsDisabled = function() { }; +const generatorOptions = { + default: inputContainerElements, + legacy: legacyContainerElements +}; + + const createFormSection = function({ selector, labels, strategy, props }) { let options = generatorOptions[strategy || 'default']; diff --git a/awx/ui/client/test/e2e/tests/test-auditor-read-only-forms.js b/awx/ui/client/test/e2e/tests/test-auditor-read-only-forms.js index 6de6d1591a..e0646fe718 100644 --- a/awx/ui/client/test/e2e/tests/test-auditor-read-only-forms.js +++ b/awx/ui/client/test/e2e/tests/test-auditor-read-only-forms.js @@ -1,76 +1,23 @@ -import uuid from 'uuid'; +import { all } from '../api.js'; + +import { + getAdminAWSCredential, + getAdminMachineCredential, + getAuditor, + getInventory, + getInventoryScript, + getNotificationTemplate, + getOrCreate, + getOrganization, + getSmartInventory, + getTeam, + getUpdatedProject, + getUser +} from '../fixtures.js'; -let testID = uuid().substr(0,8); - -let store = { - auditor: { - username: `auditor-${testID}`, - first_name: 'auditor', - last_name: 'last', - email: 'null@ansible.com', - is_superuser: false, - is_system_auditor: true, - password: 'password' - }, - adminJobTemplate: { - name: `adminJobTemplate-${testID}`, - description: `adminJobTemplate-description-${testID}`, - project: 103, - playbook: 'check.yml' - }, - adminAWSCredential: { - name: `adminAWSCredential-${testID}`, - description: `adminAWSCredential-description-${testID}`, - inputs: { - username: 'username', - password: 'password', - security_token: 'AAAAAAAAAAAAAAAAAAAAAAAAAA' - } - }, - adminMachineCredential: { - name: `adminMachineCredential-${testID}` - }, - adminOrganization: { - name: `adminOrganization-${testID}`, - }, - adminInventoryScript: { - name: `adminInventoryScript-${testID}`, - script: '#!/usr/bin/env python' - }, - adminNotificationTemplate: { - name: `adminNotificationTemplate-${testID}`, - notification_configuration: {channels: ["awx-e2e"], token: "foobar"}, - notification_type: "slack" - }, - adminProject: { - name: `adminProject-${testID}`, - scm_type: "git", - scm_url: "https://github.com/ansible/tower-example.git" - }, - adminSmartInventory: { - name: `adminSmartInventory-${testID}`, - host_filter: 'search=localhost', - kind: 'smart' - }, - adminStandardInventory: { - name: `adminStandardInventory-${testID}` - }, - adminTeam: { - name: `adminTeam-${testID}` - }, - adminUser: { - username: `adminUser-${testID}`, - first_name: `adminUser-${testID}-first`, - last_name: `adminUser-${testID}-last`, - email: `null-${testID}@ansible.com`, - is_superuser: false, - is_system_auditor: false, - password: 'password' - }, - created: {} -}; +let data = {}; let credentials, inventoryScripts, @@ -82,6 +29,7 @@ let credentials, inventories, teams; + function navigateAndWaitForSpinner(client, url) { client .url(url) @@ -89,161 +37,54 @@ function navigateAndWaitForSpinner(client, url) { .waitForElementNotVisible('div.spinny'); } + module.exports = { before: function (client, done) { + all([ + getAuditor().then(obj => data.auditor = obj), + getOrganization().then(obj => data.organization = obj), + getInventory().then(obj => data.inventory = obj), + getInventoryScript().then(obj => data.inventoryScript = obj), + getAdminAWSCredential().then(obj => data.adminAWSCredential = obj), + getAdminMachineCredential().then(obj => data.adminMachineCredential = obj), + getSmartInventory().then(obj => data.smartInventory = obj), + getTeam().then(obj => data.team = obj), + getUser().then(obj => data.user = obj), + getNotificationTemplate().then(obj => data.notificationTemplate = obj), + getUpdatedProject().then(obj => data.project = obj) + ]) + .then(() => { + client.useCss(); - client.useCss(); + credentials = client.page.credentials(); + inventoryScripts = client.page.inventoryScripts(); + templates = client.page.templates(); + notificationTemplates = client.page.notificationTemplates(); + organizations = client.page.organizations(); + projects = client.page.projects(); + users = client.page.users(); + inventories = client.page.inventories(); + teams = client.page.teams(); - credentials = client.page.credentials(); - inventoryScripts = client.page.inventoryScripts(); - templates = client.page.templates(); - notificationTemplates = client.page.notificationTemplates(); - organizations = client.page.organizations(); - projects = client.page.projects(); - users = client.page.users(); - inventories = client.page.inventories(); - teams = client.page.teams(); - - client.login(); - client.waitForAngular(); - - client.inject([store, '$http'], (store, $http) => { - - let { adminJobTemplate, - adminAWSCredential, - adminMachineCredential, - adminOrganization, - adminInventoryScript, - adminNotificationTemplate, - adminProject, - adminSmartInventory, - adminStandardInventory, - adminTeam, - adminUser, - auditor } = store; - - return $http.get('/api/v2/me') - .then(({ data }) => { - let resource = 'Amazon%20Web%20Services+cloud'; - adminAWSCredential.user = data.results[0].id; - - return $http.get(`/api/v2/credential_types/${resource}`); - }) - .then(({ data }) => { - adminAWSCredential.credential_type = data.id; - - return $http.post('/api/v2/credentials/', adminAWSCredential); - }) - .then(({ data }) => { - adminAWSCredential = data; - - return $http.post('/api/v2/organizations/', adminOrganization); - }) - .then(({ data }) => { - adminOrganization = data; - adminInventoryScript.organization = data.id; - adminNotificationTemplate.organization = data.id; - adminProject.organization = data.id; - adminSmartInventory.organization = data.id; - adminStandardInventory.organization = data.id; - adminTeam.organization = data.id; - adminUser.organization = data.id; - adminMachineCredential.organization = data.id; - - return $http.post('/api/v2/teams/', adminTeam); - }) - .then(({ data }) => { - adminTeam = data; - - return $http.get('/api/v2/credential_types/Machine+ssh'); - }) - .then(({ data }) => { - adminMachineCredential.credential_type = data.id; - - return $http.post('/api/v2/credentials/', adminMachineCredential); - }) - .then(({ data }) => { - adminMachineCredential = data; - adminJobTemplate.credential = data.id; - - return $http.post('/api/v2/users/', adminUser); - }) - .then(({ data }) => { - adminUser = data; - - return $http.post('/api/v2/notification_templates/', adminNotificationTemplate); - }) - .then(({ data }) => { - adminNotificationTemplate = data; - - return $http.post('/api/v2/inventory_scripts/', adminInventoryScript); - }) - .then(({ data }) => { - adminInventoryScript = data; - - return $http.post('/api/v2/projects/', adminProject); - }) - .then(({ data }) => { - adminProject = data; - - return $http.post('/api/v2/inventories/', adminSmartInventory); - }) - .then(({ data }) => { - adminSmartInventory = data; - - return $http.post('/api/v2/inventories/', adminStandardInventory); - }) - .then(({ data }) => { - adminStandardInventory = data; - adminJobTemplate.inventory = data.id; - - return $http.post('/api/v2/job_templates/', adminJobTemplate); - }) - .then(({ data }) => { - adminJobTemplate = data; - - return $http.post('/api/v2/users/', auditor); - }) - .then(({ data }) => { - auditor = data; - - return { - adminJobTemplate, - adminAWSCredential, - adminMachineCredential, - adminOrganization, - adminInventoryScript, - adminNotificationTemplate, - adminProject, - adminSmartInventory, - adminStandardInventory, - adminTeam, - adminUser, - auditor - }; - }); - }, - created => { - store.created = created; - - client.login(store.auditor.username, store.auditor.password); + client.login(data.auditor.username, data.auditor.password); + client.waitForAngular(); done(); }); }, 'verify an auditor\'s credentials inputs are read-only': function (client) { - navigateAndWaitForSpinner(client, `${credentials.url()}/${store.created.adminAWSCredential.id}/`); + navigateAndWaitForSpinner(client, `${credentials.url()}/${data.adminAWSCredential.id}/`); credentials.section.edit - .expect.element('@title').text.contain(store.created.adminAWSCredential.name); - + .expect.element('@title').text.contain(data.adminAWSCredential.name); + credentials.section.edit.section.details.checkAllFieldsDisabled(); }, 'verify an auditor\'s inventory scripts inputs are read-only': function (client) { - navigateAndWaitForSpinner(client, `${inventoryScripts.url()}/${store.created.adminInventoryScript.id}/`); + navigateAndWaitForSpinner(client, `${inventoryScripts.url()}/${data.inventoryScript.id}/`); inventoryScripts.section.edit - .expect.element('@title').text.contain(store.created.adminInventoryScript.name); + .expect.element('@title').text.contain(data.inventoryScript.name); inventoryScripts.section.edit.section.details.checkAllFieldsDisabled(); }, @@ -253,10 +94,10 @@ module.exports = { // TODO: re-enable these tests when JT edit has been re-factored to reliably show/remove the loading spinner // only one time. Without this, we can't tell when all the requisite data is available. // 'verify an auditor\'s job template inputs are read-only': function (client) { - // navigateAndWaitForSpinner(client, `${templates.url()}/job_template/${store.created.adminJobTemplate.id}/`); + // navigateAndWaitForSpinner(client, `${templates.url()}/job_template/${data.jobTemplate.id}/`); // // templates.section.editJobTemplate - // .expect.element('@title').text.contain(store.created.adminJobTemplate.name); + // .expect.element('@title').text.contain(data.jobTemplate.name); // // templates.section.edit.section.details.checkAllFieldsDisabled(); // }, @@ -264,10 +105,10 @@ module.exports = { // templates.expect.element('@save').to.not.be.visible; // }, 'verify an auditor\'s notification templates inputs are read-only': function (client) { - navigateAndWaitForSpinner(client, `${notificationTemplates.url()}/${store.created.adminNotificationTemplate.id}/`); + navigateAndWaitForSpinner(client, `${notificationTemplates.url()}/${data.notificationTemplate.id}/`); notificationTemplates.section.edit - .expect.element('@title').text.contain(store.created.adminNotificationTemplate.name); + .expect.element('@title').text.contain(data.notificationTemplate.name); notificationTemplates.section.edit.section.details.checkAllFieldsDisabled(); }, @@ -275,10 +116,10 @@ module.exports = { notificationTemplates.expect.element('@save').to.not.be.visible; }, 'verify an auditor\'s organizations inputs are read-only': function (client) { - navigateAndWaitForSpinner(client, `${organizations.url()}/${store.created.adminOrganization.id}/`); + navigateAndWaitForSpinner(client, `${organizations.url()}/${data.organization.id}/`); organizations.section.edit - .expect.element('@title').text.contain(store.created.adminOrganization.name); + .expect.element('@title').text.contain(data.organization.name); organizations.section.edit.section.details.checkAllFieldsDisabled(); }, @@ -286,10 +127,10 @@ module.exports = { organizations.expect.element('@save').to.not.be.visible; }, 'verify an auditor\'s smart inventory inputs are read-only': function (client) { - navigateAndWaitForSpinner(client, `${inventories.url()}/smart/${store.created.adminSmartInventory.id}/`); + navigateAndWaitForSpinner(client, `${inventories.url()}/smart/${data.smartInventory.id}/`); inventories.section.editSmartInventory - .expect.element('@title').text.contain(store.created.adminSmartInventory.name); + .expect.element('@title').text.contain(data.smartInventory.name); inventories.section.editSmartInventory.section.smartInvDetails.checkAllFieldsDisabled(); }, @@ -297,10 +138,10 @@ module.exports = { inventories.expect.element('@save').to.not.be.visible; }, 'verify an auditor\'s project inputs are read-only': function (client) { - navigateAndWaitForSpinner(client, `${projects.url()}/${store.created.adminProject.id}/`); + navigateAndWaitForSpinner(client, `${projects.url()}/${data.project.id}/`); projects.section.edit - .expect.element('@title').text.contain(store.created.adminProject.name); + .expect.element('@title').text.contain(data.project.name); projects.section.edit.section.details.checkAllFieldsDisabled(); }, @@ -308,10 +149,10 @@ module.exports = { projects.expect.element('@save').to.not.be.visible; }, 'verify an auditor\'s standard inventory inputs are read-only': function (client) { - navigateAndWaitForSpinner(client, `${inventories.url()}/inventory/${store.created.adminStandardInventory.id}/`); + navigateAndWaitForSpinner(client, `${inventories.url()}/inventory/${data.inventory.id}/`); inventories.section.editStandardInventory - .expect.element('@title').text.contain(store.created.adminStandardInventory.name); + .expect.element('@title').text.contain(data.inventory.name); inventories.section.editStandardInventory.section.standardInvDetails.checkAllFieldsDisabled(); }, @@ -319,10 +160,10 @@ module.exports = { inventories.expect.element('@save').to.not.be.visible; }, 'verify an auditor\'s teams inputs are read-only': function (client) { - navigateAndWaitForSpinner(client, `${teams.url()}/${store.created.adminTeam.id}/`); + navigateAndWaitForSpinner(client, `${teams.url()}/${data.team.id}/`); teams.section.edit - .expect.element('@title').text.contain(store.created.adminTeam.name); + .expect.element('@title').text.contain(data.team.name); teams.section.edit.section.details.checkAllFieldsDisabled(); }, @@ -330,10 +171,10 @@ module.exports = { teams.expect.element('@save').to.not.be.visible; }, 'verify an auditor\'s user inputs are read-only': function (client) { - navigateAndWaitForSpinner(client, `${users.url()}/${store.created.adminUser.id}/`); + navigateAndWaitForSpinner(client, `${users.url()}/${data.user.id}/`); users.section.edit - .expect.element('@title').text.contain(store.created.adminUser.username); + .expect.element('@title').text.contain(data.user.username); users.section.edit.section.details.checkAllFieldsDisabled(); }, From fddecfdd25bec6f6eaa3747f4ed7fbfe64bae5c5 Mon Sep 17 00:00:00 2001 From: Jake McDermott Date: Tue, 26 Sep 2017 23:26:41 -0400 Subject: [PATCH 6/6] use launch_url to specify awx host for workers --- awx/ui/client/test/e2e/README.md | 2 +- awx/ui/client/test/e2e/objects/activityStream.js | 2 +- awx/ui/client/test/e2e/objects/credentialTypes.js | 2 +- awx/ui/client/test/e2e/objects/credentials.js | 2 +- awx/ui/client/test/e2e/objects/inventories.js | 2 +- awx/ui/client/test/e2e/objects/inventoryScripts.js | 2 +- awx/ui/client/test/e2e/objects/login.js | 2 +- awx/ui/client/test/e2e/objects/notificationTemplates.js | 2 +- awx/ui/client/test/e2e/objects/organizations.js | 2 +- awx/ui/client/test/e2e/objects/projects.js | 2 +- awx/ui/client/test/e2e/objects/teams.js | 2 +- awx/ui/client/test/e2e/objects/templates.js | 2 +- awx/ui/client/test/e2e/objects/users.js | 2 +- awx/ui/client/test/e2e/settings.js | 2 ++ 14 files changed, 15 insertions(+), 13 deletions(-) diff --git a/awx/ui/client/test/e2e/README.md b/awx/ui/client/test/e2e/README.md index 8cd57f38cd..b4c1a0aab8 100644 --- a/awx/ui/client/test/e2e/README.md +++ b/awx/ui/client/test/e2e/README.md @@ -17,7 +17,7 @@ docker-compose \ up --scale chrome=2 --scale firefox=0 # run headlessly with multiple workers on the cluster -AWX_E2E_URL='https://awx:8043' AWX_E2E_WORKERS=2 npm --prefix awx/ui run e2e +AWX_E2E_LAUNCH_URL='https://awx:8043' AWX_E2E_WORKERS=2 npm --prefix awx/ui run e2e ``` **Note:** Unless overridden in [settings](settings.js), tests will run against `localhost:8043`. diff --git a/awx/ui/client/test/e2e/objects/activityStream.js b/awx/ui/client/test/e2e/objects/activityStream.js index c59a615b9a..b122c02c68 100644 --- a/awx/ui/client/test/e2e/objects/activityStream.js +++ b/awx/ui/client/test/e2e/objects/activityStream.js @@ -1,6 +1,6 @@ module.exports = { url() { - return `${this.api.globals.awxURL}/#/activity_stream` + return `${this.api.globals.launch_url}/#/activity_stream` }, elements: { title: '.List-titleText', diff --git a/awx/ui/client/test/e2e/objects/credentialTypes.js b/awx/ui/client/test/e2e/objects/credentialTypes.js index 9b2f4f4385..4584309543 100644 --- a/awx/ui/client/test/e2e/objects/credentialTypes.js +++ b/awx/ui/client/test/e2e/objects/credentialTypes.js @@ -53,7 +53,7 @@ const listPanel = { module.exports = { url() { - return `${this.api.globals.awxURL}/#/credential_types` + return `${this.api.globals.launch_url}/#/credential_types` }, sections: { header, diff --git a/awx/ui/client/test/e2e/objects/credentials.js b/awx/ui/client/test/e2e/objects/credentials.js index 783cd7526e..7cb0a6de78 100644 --- a/awx/ui/client/test/e2e/objects/credentials.js +++ b/awx/ui/client/test/e2e/objects/credentials.js @@ -229,7 +229,7 @@ const details = _.merge({}, common, { module.exports = { url() { - return `${this.api.globals.awxURL}/#/credentials` + return `${this.api.globals.launch_url}/#/credentials` }, sections: { header, diff --git a/awx/ui/client/test/e2e/objects/inventories.js b/awx/ui/client/test/e2e/objects/inventories.js index 1bbae1a1e9..bbef08ff2e 100644 --- a/awx/ui/client/test/e2e/objects/inventories.js +++ b/awx/ui/client/test/e2e/objects/inventories.js @@ -37,7 +37,7 @@ const smartInvDetails = createFormSection({ module.exports = { url() { - return `${this.api.globals.awxURL}/#/inventories`; + return `${this.api.globals.launch_url}/#/inventories`; }, sections: { header, diff --git a/awx/ui/client/test/e2e/objects/inventoryScripts.js b/awx/ui/client/test/e2e/objects/inventoryScripts.js index a34cb6dca4..3a9da46f00 100644 --- a/awx/ui/client/test/e2e/objects/inventoryScripts.js +++ b/awx/ui/client/test/e2e/objects/inventoryScripts.js @@ -22,7 +22,7 @@ const details = createFormSection({ module.exports = { url() { - return `${this.api.globals.awxURL}/#/inventory_scripts`; + return `${this.api.globals.launch_url}/#/inventory_scripts`; }, sections: { header, diff --git a/awx/ui/client/test/e2e/objects/login.js b/awx/ui/client/test/e2e/objects/login.js index 12bdd45485..7613ee855b 100644 --- a/awx/ui/client/test/e2e/objects/login.js +++ b/awx/ui/client/test/e2e/objects/login.js @@ -1,6 +1,6 @@ module.exports = { url() { - return `${this.api.globals.awxURL}/#/login` + return `${this.api.globals.launch_url}/#/login` }, elements: { username: '#login-username', diff --git a/awx/ui/client/test/e2e/objects/notificationTemplates.js b/awx/ui/client/test/e2e/objects/notificationTemplates.js index dc7371eaaf..72ebd53045 100644 --- a/awx/ui/client/test/e2e/objects/notificationTemplates.js +++ b/awx/ui/client/test/e2e/objects/notificationTemplates.js @@ -27,7 +27,7 @@ const details = createFormSection({ module.exports = { url() { - return `${this.api.globals.awxURL}/#/notification_templates`; + return `${this.api.globals.launch_url}/#/notification_templates`; }, sections: { header, diff --git a/awx/ui/client/test/e2e/objects/organizations.js b/awx/ui/client/test/e2e/objects/organizations.js index ca945ff269..b17c1d55f1 100644 --- a/awx/ui/client/test/e2e/objects/organizations.js +++ b/awx/ui/client/test/e2e/objects/organizations.js @@ -20,7 +20,7 @@ const details = createFormSection({ module.exports = { url() { - return `${this.api.globals.awxURL}/#/organizations`; + return `${this.api.globals.launch_url}/#/organizations`; }, sections: { header, diff --git a/awx/ui/client/test/e2e/objects/projects.js b/awx/ui/client/test/e2e/objects/projects.js index 399468267a..e1956a8e83 100644 --- a/awx/ui/client/test/e2e/objects/projects.js +++ b/awx/ui/client/test/e2e/objects/projects.js @@ -23,7 +23,7 @@ const details = createFormSection({ module.exports = { url() { - return `${this.api.globals.awxURL}/#/projects`; + return `${this.api.globals.launch_url}/#/projects`; }, sections: { header, diff --git a/awx/ui/client/test/e2e/objects/teams.js b/awx/ui/client/test/e2e/objects/teams.js index fe2eb7ee12..245ee2eba2 100644 --- a/awx/ui/client/test/e2e/objects/teams.js +++ b/awx/ui/client/test/e2e/objects/teams.js @@ -21,7 +21,7 @@ const details = createFormSection({ module.exports = { url() { - return `${this.api.globals.awxURL}/#/teams`; + return `${this.api.globals.launch_url}/#/teams`; }, sections: { header, diff --git a/awx/ui/client/test/e2e/objects/templates.js b/awx/ui/client/test/e2e/objects/templates.js index 0ddcaeb978..4e3b69cc4f 100644 --- a/awx/ui/client/test/e2e/objects/templates.js +++ b/awx/ui/client/test/e2e/objects/templates.js @@ -25,7 +25,7 @@ const details = createFormSection({ module.exports = { url() { - return `${this.api.globals.awxURL}/#/templates`; + return `${this.api.globals.launch_url}/#/templates`; }, sections: { header, diff --git a/awx/ui/client/test/e2e/objects/users.js b/awx/ui/client/test/e2e/objects/users.js index 997f1b003b..9d8cdc0eeb 100644 --- a/awx/ui/client/test/e2e/objects/users.js +++ b/awx/ui/client/test/e2e/objects/users.js @@ -21,7 +21,7 @@ const details = createFormSection({ module.exports = { url() { - return `${this.api.globals.awxURL}/#/users`; + return `${this.api.globals.launch_url}/#/users`; }, sections: { header, diff --git a/awx/ui/client/test/e2e/settings.js b/awx/ui/client/test/e2e/settings.js index 6496ba0b75..0a5c892461 100644 --- a/awx/ui/client/test/e2e/settings.js +++ b/awx/ui/client/test/e2e/settings.js @@ -3,6 +3,7 @@ const AWX_E2E_USERNAME = process.env.AWX_E2E_USERNAME || 'awx-e2e'; const AWX_E2E_PASSWORD = process.env.AWX_E2E_PASSWORD || 'password'; const AWX_E2E_SELENIUM_HOST = process.env.AWX_E2E_SELENIUM_HOST || 'localhost'; const AWX_E2E_SELENIUM_PORT = process.env.AWX_E2E_SELENIUM_PORT || 4444; +const AWX_E2E_LAUNCH_URL = process.env.AWX_E2E_LAUNCH_URL || AWX_E2E_URL; const AWX_E2E_TIMEOUT_SHORT = process.env.AWX_E2E_TIMEOUT_SHORT || 1000; const AWX_E2E_TIMEOUT_MEDIUM = process.env.AWX_E2E_TIMEOUT_MEDIUM || 5000; const AWX_E2E_TIMEOUT_LONG = process.env.AWX_E2E_TIMEOUT_LONG || 10000; @@ -20,6 +21,7 @@ module.exports = { retryAssertionTimeout: AWX_E2E_TIMEOUT_MEDIUM, selenium_host: AWX_E2E_SELENIUM_HOST, selenium_port: AWX_E2E_SELENIUM_PORT, + launch_url: AWX_E2E_LAUNCH_URL, shortTimeout: AWX_E2E_TIMEOUT_SHORT, waitForConditionTimeout: AWX_E2E_TIMEOUT_MEDIUM, test_workers: {