Merge pull request #1953 from mabashian/jt-launch-automated-tests

First pass at JT launch with prompts automated tests
This commit is contained in:
John Hill 2018-06-22 14:37:03 -04:00 committed by GitHub
commit aa98842364
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 208 additions and 30 deletions

View File

@ -35,7 +35,7 @@
</ul>
</div>
</div>
<at-list results="templates">
<at-list results="templates" id="templates_list">
<at-row ng-repeat="template in templates"
ng-class="{'at-Row--active': (template.id === vm.activeId)}"
template-id="{{ template.id }}"

View File

@ -1,20 +1,20 @@
<div class="Prompt">
<div class="Prompt" id="prompt_modal">
<at-modal ng-if="vm.promptDataClone">
<at-tab-group>
<at-tab ng-if="vm.steps.inventory.tab" state="vm.steps.inventory.tab">{{:: vm.strings.get('prompt.INVENTORY') }}</at-tab>
<at-tab ng-if="vm.steps.credential.tab" state="vm.steps.credential.tab">{{:: vm.strings.get('prompt.CREDENTIAL') }}</at-tab>
<at-tab ng-if="vm.steps.other_prompts.tab" state="vm.steps.other_prompts.tab">{{:: vm.strings.get('prompt.OTHER_PROMPTS') }}</at-tab>
<at-tab ng-if="vm.steps.survey.tab" state="vm.steps.survey.tab">{{:: vm.strings.get('prompt.SURVEY') }}</at-tab>
<at-tab state="vm.steps.preview.tab">{{:: vm.strings.get('prompt.PREVIEW') }}</at-tab>
<at-tab id="prompt_inventory_tab" ng-if="vm.steps.inventory.tab" state="vm.steps.inventory.tab">{{:: vm.strings.get('prompt.INVENTORY') }}</at-tab>
<at-tab id="prompt_credential_tab" ng-if="vm.steps.credential.tab" state="vm.steps.credential.tab">{{:: vm.strings.get('prompt.CREDENTIAL') }}</at-tab>
<at-tab id="prompt_other_prompts_tab" ng-if="vm.steps.other_prompts.tab" state="vm.steps.other_prompts.tab">{{:: vm.strings.get('prompt.OTHER_PROMPTS') }}</at-tab>
<at-tab id="prompt_survey_tab" ng-if="vm.steps.survey.tab" state="vm.steps.survey.tab">{{:: vm.strings.get('prompt.SURVEY') }}</at-tab>
<at-tab id="prompt_preview_tab" state="vm.steps.preview.tab">{{:: vm.strings.get('prompt.PREVIEW') }}</at-tab>
</at-tab-group>
<div class="Prompt-step">
<div ng-if="vm.steps.inventory.includeStep" ng-show="vm.steps.inventory.tab._active">
<div ng-if="vm.steps.inventory.includeStep" ng-show="vm.steps.inventory.tab._active" id="prompt_inventory_step">
<prompt-inventory
prompt-data="vm.promptDataClone"
read-only-prompts="vm.readOnlyPrompts">
</prompt-inventory>
</div>
<div ng-if="vm.steps.credential.includeStep" ng-show="vm.steps.credential.tab._active">
<div ng-if="vm.steps.credential.includeStep" ng-show="vm.steps.credential.tab._active" id="prompt_credential_step">
<prompt-credential
prompt-data="vm.promptDataClone"
credential-passwords-form="vm.forms.credentialPasswords"
@ -22,7 +22,7 @@
read-only-prompts="vm.readOnlyPrompts">
</prompt-credential>
</div>
<div ng-if="vm.steps.other_prompts.includeStep" ng-show="vm.steps.other_prompts.tab._active">
<div ng-if="vm.steps.other_prompts.includeStep" ng-show="vm.steps.other_prompts.tab._active" id="prompt_other_prompts_step">
<prompt-other-prompts
prompt-data="vm.promptDataClone"
other-prompts-form="vm.forms.otherPrompts"
@ -31,31 +31,31 @@
read-only-prompts="vm.readOnlyPrompts">
</prompt-other-prompts>
</div>
<div ng-if="vm.steps.survey.includeStep" ng-show="vm.steps.survey.tab._active">
<div ng-if="vm.steps.survey.includeStep" ng-show="vm.steps.survey.tab._active" id="prompt_survey_step">
<prompt-survey
prompt-data="vm.promptDataClone"
survey-form="vm.forms.survey"
read-only-prompts="vm.readOnlyPrompts">
</prompt-survey>
</div>
<div ng-if="vm.steps.preview.tab._active">
<div ng-if="vm.steps.preview.tab._active" id="prompt_preview_step">
<prompt-preview prompt-data="vm.promptDataClone"></prompt-preview>
</div>
</div>
<div class="Prompt-footer">
<button class="Prompt-defaultButton" ng-click="vm.cancel()" ng-show="!vm.readOnlyPrompts">{{:: vm.strings.get('CANCEL') }}</button>
<button class="Prompt-defaultButton" ng-click="vm.cancel()" ng-show="vm.readOnlyPrompts">{{:: vm.strings.get('CLOSE') }}</button>
<button class="Prompt-actionButton" ng-show="vm.steps.inventory.tab._active" ng-click="vm.next(vm.steps.inventory.tab)" ng-disabled="!vm.promptDataClone.prompts.inventory.value.id && !vm.readOnlyPrompts">{{:: vm.strings.get('NEXT') }}</button>
<button class="Prompt-actionButton"
<button id="prompt_cancel" class="Prompt-defaultButton" ng-click="vm.cancel()" ng-show="!vm.readOnlyPrompts">{{:: vm.strings.get('CANCEL') }}</button>
<button id="prompt_close" class="Prompt-defaultButton" ng-click="vm.cancel()" ng-show="vm.readOnlyPrompts">{{:: vm.strings.get('CLOSE') }}</button>
<button id="prompt_inventory_next" class="Prompt-actionButton" ng-show="vm.steps.inventory.tab._active" ng-click="vm.next(vm.steps.inventory.tab)" ng-disabled="!vm.promptDataClone.prompts.inventory.value.id && !vm.readOnlyPrompts">{{:: vm.strings.get('NEXT') }}</button>
<button id="prompt_credential_next" class="Prompt-actionButton"
ng-show="vm.steps.credential.tab._active"
ng-click="vm.next(vm.steps.credential.tab)"
ng-disabled="!vm.readOnlyPrompts &&
((preventCredsWithPasswords && (vm.promptDataClone.prompts.credentials.passwords.ssh_password || vm.promptDataClone.prompts.credentials.passwords.become_password || vm.promptDataClone.prompts.credentials.passwords.ssh_key_unlock || (vm.promptDataClone.prompts.credentials.passwords.vault && vm.promptDataClone.prompts.credentials.passwords.vault.length > 0))) ||
!vm.forms.credentialPasswords.$valid ||
(vm.promptDataClone.credentialTypeMissing && vm.promptDataClone.credentialTypeMissing.length > 0))">{{:: vm.strings.get('NEXT') }}</button>
<button class="Prompt-actionButton" ng-show="vm.steps.other_prompts.tab._active" ng-click="vm.next(vm.steps.other_prompts.tab)" ng-disabled="!vm.readOnlyPrompts && !vm.forms.otherPrompts.$valid">{{:: vm.strings.get('NEXT') }}</button>
<button class="Prompt-actionButton" ng-show="vm.steps.survey.tab._active" ng-click="vm.next(vm.steps.survey.tab)" ng-disabled="!vm.readOnlyPrompts && !vm.forms.survey.$valid">{{:: vm.strings.get('NEXT') }}</button>
<button class="Prompt-actionButton" ng-show="vm.steps.preview.tab._active && !vm.readOnlyPrompts" ng-click="vm.finish()" ng-bind="vm.actionText" ng-disabled="vm.actionButtonClicked"></button>
<button id="prompt_other_prompts_next" class="Prompt-actionButton" ng-show="vm.steps.other_prompts.tab._active" ng-click="vm.next(vm.steps.other_prompts.tab)" ng-disabled="!vm.readOnlyPrompts && !vm.forms.otherPrompts.$valid">{{:: vm.strings.get('NEXT') }}</button>
<button id="prompt_survey_next" class="Prompt-actionButton" ng-show="vm.steps.survey.tab._active" ng-click="vm.next(vm.steps.survey.tab)" ng-disabled="!vm.readOnlyPrompts && !vm.forms.survey.$valid">{{:: vm.strings.get('NEXT') }}</button>
<button id="prompt_finish" class="Prompt-actionButton" ng-show="vm.steps.preview.tab._active && !vm.readOnlyPrompts" ng-click="vm.finish()" ng-bind="vm.actionText" ng-disabled="vm.actionButtonClicked"></button>
</div>
</at-modal>
</div>

