From ee7af9d797621f3547db8af6b1610a784fb0abc1 Mon Sep 17 00:00:00 2001 From: davidcanonieto Date: Fri, 26 Oct 2018 14:03:13 +0100 Subject: [PATCH] [ADF-3671] Add automation tests for Form Widgets (#3911) * [ADF-3617] Fix bugs in Task Details Page controller * [ADF-3617] Fix bugs in Task Page controller * [ADF-3617] Separate in different describes automation tests * [ADF-3671] start working on e2e form tests * [ADF-3671] improved checks for form widgets * [ADF-3671] start adding multiline text widget tsts * [ADF-3671] added test for multiline and some improvements * [ADF-3671] Add e2e tests for Dropdown, Date, Checkbox widgets * [ADF-3671] Add e2e test for radio, number, hyperlink widgets * [ADF-3671] Add automation tests for Form Widgets * [ADF-3671] Update usage of Dropdown Widget * [ADF-3671] Fix fileModel file and rename widget controles * [ADF-3671] Rename Widget Controllers * [ADF-3671] Remove unused imports and fix dynamic table e2e test * [ADF-3671] Fix spell errors * [ADF-3671] Fix people, date time, attach widget * [ADF-3671] Fix Form People widget tests * [ADF-3671] Rebase and remove unused variables --- e2e/actions/APS/apps.actions.ts | 8 +- e2e/pages/adf/process_services/formFields.js | 46 ++- .../adf/process_services/taskDetailsPage.ts | 15 +- e2e/pages/adf/process_services/tasksPage.ts | 1 - .../adf/process_services/widgets/Amount.ts | 46 --- .../adf/process_services/widgets/Date.ts | 46 --- .../adf/process_services/widgets/Number.ts | 46 --- .../process_services/widgets/amountWidget.ts | 71 ++++ .../{attachFile.ts => attachFileWidget.ts} | 2 +- .../widgets/checkboxWidget.ts | 46 +++ .../{hyperlink.ts => containerWidget.ts} | 7 +- .../widgets/dateTimeWidget.ts | 106 +++++ .../process_services/widgets/dateWidget.ts | 61 +++ ...{multilineText.ts => displayTextWidget.ts} | 14 +- ...{displayValue.ts => displayValueWidget.ts} | 8 +- .../{displayText.ts => documentWidget.ts} | 12 +- .../{dropdown.ts => dropdownWidget.ts} | 4 +- ...{dynamicTable.ts => dynamicTableWidget.ts} | 52 ++- .../widgets/{header.ts => headerWidget.ts} | 2 +- .../{radioButtons.ts => hyperlinkWidget.ts} | 17 +- .../widgets/multilineTextWidget.ts | 48 +++ .../process_services/widgets/numberWidget.ts | 55 +++ .../adf/process_services/widgets/people.ts | 40 -- .../process_services/widgets/peopleWidget.ts | 83 ++++ .../widgets/radioButtonsWidget.ts | 55 +++ .../process_services/widgets/textWidget.ts | 54 +++ .../adf/process_services/widgets/widget.ts | 81 +++- .../dynamic_table_date_picker.e2e.ts | 8 +- e2e/process-services/form_component.e2e.ts | 35 +- .../form_widgets_component.e2e.ts | 379 ++++++++++-------- .../widgets/amount_widget.e2e.ts | 104 +++++ .../widgets/attach_folder_widget.e2e.ts | 85 ++++ .../widgets/checkbox_widget.e2e.ts | 92 +++++ .../widgets/date_time_widget.e2e.ts | 108 +++++ .../widgets/date_widget.e2e.ts | 96 +++++ .../widgets/document_template_widget.e2e.ts | 83 ++++ .../widgets/dropdown_widget.e2e.ts | 100 +++++ .../widgets/dynamic_table_widget.e2e.ts | 167 ++++++++ .../widgets/header_widget.e2e.ts | 87 ++++ .../widgets/hyperlink_widget.e2e.ts | 87 ++++ .../widgets/multi_line_widget.e2e.ts | 112 ++++++ .../widgets/number_widget.e2e.ts | 109 +++++ .../widgets/people_widget.e2e.ts | 102 +++++ .../widgets/radio_buttons_widget.e2e.ts | 95 +++++ .../widgets/text_widget.e2e.ts | 122 ++++++ e2e/resources/apps/App_file_form.zip | Bin 0 -> 15505 bytes e2e/resources/apps/Test-ADF.zip | Bin 0 -> 8928 bytes e2e/resources/apps/WidgetApps.zip | Bin 0 -> 103307 bytes e2e/util/resources.js | 203 +++++++++- .../widgets/core/form-field-types.ts | 1 + .../widgets/core/form-field-validator.ts | 3 +- 51 files changed, 2790 insertions(+), 414 deletions(-) delete mode 100644 e2e/pages/adf/process_services/widgets/Amount.ts delete mode 100644 e2e/pages/adf/process_services/widgets/Date.ts delete mode 100644 e2e/pages/adf/process_services/widgets/Number.ts create mode 100644 e2e/pages/adf/process_services/widgets/amountWidget.ts rename e2e/pages/adf/process_services/widgets/{attachFile.ts => attachFileWidget.ts} (98%) create mode 100644 e2e/pages/adf/process_services/widgets/checkboxWidget.ts rename e2e/pages/adf/process_services/widgets/{hyperlink.ts => containerWidget.ts} (81%) create mode 100644 e2e/pages/adf/process_services/widgets/dateTimeWidget.ts create mode 100644 e2e/pages/adf/process_services/widgets/dateWidget.ts rename e2e/pages/adf/process_services/widgets/{multilineText.ts => displayTextWidget.ts} (65%) rename e2e/pages/adf/process_services/widgets/{displayValue.ts => displayValueWidget.ts} (83%) rename e2e/pages/adf/process_services/widgets/{displayText.ts => documentWidget.ts} (70%) rename e2e/pages/adf/process_services/widgets/{dropdown.ts => dropdownWidget.ts} (98%) rename e2e/pages/adf/process_services/widgets/{dynamicTable.ts => dynamicTableWidget.ts} (68%) rename e2e/pages/adf/process_services/widgets/{header.ts => headerWidget.ts} (97%) rename e2e/pages/adf/process_services/widgets/{radioButtons.ts => hyperlinkWidget.ts} (62%) create mode 100644 e2e/pages/adf/process_services/widgets/multilineTextWidget.ts create mode 100644 e2e/pages/adf/process_services/widgets/numberWidget.ts delete mode 100644 e2e/pages/adf/process_services/widgets/people.ts create mode 100644 e2e/pages/adf/process_services/widgets/peopleWidget.ts create mode 100644 e2e/pages/adf/process_services/widgets/radioButtonsWidget.ts create mode 100644 e2e/pages/adf/process_services/widgets/textWidget.ts create mode 100644 e2e/process-services/widgets/amount_widget.e2e.ts create mode 100644 e2e/process-services/widgets/attach_folder_widget.e2e.ts create mode 100644 e2e/process-services/widgets/checkbox_widget.e2e.ts create mode 100644 e2e/process-services/widgets/date_time_widget.e2e.ts create mode 100644 e2e/process-services/widgets/date_widget.e2e.ts create mode 100644 e2e/process-services/widgets/document_template_widget.e2e.ts create mode 100644 e2e/process-services/widgets/dropdown_widget.e2e.ts create mode 100644 e2e/process-services/widgets/dynamic_table_widget.e2e.ts create mode 100644 e2e/process-services/widgets/header_widget.e2e.ts create mode 100644 e2e/process-services/widgets/hyperlink_widget.e2e.ts create mode 100644 e2e/process-services/widgets/multi_line_widget.e2e.ts create mode 100644 e2e/process-services/widgets/number_widget.e2e.ts create mode 100644 e2e/process-services/widgets/people_widget.e2e.ts create mode 100644 e2e/process-services/widgets/radio_buttons_widget.e2e.ts create mode 100644 e2e/process-services/widgets/text_widget.e2e.ts create mode 100644 e2e/resources/apps/App_file_form.zip create mode 100644 e2e/resources/apps/Test-ADF.zip create mode 100644 e2e/resources/apps/WidgetApps.zip diff --git a/e2e/actions/APS/apps.actions.ts b/e2e/actions/APS/apps.actions.ts index d08921460f..c822106df4 100644 --- a/e2e/actions/APS/apps.actions.ts +++ b/e2e/actions/APS/apps.actions.ts @@ -74,7 +74,13 @@ export class AppsActions { let processDefinitionList = await alfrescoJsApi.activiti.processApi.getProcessDefinitions({ deploymentId: appDefinition.deploymentId }); - let startProcessOptions: any = { processDefinitionId: processDefinitionList.data[0].id }; + let chosenProcess = processDefinitionList.data.find( (processDefinition) => { + return processDefinition.name === processName; + }); + + let processDefinitionIdToStart = chosenProcess ? chosenProcess.id : processDefinitionList.data[0].id; + + let startProcessOptions: any = { processDefinitionId: processDefinitionIdToStart }; if (typeof processName !== 'undefined') { startProcessOptions.name = processName; diff --git a/e2e/pages/adf/process_services/formFields.js b/e2e/pages/adf/process_services/formFields.js index a6a2d666cc..e3d52a1a96 100644 --- a/e2e/pages/adf/process_services/formFields.js +++ b/e2e/pages/adf/process_services/formFields.js @@ -30,6 +30,7 @@ var FormFields = function () { var selectFormDropDownArrow = element.all(by.css("adf-attach-form div[class*='mat-select-arrow']")).first(); var selectFormContent = element(by.css("div[class*='mat-select-content']")); var completeButton = element(by.id('adf-form-complete')); + var errorMessage = by.css('.adf-error-text-container .adf-error-text'); this.setFieldValue = function (By, field, value) { var fieldElement = element(By(field)); @@ -38,6 +39,16 @@ var FormFields = function () { return this; }; + this.checkWidgetIsVisible = function (fieldId) { + var fieldElement = element(by.css("adf-form-field div[id='field-"+fieldId+"-container']")); + Util.waitUntilElementIsVisible(fieldElement); + } + + this.checkWidgetIsHidden = function (fieldId) { + var hiddenElement = element(by.css("adf-form-field div[id='field-"+fieldId+"-container'][hidden]")); + Util.waitUntilElementIsVisible(hiddenElement); + } + this.getWidget = function (fieldId) { var widget = element(by.css("adf-form-field div[id='field-" + fieldId + "-container']")); Util.waitUntilElementIsVisible(widget); @@ -51,15 +62,28 @@ var FormFields = function () { }; this.getFieldLabel = function (fieldId, labelLocatorParam) { - return this.getFieldText(fieldId, labelLocatorParam); - }; - - this.getFieldText = function (fieldId, labelLocatorParam) { - var label = this.getWidget(fieldId).all(labelLocatorParam || labelLocator).first(); + var label = this.getWidget(fieldId).element(labelLocatorParam || labelLocator); Util.waitUntilElementIsVisible(label); return label.getText(); }; + this.getFieldErrorMessage = function (fieldId) { + var error = this.getWidget(fieldId).element(errorMessage); + return error.getText(); + } + + this.getFieldText = function (fieldId, labelLocatorParam) { + var label = this.getWidget(fieldId).element(labelLocatorParam || labelLocator); + Util.waitUntilElementIsVisible(label); + return label.getText(); + }; + + this.getFieldPlaceHolder = function (fieldId, locator='input') { + let placeHolderLocator = element(by.css(`${locator}#${fieldId}`)).getAttribute('placeholder'); + Util.waitUntilElementIsVisible(placeHolderLocator); + return placeHolderLocator; + }; + this.checkFieldValue = function (By, field, val) { Util.waitUntilElementHasValue(element(By(field)), val); return this; @@ -129,6 +153,18 @@ var FormFields = function () { Util.waitUntilElementIsVisible(completeButton); return completeButton.click(); }; + + this.setValueInInputById = function(fieldId, value) { + let input = element(by.id(fieldId)); + Util.waitUntilElementIsVisible(input); + input.clear().sendKeys(value); + return this; + } + + this.isCompleteFormButtonDisabled = function() { + Util.waitUntilElementIsVisible(completeButton); + return completeButton.getAttribute('disabled'); + } }; module.exports = FormFields; diff --git a/e2e/pages/adf/process_services/taskDetailsPage.ts b/e2e/pages/adf/process_services/taskDetailsPage.ts index 6ba1128422..ba0f61d889 100644 --- a/e2e/pages/adf/process_services/taskDetailsPage.ts +++ b/e2e/pages/adf/process_services/taskDetailsPage.ts @@ -16,11 +16,13 @@ */ import { AppSettingsToggles } from './dialog/appSettingsToggles'; -import { TabsPage } from '../material/tabsPage'; -import { element, by, browser, protractor } from 'protractor'; import Util = require('../../../util/util'); +import { element, by, protractor, browser } from 'protractor'; +import { TabsPage } from '../material/tabsPage'; export class TaskDetailsPage { + + appSettingsTogglesClass = new AppSettingsToggles(); formContent = element(by.css('adf-form')); formNameField = element(by.css('span[data-automation-id*="formName"] span')); assigneeField = element(by.css('span[data-automation-id*="assignee"] span')); @@ -42,6 +44,7 @@ export class TaskDetailsPage { addInvolvedUserButton = element(by.css('button[id="add-people"] span')); emailInvolvedUser = by.xpath('following-sibling::div[@class="people-email ng-star-inserted"]'); editActionInvolvedUser = by.xpath('following-sibling::div[@class="people-edit-label ng-star-inserted"]'); + involvedUserPic = by.xpath('ancestor::div/ancestor::div/preceding-sibling::div//div[@class="adf-people-search-people-pic ng-star-inserted"]'); taskDetailsInfoDrawer = element(by.tagName('adf-info-drawer')); taskDetailsSection = element(by.css('div[data-automation-id="adf-tasks-details"]')); taskDetailsEmptySection = element(by.css('div[data-automation-id="adf-tasks-details--empty"]')); @@ -307,7 +310,7 @@ export class TaskDetailsPage { } appSettingsToggles() { - return new AppSettingsToggles(); + return this.appSettingsTogglesClass; } taskInfoDrawerIsDisplayed() { @@ -361,6 +364,12 @@ export class TaskDetailsPage { return this.peopleTitle.getText(); } + getInvolvedPeopleInitialImage(user) { + let pic = this.getRowsUser(user).element(this.involvedUserPic); + Util.waitUntilElementIsVisible(pic); + return pic.getText(); + } + checkTaskDetails() { Util.waitUntilElementIsVisible(this.taskDetailsSection); return this.taskDetailsSection.getText(); diff --git a/e2e/pages/adf/process_services/tasksPage.ts b/e2e/pages/adf/process_services/tasksPage.ts index 0cc4c8aa2c..cd46558f9f 100644 --- a/e2e/pages/adf/process_services/tasksPage.ts +++ b/e2e/pages/adf/process_services/tasksPage.ts @@ -23,7 +23,6 @@ import { TaskDetailsPage } from './taskDetailsPage'; import FiltersPage = require('./filtersPage'); import ChecklistDialog = require('./dialog/createChecklistDialog'); import TasksListPage = require('./tasksListPage'); - import { element, by } from 'protractor'; export class TasksPage { diff --git a/e2e/pages/adf/process_services/widgets/Amount.ts b/e2e/pages/adf/process_services/widgets/Amount.ts deleted file mode 100644 index 0ede82684b..0000000000 --- a/e2e/pages/adf/process_services/widgets/Amount.ts +++ /dev/null @@ -1,46 +0,0 @@ -/*! - * @license - * Copyright 2016 Alfresco Software, Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { element, by, protractor } from 'protractor'; -import Util = require('../../../../util/util'); - -export class Amount { - - amountWidgetLabel11 = element(by.id('field-label11-container')); - amountWidgetInput = element(by.css('div[id="field-label11-container"] input[id="label11"]')); - - checkLabel11IsDisplayed() { - return Util.waitUntilElementIsVisible(this.amountWidgetLabel11); - } - - addIntoAmountWidget(input) { - Util.waitUntilElementIsVisible(this.amountWidgetLabel11); - this.amountWidgetInput.click(); - this.amountWidgetInput.sendKeys(input); - return this.amountWidgetInput.sendKeys(protractor.Key.ENTER); - } - - removeFromAmountWidget() { - Util.waitUntilElementIsVisible(this.amountWidgetLabel11); - - this.amountWidgetInput.getAttribute('value').then((result) => { - for (let i = result.length; i >= 0; i--) { - this.amountWidgetInput.sendKeys(protractor.Key.BACK_SPACE); - } - }); - } -} diff --git a/e2e/pages/adf/process_services/widgets/Date.ts b/e2e/pages/adf/process_services/widgets/Date.ts deleted file mode 100644 index 9afc42c046..0000000000 --- a/e2e/pages/adf/process_services/widgets/Date.ts +++ /dev/null @@ -1,46 +0,0 @@ -/*! - * @license - * Copyright 2016 Alfresco Software, Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { element, by, protractor } from 'protractor'; -import Util = require('../../../../util/util'); - -export class Date { - - dateWidgetLabel7 = element(by.id('field-label7-container')); - dateWidgetInput = element(by.css('div[id="field-label7-container"] input[id="label7"]')); - - checkLabel7IsDisplayed() { - return Util.waitUntilElementIsVisible(this.dateWidgetLabel7); - } - - addIntoDateWidget(input) { - Util.waitUntilElementIsVisible(this.dateWidgetLabel7); - this.dateWidgetInput.click(); - this.dateWidgetInput.sendKeys(input); - return this.dateWidgetInput.sendKeys(protractor.Key.ENTER); - } - - removeFromDateWidget() { - Util.waitUntilElementIsVisible(this.dateWidgetLabel7); - - this.dateWidgetInput.getAttribute('value').then((result) => { - for (let i = result.length; i >= 0; i--) { - this.dateWidgetInput.sendKeys(protractor.Key.BACK_SPACE); - } - }); - } -} diff --git a/e2e/pages/adf/process_services/widgets/Number.ts b/e2e/pages/adf/process_services/widgets/Number.ts deleted file mode 100644 index 84fe041052..0000000000 --- a/e2e/pages/adf/process_services/widgets/Number.ts +++ /dev/null @@ -1,46 +0,0 @@ -/*! - * @license - * Copyright 2016 Alfresco Software, Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { element, by, protractor } from 'protractor'; -import Util = require('../../../../util/util'); - -export class NumberWidget { - - numberWidgetLabel4 = element(by.id('field-label4-container')); - numberWidgetInput = element(by.css('div[id="field-label4-container"] input[id="label4"]')); - - checkLabel4IsDisplayed() { - return Util.waitUntilElementIsVisible(this.numberWidgetLabel4); - } - - addIntoNumberWidget(input) { - Util.waitUntilElementIsVisible(this.numberWidgetLabel4); - this.numberWidgetInput.click(); - this.numberWidgetInput.sendKeys(input); - return this.numberWidgetInput.sendKeys(protractor.Key.ENTER); - } - - removeFromNumberWidget() { - Util.waitUntilElementIsVisible(this.numberWidgetLabel4); - - this.numberWidgetInput.getAttribute('value').then((result) => { - for (let i = result.length; i >= 0; i--) { - this.numberWidgetInput.sendKeys(protractor.Key.BACK_SPACE); - } - }); - } -} diff --git a/e2e/pages/adf/process_services/widgets/amountWidget.ts b/e2e/pages/adf/process_services/widgets/amountWidget.ts new file mode 100644 index 0000000000..5c4b3ed748 --- /dev/null +++ b/e2e/pages/adf/process_services/widgets/amountWidget.ts @@ -0,0 +1,71 @@ +/*! + * @license + * Copyright 2016 Alfresco Software, Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { element, by, protractor } from 'protractor'; +import Util = require('../../../../util/util'); +import FormFields = require('../formFields'); + +export class AmountWidget { + + currency = by.css('span[class="adf-amount-widget__prefix-spacing"]'); + formFields = new FormFields(); + + getAmountFieldLabel(fieldId) { + let label = element(by.css(`adf-form-field div[id="field-${fieldId}-container"] label`)); + Util.waitUntilElementIsVisible(label); + return label.getText(); + } + + getAmountFieldCurrency(fieldId) { + return this.formFields.getWidget(fieldId).element(this.currency).getText(); + } + + setFieldValue(fieldId, value) { + return this.formFields.setValueInInputById(fieldId, value); + } + + removeFromAmountWidget(fieldId) { + Util.waitUntilElementIsVisible(this.formFields.getWidget(fieldId)); + + let amountWidgetInput = element(by.id(fieldId)); + amountWidgetInput.getAttribute('value').then((result) => { + for (let i = result.length; i >= 0; i--) { + amountWidgetInput.sendKeys(protractor.Key.BACK_SPACE); + } + }); + } + + clearFieldValue(fieldId) { + let numberField = element(by.id(fieldId)); + Util.waitUntilElementIsVisible(numberField); + return numberField.clear(); + } + + checkWidgetIsVisible(fieldId) { + return this.formFields.checkWidgetIsVisible(fieldId); + } + + getErrorMessage(fieldId) { + let errorMessage = element(by.css(`adf-form-field div[id="field-${fieldId}-container"] div[class="adf-error-text"]`)); + Util.waitUntilElementIsVisible(errorMessage); + return errorMessage.getText(); + } + + getPlaceholder(fieldId) { + return this.formFields.getFieldPlaceHolder(fieldId); + } +} diff --git a/e2e/pages/adf/process_services/widgets/attachFile.ts b/e2e/pages/adf/process_services/widgets/attachFileWidget.ts similarity index 98% rename from e2e/pages/adf/process_services/widgets/attachFile.ts rename to e2e/pages/adf/process_services/widgets/attachFileWidget.ts index e49da3f53f..704a858842 100644 --- a/e2e/pages/adf/process_services/widgets/attachFile.ts +++ b/e2e/pages/adf/process_services/widgets/attachFileWidget.ts @@ -22,7 +22,7 @@ import Util = require('../../../../util/util'); import remote = require('selenium-webdriver/remote'); import { element, by, browser } from 'protractor'; -export class AttachFile { +export class AttachFileWidget { formFields = new FormFields(); uploadLocator = by.css('button[id="attachfile"]'); diff --git a/e2e/pages/adf/process_services/widgets/checkboxWidget.ts b/e2e/pages/adf/process_services/widgets/checkboxWidget.ts new file mode 100644 index 0000000000..0e9dab12ff --- /dev/null +++ b/e2e/pages/adf/process_services/widgets/checkboxWidget.ts @@ -0,0 +1,46 @@ +/*! + * @license + * Copyright 2016 Alfresco Software, Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import FormFields = require('../formFields'); +import Util = require('../../../../util/util'); +import { by, element } from 'protractor'; + +export class CheckboxWidget { + + formFields = new FormFields(); + checkboxField = element(by.css('span[class*="mat-checkbox-label"]')); + checkboxLabel = element(by.css('span[class*="mat-checkbox-label"]')); + + getCheckboxLabel() { + Util.waitUntilElementIsVisible(this.checkboxLabel); + return this.checkboxLabel.getText(); + } + + clickCheckboxInput(fieldId) { + let checkboxInput = element.all(by.css(`mat-checkbox[id="${fieldId}"] div`)).first(); + Util.waitUntilElementIsVisible(checkboxInput); + return checkboxInput.click(); + } + + isCheckboxDisplayed(fieldId) { + return this.formFields.checkWidgetIsVisible(fieldId); + } + + isCheckboxHidden(fieldId) { + return this.formFields.checkWidgetIsHidden(fieldId); + } +} diff --git a/e2e/pages/adf/process_services/widgets/hyperlink.ts b/e2e/pages/adf/process_services/widgets/containerWidget.ts similarity index 81% rename from e2e/pages/adf/process_services/widgets/hyperlink.ts rename to e2e/pages/adf/process_services/widgets/containerWidget.ts index f2b73e3f54..be0cd412f7 100644 --- a/e2e/pages/adf/process_services/widgets/hyperlink.ts +++ b/e2e/pages/adf/process_services/widgets/containerWidget.ts @@ -18,14 +18,13 @@ import FormFields = require('../formFields'); import { by } from 'protractor'; -export class Hyperlink { +export class ContainerWidget { formFields = new FormFields(); - fieldLocator = by.css('div[class="adf-hyperlink-widget "] a'); + fileLocator = by.css("div [class*='upload-widget__content-text']"); getFieldText(fieldId) { - return this.formFields.getFieldText(fieldId, this.fieldLocator); + return this.formFields.getFieldText(fieldId, this.fileLocator); } - } diff --git a/e2e/pages/adf/process_services/widgets/dateTimeWidget.ts b/e2e/pages/adf/process_services/widgets/dateTimeWidget.ts new file mode 100644 index 0000000000..060926b6e1 --- /dev/null +++ b/e2e/pages/adf/process_services/widgets/dateTimeWidget.ts @@ -0,0 +1,106 @@ +/*! + * @license + * Copyright 2016 Alfresco Software, Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import FormFields = require('../formFields'); +import { element, by, protractor } from 'protractor'; +import Util = require('../../../../util/util'); + +export class DateTimeWidget { + + formFields = new FormFields(); + outsideLayer = element(by.css('div[class*="cdk-overlay-container"]')); + + checkWidgetIsVisible(fieldId) { + return this.formFields.checkWidgetIsVisible(fieldId); + } + + checkLabelIsVisible(fieldId) { + return this.formFields.checkWidgetIsVisible(fieldId); + } + + getDateTimeLabel(fieldId) { + let label = element(by.css(`adf-form-field div[id="field-${fieldId}-container"] label`)); + Util.waitUntilElementIsVisible(label); + return label.getText(); + } + + setDateTimeInput(fieldId, value) { + return this.formFields.setValueInInputById(fieldId, value); + } + + clearDateTimeInput(fieldId) { + let dateInput = element(by.id(fieldId)); + Util.waitUntilElementIsVisible(dateInput); + return dateInput.clear(); + } + + clickOutsideWidget(fieldId) { + let form = this.formFields.getWidget(fieldId); + Util.waitUntilElementIsVisible(form); + return form.click(); + } + + closeDataTimeWidget() { + Util.waitUntilElementIsVisible(this.outsideLayer); + return this.outsideLayer.click(); + } + + getErrorMessage(fieldId) { + let errorMessage = element(by.css(`adf-form-field div[id="field-${fieldId}-container"] div[class="adf-error-text"]`)); + Util.waitUntilElementIsVisible(errorMessage); + return errorMessage.getText(); + } + + selectDay(day) { + let selectedDay = element(by.cssContainingText('div[class*="mat-datetimepicker-calendar-body-cell-content"]', day)); + Util.waitUntilElementIsVisible(selectedDay); + return selectedDay.click(); + } + + openDatepicker(fieldId) { + return element(by.id(fieldId)).click(); + } + + private selectTime(time) { + let selectedTime = element(by.cssContainingText('div[class*="mat-datetimepicker-clock-cell"]', time)); + Util.waitUntilElementIsClickable(selectedTime); + return selectedTime.click(); + } + + selectHour(hour) { + return this.selectTime(hour); + } + + selectMinute(minute) { + return this.selectTime(minute); + } + + getPlaceholder(fieldId) { + return this.formFields.getFieldPlaceHolder(fieldId); + } + + removeFromDatetimeWidget(fieldId) { + Util.waitUntilElementIsVisible(this.formFields.getWidget(fieldId)); + + let amountWidgetInput = element(by.id(fieldId)); + amountWidgetInput.getAttribute('value').then((result) => { + for (let i = result.length; i >= 0; i--) { + amountWidgetInput.sendKeys(protractor.Key.BACK_SPACE); + } + }); + } +} diff --git a/e2e/pages/adf/process_services/widgets/dateWidget.ts b/e2e/pages/adf/process_services/widgets/dateWidget.ts new file mode 100644 index 0000000000..421aaab8ce --- /dev/null +++ b/e2e/pages/adf/process_services/widgets/dateWidget.ts @@ -0,0 +1,61 @@ +/*! + * @license + * Copyright 2016 Alfresco Software, Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import FormFields = require('../formFields'); +import { element, by } from 'protractor'; +import Util = require('../../../../util/util'); + +export class DateWidget { + + formFields = new FormFields(); + + checkWidgetIsVisible(fieldId) { + return this.formFields.checkWidgetIsVisible(fieldId); + } + + checkLabelIsVisible(fieldId) { + return this.formFields.checkWidgetIsVisible(fieldId); + } + + getDateLabel(fieldId) { + let label = element(by.css(`adf-form-field div[id="field-${fieldId}-container"] label`)); + Util.waitUntilElementIsVisible(label); + return label.getText(); + } + + setDateInput(fieldId, value) { + return this.formFields.setValueInInputById(fieldId, value); + } + + clearDateInput(fieldId) { + let dateInput = element(by.id(fieldId)); + Util.waitUntilElementIsVisible(dateInput); + return dateInput.clear(); + } + + clickOutsideWidget(fieldId) { + let form = this.formFields.getWidget(fieldId); + Util.waitUntilElementIsVisible(form); + return form.click(); + } + + getErrorMessage(fieldId) { + let errorMessage = element(by.css(`adf-form-field div[id="field-${fieldId}-container"] div[class="adf-error-text"]`)); + Util.waitUntilElementIsVisible(errorMessage); + return errorMessage.getText(); + } +} diff --git a/e2e/pages/adf/process_services/widgets/multilineText.ts b/e2e/pages/adf/process_services/widgets/displayTextWidget.ts similarity index 65% rename from e2e/pages/adf/process_services/widgets/multilineText.ts rename to e2e/pages/adf/process_services/widgets/displayTextWidget.ts index 1a7a21a9ac..76d00cbb97 100644 --- a/e2e/pages/adf/process_services/widgets/multilineText.ts +++ b/e2e/pages/adf/process_services/widgets/displayTextWidget.ts @@ -18,14 +18,22 @@ import FormFields = require('../formFields'); import { by } from 'protractor'; -export class MultilineText { +export class DisplayTextWidget { formFields = new FormFields(); + labelLocator = by.css('div[class*="adf-display-text-widget"]'); + inputLocator = by.css('input'); - valueLocator = by.css('textarea'); + getFieldLabel(fieldId) { + return this.formFields.getFieldLabel(fieldId, this.labelLocator); + } getFieldValue(fieldId) { - return this.formFields.getFieldValue(fieldId, this.valueLocator); + return this.formFields.getFieldValue(fieldId, this.inputLocator); + } + + getFieldText(fieldId) { + return this.formFields.getFieldText(fieldId, this.labelLocator); } } diff --git a/e2e/pages/adf/process_services/widgets/displayValue.ts b/e2e/pages/adf/process_services/widgets/displayValueWidget.ts similarity index 83% rename from e2e/pages/adf/process_services/widgets/displayValue.ts rename to e2e/pages/adf/process_services/widgets/displayValueWidget.ts index 833eff2ac6..34416ec98a 100644 --- a/e2e/pages/adf/process_services/widgets/displayValue.ts +++ b/e2e/pages/adf/process_services/widgets/displayValueWidget.ts @@ -18,14 +18,18 @@ import FormFields = require('../formFields'); import { by } from 'protractor'; -export class DisplayValue { +export class DisplayValueWidget { formFields = new FormFields(); - labelLocator = by.css('span[class*="unknown-text"]'); + inputLocator = by.css('input'); getFieldLabel(fieldId) { return this.formFields.getFieldLabel(fieldId, this.labelLocator); } + getFieldValue(fieldId) { + return this.formFields.getFieldValue(fieldId, this.inputLocator); + } + } diff --git a/e2e/pages/adf/process_services/widgets/displayText.ts b/e2e/pages/adf/process_services/widgets/documentWidget.ts similarity index 70% rename from e2e/pages/adf/process_services/widgets/displayText.ts rename to e2e/pages/adf/process_services/widgets/documentWidget.ts index 2a691050a4..1ebbd57fcc 100644 --- a/e2e/pages/adf/process_services/widgets/displayText.ts +++ b/e2e/pages/adf/process_services/widgets/documentWidget.ts @@ -18,14 +18,16 @@ import FormFields = require('../formFields'); import { by } from 'protractor'; -export class DisplayText { +export class DocumentWidget { formFields = new FormFields(); + fileLocator = by.css("div [class*='upload-widget__content-text']"); - labelLocator = by.css('div[class*="display-text-widget"]'); - - getFieldLabel(fieldId) { - return this.formFields.getFieldLabel(fieldId, this.labelLocator); + getFieldText(fieldId) { + return this.formFields.getFieldText(fieldId, this.fileLocator); } + getFileName(fieldId) { + return this.formFields.getFieldText(fieldId, this.fileLocator); + } } diff --git a/e2e/pages/adf/process_services/widgets/dropdown.ts b/e2e/pages/adf/process_services/widgets/dropdownWidget.ts similarity index 98% rename from e2e/pages/adf/process_services/widgets/dropdown.ts rename to e2e/pages/adf/process_services/widgets/dropdownWidget.ts index 5ca906402a..075d5e28a4 100644 --- a/e2e/pages/adf/process_services/widgets/dropdown.ts +++ b/e2e/pages/adf/process_services/widgets/dropdownWidget.ts @@ -16,10 +16,10 @@ */ import FormFields = require('../formFields'); -import Util = require('../../../../util/util'); import { by, element } from 'protractor'; +import Util = require('../../../../util/util'); -export class Dropdown { +export class DropdownWidget { formFields = new FormFields(); diff --git a/e2e/pages/adf/process_services/widgets/dynamicTable.ts b/e2e/pages/adf/process_services/widgets/dynamicTableWidget.ts similarity index 68% rename from e2e/pages/adf/process_services/widgets/dynamicTable.ts rename to e2e/pages/adf/process_services/widgets/dynamicTableWidget.ts index 0fcb62af00..278964d54d 100644 --- a/e2e/pages/adf/process_services/widgets/dynamicTable.ts +++ b/e2e/pages/adf/process_services/widgets/dynamicTableWidget.ts @@ -17,14 +17,18 @@ import FormFields = require('../formFields'); import Util = require('../../../../util/util'); -import { element, by, browser, protractor } from 'protractor'; +import { by, element, browser, protractor } from 'protractor'; + +export class DynamicTableWidget { -export class DynamicTable { formFields = new FormFields(); labelLocator = by.css('dynamic-table-widget div div'); columnNameLocator = by.css('table[id*="dynamic-table"] th'); addButton = element(by.id('label-add-row')); + cancelButton = element(by.cssContainingText('button span', 'Cancel')); + editButton = element(by.cssContainingText('button span', 'edit')); + addRow = element(by.id('dynamictable-add-row')); columnDateTime = element(by.id('columnDateTime')); columnDate = element(by.id('columnDate')); calendarHeader = element(by.css('div[class="mat-datetimepicker-calendar-header-date-time"]')); @@ -34,6 +38,7 @@ export class DynamicTable { dateWidget = element.all(by.css('button[aria-label="Open calendar"]')).first(); calendarNumber = element.all(by.css('td div')); tableRow = element.all(by.css('tbody tr')); + dataTableInput = element(by.id('id')); getFieldLabel(fieldId) { return this.formFields.getFieldLabel(fieldId, this.labelLocator); @@ -48,6 +53,49 @@ export class DynamicTable { return this.addButton.click(); } + clickAddRow() { + Util.waitUntilElementIsVisible(this.addRow); + return this.addRow.click(); + } + + clickTableRow(rowNumber) { + let tableRowByIndex = element(by.id('dynamictable-row-' + rowNumber)); + Util.waitUntilElementIsVisible(tableRowByIndex); + return tableRowByIndex.click(); + } + + clickEditButton() { + Util.waitUntilElementIsVisible(this.editButton); + return this.editButton.click(); + } + + clickCancelButton() { + Util.waitUntilElementIsVisible(this.cancelButton); + return this.cancelButton.click(); + } + + setDatatableInput(text) { + Util.waitUntilElementIsVisible(this.dataTableInput); + this.dataTableInput.clear(); + return this.dataTableInput.sendKeys(text); + } + + getTableRowText(rowNumber) { + let tableRowByIndex = element(by.id('dynamictable-row-' + rowNumber)); + Util.waitUntilElementIsVisible(tableRowByIndex); + return tableRowByIndex.getText(); + } + + checkTableRowIsVisible(rowNumber) { + let tableRowByIndex = element(by.id('dynamictable-row-' + rowNumber)); + return Util.waitUntilElementIsVisible(tableRowByIndex); + } + + checkTableRowIsNotVisible(rowNumber) { + let tableRowByIndex = element(by.id('dynamictable-row-' + rowNumber)); + return Util.waitUntilElementIsNotVisible(tableRowByIndex); + } + clickColumnDateTime() { Util.waitUntilElementIsVisible(this.columnDateTime); this.columnDateTime.click(); diff --git a/e2e/pages/adf/process_services/widgets/header.ts b/e2e/pages/adf/process_services/widgets/headerWidget.ts similarity index 97% rename from e2e/pages/adf/process_services/widgets/header.ts rename to e2e/pages/adf/process_services/widgets/headerWidget.ts index 102092a38d..399f95e1ff 100644 --- a/e2e/pages/adf/process_services/widgets/header.ts +++ b/e2e/pages/adf/process_services/widgets/headerWidget.ts @@ -18,7 +18,7 @@ import FormFields = require('../formFields'); import { by } from 'protractor'; -export class Header { +export class HeaderWidget { formFields = new FormFields(); diff --git a/e2e/pages/adf/process_services/widgets/radioButtons.ts b/e2e/pages/adf/process_services/widgets/hyperlinkWidget.ts similarity index 62% rename from e2e/pages/adf/process_services/widgets/radioButtons.ts rename to e2e/pages/adf/process_services/widgets/hyperlinkWidget.ts index 809a48c134..01c8dbcaf7 100644 --- a/e2e/pages/adf/process_services/widgets/radioButtons.ts +++ b/e2e/pages/adf/process_services/widgets/hyperlinkWidget.ts @@ -16,19 +16,22 @@ */ import FormFields = require('../formFields'); +import { by, element } from 'protractor'; import Util = require('../../../../util/util'); -import { by } from 'protractor'; -export class RadioButtons { +export class HyperlinkWidget { formFields = new FormFields(); - getSpecificOptionLabel(fieldId, optionNumber) { - let optionLocator = by.css('label[for*="radiobuttons-option_' + optionNumber + '"] div[class*="content"]'); + fieldLocator = by.css('div[class="adf-hyperlink-widget "] a'); - let option = this.formFields.getWidget(fieldId).element(optionLocator); - Util.waitUntilElementIsVisible(option); - return option.getText(); + getFieldText(fieldId) { + return this.formFields.getFieldText(fieldId, this.fieldLocator); } + getFieldLabel(fieldId) { + let label = element(by.css(`adf-form-field div[id="field-${fieldId}-container"] label`)); + Util.waitUntilElementIsVisible(label); + return label.getText(); + } } diff --git a/e2e/pages/adf/process_services/widgets/multilineTextWidget.ts b/e2e/pages/adf/process_services/widgets/multilineTextWidget.ts new file mode 100644 index 0000000000..dce3386484 --- /dev/null +++ b/e2e/pages/adf/process_services/widgets/multilineTextWidget.ts @@ -0,0 +1,48 @@ +/*! + * @license + * Copyright 2016 Alfresco Software, Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import FormFields = require('../formFields'); +import { by } from 'protractor'; + +export class MultilineTextWidget { + + formFields = new FormFields(); + + valueLocator = by.css('textarea'); + labelLocator = by.css("label[class*='adf-label']"); + + getFieldValue(fieldId) { + return this.formFields.getFieldValue(fieldId, this.valueLocator); + } + + getFieldLabel(fieldId) { + return this.formFields.getFieldLabel(fieldId, this.labelLocator); + } + + getFieldPlaceHolder(fieldId) { + return this.formFields.getFieldPlaceHolder(fieldId, 'textarea'); + } + + setValue(fieldId, value) { + return this.formFields.setFieldValue(by.id, fieldId, value); + } + + getErrorMessage(fieldId) { + return this.formFields.getFieldErrorMessage(fieldId); + } + +} diff --git a/e2e/pages/adf/process_services/widgets/numberWidget.ts b/e2e/pages/adf/process_services/widgets/numberWidget.ts new file mode 100644 index 0000000000..45f68e3122 --- /dev/null +++ b/e2e/pages/adf/process_services/widgets/numberWidget.ts @@ -0,0 +1,55 @@ +/*! + * @license + * Copyright 2016 Alfresco Software, Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { element, by } from 'protractor'; +import Util = require('../../../../util/util'); +import FormFields = require('../formFields'); + +export class NumberWidget { + + formFields = new FormFields(); + + getNumberFieldLabel(fieldId) { + let label = element(by.css(`adf-form-field div[id="field-${fieldId}-container"] label`)); + Util.waitUntilElementIsVisible(label); + return label.getText(); + } + + setFieldValue(fieldId, value) { + return this.formFields.setValueInInputById(fieldId, value); + } + + clearFieldValue(fieldId) { + let numberField = element(by.id(fieldId)); + Util.waitUntilElementIsVisible(numberField); + return numberField.clear(); + } + + checkWidgetIsVisible(fieldId) { + return this.formFields.checkWidgetIsVisible(fieldId); + } + + getErrorMessage(fieldId) { + let errorMessage = element(by.css(`adf-form-field div[id="field-${fieldId}-container"] div[class="adf-error-text"]`)); + Util.waitUntilElementIsVisible(errorMessage); + return errorMessage.getText(); + } + + getPlaceholder(fieldId) { + return this.formFields.getFieldPlaceHolder(fieldId); + } +} diff --git a/e2e/pages/adf/process_services/widgets/people.ts b/e2e/pages/adf/process_services/widgets/people.ts deleted file mode 100644 index ff0cd67c03..0000000000 --- a/e2e/pages/adf/process_services/widgets/people.ts +++ /dev/null @@ -1,40 +0,0 @@ -/*! - * @license - * Copyright 2016 Alfresco Software, Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { element, by } from 'protractor'; -import Util = require('../../../../util/util'); - -export class People { - - peopleField = element(by.css('input[data-automation-id="adf-people-search-input"]')); - firstResult = element(by.id('adf-people-widget-user-0')); - - checkPeopleFieldIsDisplayed() { - return Util.waitUntilElementIsVisible(this.peopleField); - } - - fillPeopleField(value) { - Util.waitUntilElementIsClickable(this.peopleField); - return this.peopleField.sendKeys(value); - } - - selectUserFromDropdown() { - Util.waitUntilElementIsVisible(this.firstResult); - return this.firstResult.click(); - } - -} diff --git a/e2e/pages/adf/process_services/widgets/peopleWidget.ts b/e2e/pages/adf/process_services/widgets/peopleWidget.ts new file mode 100644 index 0000000000..63690d164f --- /dev/null +++ b/e2e/pages/adf/process_services/widgets/peopleWidget.ts @@ -0,0 +1,83 @@ +/*! + * @license + * Copyright 2016 Alfresco Software, Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import FormFields = require('../formFields'); +import Util = require('../../../../util/util'); +import { by, element } from 'protractor'; + +export class PeopleWidget { + + peopleField = element(by.css('input[data-automation-id="adf-people-search-input"]')); + firstResult = element(by.id('adf-people-widget-user-0')); + + formFields = new FormFields(); + labelLocator = by.css('div[class*="display-text-widget"]'); + inputLocator = by.id('involvepeople'); + peopleDropDownList = by.css('div[class*="adf-people-widget-list"]'); + userProfileImage = by.css('div[class*="adf-people-widget-pic"'); + userProfileName = by.css('div[class*="adf-people-label-name"'); + + getFieldLabel(fieldId) { + return this.formFields.getFieldLabel(fieldId, this.labelLocator); + } + + getFieldValue(fieldId) { + return this.formFields.getFieldValue(fieldId, this.inputLocator); + } + + getFieldText(fieldId) { + return this.formFields.getFieldText(fieldId, this.labelLocator); + } + + insertUser(fieldId, value) { + return this.formFields.setValueInInputById(fieldId, value); + } + + checkDropDownListIsDisplayed() { + return Util.waitUntilElementIsVisible(element(this.peopleDropDownList)); + } + + checkUserIsListed(userName) { + let user = element(by.cssContainingText('.adf-people-label-name', userName)); + return Util.waitUntilElementIsVisible(user); + } + + checkUserNotListed(userName) { + let user = element(by.xpath('div[text()="' + userName + '"]')); + return Util.waitUntilElementIsNotVisible(user); + } + + selectUserFromDropDown(userName) { + let user = element(by.cssContainingText('.adf-people-label-name', userName)); + user.click(); + return this; + } + + checkPeopleFieldIsDisplayed() { + return Util.waitUntilElementIsVisible(this.peopleField); + } + + fillPeopleField(value) { + Util.waitUntilElementIsClickable(this.peopleField); + return this.peopleField.sendKeys(value); + } + + selectUserFromDropdown() { + Util.waitUntilElementIsVisible(this.firstResult); + return this.firstResult.click(); + } +} diff --git a/e2e/pages/adf/process_services/widgets/radioButtonsWidget.ts b/e2e/pages/adf/process_services/widgets/radioButtonsWidget.ts new file mode 100644 index 0000000000..9bc02f7e5c --- /dev/null +++ b/e2e/pages/adf/process_services/widgets/radioButtonsWidget.ts @@ -0,0 +1,55 @@ +/*! + * @license + * Copyright 2016 Alfresco Software, Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import FormFields = require('../formFields'); +import Util = require('../../../../util/util'); +import { by, element } from 'protractor'; + +export class RadioButtonsWidget { + + selectedOption = by.css('mat-radio-button[ng-reflect-checked="false"]'); + + formFields = new FormFields(); + + getSpecificOptionLabel(fieldId, optionNumber) { + let optionLocator = by.css('label[for*="radiobuttons-option_' + optionNumber + '"] div[class*="content"]'); + + let option = this.formFields.getWidget(fieldId).element(optionLocator); + Util.waitUntilElementIsVisible(option); + return option.getText(); + } + + selectOption(fieldId, optionNumber) { + let optionLocator = by.css(`label[for*="${fieldId}-option_${optionNumber}"] div[class*="content"]`); + + let option = this.formFields.getWidget(fieldId).element(optionLocator); + Util.waitUntilElementIsVisible(option); + return option.click(); + } + + isSelectionClean(fieldId) { + let option = this.formFields.getWidget(fieldId).element(this.selectedOption); + return Util.waitUntilElementIsNotVisible(option); + } + + getRadioWidgetLabel(fieldId) { + let label = element(by.css(`adf-form-field div[id="field-${fieldId}-container"] label`)); + Util.waitUntilElementIsVisible(label); + return label.getText(); + } + +} diff --git a/e2e/pages/adf/process_services/widgets/textWidget.ts b/e2e/pages/adf/process_services/widgets/textWidget.ts new file mode 100644 index 0000000000..6bd5c35bb6 --- /dev/null +++ b/e2e/pages/adf/process_services/widgets/textWidget.ts @@ -0,0 +1,54 @@ +/*! + * @license + * Copyright 2016 Alfresco Software, Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import FormFields = require('../formFields'); +import { by } from 'protractor'; + +export class TextWidget { + + formFields = new FormFields(); + + labelLocator = by.css("label[class*='adf-label']"); + + getFieldLabel(fieldId) { + return this.formFields.getFieldLabel(fieldId, this.labelLocator); + } + + getFieldPlaceHolder(fieldId) { + return this.formFields.getFieldPlaceHolder(fieldId); + } + + setValue(fieldId, value) { + return this.formFields.setFieldValue(by.id, fieldId, value); + } + + getFieldValue(fieldId) { + return this.formFields.getFieldValue(fieldId); + } + + getErrorMessage(fieldId) { + return this.formFields.getFieldErrorMessage(fieldId); + } + + isWidgetVisible(fieldId) { + return this.formFields.checkWidgetIsVisible(fieldId); + } + + isWidgetNotVisible(fieldId) { + return this.formFields.checkWidgetIsHidden(fieldId); + } +} diff --git a/e2e/pages/adf/process_services/widgets/widget.ts b/e2e/pages/adf/process_services/widgets/widget.ts index 353d4c8bb9..94b85025a4 100644 --- a/e2e/pages/adf/process_services/widgets/widget.ts +++ b/e2e/pages/adf/process_services/widgets/widget.ts @@ -15,57 +15,96 @@ * limitations under the License. */ -import { MultilineText } from './multilineText'; -import { Header } from './header'; -import { DisplayText } from './displayText'; -import { AttachFile } from './attachFile'; -import { DisplayValue } from './displayValue'; -import { RadioButtons } from './radioButtons'; -import { Hyperlink } from './hyperlink'; -import { Dropdown } from './dropdown'; -import { DynamicTable } from './dynamicTable'; -import { People } from './people'; +import { MultilineTextWidget } from './multilineTextWidget'; +import { HeaderWidget } from './headerWidget'; +import { DisplayTextWidget } from './displayTextWidget'; +import { AttachFileWidget } from './attachFileWidget'; +import { DisplayValueWidget } from './displayValueWidget'; +import { RadioButtonsWidget } from './radioButtonsWidget'; +import { HyperlinkWidget } from './hyperlinkWidget'; +import { DropdownWidget } from './dropdownWidget'; +import { DynamicTableWidget } from './dynamicTableWidget'; +import { TextWidget } from './textWidget'; +import { CheckboxWidget } from './checkboxWidget'; +import { DateWidget } from './dateWidget'; +import { DateTimeWidget } from './dateTimeWidget'; +import { NumberWidget } from './numberWidget'; +import { AmountWidget } from './amountWidget'; +import { ContainerWidget } from './containerWidget'; +import { PeopleWidget } from './peopleWidget'; +import { DocumentWidget } from './documentWidget'; export class Widget { multilineTextWidget() { - return new MultilineText(); + return new MultilineTextWidget(); } headerWidget() { - return new Header(); + return new HeaderWidget(); } displayTextWidget() { - return new DisplayText(); + return new DisplayTextWidget(); } attachFileWidget() { - return new AttachFile(); + return new AttachFileWidget(); } displayValueWidget() { - return new DisplayValue(); + return new DisplayValueWidget(); } radioWidget() { - return new RadioButtons(); + return new RadioButtonsWidget(); } hyperlink() { - return new Hyperlink(); + return new HyperlinkWidget(); } dropdown() { - return new Dropdown(); + return new DropdownWidget(); } dynamicTable() { - return new DynamicTable(); + return new DynamicTableWidget(); + } + + textWidget() { + return new TextWidget(); + } + + documentWidget() { + return new DocumentWidget(); + } + + checkboxWidget() { + return new CheckboxWidget(); + } + + dateWidget() { + return new DateWidget(); + } + + dateTimeWidget() { + return new DateTimeWidget(); + } + + numberWidget() { + return new NumberWidget(); + } + + amountWidget() { + return new AmountWidget(); + } + + containerWidget() { + return new ContainerWidget(); } peopleWidget() { - return new People(); + return new PeopleWidget(); } - } diff --git a/e2e/process-services/dynamic_table_date_picker.e2e.ts b/e2e/process-services/dynamic_table_date_picker.e2e.ts index 7a5d0b2609..0728b57581 100644 --- a/e2e/process-services/dynamic_table_date_picker.e2e.ts +++ b/e2e/process-services/dynamic_table_date_picker.e2e.ts @@ -19,8 +19,8 @@ import { LoginPage } from '../pages/adf/loginPage'; import { ProcessServicesPage } from '../pages/adf/process_services/processServicesPage'; import ProcessFiltersPage = require('../pages/adf/process_services/processFiltersPage'); import { AppNavigationBarPage } from '../pages/adf/process_services/appNavigationBarPage'; -import { DynamicTable } from '../pages/adf/process_services/widgets/dynamicTable'; -import { Dropdown } from '../pages/adf/process_services/widgets/dropdown'; +import { DynamicTableWidget } from '../pages/adf/process_services/widgets/dynamicTableWidget'; +import { DropdownWidget } from '../pages/adf/process_services/widgets/dropdownWidget'; import TestConfig = require('../test.config'); import resources = require('../util/resources'); @@ -35,7 +35,7 @@ describe('Dynamic Table', () => { let processServicesPage = new ProcessServicesPage(); let processFiltersPage = new ProcessFiltersPage(); let appNavigationBarPage = new AppNavigationBarPage(); - let dynamicTable = new DynamicTable(); + let dynamicTable = new DynamicTableWidget(); let user, tenantId, appId, apps, users; beforeAll(async(done) => { @@ -128,7 +128,7 @@ describe('Dynamic Table', () => { describe('Required Dropdown', () => { let app = resources.Files.APP_DYNAMIC_TABLE_DROPDOWN; - let dropdown = new Dropdown(); + let dropdown = new DropdownWidget(); beforeAll(async(done) => { diff --git a/e2e/process-services/form_component.e2e.ts b/e2e/process-services/form_component.e2e.ts index 24a9452b73..5ca47a861a 100644 --- a/e2e/process-services/form_component.e2e.ts +++ b/e2e/process-services/form_component.e2e.ts @@ -18,9 +18,9 @@ import { LoginPage } from '../pages/adf/loginPage'; import { NavigationBarPage } from '../pages/adf/navigationBarPage'; import { FormPage } from '../pages/adf/process_services/formPage'; -import { Date } from '../pages/adf/process_services/widgets/Date'; -import { Amount } from '../pages/adf/process_services/widgets/Amount'; -import { NumberWidget } from '../pages/adf/process_services/widgets/Number'; +import { DateWidget } from '../pages/adf/process_services/widgets/dateWidget'; +import { AmountWidget } from '../pages/adf/process_services/widgets/amountWidget'; +import { NumberWidget } from '../pages/adf/process_services/widgets/numberWidget'; import TestConfig = require('../test.config'); import AlfrescoApi = require('alfresco-js-api-node'); @@ -31,12 +31,18 @@ describe('Form Component', () => { const loginPage = new LoginPage(); const navigationBarPage = new NavigationBarPage(); const formPage = new FormPage(); - const dateWidget = new Date(); - const amountWidget = new Amount(); + const dateWidget = new DateWidget(); + const amountWidget = new AmountWidget(); const numberWidget = new NumberWidget(); let tenantId, user; + let fields = { + dateWidgetId: 'label7', + numberWidgetId: 'label4', + amountWidgetId: 'label11' + }; + let message = { test: 'Text Test', warningNumberAndAmount: 'Use a different number format', @@ -79,26 +85,27 @@ describe('Form Component', () => { }); it('[C286505] Should be able to display errors under the Error Log section', () => { - numberWidget.checkLabel4IsDisplayed(); - numberWidget.addIntoNumberWidget(message.test); + numberWidget.getNumberFieldLabel(fields.numberWidgetId); + numberWidget.setFieldValue(fields.numberWidgetId, message.test); formPage.checkErrorMessageForWidgetIsDisplayed(message.warningNumberAndAmount); formPage.checkErrorLogMessage(message.errorLogNumber); - dateWidget.checkLabel7IsDisplayed(); - dateWidget.addIntoDateWidget(message.test); + dateWidget.checkLabelIsVisible(fields.dateWidgetId); + dateWidget.setDateInput(fields.dateWidgetId, message.test); + dateWidget.clickOutsideWidget(fields.dateWidgetId); formPage.checkErrorMessageForWidgetIsDisplayed(message.warningDate); formPage.checkErrorLogMessage(message.errorLogDate); - amountWidget.checkLabel11IsDisplayed(); - amountWidget.addIntoAmountWidget(message.test); + amountWidget.getAmountFieldLabel(fields.amountWidgetId); + amountWidget.setFieldValue(fields.amountWidgetId, message.test); formPage.checkErrorMessageForWidgetIsDisplayed(message.warningNumberAndAmount); formPage.checkErrorLogMessage(message.errorLogAmount); - amountWidget.removeFromAmountWidget(); + amountWidget.removeFromAmountWidget(fields.amountWidgetId); formPage.checkErrorMessageIsNotDisplayed(message.errorLogAmount); - dateWidget.removeFromDateWidget(); - numberWidget.removeFromNumberWidget(); + dateWidget.clearDateInput(fields.dateWidgetId); + numberWidget.clearFieldValue(fields.numberWidgetId); formPage.checkErrorMessageForWidgetIsNotDisplayed(message.warningDate); formPage.checkErrorMessageIsNotDisplayed(message.errorLogDate); formPage.checkErrorLogMessage(message.errorLabel); diff --git a/e2e/process-services/form_widgets_component.e2e.ts b/e2e/process-services/form_widgets_component.e2e.ts index 845d435ff9..75b1f5c940 100644 --- a/e2e/process-services/form_widgets_component.e2e.ts +++ b/e2e/process-services/form_widgets_component.e2e.ts @@ -31,181 +31,244 @@ import resources = require('../util/resources'); import AlfrescoApi = require('alfresco-js-api-node'); import { AppsActions } from '../actions/APS/apps.actions'; import { UsersActions } from '../actions/users.actions'; +import { browser } from 'protractor'; let formInstance = new FormDefinitionModel(); describe('Form widgets', () => { - + let alfrescoJsApi; + let taskPage = new TasksPage(); + let newTask = 'First task'; let loginPage = new LoginPage(); let processServicesPage = new ProcessServicesPage(); let processUserModel; - let app = resources.Files.WIDGETS_SMOKE_TEST; - let appFields = app.form_fields; - let taskPage = new TasksPage(); let appModel; - let newTask = 'First task'; let widget = new Widget(); - let alfrescoJsApi; - beforeAll(async (done) => { - let users = new UsersActions(); - let appsActions = new AppsActions(); + describe('Form widgets', () => { + let app = resources.Files.WIDGETS_SMOKE_TEST; + let appFields = app.form_fields; - alfrescoJsApi = new AlfrescoApi({ - provider: 'BPM', - hostBpm: TestConfig.adf.url - }); + beforeAll(async (done) => { + let users = new UsersActions(); + let appsActions = new AppsActions(); - await alfrescoJsApi.login(TestConfig.adf.adminEmail, TestConfig.adf.adminPassword); - - processUserModel = await users.createTenantAndUser(alfrescoJsApi); - - await alfrescoJsApi.login(processUserModel.email, processUserModel.password); - - appModel = await appsActions.importPublishDeployApp(alfrescoJsApi, app.file_location); - - done(); - }); - - afterAll(async (done) => { - - await alfrescoJsApi.login(TestConfig.adf.adminEmail, TestConfig.adf.adminPassword); - - await alfrescoJsApi.activiti.adminTenantsApi.deleteTenant(processUserModel.tenantId); - - done(); - }); - - it('[C272778] Should display text and multi-line in form', () => { - loginPage.loginToProcessServicesUsingUserModel(processUserModel); - processServicesPage.goToProcessServices().goToApp(appModel.name) - .clickTasksButton(); - taskPage.filtersPage().goToFilter(CONSTANTS.TASK_FILTERS.MY_TASKS); - taskPage.createNewTask().addName(newTask).addDescription('Description').addForm(app.formName).clickStartButton() - .then(() => { - taskPage.tasksListPage().checkTaskIsDisplayedInTasksList(newTask); - taskPage.formFields().checkFormIsDisplayed(); - expect(taskPage.taskDetails().getTitle()).toEqual('Activities'); - }) - .then(() => { - return alfrescoJsApi.activiti.taskApi.listTasks(new Task({ sort: 'created-desc' })); - }) - .then( (response) => { - return alfrescoJsApi.activiti.taskFormsApi.getTaskForm(response.data[0].id); - }) - .then((formDefinition) => { - formInstance.setFields(formDefinition.fields); - formInstance.setAllWidgets(formDefinition.fields); - return formInstance; - }) - .then(() => { - expect(taskPage.formFields().getFieldLabel(appFields.text_id)) - .toEqual(formInstance.getWidgetBy('id', appFields.text_id).name); - expect(taskPage.formFields().getFieldValue(appFields.text_id)) - .toEqual(formInstance.getWidgetBy('id', appFields.text_id).value || ''); - - expect(widget.multilineTextWidget().getFieldValue(appFields.multiline_id)) - .toEqual(formInstance.getWidgetBy('id', appFields.multiline_id).value || ''); - expect(taskPage.formFields().getFieldLabel(appFields.multiline_id)) - .toEqual(formInstance.getWidgetBy('id', appFields.multiline_id).name); + alfrescoJsApi = new AlfrescoApi({ + provider: 'BPM', + hostBpm: TestConfig.adf.url }); + await alfrescoJsApi.login(TestConfig.adf.adminEmail, TestConfig.adf.adminPassword); + + processUserModel = await users.createTenantAndUser(alfrescoJsApi); + + await alfrescoJsApi.login(processUserModel.email, processUserModel.password); + + appModel = await appsActions.importPublishDeployApp(alfrescoJsApi, app.file_location); + + done(); + }); + + afterAll(async (done) => { + + await alfrescoJsApi.login(TestConfig.adf.adminEmail, TestConfig.adf.adminPassword); + + await alfrescoJsApi.activiti.adminTenantsApi.deleteTenant(processUserModel.tenantId); + + done(); + }); + + it('[C272778] Should display text and multi-line in form', () => { + loginPage.loginToProcessServicesUsingUserModel(processUserModel); + processServicesPage.goToProcessServices().goToApp(appModel.name) + .clickTasksButton(); + taskPage.filtersPage().goToFilter(CONSTANTS.TASK_FILTERS.MY_TASKS); + taskPage.createNewTask().addName(newTask).addDescription('Description').addForm(app.formName).clickStartButton() + .then(() => { + taskPage.tasksListPage().checkTaskIsDisplayedInTasksList(newTask); + taskPage.formFields().checkFormIsDisplayed(); + expect(taskPage.taskDetails().getTitle()).toEqual('Activities'); + }) + .then(() => { + return alfrescoJsApi.activiti.taskApi.listTasks(new Task({ sort: 'created-desc' })); + }) + .then((response) => { + return alfrescoJsApi.activiti.taskFormsApi.getTaskForm(response.data[0].id); + }) + .then((formDefinition) => { + formInstance.setFields(formDefinition.fields); + formInstance.setAllWidgets(formDefinition.fields); + return formInstance; + }) + .then(() => { + expect(taskPage.formFields().getFieldLabel(appFields.text_id)) + .toEqual(formInstance.getWidgetBy('id', appFields.text_id).name); + expect(taskPage.formFields().getFieldValue(appFields.text_id)) + .toEqual(formInstance.getWidgetBy('id', appFields.text_id).value || ''); + + expect(widget.multilineTextWidget().getFieldValue(appFields.multiline_id)) + .toEqual(formInstance.getWidgetBy('id', appFields.multiline_id).value || ''); + expect(taskPage.formFields().getFieldLabel(appFields.multiline_id)) + .toEqual(formInstance.getWidgetBy('id', appFields.multiline_id).name); + }); + + }); + + it('[C272779] Should display number and amount in form', () => { + + expect(taskPage.formFields().getFieldValue(appFields.number_id)) + .toEqual(formInstance.getWidgetBy('id', appFields.number_id).value || ''); + expect(taskPage.formFields().getFieldLabel(appFields.number_id)) + .toEqual(formInstance.getWidgetBy('id', appFields.number_id).name); + + expect(taskPage.formFields().getFieldValue(appFields.amount_id)) + .toEqual(formInstance.getWidgetBy('id', appFields.amount_id).value || ''); + expect(taskPage.formFields().getFieldLabel(appFields.amount_id)) + .toEqual(formInstance.getWidgetBy('id', appFields.amount_id).name); + }); + + it('[C272780] Should display attach file and attach folder in form', () => { + + expect(taskPage.formFields().getFieldLabel(appFields.attachFolder_id)) + .toEqual(formInstance.getWidgetBy('id', appFields.attachFolder_id).name); + expect(taskPage.formFields().getFieldLabel(appFields.attachFile_id)) + .toEqual(formInstance.getWidgetBy('id', appFields.attachFile_id).name); + }); + + it('[C272781] Should display date and date & time in form', () => { + + expect(taskPage.formFields().getFieldLabel(appFields.date_id)) + .toContain(formInstance.getWidgetBy('id', appFields.date_id).name); + expect(taskPage.formFields().getFieldValue(appFields.date_id)) + .toEqual(formInstance.getWidgetBy('id', appFields.date_id).value || ''); + + expect(taskPage.formFields().getFieldLabel(appFields.dateTime_id)) + .toContain(formInstance.getWidgetBy('id', appFields.dateTime_id).name); + expect(taskPage.formFields().getFieldValue(appFields.dateTime_id)) + .toEqual(formInstance.getWidgetBy('id', appFields.dateTime_id).value || ''); + }); + + it('[C272782] Should display people and group in form', () => { + + expect(taskPage.formFields().getFieldValue(appFields.people_id)) + .toEqual(formInstance.getWidgetBy('id', appFields.people_id).value || ''); + expect(taskPage.formFields().getFieldLabel(appFields.people_id)) + .toEqual(formInstance.getWidgetBy('id', appFields.people_id).name); + + expect(taskPage.formFields().getFieldValue(appFields.group_id)) + .toEqual(formInstance.getWidgetBy('id', appFields.group_id).value || ''); + expect(taskPage.formFields().getFieldLabel(appFields.group_id)) + .toEqual(formInstance.getWidgetBy('id', appFields.group_id).name); + }); + + it('[C272783] Should display displayText and displayValue in form', () => { + + expect(widget.displayTextWidget().getFieldLabel(appFields.displayText_id)) + .toEqual(formInstance.getWidgetBy('id', appFields.displayText_id).value); + expect(widget.displayValueWidget().getFieldLabel(appFields.displayValue_id)) + .toEqual(formInstance.getWidgetBy('id', appFields.displayValue_id).value || 'Unknown type: readonly'); + }); + + it('[C272784] Should display typeahead and header in form', () => { + expect(widget.headerWidget().getFieldLabel(appFields.header_id)) + .toEqual(formInstance.getWidgetBy('id', appFields.header_id).name); + expect(taskPage.formFields().getFieldValue(appFields.typeAhead_id)) + .toEqual(formInstance.getWidgetBy('id', appFields.typeAhead_id).value || ''); + expect(taskPage.formFields().getFieldLabel(appFields.typeAhead_id)) + .toEqual(formInstance.getWidgetBy('id', appFields.typeAhead_id).name); + }); + + it('[C272785] Should display checkbox and radio button in form', () => { + let radioOption = 1; + + expect(taskPage.formFields().getFieldLabel(appFields.checkbox_id)) + .toContain(formInstance.getWidgetBy('id', appFields.checkbox_id).name); + + expect(taskPage.formFields().getFieldLabel(appFields.radioButtons_id)) + .toContain(formInstance.getWidgetBy('id', appFields.radioButtons_id).name); + expect(widget.radioWidget().getSpecificOptionLabel(appFields.radioButtons_id, radioOption)) + .toContain(formInstance.getWidgetBy('id', appFields.radioButtons_id).options[radioOption - 1].name); + }); + + it('[C268149] Should display hyperlink, dropdown and dynamic table in form', () => { + + expect(widget.hyperlink().getFieldText(appFields.hyperlink_id)) + .toEqual(formInstance.getWidgetBy('id', appFields.hyperlink_id).hyperlinkUrl || ''); + expect(taskPage.formFields().getFieldLabel(appFields.hyperlink_id)) + .toEqual(formInstance.getWidgetBy('id', appFields.hyperlink_id).name); + + expect(taskPage.formFields().getFieldLabel(appFields.dropdown_id)) + .toContain(formInstance.getWidgetBy('id', appFields.dropdown_id).name); + expect(widget.dropdown().getSelectedOptionText(appFields.dropdown_id)) + .toContain(formInstance.getWidgetBy('id', appFields.dropdown_id).value); + + expect(widget.dynamicTable().getFieldLabel(appFields.dynamicTable_id)) + .toContain(formInstance.getWidgetBy('id', appFields.dynamicTable_id).name); + expect(widget.dynamicTable().getColumnName(appFields.dynamicTable_id)) + .toContain(formInstance.getWidgetBy('id', appFields.dynamicTable_id).columnDefinitions[0].name); + }); + }); - it('[C272779] Should display number and amount in form', () => { + describe('with fields involving other people', () => { - expect(taskPage.formFields().getFieldValue(appFields.number_id)) - .toEqual(formInstance.getWidgetBy('id', appFields.number_id).value || ''); - expect(taskPage.formFields().getFieldLabel(appFields.number_id)) - .toEqual(formInstance.getWidgetBy('id', appFields.number_id).name); + let appsActions = new AppsActions(); + let app = resources.Files.FORM_ADF; + let deployedApp, process; + let appFields = app.form_fields; - expect(taskPage.formFields().getFieldValue(appFields.amount_id)) - .toEqual(formInstance.getWidgetBy('id', appFields.amount_id).value || ''); - expect(taskPage.formFields().getFieldLabel(appFields.amount_id)) - .toEqual(formInstance.getWidgetBy('id', appFields.amount_id).name); + beforeAll(async (done) => { + let users = new UsersActions(); + + alfrescoJsApi = new AlfrescoApi({ + provider: 'BPM', + hostBpm: TestConfig.adf.url + }); + + await alfrescoJsApi.login(TestConfig.adf.adminEmail, TestConfig.adf.adminPassword); + + processUserModel = await users.createTenantAndUser(alfrescoJsApi); + + await alfrescoJsApi.login(processUserModel.email, processUserModel.password); + appModel = await appsActions.importPublishDeployApp(alfrescoJsApi, app.file_location); + + let appDefinitions = await alfrescoJsApi.activiti.appsApi.getAppDefinitions(); + deployedApp = appDefinitions.data.find((currentApp) => { + return currentApp.modelId === appModel.id; + }); + process = await appsActions.startProcess(alfrescoJsApi, appModel, app.processName); + loginPage.loginToProcessServicesUsingUserModel(processUserModel); + done(); + }); + + beforeEach(() => { + let urlToNavigateTo = `${TestConfig.adf.url}/activiti/apps/${deployedApp.id}/tasks/`; + browser.get(urlToNavigateTo); + taskPage.filtersPage().goToFilter(CONSTANTS.TASK_FILTERS.MY_TASKS); + taskPage.formFields().checkFormIsDisplayed(); + }); + + afterAll(async (done) => { + await alfrescoJsApi.activiti.processApi.deleteProcessInstance(process.id); + await alfrescoJsApi.login(TestConfig.adf.adminEmail, TestConfig.adf.adminPassword); + await alfrescoJsApi.activiti.adminTenantsApi.deleteTenant(processUserModel.tenantId); + done(); + }); + + it('[C260405] Value fields configured with process variables', () => { + taskPage.formFields().checkFormIsDisplayed(); + expect(taskPage.taskDetails().getTitle()).toEqual('Activities'); + + taskPage.formFields().setValueInInputById('label', 'value 1').completeForm(); + /* cspell:disable-next-line */ + taskPage.filtersPage().goToFilter(CONSTANTS.TASK_FILTERS.COMPLETED_TASKS); + + expect(widget.displayTextWidget().getFieldText(appFields.displayText_id)) + .toContain('value 1'); + expect(widget.textWidget().getFieldValue(appFields.text_id)) + .toEqual('value 1'); + expect(widget.displayValueWidget().getFieldValue(appFields.displayValue_id)) + .toEqual('value 1'); + }); }); - - it('[C272780] Should display attach file and attach folder in form', () => { - - expect(taskPage.formFields().getFieldLabel(appFields.attachFolder_id)) - .toEqual(formInstance.getWidgetBy('id', appFields.attachFolder_id).name); - expect(taskPage.formFields().getFieldLabel(appFields.attachFile_id)) - .toEqual(formInstance.getWidgetBy('id', appFields.attachFile_id).name); - }); - - it('[C272781] Should display date and date & time in form', () => { - - expect(taskPage.formFields().getFieldLabel(appFields.date_id)) - .toContain(formInstance.getWidgetBy('id', appFields.date_id).name); - expect(taskPage.formFields().getFieldValue(appFields.date_id)) - .toEqual(formInstance.getWidgetBy('id', appFields.date_id).value || ''); - - expect(taskPage.formFields().getFieldLabel(appFields.dateTime_id)) - .toContain(formInstance.getWidgetBy('id', appFields.dateTime_id).name); - expect(taskPage.formFields().getFieldValue(appFields.dateTime_id)) - .toEqual(formInstance.getWidgetBy('id', appFields.dateTime_id).value || ''); - }); - - it('[C272782] Should display people and group in form', () => { - - expect(taskPage.formFields().getFieldValue(appFields.people_id)) - .toEqual(formInstance.getWidgetBy('id', appFields.people_id).value || ''); - expect(taskPage.formFields().getFieldLabel(appFields.people_id)) - .toEqual(formInstance.getWidgetBy('id', appFields.people_id).name); - - expect(taskPage.formFields().getFieldValue(appFields.group_id)) - .toEqual(formInstance.getWidgetBy('id', appFields.group_id).value || ''); - expect(taskPage.formFields().getFieldLabel(appFields.group_id)) - .toEqual(formInstance.getWidgetBy('id', appFields.group_id).name); - }); - - it('[C272783] Should display displayText and displayValue in form', () => { - - expect(widget.displayTextWidget().getFieldLabel(appFields.displayText_id)) - .toEqual(formInstance.getWidgetBy('id', appFields.displayText_id).value); - expect(widget.displayValueWidget().getFieldLabel(appFields.displayValue_id)) - .toEqual(formInstance.getWidgetBy('id', appFields.displayValue_id).value || 'Unknown type: readonly'); - }); - - it('[C272784] Should display typeahead and header in form', () => { - - expect(widget.headerWidget().getFieldLabel(appFields.header_id)) - .toEqual(formInstance.getWidgetBy('id', appFields.header_id).name); - - expect(taskPage.formFields().getFieldValue(appFields.typeAhead_id)) - .toEqual(formInstance.getWidgetBy('id', appFields.typeAhead_id).value || ''); - expect(taskPage.formFields().getFieldLabel(appFields.typeAhead_id)) - .toEqual(formInstance.getWidgetBy('id', appFields.typeAhead_id).name); - }); - - it('[C272785] Should display checkbox and radio button in form', () => { - let radioOption = 1; - - expect(taskPage.formFields().getFieldLabel(appFields.checkbox_id)) - .toContain(formInstance.getWidgetBy('id', appFields.checkbox_id).name); - - expect(taskPage.formFields().getFieldLabel(appFields.radioButtons_id)) - .toContain(formInstance.getWidgetBy('id', appFields.radioButtons_id).name); - expect(widget.radioWidget().getSpecificOptionLabel(appFields.radioButtons_id, radioOption)) - .toContain(formInstance.getWidgetBy('id', appFields.radioButtons_id).options[radioOption - 1].name); - }); - - it('[C268149] Should display hyperlink, dropdown and dynamic table in form', () => { - - expect(widget.hyperlink().getFieldText(appFields.hyperlink_id)) - .toEqual(formInstance.getWidgetBy('id', appFields.hyperlink_id).hyperlinkUrl || ''); - expect(taskPage.formFields().getFieldLabel(appFields.hyperlink_id)) - .toEqual(formInstance.getWidgetBy('id', appFields.hyperlink_id).name); - - expect(taskPage.formFields().getFieldLabel(appFields.dropdown_id)) - .toContain(formInstance.getWidgetBy('id', appFields.dropdown_id).name); - expect(widget.dropdown().getSelectedOptionText(appFields.dropdown_id)) - .toContain(formInstance.getWidgetBy('id', appFields.dropdown_id).value); - - expect(widget.dynamicTable().getFieldLabel(appFields.dynamicTable_id)) - .toContain(formInstance.getWidgetBy('id', appFields.dynamicTable_id).name); - expect(widget.dynamicTable().getColumnName(appFields.dynamicTable_id)) - .toContain(formInstance.getWidgetBy('id', appFields.dynamicTable_id).columnDefinitions[0].name); - }); - }); diff --git a/e2e/process-services/widgets/amount_widget.e2e.ts b/e2e/process-services/widgets/amount_widget.e2e.ts new file mode 100644 index 0000000000..add7728588 --- /dev/null +++ b/e2e/process-services/widgets/amount_widget.e2e.ts @@ -0,0 +1,104 @@ +/*! + * @license + * Copyright 2016 Alfresco Software, Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import AlfrescoApi = require('alfresco-js-api-node'); +import { AppsActions } from '../../actions/APS/apps.actions'; +import { UsersActions } from '../../actions/users.actions'; +import { browser } from 'protractor'; +import { LoginPage } from '../../pages/adf/loginPage'; +import { TasksPage } from '../../pages/adf/process_services/tasksPage'; +import { Widget } from '../../pages/adf/process_services/widgets/widget'; + +import CONSTANTS = require('../../util/constants'); +import TestConfig = require('../../test.config'); +import resources = require('../../util/resources'); + +describe('Amount Widget', () => { + + let loginPage = new LoginPage(); + + let processUserModel; + let taskPage = new TasksPage(); + let widget = new Widget(); + let alfrescoJsApi; + let appsActions = new AppsActions(); + let appModel; + let app = resources.Files.WIDGET_CHECK_APP.AMOUNT; + let deployedApp, process; + + beforeAll(async (done) => { + let users = new UsersActions(); + + alfrescoJsApi = new AlfrescoApi({ + provider: 'BPM', + hostBpm: TestConfig.adf.url + }); + + await alfrescoJsApi.login(TestConfig.adf.adminEmail, TestConfig.adf.adminPassword); + + processUserModel = await users.createTenantAndUser(alfrescoJsApi); + + await alfrescoJsApi.login(processUserModel.email, processUserModel.password); + appModel = await appsActions.importPublishDeployApp(alfrescoJsApi, resources.Files.WIDGET_CHECK_APP.file_location); + + let appDefinitions = await alfrescoJsApi.activiti.appsApi.getAppDefinitions(); + deployedApp = appDefinitions.data.find((currentApp) => { + return currentApp.modelId === appModel.id; + }); + process = await appsActions.startProcess(alfrescoJsApi, appModel, app.processName); + loginPage.loginToProcessServicesUsingUserModel(processUserModel); + done(); + }); + + beforeEach(() => { + let urlToNavigateTo = `${TestConfig.adf.url}/activiti/apps/${deployedApp.id}/tasks/`; + browser.get(urlToNavigateTo); + taskPage.filtersPage().goToFilter(CONSTANTS.TASK_FILTERS.MY_TASKS); + taskPage.formFields().checkFormIsDisplayed(); + }); + + afterAll(async (done) => { + await alfrescoJsApi.activiti.processApi.deleteProcessInstance(process.id); + await alfrescoJsApi.login(TestConfig.adf.adminEmail, TestConfig.adf.adminPassword); + await alfrescoJsApi.activiti.adminTenantsApi.deleteTenant(processUserModel.tenantId); + done(); + }); + + it('[C274703] General, advanced, Amount and Visibility properties', () => { + taskPage.formFields().checkWidgetIsHidden(app.FIELD.amount_input_id); + widget.checkboxWidget().clickCheckboxInput(app.FIELD.checkbox_id); + taskPage.formFields().checkWidgetIsVisible(app.FIELD.amount_input_id); + + expect(taskPage.formFields().isCompleteFormButtonDisabled()).toBeTruthy(); + expect(widget.amountWidget().getAmountFieldLabel(app.FIELD.amount_input_id)).toContain('Amount'); + expect(widget.amountWidget().getPlaceholder(app.FIELD.amount_input_id)).toContain('Type amount'); + expect(widget.amountWidget().getAmountFieldCurrency(app.FIELD.amount_input_id)).toBe('$'); + + widget.amountWidget().setFieldValue(app.FIELD.amount_input_id, 4); + expect(widget.amountWidget().getErrorMessage(app.FIELD.amount_input_id)).toBe('Can\'t be less than 5'); + expect(taskPage.formFields().isCompleteFormButtonDisabled()).toBeTruthy(); + widget.amountWidget().clearFieldValue(app.FIELD.amount_input_id); + + widget.amountWidget().setFieldValue(app.FIELD.amount_input_id, 101); + expect(widget.amountWidget().getErrorMessage(app.FIELD.amount_input_id)).toBe('Can\'t be greater than 100'); + expect(taskPage.formFields().isCompleteFormButtonDisabled()).toBeTruthy(); + widget.amountWidget().clearFieldValue(app.FIELD.amount_input_id); + + widget.amountWidget().setFieldValue(app.FIELD.amount_input_id, 6); + expect(taskPage.formFields().isCompleteFormButtonDisabled()).toBeFalsy(); + }); +}); diff --git a/e2e/process-services/widgets/attach_folder_widget.e2e.ts b/e2e/process-services/widgets/attach_folder_widget.e2e.ts new file mode 100644 index 0000000000..a6e2e7871f --- /dev/null +++ b/e2e/process-services/widgets/attach_folder_widget.e2e.ts @@ -0,0 +1,85 @@ +/*! + * @license + * Copyright 2016 Alfresco Software, Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import AlfrescoApi = require('alfresco-js-api-node'); +import { AppsActions } from '../../actions/APS/apps.actions'; +import { UsersActions } from '../../actions/users.actions'; +import { browser } from 'protractor'; +import { LoginPage } from '../../pages/adf/loginPage'; +import { TasksPage } from '../../pages/adf/process_services/tasksPage'; +import { Widget } from '../../pages/adf/process_services/widgets/widget'; +import CONSTANTS = require('../../util/constants'); +import TestConfig = require('../../test.config'); +import resources = require('../../util/resources'); + +describe('Attach Folder widget', () => { + let loginPage = new LoginPage(); + let processUserModel; + let taskPage = new TasksPage(); + let widget = new Widget(); + let alfrescoJsApi; + let appsActions = new AppsActions(); + let appModel; + let app = resources.Files.WIDGET_CHECK_APP.ATTACH_FOLDER; + let deployedApp, process; + + beforeAll(async (done) => { + let users = new UsersActions(); + + alfrescoJsApi = new AlfrescoApi({ + provider: 'BPM', + hostBpm: TestConfig.adf.url + }); + + await alfrescoJsApi.login(TestConfig.adf.adminEmail, TestConfig.adf.adminPassword); + + processUserModel = await users.createTenantAndUser(alfrescoJsApi); + + await alfrescoJsApi.login(processUserModel.email, processUserModel.password); + appModel = await appsActions.importPublishDeployApp(alfrescoJsApi, resources.Files.WIDGET_CHECK_APP.file_location); + + let appDefinitions = await alfrescoJsApi.activiti.appsApi.getAppDefinitions(); + deployedApp = appDefinitions.data.find((currentApp) => { + return currentApp.modelId === appModel.id; + }); + process = await appsActions.startProcess(alfrescoJsApi, appModel, app.processName); + loginPage.loginToProcessServicesUsingUserModel(processUserModel); + done(); + }); + + beforeEach(() => { + let urlToNavigateTo = `${TestConfig.adf.url}/activiti/apps/${deployedApp.id}/tasks/`; + browser.get(urlToNavigateTo); + taskPage.filtersPage().goToFilter(CONSTANTS.TASK_FILTERS.MY_TASKS); + taskPage.formFields().checkFormIsDisplayed(); + }); + + afterAll(async (done) => { + await alfrescoJsApi.activiti.processApi.deleteProcessInstance(process.id); + await alfrescoJsApi.login(TestConfig.adf.adminEmail, TestConfig.adf.adminPassword); + await alfrescoJsApi.activiti.adminTenantsApi.deleteTenant(processUserModel.tenantId); + done(); + }); + + it('[C276745] Attach folder widget - Visibility', () => { + taskPage.formFields().checkWidgetIsHidden(app.FIELD.upload_button_id); + widget.checkboxWidget().clickCheckboxInput(app.FIELD.checkbox_id); + taskPage.formFields().checkWidgetIsVisible(app.FIELD.upload_button_id); + + expect(taskPage.formFields().isCompleteFormButtonDisabled()).toBeTruthy(); + }); +}); diff --git a/e2e/process-services/widgets/checkbox_widget.e2e.ts b/e2e/process-services/widgets/checkbox_widget.e2e.ts new file mode 100644 index 0000000000..7583acc9c2 --- /dev/null +++ b/e2e/process-services/widgets/checkbox_widget.e2e.ts @@ -0,0 +1,92 @@ +/*! + * @license + * Copyright 2016 Alfresco Software, Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import AlfrescoApi = require('alfresco-js-api-node'); +import { AppsActions } from '../../actions/APS/apps.actions'; +import { UsersActions } from '../../actions/users.actions'; +import { browser } from 'protractor'; +import { LoginPage } from '../../pages/adf/loginPage'; +import { TasksPage } from '../../pages/adf/process_services/tasksPage'; +import { Widget } from '../../pages/adf/process_services/widgets/widget'; +import CONSTANTS = require('../../util/constants'); +import TestConfig = require('../../test.config'); +import resources = require('../../util/resources'); + +describe('Checkbox Widget', () => { + + let loginPage = new LoginPage(); + let processUserModel; + let taskPage = new TasksPage(); + let widget = new Widget(); + let alfrescoJsApi; + let appsActions = new AppsActions(); + let appModel; + let app = resources.Files.WIDGET_CHECK_APP.CHECKBOX; + let deployedApp, process; + + beforeAll(async (done) => { + let users = new UsersActions(); + + alfrescoJsApi = new AlfrescoApi({ + provider: 'BPM', + hostBpm: TestConfig.adf.url + }); + + await alfrescoJsApi.login(TestConfig.adf.adminEmail, TestConfig.adf.adminPassword); + + processUserModel = await users.createTenantAndUser(alfrescoJsApi); + + await alfrescoJsApi.login(processUserModel.email, processUserModel.password); + appModel = await appsActions.importPublishDeployApp(alfrescoJsApi, resources.Files.WIDGET_CHECK_APP.file_location); + + let appDefinitions = await alfrescoJsApi.activiti.appsApi.getAppDefinitions(); + deployedApp = appDefinitions.data.find((currentApp) => { + return currentApp.modelId === appModel.id; + }); + process = await appsActions.startProcess(alfrescoJsApi, appModel, app.processName); + loginPage.loginToProcessServicesUsingUserModel(processUserModel); + done(); + }); + + beforeEach(() => { + let urlToNavigateTo = `${TestConfig.adf.url}/activiti/apps/${deployedApp.id}/tasks/`; + browser.get(urlToNavigateTo); + taskPage.filtersPage().goToFilter(CONSTANTS.TASK_FILTERS.MY_TASKS); + taskPage.formFields().checkFormIsDisplayed(); + }); + + afterAll(async (done) => { + await alfrescoJsApi.activiti.processApi.deleteProcessInstance(process.id); + await alfrescoJsApi.login(TestConfig.adf.adminEmail, TestConfig.adf.adminPassword); + await alfrescoJsApi.activiti.adminTenantsApi.deleteTenant(processUserModel.tenantId); + done(); + }); + + it('[C268554] Checkbox widget - General settings', () => { + taskPage.formFields().setValueInInputById(app.FIELD.number_input_id, 2); + expect(widget.checkboxWidget().getCheckboxLabel()).toContain(app.FIELD.checkbox_label); + expect(taskPage.formFields().isCompleteFormButtonDisabled()).toBeTruthy(); + widget.checkboxWidget().clickCheckboxInput(app.FIELD.checkbox_input_id); + expect(taskPage.formFields().isCompleteFormButtonDisabled()).toBeFalsy(); + }); + + it('[C272812] Checkbox widget - Visibility settings', () => { + widget.checkboxWidget().isCheckboxHidden(app.FIELD.checkbox_field_id); + taskPage.formFields().setValueInInputById(app.FIELD.number_input_id, 2); + widget.checkboxWidget().isCheckboxDisplayed(app.FIELD.checkbox_field_id); + }); +}); diff --git a/e2e/process-services/widgets/date_time_widget.e2e.ts b/e2e/process-services/widgets/date_time_widget.e2e.ts new file mode 100644 index 0000000000..3f20d58bea --- /dev/null +++ b/e2e/process-services/widgets/date_time_widget.e2e.ts @@ -0,0 +1,108 @@ +/*! + * @license + * Copyright 2016 Alfresco Software, Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import AlfrescoApi = require('alfresco-js-api-node'); +import { AppsActions } from '../../actions/APS/apps.actions'; +import { UsersActions } from '../../actions/users.actions'; +import { browser } from 'protractor'; +import { LoginPage } from '../../pages/adf/loginPage'; +import { TasksPage } from '../../pages/adf/process_services/tasksPage'; +import { Widget } from '../../pages/adf/process_services/widgets/widget'; +import CONSTANTS = require('../../util/constants'); +import TestConfig = require('../../test.config'); +import resources = require('../../util/resources'); + +describe('Date and time widget', () => { + + let loginPage = new LoginPage(); + let processUserModel; + let taskPage = new TasksPage(); + let widget = new Widget(); + let alfrescoJsApi; + let appsActions = new AppsActions(); + let appModel; + let app = resources.Files.WIDGET_CHECK_APP.DATETIME; + let deployedApp, process; + + beforeAll(async (done) => { + let users = new UsersActions(); + + alfrescoJsApi = new AlfrescoApi({ + provider: 'BPM', + hostBpm: TestConfig.adf.url + }); + + await alfrescoJsApi.login(TestConfig.adf.adminEmail, TestConfig.adf.adminPassword); + + processUserModel = await users.createTenantAndUser(alfrescoJsApi); + + await alfrescoJsApi.login(processUserModel.email, processUserModel.password); + appModel = await appsActions.importPublishDeployApp(alfrescoJsApi, resources.Files.WIDGET_CHECK_APP.file_location); + + let appDefinitions = await alfrescoJsApi.activiti.appsApi.getAppDefinitions(); + deployedApp = appDefinitions.data.find((currentApp) => { + return currentApp.modelId === appModel.id; + }); + process = await appsActions.startProcess(alfrescoJsApi, appModel, app.processName); + loginPage.loginToProcessServicesUsingUserModel(processUserModel); + done(); + }); + + beforeEach(() => { + let urlToNavigateTo = `${TestConfig.adf.url}/activiti/apps/${deployedApp.id}/tasks/`; + browser.get(urlToNavigateTo); + taskPage.filtersPage().goToFilter(CONSTANTS.TASK_FILTERS.MY_TASKS); + taskPage.formFields().checkFormIsDisplayed(); + }); + + afterAll(async (done) => { + await alfrescoJsApi.activiti.processApi.deleteProcessInstance(process.id); + await alfrescoJsApi.login(TestConfig.adf.adminEmail, TestConfig.adf.adminPassword); + await alfrescoJsApi.activiti.adminTenantsApi.deleteTenant(processUserModel.tenantId); + done(); + }); + + it('C268818] Date and time widget - General properties', () => { + expect(widget.dateTimeWidget().getDateTimeLabel(app.FIELD.date_time_input)).toContain('Date'); + expect(taskPage.formFields().isCompleteFormButtonDisabled()).toBeTruthy(); + + widget.dateTimeWidget().openDatepicker(app.FIELD.date_time_input); + widget.dateTimeWidget().selectDay('10'); + widget.dateTimeWidget().selectHour('8'); + widget.dateTimeWidget().selectMinute('30'); + expect(taskPage.formFields().isCompleteFormButtonDisabled()).toBeFalsy(); + + expect(widget.dateTimeWidget().getPlaceholder(app.FIELD.date_time_between_input)).toBe('Choose anything...'); + }); + + it('[C268819] Date and time widget - Advanced properties', () => { + widget.dateTimeWidget().openDatepicker(app.FIELD.date_time_between_input); + widget.dateTimeWidget().closeDataTimeWidget(); + widget.dateTimeWidget().setDateTimeInput(app.FIELD.date_time_between_input, '20-03-17 07:30 PM'); + widget.dateTimeWidget().clickOutsideWidget(app.FIELD.date_time_between_input); + expect(widget.dateTimeWidget().getErrorMessage(app.FIELD.date_time_between_input)).toContain('Can\'t be less than'); + + widget.dateTimeWidget().closeDataTimeWidget(); + + widget.dateTimeWidget().clickOutsideWidget(app.FIELD.date_time_between_input); + widget.dateTimeWidget().removeFromDatetimeWidget(app.FIELD.date_time_between_input); + widget.dateTimeWidget().closeDataTimeWidget(); + widget.dateTimeWidget().setDateTimeInput(app.FIELD.date_time_between_input, '20-03-19 07:30 PM'); + widget.dateTimeWidget().clickOutsideWidget(app.FIELD.date_time_between_input); + expect(widget.dateTimeWidget().getErrorMessage(app.FIELD.date_time_between_input)).toContain('Can\'t be greater than'); + }); +}); diff --git a/e2e/process-services/widgets/date_widget.e2e.ts b/e2e/process-services/widgets/date_widget.e2e.ts new file mode 100644 index 0000000000..de739dac58 --- /dev/null +++ b/e2e/process-services/widgets/date_widget.e2e.ts @@ -0,0 +1,96 @@ +/*! + * @license + * Copyright 2016 Alfresco Software, Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import AlfrescoApi = require('alfresco-js-api-node'); +import { AppsActions } from '../../actions/APS/apps.actions'; +import { UsersActions } from '../../actions/users.actions'; +import { browser } from 'protractor'; +import { LoginPage } from '../../pages/adf/loginPage'; +import { TasksPage } from '../../pages/adf/process_services/tasksPage'; +import { Widget } from '../../pages/adf/process_services/widgets/widget'; +import CONSTANTS = require('../../util/constants'); +import TestConfig = require('../../test.config'); +import resources = require('../../util/resources'); + +describe('Date widget', () => { + + let loginPage = new LoginPage(); + let processUserModel; + let taskPage = new TasksPage(); + let widget = new Widget(); + let alfrescoJsApi; + let appsActions = new AppsActions(); + let appModel; + let app = resources.Files.WIDGET_CHECK_APP.DATE; + let deployedApp, process; + + beforeAll(async (done) => { + let users = new UsersActions(); + + alfrescoJsApi = new AlfrescoApi({ + provider: 'BPM', + hostBpm: TestConfig.adf.url + }); + + await alfrescoJsApi.login(TestConfig.adf.adminEmail, TestConfig.adf.adminPassword); + + processUserModel = await users.createTenantAndUser(alfrescoJsApi); + + await alfrescoJsApi.login(processUserModel.email, processUserModel.password); + appModel = await appsActions.importPublishDeployApp(alfrescoJsApi, resources.Files.WIDGET_CHECK_APP.file_location); + + let appDefinitions = await alfrescoJsApi.activiti.appsApi.getAppDefinitions(); + deployedApp = appDefinitions.data.find((currentApp) => { + return currentApp.modelId === appModel.id; + }); + process = await appsActions.startProcess(alfrescoJsApi, appModel, app.processName); + loginPage.loginToProcessServicesUsingUserModel(processUserModel); + done(); + }); + + beforeEach(() => { + let urlToNavigateTo = `${TestConfig.adf.url}/activiti/apps/${deployedApp.id}/tasks/`; + browser.get(urlToNavigateTo); + taskPage.filtersPage().goToFilter(CONSTANTS.TASK_FILTERS.MY_TASKS); + taskPage.formFields().checkFormIsDisplayed(); + }); + + afterAll(async (done) => { + await alfrescoJsApi.activiti.processApi.deleteProcessInstance(process.id); + await alfrescoJsApi.login(TestConfig.adf.adminEmail, TestConfig.adf.adminPassword); + await alfrescoJsApi.activiti.adminTenantsApi.deleteTenant(processUserModel.tenantId); + done(); + }); + + it('[C268814] Date Widget - General Properties', () => { + expect(widget.dateWidget().getDateLabel(app.FIELD.date_input)).toContain('Date'); + expect(taskPage.formFields().isCompleteFormButtonDisabled()).toBeTruthy(); + widget.dateWidget().setDateInput(app.FIELD.date_input, '20-10-2018'); + widget.dateWidget().clickOutsideWidget(app.FIELD.date_input); + expect(taskPage.formFields().isCompleteFormButtonDisabled()).toBeFalsy(); + }); + + it('[C277234] Date widget - Advanced properties', () => { + widget.dateWidget().setDateInput(app.FIELD.date_between_input, '20-10-2017'); + widget.dateWidget().clickOutsideWidget(app.FIELD.date_between_input); + expect(widget.dateWidget().getErrorMessage(app.FIELD.date_between_input)).toBe('Can\'t be less than 1-10-2018'); + widget.dateWidget().clearDateInput(app.FIELD.date_between_input); + widget.dateWidget().setDateInput(app.FIELD.date_between_input, '20-10-2019'); + widget.dateWidget().clickOutsideWidget(app.FIELD.date_between_input); + expect(widget.dateWidget().getErrorMessage(app.FIELD.date_between_input)).toBe('Can\'t be greater than 31-10-2018'); + }); +}); diff --git a/e2e/process-services/widgets/document_template_widget.e2e.ts b/e2e/process-services/widgets/document_template_widget.e2e.ts new file mode 100644 index 0000000000..50361a77e7 --- /dev/null +++ b/e2e/process-services/widgets/document_template_widget.e2e.ts @@ -0,0 +1,83 @@ +/*! + * @license + * Copyright 2016 Alfresco Software, Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import AlfrescoApi = require('alfresco-js-api-node'); +import { AppsActions } from '../../actions/APS/apps.actions'; +import { UsersActions } from '../../actions/users.actions'; +import { browser } from 'protractor'; +import { LoginPage } from '../../pages/adf/loginPage'; +import { TasksPage } from '../../pages/adf/process_services/tasksPage'; +import { Widget } from '../../pages/adf/process_services/widgets/widget'; +import CONSTANTS = require('../../util/constants'); +import TestConfig = require('../../test.config'); +import resources = require('../../util/resources'); + +describe('Document Template widget', () => { + + let loginPage = new LoginPage(); + let processUserModel; + let taskPage = new TasksPage(); + let widget = new Widget(); + let alfrescoJsApi; + let appsActions = new AppsActions(); + let appModel; + let app = resources.Files.FILE_FORM_ADF; + let deployedApp, process; + + beforeAll(async (done) => { + let users = new UsersActions(); + + alfrescoJsApi = new AlfrescoApi({ + provider: 'BPM', + hostBpm: TestConfig.adf.url + }); + + await alfrescoJsApi.login(TestConfig.adf.adminEmail, TestConfig.adf.adminPassword); + + processUserModel = await users.createTenantAndUser(alfrescoJsApi); + + await alfrescoJsApi.login(processUserModel.email, processUserModel.password); + appModel = await appsActions.importPublishDeployApp(alfrescoJsApi, app.file_location); + + let appDefinitions = await alfrescoJsApi.activiti.appsApi.getAppDefinitions(); + deployedApp = appDefinitions.data.find((currentApp) => { + return currentApp.modelId === appModel.id; + }); + process = await appsActions.startProcess(alfrescoJsApi, appModel, app.processName); + loginPage.loginToProcessServicesUsingUserModel(processUserModel); + done(); + }); + + beforeEach(() => { + let urlToNavigateTo = `${TestConfig.adf.url}/activiti/apps/${deployedApp.id}/tasks/`; + browser.get(urlToNavigateTo); + taskPage.filtersPage().goToFilter(CONSTANTS.TASK_FILTERS.MY_TASKS); + taskPage.formFields().checkFormIsDisplayed(); + }); + + afterAll(async (done) => { + await alfrescoJsApi.activiti.processApi.deleteProcessInstance(process.id); + await alfrescoJsApi.login(TestConfig.adf.adminEmail, TestConfig.adf.adminPassword); + await alfrescoJsApi.activiti.adminTenantsApi.deleteTenant(processUserModel.tenantId); + done(); + }); + + it('[C260406] should check that the template contains assigned file ', () => { + expect(widget.containerWidget().getFieldText(app.form_fields.container_id)) + .toEqual(app.attached_file); + }); +}); diff --git a/e2e/process-services/widgets/dropdown_widget.e2e.ts b/e2e/process-services/widgets/dropdown_widget.e2e.ts new file mode 100644 index 0000000000..43632fa16b --- /dev/null +++ b/e2e/process-services/widgets/dropdown_widget.e2e.ts @@ -0,0 +1,100 @@ +/*! + * @license + * Copyright 2016 Alfresco Software, Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import AlfrescoApi = require('alfresco-js-api-node'); +import { AppsActions } from '../../actions/APS/apps.actions'; +import { UsersActions } from '../../actions/users.actions'; +import { browser } from 'protractor'; +import { LoginPage } from '../../pages/adf/loginPage'; +import { TasksPage } from '../../pages/adf/process_services/tasksPage'; +import { Widget } from '../../pages/adf/process_services/widgets/widget'; +import CONSTANTS = require('../../util/constants'); +import TestConfig = require('../../test.config'); +import resources = require('../../util/resources'); + +describe('Dropdown widget', () => { + + let loginPage = new LoginPage(); + let processUserModel; + let taskPage = new TasksPage(); + let widget = new Widget(); + let alfrescoJsApi; + let appsActions = new AppsActions(); + let appModel; + let app = resources.Files.WIDGET_CHECK_APP.DROPDOWN; + let deployedApp, process; + + beforeAll(async (done) => { + let users = new UsersActions(); + + alfrescoJsApi = new AlfrescoApi({ + provider: 'BPM', + hostBpm: TestConfig.adf.url + }); + + await alfrescoJsApi.login(TestConfig.adf.adminEmail, TestConfig.adf.adminPassword); + + processUserModel = await users.createTenantAndUser(alfrescoJsApi); + + await alfrescoJsApi.login(processUserModel.email, processUserModel.password); + appModel = await appsActions.importPublishDeployApp(alfrescoJsApi, resources.Files.WIDGET_CHECK_APP.file_location); + + let appDefinitions = await alfrescoJsApi.activiti.appsApi.getAppDefinitions(); + deployedApp = appDefinitions.data.find((currentApp) => { + return currentApp.modelId === appModel.id; + }); + process = await appsActions.startProcess(alfrescoJsApi, appModel, app.processName); + loginPage.loginToProcessServicesUsingUserModel(processUserModel); + done(); + }); + + beforeEach(() => { + let urlToNavigateTo = `${TestConfig.adf.url}/activiti/apps/${deployedApp.id}/tasks/`; + browser.get(urlToNavigateTo); + taskPage.filtersPage().goToFilter(CONSTANTS.TASK_FILTERS.MY_TASKS); + taskPage.formFields().checkFormIsDisplayed(); + }); + + afterAll(async (done) => { + await alfrescoJsApi.activiti.processApi.deleteProcessInstance(process.id); + await alfrescoJsApi.login(TestConfig.adf.adminEmail, TestConfig.adf.adminPassword); + await alfrescoJsApi.activiti.adminTenantsApi.deleteTenant(processUserModel.tenantId); + done(); + }); + + it('[C269051] General and Options properties', () => { + expect(taskPage.formFields().isCompleteFormButtonDisabled()).toBeTruthy(); + + widget.dropdown().selectOption('Happy'); + expect(widget.dropdown().getSelectedOptionText(app.FIELD.general_dropdown)).toContain('Happy'); + expect(taskPage.formFields().isCompleteFormButtonDisabled()).toBeFalsy(); + + widget.dropdown().selectOption('Choose one'); + expect(widget.dropdown().getSelectedOptionText(app.FIELD.general_dropdown)).toContain('Choose one'); + expect(taskPage.formFields().isCompleteFormButtonDisabled()).toBeTruthy(); + + widget.dropdown().selectOption('Sad'); + expect(widget.dropdown().getSelectedOptionText(app.FIELD.general_dropdown)).toContain('Sad'); + expect(taskPage.formFields().isCompleteFormButtonDisabled()).toBeFalsy(); + }); + + it('[C269052] Dropdown menu - Visibility', () => { + taskPage.formFields().checkWidgetIsHidden(app.FIELD.dropdown_visible); + widget.checkboxWidget().clickCheckboxInput(app.FIELD.checkbox_id); + taskPage.formFields().checkWidgetIsVisible(app.FIELD.dropdown_visible); + }); +}); diff --git a/e2e/process-services/widgets/dynamic_table_widget.e2e.ts b/e2e/process-services/widgets/dynamic_table_widget.e2e.ts new file mode 100644 index 0000000000..5d94d307f4 --- /dev/null +++ b/e2e/process-services/widgets/dynamic_table_widget.e2e.ts @@ -0,0 +1,167 @@ +/*! + * @license + * Copyright 2016 Alfresco Software, Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import AlfrescoApi = require('alfresco-js-api-node'); +import { AppsActions } from '../../actions/APS/apps.actions'; +import { UsersActions } from '../../actions/users.actions'; +import { browser } from 'protractor'; +import { LoginPage } from '../../pages/adf/loginPage'; +import { TasksPage } from '../../pages/adf/process_services/tasksPage'; +import { Widget } from '../../pages/adf/process_services/widgets/widget'; +import CONSTANTS = require('../../util/constants'); +import TestConfig = require('../../test.config'); +import resources = require('../../util/resources'); + +describe('Dynamic Table widget ', () => { + + let loginPage = new LoginPage(); + let processUserModel; + let taskPage = new TasksPage(); + let widget = new Widget(); + let alfrescoJsApi; + let appsActions = new AppsActions(); + let appModel; + let deployedApp, process; + + describe('with date widget', () => { + let app = resources.Files.WIDGET_CHECK_APP.DYNAMIC_TABLE; + + beforeAll(async (done) => { + let users = new UsersActions(); + + alfrescoJsApi = new AlfrescoApi({ + provider: 'BPM', + hostBpm: TestConfig.adf.url + }); + + await alfrescoJsApi.login(TestConfig.adf.adminEmail, TestConfig.adf.adminPassword); + + processUserModel = await users.createTenantAndUser(alfrescoJsApi); + + await alfrescoJsApi.login(processUserModel.email, processUserModel.password); + appModel = await appsActions.importPublishDeployApp(alfrescoJsApi, resources.Files.WIDGET_CHECK_APP.file_location); + + let appDefinitions = await alfrescoJsApi.activiti.appsApi.getAppDefinitions(); + deployedApp = appDefinitions.data.find((currentApp) => { + return currentApp.modelId === appModel.id; + }); + process = await appsActions.startProcess(alfrescoJsApi, appModel, app.processName); + loginPage.loginToProcessServicesUsingUserModel(processUserModel); + done(); + }); + + beforeEach(() => { + let urlToNavigateTo = `${TestConfig.adf.url}/activiti/apps/${deployedApp.id}/tasks/`; + browser.get(urlToNavigateTo); + taskPage.filtersPage().goToFilter(CONSTANTS.TASK_FILTERS.MY_TASKS); + taskPage.formFields().checkFormIsDisplayed(); + }); + + afterAll(async (done) => { + await alfrescoJsApi.activiti.processApi.deleteProcessInstance(process.id); + await alfrescoJsApi.login(TestConfig.adf.adminEmail, TestConfig.adf.adminPassword); + await alfrescoJsApi.activiti.adminTenantsApi.deleteTenant(processUserModel.tenantId); + done(); + }); + + it('[C276729] Dynamic table widget - Visiblity', () => { + taskPage.formFields().checkWidgetIsHidden(app.FIELD.dynamic_table_age_id); + widget.checkboxWidget().clickCheckboxInput(app.FIELD.checkbox_id); + taskPage.formFields().checkWidgetIsVisible(app.FIELD.dynamic_table_age_id); + }); + + it('[C279349] Dynamic table with Datetime', () => { + widget.dynamicTable().clickAddButton(); + widget.dateTimeWidget().openDatepicker(app.FIELD.dataTime_input_id); + widget.dateTimeWidget().selectDay('10'); + widget.dateTimeWidget().selectHour('8'); + widget.dateTimeWidget().selectMinute('30'); + widget.dateTimeWidget().clearDateTimeInput(app.FIELD.dataTime_input_id); + + widget.dynamicTable().clickSaveButton(); + widget.dynamicTable().getTableRow(0); + }); + }); + + describe('with date widget', () => { + + let app = resources.Files.WIDGET_CHECK_APP.DYNAMIC_TABLE_USERS; + + beforeAll(async (done) => { + let users = new UsersActions(); + + alfrescoJsApi = new AlfrescoApi({ + provider: 'BPM', + hostBpm: TestConfig.adf.url + }); + + await alfrescoJsApi.login(TestConfig.adf.adminEmail, TestConfig.adf.adminPassword); + + processUserModel = await users.createTenantAndUser(alfrescoJsApi); + + await alfrescoJsApi.login(processUserModel.email, processUserModel.password); + appModel = await appsActions.importPublishDeployApp(alfrescoJsApi, resources.Files.WIDGET_CHECK_APP.file_location); + + let appDefinitions = await alfrescoJsApi.activiti.appsApi.getAppDefinitions(); + deployedApp = appDefinitions.data.find((currentApp) => { + return currentApp.modelId === appModel.id; + }); + process = await appsActions.startProcess(alfrescoJsApi, appModel, app.processName); + loginPage.loginToProcessServicesUsingUserModel(processUserModel); + done(); + }); + + beforeEach(() => { + let urlToNavigateTo = `${TestConfig.adf.url}/activiti/apps/${deployedApp.id}/tasks/`; + browser.get(urlToNavigateTo); + taskPage.filtersPage().goToFilter(CONSTANTS.TASK_FILTERS.MY_TASKS); + taskPage.formFields().checkFormIsDisplayed(); + }); + + afterAll(async (done) => { + await alfrescoJsApi.activiti.processApi.deleteProcessInstance(process.id); + await alfrescoJsApi.login(TestConfig.adf.adminEmail, TestConfig.adf.adminPassword); + await alfrescoJsApi.activiti.adminTenantsApi.deleteTenant(processUserModel.tenantId); + done(); + }); + + it('[C260407] should check Dynamic Table widget', () => { + + widget.dynamicTable().clickAddRow(); + widget.dynamicTable().setDatatableInput('User1'); + widget.dynamicTable().clickSaveButton(); + expect(widget.dynamicTable().getTableRowText(0)).toEqual('User1'); + + widget.dynamicTable().clickTableRow(0); + widget.dynamicTable().clickEditButton(); + widget.dynamicTable().setDatatableInput('User2'); + widget.dynamicTable().clickCancelButton(); + expect(widget.dynamicTable().getTableRowText(0)).toEqual('User1'); + + widget.dynamicTable().clickEditButton(); + widget.dynamicTable().setDatatableInput('User2'); + widget.dynamicTable().clickSaveButton(); + expect(widget.dynamicTable().getTableRowText(0)).toEqual('User2'); + + widget.dynamicTable().clickAddRow(); + widget.dynamicTable().setDatatableInput('User3'); + widget.dynamicTable().clickCancelButton(); + widget.dynamicTable().checkTableRowIsNotVisible(1); + }); + }); + +}); diff --git a/e2e/process-services/widgets/header_widget.e2e.ts b/e2e/process-services/widgets/header_widget.e2e.ts new file mode 100644 index 0000000000..6b5eab7606 --- /dev/null +++ b/e2e/process-services/widgets/header_widget.e2e.ts @@ -0,0 +1,87 @@ +/*! + * @license + * Copyright 2016 Alfresco Software, Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import AlfrescoApi = require('alfresco-js-api-node'); +import { AppsActions } from '../../actions/APS/apps.actions'; +import { UsersActions } from '../../actions/users.actions'; +import { browser } from 'protractor'; +import { LoginPage } from '../../pages/adf/loginPage'; +import { TasksPage } from '../../pages/adf/process_services/tasksPage'; +import { Widget } from '../../pages/adf/process_services/widgets/widget'; +import CONSTANTS = require('../../util/constants'); +import TestConfig = require('../../test.config'); +import resources = require('../../util/resources'); + +describe('Header widget', () => { + + let loginPage = new LoginPage(); + let processUserModel; + let taskPage = new TasksPage(); + let widget = new Widget(); + let alfrescoJsApi; + let appsActions = new AppsActions(); + let appModel; + let app = resources.Files.WIDGET_CHECK_APP.HEADER; + let deployedApp, process; + + beforeAll(async (done) => { + let users = new UsersActions(); + + alfrescoJsApi = new AlfrescoApi({ + provider: 'BPM', + hostBpm: TestConfig.adf.url + }); + + await alfrescoJsApi.login(TestConfig.adf.adminEmail, TestConfig.adf.adminPassword); + + processUserModel = await users.createTenantAndUser(alfrescoJsApi); + + await alfrescoJsApi.login(processUserModel.email, processUserModel.password); + appModel = await appsActions.importPublishDeployApp(alfrescoJsApi, resources.Files.WIDGET_CHECK_APP.file_location); + + let appDefinitions = await alfrescoJsApi.activiti.appsApi.getAppDefinitions(); + deployedApp = appDefinitions.data.find((currentApp) => { + return currentApp.modelId === appModel.id; + }); + process = await appsActions.startProcess(alfrescoJsApi, appModel, app.processName); + loginPage.loginToProcessServicesUsingUserModel(processUserModel); + done(); + }); + + beforeEach(() => { + let urlToNavigateTo = `${TestConfig.adf.url}/activiti/apps/${deployedApp.id}/tasks/`; + browser.get(urlToNavigateTo); + taskPage.filtersPage().goToFilter(CONSTANTS.TASK_FILTERS.MY_TASKS); + taskPage.formFields().checkFormIsDisplayed(); + }); + + afterAll(async (done) => { + await alfrescoJsApi.activiti.processApi.deleteProcessInstance(process.id); + await alfrescoJsApi.login(TestConfig.adf.adminEmail, TestConfig.adf.adminPassword); + await alfrescoJsApi.activiti.adminTenantsApi.deleteTenant(processUserModel.tenantId); + done(); + }); + + it('[C276737] Header widget - general and visibility properties', () => { + taskPage.formFields().checkWidgetIsHidden(app.FIELD.header_id); + widget.checkboxWidget().clickCheckboxInput(app.FIELD.checkbox_id); + taskPage.formFields().checkWidgetIsVisible(app.FIELD.header_id); + + expect(widget.headerWidget().getFieldLabel(app.FIELD.header_id)).toBe('Header'); + expect(taskPage.formFields().isCompleteFormButtonDisabled()).toBeFalsy(); + }); +}); diff --git a/e2e/process-services/widgets/hyperlink_widget.e2e.ts b/e2e/process-services/widgets/hyperlink_widget.e2e.ts new file mode 100644 index 0000000000..82cccb7b5c --- /dev/null +++ b/e2e/process-services/widgets/hyperlink_widget.e2e.ts @@ -0,0 +1,87 @@ +/*! + * @license + * Copyright 2016 Alfresco Software, Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import AlfrescoApi = require('alfresco-js-api-node'); +import { AppsActions } from '../../actions/APS/apps.actions'; +import { UsersActions } from '../../actions/users.actions'; +import { browser } from 'protractor'; +import { LoginPage } from '../../pages/adf/loginPage'; +import { TasksPage } from '../../pages/adf/process_services/tasksPage'; +import { Widget } from '../../pages/adf/process_services/widgets/widget'; +import CONSTANTS = require('../../util/constants'); +import TestConfig = require('../../test.config'); +import resources = require('../../util/resources'); + +describe('Hyperlink widget', () => { + + let loginPage = new LoginPage(); + let processUserModel; + let taskPage = new TasksPage(); + let widget = new Widget(); + let alfrescoJsApi; + let appsActions = new AppsActions(); + let appModel; + let app = resources.Files.WIDGET_CHECK_APP.HYPERLINK; + let deployedApp, process; + + beforeAll(async (done) => { + let users = new UsersActions(); + + alfrescoJsApi = new AlfrescoApi({ + provider: 'BPM', + hostBpm: TestConfig.adf.url + }); + + await alfrescoJsApi.login(TestConfig.adf.adminEmail, TestConfig.adf.adminPassword); + + processUserModel = await users.createTenantAndUser(alfrescoJsApi); + + await alfrescoJsApi.login(processUserModel.email, processUserModel.password); + appModel = await appsActions.importPublishDeployApp(alfrescoJsApi, resources.Files.WIDGET_CHECK_APP.file_location); + + let appDefinitions = await alfrescoJsApi.activiti.appsApi.getAppDefinitions(); + deployedApp = appDefinitions.data.find((currentApp) => { + return currentApp.modelId === appModel.id; + }); + process = await appsActions.startProcess(alfrescoJsApi, appModel, app.processName); + loginPage.loginToProcessServicesUsingUserModel(processUserModel); + done(); + }); + + beforeEach(() => { + let urlToNavigateTo = `${TestConfig.adf.url}/activiti/apps/${deployedApp.id}/tasks/`; + browser.get(urlToNavigateTo); + taskPage.filtersPage().goToFilter(CONSTANTS.TASK_FILTERS.MY_TASKS); + taskPage.formFields().checkFormIsDisplayed(); + }); + + afterAll(async (done) => { + await alfrescoJsApi.activiti.processApi.deleteProcessInstance(process.id); + await alfrescoJsApi.login(TestConfig.adf.adminEmail, TestConfig.adf.adminPassword); + await alfrescoJsApi.activiti.adminTenantsApi.deleteTenant(processUserModel.tenantId); + done(); + }); + + it('[C276728] Hyperlink widget - Visibility', () => { + taskPage.formFields().checkWidgetIsHidden(app.FIELD.hyperlink_id); + widget.checkboxWidget().clickCheckboxInput(app.FIELD.checkbox_id); + taskPage.formFields().checkWidgetIsVisible(app.FIELD.hyperlink_id); + + expect(widget.hyperlink().getFieldLabel(app.FIELD.hyperlink_id)).toBe('Hyperlink'); + expect(widget.hyperlink().getFieldText(app.FIELD.hyperlink_id)).toBe('https://www.google.com/'); + }); +}); diff --git a/e2e/process-services/widgets/multi_line_widget.e2e.ts b/e2e/process-services/widgets/multi_line_widget.e2e.ts new file mode 100644 index 0000000000..6654973127 --- /dev/null +++ b/e2e/process-services/widgets/multi_line_widget.e2e.ts @@ -0,0 +1,112 @@ +/*! + * @license + * Copyright 2016 Alfresco Software, Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import AlfrescoApi = require('alfresco-js-api-node'); +import { AppsActions } from '../../actions/APS/apps.actions'; +import { UsersActions } from '../../actions/users.actions'; +import { browser } from 'protractor'; +import { LoginPage } from '../../pages/adf/loginPage'; +import { TasksPage } from '../../pages/adf/process_services/tasksPage'; +import { Widget } from '../../pages/adf/process_services/widgets/widget'; +import CONSTANTS = require('../../util/constants'); +import TestConfig = require('../../test.config'); +import resources = require('../../util/resources'); + +describe('Multi-line Widget', () => { + + let loginPage = new LoginPage(); + let processUserModel; + let taskPage = new TasksPage(); + let widget = new Widget(); + let alfrescoJsApi; + let appsActions = new AppsActions(); + let appModel; + let app = resources.Files.WIDGET_CHECK_APP.MULTILINE_TEXT; + let deployedApp, process; + + beforeAll(async (done) => { + let users = new UsersActions(); + + alfrescoJsApi = new AlfrescoApi({ + provider: 'BPM', + hostBpm: TestConfig.adf.url + }); + + await alfrescoJsApi.login(TestConfig.adf.adminEmail, TestConfig.adf.adminPassword); + + processUserModel = await users.createTenantAndUser(alfrescoJsApi); + + await alfrescoJsApi.login(processUserModel.email, processUserModel.password); + appModel = await appsActions.importPublishDeployApp(alfrescoJsApi, resources.Files.WIDGET_CHECK_APP.file_location); + + let appDefinitions = await alfrescoJsApi.activiti.appsApi.getAppDefinitions(); + deployedApp = appDefinitions.data.find((currentApp) => { + return currentApp.modelId === appModel.id; + }); + process = await appsActions.startProcess(alfrescoJsApi, appModel, app.processName); + loginPage.loginToProcessServicesUsingUserModel(processUserModel); + done(); + }); + + beforeEach(() => { + let urlToNavigateTo = `${TestConfig.adf.url}/activiti/apps/${deployedApp.id}/tasks/`; + browser.get(urlToNavigateTo); + taskPage.filtersPage().goToFilter(CONSTANTS.TASK_FILTERS.MY_TASKS); + taskPage.formFields().checkFormIsDisplayed(); + }); + + afterAll(async (done) => { + await alfrescoJsApi.activiti.processApi.deleteProcessInstance(process.id); + await alfrescoJsApi.login(TestConfig.adf.adminEmail, TestConfig.adf.adminPassword); + await alfrescoJsApi.activiti.adminTenantsApi.deleteTenant(processUserModel.tenantId); + done(); + }); + + it('[C268182] Multi-line Text Widget - General Properties', async () => { + let label = widget.multilineTextWidget().getFieldLabel(app.FIELD.multiSimple); + expect(label).toBe('multiSimple*'); + expect(taskPage.formFields().isCompleteFormButtonDisabled()).toBeTruthy(); + let placeHolder = widget.multilineTextWidget().getFieldPlaceHolder(app.FIELD.multiSimple); + expect(placeHolder).toBe('Type something...'); + widget.multilineTextWidget().setValue(app.FIELD.multiSimple, 'TEST'); + expect(taskPage.formFields().isCompleteFormButtonDisabled()).toBeFalsy(); + }); + + it('[C268184] Multi-line Text Widget - Advanced Properties - Min and Max', async () => { + widget.multilineTextWidget().setValue(app.FIELD.multiMinMax, 'A'); + expect(widget.multilineTextWidget().getErrorMessage(app.FIELD.multiMinMax)).toBe('Enter at least 4 characters'); + expect(taskPage.formFields().isCompleteFormButtonDisabled()).toBeTruthy(); + widget.multilineTextWidget().setValue(app.FIELD.multiMinMax, 'AAAAAAAAAAA'); + expect(widget.multilineTextWidget().getErrorMessage(app.FIELD.multiMinMax)).toBe('Enter no more than 10 characters'); + expect(taskPage.formFields().isCompleteFormButtonDisabled()).toBeTruthy(); + }); + + it('[C268184] Multi-line Text Widget - Advanced Properties - Regex Pattern property', async () => { + widget.multilineTextWidget().setValue(app.FIELD.multiSimple, 'TEST'); + widget.multilineTextWidget().setValue(app.FIELD.multiRegexp, '3'); + expect(taskPage.formFields().isCompleteFormButtonDisabled()).toBeTruthy(); + expect(widget.multilineTextWidget().getErrorMessage(app.FIELD.multiRegexp)).toBe('Enter a different value'); + widget.multilineTextWidget().setValue(app.FIELD.multiRegexp, 'TE'); + expect(taskPage.formFields().isCompleteFormButtonDisabled()).toBeFalsy(); + }); + + it('[C268232] Multi-line Text Widget - Visibility properties', async () => { + widget.textWidget().isWidgetNotVisible(app.FIELD.multiVisible); + widget.textWidget().setValue(app.FIELD.showMultiHidden, '1'); + widget.textWidget().isWidgetVisible(app.FIELD.multiVisible); + }); +}); diff --git a/e2e/process-services/widgets/number_widget.e2e.ts b/e2e/process-services/widgets/number_widget.e2e.ts new file mode 100644 index 0000000000..2f933da9ba --- /dev/null +++ b/e2e/process-services/widgets/number_widget.e2e.ts @@ -0,0 +1,109 @@ +/*! + * @license + * Copyright 2016 Alfresco Software, Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import AlfrescoApi = require('alfresco-js-api-node'); +import { AppsActions } from '../../actions/APS/apps.actions'; +import { UsersActions } from '../../actions/users.actions'; +import { browser } from 'protractor'; +import { LoginPage } from '../../pages/adf/loginPage'; +import { TasksPage } from '../../pages/adf/process_services/tasksPage'; +import { Widget } from '../../pages/adf/process_services/widgets/widget'; + +import CONSTANTS = require('../../util/constants'); +import TestConfig = require('../../test.config'); +import resources = require('../../util/resources'); + +describe('Number widget', () => { + + let loginPage = new LoginPage(); + let processUserModel; + let taskPage = new TasksPage(); + let widget = new Widget(); + let alfrescoJsApi; + let appsActions = new AppsActions(); + let appModel; + let app = resources.Files.WIDGET_CHECK_APP.NUMBER; + let deployedApp, process; + + beforeAll(async (done) => { + let users = new UsersActions(); + + alfrescoJsApi = new AlfrescoApi({ + provider: 'BPM', + hostBpm: TestConfig.adf.url + }); + + await alfrescoJsApi.login(TestConfig.adf.adminEmail, TestConfig.adf.adminPassword); + + processUserModel = await users.createTenantAndUser(alfrescoJsApi); + + await alfrescoJsApi.login(processUserModel.email, processUserModel.password); + appModel = await appsActions.importPublishDeployApp(alfrescoJsApi, resources.Files.WIDGET_CHECK_APP.file_location); + + let appDefinitions = await alfrescoJsApi.activiti.appsApi.getAppDefinitions(); + deployedApp = appDefinitions.data.find((currentApp) => { + return currentApp.modelId === appModel.id; + }); + process = await appsActions.startProcess(alfrescoJsApi, appModel, app.processName); + loginPage.loginToProcessServicesUsingUserModel(processUserModel); + done(); + }); + + beforeEach(() => { + let urlToNavigateTo = `${TestConfig.adf.url}/activiti/apps/${deployedApp.id}/tasks/`; + browser.get(urlToNavigateTo); + taskPage.filtersPage().goToFilter(CONSTANTS.TASK_FILTERS.MY_TASKS); + taskPage.formFields().checkFormIsDisplayed(); + }); + + afterAll(async (done) => { + await alfrescoJsApi.activiti.processApi.deleteProcessInstance(process.id); + await alfrescoJsApi.login(TestConfig.adf.adminEmail, TestConfig.adf.adminPassword); + await alfrescoJsApi.activiti.adminTenantsApi.deleteTenant(processUserModel.tenantId); + done(); + }); + + it('[C269111] Number Widget - General Properties', () => { + expect(taskPage.formFields().isCompleteFormButtonDisabled()).toBeTruthy(); + expect(widget.numberWidget().getNumberFieldLabel(app.FIELD.number_general)).toContain('Number General'); + expect(widget.numberWidget().getPlaceholder(app.FIELD.number_general)).toContain('Type a number'); + + widget.numberWidget().setFieldValue(app.FIELD.number_general, 2); + expect(taskPage.formFields().isCompleteFormButtonDisabled()).toBeFalsy(); + }); + + it('[C274702] Number Widget - Advanced and visibility properties', () => { + widget.numberWidget().setFieldValue(app.FIELD.number_general, 2); + + taskPage.formFields().checkWidgetIsHidden(app.FIELD.number_visible); + widget.checkboxWidget().clickCheckboxInput(app.FIELD.checkbox_id); + taskPage.formFields().checkWidgetIsVisible(app.FIELD.number_visible); + + widget.numberWidget().setFieldValue(app.FIELD.number_visible, 2); + expect(widget.numberWidget().getErrorMessage(app.FIELD.number_visible)).toBe('Can\'t be less than 3'); + expect(taskPage.formFields().isCompleteFormButtonDisabled()).toBeTruthy(); + widget.numberWidget().clearFieldValue(app.FIELD.number_visible); + + widget.numberWidget().setFieldValue(app.FIELD.number_visible, 101); + expect(widget.numberWidget().getErrorMessage(app.FIELD.number_visible)).toBe('Can\'t be greater than 100'); + expect(taskPage.formFields().isCompleteFormButtonDisabled()).toBeTruthy(); + widget.numberWidget().clearFieldValue(app.FIELD.number_visible); + + widget.numberWidget().setFieldValue(app.FIELD.number_visible, 4); + expect(taskPage.formFields().isCompleteFormButtonDisabled()).toBeFalsy(); + }); +}); diff --git a/e2e/process-services/widgets/people_widget.e2e.ts b/e2e/process-services/widgets/people_widget.e2e.ts new file mode 100644 index 0000000000..1b50acffcc --- /dev/null +++ b/e2e/process-services/widgets/people_widget.e2e.ts @@ -0,0 +1,102 @@ +/*! + * @license + * Copyright 2016 Alfresco Software, Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import AlfrescoApi = require('alfresco-js-api-node'); +import { AppsActions } from '../../actions/APS/apps.actions'; +import { UsersActions } from '../../actions/users.actions'; +import { browser } from 'protractor'; +import { LoginPage } from '../../pages/adf/loginPage'; +import { TasksPage } from '../../pages/adf/process_services/tasksPage'; +import { Widget } from '../../pages/adf/process_services/widgets/widget'; +import CONSTANTS = require('../../util/constants'); +import TestConfig = require('../../test.config'); +import resources = require('../../util/resources'); + +describe('People widget', () => { + + let loginPage = new LoginPage(); + let processUserModel; + let taskPage = new TasksPage(); + let widget = new Widget(); + let alfrescoJsApi; + let appsActions = new AppsActions(); + let appModel; + let app = resources.Files.WIDGET_CHECK_APP.ADD_PEOPLE; + let deployedApp, process; + + beforeAll(async (done) => { + let users = new UsersActions(); + + alfrescoJsApi = new AlfrescoApi({ + provider: 'BPM', + hostBpm: TestConfig.adf.url + }); + + await alfrescoJsApi.login(TestConfig.adf.adminEmail, TestConfig.adf.adminPassword); + + processUserModel = await users.createTenantAndUser(alfrescoJsApi); + + await alfrescoJsApi.login(processUserModel.email, processUserModel.password); + appModel = await appsActions.importPublishDeployApp(alfrescoJsApi, resources.Files.WIDGET_CHECK_APP.file_location); + + let appDefinitions = await alfrescoJsApi.activiti.appsApi.getAppDefinitions(); + deployedApp = appDefinitions.data.find((currentApp) => { + return currentApp.modelId === appModel.id; + }); + process = await appsActions.startProcess(alfrescoJsApi, appModel, app.processName); + loginPage.loginToProcessServicesUsingUserModel(processUserModel); + done(); + }); + + beforeEach(() => { + let urlToNavigateTo = `${TestConfig.adf.url}/activiti/apps/${deployedApp.id}/tasks/`; + browser.get(urlToNavigateTo); + taskPage.filtersPage().goToFilter(CONSTANTS.TASK_FILTERS.MY_TASKS); + taskPage.formFields().checkFormIsDisplayed(); + }); + + afterAll(async (done) => { + await alfrescoJsApi.activiti.processApi.deleteProcessInstance(process.id); + await alfrescoJsApi.login(TestConfig.adf.adminEmail, TestConfig.adf.adminPassword); + await alfrescoJsApi.activiti.adminTenantsApi.deleteTenant(processUserModel.tenantId); + done(); + }); + + it('[C212870] should check People widget', () => { + taskPage.formFields().checkWidgetIsHidden(app.FIELD.widget_id); + widget.checkboxWidget().clickCheckboxInput(app.FIELD.checkbox_id); + taskPage.formFields().checkWidgetIsVisible(app.FIELD.widget_id); + + let admin = processUserModel.firstName + ' ' + processUserModel.lastName; + widget.peopleWidget().insertUser(app.FIELD.widget_id, admin.charAt(0)); + widget.peopleWidget().checkDropDownListIsDisplayed(); + widget.peopleWidget().checkUserIsListed(admin); + widget.peopleWidget().selectUserFromDropDown(admin); + }); + + it('[C274707] Add people widget - Visibility', () => { + taskPage.formFields().checkWidgetIsHidden(app.FIELD.widget_id); + widget.checkboxWidget().clickCheckboxInput(app.FIELD.checkbox_id); + taskPage.formFields().checkWidgetIsVisible(app.FIELD.widget_id); + + let admin = processUserModel.firstName + ' ' + processUserModel.lastName; + widget.peopleWidget().insertUser(app.FIELD.widget_id, admin.charAt(0)); + widget.peopleWidget().checkDropDownListIsDisplayed(); + widget.peopleWidget().checkUserIsListed(admin); + widget.peopleWidget().selectUserFromDropDown(admin); + }); +}); diff --git a/e2e/process-services/widgets/radio_buttons_widget.e2e.ts b/e2e/process-services/widgets/radio_buttons_widget.e2e.ts new file mode 100644 index 0000000000..51f372b0ab --- /dev/null +++ b/e2e/process-services/widgets/radio_buttons_widget.e2e.ts @@ -0,0 +1,95 @@ +/*! + * @license + * Copyright 2016 Alfresco Software, Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import AlfrescoApi = require('alfresco-js-api-node'); +import { AppsActions } from '../../actions/APS/apps.actions'; +import { UsersActions } from '../../actions/users.actions'; +import { browser } from 'protractor'; +import { LoginPage } from '../../pages/adf/loginPage'; +import { TasksPage } from '../../pages/adf/process_services/tasksPage'; +import { Widget } from '../../pages/adf/process_services/widgets/widget'; +import CONSTANTS = require('../../util/constants'); +import TestConfig = require('../../test.config'); +import resources = require('../../util/resources'); + +describe('Radio Buttons Widget', () => { + + let loginPage = new LoginPage(); + let processUserModel; + let taskPage = new TasksPage(); + let widget = new Widget(); + let alfrescoJsApi; + let appsActions = new AppsActions(); + let appModel; + let app = resources.Files.WIDGET_CHECK_APP.RADIO_BUTTONS; + let deployedApp, process; + + beforeAll(async (done) => { + let users = new UsersActions(); + + alfrescoJsApi = new AlfrescoApi({ + provider: 'BPM', + hostBpm: TestConfig.adf.url + }); + + await alfrescoJsApi.login(TestConfig.adf.adminEmail, TestConfig.adf.adminPassword); + + processUserModel = await users.createTenantAndUser(alfrescoJsApi); + + await alfrescoJsApi.login(processUserModel.email, processUserModel.password); + appModel = await appsActions.importPublishDeployApp(alfrescoJsApi, resources.Files.WIDGET_CHECK_APP.file_location); + + let appDefinitions = await alfrescoJsApi.activiti.appsApi.getAppDefinitions(); + deployedApp = appDefinitions.data.find((currentApp) => { + return currentApp.modelId === appModel.id; + }); + + process = await appsActions.startProcess(alfrescoJsApi, appModel, app.processName); + loginPage.loginToProcessServicesUsingUserModel(processUserModel); + + done(); + }); + + beforeEach(() => { + let urlToNavigateTo = `${TestConfig.adf.url}/activiti/apps/${deployedApp.id}/tasks/`; + browser.get(urlToNavigateTo); + taskPage.filtersPage().goToFilter(CONSTANTS.TASK_FILTERS.MY_TASKS); + taskPage.formFields().checkFormIsDisplayed(); + }); + + afterAll(async (done) => { + await alfrescoJsApi.activiti.processApi.deleteProcessInstance(process.id); + await alfrescoJsApi.login(TestConfig.adf.adminEmail, TestConfig.adf.adminPassword); + await alfrescoJsApi.activiti.adminTenantsApi.deleteTenant(processUserModel.tenantId); + done(); + }); + + it('[C277316] Radio buttons widget - default behaviour', () => { + widget.checkboxWidget().clickCheckboxInput(app.FIELD.checkbox_id); + widget.radioWidget().isSelectionClean(app.FIELD.radio_buttons_id); + }); + + it('[C274704] Radio buttons widget - Visibility', () => { + taskPage.formFields().checkWidgetIsHidden(app.FIELD.radio_buttons_id); + expect(taskPage.formFields().isCompleteFormButtonDisabled()).toBeTruthy(); + + widget.checkboxWidget().clickCheckboxInput(app.FIELD.checkbox_id); + expect(widget.radioWidget().getRadioWidgetLabel(app.FIELD.radio_buttons_id)).toContain('Radio posts'); + widget.radioWidget().selectOption(app.FIELD.radio_buttons_id, 1); + expect(taskPage.formFields().isCompleteFormButtonDisabled()).toBeFalsy(); + }); +}); diff --git a/e2e/process-services/widgets/text_widget.e2e.ts b/e2e/process-services/widgets/text_widget.e2e.ts new file mode 100644 index 0000000000..02181e44ed --- /dev/null +++ b/e2e/process-services/widgets/text_widget.e2e.ts @@ -0,0 +1,122 @@ +/*! + * @license + * Copyright 2016 Alfresco Software, Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import AlfrescoApi = require('alfresco-js-api-node'); +import { AppsActions } from '../../actions/APS/apps.actions'; +import { UsersActions } from '../../actions/users.actions'; +import { browser } from 'protractor'; +import { LoginPage } from '../../pages/adf/loginPage'; +import { TasksPage } from '../../pages/adf/process_services/tasksPage'; +import { Widget } from '../../pages/adf/process_services/widgets/widget'; +import CONSTANTS = require('../../util/constants'); +import TestConfig = require('../../test.config'); +import resources = require('../../util/resources'); + +describe('Text widget', () => { + + let loginPage = new LoginPage(); + let processUserModel; + let taskPage = new TasksPage(); + let widget = new Widget(); + let alfrescoJsApi; + let appsActions = new AppsActions(); + let appModel; + let app = resources.Files.WIDGET_CHECK_APP.TEXT; + let deployedApp, process; + + beforeAll(async (done) => { + let users = new UsersActions(); + + alfrescoJsApi = new AlfrescoApi({ + provider: 'BPM', + hostBpm: TestConfig.adf.url + }); + + await alfrescoJsApi.login(TestConfig.adf.adminEmail, TestConfig.adf.adminPassword); + + processUserModel = await users.createTenantAndUser(alfrescoJsApi); + + await alfrescoJsApi.login(processUserModel.email, processUserModel.password); + appModel = await appsActions.importPublishDeployApp(alfrescoJsApi, resources.Files.WIDGET_CHECK_APP.file_location); + + let appDefinitions = await alfrescoJsApi.activiti.appsApi.getAppDefinitions(); + deployedApp = appDefinitions.data.find((currentApp) => { + return currentApp.modelId === appModel.id; + }); + process = await appsActions.startProcess(alfrescoJsApi, appModel, app.processName); + loginPage.loginToProcessServicesUsingUserModel(processUserModel); + done(); + }); + + beforeEach(() => { + let urlToNavigateTo = `${TestConfig.adf.url}/activiti/apps/${deployedApp.id}/tasks/`; + browser.get(urlToNavigateTo); + taskPage.filtersPage().goToFilter(CONSTANTS.TASK_FILTERS.MY_TASKS); + taskPage.formFields().checkFormIsDisplayed(); + }); + + afterAll(async (done) => { + await alfrescoJsApi.activiti.processApi.deleteProcessInstance(process.id); + await alfrescoJsApi.login(TestConfig.adf.adminEmail, TestConfig.adf.adminPassword); + await alfrescoJsApi.activiti.adminTenantsApi.deleteTenant(processUserModel.tenantId); + done(); + }); + + it('[C268157] General Properties', async () => { + let label = widget.textWidget().getFieldLabel(app.FIELD.simpleText); + expect(label).toBe('textSimple*'); + expect(taskPage.formFields().isCompleteFormButtonDisabled()).toBeTruthy(); + let placeHolder = widget.textWidget().getFieldPlaceHolder(app.FIELD.simpleText); + expect(placeHolder).toBe('Type something...'); + widget.textWidget().setValue(app.FIELD.simpleText, 'TEST'); + expect(taskPage.formFields().isCompleteFormButtonDisabled()).toBeFalsy(); + }); + + it('[C268170] Min-max length properties', async () => { + widget.textWidget().setValue(app.FIELD.textMinMax, 'A'); + expect(widget.textWidget().getErrorMessage(app.FIELD.textMinMax)).toBe('Enter at least 4 characters'); + expect(taskPage.formFields().isCompleteFormButtonDisabled()).toBeTruthy(); + widget.textWidget().setValue(app.FIELD.textMinMax, 'AAAAAAAAAAA'); + expect(widget.textWidget().getErrorMessage(app.FIELD.textMinMax)).toBe('Enter no more than 10 characters'); + expect(taskPage.formFields().isCompleteFormButtonDisabled()).toBeTruthy(); + }); + + it('[C268171] Input mask reversed checkbox properties', async () => { + widget.textWidget().setValue(app.FIELD.textMask, '18951523'); + expect(widget.textWidget().getFieldValue(app.FIELD.textMask)).toBe('1895-1523'); + }); + + it('[C268171] Input mask reversed checkbox properties', async () => { + widget.textWidget().setValue(app.FIELD.textMaskReversed, '1234567899'); + expect(widget.textWidget().getFieldValue(app.FIELD.textMaskReversed)).toBe('3456-7899'); + }); + + it('[C268177] Regex Pattern property', async () => { + widget.textWidget().setValue(app.FIELD.simpleText, 'TEST'); + widget.textWidget().setValue(app.FIELD.textRegexp, 'T'); + expect(taskPage.formFields().isCompleteFormButtonDisabled()).toBeTruthy(); + expect(widget.textWidget().getErrorMessage(app.FIELD.textRegexp)).toBe('Enter a different value'); + widget.textWidget().setValue(app.FIELD.textRegexp, 'TE'); + expect(taskPage.formFields().isCompleteFormButtonDisabled()).toBeFalsy(); + }); + + it('[C274712] Visibility condition', async () => { + widget.textWidget().isWidgetNotVisible(app.FIELD.textHidden); + widget.textWidget().setValue(app.FIELD.showHiddenText, '1'); + widget.textWidget().isWidgetVisible(app.FIELD.textHidden); + }); +}); diff --git a/e2e/resources/apps/App_file_form.zip b/e2e/resources/apps/App_file_form.zip new file mode 100644 index 0000000000000000000000000000000000000000..a9198a6d5263696de30c2fbb5f18bc89ba3dd603 GIT binary patch literal 15505 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 CW^+LR literal 0 HcmV?d00001 diff --git a/e2e/resources/apps/Test-ADF.zip b/e2e/resources/apps/Test-ADF.zip new file mode 100644 index 0000000000000000000000000000000000000000..21750c57db86e491de0c1f1c5e21a63002a3f56f GIT binary patch literal 8928 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

j-wq;>nER&1CE3z-Kgs1+@PD^NR= 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 literal 0 HcmV?d00001 diff --git a/e2e/resources/apps/WidgetApps.zip b/e2e/resources/apps/WidgetApps.zip new file mode 100644 index 0000000000000000000000000000000000000000..c187bd26180af3b7734f155dcf58ba9607cb1047 GIT binary patch literal 103307 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%0D??@yyPJwtKvGb`|x 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