View File

@ -22,12 +22,12 @@ Login.prototype.command = function command (username, password) {
loginPage
.navigate()
.waitForElementVisible('@submit', AWX_E2E_TIMEOUT_LONG)
.waitForElementNotVisible('div.spinny')
.waitForElementNotVisible('div.spinny', AWX_E2E_TIMEOUT_LONG)
.setValue('@username', username)
.setValue('@password', password)
.click('@submit')
.waitForElementVisible('div.spinny')
.waitForElementNotVisible('div.spinny');
.waitForElementVisible('div.spinny', AWX_E2E_TIMEOUT_LONG)
.waitForElementNotVisible('div.spinny', AWX_E2E_TIMEOUT_LONG);
// temporary hack while login issue is resolved
this.api.elements('css selector', '.LoginModal-alert', result => {
@ -39,8 +39,8 @@ Login.prototype.command = function command (username, password) {
loginPage.setValue('@username', username);
loginPage.setValue('@password', password);
loginPage.click('@submit');
loginPage.waitForElementVisible('div.spinny');
loginPage.waitForElementNotVisible('div.spinny');
loginPage.waitForElementVisible('div.spinny', AWX_E2E_TIMEOUT_LONG);
loginPage.waitForElementNotVisible('div.spinny', AWX_E2E_TIMEOUT_LONG);
}
});
});

View File

@ -11,7 +11,7 @@ import pagination from './sections/pagination';
import permissions from './sections/permissions';
import search from './sections/search';
const details = createFormSection({
const jtDetails = createFormSection({
selector: 'form',
props: {
formElementSelectors: [
@ -31,6 +31,25 @@ const details = createFormSection({
}
});
const wfjtDetails = createFormSection({
selector: 'form',
props: {
formElementSelectors: [
'#workflow_job_template_form .Form-textInput',
'#workflow_job_template_form select.Form-dropDown',
'#workflow_job_template_form .Form-textArea',
'#workflow_job_template_form input[type="checkbox"]',
'#workflow_job_template_form .ui-spinner-input',
'#workflow_job_template_form .ScheduleToggle-switch'
]
},
labels: {
name: 'Name',
description: 'Description'
}
});
const lookupInventory = _.merge({}, lookupModal, {
locateStrategy: 'xpath',
selector: './/div[text()="Select inventory"]/ancestor::div[contains(@class, "modal-content")]'
@ -54,7 +73,7 @@ module.exports = {
addJobTemplate: {
selector: 'div[ui-view="form"]',
sections: {
details
jtDetails
},
elements: {
title: 'div[class^="Form-title"]'
@ -63,7 +82,7 @@ module.exports = {
editJobTemplate: {
selector: 'div[ui-view="form"]',
sections: {
details,
jtDetails,
permissions
},
elements: {
@ -73,7 +92,7 @@ module.exports = {
addWorkflowJobTemplate: {
selector: 'div[ui-view="form"]',
sections: {
details
wfjtDetails
},
elements: {
title: 'div[class^="Form-title"]'
@ -82,7 +101,7 @@ module.exports = {
editWorkflowJobTemplate: {
selector: 'div[ui-view="form"]',
sections: {
details,
wfjtDetails,
permissions
},
elements: {
@ -90,7 +109,7 @@ module.exports = {
}
},
list: {
selector: '.Panel',
selector: '.at-Panel',
elements: {
badge: 'span[class~="badge"]',
title: 'div[class="List-titleText"]',

View File

@ -0,0 +1,159 @@
import { post, patch } from '../api';
import {
getOrCreate,
getUpdatedProject,
getInventory
} from '../fixtures';
let templateReferences = {};
module.exports = {
before: (client, done) => {
const resources = [
getUpdatedProject('test-launch-jt'),
getInventory('test-launch-jt')
];
Promise.all(resources)
.then(([project, inventory]) => {
const noPromptPromise = getOrCreate('/job_templates/', {
name: 'test-launch-jt-no-prompts',
inventory: inventory.id,
project: project.id,
playbook: 'hello_world.yml',
});
const promptNoPassPromise = getOrCreate('/job_templates/', {
name: 'test-launch-jt-prompts-no-pass',
inventory: inventory.id,
ask_inventory_on_launch: true,
project: project.id,
playbook: 'hello_world.yml',
ask_diff_mode_on_launch: true,
ask_variables_on_launch: true,
ask_limit_on_launch: true,
ask_tags_on_launch: true,
ask_skip_tags_on_launch: true,
ask_job_type_on_launch: true,
ask_verbosity_on_launch: true,
ask_credential_on_launch: true
});
Promise.all([noPromptPromise, promptNoPassPromise])
.then(([noPrompt, promptNoPass]) => {
templateReferences = { noPrompt, promptNoPass };
const surveyPost = post(promptNoPass.related.survey_spec, {
name: '',
description: '',
spec: [{
question_name: 'Foo',
question_description: '',
required: true,
type: 'text',
variable: 'foo',
min: 0,
max: 1024,
default: 'bar',
choices: '',
new_question: true
}]
});
surveyPost
.then(() => patch(promptNoPass.url, { survey_enabled: true }))
.then(done);
});
});
},
'login to awx': client => {
client.login();
client.resizeWindow(1200, 800);
client.waitForAngular();
},
'expected jt launch with no prompts to navigate to job details': client => {
const templates = client.page.templates();
templates.load();
templates.waitForElementVisible('input[class*="SmartSearch-input"]');
templates.section.list.section.search
.sendKeys('@input', `id:${templateReferences.noPrompt.id}`);
templates.section.list.section.search.getValue('@input', (result) => {
client.assert.equal(result.value, `id:${templateReferences.noPrompt.id}`);
});
client.pause(1000).waitForElementNotVisible('div.spinny');
templates.waitForElementVisible('i[class$="search"]');
templates.section.list.section.search.click('i[class$="search"]');
templates.waitForElementVisible('div.spinny');
templates.waitForElementNotVisible('div.spinny');
templates.expect.element('.at-Panel-headingTitleBadge').text.equal('1');
templates.expect.element(`#templates_list .at-Row[id="row-${templateReferences.noPrompt.id}"]`).visible;
templates.expect.element('i[class*="icon-launch"]').visible;
templates.expect.element('i[class*="icon-launch"]').enabled;
templates.click('i[class*="icon-launch"]');
templates.waitForElementVisible('div.spinny');
templates.waitForElementNotVisible('div.spinny');
client.waitForElementVisible('at-job-details', 10000);
client.useXpath();
client.waitForElementVisible('.//span[normalize-space(text())=\'"msg": "Hello World!"\']', 60000);
client.useCss();
},
'expected jt launch with prompts but no changes to navigate to job details': client => {
const templates = client.page.templates();
templates.load();
templates.waitForElementVisible('input[class*="SmartSearch-input"]');
templates.section.list.section.search
.sendKeys('@input', `id:${templateReferences.promptNoPass.id}`);
templates.section.list.section.search.getValue('@input', (result) => {
client.assert.equal(result.value, `id:${templateReferences.promptNoPass.id}`);
});
client.pause(1000).waitForElementNotVisible('div.spinny');
templates.waitForElementVisible('i[class$="search"]');
templates.section.list.section.search.click('i[class$="search"]');
templates.waitForElementVisible('div.spinny');
templates.waitForElementNotVisible('div.spinny');
templates.expect.element('.at-Panel-headingTitleBadge').text.equal('1');
templates.expect.element(`#templates_list .at-Row[id="row-${templateReferences.promptNoPass.id}"]`).visible;
templates.expect.element('i[class*="icon-launch"]').visible;
templates.expect.element('i[class*="icon-launch"]').enabled;
templates.click('i[class*="icon-launch"]');
templates.waitForElementVisible('#prompt-inventory');
templates.expect.element('#prompt_inventory_tab').visible;
templates.expect.element('#prompt_inventory_tab').to.have.attribute('class').which.contains('at-Tab--active');
templates.expect.element('#prompt_inventory_next').visible;
templates.expect.element('#prompt_inventory_next').enabled;
templates.click('#prompt_inventory_next');
templates.waitForElementVisible('#prompt_credential_step');
templates.expect.element('#prompt_credential_tab').visible;
templates.expect.element('#prompt_credential_tab').to.have.attribute('class').which.contains('at-Tab--active');
templates.expect.element('#prompt_credential_next').visible;
templates.expect.element('#prompt_credential_next').enabled;
templates.click('#prompt_credential_next');
templates.waitForElementVisible('#prompt_other_prompts_step');
templates.expect.element('#prompt_other_prompts_tab').visible;
templates.expect.element('#prompt_other_prompts_tab').to.have.attribute('class').which.contains('at-Tab--active');
templates.expect.element('#prompt_other_prompts_next').visible;
templates.expect.element('#prompt_other_prompts_next').enabled;
templates.click('#prompt_other_prompts_next');
templates.waitForElementVisible('#prompt_survey_step');
templates.expect.element('#prompt_survey_tab').visible;
templates.expect.element('#prompt_survey_tab').to.have.attribute('class').which.contains('at-Tab--active');
templates.expect.element('#prompt_survey_next').visible;
templates.expect.element('#prompt_survey_next').enabled;
templates.click('#prompt_survey_next');
templates.waitForElementVisible('#prompt_preview_step');
templates.expect.element('#prompt_preview_tab').visible;
templates.expect.element('#prompt_preview_tab').to.have.attribute('class').which.contains('at-Tab--active');
templates.expect.element('#prompt_finish').visible;
templates.expect.element('#prompt_finish').enabled;
templates.click('#prompt_finish');
templates.waitForElementVisible('div.spinny');
templates.waitForElementNotVisible('div.spinny');
client.waitForElementVisible('at-job-details', 10000);
client.useXpath();
client.waitForElementVisible('.//span[normalize-space(text())=\'"msg": "Hello World!"\']', 60000);
client.useCss();
client.end();
}
};