From a630cfb3909a61674d1ca94e131cfbe568c49417 Mon Sep 17 00:00:00 2001 From: Eugenio Romano Date: Fri, 4 Sep 2020 13:27:55 +0100 Subject: [PATCH] Fix e2e (#6087) * fix protractor conf * remove update env * fix cli error script cs * change screenshot plugin * remove unused param * fix * fix * fix * moment comment nvm * fix * fix * fix * fix * remove adf redirect * fix * fix * save remote report * report fix * improve save result * fix folder save * fix folder save * fix placeholder pointer , they needs refactor later * fix * fix lint * fix * remove test already covered by unit fix the test in core needed exclude the one impossible to make it works without APS basic support * lint * fix some logout missing * fix * Fix the custom-tasks-filters.e2e * fix lint * fix * fix * fix * Fix wait on start process and on process definition options * Fix logout before login again * fix uplaod test * fix * Fix infodrawer with check detail and sleep * lint * increase list of excluded test * fix * fix lint * change wait method datatable * fix * fix * revert check value * fix * fix * change tag test Co-authored-by: maurizio vitale --- .gitignore | 4 +- .travis.yml | 20 +- .../restore-content-directive.e2e.ts | 3 +- .../document-list-actions.e2e.ts | 11 +- .../document-list-pagination.e2e.ts | 49 +- e2e/content-services/pages/tag.page.ts | 10 +- e2e/content-services/pages/tree-view.page.ts | 2 +- e2e/content-services/tag-component.e2e.ts | 4 +- .../upload/upload-dialog.e2e.ts | 20 +- e2e/core/infinite-scrolling.e2e.ts | 1 + e2e/core/login/login-component.e2e.ts | 2 + e2e/core/login/redirection.e2e.ts | 170 ------- e2e/core/login/remember-me.e2e.ts | 4 +- e2e/core/pages/content-services.page.ts | 4 +- e2e/core/pages/dialog/upload-dialog.page.ts | 15 +- e2e/core/pages/header.page.ts | 129 ----- e2e/core/pages/infinite-pagination.page.ts | 2 + e2e/core/pages/metadata-view.page.ts | 6 +- e2e/core/pages/trashcan.page.ts | 4 +- e2e/core/settings-component.e2e.ts | 11 +- e2e/core/user-info-component.e2e.ts | 173 ------- .../file-extensions/viewer-component.e2e.ts | 7 +- e2e/core/viewer/info-drawer.component.e2e.ts | 103 ---- .../pages/tasks-cloud-demo.page.ts | 6 +- .../process-list-cloud-action-menu.e2e.ts | 30 +- .../comment-component-tasks.e2e.ts | 1 + .../custom-process-filters-sorting.e2e.ts | 4 +- .../custom-tasks-filters.e2e.ts | 41 +- .../dynamic-table-date-picker.e2e.ts | 1 + .../empty-process-list-component.e2e.ts | 3 + .../form-widgets-component.e2e.ts | 26 +- e2e/process-services/info-drawer.e2e.ts | 14 +- .../pages/attachment-list.page.ts | 3 - .../dialog/create-checklist-dialog.page.ts | 2 +- .../pages/dialog/start-task-dialog.page.ts | 2 +- e2e/process-services/pages/filters.page.ts | 3 +- .../pages/process-filters.page.ts | 14 +- .../pages/process-services.page.ts | 6 +- .../pages/task-details.page.ts | 37 +- .../pages/task-filters.page.ts | 4 +- e2e/process-services/pages/tasks-list.page.ts | 3 +- e2e/process-services/pages/tasks.page.ts | 8 +- e2e/process-services/people-component.e2e.ts | 4 + .../start-process-component.e2e.ts | 22 +- .../start-task-task-app.e2e.ts | 1 + e2e/process-services/task-assignee.e2e.ts | 4 + e2e/process-services/task-details-form.e2e.ts | 2 + .../task-filters-component.e2e.ts | 1 + .../task-list-pagination.e2e.ts | 3 + .../widgets/typeahead-widget.e2e.ts | 4 + e2e/protractor.conf.js | 28 +- e2e/protractor.excludes.json | 32 +- e2e/protractor/save-remote.js | 98 ++-- .../documents/other/a_eps_file.eps | Bin 307243 -> 0 bytes .../documents/other/a_png_file.png | Bin 0 -> 4513 bytes .../components/search-check-list.e2e.ts | 52 +- .../components/search-date-range.e2e.ts | 14 +- .../components/search-number-range.e2e.ts | 28 +- e2e/search/components/search-radio.e2e.ts | 40 +- e2e/search/components/search-slider.e2e.ts | 34 +- .../components/search-sorting-picker.e2e.ts | 32 +- e2e/search/components/search-text.e2e.ts | 8 +- ...arch-dialog.page.ts => search-bar.page.ts} | 14 +- e2e/search/search-component.e2e.ts | 158 +++--- e2e/search/search-filters.e2e.ts | 43 +- e2e/search/search-multiselect.e2e.ts | 34 +- e2e/search/search-page.e2e.ts | 48 +- e2e/test.config.js | 16 +- e2e/util/resources.js | 6 +- lib/cli/scripts/check-cs-env.ts | 10 +- lib/cli/scripts/init-aae-env.ts | 20 +- .../people-search-field.component.html | 2 +- .../components/people/people.component.html | 4 +- lib/testing/ng-package.json | 1 - .../content-node-selector-dialog.page.ts | 4 +- .../pages/document-list.page.ts | 4 +- .../pages/search/search-check-list.page.ts | 10 +- .../pages/search/search-slider.page.ts | 8 +- .../src/lib/core/actions/api.service.ts | 6 + .../src/lib/core/actions/drop.actions.ts | 5 - .../src/lib/core/actions/users.actions.ts | 8 +- .../card-view/card-view-date-item.page.ts | 8 +- .../card-view/card-view-text-item.page.ts | 18 +- .../core/pages/data-table-component.page.ts | 31 +- .../src/lib/core/pages/form/form-fields.ts | 10 +- .../form/widgets/attach-file-widget.page.ts | 10 +- .../form/widgets/attach-folder-widget.page.ts | 6 +- .../form/widgets/checkbox-widget.page.ts | 4 +- .../form/widgets/dynamic-table-widget.page.ts | 6 +- .../pages/form/widgets/group-widget.page.ts | 6 +- .../form/widgets/hyperlink-widget.page.ts | 4 +- .../widgets/multiline-text-widget.page.ts | 2 +- .../pages/form/widgets/people-widget.page.ts | 10 +- .../form/widgets/radio-buttons-widget.page.ts | 4 +- .../form/widgets/typeahead-widget.page.ts | 8 +- .../src/lib/core/pages/info-drawer.page.ts | 4 +- lib/testing/src/lib/core/pages/login.page.ts | 2 +- .../date-time-picker-calendar.page.ts | 6 +- .../lib/core/pages/material/dropdown.page.ts | 2 + .../src/lib/core/pages/pagination.page.ts | 19 +- .../src/lib/core/pages/snackbar.page.ts | 4 +- .../src/lib/core/utils/browser-actions.ts | 35 +- .../src/lib/core/utils/browser-visibility.ts | 11 +- .../dialog/edit-process-filter-dialog.page.ts | 6 +- .../dialog/edit-task-filter-dialog.page.ts | 6 +- .../edit-task-filter-cloud-component.page.ts | 4 +- .../widget/attach-file-widget-cloud.page.ts | 6 +- .../pages/people-cloud-component.page.ts | 6 +- .../process-filters-cloud-component.page.ts | 4 +- .../process-list-cloud-component.page.ts | 4 +- .../pages/task-list-cloud-component.page.ts | 4 +- .../actions/applications.util.ts | 6 - .../process-services/pages/filters.page.ts | 4 +- .../pages/form-fields.page.ts | 8 +- .../pages/start-process.page.ts | 1 + package-lock.json | 454 ++++++++++-------- package.json | 4 +- scripts/README.md | 21 - scripts/build/build-cli.sh | 7 + scripts/test-e2e-lib.sh | 21 +- scripts/travis/deploy/deploy-pr.sh | 31 -- scripts/travis/deploy/deploy.sh | 17 - scripts/travis/deploy/rancher-pr-deploy.js | 262 ---------- scripts/travis/deploy/rancher-update.sh | 61 --- scripts/travis/e2e/content-services-e2e.sh | 2 +- scripts/travis/e2e/core-e2e.sh | 6 +- scripts/travis/e2e/insights-e2e.sh | 4 +- .../travis/e2e/process-services-cloud-e2e.sh | 4 +- scripts/travis/e2e/process-services-e2e.sh | 7 +- scripts/travis/e2e/search-e2e.sh | 2 +- .../get-docker-image-tag-name.sh | 0 scripts/travis/release/release-docker.sh | 17 + 132 files changed, 1109 insertions(+), 1850 deletions(-) delete mode 100644 e2e/core/login/redirection.e2e.ts delete mode 100644 e2e/core/pages/header.page.ts delete mode 100644 e2e/core/user-info-component.e2e.ts delete mode 100644 e2e/core/viewer/info-drawer.component.e2e.ts delete mode 100644 e2e/resources/adf/allFileTypes/documents/other/a_eps_file.eps create mode 100644 e2e/resources/adf/allFileTypes/documents/other/a_png_file.png rename e2e/search/pages/{search-dialog.page.ts => search-bar.page.ts} (89%) delete mode 100755 scripts/travis/deploy/deploy-pr.sh delete mode 100755 scripts/travis/deploy/deploy.sh delete mode 100644 scripts/travis/deploy/rancher-pr-deploy.js delete mode 100755 scripts/travis/deploy/rancher-update.sh rename scripts/travis/{deploy => release}/get-docker-image-tag-name.sh (100%) mode change 100755 => 100644 create mode 100644 scripts/travis/release/release-docker.sh diff --git a/.gitignore b/.gitignore index cf9293e516..5b64e4a2e1 100644 --- a/.gitignore +++ b/.gitignore @@ -8,7 +8,7 @@ workspace.xml dist/ e2e/.env.cloud tmp -e2e-output/ +e2e-output*/ /e2e/downloads/ *.npmrc .history @@ -24,3 +24,5 @@ coverage/ /desktop.ini out-tsc !/.protractor-smartrunner/ +/reports/ +e2e-result-* diff --git a/.travis.yml b/.travis.yml index 331f90a98f..f5e3d5de25 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,6 +11,10 @@ before_install: . ./scripts/ci/job_hooks/before_install.sh install: echo "no install" +env: + global: + - SAVE_SCREENSHOT=true + branches: only: - master @@ -21,10 +25,6 @@ branches: stages: - name: Lint & Build Dist & Release - - name: Update Rancher - if: (type = push AND tag IS blank) OR type = api - - name: Deploy PR - if: type = pull_request && commit_message =~ /\[create preview\]/ - name: Check bundle if: type = push AND tag IS blank - name: Trigger ADF child build @@ -55,6 +55,7 @@ jobs: script: - ./scripts/travis/build/build.sh && ./scripts/lint.sh && ./scripts/travis/release/release-npm.sh after_success: + - ./scripts/travis/release/release-docker.sh - ./scripts/ci/utils/artifact-to-s3.sh -a ./dist/demo-shell -o "$S3_DBP_FOLDER/alfresco-demoshell.tar.bz2" - ./scripts/ci/utils/artifact-to-s3.sh -a ./lib/dist -o "$S3_DBP_FOLDER/alfresco-libs.tar.bz2" @@ -110,17 +111,6 @@ jobs: - ./scripts/ci/utils/artifact-from-s3.sh -a "$S3_DBP_FOLDER/alfresco-libs.tar.bz2" -o "./lib/dist" script: ./scripts/travis/e2e/process-services-cloud-e2e.sh - - - stage: Update Rancher - name: Update Rancher - before_script: ./scripts/ci/utils/artifact-from-s3.sh -a "$S3_DBP_FOLDER/alfresco-demoshell.tar.bz2" -o "./dist/demo-shell" - script: ./scripts/travis/deploy/deploy.sh - - - stage: Deploy PR - name: Deploy PR - script: ./scripts/travis/deploy/deploy-pr.sh - - - stage: Check bundle script: - ADF_VERSION=$(npm view @alfresco/adf-core@${TAG_NPM} version) diff --git a/e2e/content-services/directives/restore-content-directive.e2e.ts b/e2e/content-services/directives/restore-content-directive.e2e.ts index 8ad8fca88a..b5d3d73095 100644 --- a/e2e/content-services/directives/restore-content-directive.e2e.ts +++ b/e2e/content-services/directives/restore-content-directive.e2e.ts @@ -142,6 +142,8 @@ describe('Restore content directive', () => { await trashcanPage.clickRestore(); await trashcanPage.getDocumentList().dataTablePage().checkRowContentIsNotDisplayed(testFile.entry.name); + await notificationHistoryPage.checkNotifyContains(testFile.entry.name + ' item restored'); + await navigationBarPage.clickContentServicesButton(); await contentServicesPage.waitForTableBody(); await contentServicesPage.checkContentIsDisplayed(testFile.entry.name); @@ -150,7 +152,6 @@ describe('Restore content directive', () => { await navigationBarPage.clickTrashcanButton(); await trashcanPage.waitForTableBody(); await trashcanPage.getDocumentList().dataTablePage().checkRowContentIsDisplayed(testFile.entry.name); - await notificationHistoryPage.checkNotifyContains(testFile.entry.name + ' item restored'); }); it('[C260239] Should restore folder with content', async () => { diff --git a/e2e/content-services/document-list/document-list-actions.e2e.ts b/e2e/content-services/document-list/document-list-actions.e2e.ts index 0a040691f7..04936ebb77 100644 --- a/e2e/content-services/document-list/document-list-actions.e2e.ts +++ b/e2e/content-services/document-list/document-list-actions.e2e.ts @@ -223,10 +223,17 @@ describe('Document List Component - Actions', () => { it('[C260060] Should be able to open a file/folder through double click action - folder', async () => { const folderTwoModel = new FolderModel({ name: 'folderTwo' }); const numberOfSubFolders = 3; - await contentServicesPage.createAndOpenNewFolder(folderTwoModel.name); + + await contentServicesPage.createNewFolder(folderTwoModel.name); + const nodeIdSubFolderTwo = await contentServicesPage.getAttributeValueForElement(folderTwoModel.name, 'Node id'); + await contentServicesPage.openFolder(folderTwoModel.name); + for (let i = 0; i < numberOfSubFolders; i++) { - await contentServicesPage.createNewFolder('subFolder' + (i + 1)); + await uploadActions.createFolder('subfolder' + (i + 1), nodeIdSubFolderTwo); } + + await browser.refresh(); + await contentServicesPage.checkContentsAreDisplayed(numberOfSubFolders); }); diff --git a/e2e/content-services/document-list/document-list-pagination.e2e.ts b/e2e/content-services/document-list/document-list-pagination.e2e.ts index 0e3d8bc11f..aed3d8b45e 100644 --- a/e2e/content-services/document-list/document-list-pagination.e2e.ts +++ b/e2e/content-services/document-list/document-list-pagination.e2e.ts @@ -111,7 +111,7 @@ describe('Document List - Pagination', () => { it('[C260062] Should use default pagination settings', async () => { await contentServicesPage.openFolder(newFolderModel.name); await expect(await paginationPage.getCurrentItemsPerPage()).toEqual(itemsPerPage.twenty); - await expect(await paginationPage.getPaginationRange()).toEqual( `Showing 1-${nrOfFiles} of ${nrOfFiles}`); + await expect(await paginationPage.getPaginationRange()).toEqual(`Showing 1-${nrOfFiles} of ${nrOfFiles}`); await expect(await contentServicesPage.numberOfResultsDisplayed()).toBe(nrOfFiles); const list = await contentServicesPage.getAllRowsNameColumn(); await expect(ArrayUtil.arrayContainsArray(list, fileNames)).toEqual(true); @@ -125,7 +125,7 @@ describe('Document List - Pagination', () => { await contentServicesPage.checkDocumentListElementsAreDisplayed(); await expect(await paginationPage.getCurrentItemsPerPage()).toEqual(itemsPerPage.twenty); - await expect(await paginationPage.getPaginationRange()).toEqual( `Showing 1-${nrOfFiles} of ${nrOfFiles}`); + await expect(await paginationPage.getPaginationRange()).toEqual(`Showing 1-${nrOfFiles} of ${nrOfFiles}`); await expect(await contentServicesPage.numberOfResultsDisplayed()).toBe(nrOfFiles); const list = await contentServicesPage.getAllRowsNameColumn(); await expect(ArrayUtil.arrayContainsArray(list, fileNames)).toEqual(true); @@ -155,7 +155,7 @@ describe('Document List - Pagination', () => { currentPage++; await contentServicesPage.checkDocumentListElementsAreDisplayed(); await expect(await paginationPage.getCurrentItemsPerPage()).toEqual(itemsPerPage.five); - await expect(await paginationPage.getPaginationRange()).toEqual( `Showing 6-${itemsPerPage.fiveValue * currentPage} of ${nrOfFiles}`); + await expect(await paginationPage.getPaginationRange()).toEqual(`Showing 6-${itemsPerPage.fiveValue * currentPage} of ${nrOfFiles}`); await expect(await contentServicesPage.numberOfResultsDisplayed()).toBe(itemsPerPage.fiveValue); list = await contentServicesPage.getAllRowsNameColumn(); await expect(ArrayUtil.arrayContainsArray(list, fileNames.slice(5, 10))).toEqual(true); @@ -228,7 +228,7 @@ describe('Document List - Pagination', () => { await contentServicesPage.checkDocumentListElementsAreDisplayed(); await expect(await paginationPage.getCurrentItemsPerPage()).toEqual(itemsPerPage.fifteen); - await expect(await paginationPage.getPaginationRange()).toEqual( `Showing 16-${nrOfFiles} of ${nrOfFiles}`); + await expect(await paginationPage.getPaginationRange()).toEqual(`Showing 16-${nrOfFiles} of ${nrOfFiles}`); await expect(await contentServicesPage.numberOfResultsDisplayed()).toBe(nrOfFiles - itemsPerPage.fifteenValue); list = await contentServicesPage.getAllRowsNameColumn(); await expect(ArrayUtil.arrayContainsArray(list, fileNames.slice(15, 20))).toEqual(true); @@ -309,7 +309,7 @@ describe('Document List - Pagination', () => { await paginationPage.clickOnNextPage(); await contentServicesPage.checkDocumentListElementsAreDisplayed(); await expect(await paginationPage.getCurrentItemsPerPage()).toEqual(itemsPerPage.twenty); - await expect(await paginationPage.getPaginationRange()).toEqual( `Showing 21-${secondSetNumber} of ${secondSetNumber}`); + await expect(await paginationPage.getPaginationRange()).toEqual(`Showing 21-${secondSetNumber} of ${secondSetNumber}`); await expect(await contentServicesPage.numberOfResultsDisplayed()).toBe(secondSetNumber - itemsPerPage.twentyValue); list = await contentServicesPage.getAllRowsNameColumn(); await expect(ArrayUtil.arrayContainsArray(list, secondSetOfFiles.slice(20, 25))).toEqual(true); @@ -319,7 +319,7 @@ describe('Document List - Pagination', () => { await paginationPage.clickItemsPerPageDropdown(); await expect(await paginationPage.getItemsPerPageDropdownOptions()).toEqual(['5', '10', '15', '20']); - await LocalStorageUtil.setUserPreference('supportedPageSizes', JSON.stringify([5, 10, 15, 21])); + await LocalStorageUtil.setUserPreference('supportedPageSizes', JSON.stringify([5, 10, 15, 21])); await contentServicesPage.goToDocumentList(); await browser.refresh(); @@ -328,19 +328,19 @@ describe('Document List - Pagination', () => { await contentServicesPage.checkContentIsDisplayed(docxFileModel.name); await uploadDialog.clickOnCloseButton(); await uploadDialog.dialogIsNotDisplayed(); - await paginationPage.clickItemsPerPageDropdown(); - await expect(await paginationPage.getItemsPerPageDropdownOptions()).toEqual([ '5', '10', '15', '21' ]); + await paginationPage.clickItemsPerPageDropdown(); + await expect(await paginationPage.getItemsPerPageDropdownOptions()).toEqual(['5', '10', '15', '21']); await paginationPage.clickItemsPerPageDropdown(); await paginationPage.selectItemsPerPage(itemsPerPage.twentyOne); await expect(await paginationPage.getCurrentItemsPerPage()).toEqual(itemsPerPage.twentyOne); await browser.refresh(); - await expect(await paginationPage.getPaginationRange()).toEqual( `Showing 1-${itemsPerPage.twentyOneValue} of ${numberOfFilesAfterUpload}`); + await expect(await paginationPage.getPaginationRange()).toEqual(`Showing 1-${itemsPerPage.twentyOneValue} of ${numberOfFilesAfterUpload}`); await expect(await contentServicesPage.numberOfResultsDisplayed()).toBe(itemsPerPage.twentyOneValue); - await LocalStorageUtil.setUserPreference('supportedPageSizes', JSON.stringify([5, 10, 15, 20])); + await LocalStorageUtil.setUserPreference('supportedPageSizes', JSON.stringify([5, 10, 15, 20])); await browser.refresh(); - await paginationPage.clickItemsPerPageDropdown(); + await paginationPage.clickItemsPerPageDropdown(); await expect(await paginationPage.getItemsPerPageDropdownOptions()).toEqual(['5', '10', '15', '20']); }); @@ -349,20 +349,28 @@ describe('Document List - Pagination', () => { await contentServicesPage.openFolder(newFolderModel.name); await expect(await contentServicesPage.getActiveBreadcrumb()).toEqual(newFolderModel.name); await expect(await paginationPage.getCurrentItemsPerPage()).toEqual(itemsPerPage.five); - await contentServicesPage.createAndOpenNewFolder(folderTwoModel.name); + + await apiService.getInstance().login(acsUser.email, acsUser.password); + await contentServicesPage.createNewFolder(folderTwoModel.name); + const nodeIdSubFolderTwo = await contentServicesPage.getAttributeValueForElement(folderTwoModel.name, 'Node id'); + await contentServicesPage.openFolder(folderTwoModel.name); for (let i = 0; i < numberOfSubFolders; i++) { - await contentServicesPage.createNewFolder('subfolder' + (i + 1)); + await uploadActions.createFolder('subfolder' + (i + 1), nodeIdSubFolderTwo); } + await browser.refresh(); await expect(await paginationPage.getPaginationRange()).toEqual(`Showing 1-${itemsPerPage.fiveValue} of ${numberOfSubFolders}`); await paginationPage.clickOnNextPage(); await expect(await paginationPage.getPaginationRange()).toEqual(`Showing 6-${numberOfSubFolders} of ${numberOfSubFolders}`); - await contentServicesPage.openFolder('subfolder6'); + const nodeIdSubFolder6 = await contentServicesPage.getAttributeValueForElement('subfolder6', 'Node id'); + for (let i = 0; i < numberOfSubFolders; i++) { - await contentServicesPage.createNewFolder('subfolder' + (i + 1)); + await uploadActions.createFolder('subfolder' + (i + 1), nodeIdSubFolder6); } + await browser.refresh(); + await expect(await paginationPage.getPaginationRange()).toEqual(`Showing 1-${itemsPerPage.fiveValue} of ${numberOfSubFolders}`); await expect(await paginationPage.getCurrentPage()).toEqual('Page 1'); await expect(await paginationPage.getTotalPages()).toEqual('of 2'); @@ -374,10 +382,17 @@ describe('Document List - Pagination', () => { await paginationPage.selectItemsPerPage(itemsPerPage.five); await contentServicesPage.openFolder(newFolderModel.name); await expect(await paginationPage.getCurrentItemsPerPage()).toEqual(itemsPerPage.five); - await contentServicesPage.createAndOpenNewFolder(folderTwoModel.name); + + await apiService.getInstance().login(acsUser.email, acsUser.password); + await contentServicesPage.createNewFolder(folderTwoModel.name); + const nodeIdSubFolderTwo = await contentServicesPage.getAttributeValueForElement(folderTwoModel.name, 'Node id'); + await contentServicesPage.openFolder(folderTwoModel.name); + for (let i = 0; i < numberOfSubFolders; i++) { - await contentServicesPage.createNewFolder('subfolder' + (i + 1)); + await uploadActions.createFolder('subfolder' + (i + 1), nodeIdSubFolderTwo); } + + await browser.refresh(); await expect(await paginationPage.getPaginationRange()).toEqual(`Showing 1-${itemsPerPage.fiveValue} of ${numberOfSubFolders}`); await contentServicesPage.chooseSelectionMode('Single'); diff --git a/e2e/content-services/pages/tag.page.ts b/e2e/content-services/pages/tag.page.ts index d8df191326..46ce55c4be 100644 --- a/e2e/content-services/pages/tag.page.ts +++ b/e2e/content-services/pages/tag.page.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { element, by, protractor, browser } from 'protractor'; +import { Locator, element, by, protractor, browser } from 'protractor'; import { BrowserVisibility, BrowserActions } from '@alfresco/adf-testing'; export class TagPage { @@ -26,9 +26,9 @@ export class TagPage { tagListRow = element(by.css('adf-tag-node-actions-list mat-list-item')); tagListByNodeIdRow = element(by.css('adf-tag-node-list mat-chip')); errorMessage = element(by.css('mat-hint[data-automation-id="errorMessage"]')); - tagListRowLocator = by.css('adf-tag-node-actions-list mat-list-item div'); - tagListByNodeIdRowLocator = by.css('adf-tag-node-list mat-chip span'); - tagListContentServicesRowLocator = by.css('div[class*="adf-list-tag"]'); + tagListRowLocator: Locator = by.css('adf-tag-node-actions-list mat-list-item div'); + tagListByNodeIdRowLocator: Locator = by.css('adf-tag-node-list mat-chip span'); + tagListContentServicesRowLocator: Locator = by.css('div[class*="adf-list-tag"]'); showDeleteButton = element(by.id('adf-remove-button-tag')); showMoreButton = element(by.css('button[data-automation-id="show-more-tags"]')); showLessButton = element(by.css('button[data-automation-id="show-fewer-tags"]')); @@ -76,7 +76,7 @@ export class TagPage { async getNewTagPlaceholder(): Promise { await BrowserVisibility.waitUntilElementIsVisible(this.newTagInput); - return this.newTagInput.getAttribute('placeholder'); + return this.newTagInput.getAttribute('data-placeholder'); } async addTagButtonIsEnabled(): Promise { diff --git a/e2e/content-services/pages/tree-view.page.ts b/e2e/content-services/pages/tree-view.page.ts index 506d3e62f3..ae7a19891d 100644 --- a/e2e/content-services/pages/tree-view.page.ts +++ b/e2e/content-services/pages/tree-view.page.ts @@ -21,7 +21,7 @@ import { BrowserVisibility, BrowserActions } from '@alfresco/adf-testing'; export class TreeViewPage { treeViewTitle = element(by.cssContainingText('app-tree-view div', 'TREE VIEW TEST')); - nodeIdInput = element(by.css('input[placeholder="Node Id"]')); + nodeIdInput = element(by.css('input[data-placeholder="Node Id"]')); noNodeMessage = element(by.id('adf-tree-view-missing-node')); nodesOnPage = element.all(by.css('mat-tree-node')); diff --git a/e2e/content-services/tag-component.e2e.ts b/e2e/content-services/tag-component.e2e.ts index a4009af4a6..88cc0d4e22 100644 --- a/e2e/content-services/tag-component.e2e.ts +++ b/e2e/content-services/tag-component.e2e.ts @@ -73,8 +73,10 @@ describe('Tag component', () => { afterAll(async () => { await navigationBarPage.clickLogoutButton(); - + await apiService.getInstance().login(browser.params.testConfig.admin.email, browser.params.testConfig.admin.password); await uploadActions.deleteFileOrFolder(pdfUploadedFile.entry.id); + + await loginPage.login(acsUser.email, acsUser.password); }); it('[C260374] Should NOT be possible to add a new tag without Node ID', async () => { diff --git a/e2e/content-services/upload/upload-dialog.e2e.ts b/e2e/content-services/upload/upload-dialog.e2e.ts index ca9af6a267..31d57f23f5 100644 --- a/e2e/content-services/upload/upload-dialog.e2e.ts +++ b/e2e/content-services/upload/upload-dialog.e2e.ts @@ -17,7 +17,7 @@ import { ApiService, - BrowserActions, + BrowserActions, BrowserVisibility, LoginPage, UploadActions, UserModel, @@ -98,15 +98,15 @@ describe('Upload component', () => { await apiService.getInstance().login(acsUser.email, acsUser.password); await loginPage.login(acsUser.email, acsUser.password); await contentServicesPage.goToDocumentList(); - }); + }); beforeEach(async () => { await contentServicesPage.goToDocumentList(); }); afterEach(async () => { - const nbResults = await contentServicesPage.numberOfResultsDisplayed(); - if (nbResults > 1) { + const nbResults = await contentServicesPage.emptyFolder.isPresent(); + if (!nbResults) { const nodeIds = await contentServicesPage.getElementsDisplayedId(); for (const nodeId of nodeIds) { await uploadActions.deleteFileOrFolder(nodeId); @@ -173,7 +173,7 @@ describe('Upload component', () => { await uploadDialog.clickOnCloseButton(); await uploadDialog.dialogIsNotDisplayed(); await contentServicesPage.uploadFile(pdfFileModel.location); - await contentServicesPage .checkContentIsDisplayed(pdfFileModel.name); + await contentServicesPage.checkContentIsDisplayed(pdfFileModel.name); await uploadDialog.fileIsUploaded(pdfFileModel.name); await uploadDialog.fileIsNotDisplayedInDialog(pngFileModel.name); await uploadDialog.fileIsNotDisplayedInDialog(pngFileModelTwo.name); @@ -202,6 +202,7 @@ describe('Upload component', () => { await BrowserActions.click(versionManagePage.showNewVersionButton); await versionManagePage.uploadNewVersionFile(pngFileModel.location); await versionManagePage.closeVersionDialog(); + await uploadDialog.removeUploadedFile(pngFileModel.name); await contentServicesPage.checkContentIsDisplayed(pngFileModel.name); await uploadDialog.clickOnCloseButton(); @@ -231,18 +232,23 @@ describe('Upload component', () => { await uploadDialog.dialogIsNotDisplayed(); await uploadToggles.enableFolderUpload(); + await browser.executeScript(` setInterval(() => { if(document.querySelector('[data-automation-id="adf"]')){ document.querySelector("#adf-upload-dialog-cancel-all").click(); document.querySelector("#adf-upload-dialog-cancel").click(); } - }, 500)`); + }, 2000)`); await contentServicesPage.uploadFolder(adfBigFolder.location); - await expect(await uploadDialog.getTitleText()).toEqual('Upload canceled'); + await uploadDialog.fileIsUploaded('a_png_noBackground_file.PNG'); + await uploadDialog.fileIsCancelled('a_png_noBackground_file.PNG'); + + await BrowserVisibility.waitUntilElementHasText(uploadDialog.title, 'Upload canceled'); await uploadDialog.clickOnCloseButton(); await uploadDialog.dialogIsNotDisplayed(); await contentServicesPage.openFolder(adfBigFolder.name); + await browser.sleep(2000); // We need to wai when we upload too many files we have to wait the revert await expect(contentServicesPage.numberOfResultsDisplayed()).toBe(0); }); }); diff --git a/e2e/core/infinite-scrolling.e2e.ts b/e2e/core/infinite-scrolling.e2e.ts index 61ef0f43cf..6f37881a5d 100644 --- a/e2e/core/infinite-scrolling.e2e.ts +++ b/e2e/core/infinite-scrolling.e2e.ts @@ -121,6 +121,7 @@ describe('Enable infinite scrolling', () => { await contentServicesPage.doubleClickRow(folderModel.name); await contentServicesPage.enableInfiniteScrolling(); + await expect(await contentServicesPage.numberOfResultsDisplayed()).toBe(pageSize); await infinitePaginationPage.clickLoadMoreButton(); await expect(await contentServicesPage.numberOfResultsDisplayed()).toBe(nrOfFiles); diff --git a/e2e/core/login/login-component.e2e.ts b/e2e/core/login/login-component.e2e.ts index a65978e3b1..4f3951d3e1 100644 --- a/e2e/core/login/login-component.e2e.ts +++ b/e2e/core/login/login-component.e2e.ts @@ -53,6 +53,8 @@ describe('Login component', () => { const usersActions = new UsersActions(apiService); beforeAll(async () => { + await LocalStorageUtil.setStorageItem('authType', 'BASIC'); + await apiService.getInstance().login(browser.params.testConfig.admin.email, browser.params.testConfig.admin.password); await usersActions.createUser(userA); diff --git a/e2e/core/login/redirection.e2e.ts b/e2e/core/login/redirection.e2e.ts deleted file mode 100644 index 419a8b5aff..0000000000 --- a/e2e/core/login/redirection.e2e.ts +++ /dev/null @@ -1,170 +0,0 @@ -/*! - * @license - * Copyright 2019 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 { browser } from 'protractor'; - -import { - ApiService, - LocalStorageUtil, - SettingsPage, - StringUtil, - UploadActions, - UsersActions -} from '@alfresco/adf-testing'; -import { ContentServicesPage } from '../../core/pages/content-services.page'; -import { ProcessServicesPage } from '../../process-services/pages/process-services.page'; -import { NavigationBarPage } from '../../core/pages/navigation-bar.page'; -import { LogoutPage } from '../../core/pages/logout.page'; -import { LoginShellPage } from '../../core/pages/login-shell.page'; - -describe('Login component - Redirect', () => { - - const settingsPage = new SettingsPage(); - const processServicesPage = new ProcessServicesPage(); - const navigationBarPage = new NavigationBarPage(); - const contentServicesPage = new ContentServicesPage(); - const loginPage = new LoginShellPage(); - const logoutPage = new LogoutPage(); - - let user; - let uploadedFolder; - - const apiService = new ApiService(); - const uploadActions = new UploadActions(apiService); - const usersActions = new UsersActions(apiService); - - beforeAll(async () => { - await apiService.getInstance().login(browser.params.testConfig.admin.email, browser.params.testConfig.admin.password); - - user = await usersActions.createUser(); - await apiService.getInstance().login(user.email, user.password); - - uploadedFolder = await uploadActions.createFolder('protecteFolder' + StringUtil.generateRandomString(), '-my-'); - }); - - it('[C213838] Should after login in CS be redirect to Login page when try to access to PS', async () => { - await LocalStorageUtil.setStorageItem('providers', 'ECM'); - - await loginPage.goToLoginPage(); - await loginPage.clickSettingsIcon(); - await settingsPage.setProviderEcm(); - await loginPage.login(user.email, user.password); - - await navigationBarPage.clickContentServicesButton(); - await contentServicesPage.checkAcsContainer(); - - await navigationBarPage.navigateToProcessServicesPage(); - - await loginPage.waitForElements(); - }); - - it('[C260085] Should after login in PS be redirect to Login page when try to access to CS', async () => { - await LocalStorageUtil.setStorageItem('providers', 'BPM'); - - await loginPage.goToLoginPage(); - await loginPage.clickSettingsIcon(); - await settingsPage.setProviderBpm(); - - await loginPage.enableSuccessRouteSwitch(); - await loginPage.enterSuccessRoute('activiti'); - - await loginPage.login(browser.params.testConfig.admin.email, browser.params.testConfig.admin.password); - - await navigationBarPage.navigateToProcessServicesPage(); - await processServicesPage.checkApsContainer(); - - await navigationBarPage.clickContentServicesButton(); - - await loginPage.waitForElements(); - }); - - it('[C260081] Should after login in BOTH not be redirect to Login page when try to access to CS or PS', async () => { - await LocalStorageUtil.setStorageItem('providers', 'ALL'); - - await loginPage.goToLoginPage(); - await loginPage.clickSettingsIcon(); - - await settingsPage.setProviderEcmBpm(); - - await loginPage.login(browser.params.testConfig.admin.email, browser.params.testConfig.admin.password); - - await navigationBarPage.navigateToProcessServicesPage(); - await processServicesPage.checkApsContainer(); - - await navigationBarPage.clickContentServicesButton(); - await contentServicesPage.checkAcsContainer(); - }); - - it('[C260088] Should be re-redirect to the request URL after login when try to access to a protect URL ', async () => { - await loginPage.goToLoginPage(); - await loginPage.clickSettingsIcon(); - await settingsPage.setProviderEcm(); - await loginPage.login(user.email, user.password); - - await navigationBarPage.openContentServicesFolder(uploadedFolder.entry.id); - - let actualUrl = await browser.getCurrentUrl(); - await expect(actualUrl).toEqual(browser.baseUrl + '/files/' + uploadedFolder.entry.id); - - await contentServicesPage.waitForTableBody(); - - await navigationBarPage.clickLogoutButton(); - - await logoutPage.checkLogoutSectionIsDisplayed(); - - await navigationBarPage.openContentServicesFolder(uploadedFolder.entry.id); - - await loginPage.waitForElements(); - - await loginPage.login(user.email, user.password); - - actualUrl = await browser.getCurrentUrl(); - await expect(actualUrl).toEqual(browser.baseUrl + '/files/' + uploadedFolder.entry.id); - }); - - it('[C299161] Should redirect user to requested URL after reloading login page', async () => { - await loginPage.goToLoginPage(); - await loginPage.clickSettingsIcon(); - await settingsPage.setProviderEcm(); - await loginPage.login(user.email, user.password); - - await navigationBarPage.openContentServicesFolder(uploadedFolder.entry.id); - - const currentUrl = await browser.getCurrentUrl(); - await expect(currentUrl).toEqual(browser.baseUrl + '/files/' + uploadedFolder.entry.id); - - await contentServicesPage.waitForTableBody(); - - await navigationBarPage.clickLogoutButton(); - - await logoutPage.checkLogoutSectionIsDisplayed(); - - await navigationBarPage.openContentServicesFolder(uploadedFolder.entry.id); - await loginPage.waitForElements(); - await browser.refresh(); - await loginPage.waitForElements(); - - await loginPage.enterUsername(user.email); - await loginPage.enterPassword(user.password); - await loginPage.clickSignInButton(); - - await navigationBarPage.checkMenuButtonIsDisplayed(); - - const actualUrl = await browser.getCurrentUrl(); - await expect(actualUrl).toEqual(browser.baseUrl + '/files/' + uploadedFolder.entry.id); - }); -}); diff --git a/e2e/core/login/remember-me.e2e.ts b/e2e/core/login/remember-me.e2e.ts index 5f9696295d..5c52502aa1 100644 --- a/e2e/core/login/remember-me.e2e.ts +++ b/e2e/core/login/remember-me.e2e.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { SettingsPage } from '@alfresco/adf-testing'; +import { SettingsPage, LocalStorageUtil } from '@alfresco/adf-testing'; import { LoginShellPage } from '../../core/pages/login-shell.page'; describe('Login component - Remember Me', () => { @@ -24,6 +24,8 @@ describe('Login component - Remember Me', () => { const loginPage = new LoginShellPage(); beforeAll(async () => { + await LocalStorageUtil.setStorageItem('authType', 'BASIC'); + await loginPage.goToLoginPage(); await loginPage.clickSettingsIcon(); await settingsPage.setProviderEcmBpm(); diff --git a/e2e/core/pages/content-services.page.ts b/e2e/core/pages/content-services.page.ts index ab6f3b735b..f1c99d0c13 100644 --- a/e2e/core/pages/content-services.page.ts +++ b/e2e/core/pages/content-services.page.ts @@ -16,7 +16,7 @@ */ import { DropActions, BrowserActions, BrowserVisibility, DateUtil, DocumentListPage, TogglePage, DropdownPage } from '@alfresco/adf-testing'; -import { $$, browser, by, element, ElementFinder, protractor } from 'protractor'; +import { Locator, $$, browser, by, element, ElementFinder, protractor } from 'protractor'; import { CreateLibraryDialogPage } from './dialog/create-library-dialog.page'; import { FolderDialogPage } from './dialog/folder-dialog.page'; import { NavigationBarPage } from './navigation-bar.page'; @@ -47,7 +47,7 @@ export class ContentServicesPage { deleteNodesButton = element(by.css('button[data-automation-id="delete-toolbar-button"]')); createLibraryButton = element(by.css('button[data-automation-id="create-new-library"]')); activeBreadcrumb = element(by.css('div[class*="active"]')); - tooltip = by.css('div[class*="--text adf-full-width"] span'); + tooltip: Locator = by.css('div[class*="--text adf-full-width"] span'); uploadFileButton = element(by.css('.adf-upload-button-file-container button')); uploadFileButtonInput = element(by.css('input[data-automation-id="upload-single-file"]')); uploadMultipleFileButton = element(by.css('input[data-automation-id="upload-multiple-files"]')); diff --git a/e2e/core/pages/dialog/upload-dialog.page.ts b/e2e/core/pages/dialog/upload-dialog.page.ts index 78962cf085..01b9e439df 100644 --- a/e2e/core/pages/dialog/upload-dialog.page.ts +++ b/e2e/core/pages/dialog/upload-dialog.page.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { element, by, browser, ElementFinder } from 'protractor'; +import { element, by, browser, ElementFinder, Locator } from 'protractor'; import { BrowserVisibility, BrowserActions } from '@alfresco/adf-testing'; export class UploadDialogPage { @@ -23,9 +23,9 @@ export class UploadDialogPage { closeButton = element((by.css('footer[class*="upload-dialog__actions"] button[id="adf-upload-dialog-close"]'))); dialog = element(by.css('div[id="upload-dialog"]')); minimizedDialog = element(by.css('div[class*="upload-dialog--minimized"]')); - uploadedStatusIcon = by.css('mat-icon[class*="status--done"]'); - cancelledStatusIcon = by.css('div[class*="status--cancelled"]'); - errorStatusIcon = by.css('div[class*="status--error"] mat-icon'); + uploadedStatusIcon: Locator = by.css('mat-icon[class*="status--done"]'); + cancelledStatusIcon: Locator = by.css('div[class*="status--cancelled"]'); + errorStatusIcon: Locator = by.css('div[class*="status--error"] mat-icon'); errorTooltip = element(by.css('div.mat-tooltip')); rowByRowName = by.xpath('ancestor::adf-file-uploading-list-row'); title = element(by.css('span[class*="upload-dialog__title"]')); @@ -63,14 +63,15 @@ export class UploadDialogPage { return element.all(by.css(`div[class*='uploading-row'] span[title="${content}"]`)).first(); } - getRowByRowName(content: string) { + async getRowByRowName(content: string): Promise { const rows = this.getRowsByName(content); + await BrowserVisibility.waitUntilElementIsVisible(rows); return rows.element(this.rowByRowName); } async fileIsUploaded(content: string): Promise { const row = await this.getRowByRowName(content); - await BrowserVisibility.waitUntilElementIsVisible(row.element(this.uploadedStatusIcon)); + await BrowserVisibility.waitUntilElementIsVisible(row.element(this.uploadedStatusIcon), 60000); } async fileIsError(content: string) { @@ -102,6 +103,7 @@ export class UploadDialogPage { async fileIsCancelled(content: string): Promise { const row = await this.getRowByRowName(content); + await BrowserVisibility.waitUntilElementIsVisible(row); await BrowserVisibility.waitUntilElementIsVisible(row.element(this.cancelledStatusIcon), 10000); } @@ -110,7 +112,6 @@ export class UploadDialogPage { await BrowserVisibility.waitUntilElementIsVisible(row.element(this.uploadedStatusIcon)); const elementRow = await this.getRowByRowName(content); await BrowserActions.click(elementRow.element(this.uploadedStatusIcon)); - } async getTitleText(): Promise { diff --git a/e2e/core/pages/header.page.ts b/e2e/core/pages/header.page.ts deleted file mode 100644 index 873c1d686c..0000000000 --- a/e2e/core/pages/header.page.ts +++ /dev/null @@ -1,129 +0,0 @@ -/*! - * @license - * Copyright 2019 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 { BrowserVisibility, BrowserActions } from '@alfresco/adf-testing'; - -export class HeaderPage { - - checkBox = element(by.cssContainingText('.mat-checkbox-label', 'Show menu button')); - headerColor = element(by.css('option[value="primary"]')); - titleInput = element(by.css('input[name="title"]')); - iconInput = element(by.css('input[placeholder="URL path"]')); - hexColorInput = element(by.css('input[placeholder="hex color code"]')); - logoHyperlinkInput = element(by.css('input[placeholder="Redirect URL"]')); - logoTooltipInput = element(by.css('input[placeholder="Tooltip text"]')); - positionStart = element.all(by.css('mat-radio-button[value="start"]')).first(); - positionEnd = element.all(by.css('mat-radio-button[value="end"]')).first(); - sideBarPositionRight = element(by.css('mat-sidenav.mat-drawer.mat-sidenav.mat-drawer-end')); - sideBarPositionLeft = element(by.css('mat-sidenav.mat-drawer.mat-sidenav')); - - async checkShowMenuCheckBoxIsDisplayed(): Promise { - await BrowserVisibility.waitUntilElementIsVisible(this.checkBox); - } - - async checkChooseHeaderColourIsDisplayed(): Promise { - await BrowserVisibility.waitUntilElementIsVisible(this.headerColor); - } - - async checkChangeTitleIsDisplayed(): Promise { - await BrowserVisibility.waitUntilElementIsVisible(this.titleInput); - } - - async checkChangeUrlPathIsDisplayed(): Promise { - await BrowserVisibility.waitUntilElementIsVisible(this.iconInput); - } - - async clickShowMenuButton(): Promise { - const checkBox = element(by.css('mat-checkbox')); - await BrowserActions.click(checkBox.get(0)); - } - - async changeHeaderColor(color: string): Promise { - const headerColor = element(by.css('option[value="' + color + '"]')); - await BrowserActions.click(headerColor); - } - - async checkAppTitle(name: string): Promise { - const title = element(by.cssContainingText('.adf-app-title', name)); - await BrowserVisibility.waitUntilElementIsVisible(title); - } - - async addTitle(title: string): Promise { - await BrowserActions.click(this.titleInput); - await BrowserActions.clearSendKeys(this.titleInput, title); - await this.titleInput.sendKeys(protractor.Key.ENTER); - } - - async checkIconIsDisplayed(url: string): Promise { - const icon = element(by.css('img[src="' + url + '"]')); - await BrowserVisibility.waitUntilElementIsVisible(icon); - } - - async addIcon(url: string): Promise { - await BrowserActions.click(this.iconInput); - await BrowserActions.clearSendKeys(this.iconInput, url); - await this.iconInput.sendKeys(protractor.Key.ENTER); - } - - async checkHexColorInputIsDisplayed(): Promise { - await BrowserVisibility.waitUntilElementIsVisible(this.hexColorInput); - } - - async checkLogoHyperlinkInputIsDisplayed(): Promise { - await BrowserVisibility.waitUntilElementIsVisible(this.logoHyperlinkInput); - } - - async checkLogoTooltipInputIsDisplayed(): Promise { - await BrowserVisibility.waitUntilElementIsVisible(this.logoTooltipInput); - } - - async addHexCodeColor(hexCode: string): Promise { - await BrowserActions.click(this.hexColorInput); - await this.hexColorInput.sendKeys(hexCode); - await this.hexColorInput.sendKeys(protractor.Key.ENTER); - } - - async addLogoHyperlink(hyperlink: string): Promise { - await BrowserActions.click(this.logoHyperlinkInput); - await this.logoHyperlinkInput.sendKeys(hyperlink); - await this.logoHyperlinkInput.sendKeys(protractor.Key.ENTER); - } - - async addLogoTooltip(tooltip: string): Promise { - await BrowserActions.click(this.logoTooltipInput); - await this.logoTooltipInput.sendKeys(tooltip); - await this.logoTooltipInput.sendKeys(protractor.Key.ENTER); - } - - async sideBarPositionStart(): Promise { - await BrowserActions.click(this.positionStart); - } - - async sideBarPositionEnd(): Promise { - await BrowserActions.click(this.positionEnd); - } - - async checkSidebarPositionStart(): Promise { - await BrowserVisibility.waitUntilElementIsVisible(this.sideBarPositionLeft); - } - - async checkSidebarPositionEnd(): Promise { - await BrowserVisibility.waitUntilElementIsVisible(this.sideBarPositionRight); - } - -} diff --git a/e2e/core/pages/infinite-pagination.page.ts b/e2e/core/pages/infinite-pagination.page.ts index c63589bf4d..702337e59d 100644 --- a/e2e/core/pages/infinite-pagination.page.ts +++ b/e2e/core/pages/infinite-pagination.page.ts @@ -22,6 +22,7 @@ export class InfinitePaginationPage { rootElement: ElementFinder; loadMoreButton; + loading = element(by.css('[data-automation-id="adf-infinite-pagination-spinner"]')); constructor(rootElement = element.all(by.css('adf-infinite-pagination')).first()) { this.rootElement = rootElement; @@ -30,6 +31,7 @@ export class InfinitePaginationPage { async clickLoadMoreButton(): Promise { await BrowserActions.click(this.loadMoreButton); + await BrowserVisibility.waitUntilElementIsNotVisible(this.loading); } async checkLoadMoreButtonIsNotDisplayed(): Promise { diff --git a/e2e/core/pages/metadata-view.page.ts b/e2e/core/pages/metadata-view.page.ts index 8cc5f54d2b..33c3674e3f 100644 --- a/e2e/core/pages/metadata-view.page.ts +++ b/e2e/core/pages/metadata-view.page.ts @@ -15,14 +15,14 @@ * limitations under the License. */ -import { by, element, Key, protractor } from 'protractor'; +import { Locator, by, element, Key, protractor } from 'protractor'; import { BrowserVisibility, BrowserActions } from '@alfresco/adf-testing'; export class MetadataViewPage { title = element(by.css(`div[info-drawer-title]`)); expandedAspect = element(by.css(`mat-expansion-panel-header[aria-expanded='true']`)); - aspectTitle = by.css(`mat-panel-title`); + aspectTitle: Locator = by.css(`mat-panel-title`); name = element(by.css(`[data-automation-id='card-textitem-value-name']`)); creator = element(by.css(`[data-automation-id='card-textitem-value-createdByUser.displayName']`)); createdDate = element(by.css(`span[data-automation-id='card-dateitem-createdAt'] span`)); @@ -43,7 +43,7 @@ export class MetadataViewPage { presetSwitch = element(by.id('adf-toggle-custom-preset')); defaultPropertiesSwitch = element(by.id('adf-metadata-default-properties')); closeButton = element(by.cssContainingText('button.mat-button span', 'Close')); - displayAspect = element(by.css(`input[placeholder='Display Aspect']`)); + displayAspect = element(by.css(`input[data-placeholder='Display Aspect']`)); applyAspect = element(by.cssContainingText(`button span.mat-button-wrapper`, 'Apply Aspect')); saveMetadataButton = element(by.css(`[data-automation-id='save-metadata']`)); resetMetadataButton = element(by.css(`[data-automation-id='reset-metadata']`)); diff --git a/e2e/core/pages/trashcan.page.ts b/e2e/core/pages/trashcan.page.ts index dcc7b3dc7d..39bc1337d5 100644 --- a/e2e/core/pages/trashcan.page.ts +++ b/e2e/core/pages/trashcan.page.ts @@ -16,12 +16,12 @@ */ import { BrowserActions, BrowserVisibility, DocumentListPage } from '@alfresco/adf-testing'; -import { element, by } from 'protractor'; +import { Locator, element, by } from 'protractor'; export class TrashcanPage { contentList = new DocumentListPage(element(by.css('adf-document-list'))); - rows = by.css('adf-document-list div[class*="adf-datatable-body"] adf-datatable-row[class*="adf-datatable-row"]'); + rows: Locator = by.css('adf-document-list div[class*="adf-datatable-body"] adf-datatable-row[class*="adf-datatable-row"]'); tableBody = element.all(by.css('adf-document-list .adf-datatable-body')).first(); pagination = element(by.css('adf-pagination')); emptyTrashcan = element(by.css('adf-empty-content')); diff --git a/e2e/core/settings-component.e2e.ts b/e2e/core/settings-component.e2e.ts index 74d1dcaa8a..c642c2ddcf 100644 --- a/e2e/core/settings-component.e2e.ts +++ b/e2e/core/settings-component.e2e.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { BrowserActions, SettingsPage } from '@alfresco/adf-testing'; +import { BrowserActions, LocalStorageUtil, SettingsPage } from '@alfresco/adf-testing'; import { browser, protractor } from 'protractor'; import { ContentServicesPage } from '../core/pages/content-services.page'; import { NavigationBarPage } from '../core/pages/navigation-bar.page'; @@ -33,6 +33,11 @@ describe('Settings component', () => { 'Possible causes: the network is offline, Origin is not allowed by Access-Control-Allow-Origin, the page is being unloaded, etc.'; describe('Should be able to change Urls in the Settings', () => { + beforeAll(async () => { + await LocalStorageUtil.setStorageItem('authType', 'BASIC'); + await browser.refresh(); + }); + beforeEach(async () => { await settingsPage.goToSettingsPage(); }); @@ -94,10 +99,14 @@ describe('Settings component', () => { describe('Settings Component - Basic Authentication', () => { beforeAll(async () => { + await LocalStorageUtil.setStorageItem('authType', 'OAUTH'); + await browser.refresh(); await settingsPage.goToSettingsPage(); await settingsPage.setProvider('ALL'); await settingsPage.setContentServicesURL(browser.params.testConfig.appConfig.ecmHost); await settingsPage.setProcessServicesURL(browser.params.testConfig.appConfig.bpmHost); + await settingsPage.setImplicitFlow(false); + await settingsPage.setSilentLogin(false); await settingsPage.clickApply(); }); diff --git a/e2e/core/user-info-component.e2e.ts b/e2e/core/user-info-component.e2e.ts deleted file mode 100644 index 90fd7996eb..0000000000 --- a/e2e/core/user-info-component.e2e.ts +++ /dev/null @@ -1,173 +0,0 @@ -/*! - * @license - * Copyright 2019 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 { PeopleApi } from '@alfresco/js-api'; -import { ApiService, LocalStorageUtil, LoginPage, UserInfoPage, UsersActions } from '@alfresco/adf-testing'; -import { browser } from 'protractor'; -import { FileModel } from '../models/ACS/file.model'; -import { NavigationBarPage } from '../core/pages/navigation-bar.page'; -import * as path from 'path'; -import * as fs from 'fs'; - -describe('User Info component', () => { - - const loginPage = new LoginPage(); - const userInfoPage = new UserInfoPage(); - const navigationBarPage = new NavigationBarPage(); - - const apiService = new ApiService(); - const usersActions = new UsersActions(apiService); - const peopleApi: PeopleApi = new PeopleApi(apiService.getInstance()); - - let user; - - const acsAvatarFileModel = new FileModel({ - 'name': browser.params.resources.Files.PROFILE_IMAGES.ECM.file_name, - 'location': browser.params.resources.Files.PROFILE_IMAGES.ECM.file_location - }); - const apsAvatarFileModel = new FileModel({ - 'name': browser.params.resources.Files.PROFILE_IMAGES.BPM.file_name, - 'location': browser.params.resources.Files.PROFILE_IMAGES.BPM.file_location - }); - - beforeAll(async () => { - await apiService.getInstance().login(browser.params.testConfig.admin.email, browser.params.testConfig.admin.password); - - user = await usersActions.createUser(); - }); - - afterAll(async () => { - await navigationBarPage.clickLogoutButton(); - }); - - it('[C260111] Should display UserInfo when Process Services and Content Services are enabled', async () => { - await loginPage.login(user.email, user.password); - - await userInfoPage.clickUserProfile(); - await userInfoPage.dialogIsDisplayed(); - await userInfoPage.checkContentServicesTabIsSelected(); - - await expect(await userInfoPage.getContentHeaderTitle()).toEqual(user.firstName + ' ' + user.lastName); - await expect(await userInfoPage.getContentTitle()).toEqual(user.firstName + ' ' + user.lastName); - await expect(await userInfoPage.getContentEmail()).toEqual(user.email); - await expect(await userInfoPage.getContentJobTitle()).toEqual('N/A'); - - await userInfoPage.checkInitialImage(); - await userInfoPage.APSProfileImageNotDisplayed(); - await userInfoPage.ACSProfileImageNotDisplayed(); - - await userInfoPage.clickOnProcessServicesTab(); - await userInfoPage.checkProcessServicesTabIsSelected(); - - await browser.sleep(1000); - - await expect(await userInfoPage.getProcessHeaderTitle()).toEqual(user.firstName + ' ' + user.lastName); - await expect(await userInfoPage.getProcessTitle()).toEqual(user.firstName + ' ' + user.lastName); - await expect(await userInfoPage.getProcessEmail()).toEqual(user.email); - - await userInfoPage.closeUserProfile(); - }); - - it('[C260113] Should display UserInfo when Content Services is enabled and Process Services is disabled', async () => { - await loginPage.login(user.email, user.password); - - await userInfoPage.clickUserProfile(); - await userInfoPage.dialogIsDisplayed(); - - await expect(await userInfoPage.getContentHeaderTitle()).toEqual(user.firstName + ' ' + user.lastName); - await expect(await userInfoPage.getContentTitle()).toEqual(user.firstName + ' ' + user.lastName); - await expect(await userInfoPage.getContentEmail()).toEqual(user.email); - await expect(await userInfoPage.getContentJobTitle()).toEqual('N/A'); - - await userInfoPage.checkInitialImage(); - await userInfoPage.APSProfileImageNotDisplayed(); - await userInfoPage.ACSProfileImageNotDisplayed(); - await userInfoPage.closeUserProfile(); - await userInfoPage.dialogIsNotDisplayed(); - }); - - it('[C260115] Should display UserInfo when Process Services is enabled and Content Services is disabled', async () => { - await LocalStorageUtil.setStorageItem('providers', 'BPM'); - - await loginPage.login(user.email, user.password); - - await userInfoPage.clickUserProfile(); - - await userInfoPage.dialogIsDisplayed(); - - await expect(await userInfoPage.getProcessHeaderTitle()).toEqual(user.firstName + ' ' + user.lastName); - await expect(await userInfoPage.getProcessTitle()).toEqual(user.firstName + ' ' + user.lastName); - await expect(await userInfoPage.getProcessEmail()).toEqual(user.email); - - await userInfoPage.checkInitialImage(); - await userInfoPage.APSProfileImageNotDisplayed(); - await userInfoPage.ACSProfileImageNotDisplayed(); - await userInfoPage.closeUserProfile(); - }); - - it('[C260117] Should display UserInfo with profile image uploaded in ACS', async () => { - await LocalStorageUtil.setStorageItem('providers', 'ECM'); - - await updateAvatarACS(); - - await loginPage.login(user.email, user.password); - - await userInfoPage.clickUserProfile(); - - await userInfoPage.checkACSProfileImage(); - await userInfoPage.APSProfileImageNotDisplayed(); - await userInfoPage.closeUserProfile(); - }); - - it('[C260118] Should display UserInfo with profile image uploaded in APS', async () => { - await LocalStorageUtil.setStorageItem('providers', 'BPM'); - - const users = new UsersActions(apiService); - await apiService.getInstance().login(user.email, user.password); - await users.changeProfilePictureAps(apsAvatarFileModel.getLocation()); - - await loginPage.login(user.email, user.password); - - await userInfoPage.clickUserProfile(); - - await userInfoPage.checkAPSProfileImage(); - await userInfoPage.ACSProfileImageNotDisplayed(); - await userInfoPage.initialImageNotDisplayed(); - await userInfoPage.closeUserProfile(); - }); - - it('[C260120] Should not display profile image in UserInfo when deleted in ACS', async () => { - await LocalStorageUtil.setStorageItem('providers', 'ECM'); - - await peopleApi.deleteAvatarImage(user.email); - - await loginPage.login(user.email, user.password); - - await userInfoPage.clickUserProfile(); - - await userInfoPage.checkInitialImage(); - await userInfoPage.ACSProfileImageNotDisplayed(); - await userInfoPage.closeUserProfile(); - }); - - const updateAvatarACS = async function () { - await apiService.getInstance().login(user.email, user.password); - const absolutePath = path.resolve(path.join(browser.params.testConfig.main.rootPath, acsAvatarFileModel.getLocation())); - const file: any = fs.readFileSync(absolutePath); - await peopleApi.updateAvatarImage('-me-', file); - }; -}); diff --git a/e2e/core/viewer/file-extensions/viewer-component.e2e.ts b/e2e/core/viewer/file-extensions/viewer-component.e2e.ts index 302b798315..a41d344778 100644 --- a/e2e/core/viewer/file-extensions/viewer-component.e2e.ts +++ b/e2e/core/viewer/file-extensions/viewer-component.e2e.ts @@ -17,7 +17,7 @@ import { browser } from 'protractor'; import { - ApiService, + ApiService, LocalStorageUtil, LoginPage, StringUtil, UploadActions, @@ -38,7 +38,7 @@ describe('Viewer', () => { const contentServicesPage = new ContentServicesPage(); const navigationBarPage = new NavigationBarPage(); - const apiService = new ApiService(); + const apiService = new ApiService({ authType: 'ECM', provider: 'ECM' }); const uploadActions = new UploadActions(apiService); const usersActions = new UsersActions(apiService); @@ -57,6 +57,9 @@ describe('Viewer', () => { }); beforeAll(async () => { + await LocalStorageUtil.setStorageItem('providers', 'ECM'); + await LocalStorageUtil.setStorageItem('authType', 'BASIC'); + await apiService.getInstance().login(browser.params.testConfig.admin.email, browser.params.testConfig.admin.password); await usersActions.createUser(acsUser); diff --git a/e2e/core/viewer/info-drawer.component.e2e.ts b/e2e/core/viewer/info-drawer.component.e2e.ts deleted file mode 100644 index 7a4289a8af..0000000000 --- a/e2e/core/viewer/info-drawer.component.e2e.ts +++ /dev/null @@ -1,103 +0,0 @@ -/*! - * @license - * Copyright 2019 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 { browser } from 'protractor'; -import { - ApiService, - LoginPage, - StringUtil, - UploadActions, - UserModel, - UsersActions, - ViewerPage -} from '@alfresco/adf-testing'; -import { NavigationBarPage } from '../../core/pages/navigation-bar.page'; -import { ContentServicesPage } from '../../core/pages/content-services.page'; -import { FileModel } from '../../models/ACS/file.model'; -import CONSTANTS = require('../../util/constants'); - -describe('Info Drawer', () => { - - const viewerPage = new ViewerPage(); - const navigationBarPage = new NavigationBarPage(); - const loginPage = new LoginPage(); - const contentServicesPage = new ContentServicesPage(); - - const apiService = new ApiService(); - const usersActions = new UsersActions(apiService); - const uploadActions = new UploadActions(apiService); - let site; - const acsUser = new UserModel(); - let pngFileUploaded; - - const pngFileInfo = new FileModel({ - 'name': browser.params.resources.Files.ADF_DOCUMENTS.PNG.file_name, - 'location': browser.params.resources.Files.ADF_DOCUMENTS.PNG.file_path - }); - - beforeAll(async () => { - await apiService.getInstance().login(browser.params.testConfig.admin.email, browser.params.testConfig.admin.password); - await usersActions.createUser(acsUser); - - site = await apiService.getInstance().core.sitesApi.createSite({ - title: StringUtil.generateRandomString(8), - visibility: 'PUBLIC' - }); - - await apiService.getInstance().core.sitesApi.addSiteMember(site.entry.id, { - id: acsUser.email, - role: CONSTANTS.CS_USER_ROLES.MANAGER - }); - - await apiService.getInstance().login(acsUser.email, acsUser.password); - - pngFileUploaded = await uploadActions.uploadFile(pngFileInfo.location, pngFileInfo.name, site.entry.guid); - }); - - afterAll(async () => { - await apiService.getInstance().login(acsUser.email, acsUser.password); - await uploadActions.deleteFileOrFolder(pngFileUploaded.entry.id); - await apiService.getInstance().core.sitesApi.deleteSite(site.entry.id, { permanent: true }); - }); - - beforeEach(async() => { - await loginPage.login(acsUser.email, acsUser.password); - - await navigationBarPage.goToSite(site); - await contentServicesPage.checkAcsContainer(); - }); - - it('[C277251] Should display the icon when the icon property is defined', async () => { - await viewerPage.viewFile(pngFileUploaded.entry.name); - await viewerPage.clickLeftSidebarButton(); - await viewerPage.enableShowTabWithIcon(); - await viewerPage.enableShowTabWithIconAndLabel(); - await viewerPage.checkTabHasNoIcon(0); - await expect(await viewerPage.getTabIconById(1)).toBe('face'); - await expect(await viewerPage.getTabIconById(2)).toBe('comment'); - }); - - it('[C277252] Should display the label when the label property is defined', async () => { - await viewerPage.viewFile(pngFileUploaded.entry.name); - await viewerPage.clickLeftSidebarButton(); - await viewerPage.enableShowTabWithIcon(); - await viewerPage.enableShowTabWithIconAndLabel(); - await expect(await viewerPage.getTabLabelById(0)).toBe('SETTINGS'); - await viewerPage.checkTabHasNoLabel(1); - await expect(await viewerPage.getTabLabelById(2)).toBe('COMMENTS'); - }); -}); diff --git a/e2e/process-services-cloud/pages/tasks-cloud-demo.page.ts b/e2e/process-services-cloud/pages/tasks-cloud-demo.page.ts index 3dc5b0e8c1..766799d9ec 100644 --- a/e2e/process-services-cloud/pages/tasks-cloud-demo.page.ts +++ b/e2e/process-services-cloud/pages/tasks-cloud-demo.page.ts @@ -40,9 +40,9 @@ export class TasksCloudDemoPage { selectedRows = element(by.xpath("//div[text()=' Selected Rows: ']")); noOfSelectedRows = element.all(by.xpath("//div[text()=' Selected Rows: ']//li")); addActionTitle = element(by.cssContainingText('.mat-card-title', 'Add Action')); - keyInputField = element(by.css('input[placeholder="Key"]')); - titleInputField = element(by.css('input[placeholder="Title"]')); - iconInputField = element(by.css('input[placeholder="Icon"]')); + keyInputField = element(by.css('input[data-placeholder="Key"]')); + titleInputField = element(by.css('input[data-placeholder="Title"]')); + iconInputField = element(by.css('input[data-placeholder="Icon"]')); addActionButton = element(by.cssContainingText('button span', 'Add')); disableCheckbox = element(by.css(`mat-checkbox[formcontrolname='disabled']`)); visibleCheckbox = element(by.css(`mat-checkbox[formcontrolname='visible']`)); diff --git a/e2e/process-services-cloud/process-list-cloud-action-menu.e2e.ts b/e2e/process-services-cloud/process-list-cloud-action-menu.e2e.ts index c664f0b8c8..7ab3ecc83b 100644 --- a/e2e/process-services-cloud/process-list-cloud-action-menu.e2e.ts +++ b/e2e/process-services-cloud/process-list-cloud-action-menu.e2e.ts @@ -52,34 +52,27 @@ describe('Process list cloud', () => { let testUser, groupInfo, editProcess, deleteProcess; beforeAll(async () => { - await apiService.login(browser.params.identityAdmin.email, browser.params.identityAdmin.password); + await apiService.login(browser.params.identityAdmin.email, browser.params.identityAdmin.password); - testUser = await identityService.createIdentityUserWithRole( [identityService.ROLES.ACTIVITI_USER]); - groupInfo = await groupIdentityService.getGroupInfoByGroupName('hr'); - await identityService.addUserToGroup(testUser.idIdentityService, groupInfo.id); + testUser = await identityService.createIdentityUserWithRole([identityService.ROLES.ACTIVITI_USER]); + groupInfo = await groupIdentityService.getGroupInfoByGroupName('hr'); + await identityService.addUserToGroup(testUser.idIdentityService, groupInfo.id); - await apiService.login(testUser.email, testUser.password); - const processDefinition = await processDefinitionService + await apiService.login(testUser.email, testUser.password); + const processDefinition = await processDefinitionService .getProcessDefinitionByName(browser.params.resources.ACTIVITI_CLOUD_APPS.SIMPLE_APP.processes.simpleProcess, simpleApp); - editProcess = await processInstancesService.createProcessInstance(processDefinition.entry.key, simpleApp, { + editProcess = await processInstancesService.createProcessInstance(processDefinition.entry.key, simpleApp, { 'name': StringUtil.generateRandomString(), 'businessKey': StringUtil.generateRandomString() }); - deleteProcess = await processInstancesService.createProcessInstance(processDefinition.entry.key, simpleApp, { + deleteProcess = await processInstancesService.createProcessInstance(processDefinition.entry.key, simpleApp, { 'name': StringUtil.generateRandomString(), 'businessKey': StringUtil.generateRandomString() }); - await loginSSOPage.login(testUser.email, testUser.password); - }); + await loginSSOPage.login(testUser.email, testUser.password); - afterAll(async () => { - await apiService.login(browser.params.identityAdmin.email, browser.params.identityAdmin.password); - await identityService.deleteIdentityUser(testUser.idIdentityService); - }); - - beforeAll(async () => { await navigationBarPage.navigateToProcessServicesCloudPage(); await appListCloudComponent.checkApsContainer(); await appListCloudComponent.goToApp(simpleApp); @@ -101,6 +94,11 @@ describe('Process list cloud', () => { await processCloudDemoPage.processFilterCloudComponent.clickRunningProcessesFilter(); }); + afterAll(async () => { + await apiService.login(browser.params.identityAdmin.email, browser.params.identityAdmin.password); + await identityService.deleteIdentityUser(testUser.idIdentityService); + }); + it('[C315236] Should be able to see and execute custom action menu', async () => { await processCloudDemoPage.editProcessFilterCloudComponent().openFilter(); await processCloudDemoPage.editProcessFilterCloudComponent().setProcessName(editProcess.entry.name); diff --git a/e2e/process-services/comment-component-tasks.e2e.ts b/e2e/process-services/comment-component-tasks.e2e.ts index 4ab7665fac..7b7dfe1a5f 100644 --- a/e2e/process-services/comment-component-tasks.e2e.ts +++ b/e2e/process-services/comment-component-tasks.e2e.ts @@ -116,6 +116,7 @@ describe('Comment component for Processes', () => { await expect(await commentsPage.getTime(0)).toMatch(/(ago|few)/); await expect(await commentsPage.getTime(1)).toMatch(/(ago|few)/); + await navigationBarPage.clickLogoutButton(); await loginPage.login(secondUser.email, secondUser.password); await apiService.getInstance().activiti.taskApi.addTaskComment(thirdTaskComment, newTaskId); diff --git a/e2e/process-services/custom-process-filters-sorting.e2e.ts b/e2e/process-services/custom-process-filters-sorting.e2e.ts index 343c8ffc76..273ae4f61a 100644 --- a/e2e/process-services/custom-process-filters-sorting.e2e.ts +++ b/e2e/process-services/custom-process-filters-sorting.e2e.ts @@ -70,7 +70,9 @@ describe('Sorting for process filters', () => { await apiService.getInstance().login(browser.params.testConfig.admin.email, browser.params.testConfig.admin.password); await apiService.getInstance().activiti.adminTenantsApi.deleteTenant(tenantId); - }); + + await navigationBarPage.clickLogoutButton(); + }); it('[C260476] Should be able to create a filter on APS for running processes - Oldest first and check on ADF', async () => { await apiService.getInstance().activiti.userFiltersApi.createUserProcessInstanceFilter({ diff --git a/e2e/process-services/custom-tasks-filters.e2e.ts b/e2e/process-services/custom-tasks-filters.e2e.ts index 51b92ff3ba..bcf5e4b15d 100644 --- a/e2e/process-services/custom-tasks-filters.e2e.ts +++ b/e2e/process-services/custom-tasks-filters.e2e.ts @@ -144,7 +144,7 @@ describe('Start Task - Custom App', () => { it('[C286367] 20 Items per page', async () => { await taskListSinglePage.typeItemsPerPage(itemsPerPage.twentyValue); - await taskListSinglePage.taskList().getDataTable().waitForTableBody(); + await taskListSinglePage.taskList().getDataTable().waitTillContentLoaded(); await expect(await paginationPage.getCurrentItemsPerPage()).toEqual(itemsPerPage.twenty); await expect(await paginationPage.getPaginationRange()).toEqual('Showing 1-' + nrOfTasks + ' of ' + nrOfTasks); await expect(await taskListSinglePage.taskList().getDataTable().numberOfRows()).toBe(nrOfTasks); @@ -158,7 +158,7 @@ describe('Start Task - Custom App', () => { it('[C286365] 5 Items per page', async () => { await taskListSinglePage.typeItemsPerPage(itemsPerPage.fiveValue); - await taskListSinglePage.taskList().getDataTable().waitForTableBody(); + await taskListSinglePage.taskList().getDataTable().waitTillContentLoaded(); await expect(await paginationPage.getCurrentItemsPerPage()).toEqual(itemsPerPage.five); await expect(await paginationPage.getPaginationRange()).toEqual('Showing 1-' + itemsPerPage.fiveValue * currentPage + ' of ' + nrOfTasks); await expect(await taskListSinglePage.taskList().getDataTable().numberOfRows()).toBe(itemsPerPage.fiveValue); @@ -170,7 +170,7 @@ describe('Start Task - Custom App', () => { await paginationPage.clickOnNextPage(); currentPage++; - await taskListSinglePage.taskList().getDataTable().waitForTableBody(); + await taskListSinglePage.taskList().getDataTable().waitTillContentLoaded(); await expect(await paginationPage.getCurrentItemsPerPage()).toEqual(itemsPerPage.five); await expect(await paginationPage.getPaginationRange()).toEqual('Showing 6-' + itemsPerPage.fiveValue * currentPage + ' of ' + nrOfTasks); await expect(await taskListSinglePage.taskList().getDataTable().numberOfRows()).toBe(itemsPerPage.fiveValue); @@ -181,7 +181,7 @@ describe('Start Task - Custom App', () => { await paginationPage.clickOnNextPage(); currentPage++; - await taskListSinglePage.taskList().getDataTable().waitForTableBody(); + await taskListSinglePage.taskList().getDataTable().waitTillContentLoaded(); await expect(await paginationPage.getCurrentItemsPerPage()).toEqual(itemsPerPage.five); await expect(await paginationPage.getPaginationRange()).toEqual('Showing 11-' + itemsPerPage.fiveValue * currentPage + ' of ' + nrOfTasks); await expect(await taskListSinglePage.taskList().getDataTable().numberOfRows()).toBe(itemsPerPage.fiveValue); @@ -192,7 +192,7 @@ describe('Start Task - Custom App', () => { await paginationPage.clickOnNextPage(); currentPage++; - await taskListSinglePage.taskList().getDataTable().waitForTableBody(); + await taskListSinglePage.taskList().getDataTable().waitTillContentLoaded(); await expect(await paginationPage.getCurrentItemsPerPage()).toEqual(itemsPerPage.five); await expect(await paginationPage.getPaginationRange()).toEqual('Showing 16-' + itemsPerPage.fiveValue * currentPage + ' of ' + nrOfTasks); await expect(await taskListSinglePage.taskList().getDataTable().numberOfRows()).toBe(itemsPerPage.fiveValue); @@ -204,7 +204,7 @@ describe('Start Task - Custom App', () => { it('[C286364] 10 Items per page', async () => { currentPage = 1; await taskListSinglePage.typeItemsPerPage(itemsPerPage.tenValue); - await taskListSinglePage.taskList().getDataTable().waitForTableBody(); + await taskListSinglePage.taskList().getDataTable().waitTillContentLoaded(); await expect(await paginationPage.getCurrentItemsPerPage()).toEqual(itemsPerPage.ten); await expect(await paginationPage.getPaginationRange()).toEqual('Showing 1-' + itemsPerPage.tenValue * currentPage + ' of ' + nrOfTasks); await expect(await taskListSinglePage.taskList().getDataTable().numberOfRows()).toBe(itemsPerPage.tenValue); @@ -213,7 +213,7 @@ describe('Start Task - Custom App', () => { }); await paginationPage.clickOnNextPage(); currentPage++; - await taskListSinglePage.taskList().getDataTable().waitForTableBody(); + await taskListSinglePage.taskList().getDataTable().waitTillContentLoaded(); await expect(await paginationPage.getCurrentItemsPerPage()).toEqual(itemsPerPage.ten); await expect(await paginationPage.getPaginationRange()).toEqual('Showing 11-' + itemsPerPage.tenValue * currentPage + ' of ' + nrOfTasks); await expect(await taskListSinglePage.taskList().getDataTable().numberOfRows()).toBe(itemsPerPage.tenValue); @@ -225,7 +225,7 @@ describe('Start Task - Custom App', () => { it('[C286363] 15 Items per page', async () => { currentPage = 1; await taskListSinglePage.typeItemsPerPage(itemsPerPage.fifteenValue); - await taskListSinglePage.taskList().getDataTable().waitForTableBody(); + await taskListSinglePage.taskList().getDataTable().waitTillContentLoaded(); await expect(await paginationPage.getCurrentItemsPerPage()).toEqual(itemsPerPage.fifteen); await expect(await paginationPage.getPaginationRange()).toEqual('Showing 1-' + itemsPerPage.fifteenValue * currentPage + ' of ' + nrOfTasks); await expect(await taskListSinglePage.taskList().getDataTable().numberOfRows()).toBe(itemsPerPage.fifteenValue); @@ -234,7 +234,7 @@ describe('Start Task - Custom App', () => { }); currentPage++; await paginationPage.clickOnNextPage(); - await taskListSinglePage.taskList().getDataTable().waitForTableBody(); + await taskListSinglePage.taskList().getDataTable().waitTillContentLoaded(); await expect(await paginationPage.getCurrentItemsPerPage()).toEqual(itemsPerPage.fifteen); await expect(await paginationPage.getPaginationRange()).toEqual('Showing 16-' + nrOfTasks + ' of ' + nrOfTasks); await expect(await taskListSinglePage.taskList().getDataTable().numberOfRows()).toBe(nrOfTasks - itemsPerPage.fifteenValue); @@ -260,7 +260,7 @@ describe('Start Task - Custom App', () => { currentPage = 1; await taskListSinglePage.typeItemsPerPage(itemsPerPage.fiveValue); await taskListSinglePage.typePage(currentPage); - await taskListSinglePage.taskList().getDataTable().waitForTableBody(); + await taskListSinglePage.taskList().getDataTable().waitTillContentLoaded(); await expect(await paginationPage.getCurrentPage()).toEqual('Page ' + currentPage); await expect(await paginationPage.getTotalPages()).toEqual(totalNrOfPages); await paginationPage.checkPageSelectorIsDisplayed(); @@ -271,7 +271,7 @@ describe('Start Task - Custom App', () => { currentPage++; await taskListSinglePage.typePage(currentPage); - await taskListSinglePage.taskList().getDataTable().waitForTableBody(); + await taskListSinglePage.taskList().getDataTable().waitTillContentLoaded(); await expect(await paginationPage.getCurrentPage()).toEqual('Page ' + currentPage); await expect(await paginationPage.getTotalPages()).toEqual(totalNrOfPages); await paginationPage.checkPageSelectorIsDisplayed(); @@ -282,7 +282,7 @@ describe('Start Task - Custom App', () => { currentPage++; await taskListSinglePage.typePage(currentPage); - await taskListSinglePage.taskList().getDataTable().waitForTableBody(); + await taskListSinglePage.taskList().getDataTable().waitTillContentLoaded(); await expect(await paginationPage.getCurrentPage()).toEqual('Page ' + currentPage); await expect(await paginationPage.getTotalPages()).toEqual(totalNrOfPages); await paginationPage.checkPageSelectorIsDisplayed(); @@ -293,7 +293,7 @@ describe('Start Task - Custom App', () => { currentPage++; await taskListSinglePage.typePage(currentPage); - await taskListSinglePage.taskList().getDataTable().waitForTableBody(); + await taskListSinglePage.taskList().getDataTable().waitTillContentLoaded(); await expect(await paginationPage.getCurrentPage()).toEqual('Page ' + currentPage); await expect(await paginationPage.getTotalPages()).toEqual(totalNrOfPages); await paginationPage.checkPageSelectorIsDisplayed(); @@ -315,7 +315,7 @@ describe('Start Task - Custom App', () => { it('[C286413] Task is displayed when typing into dueAfter field a date before the tasks due date', async () => { await taskListSinglePage.typeDueAfter(beforeDate); - await taskListSinglePage.taskList().checkContentIsDisplayed(paginationTasksName[0]); + await taskListSinglePage.taskList().getDataTable().waitTillContentLoaded(); await expect(await taskListSinglePage.taskList().getDataTable().numberOfRows()).toBe(1); }); @@ -336,7 +336,7 @@ describe('Start Task - Custom App', () => { it('[C286425] Task is displayed when typing into dueBefore field a date after the task due date', async () => { await taskListSinglePage.typeDueBefore(afterDate); - await taskListSinglePage.taskList().checkContentIsDisplayed(paginationTasksName[0]); + await taskListSinglePage.taskList().getDataTable().waitTillContentLoaded(); await expect(await taskListSinglePage.taskList().getDataTable().numberOfRows()).toBe(1); }); @@ -354,7 +354,7 @@ describe('Start Task - Custom App', () => { it('[C286427] Task is displayed when typing into dueAfter field a date before the tasks due date and into dueBefore a date after', async () => { await taskListSinglePage.typeDueBefore(afterDate); await taskListSinglePage.typeDueAfter(beforeDate); - await taskListSinglePage.taskList().checkContentIsDisplayed(paginationTasksName[0]); + await taskListSinglePage.taskList().getDataTable().waitTillContentLoaded(); await expect(await taskListSinglePage.taskList().getDataTable().numberOfRows()).toBe(1); }); @@ -362,6 +362,7 @@ describe('Start Task - Custom App', () => { await taskListSinglePage.typeDueBefore(afterDate); await taskListSinglePage.typeDueAfter(afterDate); await taskListSinglePage.paginationPage().checkPaginationIsNotDisplayed(); + await expect(await taskListSinglePage.taskList().getNoTasksFoundMessage()).toEqual(noTasksFoundMessage); }); it('[C280515] Should be able to see only the tasks of a specific app when typing the apps id in the appId field', async () => { @@ -397,6 +398,7 @@ describe('Start Task - Custom App', () => { it('[C280629] Should be able to see only the task with specific taskId when typing it in the task Id field', async () => { await taskListSinglePage.typeTaskId(taskWithDueDate.id); + await taskListSinglePage.taskList().getDataTable().waitTillContentLoaded(); await expect(await taskListSinglePage.getTaskId()).toEqual(taskWithDueDate.id); await taskListSinglePage.taskList().checkContentIsDisplayed(taskWithDueDate.name); @@ -405,6 +407,7 @@ describe('Start Task - Custom App', () => { it('[C280630] Should be able to see No tasks found when typing an invalid taskId', async () => { await taskListSinglePage.typeTaskId(invalidTaskId); + await expect(await taskListSinglePage.getTaskId()).toEqual(invalidTaskId); await expect(await taskListSinglePage.taskList().getNoTasksFoundMessage()).toEqual(noTasksFoundMessage); @@ -412,6 +415,7 @@ describe('Start Task - Custom App', () => { it('[C286589] Should be able to see only completed tasks when choosing Completed from state drop down', async () => { await taskListSinglePage.selectState('Completed'); + await taskListSinglePage.taskList().getDataTable().waitTillContentLoaded(); await taskListSinglePage.taskList().checkContentIsDisplayed(completedTasks[0].name); await taskListSinglePage.taskList().checkContentIsDisplayed(completedTasks[1].name); @@ -421,6 +425,7 @@ describe('Start Task - Custom App', () => { it('[C286597] Should be able to see only running tasks when choosing Active from state drop down', async () => { await taskListSinglePage.selectState('Active'); + await taskListSinglePage.taskList().getDataTable().waitTillContentLoaded(); await taskListSinglePage.taskList().checkContentIsNotDisplayed(completedTasks[0].name); await taskListSinglePage.taskList().checkContentIsNotDisplayed(completedTasks[1].name); @@ -433,6 +438,7 @@ describe('Start Task - Custom App', () => { it('[C286598] Should be able to see all tasks when choosing All from state drop down', async () => { await taskListSinglePage.selectState('All'); + await taskListSinglePage.taskList().getDataTable().waitTillContentLoaded(); await taskListSinglePage.taskList().checkContentIsDisplayed(completedTasks[0].name); await taskListSinglePage.taskList().checkContentIsDisplayed(completedTasks[1].name); @@ -452,7 +458,7 @@ describe('Start Task - Custom App', () => { await taskListSinglePage.clickResetButton(); await taskListSinglePage.typeProcessDefinitionId(processDefinitionId.processDefinitionId); - + await taskListSinglePage.taskList().getDataTable().waitTillContentLoaded(); await expect(await taskListSinglePage.taskList().getDataTable().numberOfRows()).toBe(4); await taskListSinglePage.getAllProcessDefinitionIds().then(async (list) => { await expect(ArrayUtil.arrayContainsArray(list, processDefinitionIds)).toEqual(true); @@ -475,6 +481,7 @@ describe('Start Task - Custom App', () => { await taskListSinglePage.clickResetButton(); await taskListSinglePage.typeProcessInstanceId(processDefinitionId.id); + await taskListSinglePage.taskList().getDataTable().waitTillContentLoaded(); await expect(await taskListSinglePage.getProcessInstanceId()).toEqual(processDefinitionId.id); await expect(await taskListSinglePage.taskList().getDataTable().numberOfRows()).toBe(1); diff --git a/e2e/process-services/dynamic-table-date-picker.e2e.ts b/e2e/process-services/dynamic-table-date-picker.e2e.ts index e9e64a0c3f..d6c71a8ccf 100644 --- a/e2e/process-services/dynamic-table-date-picker.e2e.ts +++ b/e2e/process-services/dynamic-table-date-picker.e2e.ts @@ -78,6 +78,7 @@ describe('Dynamic Table', () => { afterAll(async () => { await apiService.getInstance().login(user.email, user.password); await apiService.getInstance().activiti.modelsApi.deleteModel(appId); + await navigationBarPage.clickLogoutButton(); }); beforeEach(async () => { diff --git a/e2e/process-services/empty-process-list-component.e2e.ts b/e2e/process-services/empty-process-list-component.e2e.ts index 13c18f3763..55f0d2c7c7 100644 --- a/e2e/process-services/empty-process-list-component.e2e.ts +++ b/e2e/process-services/empty-process-list-component.e2e.ts @@ -65,8 +65,10 @@ describe('Empty Process List Test', () => { await processFiltersPage.clickCreateProcessButton(); await processFiltersPage.clickNewProcessDropdown(); + await processFiltersPage.checkStartProcessIsDisplay(); await startProcessPage.selectFromProcessDropdown(appWithProcess.process_wse_name); await startProcessPage.clickStartProcessButton(); + await expect(await processFiltersPage.numberOfProcessRows()).toEqual(1); await processDetailsPage.checkProcessDetailsCard(); @@ -78,6 +80,7 @@ describe('Empty Process List Test', () => { await processFiltersPage.clickCreateProcessButton(); await processFiltersPage.clickNewProcessDropdown(); + await processFiltersPage.checkStartProcessIsDisplay(); await startProcessPage.selectFromProcessDropdown(simpleAppWithUserForm.processName); await startProcessPage.clickStartProcessButton(); diff --git a/e2e/process-services/form-widgets-component.e2e.ts b/e2e/process-services/form-widgets-component.e2e.ts index 1f89ec4ce3..dc8160bfb4 100644 --- a/e2e/process-services/form-widgets-component.e2e.ts +++ b/e2e/process-services/form-widgets-component.e2e.ts @@ -18,7 +18,6 @@ import { ApiService, ApplicationsUtil, - BrowserActions, LoginPage, ProcessUtil, UsersActions, @@ -44,12 +43,12 @@ describe('Form widgets', () => { const newTask = 'First task'; let processUserModel; - let appModel; + let appModelWidget; describe('Form widgets', () => { - const app = browser.params.resources.Files.WIDGETS_SMOKE_TEST; - const appFields = app.form_fields; + const appWidget = browser.params.resources.Files.WIDGETS_SMOKE_TEST; + const appFields = appWidget.form_fields; beforeAll(async () => { await apiService.getInstance().login(browser.params.testConfig.admin.email, browser.params.testConfig.admin.password); @@ -58,17 +57,17 @@ describe('Form widgets', () => { await apiService.getInstance().login(processUserModel.email, processUserModel.password); - appModel = await applicationsService.importPublishDeployApp(app.file_path); + appModelWidget = await applicationsService.importPublishDeployApp(appWidget.file_path); await loginPage.login(processUserModel.email, processUserModel.password); - await (await new NavigationBarPage().navigateToProcessServicesPage()).goToApp(appModel.name); + await (await new NavigationBarPage().navigateToProcessServicesPage()).goToApp(appModelWidget.name); await taskPage.filtersPage().goToFilter(CONSTANTS.TASK_FILTERS.MY_TASKS); const task = await taskPage.createNewTask(); await task.addName(newTask); await task.addDescription('Description'); - await task.selectForm(app.formName); + await task.selectForm(appWidget.formName); await task.clickStartButton(); await taskPage.tasksListPage().checkContentIsDisplayed(newTask); @@ -83,9 +82,11 @@ describe('Form widgets', () => { }); afterAll(async () => { + await new NavigationBarPage().clickLogoutButton(); await apiService.getInstance().login(browser.params.testConfig.admin.email, browser.params.testConfig.admin.password); await apiService.getInstance().activiti.adminTenantsApi.deleteTenant(processUserModel.tenantId); + await apiService.getInstance().logout(); }); it('[C272778] Should display text and multi-line in form', async () => { @@ -193,8 +194,9 @@ describe('Form widgets', () => { describe('with fields involving other people', () => { const app = browser.params.resources.Files.FORM_ADF; - let deployedApp, process; + let process; const appFields = app.form_fields; + let appModel; beforeAll(async () => { await apiService.getInstance().login(browser.params.testConfig.admin.email, browser.params.testConfig.admin.password); @@ -204,18 +206,14 @@ describe('Form widgets', () => { await apiService.getInstance().login(processUserModel.email, processUserModel.password); appModel = await applicationsService.importPublishDeployApp(app.file_path); - const appDefinitions = await apiService.getInstance().activiti.appsApi.getAppDefinitions(); - deployedApp = appDefinitions.data.find((currentApp) => { - return currentApp.modelId === appModel.id; - }); const processUtil = new ProcessUtil(apiService); process = await processUtil.startProcessOfApp(appModel.name); await loginPage.login(processUserModel.email, processUserModel.password); }); beforeEach(async () => { - const urlToNavigateTo = `${browser.baseUrl}/activiti/apps/${deployedApp.id}/tasks/`; - await BrowserActions.getUrl(urlToNavigateTo); + await (await new NavigationBarPage().navigateToProcessServicesPage()).goToApp(appModel.name); + await taskPage.filtersPage().goToFilter(CONSTANTS.TASK_FILTERS.MY_TASKS); await taskPage.formFields().checkFormIsDisplayed(); }); diff --git a/e2e/process-services/info-drawer.e2e.ts b/e2e/process-services/info-drawer.e2e.ts index c426412433..0c7b79ca9c 100644 --- a/e2e/process-services/info-drawer.e2e.ts +++ b/e2e/process-services/info-drawer.e2e.ts @@ -39,7 +39,6 @@ import moment = require('moment'); describe('Info Drawer', () => { const app = browser.params.resources.Files.SIMPLE_APP_WITH_USER_FORM; - const loginPage = new LoginPage(); const navigationBarPage = new NavigationBarPage(); const taskPage = new TasksPage(); @@ -138,8 +137,12 @@ describe('Info Drawer', () => { await taskPage.checkTaskTitle(name); await expect(await taskPage.taskDetails().getPriority()).toEqual(taskDetails.priority); await taskPage.taskDetails().updatePriority('40'); + await taskPage.taskDetails().checkTaskDetailsDisplayed(); + await browser.sleep(2000); await expect(await taskPage.taskDetails().getPriority()).toEqual('40'); await taskPage.taskDetails().updatePriority(); + await taskPage.taskDetails().checkTaskDetailsDisplayed(); + await browser.sleep(2000); await expect(await taskPage.taskDetails().getPriority()).toEqual('0'); await taskPage.taskDetails().clickCompleteFormTask(); @@ -254,10 +257,12 @@ describe('Info Drawer', () => { await taskPage.tasksListPage().checkTaskListIsLoaded(); await taskPage.tasksListPage().getDataTable().waitForTableBody(); await taskPage.tasksListPage().checkContentIsNotDisplayed(app.taskName); + await taskPage.filtersPage().goToFilter(CONSTANTS.TASK_FILTERS.INV_TASKS); await taskPage.tasksListPage().checkTaskListIsLoaded(); - await taskPage.tasksListPage().getDataTable().waitForTableBody(); + await taskPage.taskDetails().checkTaskDetailsDisplayed(); + await browser.sleep(2000); await shouldHaveInfoDrawerDetails({ ...taskDetails, dueDate: date.header, @@ -277,10 +282,13 @@ describe('Info Drawer', () => { await taskPage.tasksListPage().checkTaskListIsLoaded(); await taskPage.tasksListPage().getDataTable().waitForTableBody(); await taskPage.filtersPage().goToFilter(CONSTANTS.TASK_FILTERS.INV_TASKS); + await taskPage.tasksListPage().checkContentIsDisplayed(app.taskName); await taskPage.tasksListPage().selectRow(app.taskName); await taskPage.checkTaskTitle(app.taskName); + await taskPage.taskDetails().checkTaskDetailsDisplayed(); + await browser.sleep(2000); await shouldHaveInfoDrawerDetails({ ...taskDetails, dueDate: 'No date', @@ -303,6 +311,8 @@ describe('Info Drawer', () => { await taskPage.tasksListPage().selectRow(name); await taskPage.checkTaskTitle(name); + await taskPage.taskDetails().checkTaskDetailsDisplayed(); + await browser.sleep(2000); await expect(await taskPage.taskDetails().isAssigneeClickable()).toBeTruthy(); await shouldHaveInfoDrawerDetails({ ...taskDetails, diff --git a/e2e/process-services/pages/attachment-list.page.ts b/e2e/process-services/pages/attachment-list.page.ts index 3141c0c62f..ce682f9e94 100644 --- a/e2e/process-services/pages/attachment-list.page.ts +++ b/e2e/process-services/pages/attachment-list.page.ts @@ -17,7 +17,6 @@ import { element, by, protractor, browser } from 'protractor'; import * as path from 'path'; -import * as remote from 'selenium-webdriver/remote'; import { BrowserVisibility, BrowserActions } from '@alfresco/adf-testing'; export class AttachmentListPage { @@ -34,8 +33,6 @@ export class AttachmentListPage { } async clickAttachFileButton(fileLocation: string): Promise { - browser.setFileDetector(new remote.FileDetector()); - await BrowserVisibility.waitUntilElementIsPresent(this.attachFileButton); await this.attachFileButton.sendKeys(path.resolve(path.join(browser.params.testConfig.main.rootPath, fileLocation))); } diff --git a/e2e/process-services/pages/dialog/create-checklist-dialog.page.ts b/e2e/process-services/pages/dialog/create-checklist-dialog.page.ts index ac3c0a7db0..1013c4b8cb 100644 --- a/e2e/process-services/pages/dialog/create-checklist-dialog.page.ts +++ b/e2e/process-services/pages/dialog/create-checklist-dialog.page.ts @@ -43,7 +43,7 @@ export class ChecklistDialog { async getNameFieldPlaceholder(): Promise { await BrowserVisibility.waitUntilElementIsVisible(this.nameField); - return this.nameField.getAttribute('placeholder'); + return this.nameField.getAttribute('data-placeholder'); } async checkCancelButtonIsEnabled(): Promise { diff --git a/e2e/process-services/pages/dialog/start-task-dialog.page.ts b/e2e/process-services/pages/dialog/start-task-dialog.page.ts index bc6aaede58..03e0e80978 100644 --- a/e2e/process-services/pages/dialog/start-task-dialog.page.ts +++ b/e2e/process-services/pages/dialog/start-task-dialog.page.ts @@ -59,7 +59,7 @@ export class StartTaskDialogPage { async getAssignee(): Promise { await BrowserVisibility.waitUntilElementIsVisible(this.assignee); - return this.assignee.getAttribute('placeholder'); + return this.assignee.getAttribute('data-placeholder'); } async selectForm(form): Promise { diff --git a/e2e/process-services/pages/filters.page.ts b/e2e/process-services/pages/filters.page.ts index 4b38ebf646..11df766eb2 100644 --- a/e2e/process-services/pages/filters.page.ts +++ b/e2e/process-services/pages/filters.page.ts @@ -29,7 +29,8 @@ export class FiltersPage { async goToFilter(filterName): Promise { await BrowserActions.closeMenuAndDialogs(); - await BrowserActions.clickExecuteScript(`[data-automation-id="${filterName}_filter"]`); + const filter = element(by.css(`button[data-automation-id="${filterName}_filter"]`)); + await BrowserActions.click(filter); } async sortByName(sortOrder: string): Promise { diff --git a/e2e/process-services/pages/process-filters.page.ts b/e2e/process-services/pages/process-filters.page.ts index bcda1a046e..30a71f3561 100644 --- a/e2e/process-services/pages/process-filters.page.ts +++ b/e2e/process-services/pages/process-filters.page.ts @@ -16,7 +16,7 @@ */ import { BrowserActions, BrowserVisibility, DataTableComponentPage, StartProcessPage } from '@alfresco/adf-testing'; -import { by, element } from 'protractor'; +import { Locator, by, element } from 'protractor'; export class ProcessFiltersPage { @@ -30,10 +30,11 @@ export class ProcessFiltersPage { accordionMenu = element(by.css('.app-processes-menu mat-accordion')); buttonWindow = element(by.css('div > button[data-automation-id="btn-start-process"] > div')); noContentMessage = element.all(by.css('.adf-empty-content__title')).first(); - rows = by.css('adf-process-instance-list .adf-datatable-body adf-datatable-row[class*="adf-datatable-row"]'); + rows: Locator = by.css('adf-process-instance-list .adf-datatable-body adf-datatable-row[class*="adf-datatable-row"]'); tableBody = element.all(by.css('adf-datatable .adf-datatable-body')).first(); - nameColumn = by.css('div[class*="adf-datatable-body"] adf-datatable-row[class*="adf-datatable-row"] div[title="Name"] span'); - processIcon = by.css('adf-icon[data-automation-id="adf-filter-icon"]'); + nameColumn: Locator = by.css('div[class*="adf-datatable-body"] adf-datatable-row[class*="adf-datatable-row"] div[title="Name"] span'); + processIcon: Locator = by.css('adf-icon[data-automation-id="adf-filter-icon"]'); + startProcessEl = element(by.css('adf-start-process .adf-start-process')); async startProcess(): Promise { await this.clickCreateProcessButton(); @@ -67,6 +68,10 @@ export class ProcessFiltersPage { await BrowserActions.click(this.newProcessButton); } + async checkStartProcessIsDisplay(): Promise { + await BrowserVisibility.waitUntilElementIsVisible(this.startProcessEl); + } + async checkNoContentMessage(): Promise { await BrowserVisibility.waitUntilElementIsVisible(this.noContentMessage); } @@ -83,6 +88,7 @@ export class ProcessFiltersPage { } async numberOfProcessRows(): Promise { + await BrowserVisibility.waitUntilElementIsVisible(element.all(this.rows).first()); return element.all(this.rows).count(); } diff --git a/e2e/process-services/pages/process-services.page.ts b/e2e/process-services/pages/process-services.page.ts index 17a5dee1d8..123b0d065e 100644 --- a/e2e/process-services/pages/process-services.page.ts +++ b/e2e/process-services/pages/process-services.page.ts @@ -17,15 +17,15 @@ import { ProcessServiceTabBarPage } from './process-service-tab-bar.page'; -import { element, by } from 'protractor'; +import { Locator, element, by } from 'protractor'; import { BrowserVisibility, BrowserActions } from '@alfresco/adf-testing'; export class ProcessServicesPage { apsAppsContainer = element(by.css('.adf-app-listgrid')); taskApp = element(by.css('mat-card[title="Task App"]')); - iconTypeLocator = by.css('mat-icon[class*="card-logo-icon"]'); - descriptionLocator = by.css('mat-card-subtitle[class*="subtitle"]'); + iconTypeLocator: Locator = by.css('mat-icon[class*="card-logo-icon"]'); + descriptionLocator: Locator = by.css('mat-card-subtitle[class*="subtitle"]'); async checkApsContainer(): Promise { await BrowserVisibility.waitUntilElementIsVisible(this.apsAppsContainer); diff --git a/e2e/process-services/pages/task-details.page.ts b/e2e/process-services/pages/task-details.page.ts index fbf7e41c04..3bcf76605c 100644 --- a/e2e/process-services/pages/task-details.page.ts +++ b/e2e/process-services/pages/task-details.page.ts @@ -16,7 +16,7 @@ */ import { BrowserActions, BrowserVisibility, DropdownPage, TabsPage } from '@alfresco/adf-testing'; -import { browser, by, element, Key } from 'protractor'; +import { Locator, browser, by, element, Key } from 'protractor'; import { AppSettingsTogglesPage } from './dialog/app-settings-toggles.page'; export class TaskDetailsPage { @@ -24,6 +24,7 @@ export class TaskDetailsPage { appSettingsTogglesClass = new AppSettingsTogglesPage(); formContent = element(by.css('adf-form')); + formNameField = element(by.css('[data-automation-id="card-textitem-value-formName"]')); formNameButton = element(by.css('[data-automation-id="card-textitem-toggle-formName"]')); assigneeField = element(by.css('[data-automation-id="card-textitem-value-assignee"]')); @@ -43,8 +44,8 @@ export class TaskDetailsPage { addCommentButton = element(by.css('[data-automation-id="comments-input-add"]')); involvePeopleButton = element(by.css('div[class*="add-people"]')); addPeopleField = element(by.css('input[data-automation-id="adf-people-search-input"]')); - addInvolvedUserButton = element(by.css('button[id="add-people"] span')); - emailInvolvedUser = by.xpath('following-sibling::div[@class="adf-people-email"]'); + addInvolvedUserButton = element(by.css('button[id="add-people"]')); + emailInvolvedUser: Locator = by.css('[data-automation-id="adf-people-email"]'); taskDetailsInfoDrawer = element(by.tagName('adf-info-drawer')); taskDetailsSection = element(by.css('div[data-automation-id="app-tasks-details"]')); taskDetailsEmptySection = element(by.css('div[data-automation-id="adf-tasks-details--empty"]')); @@ -110,24 +111,17 @@ export class TaskDetailsPage { await this.attachFormDropdown.selectDropdownOption(option); } - async checkCancelAttachFormIsDisplayed(): Promise { - await BrowserVisibility.waitUntilElementIsVisible(this.cancelAttachForm); - } - async noFormIsDisplayed(): Promise { await BrowserVisibility.waitUntilElementIsNotVisible(this.formContent); } - async clickCancelAttachForm(): Promise { - await BrowserActions.click(this.cancelAttachForm); - } - async checkRemoveAttachFormIsDisplayed() { await BrowserVisibility.waitUntilElementIsVisible(this.removeAttachForm); } async clickRemoveAttachForm(): Promise { await BrowserActions.click(this.removeAttachForm); + await browser.sleep(2000); } async checkAttachFormButtonIsDisplayed(): Promise { @@ -142,9 +136,8 @@ export class TaskDetailsPage { return BrowserActions.click(this.attachFormButton); } - async checkFormIsAttached(formName): Promise { - const attachedFormName = await BrowserActions.getInputValue(this.formNameField); - await expect(attachedFormName).toEqual(formName); + async checkFormIsAttached(formName: string): Promise { + await BrowserVisibility.waitUntilElementHasValue(this.formNameField, formName); } getFormName(): Promise { @@ -210,7 +203,7 @@ export class TaskDetailsPage { async getDescriptionPlaceholder(): Promise { await BrowserVisibility.waitUntilElementIsPresent(this.descriptionField); - return this.descriptionField.getAttribute('placeholder'); + return this.descriptionField.getAttribute('data-placeholder'); } getDueDate(): Promise { @@ -226,11 +219,13 @@ export class TaskDetailsPage { await BrowserActions.clearWithBackSpace(this.priority); await BrowserActions.clearSendKeys(element(by.css('input[data-automation-id="card-textitem-value-priority"]')), priority ? priority : ' '); await this.priority.sendKeys(Key.TAB); + await browser.sleep(1000); } async updateDueDate(): Promise { await BrowserActions.click(this.dueDateField); await BrowserActions.click(element.all(by.css('.mat-datetimepicker-calendar-body-cell')).first()); + await browser.sleep(1000); } async updateDescription(description?: string): Promise { @@ -238,12 +233,16 @@ export class TaskDetailsPage { await BrowserActions.clearWithBackSpace(this.descriptionField); await BrowserActions.clearSendKeys(element(by.css('[data-automation-id="card-textitem-value-description"]')), description ? description : ''); await this.descriptionField.sendKeys(Key.TAB); + await browser.sleep(1000); } async updateAssignee(fullName: string): Promise { await BrowserActions.click(this.assigneeButton); await BrowserActions.clearSendKeys(element(by.css('[id="userSearchText"]')), fullName); await BrowserActions.click(element(by.cssContainingText('.adf-people-full-name', fullName))); + await BrowserVisibility.waitUntilElementIsVisible(element(by.css(`adf-datatable-row[class*='is-selected']`))); + + await browser.sleep(2000); await BrowserActions.click(element(by.css('button[id="add-people"]'))); } @@ -293,7 +292,7 @@ export class TaskDetailsPage { } async checkUserIsSelected(user: string): Promise { - const row = element(by.cssContainingText('div[class*="search-list-container"] div[class*="people-full-name"]', user)); + const row = this.getRowsUser(user); await BrowserVisibility.waitUntilElementIsVisible(row); } @@ -302,7 +301,7 @@ export class TaskDetailsPage { } getRowsUser(user: string) { - return element(by.cssContainingText('div[class*="people-full-name"]', user)); + return element(by.css(`div[data-automation-id="adf-people-full-name-${user.replace(' ', '-')}"]`)); } async removeInvolvedUser(user): Promise { @@ -349,7 +348,7 @@ export class TaskDetailsPage { async getInvolvePeoplePlaceholder(): Promise { await BrowserVisibility.waitUntilElementIsVisible(this.addPeopleField); - return this.addPeopleField.getAttribute('placeholder'); + return this.addPeopleField.getAttribute('data-placeholder'); } async checkCancelButtonIsEnabled(): Promise { @@ -408,7 +407,7 @@ export class TaskDetailsPage { } async clickCompleteFormTask(): Promise { - await BrowserActions.clickScript(this.completeFormTask); + await BrowserActions.click(this.completeFormTask); } async getEmptyTaskDetailsMessage(): Promise { diff --git a/e2e/process-services/pages/task-filters.page.ts b/e2e/process-services/pages/task-filters.page.ts index 020c0238ca..1a1d96d6ef 100644 --- a/e2e/process-services/pages/task-filters.page.ts +++ b/e2e/process-services/pages/task-filters.page.ts @@ -15,13 +15,13 @@ * limitations under the License. */ -import { by, ElementFinder } from 'protractor'; +import { Locator, by, ElementFinder } from 'protractor'; import { BrowserVisibility, BrowserActions } from '@alfresco/adf-testing'; export class TaskFiltersPage { filter; - taskIcon = by.css('adf-icon[data-automation-id="adf-filter-icon"]'); + taskIcon: Locator = by.css('adf-icon[data-automation-id="adf-filter-icon"]'); constructor(filter: ElementFinder) { this.filter = filter; diff --git a/e2e/process-services/pages/tasks-list.page.ts b/e2e/process-services/pages/tasks-list.page.ts index e79f814010..fb33eb6cad 100644 --- a/e2e/process-services/pages/tasks-list.page.ts +++ b/e2e/process-services/pages/tasks-list.page.ts @@ -16,7 +16,7 @@ */ import { BrowserActions, BrowserVisibility, DataTableComponentPage } from '@alfresco/adf-testing'; -import { by, element } from 'protractor'; +import { browser, by, element } from 'protractor'; export class TasksListPage { @@ -46,6 +46,7 @@ export class TasksListPage { async selectRow(taskName: string): Promise { await this.dataTable.selectRow('Name', taskName); + await browser.sleep(1000); } getAllRowsNameColumn(): Promise { diff --git a/e2e/process-services/pages/tasks.page.ts b/e2e/process-services/pages/tasks.page.ts index ba933f4cd6..e140aca0e2 100644 --- a/e2e/process-services/pages/tasks.page.ts +++ b/e2e/process-services/pages/tasks.page.ts @@ -21,7 +21,7 @@ import { TaskDetailsPage } from './task-details.page'; import { FiltersPage } from './filters.page'; import { ChecklistDialog } from './dialog/create-checklist-dialog.page'; import { TasksListPage } from './tasks-list.page'; -import { element, by } from 'protractor'; +import { Locator, element, by } from 'protractor'; import { BrowserVisibility, BrowserActions, FormFields } from '@alfresco/adf-testing'; export class TasksPage { @@ -29,14 +29,14 @@ export class TasksPage { newTaskButton = element(by.css('button[data-automation-id="btn-start-task"]')); addChecklistButton = element(by.css('button[class*="adf-add-to-checklist-button"]')); rowByRowName = by.xpath('ancestor::mat-chip'); - checklistContainer = by.css('div[class*="checklist-menu"]'); + checklistContainer: Locator = by.css('div[class*="checklist-menu"]'); taskTitle = '.adf-activiti-task-details__header span'; - rows = by.css('div[class*="adf-datatable-body"] adf-datatable-row[class*="adf-datatable-row"] div[class*="adf-datatable-cell"]'); + rows: Locator = by.css('div[class*="adf-datatable-body"] adf-datatable-row[class*="adf-datatable-row"] div[class*="adf-datatable-cell"]'); completeButtonNoForm = element(by.id('adf-no-form-complete-button')); checklistDialog = element(by.id('checklist-dialog')); checklistNoMessage = element(by.id('checklist-none-message')); numberOfChecklists = element(by.css('[data-automation-id="checklist-label"] mat-chip')); - sortByName = by.css('div[data-automation-id="auto_id_name"]'); + sortByName: Locator = by.css('div[data-automation-id="auto_id_name"]'); async createNewTask(): Promise { await this.clickOnCreateButton(); diff --git a/e2e/process-services/people-component.e2e.ts b/e2e/process-services/people-component.e2e.ts index 95e9d01ab4..27b8861dbb 100644 --- a/e2e/process-services/people-component.e2e.ts +++ b/e2e/process-services/people-component.e2e.ts @@ -71,6 +71,10 @@ describe('People component', () => { await taskPage.filtersPage().goToFilter(CONSTANTS.TASK_FILTERS.MY_TASKS); }); + afterEach(async () => { + await navigationBarPage.clickLogoutButton(); + }); + it('[C279989] Should no people be involved when no user is typed', async () => { await taskPage.tasksListPage().checkContentIsDisplayed(tasks[0]); await taskPage.tasksListPage().selectRow(tasks[0]); diff --git a/e2e/process-services/start-process-component.e2e.ts b/e2e/process-services/start-process-component.e2e.ts index a22c4b9b7a..71634069d9 100644 --- a/e2e/process-services/start-process-component.e2e.ts +++ b/e2e/process-services/start-process-component.e2e.ts @@ -18,7 +18,7 @@ import CONSTANTS = require('../util/constants'); import { ApiService, - ApplicationsUtil, + ApplicationsUtil, BrowserActions, FileBrowserUtil, LocalStorageUtil, LoginPage, @@ -81,6 +81,7 @@ describe('Start Process Component', () => { }); describe('Provider: BPM', () => { + beforeAll(async () => { await apiService.getInstance().login(browser.params.testConfig.admin.email, browser.params.testConfig.admin.password); @@ -110,6 +111,10 @@ describe('Start Process Component', () => { await processServicesPage.checkApsContainer(); }); + afterEach(async () => { + await navigationBarPage.clickLogoutButton(); + }); + it('[C260458] Should NOT be able to start a process without process model', async () => { await processServicesPage.goToApp('Task App'); await processServiceTabBarPage.clickProcessButton(); @@ -120,15 +125,17 @@ describe('Start Process Component', () => { }); describe(' Once logged with user with app', () => { - beforeAll(async () => { - await loginPage.login(secondProcUserModel.email, secondProcUserModel.password); - }); beforeEach(async () => { + await loginPage.login(secondProcUserModel.email, secondProcUserModel.password); await navigationBarPage.navigateToProcessServicesPage(); await processServicesPage.checkApsContainer(); }); + afterEach(async () => { + await navigationBarPage.clickLogoutButton(); + }); + it('[C260441] Should display start process form and default name when creating a new process after selecting the process definition', async () => { await processServicesPage.goToApp('Task App'); await processServiceTabBarPage.clickProcessButton(); @@ -373,7 +380,6 @@ describe('Start Process Component', () => { }); it('[C260457] Should display process in Completed when cancelled', async () => { - await loginPage.login(secondProcUserModel.email, secondProcUserModel.password); await navigationBarPage.navigateToProcessServicesPage(); await processServicesPage.checkApsContainer(); await processServicesPage.goToApp(app.title); @@ -494,11 +500,9 @@ describe('Start Process Component', () => { await applicationsService.importPublishDeployApp(startProcessAttachFileApp.file_path); }); - afterAll(async () => { - await navigationBarPage.clickLogoutButton(); - }); - it('[C260490] Should be able to start a Process within ACS', async () => { + await BrowserActions.getUrl(`${browser.baseUrl}/settings`); + await LocalStorageUtil.setStorageItem('providers', 'ALL'); await loginPage.login(processUserModel.email, processUserModel.password); diff --git a/e2e/process-services/start-task-task-app.e2e.ts b/e2e/process-services/start-task-task-app.e2e.ts index 283d585a38..a27e047716 100644 --- a/e2e/process-services/start-task-task-app.e2e.ts +++ b/e2e/process-services/start-task-task-app.e2e.ts @@ -86,6 +86,7 @@ describe('Start Task - Task App', () => { await task.clickStartButton(); await taskPage.tasksListPage().checkContentIsDisplayed(tasks[0]); const taskDetails = await taskPage.taskDetails(); + await taskDetails.clickInvolvePeopleButton(); await taskDetails.typeUser(assigneeUserModel.firstName + ' ' + assigneeUserModel.lastName); await taskDetails.selectUserToInvolve(assigneeUserModel.firstName + ' ' + assigneeUserModel.lastName); diff --git a/e2e/process-services/task-assignee.e2e.ts b/e2e/process-services/task-assignee.e2e.ts index 15dca47713..c494829c74 100644 --- a/e2e/process-services/task-assignee.e2e.ts +++ b/e2e/process-services/task-assignee.e2e.ts @@ -82,6 +82,7 @@ describe('Task Assignee', () => { afterAll(async () => { await apiService.getInstance().login(browser.params.testConfig.admin.email, browser.params.testConfig.admin.password); await apiService.getInstance().activiti.adminTenantsApi.deleteTenant(user.tenantId); + await navigationBarPage.clickLogoutButton(); }); beforeEach(async () => { @@ -214,6 +215,7 @@ describe('Task Assignee', () => { await taskPage.tasksListPage().checkTaskListIsLoaded(); await taskPage.tasksListPage().checkContentIsNotDisplayed(app.userTasks.candidateTask); + await navigationBarPage.clickLogoutButton(); await loginPage.login(candidate2.email, candidate2.password); await navigationBarPage.navigateToProcessServicesPage(); await processServicesPage.checkApsContainer(); @@ -225,6 +227,7 @@ describe('Task Assignee', () => { await taskPage.tasksListPage().checkTaskListIsLoaded(); await taskPage.tasksListPage().checkContentIsNotDisplayed(app.userTasks.candidateTask); + await navigationBarPage.clickLogoutButton(); await loginPage.login(candidate1.email, candidate1.password); await navigationBarPage.navigateToProcessServicesPage(); await processServicesPage.checkApsContainer(); @@ -253,6 +256,7 @@ describe('Task Assignee', () => { await taskPage.tasksListPage().checkContentIsDisplayed(app.userTasks.candidateTask); await taskPage.taskDetails().checkClaimEnabled(); + await navigationBarPage.clickLogoutButton(); await loginPage.login(candidate2.email, candidate2.password); await navigationBarPage.navigateToProcessServicesPage(); await processServicesPage.checkApsContainer(); diff --git a/e2e/process-services/task-details-form.e2e.ts b/e2e/process-services/task-details-form.e2e.ts index 7653a75f6d..c5489ae09d 100644 --- a/e2e/process-services/task-details-form.e2e.ts +++ b/e2e/process-services/task-details-form.e2e.ts @@ -150,6 +150,7 @@ describe('Task Details - Form', () => { await taskDetailsPage.checkRemoveAttachFormIsDisplayed(); await tasksListPage.selectRow(otherTask.name); + await taskDetailsPage.checkFormIsAttached(otherAttachedForm.name); }); @@ -214,6 +215,7 @@ describe('Task Details - Form', () => { await tasksListPage.checkTaskListIsLoaded(); await tasksListPage.checkContentIsDisplayed(newTask.name); await tasksListPage.selectRow(newTask.name); + await widget.tab().checkTabIsDisplayedByLabel(tab.tabWithFields); await widget.tab().checkTabIsDisplayedByLabel(tab.tabFieldValue); }); diff --git a/e2e/process-services/task-filters-component.e2e.ts b/e2e/process-services/task-filters-component.e2e.ts index 81ebac748c..f11829de60 100644 --- a/e2e/process-services/task-filters-component.e2e.ts +++ b/e2e/process-services/task-filters-component.e2e.ts @@ -65,6 +65,7 @@ describe('Task', () => { await apiService.getInstance().activiti.modelsApi.deleteModel(appId); await apiService.getInstance().login(browser.params.testConfig.admin.email, browser.params.testConfig.admin.password); await apiService.getInstance().activiti.adminTenantsApi.deleteTenant(user.tenantId); + await navigationBarPage.clickLogoutButton(); }); it('[C279967] Should display default filters when an app is deployed', async () => { diff --git a/e2e/process-services/task-list-pagination.e2e.ts b/e2e/process-services/task-list-pagination.e2e.ts index 901b8d6dc8..a4b77c0553 100644 --- a/e2e/process-services/task-list-pagination.e2e.ts +++ b/e2e/process-services/task-list-pagination.e2e.ts @@ -96,13 +96,16 @@ describe('Task List Pagination', () => { await expect(await paginationPage.getCurrentItemsPerPage()).toEqual(itemsPerPage.five); await expect(await paginationPage.getPaginationRange()).toEqual('Showing 1-' + itemsPerPage.fiveValue + ' of ' + nrOfTasks); await expect(await taskPage.tasksListPage().getDataTable().numberOfRows()).toBe(itemsPerPage.fiveValue); + await paginationPage.clickOnNextPage(); currentPage++; + await expect(await paginationPage.getCurrentItemsPerPage()).toEqual(itemsPerPage.five); await expect(await paginationPage.getPaginationRange()).toEqual('Showing 6-' + itemsPerPage.fiveValue * currentPage + ' of ' + nrOfTasks); await expect(await taskPage.tasksListPage().getDataTable().numberOfRows()).toBe(itemsPerPage.fiveValue); await paginationPage.clickOnNextPage(); currentPage++; + await expect(await paginationPage.getCurrentItemsPerPage()).toEqual(itemsPerPage.five); await expect(await paginationPage.getPaginationRange()).toEqual('Showing 11-' + itemsPerPage.fiveValue * currentPage + ' of ' + nrOfTasks); await expect(await taskPage.tasksListPage().getDataTable().numberOfRows()).toBe(itemsPerPage.fiveValue); diff --git a/e2e/process-services/widgets/typeahead-widget.e2e.ts b/e2e/process-services/widgets/typeahead-widget.e2e.ts index dad3ee7662..b37902bafe 100644 --- a/e2e/process-services/widgets/typeahead-widget.e2e.ts +++ b/e2e/process-services/widgets/typeahead-widget.e2e.ts @@ -68,6 +68,7 @@ describe('Typeahead widget', () => { await expect(suggestions.sort()).toEqual(typeaheadWidget.case1.result.sort()); await widget.typeahedWidget().fillTypeaheadField(typeaheadWidget.case2.searchTerm); + await widget.typeahedWidget().checkDropDownListIsDisplayed(); suggestions = await widget.typeahedWidget().getDropDownList(); await expect(suggestions.sort()).toEqual(typeaheadWidget.case2.result); @@ -80,6 +81,9 @@ describe('Typeahead widget', () => { await taskPage.tasksListPage().selectRow(name); await widget.typeahedWidget().checkTypeaheadFieldIsDisplayed(); + + await browser.sleep(1000); + await expect(await widget.typeahedWidget().getFieldValue('1583773306434')).toBe(typeaheadWidget.case2.result[0]); }); }); diff --git a/e2e/protractor.conf.js b/e2e/protractor.conf.js index 4afe50310e..cf9cbfe374 100644 --- a/e2e/protractor.conf.js +++ b/e2e/protractor.conf.js @@ -176,12 +176,14 @@ exports.config = { SELENIUM_PROMISE_MANAGER: false, plugins: [{ - package: 'jasmine2-protractor-utils', - disableScreenshot: false, - screenshotOnExpectFailure: true, - screenshotOnSpecFailure: false, - clearFoldersBeforeTest: true, - screenshotPath: path.resolve(__dirname, 'e2e-output/screenshots/') + package: 'protractor-screenshoter-plugin', + screenshotPath: path.resolve(__dirname, '../e2e-output/'), + screenshotOnExpect: 'failure', + withLogs: true, + writeReportFreq: 'end', + imageToAscii: 'none', + htmlOnExpect: 'none', + htmlOnSpec: 'none' }], onCleanUp(results) { @@ -219,7 +221,7 @@ exports.config = { jasmine.getEnv().addReporter( new SpecReporter({ spec: { - displayStacktrace: true, + displayStacktrace: 'raw', displayDuration: true } }) @@ -239,10 +241,12 @@ exports.config = { await LocalStorageUtil.setStorageItem('providers', browser.params.testConfig.appConfig.provider); await LocalStorageUtil.setStorageItem('baseShareUrl', HOST); + // @ts-ignore + await LocalStorageUtil.setStorageItem('authType', browser.params.testConfig.appConfig.authType); + // @ts-ignore if (browser.params.testConfig.appConfig.authType === 'OAUTH') { - // @ts-ignore - await LocalStorageUtil.setStorageItem('authType', browser.params.testConfig.appConfig.authType); + // @ts-ignore await LocalStorageUtil.setStorageItem('identityHost', browser.params.testConfig.appConfig.identityHost); // @ts-ignore @@ -270,19 +274,19 @@ exports.config = { afterLaunch: async function () { if (SAVE_SCREENSHOT) { - console.log(`Save screenshot failures enabled`); + console.log(`Save screenshot enabled`); let retryCount = 1; if (argv.retry) { retryCount = ++argv.retry; } try { - await uploadScreenshot(retryCount); + await uploadScreenshot(retryCount, (process.env.FOLDER || '')); } catch (error) { console.error('Error saving screenshot', error); } }else{ - console.log(`Save screenshot failures disabled`); + console.log(`Save screenshot disabled`); } return retry.afterLaunch(MAX_RETRIES); diff --git a/e2e/protractor.excludes.json b/e2e/protractor.excludes.json index 595cbb08a3..7d43e283af 100644 --- a/e2e/protractor.excludes.json +++ b/e2e/protractor.excludes.json @@ -2,6 +2,36 @@ "C362240": "Include once ADF starts using ACS 7 , https://issues.alfresco.com/jira/browse/ADF-5182", "C362241": "Include once ADF starts using ACS 7, https://issues.alfresco.com/jira/browse/ADF-5182", "C362242": "Include once ADF starts using ACS 7, https://issues.alfresco.com/jira/browse/ADF-5182", - "C362265": "Include once ADF starts using ACS 7, https://issues.alfresco.com/jira/browse/ADF-5182" + "C362265": "Include once ADF starts using ACS 7, https://issues.alfresco.com/jira/browse/ADF-5182", + "C291980": "Facet not working", + "C291981": "Facet not working", + "C299200": "Facet not working", + "C297509": "Facet not working", + "C277751": "setting problem APS not basic", + "C277752": "setting problem APS not basic", + "C277753": "setting problem APS not basic", + "C260049": "login problem APS not basic", + "C260051": "login problem APS not basic", + "C279932": "login problem APS not basic", + "C279931": "login problem APS not basic", + "C279930": "login problem APS not basic", + "C282010": "Select user or Select form issue", + "C263942": "Select user or Select form issue", + "C263947": "Select user or Select form issue", + "C260383": "Select user or Select form issue", + "C260422": "Select user or Select form issue", + "C212922": "Select user or Select form issue", + "C315268": "Select user or Select form issue", + "C246522": "Select user or Select form issue", + "C299040": "Select user or Select form issue", + "C286516": "Select user or Select form issue", + "C261030": "Select user or Select form issue", + "C280013": "Select user or Select form issue", + "C261031": "Select user or Select form issue", + "C280014": "Select user or Select form issue", + "C279990": "Select user or Select form issue", + "C246534": "Attach file issue", + "C279886": "Attach file issue", + "C291893": "flaky test" } diff --git a/e2e/protractor/save-remote.js b/e2e/protractor/save-remote.js index 79084321ff..2ef0a8a6b6 100644 --- a/e2e/protractor/save-remote.js +++ b/e2e/protractor/save-remote.js @@ -13,65 +13,55 @@ function buildNumber() { return process.env.TRAVIS_BUILD_NUMBER; } -async function uploadScreenshot(retryCount) { - console.log(`Start uploading failures screenshot ${retryCount}`); +async function uploadScreenshot(retryCount, suffixFileName) { + console.log(`Start uploading report ${retryCount}`); - let files = fs.readdirSync(path.join(__dirname, '../e2e-output/screenshots')); + let alfrescoJsApi = new AlfrescoApi({ + provider: 'ECM', + hostEcm: TestConfig.screenshot.url + }); - if (files && files.length > 0) { + await alfrescoJsApi.login(TestConfig.screenshot.username, TestConfig.screenshot.password); - let alfrescoJsApi = new AlfrescoApi({ - provider: 'ECM', - hostEcm: TestConfig.screenshot.url + let folderNode; + + try { + folderNode = await alfrescoJsApi.nodes.addNode('-my-', { + 'name': `retry-${retryCount}`, + 'relativePath': `Builds/${buildNumber()}/`, + 'nodeType': 'cm:folder' + }, {}, { + 'overwrite': true + }); + } catch (error) { + folderNode = await alfrescoJsApi.nodes.getNode('-my-', { + 'relativePath': `Builds/${buildNumber()}/retry-${retryCount}`, + 'nodeType': 'cm:folder' + }, {}, { + 'overwrite': true }); - - await alfrescoJsApi.login(TestConfig.screenshot.username, TestConfig.screenshot.password); - - let folder; - - try { - folder = await alfrescoJsApi.nodes.addNode('-my-', { - 'name': `retry-${retryCount}`, - 'relativePath': `Builds/${buildNumber()}/screenshot`, - 'nodeType': 'cm:folder' - }, {}, { - 'overwrite': true - }); - } catch (error) { - folder = await alfrescoJsApi.nodes.getNode('-my-', { - 'relativePath': `Builds/${buildNumber()}/screenshot/retry-${retryCount}`, - 'nodeType': 'cm:folder' - }, {}, { - 'overwrite': true - }); - } - - for (const fileName of files) { - let pathFile = path.join(__dirname, '../e2e-output/screenshots', fileName); - let file = fs.createReadStream(pathFile); - - let safeFileName = fileName.match(/\[(.*?)\]/); - - if (safeFileName) { - const safeFileNameMatch = `${safeFileName[1]}.png`; - try { - await alfrescoJsApi.upload.uploadFile( - file, - '', - folder.entry.id, - null, - { - 'name': safeFileNameMatch, - 'nodeType': 'cm:content', - 'autoRename': true - } - ); - } catch (error) { - console.log(error); - } - } - } } + + fs.renameSync(path.resolve(__dirname, '../../e2e-output/'), path.resolve(__dirname, `../../e2e-output-${retryCount}/`)) + + const child_process = require("child_process"); + child_process.execSync(` tar -czvf ../e2e-result-${suffixFileName}-${retryCount}.tar .`, { + cwd: path.resolve(__dirname, `../../e2e-output-${retryCount}/`) + }); + + let pathFile = path.join(__dirname, `../../e2e-result-${suffixFileName}-${retryCount}.tar`); + let file = fs.createReadStream(pathFile); + await alfrescoJsApi.upload.uploadFile( + file, + '', + folderNode.entry.id, + null, + { + 'name': `e2e-result-${suffixFileName}-${retryCount}.tar`, + 'nodeType': 'cm:content', + 'autoRename': true + } + ); } module.exports = { diff --git a/e2e/resources/adf/allFileTypes/documents/other/a_eps_file.eps b/e2e/resources/adf/allFileTypes/documents/other/a_eps_file.eps deleted file mode 100644 index b89e9d2bf09a9cbb9e1faea6516b809ba05b82ec..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 307243 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 diff --git a/e2e/resources/adf/allFileTypes/documents/other/a_png_file.png b/e2e/resources/adf/allFileTypes/documents/other/a_png_file.png new file mode 100644 index 0000000000000000000000000000000000000000..e50d31fca35b6051b697dbcf5e7017cf8b9ef5b3 GIT binary patch literal 4513 zcmbW5_dC^(`^SxxP3Z8pqHM2YOUOuMuM{sbk9myjy>haSmB>7{2pQ)%Qe?*AaFBV7 zj6_!U9>u3W;QQ0}@xy)H*Yk(_dS3T+J%4#3jSMvDsM)DWNJ!|kwbY-GkdS8n+rucy z|IP3er^9~&i;><_jqUC2e@ZzM-5dvGe}5l~#g>tfY;JBYFE3*o8dg_V=jP^)kB?_& zW+o;khKGkOEG!yjBy4PKl9Q8LT3Y)1`yCw}_4V~RI5=iWNJK?N$4iR4ySrb!c+pW* z*xcMKDqgu-V*HT3X7?%p4IBp{}m(=H~YL_3H%^k}4go zpSISG6%~tvgS+$dm13fG3CK$Mdo9TLs=GW`Ys>E9qJiWjOnx3ZJ#9HOxXvC%RC%x< zE7ctyzR=sVf{fp&tHt~K;js0C)|TtpnKOJm4UrL}X3wS@8amTbE0rINtEqmlv>>>; zU|+mga&a6sHmrW(zFu0=WT3MkE&kK&=}I($5-yxVLc*w}t^V+7P~KKP{Q39$tg%Zx z)0t*G_p~csF*A)ur%Kt;L7~urhOvO6-+l@QmseMN8(P-O{Cahnkg<`2Vl%Iw`k!78 z-7&A3c=!Lpm2&R*me#GZi?)dGlRW&?@MaEJ)vz2LTsnhMEtZ)viHcXX$Jc)vHP>jQ zA4pd85bqu<&&Ju@9Ppj>*aq^ES;avQ-*fOubI1DI*LXc1kCn!EIS=s{wbN(c&9;sc zs)hm?>`}Z0clB2^Y2X4_=Kddtr{BtM=<4cj`F^xEn>hc{HHLTavi&U!msZiCT(9lD z5c4^@z?K~|8J(?h8HuT`{FviKlD(8b^Jm{K=3^^2xZLf2PT`hk_kD?2&r*v>w2e!U9JIAXe`Kl1Q$!*{==fH%QW0ETp5F>#fyZOBHk?gz_+9j#)k zizV#O2=x{G;dtL-+s2e3gWzFKEkI6u8RoTL$IX)c60>RI*15d*;B|~9$&Es86QHfz z=Rl2KU?(x)Yw|CCGT{C!G+S4FWiZKh8X3-z!=fvH%N2lhJvkJU%(Bu$2Y(OTHR^pzWYy4IB3POQKr`Q>hef;`|C%{P!8;w960mI3}IqRfcssES=Ex1&q9tqns|1ZNv z#tAA8QUD_03MNkgH0o2O6`79zEXC0u(6aJ;G4QTqldYK6aQp0+-3#%vUkn+E&YlGH zABrBtap3{=inlOiruA%-C-!%<#pS&Fq%6(@UoXQ-_F|#d9CG!zRB;6+@6arEdqP_! zgD_?MHte2mp!V|Gq0ID7a%*dA!}l^X2TMz9YwOJ|j+nyvTF#^^A#brdwp+g^GkR0| z)6H|5Dp>y0eLk#e{C2{Ue1qWke5RL2X=+;p)13eEV~VbdTs_xK3zN705#Y=w)Y;Cy z_~rKOz_z;|i}BLnn}T2|F*tje_zWV6us@>*6WAe1P3`1%NgxA07}24TLzRVcyf7 zOK5<@IArbwaeh*6Nnf{jIrt_48}0{M57f1mcFk(E2YID
  • *8Bjkyf{0dZkG9S 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 { const loginPage = new LoginPage(); const searchFiltersPage = new SearchFiltersPage(); - const searchDialog = new SearchDialogPage(); + const searchBarPage = new SearchBarPage(); const searchResults = new SearchResultsPage(); const navigationBarPage = new NavigationBarPage(); @@ -151,9 +151,9 @@ describe('Search Checklist Component', () => { } await LocalStorageUtil.setConfigField('search', JSON.stringify(jsonFile)); - await searchDialog.clickOnSearchIcon(); - await searchDialog.checkSearchBarIsVisible(); - await searchDialog.enterTextAndPressEnter(randomName); + await searchBarPage.clickOnSearchIcon(); + await searchBarPage.checkSearchBarIsVisible(); + await searchBarPage.enterTextAndPressEnter(randomName); await searchFiltersPage.clickCheckListFilter(); await expect(await searchFiltersPage.checkListFiltersPage().getCheckListOptionsNumberOnPage()).toBe(5); @@ -190,9 +190,9 @@ describe('Search Checklist Component', () => { await LocalStorageUtil.setConfigField('search', JSON.stringify(jsonFile)); - await searchDialog.clickOnSearchIcon(); - await searchDialog.checkSearchBarIsVisible(); - await searchDialog.enterTextAndPressEnter(randomName); + await searchBarPage.clickOnSearchIcon(); + await searchBarPage.checkSearchBarIsVisible(); + await searchBarPage.enterTextAndPressEnter(randomName); await searchFiltersPage.clickCheckListFilter(); await expect(await searchFiltersPage.checkListFiltersPage().getCheckListOptionsNumberOnPage()).toBe(10); @@ -204,9 +204,9 @@ describe('Search Checklist Component', () => { await LocalStorageUtil.setConfigField('search', JSON.stringify(jsonFile)); - await searchDialog.clickOnSearchIcon(); - await searchDialog.checkSearchBarIsVisible(); - await searchDialog.enterTextAndPressEnter(randomName); + await searchBarPage.clickOnSearchIcon(); + await searchBarPage.checkSearchBarIsVisible(); + await searchBarPage.enterTextAndPressEnter(randomName); await searchFiltersPage.clickCheckListFilter(); await expect(await searchFiltersPage.checkListFiltersPage().getCheckListOptionsNumberOnPage()).toBe(10); @@ -219,9 +219,9 @@ describe('Search Checklist Component', () => { await LocalStorageUtil.setConfigField('search', JSON.stringify(jsonFile)); - await searchDialog.clickOnSearchIcon(); - await searchDialog.checkSearchBarIsVisible(); - await searchDialog.enterTextAndPressEnter(randomName); + await searchBarPage.clickOnSearchIcon(); + await searchBarPage.checkSearchBarIsVisible(); + await searchBarPage.enterTextAndPressEnter(randomName); await searchFiltersPage.clickCheckListFilter(); await expect(await searchFiltersPage.checkListFiltersPage().getCheckListOptionsNumberOnPage()).toBe(9); @@ -243,9 +243,9 @@ describe('Search Checklist Component', () => { await LocalStorageUtil.setConfigField('search', JSON.stringify(jsonFile)); - await searchDialog.clickOnSearchIcon(); - await searchDialog.checkSearchBarIsVisible(); - await searchDialog.enterTextAndPressEnter(randomName); + await searchBarPage.clickOnSearchIcon(); + await searchBarPage.checkSearchBarIsVisible(); + await searchBarPage.enterTextAndPressEnter(randomName); await searchFiltersPage.clickCheckListFilter(); await expect(await searchFiltersPage.checkListFiltersPage().getCheckListOptionsNumberOnPage()).toBe(5); @@ -266,9 +266,9 @@ describe('Search Checklist Component', () => { await LocalStorageUtil.setConfigField('search', JSON.stringify(jsonFile)); - await searchDialog.clickOnSearchIcon(); - await searchDialog.checkSearchBarIsVisible(); - await searchDialog.enterTextAndPressEnter(randomName); + await searchBarPage.clickOnSearchIcon(); + await searchBarPage.checkSearchBarIsVisible(); + await searchBarPage.enterTextAndPressEnter(randomName); await searchFiltersPage.clickCheckListFilter(); await expect(await searchFiltersPage.checkListFiltersPage().getCheckListOptionsNumberOnPage()).toBe(5); @@ -303,9 +303,9 @@ describe('Search Checklist Component', () => { await LocalStorageUtil.setConfigField('search', JSON.stringify(jsonFile)); - await searchDialog.clickOnSearchIcon(); - await searchDialog.checkSearchBarIsVisible(); - await searchDialog.enterTextAndPressEnter(randomName); + await searchBarPage.clickOnSearchIcon(); + await searchBarPage.checkSearchBarIsVisible(); + await searchBarPage.enterTextAndPressEnter(randomName); await searchFiltersPage.clickCheckListFilter(); await searchFiltersPage.checkListFiltersPage().clickCheckListOption(filterType.folder); @@ -330,9 +330,9 @@ describe('Search Checklist Component', () => { await LocalStorageUtil.setConfigField('search', JSON.stringify(jsonFile)); - await searchDialog.clickOnSearchIcon(); - await searchDialog.checkSearchBarIsVisible(); - await searchDialog.enterTextAndPressEnter(randomName); + await searchBarPage.clickOnSearchIcon(); + await searchBarPage.checkSearchBarIsVisible(); + await searchBarPage.enterTextAndPressEnter(randomName); await searchFiltersPage.clickCheckListFilter(); await searchFiltersPage.checkListFiltersPage().checkCheckListOptionIsDisplayed(filterType.folder); diff --git a/e2e/search/components/search-date-range.e2e.ts b/e2e/search/components/search-date-range.e2e.ts index 309865df17..cdebfd8b4b 100644 --- a/e2e/search/components/search-date-range.e2e.ts +++ b/e2e/search/components/search-date-range.e2e.ts @@ -17,7 +17,7 @@ import { DataTableComponentPage, DatePickerCalendarPage, DateUtil, LocalStorageUtil, LoginPage } from '@alfresco/adf-testing'; import { browser, ElementFinder } from 'protractor'; -import { SearchDialogPage } from '../pages/search-dialog.page'; +import { SearchBarPage } from '../pages/search-bar.page'; import { NavigationBarPage } from '../../core/pages/navigation-bar.page'; import { SearchFiltersPage } from '../pages/search-filters.page'; import { SearchResultsPage } from '../pages/search-results.page'; @@ -26,7 +26,7 @@ import { SearchConfiguration } from '../search.config'; describe('Search Date Range Filter', () => { const loginPage = new LoginPage(); - const searchDialog = new SearchDialogPage(); + const searchBarPage = new SearchBarPage(); const searchFilters = new SearchFiltersPage(); const dateRangeFilter = searchFilters.createdDateRangeFilterPage(); const searchResults = new SearchResultsPage(); @@ -37,9 +37,9 @@ describe('Search Date Range Filter', () => { beforeAll(async () => { await loginPage.login(browser.params.testConfig.admin.email, browser.params.testConfig.admin.password); - await searchDialog.checkSearchIconIsVisible(); - await searchDialog.clickOnSearchIcon(); - await searchDialog.enterTextAndPressEnter('*'); + await searchBarPage.checkSearchIconIsVisible(); + await searchBarPage.clickOnSearchIcon(); + await searchBarPage.enterTextAndPressEnter('*'); }); beforeEach(async () => { @@ -181,8 +181,8 @@ describe('Search Date Range Filter', () => { await LocalStorageUtil.setConfigField('search', JSON.stringify(jsonFile)); - await searchDialog.clickOnSearchIcon(); - await searchDialog.enterTextAndPressEnter('*'); + await searchBarPage.clickOnSearchIcon(); + await searchBarPage.enterTextAndPressEnter('*'); await searchFilters.checkCreatedRangeFilterIsDisplayed(); await searchFilters.clickCreatedRangeFilterHeader(); await searchFilters.checkCreatedRangeFilterIsExpanded(); diff --git a/e2e/search/components/search-number-range.e2e.ts b/e2e/search/components/search-number-range.e2e.ts index 958ef6913d..ba3e9823e6 100644 --- a/e2e/search/components/search-number-range.e2e.ts +++ b/e2e/search/components/search-number-range.e2e.ts @@ -25,7 +25,7 @@ import { UserModel, UsersActions } from '@alfresco/adf-testing'; -import { SearchDialogPage } from '../pages/search-dialog.page'; +import { SearchBarPage } from '../pages/search-bar.page'; import { SearchResultsPage } from '../pages/search-results.page'; import { NavigationBarPage } from '../../core/pages/navigation-bar.page'; import { SearchFiltersPage } from '../pages/search-filters.page'; @@ -36,7 +36,7 @@ import { SearchConfiguration } from '../search.config'; describe('Search Number Range Filter', () => { const loginPage = new LoginPage(); - const searchDialog = new SearchDialogPage(); + const searchBarPage = new SearchBarPage(); const searchFilters = new SearchFiltersPage(); const sizeRangeFilter = searchFilters.sizeRangeFilterPage(); const searchResults = new SearchResultsPage(); @@ -74,9 +74,9 @@ describe('Search Number Range Filter', () => { await loginPage.login(acsUser.email, acsUser.password); - await searchDialog.checkSearchIconIsVisible(); - await searchDialog.clickOnSearchIcon(); - await searchDialog.enterTextAndPressEnter('*'); + await searchBarPage.checkSearchIconIsVisible(); + await searchBarPage.clickOnSearchIcon(); + await searchBarPage.enterTextAndPressEnter('*'); }); afterAll(async () => { @@ -375,9 +375,9 @@ describe('Search Number Range Filter', () => { await LocalStorageUtil.setConfigField('search', JSON.stringify(jsonFile)); - await searchDialog.checkSearchIconIsVisible(); - await searchDialog.clickOnSearchIcon(); - await searchDialog.enterTextAndPressEnter('*'); + await searchBarPage.checkSearchIconIsVisible(); + await searchBarPage.clickOnSearchIcon(); + await searchBarPage.enterTextAndPressEnter('*'); await searchFilters.checkSizeRangeFilterIsDisplayed(); await searchFilters.clickSizeRangeFilterHeader(); @@ -412,9 +412,9 @@ describe('Search Number Range Filter', () => { await LocalStorageUtil.setConfigField('search', JSON.stringify(jsonFile)); - await searchDialog.checkSearchIconIsVisible(); - await searchDialog.clickOnSearchIcon(); - await searchDialog.enterTextAndPressEnter('*'); + await searchBarPage.checkSearchIconIsVisible(); + await searchBarPage.clickOnSearchIcon(); + await searchBarPage.enterTextAndPressEnter('*'); await searchFilters.checkSizeRangeFilterIsDisplayed(); await searchFilters.clickSizeRangeFilterHeader(); @@ -448,9 +448,9 @@ describe('Search Number Range Filter', () => { await LocalStorageUtil.setConfigField('search', JSON.stringify(jsonFile)); - await searchDialog.checkSearchIconIsVisible(); - await searchDialog.clickOnSearchIcon(); - await searchDialog.enterTextAndPressEnter('*'); + await searchBarPage.checkSearchIconIsVisible(); + await searchBarPage.clickOnSearchIcon(); + await searchBarPage.enterTextAndPressEnter('*'); await searchFilters.checkSizeRangeFilterIsDisplayed(); await searchFilters.clickSizeRangeFilterHeader(); diff --git a/e2e/search/components/search-radio.e2e.ts b/e2e/search/components/search-radio.e2e.ts index 79273de46d..577296c32a 100644 --- a/e2e/search/components/search-radio.e2e.ts +++ b/e2e/search/components/search-radio.e2e.ts @@ -28,7 +28,7 @@ import { import { SearchFiltersPage } from '../pages/search-filters.page'; import { SearchResultsPage } from '../pages/search-results.page'; import { NavigationBarPage } from '../../core/pages/navigation-bar.page'; -import { SearchDialogPage } from '../pages/search-dialog.page'; +import { SearchBarPage } from '../pages/search-bar.page'; import { SearchConfiguration } from '../search.config'; import { browser } from 'protractor'; @@ -37,7 +37,7 @@ describe('Search Radio Component', () => { const loginPage = new LoginPage(); const searchFiltersPage = new SearchFiltersPage(); const navigationBarPage = new NavigationBarPage(); - const searchDialog = new SearchDialogPage(); + const searchBarPage = new SearchBarPage(); const searchResults = new SearchResultsPage(); const acsUser = new UserModel(); @@ -148,9 +148,9 @@ describe('Search Radio Component', () => { await LocalStorageUtil.setConfigField('search', JSON.stringify(jsonFile)); - await searchDialog.clickOnSearchIcon(); - await searchDialog.checkSearchBarIsVisible(); - await searchDialog.enterTextAndPressEnter(randomName); + await searchBarPage.clickOnSearchIcon(); + await searchBarPage.checkSearchBarIsVisible(); + await searchBarPage.enterTextAndPressEnter(randomName); await searchFiltersPage.clickTypeFilterHeader(); await expect(await searchFiltersPage.typeFiltersPage().getRadioButtonsNumberOnPage()).toBe(10); @@ -161,9 +161,9 @@ describe('Search Radio Component', () => { await LocalStorageUtil.setConfigField('search', JSON.stringify(jsonFile)); - await searchDialog.clickOnSearchIcon(); - await searchDialog.checkSearchBarIsVisible(); - await searchDialog.enterTextAndPressEnter(randomName); + await searchBarPage.clickOnSearchIcon(); + await searchBarPage.checkSearchBarIsVisible(); + await searchBarPage.enterTextAndPressEnter(randomName); await searchFiltersPage.clickTypeFilterHeader(); await expect(await searchFiltersPage.typeFiltersPage().getRadioButtonsNumberOnPage()).toBe(10); @@ -173,9 +173,9 @@ describe('Search Radio Component', () => { await LocalStorageUtil.setConfigField('search', JSON.stringify(jsonFile)); - await searchDialog.clickOnSearchIcon(); - await searchDialog.checkSearchBarIsVisible(); - await searchDialog.enterTextAndPressEnter(randomName); + await searchBarPage.clickOnSearchIcon(); + await searchBarPage.checkSearchBarIsVisible(); + await searchBarPage.enterTextAndPressEnter(randomName); await searchFiltersPage.clickTypeFilterHeader(); await expect(await searchFiltersPage.typeFiltersPage().getRadioButtonsNumberOnPage()).toBe(9); @@ -200,9 +200,9 @@ describe('Search Radio Component', () => { await LocalStorageUtil.setConfigField('search', JSON.stringify(jsonFile)); - await searchDialog.clickOnSearchIcon(); - await searchDialog.checkSearchBarIsVisible(); - await searchDialog.enterTextAndPressEnter(randomName); + await searchBarPage.clickOnSearchIcon(); + await searchBarPage.checkSearchBarIsVisible(); + await searchBarPage.enterTextAndPressEnter(randomName); await searchFiltersPage.clickTypeFilterHeader(); await expect(await searchFiltersPage.typeFiltersPage().getRadioButtonsNumberOnPage()).toBe(5); @@ -229,9 +229,9 @@ describe('Search Radio Component', () => { await LocalStorageUtil.setConfigField('search', JSON.stringify(jsonFile)); - await searchDialog.clickOnSearchIcon(); - await searchDialog.checkSearchBarIsVisible(); - await searchDialog.enterTextAndPressEnter(randomName); + await searchBarPage.clickOnSearchIcon(); + await searchBarPage.checkSearchBarIsVisible(); + await searchBarPage.enterTextAndPressEnter(randomName); await searchFiltersPage.clickTypeFilterHeader(); await expect(await searchFiltersPage.typeFiltersPage().getRadioButtonsNumberOnPage()).toBe(5); @@ -276,9 +276,9 @@ describe('Search Radio Component', () => { await LocalStorageUtil.setConfigField('search', JSON.stringify(jsonFile)); - await searchDialog.clickOnSearchIcon(); - await searchDialog.checkSearchBarIsVisible(); - await searchDialog.enterTextAndPressEnter(randomName); + await searchBarPage.clickOnSearchIcon(); + await searchBarPage.checkSearchBarIsVisible(); + await searchBarPage.enterTextAndPressEnter(randomName); await searchFiltersPage.clickTypeFilterHeader(); await searchFiltersPage.typeFiltersPage().checkFilterRadioButtonIsDisplayed(filterType.none); diff --git a/e2e/search/components/search-slider.e2e.ts b/e2e/search/components/search-slider.e2e.ts index ee24122620..c5503f6d0b 100644 --- a/e2e/search/components/search-slider.e2e.ts +++ b/e2e/search/components/search-slider.e2e.ts @@ -24,7 +24,7 @@ import { UserModel, UsersActions } from '@alfresco/adf-testing'; -import { SearchDialogPage } from '../pages/search-dialog.page'; +import { SearchBarPage } from '../pages/search-bar.page'; import { SearchResultsPage } from '../pages/search-results.page'; import { NavigationBarPage } from '../../core/pages/navigation-bar.page'; import { SearchFiltersPage } from '../pages/search-filters.page'; @@ -35,7 +35,7 @@ import { SearchConfiguration } from '../search.config'; describe('Search Slider Filter', () => { const loginPage = new LoginPage(); - const searchDialog = new SearchDialogPage(); + const searchBarPage = new SearchBarPage(); const searchFilters = new SearchFiltersPage(); const sizeSliderFilter = searchFilters.sizeSliderFilterPage(); const searchResults = new SearchResultsPage(); @@ -67,9 +67,9 @@ describe('Search Slider Filter', () => { await loginPage.login(acsUser.email, acsUser.password); - await searchDialog.checkSearchIconIsVisible(); - await searchDialog.clickOnSearchIcon(); - await searchDialog.enterTextAndPressEnter('*'); + await searchBarPage.checkSearchIconIsVisible(); + await searchBarPage.clickOnSearchIcon(); + await searchBarPage.enterTextAndPressEnter('*'); }); afterAll(async () => { @@ -162,9 +162,9 @@ describe('Search Slider Filter', () => { await LocalStorageUtil.setConfigField('search', JSON.stringify(jsonFile)); - await searchDialog.checkSearchIconIsVisible(); - await searchDialog.clickOnSearchIcon(); - await searchDialog.enterTextAndPressEnter('*'); + await searchBarPage.checkSearchIconIsVisible(); + await searchBarPage.clickOnSearchIcon(); + await searchBarPage.enterTextAndPressEnter('*'); await searchFilters.checkSizeSliderFilterIsDisplayed(); await searchFilters.clickSizeSliderFilterHeader(); @@ -179,9 +179,9 @@ describe('Search Slider Filter', () => { await LocalStorageUtil.setConfigField('search', JSON.stringify(jsonFile)); - await searchDialog.checkSearchIconIsVisible(); - await searchDialog.clickOnSearchIcon(); - await searchDialog.enterTextAndPressEnter('*'); + await searchBarPage.checkSearchIconIsVisible(); + await searchBarPage.clickOnSearchIcon(); + await searchBarPage.enterTextAndPressEnter('*'); await searchFilters.checkSizeSliderFilterIsDisplayed(); await searchFilters.clickSizeSliderFilterHeader(); @@ -198,9 +198,9 @@ describe('Search Slider Filter', () => { await LocalStorageUtil.setConfigField('search', JSON.stringify(jsonFile)); - await searchDialog.checkSearchIconIsVisible(); - await searchDialog.clickOnSearchIcon(); - await searchDialog.enterTextAndPressEnter('*'); + await searchBarPage.checkSearchIconIsVisible(); + await searchBarPage.clickOnSearchIcon(); + await searchBarPage.enterTextAndPressEnter('*'); await searchFilters.checkSizeSliderFilterIsDisplayed(); await searchFilters.clickSizeSliderFilterHeader(); @@ -217,9 +217,9 @@ describe('Search Slider Filter', () => { await LocalStorageUtil.setConfigField('search', JSON.stringify(jsonFile)); - await searchDialog.checkSearchIconIsVisible(); - await searchDialog.clickOnSearchIcon(); - await searchDialog.enterTextAndPressEnter('*'); + await searchBarPage.checkSearchIconIsVisible(); + await searchBarPage.clickOnSearchIcon(); + await searchBarPage.enterTextAndPressEnter('*'); await searchFilters.checkSizeSliderFilterIsDisplayed(); await searchFilters.clickSizeSliderFilterHeader(); diff --git a/e2e/search/components/search-sorting-picker.e2e.ts b/e2e/search/components/search-sorting-picker.e2e.ts index 82902b6b5e..ae140a2f58 100644 --- a/e2e/search/components/search-sorting-picker.e2e.ts +++ b/e2e/search/components/search-sorting-picker.e2e.ts @@ -24,7 +24,7 @@ import { UserModel, UsersActions } from '@alfresco/adf-testing'; -import { SearchDialogPage } from '../pages/search-dialog.page'; +import { SearchBarPage } from '../pages/search-bar.page'; import { SearchResultsPage } from '../pages/search-results.page'; import { NavigationBarPage } from '../../core/pages/navigation-bar.page'; import { SearchFiltersPage } from '../pages/search-filters.page'; @@ -35,7 +35,7 @@ import { SearchConfiguration } from '../search.config'; describe('Search Sorting Picker', () => { const loginPage = new LoginPage(); - const searchDialog = new SearchDialogPage(); + const searchBarPage = new SearchBarPage(); const searchFilters = new SearchFiltersPage(); const searchResults = new SearchResultsPage(); const navigationBarPage = new NavigationBarPage(); @@ -83,8 +83,8 @@ describe('Search Sorting Picker', () => { }); beforeEach(async () => { - await searchDialog.clickOnSearchIcon(); - await searchDialog.enterTextAndPressEnter(search); + await searchBarPage.clickOnSearchIcon(); + await searchBarPage.enterTextAndPressEnter(search); }); afterEach(async () => { @@ -111,9 +111,9 @@ describe('Search Sorting Picker', () => { }); await LocalStorageUtil.setConfigField('search', JSON.stringify(jsonFile)); - await searchDialog.checkSearchIconIsVisible(); - await searchDialog.clickOnSearchIcon(); - await searchDialog.enterTextAndPressEnter(search); + await searchBarPage.checkSearchIconIsVisible(); + await searchBarPage.clickOnSearchIcon(); + await searchBarPage.enterTextAndPressEnter(search); await searchSortingPicker.checkSortingDropdownIsDisplayed(); await searchSortingPicker.clickSortingDropdown(); @@ -127,9 +127,9 @@ describe('Search Sorting Picker', () => { const removedOption = jsonFile.sorting.options.splice(0, 1); await LocalStorageUtil.setConfigField('search', JSON.stringify(jsonFile)); - await searchDialog.checkSearchIconIsVisible(); - await searchDialog.clickOnSearchIcon(); - await searchDialog.enterTextAndPressEnter(search); + await searchBarPage.checkSearchIconIsVisible(); + await searchBarPage.clickOnSearchIcon(); + await searchBarPage.enterTextAndPressEnter(search); await searchSortingPicker.checkSortingDropdownIsDisplayed(); await searchSortingPicker.clickSortingDropdown(); @@ -152,9 +152,9 @@ describe('Search Sorting Picker', () => { await LocalStorageUtil.setConfigField('search', JSON.stringify(jsonFile)); - await searchDialog.checkSearchIconIsVisible(); - await searchDialog.clickOnSearchIcon(); - await searchDialog.enterTextAndPressEnter(search); + await searchBarPage.checkSearchIconIsVisible(); + await searchBarPage.clickOnSearchIcon(); + await searchBarPage.enterTextAndPressEnter(search); await searchSortingPicker.checkSortingDropdownIsDisplayed(); await searchSortingPicker.clickSortingDropdown(); @@ -203,9 +203,9 @@ describe('Search Sorting Picker', () => { }); await LocalStorageUtil.setConfigField('search', JSON.stringify(jsonFile)); - await searchDialog.checkSearchIconIsVisible(); - await searchDialog.clickOnSearchIcon(); - await searchDialog.enterTextAndPressEnter(search); + await searchBarPage.checkSearchIconIsVisible(); + await searchBarPage.clickOnSearchIcon(); + await searchBarPage.enterTextAndPressEnter(search); await searchSortingPicker.checkSortingDropdownIsDisplayed(); await searchSortingPicker.sortBy('ASC', 'Modified Date'); diff --git a/e2e/search/components/search-text.e2e.ts b/e2e/search/components/search-text.e2e.ts index 6c0c5b7819..6a83639648 100644 --- a/e2e/search/components/search-text.e2e.ts +++ b/e2e/search/components/search-text.e2e.ts @@ -27,7 +27,7 @@ import { UserModel, UsersActions } from '@alfresco/adf-testing'; -import { SearchDialogPage } from '../pages/search-dialog.page'; +import { SearchBarPage } from '../pages/search-bar.page'; import { SearchResultsPage } from '../pages/search-results.page'; import { SearchFiltersPage } from '../pages/search-filters.page'; import { NavigationBarPage } from '../../core/pages/navigation-bar.page'; @@ -40,7 +40,7 @@ describe('Search component - Text widget', () => { const searchFiltersPage = new SearchFiltersPage(); const loginPage = new LoginPage(); - const searchDialog = new SearchDialogPage(); + const searchBarPage = new SearchBarPage(); const searchResultPage = new SearchResultsPage(); const apiService = new ApiService(); @@ -101,8 +101,8 @@ describe('Search component - Text widget', () => { await navigationBarPage.clickContentServicesButton(); await LocalStorageUtil.setConfigField('search', JSON.stringify(jsonFile)); - await searchDialog.clickOnSearchIcon(); - await searchDialog.enterTextAndPressEnter('*'); + await searchBarPage.clickOnSearchIcon(); + await searchBarPage.enterTextAndPressEnter('*'); await searchResultPage.tableIsLoaded(); await searchFiltersPage.checkCheckListFilterIsDisplayed(); diff --git a/e2e/search/pages/search-dialog.page.ts b/e2e/search/pages/search-bar.page.ts similarity index 89% rename from e2e/search/pages/search-dialog.page.ts rename to e2e/search/pages/search-bar.page.ts index d3039af72a..a4590f27a3 100644 --- a/e2e/search/pages/search-dialog.page.ts +++ b/e2e/search/pages/search-bar.page.ts @@ -15,19 +15,19 @@ * limitations under the License. */ -import { browser, by, element, ElementFinder, protractor } from 'protractor'; +import { Locator, browser, by, element, ElementFinder, protractor } from 'protractor'; import { BrowserVisibility, BrowserActions } from '@alfresco/adf-testing'; -export class SearchDialogPage { +export class SearchBarPage { searchIcon = element(by.css(`button[class*='adf-search-button']`)); searchBar = element(by.css(`adf-search-control input`)); searchBarExpanded = element(by.css(`adf-search-control mat-form-field[class*="mat-focused"] input`)); noResultMessage = element(by.css(`p[class*='adf-search-fixed-text']`)); - rowsAuthor = by.css(`.mat-list-text p[class*='adf-search-fixed-text']`); - completeName = by.css(`h4[class*='adf-search-fixed-text']`); - highlightName = by.css(`.adf-highlight`); - searchDialog = element(by.css(`mat-list[id='autocomplete-search-result-list']`)); + rowsAuthor: Locator = by.css(`.mat-list-text p[class*='adf-search-fixed-text']`); + completeName: Locator = by.css(`h4[class*='adf-search-fixed-text']`); + highlightName: Locator = by.css(`.adf-highlight`); + searchBarPage = element(by.css(`mat-list[id='autocomplete-search-result-list']`)); async pressDownArrowAndEnter(): Promise { await element(by.css(`adf-search-control div input`)).sendKeys(protractor.Key.ARROW_DOWN); @@ -70,7 +70,7 @@ export class SearchDialogPage { } async resultTableContainsRow(name: string): Promise { - await BrowserVisibility.waitUntilElementIsVisible(this.searchDialog); + await BrowserVisibility.waitUntilElementIsVisible(this.searchBarPage); await BrowserVisibility.waitUntilElementIsVisible(this.getRowByRowName(name)); } diff --git a/e2e/search/search-component.e2e.ts b/e2e/search/search-component.e2e.ts index 0d6740d30e..0a4905a748 100644 --- a/e2e/search/search-component.e2e.ts +++ b/e2e/search/search-component.e2e.ts @@ -28,7 +28,7 @@ import { UsersActions, ViewerPage } from '@alfresco/adf-testing'; -import { SearchDialogPage } from './pages/search-dialog.page'; +import { SearchBarPage } from './pages/search-bar.page'; import { ContentServicesPage } from '../core/pages/content-services.page'; import { SearchResultsPage } from './pages/search-results.page'; import { FileModel } from '../models/ACS/file.model'; @@ -51,7 +51,7 @@ describe('Search component - Search Bar', () => { const contentServicesPage = new ContentServicesPage(); const navigationBarPage = new NavigationBarPage(); - const searchDialog = new SearchDialogPage(); + const searchBarPage = new SearchBarPage(); const searchResultPage = new SearchResultsPage(); const viewerPage = new ViewerPage(); @@ -126,140 +126,140 @@ describe('Search component - Search Bar', () => { }); it('[C272798] Search bar should be visible', async () => { - await searchDialog.checkSearchBarIsNotVisible(); - await searchDialog.checkSearchIconIsVisible(); + await searchBarPage.checkSearchBarIsNotVisible(); + await searchBarPage.checkSearchIconIsVisible(); - await searchDialog.clickOnSearchIcon(); - await searchDialog.checkSearchBarIsVisible(); - await searchDialog.checkSearchIconIsVisible(); + await searchBarPage.clickOnSearchIcon(); + await searchBarPage.checkSearchBarIsVisible(); + await searchBarPage.checkSearchIconIsVisible(); await BrowserActions.closeMenuAndDialogs(); - await searchDialog.checkSearchBarIsNotVisible(); - await searchDialog.checkSearchIconIsVisible(); + await searchBarPage.checkSearchBarIsNotVisible(); + await searchBarPage.checkSearchIconIsVisible(); }); it('[C272799] Should be possible to hide search bar after input', async () => { - await searchDialog.checkSearchIconIsVisible(); - await searchDialog.clickOnSearchIcon(); - await searchDialog.enterText(firstFolderModel.shortName); + await searchBarPage.checkSearchIconIsVisible(); + await searchBarPage.clickOnSearchIcon(); + await searchBarPage.enterText(firstFolderModel.shortName); }); it('[C260255] Should display message when searching for an inexistent file', async () => { - await searchDialog.checkSearchBarIsNotVisible(); - await searchDialog.clickOnSearchIcon(); - await searchDialog.checkNoResultMessageIsNotDisplayed(); - await searchDialog.enterText(search.inactive.name); - await searchDialog.checkNoResultMessageIsDisplayed(); + await searchBarPage.checkSearchBarIsNotVisible(); + await searchBarPage.clickOnSearchIcon(); + await searchBarPage.checkNoResultMessageIsNotDisplayed(); + await searchBarPage.enterText(search.inactive.name); + await searchBarPage.checkNoResultMessageIsDisplayed(); }); it('[C260256] Should display file/folder in search suggestion when typing first characters', async () => { - await searchDialog.clickOnSearchIcon(); - await searchDialog.checkSearchBarIsVisible(); - await searchDialog.enterText(firstFolderModel.shortName); + await searchBarPage.clickOnSearchIcon(); + await searchBarPage.checkSearchBarIsVisible(); + await searchBarPage.enterText(firstFolderModel.shortName); - await searchDialog.resultTableContainsRow(firstFolderModel.name); + await searchBarPage.resultTableContainsRow(firstFolderModel.name); - await expect(await searchDialog.getSpecificRowsHighlightName(firstFolderModel.name)).toEqual(firstFolderModel.shortName); - await expect(await searchDialog.getSpecificRowsAuthor(firstFolderModel.name)).toEqual(acsUser.firstName + ' ' + acsUser.lastName); - await expect(await searchDialog.getSpecificRowsCompleteName(firstFolderModel.name)).toEqual(firstFolderModel.name); + await expect(await searchBarPage.getSpecificRowsHighlightName(firstFolderModel.name)).toEqual(firstFolderModel.shortName); + await expect(await searchBarPage.getSpecificRowsAuthor(firstFolderModel.name)).toEqual(acsUser.firstName + ' ' + acsUser.lastName); + await expect(await searchBarPage.getSpecificRowsCompleteName(firstFolderModel.name)).toEqual(firstFolderModel.name); - await searchDialog.clearText(); + await searchBarPage.clearText(); - await searchDialog.clickOnSearchIcon(); - await searchDialog.enterText(firstFileModel.shortName); - await searchDialog.resultTableContainsRow(firstFileModel.name); + await searchBarPage.clickOnSearchIcon(); + await searchBarPage.enterText(firstFileModel.shortName); + await searchBarPage.resultTableContainsRow(firstFileModel.name); - await expect(await searchDialog.getSpecificRowsHighlightName(firstFileModel.name)).toEqual(firstFileModel.shortName); - await expect(await searchDialog.getSpecificRowsAuthor(firstFileModel.name)).toEqual(acsUser.firstName + ' ' + acsUser.lastName); + await expect(await searchBarPage.getSpecificRowsHighlightName(firstFileModel.name)).toEqual(firstFileModel.shortName); + await expect(await searchBarPage.getSpecificRowsAuthor(firstFileModel.name)).toEqual(acsUser.firstName + ' ' + acsUser.lastName); - await expect(await searchDialog.getSpecificRowsCompleteName(firstFileModel.name)).toEqual(firstFileModel.name); + await expect(await searchBarPage.getSpecificRowsCompleteName(firstFileModel.name)).toEqual(firstFileModel.name); }); it('[C272800] Should display file/folder in search suggestion when typing name', async () => { - await searchDialog.clickOnSearchIcon(); - await searchDialog.checkSearchBarIsVisible(); - await searchDialog.enterText(firstFolderModel.name); + await searchBarPage.clickOnSearchIcon(); + await searchBarPage.checkSearchBarIsVisible(); + await searchBarPage.enterText(firstFolderModel.name); - await searchDialog.resultTableContainsRow(firstFolderModel.name); + await searchBarPage.resultTableContainsRow(firstFolderModel.name); - await expect(await searchDialog.getSpecificRowsHighlightName(firstFolderModel.name)).toEqual(firstFolderModel.name); - await expect(await searchDialog.getSpecificRowsAuthor(firstFolderModel.name)).toEqual(acsUser.firstName + ' ' + acsUser.lastName); - await expect(await searchDialog.getSpecificRowsCompleteName(firstFolderModel.name)).toEqual(firstFolderModel.name); + await expect(await searchBarPage.getSpecificRowsHighlightName(firstFolderModel.name)).toEqual(firstFolderModel.name); + await expect(await searchBarPage.getSpecificRowsAuthor(firstFolderModel.name)).toEqual(acsUser.firstName + ' ' + acsUser.lastName); + await expect(await searchBarPage.getSpecificRowsCompleteName(firstFolderModel.name)).toEqual(firstFolderModel.name); - await searchDialog.clearText(); + await searchBarPage.clearText(); - await searchDialog.clickOnSearchIcon(); - await searchDialog.enterText(firstFileModel.name); - await searchDialog.resultTableContainsRow(firstFileModel.name); + await searchBarPage.clickOnSearchIcon(); + await searchBarPage.enterText(firstFileModel.name); + await searchBarPage.resultTableContainsRow(firstFileModel.name); - await expect(await searchDialog.getSpecificRowsHighlightName(firstFileModel.name)).toEqual(firstFileModel.name); - await expect(await searchDialog.getSpecificRowsAuthor(firstFileModel.name)).toEqual(acsUser.firstName + ' ' + acsUser.lastName); - await expect(await searchDialog.getSpecificRowsCompleteName(firstFileModel.name)).toEqual(firstFileModel.name); + await expect(await searchBarPage.getSpecificRowsHighlightName(firstFileModel.name)).toEqual(firstFileModel.name); + await expect(await searchBarPage.getSpecificRowsAuthor(firstFileModel.name)).toEqual(acsUser.firstName + ' ' + acsUser.lastName); + await expect(await searchBarPage.getSpecificRowsCompleteName(firstFileModel.name)).toEqual(firstFileModel.name); }); it('[C260257] Should display content when clicking on folder from search suggestions', async () => { - await searchDialog.clickOnSearchIcon(); - await searchDialog.checkSearchBarIsVisible(); - await searchDialog.enterText(firstFolderModel.shortName); + await searchBarPage.clickOnSearchIcon(); + await searchBarPage.checkSearchBarIsVisible(); + await searchBarPage.enterText(firstFolderModel.shortName); - await searchDialog.resultTableContainsRow(firstFolderModel.name); - await searchDialog.clickOnSpecificRow(firstFolderModel.name); + await searchBarPage.resultTableContainsRow(firstFolderModel.name); + await searchBarPage.clickOnSpecificRow(firstFolderModel.name); await expect(await contentServicesPage.currentFolderName()).toEqual(firstFolderModel.name); - await searchDialog.checkSearchIconIsVisible(); - await searchDialog.clickOnSearchIcon(); - await searchDialog.checkSearchBarIsVisible(); - await searchDialog.enterText(firstFileModel.name); - await searchDialog.resultTableContainsRow(firstFileModel.name); + await searchBarPage.checkSearchIconIsVisible(); + await searchBarPage.clickOnSearchIcon(); + await searchBarPage.checkSearchBarIsVisible(); + await searchBarPage.enterText(firstFileModel.name); + await searchBarPage.resultTableContainsRow(firstFileModel.name); - await searchDialog.clickOnSpecificRow(firstFileModel.name); + await searchBarPage.clickOnSpecificRow(firstFileModel.name); await expect(await viewerPage.getDisplayedFileName()).toEqual(firstFileModel.name); await viewerPage.clickCloseButton(); }); it('[C272801] Should display message when searching for non-existent folder', async () => { - await searchDialog.checkSearchIconIsVisible(); - await searchDialog.clickOnSearchIcon(); - await searchDialog.checkSearchBarIsVisible(); - await searchDialog.enterTextAndPressEnter(search.inactive.name); + await searchBarPage.checkSearchIconIsVisible(); + await searchBarPage.clickOnSearchIcon(); + await searchBarPage.checkSearchBarIsVisible(); + await searchBarPage.enterTextAndPressEnter(search.inactive.name); await searchResultPage.checkNoResultMessageIsDisplayed(); }); it('[C272802] Should be able to find an existent folder in search results', async () => { - await searchDialog.checkSearchIconIsVisible(); - await searchDialog.clickOnSearchIcon(); - await searchDialog.checkSearchBarIsVisible(); - await searchDialog.enterTextAndPressEnter(firstFolderModel.name); + await searchBarPage.checkSearchIconIsVisible(); + await searchBarPage.clickOnSearchIcon(); + await searchBarPage.checkSearchBarIsVisible(); + await searchBarPage.enterTextAndPressEnter(firstFolderModel.name); await searchResultPage.checkContentIsDisplayed(firstFolderModel.name); }); it('[C260258] Should be able to find an existent file in search results', async () => { - await searchDialog.checkSearchIconIsVisible(); - await searchDialog.clickOnSearchIcon(); - await searchDialog.checkSearchBarIsVisible(); - await searchDialog.enterTextAndPressEnter(firstFileModel.name); + await searchBarPage.checkSearchIconIsVisible(); + await searchBarPage.clickOnSearchIcon(); + await searchBarPage.checkSearchBarIsVisible(); + await searchBarPage.enterTextAndPressEnter(firstFileModel.name); await searchResultPage.checkContentIsDisplayed(firstFileModel.name); }); it('[C91321] Should be able to use down arrow key when navigating throw suggestions', async () => { - await searchDialog.checkSearchIconIsVisible(); - await searchDialog.clickOnSearchIcon(); - await searchDialog.checkSearchBarIsVisible(); - await searchDialog.enterText(secondFolder.shortName); - await searchDialog.pressDownArrowAndEnter(); + await searchBarPage.checkSearchIconIsVisible(); + await searchBarPage.clickOnSearchIcon(); + await searchBarPage.checkSearchBarIsVisible(); + await searchBarPage.enterText(secondFolder.shortName); + await searchBarPage.pressDownArrowAndEnter(); await expect(await contentServicesPage.currentFolderName()).toEqual(secondFolder.name); }); it('[C290137] Should be able to search by \'%\'', async () => { - await searchDialog.clickOnSearchIcon(); - await searchDialog.enterTextAndPressEnter('%'); + await searchBarPage.clickOnSearchIcon(); + await searchBarPage.enterTextAndPressEnter('%'); await searchResultPage.tableIsLoaded(); }); @@ -271,10 +271,10 @@ describe('Search component - Search Bar', () => { await LocalStorageUtil.setConfigField('search', JSON.stringify(searchConfiguration)); - await searchDialog.checkSearchIconIsVisible(); - await searchDialog.clickOnSearchIcon(); - await searchDialog.checkSearchBarIsVisible(); - await searchDialog.enterTextAndPressEnter(term); + await searchBarPage.checkSearchIconIsVisible(); + await searchBarPage.clickOnSearchIcon(); + await searchBarPage.checkSearchBarIsVisible(); + await searchBarPage.enterTextAndPressEnter(term); }); it('[C299212] Should be able to configure the highlight option for search results', async () => { diff --git a/e2e/search/search-filters.e2e.ts b/e2e/search/search-filters.e2e.ts index cc0db2d910..3b5fde1bf0 100644 --- a/e2e/search/search-filters.e2e.ts +++ b/e2e/search/search-filters.e2e.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { SearchDialogPage } from './pages/search-dialog.page'; +import { SearchBarPage } from './pages/search-bar.page'; import { SearchFiltersPage } from './pages/search-filters.page'; import { SearchResultsPage } from './pages/search-results.page'; import { FileModel } from '../models/ACS/file.model'; @@ -38,7 +38,7 @@ import { SearchConfiguration } from './search.config'; describe('Search Filters', () => { const loginPage = new LoginPage(); - const searchDialog = new SearchDialogPage(); + const searchBarPage = new SearchBarPage(); const searchFiltersPage = new SearchFiltersPage(); const paginationPage = new PaginationPage(); const contentList = new DocumentListPage(); @@ -119,10 +119,10 @@ describe('Search Filters', () => { }); it('[C286298] Should be able to cancel a filter using "x" button from the toolbar', async () => { - await searchDialog.checkSearchIconIsVisible(); - await searchDialog.clickOnSearchIcon(); + await searchBarPage.checkSearchIconIsVisible(); + await searchBarPage.clickOnSearchIcon(); - await searchDialog.enterTextAndPressEnter(fileUploaded.entry.name); + await searchBarPage.enterTextAndPressEnter(fileUploaded.entry.name); await searchFiltersPage.checkSearchFiltersIsDisplayed(); @@ -181,8 +181,8 @@ describe('Search Filters', () => { jsonFile['filterWithContains'] = true; await LocalStorageUtil.setConfigField('search', JSON.stringify(jsonFile)); - await searchDialog.clickOnSearchIcon(); - await searchDialog.enterTextAndPressEnter('*'); + await searchBarPage.clickOnSearchIcon(); + await searchBarPage.enterTextAndPressEnter('*'); await searchResults.tableIsLoaded(); @@ -191,7 +191,10 @@ describe('Search Filters', () => { }); it('[C291980] Should group search facets under specified labels', async () => { - await BrowserActions.getUrl(`${browser.baseUrl}/search;q=*`); + await LocalStorageUtil.setConfigField('search', JSON.stringify(jsonFile)); + + await searchBarPage.clickOnSearchIcon(); + await searchBarPage.enterTextAndPressEnter('*'); await searchFiltersPage.checkDefaultFacetQueryGroupIsDisplayed(); await searchFiltersPage.checkTypeFacetQueryGroupIsDisplayed(); @@ -203,8 +206,8 @@ describe('Search Filters', () => { await LocalStorageUtil.setConfigField('search', JSON.stringify(jsonFile)); - await searchDialog.clickOnSearchIcon(); - await searchDialog.enterTextAndPressEnter('*'); + await searchBarPage.clickOnSearchIcon(); + await searchBarPage.enterTextAndPressEnter('*'); await searchResults.tableIsLoaded(); @@ -214,7 +217,9 @@ describe('Search Filters', () => { }); it('[C297509] Should display search intervals under specified labels from config', async () => { - await BrowserActions.getUrl(`${browser.baseUrl}/search;q=*`); + await LocalStorageUtil.setConfigField('search', JSON.stringify(jsonFile)); + await searchBarPage.clickOnSearchIcon(); + await searchBarPage.enterTextAndPressEnter('*'); await searchFiltersPage.checkFacetIntervalsByCreatedIsDisplayed(); await searchFiltersPage.checkFacetIntervalsByCreatedIsExpanded(); @@ -232,9 +237,9 @@ describe('Search Filters', () => { it('[C299200] Should reset the filters facet with search query', async () => { await navigationBarPage.clickContentServicesButton(); - await searchDialog.checkSearchIconIsVisible(); - await searchDialog.clickOnSearchIcon(); - await searchDialog.enterTextAndPressEnter(fileTypeTxt1.entry.name); + await searchBarPage.checkSearchIconIsVisible(); + await searchBarPage.clickOnSearchIcon(); + await searchBarPage.enterTextAndPressEnter(fileTypeTxt1.entry.name); await searchFiltersPage.checkSearchFiltersIsDisplayed(); await searchResults.tableIsLoaded(); @@ -242,10 +247,10 @@ describe('Search Filters', () => { await searchFiltersPage.checkFileTypeFacetLabelIsDisplayed('Plain Text (1)'); await searchFiltersPage.checkFileTypeFacetLabelIsNotDisplayed('JPEG Image'); - await searchDialog.checkSearchIconIsVisible(); - await searchDialog.clickOnSearchIcon(); + await searchBarPage.checkSearchIconIsVisible(); + await searchBarPage.clickOnSearchIcon(); - await searchDialog.enterTextAndPressEnter(fileNamePrefix); + await searchBarPage.enterTextAndPressEnter(fileNamePrefix); await searchFiltersPage.checkSearchFiltersIsDisplayed(); await searchResults.tableIsLoaded(); await searchResults.checkContentIsDisplayed(fileTypeTxt1.entry.name); @@ -262,8 +267,8 @@ describe('Search Filters', () => { jsonFile.facetFields.fields[1].label = 'My File Sizes'; await LocalStorageUtil.setConfigField('search', JSON.stringify(jsonFile)); - await searchDialog.clickOnSearchIcon(); - await searchDialog.enterTextAndPressEnter('*'); + await searchBarPage.clickOnSearchIcon(); + await searchBarPage.enterTextAndPressEnter('*'); await searchResults.tableIsLoaded(); await searchFiltersPage.checkCustomFacetFieldLabelIsDisplayed('My File Types'); diff --git a/e2e/search/search-multiselect.e2e.ts b/e2e/search/search-multiselect.e2e.ts index 4c4d12ca2c..b8dde80762 100644 --- a/e2e/search/search-multiselect.e2e.ts +++ b/e2e/search/search-multiselect.e2e.ts @@ -17,7 +17,7 @@ import { ApiService, LoginPage, StringUtil, UploadActions, UserModel, UsersActions } from '@alfresco/adf-testing'; import { browser } from 'protractor'; -import { SearchDialogPage } from './pages/search-dialog.page'; +import { SearchBarPage } from './pages/search-bar.page'; import { SearchResultsPage } from './pages/search-results.page'; import { SearchFiltersPage } from './pages/search-filters.page'; import { FileModel } from '../models/ACS/file.model'; @@ -26,7 +26,7 @@ import CONSTANTS = require('../util/constants'); describe('Search Component - Multi-Select Facet', () => { const loginPage = new LoginPage(); - const searchDialog = new SearchDialogPage(); + const searchBarPage = new SearchBarPage(); const searchResultsPage = new SearchResultsPage(); const searchFiltersPage = new SearchFiltersPage(); const navigationBarPage = new NavigationBarPage(); @@ -75,9 +75,9 @@ describe('Search Component - Multi-Select Facet', () => { await loginPage.login(acsUser.email, acsUser.password); - await searchDialog.checkSearchIconIsVisible(); - await searchDialog.clickOnSearchIcon(); - await searchDialog.enterTextAndPressEnter(`${randomName}`); + await searchBarPage.checkSearchIconIsVisible(); + await searchBarPage.clickOnSearchIcon(); + await searchBarPage.enterTextAndPressEnter(`${randomName}`); userOption = `${acsUser.firstName} ${acsUser.lastName}`; @@ -100,9 +100,9 @@ describe('Search Component - Multi-Select Facet', () => { it('[C280054] Should be able to select multiple items from a search facet filter', async () => { await loginPage.login(acsUser.email, acsUser.password); - await searchDialog.checkSearchIconIsVisible(); - await searchDialog.clickOnSearchIcon(); - await searchDialog.enterTextAndPressEnter(`${randomName}`); + await searchBarPage.checkSearchIconIsVisible(); + await searchBarPage.clickOnSearchIcon(); + await searchBarPage.enterTextAndPressEnter(`${randomName}`); userOption = `${acsUser.firstName} ${acsUser.lastName}`; @@ -167,9 +167,9 @@ describe('Search Component - Multi-Select Facet', () => { await loginPage.login(userUploadingImg.email, userUploadingImg.password); - await searchDialog.checkSearchIconIsVisible(); - await searchDialog.clickOnSearchIcon(); - await searchDialog.enterTextAndPressEnter(`*${randomName}*`); + await searchBarPage.checkSearchIconIsVisible(); + await searchBarPage.clickOnSearchIcon(); + await searchBarPage.enterTextAndPressEnter(`*${randomName}*`); await searchFiltersPage.checkSearchFiltersIsDisplayed(); await searchFiltersPage.creatorCheckListFiltersPage().filterBy(`${userUploadingTxt.firstName} ${userUploadingTxt.lastName}`); @@ -214,9 +214,9 @@ describe('Search Component - Multi-Select Facet', () => { await loginPage.login(acsUser.email, acsUser.password); - await searchDialog.checkSearchIconIsVisible(); - await searchDialog.clickOnSearchIcon(); - await searchDialog.enterTextAndPressEnter(`*${randomName}*`); + await searchBarPage.checkSearchIconIsVisible(); + await searchBarPage.clickOnSearchIcon(); + await searchBarPage.enterTextAndPressEnter(`*${randomName}*`); await searchFiltersPage.checkSearchFiltersIsDisplayed(); }); @@ -229,9 +229,9 @@ describe('Search Component - Multi-Select Facet', () => { it('[C280058] Should update filter facets items number when another filter facet item is selected', async () => { await loginPage.login(acsUser.email, acsUser.password); - await searchDialog.checkSearchIconIsVisible(); - await searchDialog.clickOnSearchIcon(); - await searchDialog.enterTextAndPressEnter(`*${randomName}*`); + await searchBarPage.checkSearchIconIsVisible(); + await searchBarPage.clickOnSearchIcon(); + await searchBarPage.enterTextAndPressEnter(`*${randomName}*`); await searchFiltersPage.checkSearchFiltersIsDisplayed(); await searchFiltersPage.fileTypeCheckListFiltersPage().filterBy('Plain Text'); diff --git a/e2e/search/search-page.e2e.ts b/e2e/search/search-page.e2e.ts index 66d945f35f..a019ee558a 100644 --- a/e2e/search/search-page.e2e.ts +++ b/e2e/search/search-page.e2e.ts @@ -19,7 +19,7 @@ import { browser } from 'protractor'; import { ApiService, LoginPage, StringUtil, UploadActions, UserModel, UsersActions } from '@alfresco/adf-testing'; -import { SearchDialogPage } from './pages/search-dialog.page'; +import { SearchBarPage } from './pages/search-bar.page'; import { ContentServicesPage } from '../core/pages/content-services.page'; import { SearchResultsPage } from './pages/search-results.page'; import { FolderModel } from '../models/ACS/folder.model'; @@ -42,7 +42,7 @@ describe('Search component - Search Page', () => { const loginPage = new LoginPage(); const contentServicesPage = new ContentServicesPage(); - const searchDialog = new SearchDialogPage(); + const searchBarPage = new SearchBarPage(); const searchResultPage = new SearchResultsPage(); const navigationBarPage = new NavigationBarPage(); @@ -97,24 +97,24 @@ describe('Search component - Search Page', () => { it('[C260264] Should display message when no results are found', async() => { const notExistentFileName = StringUtil.generateRandomString(); - await searchDialog.checkSearchBarIsNotVisible(); - await searchDialog.checkSearchIconIsVisible(); - await searchDialog.clickOnSearchIcon(); - await searchDialog.enterTextAndPressEnter(notExistentFileName); + await searchBarPage.checkSearchBarIsNotVisible(); + await searchBarPage.checkSearchIconIsVisible(); + await searchBarPage.clickOnSearchIcon(); + await searchBarPage.enterTextAndPressEnter(notExistentFileName); await searchResultPage.checkNoResultMessageIsDisplayed(); }); it('[C272810] Should display only files corresponding to search', async () => { - await searchDialog.clickOnSearchIcon(); - await searchDialog.enterTextAndPressEnter(search.active.firstFile); + await searchBarPage.clickOnSearchIcon(); + await searchBarPage.enterTextAndPressEnter(search.active.firstFile); await searchResultPage.checkContentIsDisplayed(search.active.firstFile); await expect(await searchResultPage.numberOfResultsDisplayed()).toBe(1); }); it('[C260267] Should display content when opening a folder from search results', async () => { - await searchDialog.clickOnSearchIcon(); - await searchDialog.enterTextAndPressEnter(emptyFolderModel.name); + await searchBarPage.clickOnSearchIcon(); + await searchBarPage.enterTextAndPressEnter(emptyFolderModel.name); await searchResultPage.checkNoResultMessageIsNotDisplayed(); await searchResultPage.checkContentIsDisplayed(emptyFolderModel.name); @@ -124,8 +124,8 @@ describe('Search component - Search Page', () => { }); it('[C260261] Should be able to delete a file from search results', async () => { - await searchDialog.clickOnSearchIcon(); - await searchDialog.enterTextAndPressEnter(search.active.firstFile); + await searchBarPage.clickOnSearchIcon(); + await searchBarPage.enterTextAndPressEnter(search.active.firstFile); await searchResultPage.checkContentIsDisplayed(search.active.firstFile); @@ -134,16 +134,16 @@ describe('Search component - Search Page', () => { await searchResultPage.checkNoResultMessageIsDisplayed(); await searchResultPage.checkContentIsNotDisplayed(search.active.firstFile); - await searchDialog.checkSearchBarIsNotVisible(); - await searchDialog.checkSearchIconIsVisible(); - await searchDialog.clickOnSearchIcon(); - await searchDialog.enterTextAndPressEnter(search.active.firstFile); + await searchBarPage.checkSearchBarIsNotVisible(); + await searchBarPage.checkSearchIconIsVisible(); + await searchBarPage.clickOnSearchIcon(); + await searchBarPage.enterTextAndPressEnter(search.active.firstFile); await searchResultPage.checkNoResultMessageIsDisplayed(); }); it('[C272809] Should be able to delete a folder from search results', async () => { - await searchDialog.clickOnSearchIcon(); - await searchDialog.enterTextAndPressEnter(emptyFolderModel.name); + await searchBarPage.clickOnSearchIcon(); + await searchBarPage.enterTextAndPressEnter(emptyFolderModel.name); await searchResultPage.checkContentIsDisplayed(emptyFolderModel.name); await searchResultPage.checkNoResultMessageIsNotDisplayed(); @@ -151,16 +151,16 @@ describe('Search component - Search Page', () => { await searchResultPage.deleteContent(emptyFolderModel.name); await searchResultPage.checkNoResultMessageIsDisplayed(); - await searchDialog.checkSearchBarIsNotVisible(); - await searchDialog.checkSearchIconIsVisible(); - await searchDialog.clickOnSearchIcon(); - await searchDialog.enterTextAndPressEnter(emptyFolderModel.name); + await searchBarPage.checkSearchBarIsNotVisible(); + await searchBarPage.checkSearchIconIsVisible(); + await searchBarPage.clickOnSearchIcon(); + await searchBarPage.enterTextAndPressEnter(emptyFolderModel.name); await searchResultPage.checkNoResultMessageIsDisplayed(); }); it('[C286675] Should display results when searching for all elements', async () => { - await searchDialog.clickOnSearchIcon(); - await searchDialog.enterTextAndPressEnter('*'); + await searchBarPage.clickOnSearchIcon(); + await searchBarPage.enterTextAndPressEnter('*'); await searchResultPage.checkNoResultMessageIsNotDisplayed(); }); diff --git a/e2e/test.config.js b/e2e/test.config.js index f378eae227..a8f4ed2a24 100644 --- a/e2e/test.config.js +++ b/e2e/test.config.js @@ -15,7 +15,7 @@ const HOST_BPM = process.env.PROXY_HOST_ADF || HOST || 'bpm'; const PROVIDER = process.env.PROVIDER ? process.env.PROVIDER : 'ALL'; const AUTH_TYPE = process.env.AUTH_TYPE ? process.env.AUTH_TYPE : 'BASIC'; -const OAUTH_HOST = process.env.HOST_SSO || process.env.PROXY_HOST_ADF || HOST || 'oauth'; +const HOST_SSO = process.env.HOST_SSO || process.env.PROXY_HOST_ADF || HOST || 'oauth'; const OAUTH_CLIENT_ID = process.env.OAUTH_CLIENDID || 'alfresco'; const IDENTITY_ADMIN_EMAIL = process.env.IDENTITY_ADMIN_EMAIL || "defaultadmin"; @@ -30,10 +30,6 @@ const PASSWORD_ADF = process.env.PASSWORD_ADF || "defaultuserpassword"; const REDIRECT_URI = process.env.REDIRECT_URI || "/"; const REDIRECT_URI_LOGOUT = process.env.REDIRECT_URI_LOGOUT || "/logout"; -const SCREENSHOT_URL = process.env.SCREENSHOT_URL || HOST; -const SCREENSHOT_PASSWORD = process.env.SCREENSHOT_PASSWORD || process.env.PASSWORD_ADF; -const SCREENSHOT_USERNAME = process.env.SCREENSHOT_USERNAME || process.env.USERNAME_ADF; - const EXTERNAL_ACS_HOST = process.env.EXTERNAL_ACS_HOST; const LOG_LEVEL = process.env.LOG_LEVEL || 'ERROR'; @@ -41,11 +37,11 @@ const appConfig = { "log": LOG_LEVEL, "ecmHost": HOST_ECM, "bpmHost": HOST_BPM, - "identityHost": `${OAUTH_HOST}/auth/admin/realms/alfresco`, + "identityHost": `${HOST_SSO}/auth/admin/realms/alfresco`, "provider": PROVIDER, "authType": AUTH_TYPE, "oauth2": { - "host": `${OAUTH_HOST}/auth/realms/alfresco`, + "host": `${HOST_SSO}/auth/realms/alfresco`, "clientId": OAUTH_CLIENT_ID, "scope": "openid", "secret": "", @@ -98,9 +94,9 @@ module.exports = { }, screenshot: { - url: SCREENSHOT_URL, - password: SCREENSHOT_PASSWORD, - username: SCREENSHOT_USERNAME + url: HOST_ECM, + username: USERNAME_ADF, + password: PASSWORD_ADF }, adf_external_acs: { diff --git a/e2e/util/resources.js b/e2e/util/resources.js index 8c20440670..a679ec2fe7 100644 --- a/e2e/util/resources.js +++ b/e2e/util/resources.js @@ -676,9 +676,9 @@ exports.Files = { folder_name: "images-rendition" }, ADF_FOLDER: { - folder_location: "/resources/adf", - folder_path: path.join(__dirname, '../resources/adf'), - folder_name: "adf" + folder_location: "/resources/adf/allFileTypes/images", + folder_path: path.join(__dirname, '../resources/adf/allFileTypes/images'), + folder_name: "images" }, }, diff --git a/lib/cli/scripts/check-cs-env.ts b/lib/cli/scripts/check-cs-env.ts index 595de82a63..8b6faac67b 100755 --- a/lib/cli/scripts/check-cs-env.ts +++ b/lib/cli/scripts/check-cs-env.ts @@ -31,14 +31,14 @@ async function checkEnv() { }); await alfrescoJsApi.login(program.username, program.password); - } catch (e) { + } catch (error) { console.log('Login error environment down or inaccessible'); counter++; if (MAX_RETRY === counter) { console.log('Give up'); process.exit(1); } else { - console.log(`Retry in 1 minute attempt N ${counter}`); + console.log(`Retry in 1 minute attempt N ${counter}`, error); sleep(TIMEOUT); checkEnv(); } @@ -74,8 +74,8 @@ async function checkDiskSpaceFullEnv() { 'overwrite': true }); } + let pathFile = path.join(__dirname, '../', 'README.md'); - let pathFile = path.join(__dirname, '../../', 'README.md'); let file = fs.createReadStream(pathFile); let uploadedFile = await alfrescoJsApi.upload.uploadFile( @@ -94,6 +94,8 @@ async function checkDiskSpaceFullEnv() { } catch (error) { counter++; + console.log('error', error); + if (MAX_RETRY === counter) { console.log('============================================================='); console.log('================ Not able to upload a file =================='); @@ -101,7 +103,7 @@ async function checkDiskSpaceFullEnv() { console.log('============================================================='); process.exit(1); } else { - console.log(`Retry in 1 minute attempt N ${counter}`); + console.log(`Retry in 1 minute attempt N ${counter}`, error); sleep(TIMEOUT); checkDiskSpaceFullEnv(); } diff --git a/lib/cli/scripts/init-aae-env.ts b/lib/cli/scripts/init-aae-env.ts index aeb1be61df..f964183def 100644 --- a/lib/cli/scripts/init-aae-env.ts +++ b/lib/cli/scripts/init-aae-env.ts @@ -65,20 +65,24 @@ async function healthCheck(nameService: string) { logger.info(`${nameService} is UP!`); } } catch (error) { - logger.error(`${nameService} is not reachable ${error.status} `); + logger.error(`${nameService} is not reachable error: `, error); isValid = false; } } -function getApplicationByStatus(status: string) { +async function getApplicationByStatus(status: string) { const url = `${args.host}/deployment-service/v1/applications/`; const pathParams = {}, queryParams = { status: status }, headerParams = {}, formParams = {}, bodyParam = {}, contentTypes = ['application/json'], accepts = ['application/json']; try { + await alfrescoJsApiDevops.login(args.devopsUsername, args.devopsPassword); + return alfrescoJsApiDevops.oauth2Auth.callCustomApi(url, 'GET', pathParams, queryParams, headerParams, formParams, bodyParam, - contentTypes, accepts); + contentTypes, accepts).on('error',(error)=>{ + logger.error(`Get application by status ${error} `); + }); } catch (error) { logger.error(`Get application by status ${error.status} `); @@ -203,6 +207,7 @@ function deploy(model: any) { function getAlfrescoJsApiInstance(configArgs: ConfigArgs) { const config = { provider: 'BPM', + hostEcm: `${configArgs.host}`, hostBpm: `${configArgs.host}`, authType: 'OAUTH', oauth2: { @@ -395,6 +400,11 @@ async function main(configArgs: ConfigArgs) { } alfrescoJsApiModeler = getAlfrescoJsApiInstance(args); + + AAE_MICROSERVICES.map(async (serviceName) => { + await healthCheck(serviceName); + }); + await alfrescoJsApiModeler.login(args.modelerUsername, args.modelerPassword).then(() => { logger.info('login SSO ok'); }, (error) => { @@ -402,10 +412,6 @@ async function main(configArgs: ConfigArgs) { process.exit(1); }); - AAE_MICROSERVICES.map(async (serviceName) => { - await healthCheck(serviceName); - }); - if (isValid) { logger.error('The environment is up and running'); alfrescoJsApiDevops = getAlfrescoJsApiInstance(args); diff --git a/lib/process-services/src/lib/people/components/people-search-field/people-search-field.component.html b/lib/process-services/src/lib/people/components/people-search-field/people-search-field.component.html index 19378a98eb..fcdd0c4b06 100644 --- a/lib/process-services/src/lib/people/components/people-search-field/people-search-field.component.html +++ b/lib/process-services/src/lib/people/components/people-search-field/people-search-field.component.html @@ -24,7 +24,7 @@ -
    {{ getDisplayUser(entry.row.obj.firstName, entry.row.obj.lastName, ' ') }}
    +
    {{ getDisplayUser(entry.row.obj.firstName, entry.row.obj.lastName, ' ') }}
    diff --git a/lib/process-services/src/lib/people/components/people/people.component.html b/lib/process-services/src/lib/people/components/people/people.component.html index 16dd1549ea..ea99617edb 100644 --- a/lib/process-services/src/lib/people/components/people/people.component.html +++ b/lib/process-services/src/lib/people/components/people/people.component.html @@ -41,8 +41,8 @@ diff --git a/lib/testing/ng-package.json b/lib/testing/ng-package.json index 3c7e87dc7c..72cc5b7dd8 100644 --- a/lib/testing/ng-package.json +++ b/lib/testing/ng-package.json @@ -8,7 +8,6 @@ "@alfresco/js-api": "@alfresco/js-api", "moment-es6": "moment-es6", "protractor": "protractor", - "selenium-webdriver/remote": "selenium-webdriver/remote", "selenium-webdriver": "selenium-webdriver", "fs": "fs", "path": "path" diff --git a/lib/testing/src/lib/content-services/dialog/content-node-selector-dialog.page.ts b/lib/testing/src/lib/content-services/dialog/content-node-selector-dialog.page.ts index 8f0310206a..dd538c74fb 100644 --- a/lib/testing/src/lib/content-services/dialog/content-node-selector-dialog.page.ts +++ b/lib/testing/src/lib/content-services/dialog/content-node-selector-dialog.page.ts @@ -15,8 +15,7 @@ * limitations under the License. */ -import { by, element, browser } from 'protractor'; -import * as remote from 'selenium-webdriver/remote'; +import { by, element } from 'protractor'; import { DocumentListPage } from '../pages/document-list.page'; import { BrowserVisibility } from '../../core/utils/browser-visibility'; import { BrowserActions } from '../../core/utils/browser-actions'; @@ -137,7 +136,6 @@ export class ContentNodeSelectorDialogPage { await this.dataTable.waitForTableBody(); await this.breadcrumbDropdownPage.checkCurrentFolderIsDisplayed(); - await browser.setFileDetector(new remote.FileDetector()); const uploadButton = element(by.css('adf-upload-button input')); await BrowserVisibility.waitUntilElementIsPresent(uploadButton); await uploadButton.sendKeys(fileLocation); diff --git a/lib/testing/src/lib/content-services/pages/document-list.page.ts b/lib/testing/src/lib/content-services/pages/document-list.page.ts index a2904a40e1..284d213066 100644 --- a/lib/testing/src/lib/content-services/pages/document-list.page.ts +++ b/lib/testing/src/lib/content-services/pages/document-list.page.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { by, element, ElementFinder, browser } from 'protractor'; +import { Locator, by, element, ElementFinder, browser } from 'protractor'; import { DataTableComponentPage } from '../../core/pages/data-table-component.page'; import { BrowserVisibility } from '../../core/utils/browser-visibility'; import { BrowserActions } from '../../core/utils/browser-actions'; @@ -23,7 +23,7 @@ import { BrowserActions } from '../../core/utils/browser-actions'; export class DocumentListPage { rootElement: ElementFinder; - optionButton = by.css('button[data-automation-id*="action_menu_"]'); + optionButton: Locator = by.css('button[data-automation-id*="action_menu_"]'); tableBody: ElementFinder; dataTable: DataTableComponentPage; diff --git a/lib/testing/src/lib/content-services/pages/search/search-check-list.page.ts b/lib/testing/src/lib/content-services/pages/search/search-check-list.page.ts index 6f5cd26695..1430966b89 100644 --- a/lib/testing/src/lib/content-services/pages/search/search-check-list.page.ts +++ b/lib/testing/src/lib/content-services/pages/search/search-check-list.page.ts @@ -15,17 +15,17 @@ * limitations under the License. */ -import { element, by, ElementFinder, browser } from 'protractor'; +import { Locator, element, by, ElementFinder, browser } from 'protractor'; import { BrowserActions } from '../../../core/utils/browser-actions'; import { BrowserVisibility } from '../../../core/utils/browser-visibility'; export class SearchCheckListPage { filter: ElementFinder; - inputBy = by.css('div[class*="mat-expansion-panel-content"] input'); - showMoreBy = by.css('button[title="Show more"]'); - showLessBy = by.css('button[title="Show less"]'); - clearAllButton = by.css('button'); + inputBy: Locator = by.css('div[class*="mat-expansion-panel-content"] input'); + showMoreBy: Locator = by.css('button[title="Show more"]'); + showLessBy: Locator = by.css('button[title="Show less"]'); + clearAllButton: Locator = by.css('button'); constructor(filter: ElementFinder) { this.filter = filter; diff --git a/lib/testing/src/lib/content-services/pages/search/search-slider.page.ts b/lib/testing/src/lib/content-services/pages/search/search-slider.page.ts index c874ffa9ae..8ab359eac0 100644 --- a/lib/testing/src/lib/content-services/pages/search/search-slider.page.ts +++ b/lib/testing/src/lib/content-services/pages/search/search-slider.page.ts @@ -15,16 +15,16 @@ * limitations under the License. */ -import { browser, by, ElementFinder } from 'protractor'; +import { Locator, browser, by, ElementFinder } from 'protractor'; import { BrowserVisibility } from '../../../core/utils/browser-visibility'; import { BrowserActions } from '../../../core/utils/browser-actions'; export class SearchSliderPage { filter: ElementFinder; - slider = by.css('mat-slider[data-automation-id="slider-range"]'); - clearButton = by.css('button[data-automation-id="slider-btn-clear"]'); - sliderWithThumbLabel = by.css('mat-slider[data-automation-id="slider-range"][class*="mat-slider-thumb-label-showing"]'); + slider: Locator = by.css('mat-slider[data-automation-id="slider-range"]'); + clearButton: Locator = by.css('button[data-automation-id="slider-btn-clear"]'); + sliderWithThumbLabel: Locator = by.css('mat-slider[data-automation-id="slider-range"][class*="mat-slider-thumb-label-showing"]'); constructor(filter: ElementFinder) { this.filter = filter; diff --git a/lib/testing/src/lib/core/actions/api.service.ts b/lib/testing/src/lib/core/actions/api.service.ts index f9e070b1f7..ed349862f4 100644 --- a/lib/testing/src/lib/core/actions/api.service.ts +++ b/lib/testing/src/lib/core/actions/api.service.ts @@ -39,12 +39,16 @@ export class ApiService { constructor(clientIdOrAppConfig?: AlfrescoApiConfig | string, host?: string, hostSso?: string, provider?: string) { if (browser.params.testConfig && browser.params.testConfig.appConfig) { + Logger.log('Get Config ApiService from browser params'); + this.config = { ...browser.params.testConfig.appConfig }; this.config.hostEcm = browser.params.testConfig.appConfig.ecmHost; this.config.hostBpm = browser.params.testConfig.appConfig.bpmHost; } if (clientIdOrAppConfig && typeof clientIdOrAppConfig !== 'string') { + Logger.log('overwrite ApiService config param'); + this.config = { ...this.config, ...clientIdOrAppConfig }; this.config.hostEcm = clientIdOrAppConfig.hostEcm ? clientIdOrAppConfig.hostEcm : this.config.hostEcm; @@ -54,6 +58,8 @@ export class ApiService { } if (hostSso) { + Logger.log('overwrite ApiService hostSso param'); + this.config.oauth2.host = hostSso; } diff --git a/lib/testing/src/lib/core/actions/drop.actions.ts b/lib/testing/src/lib/core/actions/drop.actions.ts index c3b1b38acd..e8dc6d1126 100644 --- a/lib/testing/src/lib/core/actions/drop.actions.ts +++ b/lib/testing/src/lib/core/actions/drop.actions.ts @@ -18,7 +18,6 @@ import { browser, ElementFinder } from 'protractor'; import * as path from 'path'; import * as fs from 'fs'; -import * as remote from 'selenium-webdriver/remote'; import { BrowserActions } from '../utils/browser-actions'; const JS_BIND_INPUT = function(target) { @@ -78,8 +77,6 @@ const JS_BIND_INPUT_FOLDER = function(target) { export class DropActions { static async dropFile(dropArea, filePath) { - browser.setFileDetector(new remote.FileDetector()); - const absolutePath = path.resolve(path.join(browser.params.testConfig.main.rootPath, filePath)); fs.accessSync(absolutePath, fs.constants.F_OK); @@ -89,8 +86,6 @@ export class DropActions { } static async dropFolder(dropArea, folderPath) { - browser.setFileDetector(new remote.FileDetector()); - const absolutePath = path.resolve(path.join(browser.params.testConfig.main.rootPath, folderPath)); fs.accessSync(absolutePath, fs.constants.F_OK); diff --git a/lib/testing/src/lib/core/actions/users.actions.ts b/lib/testing/src/lib/core/actions/users.actions.ts index 9075f14ddb..658bdec6e1 100644 --- a/lib/testing/src/lib/core/actions/users.actions.ts +++ b/lib/testing/src/lib/core/actions/users.actions.ts @@ -17,7 +17,6 @@ import * as path from 'path'; import * as fs from 'fs'; -import * as remote from 'selenium-webdriver/remote'; import { browser } from 'protractor'; import { ImageUploadRepresentation, UserRepresentation } from '@alfresco/js-api'; @@ -99,6 +98,11 @@ export class UsersActions { return user; } + async createUserWithName(firstName: string, lastName: string): Promise { + const user = new UserModel({ firstName: firstName, lastName: lastName}); + return this.createUser(user); + } + async createTenantAndUser(email?: string, firstName?: string, lastName?: string, password?: string): Promise { const newTenant = await this.api.apiService.activiti.adminTenantsApi.createTenant(new Tenant()); @@ -127,8 +131,6 @@ export class UsersActions { } async changeProfilePictureAps(fileLocation: string): Promise { - browser.setFileDetector(new remote.FileDetector()); - const pathFile = path.join(browser.params.testConfig.main.rootPath + fileLocation); const file = fs.createReadStream(pathFile); diff --git a/lib/testing/src/lib/core/pages/card-view/card-view-date-item.page.ts b/lib/testing/src/lib/core/pages/card-view/card-view-date-item.page.ts index 69cd028dea..d832ed4e42 100644 --- a/lib/testing/src/lib/core/pages/card-view/card-view-date-item.page.ts +++ b/lib/testing/src/lib/core/pages/card-view/card-view-date-item.page.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { element, by, ElementFinder } from 'protractor'; +import { Locator, element, by, ElementFinder } from 'protractor'; import { DateTimePickerPage } from '../material/date-time-picker.page'; import { DatePickerPage } from '../material/date-picker.page'; import { BrowserVisibility } from '../../utils/browser-visibility'; @@ -26,10 +26,10 @@ export class CardDateItemPage { dateTimePickerPage: DateTimePickerPage; datePickerPage = new DatePickerPage(); - labelLocator = by.css('div[data-automation-id*="card-dateitem-label"]'); - valueLocator = by.css('span[data-automation-id*="card-date"]'); + labelLocator: Locator = by.css('div[data-automation-id*="card-dateitem-label"]'); + valueLocator: Locator = by.css('span[data-automation-id*="card-date"]'); dateTimePicker = element.all(by.css('.mat-datetimepicker-toggle')).first(); - saveButton = by.css('button[data-automation-id*="card-dateitem-update"]'); + saveButton: Locator = by.css('button[data-automation-id*="card-dateitem-update"]'); constructor(label: string = 'minDate') { this.rootElement = element(by.xpath(`//div[contains(@data-automation-id, "label-${label}")]/ancestor::adf-card-view-dateitem`)); diff --git a/lib/testing/src/lib/core/pages/card-view/card-view-text-item.page.ts b/lib/testing/src/lib/core/pages/card-view/card-view-text-item.page.ts index 87c9e5e0b1..49230af0ff 100644 --- a/lib/testing/src/lib/core/pages/card-view/card-view-text-item.page.ts +++ b/lib/testing/src/lib/core/pages/card-view/card-view-text-item.page.ts @@ -15,19 +15,19 @@ * limitations under the License. */ -import { element, by, ElementFinder, Key } from 'protractor'; +import { Locator, element, by, ElementFinder, Key } from 'protractor'; import { BrowserActions, BrowserVisibility } from '../../utils/public-api'; export class CardTextItemPage { rootElement: ElementFinder; - textField = by.css('[data-automation-id*="card-textitem-value"]'); - saveButton = by.css('button[data-automation-id*="card-textitem-update"]'); - clearButton = by.css('button[data-automation-id*="card-textitem-reset"]'); - field = by.css('[data-automation-id*="card-textitem-value"]'); - labelLocator = by.css('div[data-automation-id*="card-textitem-label"]'); - errorMessage = by.css('.adf-textitem-editable-error'); - clickableElement = by.css('.adf-textitem-clickable'); - readOnlyField = by.css('.adf-property-read-only'); + textField: Locator = by.css('[data-automation-id*="card-textitem-value"]'); + saveButton: Locator = by.css('button[data-automation-id*="card-textitem-update"]'); + clearButton: Locator = by.css('button[data-automation-id*="card-textitem-reset"]'); + field: Locator = by.css('[data-automation-id*="card-textitem-value"]'); + labelLocator: Locator = by.css('div[data-automation-id*="card-textitem-label"]'); + errorMessage: Locator = by.css('.adf-textitem-editable-error'); + clickableElement: Locator = by.css('.adf-textitem-clickable'); + readOnlyField: Locator = by.css('.adf-property-read-only'); constructor(label: string = 'assignee') { this.rootElement = element(by.xpath(`//div[contains(@data-automation-id, "label-${label}")]/ancestor::adf-card-view-textitem`)); diff --git a/lib/testing/src/lib/core/pages/data-table-component.page.ts b/lib/testing/src/lib/core/pages/data-table-component.page.ts index d820763f8a..708c20d70e 100644 --- a/lib/testing/src/lib/core/pages/data-table-component.page.ts +++ b/lib/testing/src/lib/core/pages/data-table-component.page.ts @@ -15,9 +15,10 @@ * limitations under the License. */ -import { browser, by, element, protractor, ElementFinder, ElementArrayFinder } from 'protractor'; +import { Locator, browser, by, element, protractor, ElementFinder, ElementArrayFinder } from 'protractor'; import { BrowserVisibility } from '../utils/browser-visibility'; import { BrowserActions } from '../utils/browser-actions'; +import { Logger } from '../utils/logger'; export class DataTableComponentPage { @@ -25,7 +26,7 @@ export class DataTableComponentPage { list: ElementArrayFinder; contents: ElementArrayFinder; tableBody: ElementFinder; - rows = by.css(`adf-datatable div[class*='adf-datatable-body'] adf-datatable-row[class*='adf-datatable-row']`); + rows: Locator = by.css(`adf-datatable div[class*='adf-datatable-body'] adf-datatable-row[class*='adf-datatable-row']`); allColumns: ElementArrayFinder; selectedRowNumber: ElementFinder; allSelectedRows: ElementArrayFinder; @@ -177,7 +178,12 @@ export class DataTableComponentPage { } async numberOfRows(): Promise { - return this.rootElement.all(this.rows).count(); + try { + await this.waitForFirstRow(); + return this.rootElement.all(this.rows).count(); + } catch (e) { + return 0; + } } async waitForFirstRow(): Promise { @@ -186,7 +192,7 @@ export class DataTableComponentPage { async getAllRowsColumnValues(column: string): Promise { let columnValues: string[] = []; - const columnLocator = by.css("adf-datatable div[class*='adf-datatable-body'] adf-datatable-row[class*='adf-datatable-row'] div[title='" + column + "'] span"); + const columnLocator: Locator = by.css("adf-datatable div[class*='adf-datatable-body'] adf-datatable-row[class*='adf-datatable-row'] div[title='" + column + "'] span"); try { await BrowserVisibility.waitUntilElementIsPresent(element.all(columnLocator).first(), 1000); @@ -200,7 +206,7 @@ export class DataTableComponentPage { } async getRowsWithSameColumnValues(columnName: string, columnValue: string) { - const columnLocator = by.css(`div[title='${columnName}'] div[data-automation-id="text_${columnValue}"] span`); + const columnLocator: Locator = by.css(`div[title='${columnName}'] div[data-automation-id="text_${columnValue}"] span`); await BrowserVisibility.waitUntilElementIsVisible(this.rootElement.all(columnLocator).first()); return this.rootElement.all(columnLocator).getText(); } @@ -230,7 +236,7 @@ export class DataTableComponentPage { * @param sortOrder : 'ASC' to sort the list ascendant and 'DESC' for descendant */ async sortByColumn(sortOrder: string, titleColumn: string): Promise { - const locator = by.css(`div[data-automation-id="auto_id_${titleColumn}"]`); + const locator: Locator = by.css(`div[data-automation-id="auto_id_${titleColumn}"]`); await BrowserVisibility.waitUntilElementIsVisible(element(locator)); const result = await element(locator).getAttribute('class'); if (sortOrder.toLocaleLowerCase() === 'asc') { @@ -281,7 +287,18 @@ export class DataTableComponentPage { } async waitTillContentLoaded(): Promise { - await browser.driver.sleep(500); + await browser.sleep(500); + Logger.log('wait datatable loading'); + + if (element(by.tagName('mat-spinner')).isPresent()) { + await BrowserVisibility.waitUntilElementIsNotPresent(element(by.tagName('mat-spinner'))); + } else { + try { + await BrowserVisibility.waitUntilElementIsPresent(element(by.tagName('mat-spinner')), 500); + } catch (error) { + } + } + await BrowserVisibility.waitUntilElementIsVisible(this.contents.first()); } diff --git a/lib/testing/src/lib/core/pages/form/form-fields.ts b/lib/testing/src/lib/core/pages/form/form-fields.ts index e3b1e3483f..a07e2dcb32 100644 --- a/lib/testing/src/lib/core/pages/form/form-fields.ts +++ b/lib/testing/src/lib/core/pages/form/form-fields.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { by, element, ElementFinder } from 'protractor'; +import { Locator, by, element, ElementFinder } from 'protractor'; import { BrowserVisibility, BrowserActions } from '../../utils/public-api'; import { DropdownPage } from '../material/dropdown.page'; @@ -24,8 +24,8 @@ export class FormFields { formContent = element(by.css('adf-form-renderer')); refreshButton = element(by.css('div[class*="form-reload-button"] mat-icon')); saveButton = element(by.cssContainingText('mat-card-actions[class*="adf-for"] span', 'SAVE')); - valueLocator = by.css('input'); - labelLocator = by.css('label'); + valueLocator: Locator = by.css('input'); + labelLocator: Locator = by.css('label'); noFormMessage = element(by.css('.adf-empty-content__title')); noFormMessageStandaloneTask = element(by.css('adf-task-standalone #adf-no-form-message')); noFormTemplate = element(by.css('adf-empty-content')); @@ -35,7 +35,7 @@ export class FormFields { completeButton = element(by.id('adf-form-complete')); completeNoFormButton = element(by.id('adf-no-form-complete-button')); cancelButton = element(by.css('#adf-no-form-cancel-button')); - errorMessage = by.css('.adf-error-text-container .adf-error-text'); + errorMessage: Locator = by.css('.adf-error-text-container .adf-error-text'); selectFormDropdown = new DropdownPage(element.all(by.css('.adf-attach-form .mat-select-arrow')).first()); @@ -89,7 +89,7 @@ export class FormFields { async getFieldPlaceHolder(fieldId: string, locator = 'input'): Promise { const placeHolderLocator = element(by.css(`${locator}#${fieldId}`)); await BrowserVisibility.waitUntilElementIsVisible(placeHolderLocator); - return placeHolderLocator.getAttribute('placeholder'); + return placeHolderLocator.getAttribute('data-placeholder'); } async checkFieldValue(locator, field, val): Promise { diff --git a/lib/testing/src/lib/core/pages/form/widgets/attach-file-widget.page.ts b/lib/testing/src/lib/core/pages/form/widgets/attach-file-widget.page.ts index e2ab396fda..6f5f01184f 100644 --- a/lib/testing/src/lib/core/pages/form/widgets/attach-file-widget.page.ts +++ b/lib/testing/src/lib/core/pages/form/widgets/attach-file-widget.page.ts @@ -17,15 +17,14 @@ import { FormFields } from '../form-fields'; import { BrowserVisibility, BrowserActions } from '../../../utils/public-api'; -import * as remote from 'selenium-webdriver/remote'; -import { element, by, browser } from 'protractor'; +import { Locator, element, by, browser } from 'protractor'; export class AttachFileWidgetPage { formFields = new FormFields(); - uploadLocator = by.css('button[id="attachfile"]'); + uploadLocator: Locator = by.css('button[id="attachfile"]'); localStorageButton = element(by.css('input[id="attachfile"]')); - filesListLocator = by.css('div[id="adf-attach-widget-readonly-list"]'); + filesListLocator: Locator = by.css('div[id="adf-attach-widget-readonly-list"]'); attachFileWidget = element(by.css('#attachfile')); attachedFileMenu = element(by.css('mat-list-item button')); attachedFileOptions = element(by.css('.mat-menu-panel .mat-menu-content')); @@ -34,7 +33,6 @@ export class AttachFileWidgetPage { removeFileOptionButton = element(by.css(`.mat-menu-panel .mat-menu-content button[id$="remove"]`)); async attachFile(fieldId, fileLocation): Promise { - browser.setFileDetector(new remote.FileDetector()); const widget = await this.formFields.getWidget(fieldId); const uploadButton = await widget.element(this.uploadLocator); await BrowserActions.click(uploadButton); @@ -107,7 +105,6 @@ export class AttachFileWidgetPage { } async checkUploadIsNotVisible(fieldId): Promise { - browser.setFileDetector(new remote.FileDetector()); const widget = await this.formFields.getWidget(fieldId); const uploadButton = await widget.element(this.uploadLocator); await BrowserVisibility.waitUntilElementIsNotPresent(uploadButton); @@ -119,7 +116,6 @@ export class AttachFileWidgetPage { } async clickUploadButton(fieldId): Promise { - browser.setFileDetector(new remote.FileDetector()); await BrowserActions.closeMenuAndDialogs(); const widget = await this.formFields.getWidget(fieldId); const uploadButton = await widget.element(this.uploadLocator); diff --git a/lib/testing/src/lib/core/pages/form/widgets/attach-folder-widget.page.ts b/lib/testing/src/lib/core/pages/form/widgets/attach-folder-widget.page.ts index 88fc855f91..7e803b8bf6 100644 --- a/lib/testing/src/lib/core/pages/form/widgets/attach-folder-widget.page.ts +++ b/lib/testing/src/lib/core/pages/form/widgets/attach-folder-widget.page.ts @@ -15,18 +15,16 @@ * limitations under the License. */ -import * as remote from 'selenium-webdriver/remote'; -import { browser, by, element } from 'protractor'; +import { Locator, by, element } from 'protractor'; import { FormFields } from '../form-fields'; import { BrowserActions, BrowserVisibility } from '../../../utils/public-api'; export class AttachFolderWidgetPage { formFields: FormFields = new FormFields(); - foldersListLocator = by.css('.adf-attach-folder-result'); + foldersListLocator: Locator = by.css('.adf-attach-folder-result'); async clickWidget(fieldId: string): Promise { - browser.setFileDetector(new remote.FileDetector()); const widget = await this.formFields.getWidget(fieldId).element(by.css(`button[id="folder-${fieldId}-button"]`)); await BrowserActions.click(widget); } diff --git a/lib/testing/src/lib/core/pages/form/widgets/checkbox-widget.page.ts b/lib/testing/src/lib/core/pages/form/widgets/checkbox-widget.page.ts index 2fe6a35738..753c32192e 100644 --- a/lib/testing/src/lib/core/pages/form/widgets/checkbox-widget.page.ts +++ b/lib/testing/src/lib/core/pages/form/widgets/checkbox-widget.page.ts @@ -17,13 +17,13 @@ import { FormFields } from '../form-fields'; import { BrowserActions, BrowserVisibility } from '../../../utils/public-api'; -import { by, element } from 'protractor'; +import { Locator, by, element } from 'protractor'; export class CheckboxWidgetPage { formFields = new FormFields(); checkboxLabel = element(by.css('span[class*="mat-checkbox-label"]')); - checkboxLocator = by.css('mat-checkbox'); + checkboxLocator: Locator = by.css('mat-checkbox'); getCheckboxLabel(): Promise { return BrowserActions.getText(this.checkboxLabel); diff --git a/lib/testing/src/lib/core/pages/form/widgets/dynamic-table-widget.page.ts b/lib/testing/src/lib/core/pages/form/widgets/dynamic-table-widget.page.ts index cc8287cf61..f0392d782c 100644 --- a/lib/testing/src/lib/core/pages/form/widgets/dynamic-table-widget.page.ts +++ b/lib/testing/src/lib/core/pages/form/widgets/dynamic-table-widget.page.ts @@ -16,15 +16,15 @@ */ import { FormFields } from '../form-fields'; -import { by, element, protractor } from 'protractor'; +import { Locator, by, element, protractor } from 'protractor'; import { BrowserVisibility, BrowserActions } from '../../../utils/public-api'; export class DynamicTableWidgetPage { formFields = new FormFields(); - labelLocator = by.css('dynamic-table-widget div div'); - columnNameLocator = by.css('table[id*="dynamic-table"] th'); + labelLocator: Locator = by.css('dynamic-table-widget div div'); + columnNameLocator: Locator = by.css('table[id*="dynamic-table"] th'); cancelButton = element(by.cssContainingText('button span', 'Cancel')); editButton = element(by.cssContainingText('button span', 'edit')); columnDateTime = element(by.id('columnDateTime')); diff --git a/lib/testing/src/lib/core/pages/form/widgets/group-widget.page.ts b/lib/testing/src/lib/core/pages/form/widgets/group-widget.page.ts index 52b00dede0..23768daa2a 100644 --- a/lib/testing/src/lib/core/pages/form/widgets/group-widget.page.ts +++ b/lib/testing/src/lib/core/pages/form/widgets/group-widget.page.ts @@ -16,7 +16,7 @@ */ import { FormFields } from '../form-fields'; -import { by, element } from 'protractor'; +import { Locator, by, element } from 'protractor'; import { BrowserVisibility, BrowserActions } from '../../../utils/public-api'; export class GroupWidgetPage { @@ -24,7 +24,7 @@ export class GroupWidgetPage { groupField = element(by.css('input[data-automation-id="adf-group-search-input"]')); firstResult = element(by.id('adf-group-widget-user-0')); formFields = new FormFields(); - groupDropDownList = by.css('.mat-autocomplete-panel'); + groupDropDownList: Locator = by.css('.mat-autocomplete-panel'); getFieldLabel(fieldId: string): Promise { return this.formFields.getFieldLabel(fieldId); @@ -52,7 +52,7 @@ export class GroupWidgetPage { } async getDropDownList(): Promise { - const user = by.css('[id="adf-group-label-name"]'); + const user: Locator = by.css('[id="adf-group-label-name"]'); await BrowserVisibility.waitUntilElementIsVisible(element(user)); return element.all(user).map((elementFinder) => elementFinder.getText()); } diff --git a/lib/testing/src/lib/core/pages/form/widgets/hyperlink-widget.page.ts b/lib/testing/src/lib/core/pages/form/widgets/hyperlink-widget.page.ts index 9f8d87c14d..e69015fd8f 100644 --- a/lib/testing/src/lib/core/pages/form/widgets/hyperlink-widget.page.ts +++ b/lib/testing/src/lib/core/pages/form/widgets/hyperlink-widget.page.ts @@ -16,13 +16,13 @@ */ import { FormFields } from '../form-fields'; -import { by, element } from 'protractor'; +import { Locator, by, element } from 'protractor'; import { BrowserActions } from '../../../utils/public-api'; export class HyperlinkWidgetPage { formFields = new FormFields(); - fieldLocator = by.css('.adf-hyperlink-widget a'); + fieldLocator: Locator = by.css('.adf-hyperlink-widget a'); async getFieldText(fieldId: string): Promise { return this.formFields.getFieldText(fieldId, this.fieldLocator); diff --git a/lib/testing/src/lib/core/pages/form/widgets/multiline-text-widget.page.ts b/lib/testing/src/lib/core/pages/form/widgets/multiline-text-widget.page.ts index 59823aa26b..e013382fdd 100644 --- a/lib/testing/src/lib/core/pages/form/widgets/multiline-text-widget.page.ts +++ b/lib/testing/src/lib/core/pages/form/widgets/multiline-text-widget.page.ts @@ -16,7 +16,7 @@ */ import { FormFields } from '../form-fields'; -import { by, Locator } from 'protractor'; +import { Locator, by } from 'protractor'; export class MultilineTextWidgetPage { diff --git a/lib/testing/src/lib/core/pages/form/widgets/people-widget.page.ts b/lib/testing/src/lib/core/pages/form/widgets/people-widget.page.ts index 3c0885f14e..f8ea411740 100644 --- a/lib/testing/src/lib/core/pages/form/widgets/people-widget.page.ts +++ b/lib/testing/src/lib/core/pages/form/widgets/people-widget.page.ts @@ -16,7 +16,7 @@ */ import { FormFields } from '../form-fields'; -import { by, element } from 'protractor'; +import { by, element, Locator } from 'protractor'; import { BrowserVisibility, BrowserActions } from '../../../utils/public-api'; export class PeopleWidgetPage { @@ -24,9 +24,9 @@ export class PeopleWidgetPage { 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"]'); + labelLocator: Locator = by.css('div[class*="display-text-widget"]'); + inputLocator: Locator = by.id('involvepeople'); + peopleDropDownList: Locator = by.css('div[class*="adf-people-widget-list"]'); getFieldLabel(fieldId: string): Promise { return this.formFields.getFieldLabel(fieldId, this.labelLocator); @@ -60,7 +60,7 @@ export class PeopleWidgetPage { async getDropDownList(): Promise { await this.checkDropDownListIsDisplayed(); - const users = by.css('.adf-people-label-name'); + const users: Locator = by.css('.adf-people-label-name'); await BrowserVisibility.waitUntilElementIsVisible(element(users)); return element.all(users).map((elementFinder) => elementFinder.getText()); } diff --git a/lib/testing/src/lib/core/pages/form/widgets/radio-buttons-widget.page.ts b/lib/testing/src/lib/core/pages/form/widgets/radio-buttons-widget.page.ts index 08592c3120..a8c2a09213 100644 --- a/lib/testing/src/lib/core/pages/form/widgets/radio-buttons-widget.page.ts +++ b/lib/testing/src/lib/core/pages/form/widgets/radio-buttons-widget.page.ts @@ -26,14 +26,14 @@ export class RadioButtonsWidgetPage { formFields: FormFields = new FormFields(); async getSpecificOptionLabel(fieldId, optionNumber): Promise { - const optionLocator = by.css('label[for*="radiobuttons-option_' + optionNumber + '"]'); + const optionLocator: Locator = by.css('label[for*="radiobuttons-option_' + optionNumber + '"]'); const widget = await this.formFields.getWidget(fieldId); const option = widget.element(optionLocator); return BrowserActions.getText(option); } async selectOption(fieldId, optionNumber): Promise { - const optionLocator = by.css(`label[for*="${fieldId}-option_${optionNumber}"]`); + const optionLocator: Locator = by.css(`label[for*="${fieldId}-option_${optionNumber}"]`); const widget = await this.formFields.getWidget(fieldId); const option = widget.element(optionLocator); await BrowserActions.click(option); diff --git a/lib/testing/src/lib/core/pages/form/widgets/typeahead-widget.page.ts b/lib/testing/src/lib/core/pages/form/widgets/typeahead-widget.page.ts index e2611f4401..f0090d4497 100644 --- a/lib/testing/src/lib/core/pages/form/widgets/typeahead-widget.page.ts +++ b/lib/testing/src/lib/core/pages/form/widgets/typeahead-widget.page.ts @@ -16,7 +16,7 @@ */ import { FormFields } from '../form-fields'; -import { by, element } from 'protractor'; +import { Locator, by, element } from 'protractor'; import { BrowserVisibility, BrowserActions } from '../../../utils/public-api'; export class TypeaheadWidgetPage { @@ -24,7 +24,7 @@ export class TypeaheadWidgetPage { field = element(by.css('input[data-automation-id="adf-typeahed-search-input"]')); firstResult = element(by.id('adf-typeahed-widget-user-0')); formFields = new FormFields(); - groupDropDownList = by.css('.mat-autocomplete-panel'); + groupDropDownList: Locator = by.css('.mat-autocomplete-panel'); getFieldLabel(fieldId: string): Promise { return this.formFields.getFieldLabel(fieldId); @@ -52,7 +52,7 @@ export class TypeaheadWidgetPage { } async getDropDownList(): Promise { - const option = by.css('[id="adf-typeahed-label-name"]'); + const option: Locator = by.css('[id="adf-typeahed-label-name"]'); await BrowserVisibility.waitUntilElementIsVisible(element(option)); return element.all(option).map((elementFinder) => elementFinder.getText()); } @@ -68,7 +68,7 @@ export class TypeaheadWidgetPage { async fillTypeaheadField(value: string): Promise { await BrowserVisibility.waitUntilElementIsClickable(this.field); - await BrowserActions.clearSendKeys(this.field, value); + await BrowserActions.clearSendKeys(this.field, value, 10); } async selectOptionFromDropdown(): Promise { diff --git a/lib/testing/src/lib/core/pages/info-drawer.page.ts b/lib/testing/src/lib/core/pages/info-drawer.page.ts index cc738c691f..5b4409d1a6 100644 --- a/lib/testing/src/lib/core/pages/info-drawer.page.ts +++ b/lib/testing/src/lib/core/pages/info-drawer.page.ts @@ -15,14 +15,14 @@ * limitations under the License. */ -import { element, by, ElementFinder } from 'protractor'; +import { Locator, element, by, ElementFinder } from 'protractor'; import { BrowserVisibility } from './../utils/browser-visibility'; import { TabsPage } from './material/tabs.page'; export class InfoDrawerPage { rootElement: ElementFinder; - infoDrawerHeader = by.css('adf-info-drawer-layout-header'); + infoDrawerHeader: Locator = by.css('adf-info-drawer-layout-header'); tabsPage: TabsPage = new TabsPage(); constructor(classLocator: string = 'adf-info-drawer') { diff --git a/lib/testing/src/lib/core/pages/login.page.ts b/lib/testing/src/lib/core/pages/login.page.ts index e71ad79134..67eeace954 100644 --- a/lib/testing/src/lib/core/pages/login.page.ts +++ b/lib/testing/src/lib/core/pages/login.page.ts @@ -70,8 +70,8 @@ export class LoginPage { const loginURL: string = browser.baseUrl + (browser.params.loginRoute ? browser.params.loginRoute : ''); - await BrowserActions.getUrl(loginURL); const oauth2 = await LocalStorageUtil.getConfigField('oauth2'); + await BrowserActions.getUrl(loginURL); if (oauth2 && oauth2.silentLogin === false) { await this.clickOnSSOButton(); diff --git a/lib/testing/src/lib/core/pages/material/date-time-picker-calendar.page.ts b/lib/testing/src/lib/core/pages/material/date-time-picker-calendar.page.ts index 9ef893cb2d..696052c1a9 100644 --- a/lib/testing/src/lib/core/pages/material/date-time-picker-calendar.page.ts +++ b/lib/testing/src/lib/core/pages/material/date-time-picker-calendar.page.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { element, by } from 'protractor'; +import { Locator, element, by } from 'protractor'; import { BrowserVisibility } from '../../utils/browser-visibility'; import { BrowserActions } from '../../utils/browser-actions'; @@ -26,8 +26,8 @@ export class DateTimePickerCalendarPage { timePicker = element(by.css('.mat-datetimepicker-clock')); hourTime = element.all(by.css('.mat-datetimepicker-clock-hours .mat-datetimepicker-clock-cell')).first(); minutesTime = element.all(by.css('.mat-datetimepicker-clock-minutes .mat-datetimepicker-clock-cell')).first(); - firstEnabledHourSelector = by.css('.mat-datetimepicker-clock-cell:not(.mat-datetimepicker-clock-cell-disabled)'); - firstEnabledMinutesSelector = by.css('.mat-datetimepicker-clock-cell:not(.mat-datetimepicker-clock-cell-disabled)'); + firstEnabledHourSelector: Locator = by.css('.mat-datetimepicker-clock-cell:not(.mat-datetimepicker-clock-cell-disabled)'); + firstEnabledMinutesSelector: Locator = by.css('.mat-datetimepicker-clock-cell:not(.mat-datetimepicker-clock-cell-disabled)'); hoursPicker = element(by.css('.mat-datetimepicker-clock-hours')); minutePicker = element(by.css('.mat-datetimepicker-clock-minutes')); diff --git a/lib/testing/src/lib/core/pages/material/dropdown.page.ts b/lib/testing/src/lib/core/pages/material/dropdown.page.ts index a3dd544483..95fe3070c8 100644 --- a/lib/testing/src/lib/core/pages/material/dropdown.page.ts +++ b/lib/testing/src/lib/core/pages/material/dropdown.page.ts @@ -28,10 +28,12 @@ export class DropdownPage { } async clickDropdown(): Promise { + await BrowserVisibility.waitUntilElementIsVisible(this.dropDownElement); await BrowserActions.click(this.dropDownElement); } async selectOption(option: string): Promise { + await BrowserVisibility.waitUntilElementIsVisible(element.all(by.cssContainingText('mat-option span.mat-option-text', option)).first()); const optionElement = element.all(by.cssContainingText('mat-option span.mat-option-text', option)).first(); await BrowserActions.click(optionElement); } diff --git a/lib/testing/src/lib/core/pages/pagination.page.ts b/lib/testing/src/lib/core/pages/pagination.page.ts index d11cb81693..4019bd79a1 100644 --- a/lib/testing/src/lib/core/pages/pagination.page.ts +++ b/lib/testing/src/lib/core/pages/pagination.page.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { browser, by, element } from 'protractor'; +import { Locator, by, element } from 'protractor'; import { BrowserVisibility } from '../utils/browser-visibility'; import { BrowserActions } from '../utils/browser-actions'; @@ -25,7 +25,7 @@ export class PaginationPage { pageSelectorArrow = element(by.css('button[data-automation-id="page-selector"]')); itemsPerPage = element(by.css('.adf-pagination__max-items')); itemsPerPageOpenDropdown = element(by.css('.adf-pagination__perpage-block button')); - itemsPerPageOptions = by.css('.adf-pagination__page-selector .mat-menu-item'); + itemsPerPageOptions: Locator = by.css('.adf-pagination__page-selector .mat-menu-item'); currentPage = element(by.css('.adf-pagination__current-page')); totalPages = element(by.css('.adf-pagination__total-pages')); paginationRange = element(by.css('.adf-pagination__range')); @@ -33,13 +33,13 @@ export class PaginationPage { nextButtonDisabled = element(by.css('button[class*="adf-pagination__next-button"][disabled]')); previousButtonDisabled = element(by.css('button[class*="adf-pagination__previous-button"][disabled]')); pageDropDown = element(by.css('div[class*="adf-pagination__actualinfo-block"] button')); - pageDropDownOptions = by.css('div[class*="mat-menu-content"] button'); + pageDropDownOptions: Locator = by.css('div[class*="mat-menu-content"] button'); paginationSection = element(by.css('adf-pagination')); paginationSectionEmpty = element(by.css('adf-pagination[class*="adf-pagination__empty"]')); totalFiles = element(by.css('.adf-pagination__range')); async selectItemsPerPage(numberOfItem: string): Promise { - await browser.executeScript(`document.querySelector('div[class*="adf-pagination__perpage-block"] button').click();`); + await BrowserActions.clickExecuteScript(`div[class*="adf-pagination__perpage-block"] button`); await BrowserVisibility.waitUntilElementIsVisible(this.pageSelectorDropDown); const itemsPerPage = element.all(by.cssContainingText('.mat-menu-item', numberOfItem)).first(); await BrowserVisibility.waitUntilElementIsPresent(itemsPerPage); @@ -80,7 +80,7 @@ export class PaginationPage { } async clickOnNextPage(): Promise { - await browser.executeScript(`document.querySelector('button[class*="adf-pagination__next-button"]').click();`); + return BrowserActions.click(this.nextPageButton); } async clickOnPageDropdown(): Promise { @@ -138,13 +138,4 @@ export class PaginationPage { const totalNumberOfFiles = await BrowserActions.getText(this.totalFiles); return totalNumberOfFiles.split('of ')[1]; } - - /* - * Wait until the total number of items is less then specified value - */ - async waitUntilNoOfItemsIsLessThenValue(expectedValue: number): Promise { - await BrowserVisibility.waitUntilElementIsVisible(this.totalFiles); - const condition = () => this.totalFiles.getText().then(value => value && +value.split('of ')[1] < expectedValue); - return browser.wait(condition, 10000); - } } diff --git a/lib/testing/src/lib/core/pages/snackbar.page.ts b/lib/testing/src/lib/core/pages/snackbar.page.ts index a0d2837422..cdffb23704 100644 --- a/lib/testing/src/lib/core/pages/snackbar.page.ts +++ b/lib/testing/src/lib/core/pages/snackbar.page.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { element, by } from 'protractor'; +import { Locator, element, by } from 'protractor'; import { BrowserVisibility } from '../utils/browser-visibility'; import { BrowserActions } from '../utils/browser-actions'; @@ -23,7 +23,7 @@ export class SnackbarPage { notificationSnackBar = element.all(by.css('simple-snack-bar span')).first(); snackBarAction = element(by.css('simple-snack-bar button span')); - snackBarContainerCss = by.css('.mat-snack-bar-container'); + snackBarContainerCss: Locator = by.css('.mat-snack-bar-container'); async waitForSnackBarToAppear() { return BrowserVisibility.waitUntilElementIsVisible(element.all(this.snackBarContainerCss).first(), 5000, diff --git a/lib/testing/src/lib/core/utils/browser-actions.ts b/lib/testing/src/lib/core/utils/browser-actions.ts index ddd5521e63..19bfc2420c 100644 --- a/lib/testing/src/lib/core/utils/browser-actions.ts +++ b/lib/testing/src/lib/core/utils/browser-actions.ts @@ -37,11 +37,15 @@ export class BrowserActions { } static async clickScript(elementFinder: ElementFinder): Promise { + Logger.info(`Click script ${elementFinder.locator().toString()}`); + await browser.executeScript(`arguments[0].scrollIntoView();`, elementFinder); await browser.executeScript(`arguments[0].click();`, elementFinder); } static async clickExecuteScript(elementCssSelector: string): Promise { + Logger.info(`Click execute script ${elementCssSelector}`); + await BrowserVisibility.waitUntilElementIsPresent(element(by.css(elementCssSelector))); await browser.executeScript(`document.querySelector('${elementCssSelector}').click();`); } @@ -69,13 +73,26 @@ export class BrowserActions { Logger.info(`Get Text ${elementFinder.locator().toString()}`); const present = await BrowserVisibility.waitUntilElementIsPresent(elementFinder); + if (present) { - return elementFinder.getText(); + let text = await elementFinder.getText(); + + if (text === '') { // DO NOT REMOVE BUG sometime wrongly return empty text for cdk elements + text = await this.getTextScript(elementFinder); + return text?.trim(); + } + + return text; } else { + Logger.error(`Get Text ${elementFinder.locator().toString()} not present`); return ''; } } + static async getTextScript(elementFinder: ElementFinder): Promise { + return browser.executeScript(`return arguments[0].textContent`, elementFinder); + } + static async getInputValue(elementFinder: ElementFinder): Promise { Logger.info(`Get Input value ${elementFinder.locator().toString()}`); @@ -83,6 +100,7 @@ export class BrowserActions { if (present) { return elementFinder.getAttribute('value'); } else { + Logger.error(`Get Input value ${elementFinder.locator().toString()} not present`); return ''; } } @@ -109,13 +127,22 @@ export class BrowserActions { } } - static async clearSendKeys(elementFinder: ElementFinder, text: string): Promise { + static async clearSendKeys(elementFinder: ElementFinder, text: string, sleepTime: number = 0): Promise { Logger.info(`Clear and sendKeys text:${text} locator:${elementFinder.locator().toString()}`); await this.click(elementFinder); await elementFinder.sendKeys(''); await elementFinder.clear(); - await elementFinder.sendKeys(text); + + if (sleepTime === 0) { + await elementFinder.sendKeys(text); + } else { + for (let i = 0; i < text.length; i++) { + await elementFinder.sendKeys(text[i]); + await browser.sleep(sleepTime); + } + } + } static async checkIsDisabled(elementFinder: ElementFinder): Promise { @@ -138,7 +165,7 @@ export class BrowserActions { const container = element(by.css('div.cdk-overlay-backdrop.cdk-overlay-transparent-backdrop.cdk-overlay-backdrop-showing')); await browser.actions().sendKeys(protractor.Key.ESCAPE).perform(); - await BrowserVisibility.waitUntilElementIsNotVisible(container); + await BrowserVisibility.waitUntilElementIsNotVisible(container, 1000); } static async closeDisabledMenu(): Promise { diff --git a/lib/testing/src/lib/core/utils/browser-visibility.ts b/lib/testing/src/lib/core/utils/browser-visibility.ts index 446ca84063..6c3fd66437 100644 --- a/lib/testing/src/lib/core/utils/browser-visibility.ts +++ b/lib/testing/src/lib/core/utils/browser-visibility.ts @@ -83,7 +83,16 @@ export class BrowserVisibility { static async waitUntilElementHasValue(elementToCheck: ElementFinder, elementValue, waitTimeout: number = BrowserVisibility.DEFAULT_TIMEOUT): Promise { Logger.info(`Wait Until Element has value ${elementToCheck.locator().toString()} for ${waitTimeout}`); - return browser.wait(protractor.ExpectedConditions.textToBePresentInElementValue(elementToCheck, elementValue), waitTimeout, 'Element doesn\'t have a value ' + elementToCheck.locator()); + return browser.wait(protractor.ExpectedConditions.textToBePresentInElementValue(elementToCheck, elementValue), waitTimeout, `Element doesn\'t have a value ${elementValue} ${elementToCheck.locator()}`); + } + + /* + * Wait for element to have text + */ + static async waitUntilElementHasText(elementToCheck: ElementFinder, text, waitTimeout: number = BrowserVisibility.DEFAULT_TIMEOUT): Promise { + Logger.info(`Wait Until Element has value ${elementToCheck.locator().toString()} for ${waitTimeout}`); + + return browser.wait(protractor.ExpectedConditions.textToBePresentInElement(elementToCheck, text), waitTimeout, `Element doesn\'t have the text ${text} ${elementToCheck.locator()}`); } static async waitUntilElementIsNotPresent(elementToCheck: ElementFinder, waitTimeout: number = BrowserVisibility.NOT_VISIBLE_DEFAULT_TIMEOUT): Promise { diff --git a/lib/testing/src/lib/process-services-cloud/pages/dialog/edit-process-filter-dialog.page.ts b/lib/testing/src/lib/process-services-cloud/pages/dialog/edit-process-filter-dialog.page.ts index 03ce37ec44..95617cf47b 100644 --- a/lib/testing/src/lib/process-services-cloud/pages/dialog/edit-process-filter-dialog.page.ts +++ b/lib/testing/src/lib/process-services-cloud/pages/dialog/edit-process-filter-dialog.page.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { browser, by, element } from 'protractor'; +import { Locator, browser, by, element } from 'protractor'; import { BrowserVisibility } from '../../../core/utils/browser-visibility'; import { BrowserActions } from '../../../core/utils/browser-actions'; @@ -24,8 +24,8 @@ export class EditProcessFilterDialogPage { componentElement = element(by.css('adf-cloud-process-filter-dialog-cloud')); title = element(by.id('adf-process-filter-dialog-title')); filterNameInput = element(by.id('adf-filter-name-id')); - saveButtonLocator = by.id('adf-save-button-id'); - cancelButtonLocator = by.id('adf-cancel-button-id'); + saveButtonLocator: Locator = by.id('adf-save-button-id'); + cancelButtonLocator: Locator = by.id('adf-cancel-button-id'); async clickOnSaveButton(): Promise { const saveButton = this.componentElement.element(this.saveButtonLocator); diff --git a/lib/testing/src/lib/process-services-cloud/pages/dialog/edit-task-filter-dialog.page.ts b/lib/testing/src/lib/process-services-cloud/pages/dialog/edit-task-filter-dialog.page.ts index e158a8893d..38386c92af 100644 --- a/lib/testing/src/lib/process-services-cloud/pages/dialog/edit-task-filter-dialog.page.ts +++ b/lib/testing/src/lib/process-services-cloud/pages/dialog/edit-task-filter-dialog.page.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { browser, by, element } from 'protractor'; +import { browser, by, element, Locator } from 'protractor'; import { BrowserVisibility } from '../../../core/utils/browser-visibility'; import { BrowserActions } from '../../../core/utils/browser-actions'; @@ -24,8 +24,8 @@ export class EditTaskFilterDialogPage { componentElement = element(by.css('adf-cloud-task-filter-dialog')); title = element(by.id('adf-task-filter-dialog-title')); filterNameInput = element(by.id('adf-filter-name-id')); - saveButtonLocator = by.id('adf-save-button-id'); - cancelButtonLocator = by.id('adf-cancel-button-id'); + saveButtonLocator: Locator = by.id('adf-save-button-id'); + cancelButtonLocator: Locator = by.id('adf-cancel-button-id'); async clickOnSaveButton(): Promise { const saveButton = this.componentElement.element(this.saveButtonLocator); diff --git a/lib/testing/src/lib/process-services-cloud/pages/edit-task-filter-cloud-component.page.ts b/lib/testing/src/lib/process-services-cloud/pages/edit-task-filter-cloud-component.page.ts index acf968bcf9..6668fc957e 100644 --- a/lib/testing/src/lib/process-services-cloud/pages/edit-task-filter-cloud-component.page.ts +++ b/lib/testing/src/lib/process-services-cloud/pages/edit-task-filter-cloud-component.page.ts @@ -30,8 +30,8 @@ export class EditTaskFilterCloudComponentPage { id = element(by.css('input[data-automation-id="adf-cloud-edit-task-property-taskId"]')); processDefinitionId = element(by.css('input[data-automation-id="adf-cloud-edit-task-property-processDefinitionId"]')); processInstanceId = element(by.css('input[data-automation-id="adf-cloud-edit-task-property-processInstanceId"]')); - lastModifiedFrom = element(by.css('input[placeholder="LastModifiedFrom"]')); - lastModifiedTo = element(by.css('input[placeholder="LastModifiedTo"]')); + lastModifiedFrom = element(by.css('input[data-placeholder="LastModifiedFrom"]')); + lastModifiedTo = element(by.css('input[data-placeholder="LastModifiedTo"]')); parentTaskId = element(by.css('input[data-automation-id="adf-cloud-edit-task-property-parentTaskId"]')); owner = element(by.css('input[data-automation-id="adf-cloud-edit-task-property-owner"]')); saveButton = element(by.css('[data-automation-id="adf-filter-action-save"]')); diff --git a/lib/testing/src/lib/process-services-cloud/pages/form/widget/attach-file-widget-cloud.page.ts b/lib/testing/src/lib/process-services-cloud/pages/form/widget/attach-file-widget-cloud.page.ts index de2565ee8f..c63949989d 100644 --- a/lib/testing/src/lib/process-services-cloud/pages/form/widget/attach-file-widget-cloud.page.ts +++ b/lib/testing/src/lib/process-services-cloud/pages/form/widget/attach-file-widget-cloud.page.ts @@ -15,15 +15,14 @@ * limitations under the License. */ -import * as remote from 'selenium-webdriver/remote'; -import { element, by, browser, ElementFinder } from 'protractor'; +import { Locator, element, by, ElementFinder } from 'protractor'; import { BrowserActions } from '../../../../core/utils/browser-actions'; import { BrowserVisibility } from '../../../../core/utils/browser-visibility'; export class AttachFileWidgetCloudPage { widget: ElementFinder; - filesListLocator = by.css('div[id="adf-attach-widget-readonly-list"]'); + filesListLocator: Locator = by.css('div[id="adf-attach-widget-readonly-list"]'); constructor(fieldId: string) { this.assignWidget(fieldId); @@ -34,7 +33,6 @@ export class AttachFileWidgetCloudPage { } async attachLocalFile(fileLocation: string): Promise { - await browser.setFileDetector(new remote.FileDetector()); const uploadButton = element(by.css('adf-upload-button input')); await BrowserVisibility.waitUntilElementIsPresent(uploadButton); await uploadButton.sendKeys(fileLocation); diff --git a/lib/testing/src/lib/process-services-cloud/pages/people-cloud-component.page.ts b/lib/testing/src/lib/process-services-cloud/pages/people-cloud-component.page.ts index d96b84c3db..cfb7e89ef9 100644 --- a/lib/testing/src/lib/process-services-cloud/pages/people-cloud-component.page.ts +++ b/lib/testing/src/lib/process-services-cloud/pages/people-cloud-component.page.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { by, element, protractor } from 'protractor'; +import { Locator, by, element, protractor } from 'protractor'; import { BrowserVisibility } from '../../core/utils/browser-visibility'; import { BrowserActions } from '../../core/utils/browser-actions'; import { FormFields } from '../../core/pages/form/form-fields'; @@ -26,8 +26,8 @@ export class PeopleCloudComponentPage { assigneeField = element(by.css('input[data-automation-id="adf-people-cloud-search-input"]')); selectionReady = element(by.css('div[data-automation-id="adf-people-cloud-row"]')); formFields = new FormFields(); - labelLocator = by.css("label[class*='adf-label']"); - inputLocator = by.css('input'); + labelLocator: Locator = by.css("label[class*='adf-label']"); + inputLocator: Locator = by.css('input'); assigneeChipList = element(by.css('mat-chip-list[data-automation-id="adf-cloud-people-chip-list"]')); async clearAssignee(): Promise { diff --git a/lib/testing/src/lib/process-services-cloud/pages/process-filters-cloud-component.page.ts b/lib/testing/src/lib/process-services-cloud/pages/process-filters-cloud-component.page.ts index 0b6a23358b..5e6ade2166 100644 --- a/lib/testing/src/lib/process-services-cloud/pages/process-filters-cloud-component.page.ts +++ b/lib/testing/src/lib/process-services-cloud/pages/process-filters-cloud-component.page.ts @@ -15,14 +15,14 @@ * limitations under the License. */ -import { by, element, ElementFinder } from 'protractor'; +import { Locator, by, element, ElementFinder } from 'protractor'; import { BrowserVisibility } from '../../core/utils/browser-visibility'; import { BrowserActions } from '../../core/utils/browser-actions'; export class ProcessFiltersCloudComponentPage { filter: ElementFinder; - filterIcon = by.css('adf-icon[data-automation-id="adf-filter-icon"]'); + filterIcon: Locator = by.css('adf-icon[data-automation-id="adf-filter-icon"]'); processFilters = element(by.css("mat-expansion-panel[data-automation-id='Process Filters']")); diff --git a/lib/testing/src/lib/process-services-cloud/pages/process-list-cloud-component.page.ts b/lib/testing/src/lib/process-services-cloud/pages/process-list-cloud-component.page.ts index 3099c24a64..54dd1c18d7 100644 --- a/lib/testing/src/lib/process-services-cloud/pages/process-list-cloud-component.page.ts +++ b/lib/testing/src/lib/process-services-cloud/pages/process-list-cloud-component.page.ts @@ -17,7 +17,7 @@ import { BrowserVisibility } from '../../core/utils/browser-visibility'; import { DataTableComponentPage } from '../../core/pages/data-table-component.page'; -import { element, by, browser } from 'protractor'; +import { Locator, element, by, browser } from 'protractor'; import { BrowserActions } from '../../core/utils/browser-actions'; export class ProcessListCloudComponentPage { @@ -30,7 +30,7 @@ export class ProcessListCloudComponentPage { processList = element(by.css('adf-cloud-process-list')); noProcessFound = element.all(by.css('.adf-empty-content__title')).first(); actionMenu = element(by.css('*[role="menu"]')); - optionButton = by.css('button[data-automation-id*="action_menu_"]'); + optionButton: Locator = by.css('button[data-automation-id*="action_menu_"]'); dataTable = new DataTableComponentPage(this.processList); diff --git a/lib/testing/src/lib/process-services-cloud/pages/task-list-cloud-component.page.ts b/lib/testing/src/lib/process-services-cloud/pages/task-list-cloud-component.page.ts index 2f26ad3b7f..0ca9e6c00b 100644 --- a/lib/testing/src/lib/process-services-cloud/pages/task-list-cloud-component.page.ts +++ b/lib/testing/src/lib/process-services-cloud/pages/task-list-cloud-component.page.ts @@ -17,7 +17,7 @@ import { BrowserVisibility } from '../../core/utils/browser-visibility'; import { DataTableComponentPage } from '../../core/pages/data-table-component.page'; -import { element, by, ElementFinder } from 'protractor'; +import { element, by, ElementFinder, Locator } from 'protractor'; import { BrowserActions } from '../../core/utils/browser-actions'; const column = { @@ -37,7 +37,7 @@ export class TaskListCloudComponentPage { taskList = element(by.css('adf-cloud-task-list')); noTasksFound = element.all(by.css('.adf-empty-content__title')).first(); actionMenu = element(by.css('*[role="menu"]')); - optionButton = by.css('button[data-automation-id*="action_menu_"]'); + optionButton: Locator = by.css('button[data-automation-id*="action_menu_"]'); dataTable = new DataTableComponentPage(this.taskList); diff --git a/lib/testing/src/lib/process-services/actions/applications.util.ts b/lib/testing/src/lib/process-services/actions/applications.util.ts index 3224b4064f..6e4e3334cc 100644 --- a/lib/testing/src/lib/process-services/actions/applications.util.ts +++ b/lib/testing/src/lib/process-services/actions/applications.util.ts @@ -16,7 +16,6 @@ */ import { Logger } from '../../core/utils/logger'; -import * as remote from 'selenium-webdriver/remote'; import { browser } from 'protractor'; import { ApiService } from '../../core/actions/api.service'; import { AppDefinitionUpdateResultRepresentation } from '@alfresco/js-api'; @@ -50,8 +49,6 @@ export class ApplicationsUtil { } async publishDeployApp(appId: number): Promise { - browser.setFileDetector(new remote.FileDetector()); - const publishApp = await this.api.getInstance().activiti.appsApi.publishAppDefinition(appId, new AppPublish()); await this.api.getInstance().activiti.appsApi.deployAppDefinitions({ appDefinitions: [{ id: publishApp.appDefinition.id }] }); @@ -71,8 +68,6 @@ export class ApplicationsUtil { } async importNewVersionAppDefinitionPublishDeployApp(appFileLocation: string, modelId: number) { - browser.setFileDetector(new remote.FileDetector()); - const pathFile = path.join(browser.params.testConfig.main.rootPath + appFileLocation); const file = fs.createReadStream(pathFile); @@ -87,7 +82,6 @@ export class ApplicationsUtil { async importApplication(appFileLocation: string, options = {}): Promise { try { - browser.setFileDetector(new remote.FileDetector()); const file = fs.createReadStream(appFileLocation); return await this.api.getInstance().activiti.appsDefinitionApi.importAppDefinition(file, options); } catch (error) { diff --git a/lib/testing/src/lib/process-services/pages/filters.page.ts b/lib/testing/src/lib/process-services/pages/filters.page.ts index 407c8b9602..f0e4457a6f 100644 --- a/lib/testing/src/lib/process-services/pages/filters.page.ts +++ b/lib/testing/src/lib/process-services/pages/filters.page.ts @@ -17,13 +17,13 @@ import { BrowserVisibility } from '../../core/utils/browser-visibility'; import { BrowserActions } from '../../core/utils/browser-actions'; -import { by, element, ElementFinder } from 'protractor'; +import { Locator, by, element, ElementFinder } from 'protractor'; export class FiltersPage { accordionMenu = element(by.css('.app-processes-menu mat-accordion')); buttonWindow = element(by.css('div > button[data-automation-id="btn-start-process"] > div')); - processIcon = by.css('adf-icon[data-automation-id="adf-filter-icon"]'); + processIcon: Locator = by.css('adf-icon[data-automation-id="adf-filter-icon"]'); async clickFilterButton(filterElement: ElementFinder): Promise { await BrowserActions.click(filterElement); diff --git a/lib/testing/src/lib/process-services/pages/form-fields.page.ts b/lib/testing/src/lib/process-services/pages/form-fields.page.ts index 1f51f62f4f..82a1aef8c6 100644 --- a/lib/testing/src/lib/process-services/pages/form-fields.page.ts +++ b/lib/testing/src/lib/process-services/pages/form-fields.page.ts @@ -26,13 +26,13 @@ export class FormFieldsPage { formContent = element(by.css('adf-form')); refreshButton = element(by.css('div[class*="form-reload-button"] mat-icon')); saveButton = element(by.cssContainingText('mat-card-actions[class*="adf-for"] span', 'SAVE')); - valueLocator = by.css('input'); - labelLocator = by.css('label'); + valueLocator: Locator = by.css('input'); + labelLocator: Locator = by.css('label'); noFormMessage = element(by.css('.adf-empty-content__title')); completedTaskNoFormMessage = element(by.css('div[id*="completed-form-message"] p')); attachFormButton = element(by.id('adf-attach-form-attach-button')); completeButton = element(by.id('adf-form-complete')); - errorMessage = by.css('.adf-error-text-container .adf-error-text'); + errorMessage: Locator = by.css('.adf-error-text-container .adf-error-text'); selectFormDropdown = new DropdownPage(element.all(by.css('.adf-attach-form .mat-select-arrow')).first()); @@ -86,7 +86,7 @@ export class FormFieldsPage { async getFieldPlaceHolder(fieldId: string, locator = 'input'): Promise { const placeHolderLocator = element(by.css(`${locator}#${fieldId}`)); await BrowserVisibility.waitUntilElementIsVisible(placeHolderLocator); - return placeHolderLocator.getAttribute('placeholder'); + return placeHolderLocator.getAttribute('data-placeholder'); } async checkFieldValue(locator, field, val): Promise { diff --git a/lib/testing/src/lib/process-services/pages/start-process.page.ts b/lib/testing/src/lib/process-services/pages/start-process.page.ts index 3a10050107..4f83401fae 100644 --- a/lib/testing/src/lib/process-services/pages/start-process.page.ts +++ b/lib/testing/src/lib/process-services/pages/start-process.page.ts @@ -75,6 +75,7 @@ export class StartProcessPage { } async clickProcessDropdownArrow(): Promise { + await BrowserVisibility.waitUntilElementIsVisible(this.selectProcessDropdownArrow); await BrowserActions.click(this.selectProcessDropdownArrow); } diff --git a/package-lock.json b/package-lock.json index c3bd3b8a24..8c3fc436c4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4131,9 +4131,9 @@ } }, "@types/selenium-webdriver": { - "version": "3.0.17", - "resolved": "https://registry.npmjs.org/@types/selenium-webdriver/-/selenium-webdriver-3.0.17.tgz", - "integrity": "sha512-tGomyEuzSC1H28y2zlW6XPCaDaXFaD6soTdb4GNdmte2qfHtrKqhy0ZFs4r/1hpazCfEZqeTSRLvSasmEx89uw==", + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/@types/selenium-webdriver/-/selenium-webdriver-4.0.9.tgz", + "integrity": "sha512-HopIwBE7GUXsscmt/J0DhnFXLSmO04AfxT6b8HAprknwka7pqEWquWDMXxCjd+NUHK9MkCe1SDKKsMiNmCItbQ==", "dev": true }, "@types/semver": { @@ -6960,6 +6960,12 @@ "integrity": "sha512-7p4Kn/gffhQaavNfyDFg7LS5S/UT1JAjyGd4UqR2+jzoYF02eDkj0Ec3+48TsIa4zghjLY87nQHIh/ecK9qLdw==", "dev": true }, + "circular-json": { + "version": "0.5.9", + "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.5.9.tgz", + "integrity": "sha512-4ivwqHpIFJZBuhN3g/pEcdbnGUywkBblloGbkglyloVjjR3uT6tieI89MVOfbP2tHX5sgb01FuLgAOzebNlJNQ==", + "dev": true + }, "class-utils": { "version": "0.3.6", "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", @@ -7325,6 +7331,12 @@ "simple-swizzle": "^0.2.2" } }, + "colorette": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.1.tgz", + "integrity": "sha512-puCDz0CzydiSYOrnXpz/PKd69zRrribezjtE9yd4zvytoRc8+RY/KJPvtPFKZS3E3wP6neGyMe0vOTlHO5L3Pw==", + "dev": true + }, "colors": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", @@ -8083,6 +8095,12 @@ "minimatch": "^3.0.4" }, "dependencies": { + "commander": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-6.1.0.tgz", + "integrity": "sha512-wl7PNrYWd2y5mp1OK/LhTlv8Ff4kQJQRXXAvF+uU/TPNiVJUxZLRYGj/B0y/lPGAVcSbJqH2Za/cvHmrPMC8mA==", + "dev": true + }, "fs-extra": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.0.1.tgz", @@ -8861,9 +8879,9 @@ } }, "date-fns": { - "version": "2.15.0", - "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.15.0.tgz", - "integrity": "sha512-ZCPzAMJZn3rNUvvQIMlXhDr4A+Ar07eLeGsGREoWU19a3Pqf5oYa+ccd+B3F6XVtQY6HANMFdOQ8A+ipFnvJdQ==", + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.16.1.tgz", + "integrity": "sha512-sAJVKx/FqrLYHAQeN7VpJrPhagZc9R4ImZIWYRFZaaohR3KzmuK88touwsSwSVT8Qcbd4zoDsnGfX4GFB4imyQ==", "dev": true }, "date-format": { @@ -10309,6 +10327,12 @@ "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz", "integrity": "sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA==" }, + "fastest-levenshtein": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.12.tgz", + "integrity": "sha512-On2N+BpYJ15xIC974QNVuYGMOlEVt4s0EOI3wwMqOmK1fdDY+FN/zltPV8vosq4ad4c/gJ1KHScUn/6AWIgiow==", + "dev": true + }, "fastparse": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.2.tgz", @@ -13371,59 +13395,6 @@ "colors": "1.4.0" } }, - "jasmine2-protractor-utils": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/jasmine2-protractor-utils/-/jasmine2-protractor-utils-1.3.0.tgz", - "integrity": "sha1-lARqq7x0rQpLdGvNTcMFB1h7Z+M=", - "dev": true, - "requires": { - "fs-extra": "^0.26.5", - "mkdirp": "^0.5.1", - "q": "^1.4.1" - }, - "dependencies": { - "fs-extra": { - "version": "0.26.7", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.26.7.tgz", - "integrity": "sha1-muH92UiXeY7at20JGM9C0MMYT6k=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "jsonfile": "^2.1.0", - "klaw": "^1.0.0", - "path-is-absolute": "^1.0.0", - "rimraf": "^2.2.8" - } - }, - "jsonfile": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz", - "integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.6" - } - }, - "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "dev": true, - "requires": { - "minimist": "^1.2.5" - } - }, - "rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - } - } - }, "jasminewd2": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/jasminewd2/-/jasminewd2-2.2.0.tgz", @@ -13694,24 +13665,23 @@ } }, "karma": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/karma/-/karma-5.1.1.tgz", - "integrity": "sha512-xAlOr5PMqUbiKXSv5PCniHWV3aiwj6wIZ0gUVcwpTCPVQm/qH2WAMFWxtnpM6KJqhkRWrIpovR4Rb0rn8GtJzQ==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/karma/-/karma-5.2.0.tgz", + "integrity": "sha512-xT6n0ZVEB48jRHiwA+0nMx5uIah2daQNUZG9wDn9BC/XhFZbmiEid58GykV5m7gYLMa4q2sIFyET6gYuzIwcUw==", "dev": true, "requires": { "body-parser": "^1.19.0", "braces": "^3.0.2", - "chokidar": "^3.0.0", + "chokidar": "^3.4.2", "colors": "^1.4.0", "connect": "^3.7.0", "di": "^0.0.1", "dom-serialize": "^2.2.1", - "flatted": "^2.0.2", "glob": "^7.1.6", "graceful-fs": "^4.2.4", "http-proxy": "^1.18.1", "isbinaryfile": "^4.0.6", - "lodash": "^4.17.15", + "lodash": "^4.17.19", "log4js": "^6.2.1", "mime": "^2.4.5", "minimatch": "^3.0.4", @@ -13719,7 +13689,7 @@ "range-parser": "^1.2.1", "rimraf": "^3.0.2", "socket.io": "^2.3.0", - "source-map": "^0.6.1", + "source-map": "^0.7.3", "tmp": "0.2.1", "ua-parser-js": "0.7.21", "yargs": "^15.3.1" @@ -13814,9 +13784,9 @@ "dev": true }, "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", + "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", "dev": true }, "string-width": { @@ -14007,13 +13977,13 @@ "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", "dev": true }, - "klaw": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/klaw/-/klaw-1.3.1.tgz", - "integrity": "sha1-QIhDO0azsbolnXh4XY6W9zugJDk=", + "klaw-sync": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/klaw-sync/-/klaw-sync-6.0.0.tgz", + "integrity": "sha512-nIeuVSzdCCs6TDPTqI8w1Yre34sSq7AkZ4B3sfOBbI2CgVSB4Du4aLQijFU2+lhAFCwt9+42Hel6lQNIv6AntQ==", "dev": true, "requires": { - "graceful-fs": "^4.1.9" + "graceful-fs": "^4.1.11" } }, "kleur": { @@ -14287,6 +14257,12 @@ "color-name": "~1.1.4" } }, + "commander": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-6.1.0.tgz", + "integrity": "sha512-wl7PNrYWd2y5mp1OK/LhTlv8Ff4kQJQRXXAvF+uU/TPNiVJUxZLRYGj/B0y/lPGAVcSbJqH2Za/cvHmrPMC8mA==", + "dev": true + }, "cosmiconfig": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.0.tgz", @@ -15478,9 +15454,9 @@ "dev": true }, "meow": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/meow/-/meow-7.1.0.tgz", - "integrity": "sha512-kq5F0KVteskZ3JdfyQFivJEj2RaA8NFsS4+r9DaMKLcUHpk5OcHS3Q0XkCXONB1mZRPsu/Y/qImKri0nwSEZog==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/meow/-/meow-7.1.1.tgz", + "integrity": "sha512-GWHvA5QOcS412WCo8vwKDlTelGLsCGBVevQB5Kva961rmNfun0PCbv5+xta2kUMFJyR8/oWnn7ddeKdosbAPbA==", "dev": true, "requires": { "@types/minimist": "^1.2.0", @@ -15712,9 +15688,9 @@ "dev": true }, "mini-css-extract-plugin": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-0.10.0.tgz", - "integrity": "sha512-QgKgJBjaJhxVPwrLNqqwNS0AGkuQQ31Hp4xGXEK/P7wehEg6qmNtReHKai3zRXqY60wGVWLYcOMJK2b98aGc3A==", + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-0.10.1.tgz", + "integrity": "sha512-9B10gZixtNjHerADBrMxPXM5G0uL0CRGMcLRV67I8nd1SKbwJrI0okKUzD+PxKsUZ9Dxt8/hPvtzF0DrRnrOyA==", "dev": true, "requires": { "loader-utils": "^1.1.0", @@ -18720,29 +18696,6 @@ } } }, - "postcss-reporter": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/postcss-reporter/-/postcss-reporter-6.0.1.tgz", - "integrity": "sha512-LpmQjfRWyabc+fRygxZjpRxfhRf9u/fdlKf4VHG4TSPbV2XNsuISzYW1KL+1aQzx53CAppa1bKG4APIB/DOXXw==", - "dev": true, - "requires": { - "chalk": "^2.4.1", - "lodash": "^4.17.11", - "log-symbols": "^2.2.0", - "postcss": "^7.0.7" - }, - "dependencies": { - "log-symbols": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz", - "integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==", - "dev": true, - "requires": { - "chalk": "^2.0.1" - } - } - } - }, "postcss-resolve-nested-selector": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/postcss-resolve-nested-selector/-/postcss-resolve-nested-selector-0.1.1.tgz", @@ -18995,6 +18948,12 @@ "integrity": "sha1-vShOV8hPEyXacCur/IKlMoGQwMU=", "dev": true }, + "@types/selenium-webdriver": { + "version": "3.0.17", + "resolved": "https://registry.npmjs.org/@types/selenium-webdriver/-/selenium-webdriver-3.0.17.tgz", + "integrity": "sha512-tGomyEuzSC1H28y2zlW6XPCaDaXFaD6soTdb4GNdmte2qfHtrKqhy0ZFs4r/1hpazCfEZqeTSRLvSasmEx89uw==", + "dev": true + }, "ansi-regex": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", @@ -19506,6 +19465,45 @@ } } }, + "protractor-screenshoter-plugin": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/protractor-screenshoter-plugin/-/protractor-screenshoter-plugin-0.10.3.tgz", + "integrity": "sha512-OF9kGe1rMxBQY4uXzXQUFT14EB83rz8DlDcxmH5HcOHPBpUhGh+Nwo7+K87w1LoLcTuGdG7Bz+/hGwoGguDfsA==", + "dev": true, + "requires": { + "circular-json": "^0.5.1", + "fs-extra": "^7.0.0", + "klaw-sync": "^6.0.0", + "lodash": "^4.17.11", + "mkdirp": "^0.5.1", + "moment": "^2.20.1", + "q": "^1.5.1", + "screenshoter-report-analyzer": "^0.6", + "uuid": "^3.1.0" + }, + "dependencies": { + "fs-extra": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + } + } + }, "protractor-smartrunner": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/protractor-smartrunner/-/protractor-smartrunner-0.1.1.tgz", @@ -20105,6 +20103,17 @@ "stringify-entities": "^3.0.0", "unherit": "^1.0.4", "xtend": "^4.0.1" + }, + "dependencies": { + "mdast-util-compact": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-compact/-/mdast-util-compact-2.0.1.tgz", + "integrity": "sha512-7GlnT24gEwDrdAwEHrU4Vv5lLWrEer4KOkAiKT9nYstsTad7Oc1TwqT2zIMKRdZF7cTuaf+GA1E4Kv7jJh8mPA==", + "dev": true, + "requires": { + "unist-util-visit": "^2.0.0" + } + } } }, "remove-trailing-separator": { @@ -20848,6 +20857,12 @@ "ajv-keywords": "^3.4.1" } }, + "screenshoter-report-analyzer": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/screenshoter-report-analyzer/-/screenshoter-report-analyzer-0.6.0.tgz", + "integrity": "sha1-Cm+I1fXRrBa2z3Ji7/ujH+5I7RI=", + "dev": true + }, "scss-bundle": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/scss-bundle/-/scss-bundle-2.3.2.tgz", @@ -23315,19 +23330,21 @@ } }, "stylelint": { - "version": "13.6.1", - "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-13.6.1.tgz", - "integrity": "sha512-XyvKyNE7eyrqkuZ85Citd/Uv3ljGiuYHC6UiztTR6sWS9rza8j3UeQv/eGcQS9NZz/imiC4GKdk1EVL3wst5vw==", + "version": "13.7.0", + "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-13.7.0.tgz", + "integrity": "sha512-1wStd4zVetnlHO98VjcHQbjSDmvcA39smkZQMct2cf+hom40H0xlQNdzzbswoG/jGBh61/Ue9m7Lu99PY51O6A==", "dev": true, "requires": { - "@stylelint/postcss-css-in-js": "^0.37.1", + "@stylelint/postcss-css-in-js": "^0.37.2", "@stylelint/postcss-markdown": "^0.36.1", - "autoprefixer": "^9.8.0", + "autoprefixer": "^9.8.6", "balanced-match": "^1.0.0", "chalk": "^4.1.0", - "cosmiconfig": "^6.0.0", + "cosmiconfig": "^7.0.0", "debug": "^4.1.1", "execall": "^2.0.0", + "fast-glob": "^3.2.4", + "fastest-levenshtein": "^1.0.12", "file-entry-cache": "^5.0.1", "get-stdin": "^8.0.0", "global-modules": "^2.0.0", @@ -23338,18 +23355,16 @@ "import-lazy": "^4.0.0", "imurmurhash": "^0.1.4", "known-css-properties": "^0.19.0", - "leven": "^3.1.0", - "lodash": "^4.17.15", + "lodash": "^4.17.20", "log-symbols": "^4.0.0", "mathml-tag-names": "^2.1.3", - "meow": "^7.0.1", + "meow": "^7.1.1", "micromatch": "^4.0.2", "normalize-selector": "^0.2.0", "postcss": "^7.0.32", "postcss-html": "^0.36.0", "postcss-less": "^3.1.4", "postcss-media-query-parser": "^0.2.3", - "postcss-reporter": "^6.0.1", "postcss-resolve-nested-selector": "^0.1.1", "postcss-safe-parser": "^4.0.2", "postcss-sass": "^0.4.4", @@ -23365,7 +23380,7 @@ "style-search": "^0.1.0", "sugarss": "^2.0.0", "svg-tags": "^1.0.0", - "table": "^5.4.6", + "table": "^6.0.1", "v8-compile-cache": "^2.1.1", "write-file-atomic": "^3.0.3" }, @@ -23376,14 +23391,19 @@ "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", "dev": true }, - "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "autoprefixer": { + "version": "9.8.6", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.8.6.tgz", + "integrity": "sha512-XrvP4VVHdRBCdX1S3WXVD8+RyG9qeb1D5Sn1DeLiG2xfSpzellk5k54xbUERJ3M5DggQxes39UGOTP8CFrEGbg==", "dev": true, "requires": { - "@types/color-name": "^1.1.1", - "color-convert": "^2.0.1" + "browserslist": "^4.12.0", + "caniuse-lite": "^1.0.30001109", + "colorette": "^1.2.1", + "normalize-range": "^0.1.2", + "num2fraction": "^1.2.2", + "postcss": "^7.0.32", + "postcss-value-parser": "^4.1.0" } }, "chalk": { @@ -23394,6 +23414,33 @@ "requires": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "dev": true, + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } } }, "color-convert": { @@ -23406,16 +23453,16 @@ } }, "cosmiconfig": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz", - "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.0.tgz", + "integrity": "sha512-pondGvTuVYDk++upghXJabWzL6Kxu6f26ljFw64Swq9v6sQPUL3EUlVDV56diOjpCayKihL6hVe8exIACU4XcA==", "dev": true, "requires": { "@types/parse-json": "^4.0.0", - "import-fresh": "^3.1.0", + "import-fresh": "^3.2.1", "parse-json": "^5.0.0", "path-type": "^4.0.0", - "yaml": "^1.7.2" + "yaml": "^1.10.0" } }, "emoji-regex": { @@ -23444,12 +23491,6 @@ "slash": "^3.0.0" } }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, "import-fresh": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz", @@ -23512,15 +23553,6 @@ "supports-color": "^6.1.0" }, "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, "chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", @@ -23542,36 +23574,6 @@ } } } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } } } }, @@ -23608,12 +23610,12 @@ } }, "supports-color": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", - "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", "dev": true, "requires": { - "has-flag": "^4.0.0" + "has-flag": "^3.0.0" } } } @@ -23785,43 +23787,95 @@ "dev": true }, "table": { - "version": "5.4.6", - "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz", - "integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/table/-/table-6.0.1.tgz", + "integrity": "sha512-fmr6168splcy/3XIvhSm5w6hYYOqyr3plAsd7OqoerzyoMnIpoxYuwrpdO2Cm22dh6KCnvirvigPrFZp+tdWFA==", "dev": true, "requires": { - "ajv": "^6.10.2", - "lodash": "^4.17.14", - "slice-ansi": "^2.1.0", - "string-width": "^3.0.0" + "ajv": "^6.12.4", + "lodash": "^4.17.20", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.0" }, "dependencies": { - "astral-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", - "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", + "ajv": { + "version": "6.12.4", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.4.tgz", + "integrity": "sha512-eienB2c9qVQs2KWexhkrdMLVDoIQCz5KSeLxwg9Lzk4DOfBtIK9PQwwufcsn1jjGuf9WZmqPMbGxOzfcuphJCQ==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "dev": true, + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true }, "slice-ansi": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", - "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", "dev": true, "requires": { - "ansi-styles": "^3.2.0", - "astral-regex": "^1.0.0", - "is-fullwidth-code-point": "^2.0.0" + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" } }, "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", "dev": true, "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" } } } @@ -25690,6 +25744,12 @@ "selenium-webdriver": "^3.0.1" }, "dependencies": { + "@types/selenium-webdriver": { + "version": "3.0.17", + "resolved": "https://registry.npmjs.org/@types/selenium-webdriver/-/selenium-webdriver-3.0.17.tgz", + "integrity": "sha512-tGomyEuzSC1H28y2zlW6XPCaDaXFaD6soTdb4GNdmte2qfHtrKqhy0ZFs4r/1hpazCfEZqeTSRLvSasmEx89uw==", + "dev": true + }, "rimraf": { "version": "2.7.1", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", diff --git a/package.json b/package.json index 28437d0b25..0ab5d7695e 100644 --- a/package.json +++ b/package.json @@ -120,7 +120,7 @@ "@types/node": "^14.6.0", "@types/pdfjs-dist": "^2.1.5", "@types/request": "^2.48.5", - "@types/selenium-webdriver": "^3.0.8", + "@types/selenium-webdriver": "^4.0.9", "ajv-cli": "^3.0.0", "codelyzer": "^6.0.0", "commander": "6.0.0", @@ -136,7 +136,6 @@ "jasmine-core": "~2.8.0", "jasmine-reporters": "^2.3.2", "jasmine-spec-reporter": "~5.0.2", - "jasmine2-protractor-utils": "1.3.0", "js-yaml": "^3.14.0", "karma": "^5.1.1", "karma-chrome-launcher": "~3.1.0", @@ -161,6 +160,7 @@ "protractor": "^5.4.2", "protractor-retry": "^1.2.9", "protractor-smartrunner": "^0.1.1", + "protractor-screenshoter-plugin": "0.10.3", "remark": "^12.0.1", "remark-frontmatter": "^1.2.0", "rimraf": "^3.0.2", diff --git a/scripts/README.md b/scripts/README.md index c45b7c042d..ca1c761fc3 100644 --- a/scripts/README.md +++ b/scripts/README.md @@ -218,27 +218,6 @@ This script test the distribution of ADF against the demo shell ./test-dist ``` -# rancher-update.sh - -***rancher-update.sh*** - -Internal script for update the rancher env - -| Option | Description | -| --- | --- | -|--access_key |rancher access key| -|--secret_key |rancher secret key| -|--url |rancher_url| -|--environment_name s|ervice name to replace in rancher| -|--image |image to gater and load in the service, example: docker:alfresco/demo-shell:latest| - -## Examples - -```sh -/rancher-update.sh --access_key ACCESS_KEY --secret_key SECRET_KEY --url RANCHER_URL--environment_name adf-master --image docker:alfresco/demo-shell:master -``` - - # test-e2e-lib.sh ***test-e2e-lib.sh*** diff --git a/scripts/build/build-cli.sh b/scripts/build/build-cli.sh index 953ca24397..808268d375 100755 --- a/scripts/build/build-cli.sh +++ b/scripts/build/build-cli.sh @@ -2,8 +2,14 @@ DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +cd $DIR/../../ +rm -rf lib/cli/dist +rm -rf lib/dist/cli + cd $DIR/../../lib/cli/ + + echo "====== Cli ======" echo "====== Build ======" npm i @@ -11,6 +17,7 @@ npm run dist cd $DIR/../../ cp -R ./lib/cli/dist lib/dist/cli/ +cp ./lib/cli/README.md lib/dist/cli/README.md echo "====== Move to node_modules ======" rm -rf ./node_modules/@alfresco/adf-cli/ && \ diff --git a/scripts/test-e2e-lib.sh b/scripts/test-e2e-lib.sh index e7f942ae54..1544c12c74 100755 --- a/scripts/test-e2e-lib.sh +++ b/scripts/test-e2e-lib.sh @@ -4,7 +4,6 @@ DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" cd "$DIR/../" BROWSER_RUN=false DEVELOPMENT=false -EXECLINT=false LITESERVER=false EXEC_VERSION_JSAPI=false TIMEOUT=120000 @@ -18,7 +17,6 @@ show_help() { echo "-p or --password" echo "-identity_admin_email" echo "-identity_admin_password" - echo "-e or --email" echo "-b or --browser run the test in the browser (No headless mode)" echo "-s or --spec run a single test file" echo "-f or --folder run a single folder test" @@ -29,7 +27,6 @@ show_help() { echo "-host_sso the entire path including the name of the realm" echo "-save save the error screenshot and report in the remote env" echo "-timeout or --timeout override the timeout foe the wait utils" - echo "-l --lint enable lint" echo "-m --maxInstances max instances parallel for tests" echo "-log or --log print all the browser log" echo "-db or --debug run the debugger" @@ -58,10 +55,7 @@ set_identity_admin_password(){ IDENTITY_ADMIN_PASSWORD=$1 export IDENTITY_ADMIN_PASSWORD=$IDENTITY_ADMIN_PASSWORD } -set_email(){ - EMAIL=$1 - export EMAIL_ADF=$EMAIL -} + set_host(){ HOST=$1 export URL_HOST_ADF=$HOST @@ -120,10 +114,6 @@ set_prefix(){ export PREFIX=$PREFIX } -lint(){ - EXECLINT=true -} - debug(){ export DEBUG=true; } @@ -155,7 +145,6 @@ while [[ $1 == -* ]]; do -p|--password) set_password $2; shift 2;; -identity_admin_email) set_identity_admin_email $2; shift 2;; -identity_admin_password) set_identity_admin_password $2; shift 2;; - -e|--email) set_email $2; shift 2;; -f|--folder) set_test_folder $2; shift 2;; -timeout|--timeout) set_timeout $2; shift 2;; -b|--browser) set_browser; shift;; @@ -167,11 +156,10 @@ while [[ $1 == -* ]]; do -ud|--use-dist) lite_server; shift;; -save) set_save_screenshot; shift;; -proxy|--proxy) set_proxy $2; shift 2;; - -s|--seleniumServer) set_selenium $2; shift 2;; + --seleniumServer) set_selenium $2; shift 2;; -host|--host) set_host $2; shift 2;; -log|--log) set_log; shift ;; -host_sso|--host_sso) set_host_sso $2; shift 2;; - -l|--lint) lint; shift;; -m|--maxInstances) max_instances $2; shift 2;; -vjsapi) version_js_api $2; shift 2;; -*) echo "invalid option: $1" 1>&2; show_help; exit 1;; @@ -188,10 +176,6 @@ if $EXEC_VERSION_JSAPI == true; then npm install alfresco-js-api@${JSAPI_VERSION} fi -if [[ $EXECLINT == "true" ]]; then - npm run lint-e2e || exit 1 -fi - echo "====== Update webdriver-manager =====" if [ "$CI" = "true" ]; then export chrome=$(google-chrome --product-version) @@ -220,6 +204,7 @@ else npm run lite-server-e2e>/dev/null & $DEBUG_OPTION ./node_modules/protractor/bin/protractor ./e2e/protractor.conf.js || exit 1 else + echo "====== Run without lite-server =====" $DEBUG_OPTION ./node_modules/protractor/bin/protractor ./e2e/protractor.conf.js || exit 1 fi fi diff --git a/scripts/travis/deploy/deploy-pr.sh b/scripts/travis/deploy/deploy-pr.sh deleted file mode 100755 index 1c3ba9d082..0000000000 --- a/scripts/travis/deploy/deploy-pr.sh +++ /dev/null @@ -1,31 +0,0 @@ -#!/usr/bin/env bash - -DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" - -cd $DIR/../../../ - -# Get Tag Image -TAG_VERSION=$(./scripts/travis/deploy/get-docker-image-tag-name.sh) -echo "Running the docker with tag" $TAG_VERSION - -# Publish Image to docker - -sed s%href=\".\"%href=\".\"%g \ --i ./dist/demo-shell/index.html - -mkdir "./demo-shell/tmp/" -mv ./dist/demo-shell/* ./demo-shell/tmp - -mkdir -p "./dist/demo-shell/${TRAVIS_BUILD_NUMBER}" -mv ./demo-shell/tmp/* ./dist/demo-shell/${TRAVIS_BUILD_NUMBER} - -./node_modules/@alfresco/adf-cli/bin/adf-cli docker-publish --loginCheck --loginUsername "$DOCKER_REPOSITORY_USER" --loginPassword "$DOCKER_REPOSITORY_PASSWORD" --loginRepo "$DOCKER_REPOSITORY_DOMAIN" --dockerRepo "$DOCKER_REPOSITORY" --dockerTags "$TAG_VERSION" --pathProject "$(pwd)" - -echo "Update rancher with docker tag" $TAG_VERSION --url $REPO_RANCHER --environment_name $REPO_RANCHER_ADF_NAME - -# Deploy PR in Rancher env -(node --no-deprecation ./scripts/travis/deploy/rancher-pr-deploy.js -n $TRAVIS_BUILD_NUMBER -u $RANCHER_TOKEN -p $RANCHER_SECRET -s $REPO_RANCHER --image "alfresco/demo-shell:develop-$TRAVIS_BUILD_NUMBER" --env $ENVIRONMENT_NAME -r $ENVIRONMENT_URL || exit 1); - -# Restore the app in the main run the unit test -mv ./dist/demo-shell/${TRAVIS_BUILD_NUMBER} "./demo-shell/tmp/" -mv "./dist/demo-shell/" "./dist/demo-shell/" diff --git a/scripts/travis/deploy/deploy.sh b/scripts/travis/deploy/deploy.sh deleted file mode 100755 index 9e29b383b8..0000000000 --- a/scripts/travis/deploy/deploy.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/usr/bin/env bash - -DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" - -cd $DIR/../../../ - -# Get Tag Image -TAG_VERSION=$(./scripts/travis/deploy/get-docker-image-tag-name.sh) -echo "Running the docker with tag" $TAG_VERSION - -# Publish Image to docker -./node_modules/@alfresco/adf-cli/bin/adf-cli docker-publish --loginCheck --loginUsername "$DOCKER_REPOSITORY_USER" --loginPassword "$DOCKER_REPOSITORY_PASSWORD" --loginRepo "$DOCKER_REPOSITORY_DOMAIN" --dockerRepo "$DOCKER_REPOSITORY" --dockerTags "$TAG_VERSION,$TRAVIS_BRANCH" --pathProject "$(pwd)" - -echo "Update rancher with docker tag" $TAG_VERSION --url $REPO_RANCHER --environment_name $REPO_RANCHER_ADF_NAME - -# Deploy PR in Rancher env -./scripts/travis/deploy/rancher-update.sh --access_key $RANCHER_TOKEN --secret_key $RANCHER_SECRET --url $REPO_RANCHER --environment_name $REPO_RANCHER_ADF_NAME --image docker:$DOCKER_REPOSITORY:$TAG_VERSION diff --git a/scripts/travis/deploy/rancher-pr-deploy.js b/scripts/travis/deploy/rancher-pr-deploy.js deleted file mode 100644 index 88525d0ed1..0000000000 --- a/scripts/travis/deploy/rancher-pr-deploy.js +++ /dev/null @@ -1,262 +0,0 @@ -var program = require('commander'); -var request = require('request'); - -function asyncRequest(option) { - return new Promise(function (resolve, reject) { - request(option, function (error, res, body) { - if (!error && (res.statusCode == 200 || res.statusCode == 201)) { - resolve(body); - } else { - console.log("Error " + JSON.stringify(body)); - reject(error + JSON.stringify(body)); - throw "Error"; - } - }); - }); -} - -async function main() { - - program - .version('0.1.0') - .option('-n, --name [type]', 'Name to give at the service in rancher') - .option('-r, --remote [type]', 'Remote environment host adf.lab.com ') - .option('-e, --env [type]', 'Name to give at the service in rancher') - .option('-i, --image [type]', 'Docker image to load') - .option('-s, --server [type]', 'Server RANCHER_SERVER URL') - .option('-p, --password [type]', 'password RANCHER') - .option('-u, --username [type]', 'username RANCHER') - .parse(process.argv); - - auth = 'Basic ' + new Buffer(program.username + ':' + program.password).toString('base64'); - - var project = await asyncRequest({ - url: program.server + `/v1/project?name=${program.env}`, - method: 'GET', - json: true, - headers: { - "content-type": "application/json", - "accept": "application/json", - "Authorization": auth - }, - body: "" - }).catch((error) => { - console.log('Project name error'+ error); - }); - - var stacks = await asyncRequest({ - url: `${program.server}/v2-beta/projects/${project.data[0].id}/stacks?limit=-1&sort=name`, - method: 'GET', - json: true, - headers: { - "content-type": "application/json", - "accept": "application/json", - "Authorization": auth - }, - body: "" - }).catch((error) => { - console.log('Stacks error'+ error); - }); - - - var stackId = stacks.data[0].id; - var environmentId = project.data[0].id; - - console.log("StackId " + stackId); - console.log("ID environment " + environmentId); - console.log("image to Load " + program.image); - - var postData = { - "scale": 1, - "assignServiceIpAddress": false, - "startOnCreate": true, - "type": "service", - "stackId": stackId, - "launchConfig": { - "instanceTriggeredStop": "stop", - "kind": "container", - "networkMode": "managed", - "privileged": false, - "publishAllPorts": false, - "readOnly": false, - "runInit": false, - "startOnCreate": true, - "stdinOpen": true, - "tty": true, - "vcpu": 1, - "drainTimeoutMs": 0, - "type": "launchConfig", - "labels": {"io.rancher.container.pull_image": "always"}, - "restartPolicy": {"name": "always"}, - "secrets": [], - "dataVolumes": [], - "dataVolumesFrom": [], - "dns": [], - "dnsSearch": [], - "capAdd": [], - "capDrop": [], - "devices": [], - "logConfig": {"driver": "", "config": {}}, - "dataVolumesFromLaunchConfigs": [], - "imageUuid": "docker:"+program.image, - "ports": [], - "blkioWeight": null, - "cgroupParent": null, - "count": null, - "cpuCount": null, - "cpuPercent": null, - "cpuPeriod": null, - "cpuQuota": null, - "cpuRealtimePeriod": null, - "cpuRealtimeRuntime": null, - "cpuSet": null, - "cpuSetMems": null, - "cpuShares": null, - "createIndex": null, - "created": null, - "deploymentUnitUuid": null, - "description": null, - "diskQuota": null, - "domainName": null, - "externalId": null, - "firstRunning": null, - "healthInterval": null, - "healthRetries": null, - "healthState": null, - "healthTimeout": null, - "hostname": null, - "ioMaximumBandwidth": null, - "ioMaximumIOps": null, - "ip": null, - "ip6": null, - "ipcMode": null, - "isolation": null, - "kernelMemory": null, - "memory": null, - "memoryMb": null, - "memoryReservation": null, - "memorySwap": null, - "memorySwappiness": null, - "milliCpuReservation": null, - "oomScoreAdj": null, - "pidMode": null, - "pidsLimit": null, - "removed": null, - "requestedIpAddress": null, - "shmSize": null, - "startCount": null, - "stopSignal": null, - "stopTimeout": null, - "user": null, - "userdata": null, - "usernsMode": null, - "uts": null, - "uuid": null, - "volumeDriver": null, - "workingDir": null, - "networkLaunchConfig": null - }, - "secondaryLaunchConfigs": [], - "name": program.name, - "createIndex": null, - "created": null, - "description": null, - "externalId": null, - "healthState": null, - "kind": null, - "removed": null, - "selectorContainer": null, - "selectorLink": null, - "uuid": null, - "vip": null, - "fqdn": null - }; - - console.log("Create Service "); - - var createService = await asyncRequest({ - url: `${program.server}/v2-beta/projects/${environmentId}/service`, - method: 'POST', - json: true, - headers: { - "content-type": "application/json", - "accept": "application/json", - "Authorization": auth - }, - body: postData, - }).catch((error) => { - console.log('Error createService'+ error); - }); - - if (!createService) { - return; - } - - console.log("New environment ID " + createService.id); - - var loadBalancer = await asyncRequest({ - url: `${program.server}/v1/projects/${environmentId}/loadbalancerservices`, - method: 'GET', - json: true, - headers: { - "content-type": "application/json", - "accept": "application/json", - "Authorization": auth - }, - body: postData, - }).catch((error) => { - console.log('Error loadBalancer'+ error); - }); - - if (!loadBalancer) { - return; - } - - var loadBalancerId = loadBalancer.data[0].id; - console.log("Load balancer ID " + loadBalancerId); - - var loadBalancerGet = await asyncRequest({ - url: `${program.server}/v2-beta/projects/${environmentId}/loadbalancerservices/${loadBalancerId}`, - method: 'GET', - json: true, - headers: { - "content-type": "application/json", - "accept": "application/json", - "Authorization": auth - } - }).catch((error) => { - console.log('Error get load balancer'+ error); - }); - - //console.log("Load balancer ID " + JSON.stringify(loadBalancerGet.lbConfig.portRules)); - - var newRule = { - "type": "portRule", - "hostname": program.remote, - "path": `/${program.name}`, - "priority": 1, - "protocol": "http", - "serviceId": createService.id, - "sourcePort": 80, - "targetPort": 8080 - }; - - loadBalancerGet.lbConfig.portRules.push(newRule); - - await asyncRequest({ - url: `${program.server}/v2-beta/projects/${environmentId}/loadbalancerservices/${loadBalancerId}`, - method: 'PUT', - json: true, - headers: { - "content-type": "application/json", - "accept": "application/json", - "Authorization": auth - }, - body: loadBalancerGet - }).catch((error) => { - console.log('Error Update load balancer'+ error); - }); - -} - -main(); diff --git a/scripts/travis/deploy/rancher-update.sh b/scripts/travis/deploy/rancher-update.sh deleted file mode 100755 index 1c576fe706..0000000000 --- a/scripts/travis/deploy/rancher-update.sh +++ /dev/null @@ -1,61 +0,0 @@ -#!/bin/bash - -show_help() { - echo "Usage: docker_publish.sh" - echo "" - echo "--access_key rancher access key" - echo "--secret_key rancher secret key" - echo "--url rancher_url" - echo "--environment_name service name to replace in rancher" - echo "--image image to gater and load in the service, example: docker:alfresco/demo-shell:latest" -} - -access_key(){ - ACCESSKEY=$1 -} - -secret_key(){ - SECRETKEY=$1 -} - -url(){ - RANCHERURL=$1 -} - -environment_name(){ - ENVIRONMENTNAME=$1 -} - -image_name(){ - IMAGE=$1 -} - -while [[ $1 == -* ]]; do - case "$1" in - -h|--help|-\?) show_help; exit 0;; - --access_key) access_key $2; shift 2;; - --secret_key) secret_key $2; shift 2;; - --url) url $2; shift 2;; - --environment_name) environment_name $2; shift 2;; - --image) image_name $2; shift 2;; - -*) echo "invalid option: $1" 1>&2; show_help; exit 1;; - esac -done - -docker pull etlweather/gaucho:alpine - -docker=$(which docker) - -echo "getting the id" - -ENVIRONMENTID=$($docker run --rm -e CATTLE_ACCESS_KEY=$ACCESSKEY \ - -e CATTLE_SECRET_KEY=$SECRETKEY \ - -e CATTLE_URL=$RANCHERURL \ - etlweather/gaucho:alpine id_of $ENVIRONMENTNAME) - -echo "id retrieved! is $ENVIRONMENTID" - -$docker run --rm -e CATTLE_ACCESS_KEY=$ACCESSKEY \ - -e CATTLE_SECRET_KEY=$SECRETKEY \ - -e CATTLE_URL=$RANCHERURL \ - etlweather/gaucho:alpine upgrade $ENVIRONMENTID --imageUuid $IMAGE --auto_complete true --timeout 600 diff --git a/scripts/travis/e2e/content-services-e2e.sh b/scripts/travis/e2e/content-services-e2e.sh index 88ea711452..bddd9cfc7e 100755 --- a/scripts/travis/e2e/content-services-e2e.sh +++ b/scripts/travis/e2e/content-services-e2e.sh @@ -22,7 +22,7 @@ fi; ./node_modules/@alfresco/adf-cli/bin/adf-cli check-cs-env --host "$E2E_HOST" -u "$E2E_USERNAME" -p "$E2E_PASSWORD" || exit 1 #-b is needed to run the Folder upload test that are not workin in Headless chrome -RUN_E2E=$(echo ./scripts/test-e2e-lib.sh -host http://localhost:4200 -proxy "$E2E_HOST" -u "$E2E_USERNAME" -p "$E2E_PASSWORD" -e "$E2E_EMAIL" --use-dist -b -save -m 4 || exit 1) +RUN_E2E=$(echo ./scripts/test-e2e-lib.sh -host http://localhost:4200 -proxy "$E2E_HOST" -u "$E2E_USERNAME" -p "$E2E_PASSWORD" --use-dist -b -m 4 || exit 1) if [[ $AFFECTED_LIBS =~ "testing" || $AFFECTED_LIBS =~ "$CONTEXT_ENV" || "${TRAVIS_EVENT_TYPE}" == "push" ]]; then echo "Run all e2e $CONTEXT_ENV" $RUN_CHECK diff --git a/scripts/travis/e2e/core-e2e.sh b/scripts/travis/e2e/core-e2e.sh index cd993a8978..d4e339421e 100755 --- a/scripts/travis/e2e/core-e2e.sh +++ b/scripts/travis/e2e/core-e2e.sh @@ -9,7 +9,7 @@ cd $DIR/../../../ export CONTEXT_ENV="core" export PROVIDER='ALL' -export AUTH_TYPE='BASIC' +export AUTH_TYPE='OAUTH' if [ "${TRAVIS_EVENT_TYPE}" == "pull_request" ]; then echo "Calculate affected e2e $BASE_HASH $HEAD_HASH" @@ -20,9 +20,9 @@ if [ "${TRAVIS_EVENT_TYPE}" == "pull_request" ]; then echo "Affected e2e ${AFFECTED_E2E}" fi; -./node_modules/@alfresco/adf-cli/bin/adf-cli check-ps-env --host "$E2E_HOST" -u "$E2E_USERNAME" -p "$E2E_PASSWORD" || exit 1 +#./node_modules/@alfresco/adf-cli/bin/adf-cli check-ps-env --host "$E2E_HOST" -u "$E2E_USERNAME" -p "$E2E_PASSWORD" || exit 1 TODO chanvge login check SSO ./node_modules/@alfresco/adf-cli/bin/adf-cli check-cs-env --host "$E2E_HOST" -u "$E2E_USERNAME" -p "$E2E_PASSWORD" || exit 1 -RUN_E2E=$(echo ./scripts/test-e2e-lib.sh -host http://localhost:4200 -proxy "$E2E_HOST" -u "$E2E_USERNAME" -p "$E2E_PASSWORD" -e "$E2E_EMAIL" --use-dist -m 2 || exit 1) +RUN_E2E=$(echo ./scripts/test-e2e-lib.sh -host http://localhost:4200 -proxy "$E2E_HOST" -u "$E2E_USERNAME" -p "$E2E_PASSWORD" --use-dist -m 2 || exit 1) if [[ $AFFECTED_LIBS =~ "testing" || $AFFECTED_LIBS =~ "$CONTEXT_ENV" || "${TRAVIS_EVENT_TYPE}" == "push" ]]; then echo "Run all e2e $CONTEXT_ENV" diff --git a/scripts/travis/e2e/insights-e2e.sh b/scripts/travis/e2e/insights-e2e.sh index 1b34eb7142..a46617495a 100755 --- a/scripts/travis/e2e/insights-e2e.sh +++ b/scripts/travis/e2e/insights-e2e.sh @@ -5,7 +5,7 @@ echo "Start insight e2e" DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" export PROVIDER='BPM' -export AUTH_TYPE='BASIC' +export AUTH_TYPE='OAUTH' cd $DIR/../../../ @@ -20,5 +20,5 @@ fi; if [[ $AFFECTED_LIBS =~ "testing" || $AFFECTED_LIBS =~ "insight" || "${TRAVIS_EVENT_TYPE}" == "push" ]]; then ./node_modules/@alfresco/adf-cli/bin/adf-cli check-ps-env --host "$E2E_HOST" -u "$E2E_USERNAME" -p "$E2E_PASSWORD" || exit 1; - ./scripts/test-e2e-lib.sh -host http://localhost:4200 -proxy "$E2E_HOST" -u "$E2E_USERNAME" -p "$E2E_PASSWORD" -e "$E2E_EMAIL" --folder insights --use-dist || exit 1; + ./scripts/test-e2e-lib.sh -host http://localhost:4200 -proxy "$E2E_HOST" -u "$E2E_USERNAME" -p "$E2E_PASSWORD" --folder insights --use-dist || exit 1; fi; diff --git a/scripts/travis/e2e/process-services-cloud-e2e.sh b/scripts/travis/e2e/process-services-cloud-e2e.sh index 15f198b25f..8e33b2bdaa 100755 --- a/scripts/travis/e2e/process-services-cloud-e2e.sh +++ b/scripts/travis/e2e/process-services-cloud-e2e.sh @@ -19,10 +19,10 @@ if [ "${TRAVIS_EVENT_TYPE}" == "pull_request" ]; then echo "Affected e2e ${AFFECTED_E2E}" fi; -RUN_E2E=$(echo ./scripts/test-e2e-lib.sh -host http://localhost:4200 -proxy "$E2E_HOST" -u "$E2E_USERNAME" -p "$E2E_PASSWORD" -e "$E2E_EMAIL" -host_sso "$E2E_HOST" -identity_admin_email "$E2E_ADMIN_EMAIL_IDENTITY" -identity_admin_password "$E2E_ADMIN_PASSWORD_IDENTITY" -prefix $TRAVIS_BUILD_NUMBER --use-dist -m 2 -save -b ) +RUN_E2E=$(echo ./scripts/test-e2e-lib.sh -host http://localhost:4200 -proxy "$E2E_HOST" -u "$E2E_USERNAME" -p "$E2E_PASSWORD" -host_sso "$HOST_SSO" -identity_admin_email "$E2E_ADMIN_EMAIL_IDENTITY" -identity_admin_password "$E2E_ADMIN_PASSWORD_IDENTITY" -prefix $TRAVIS_BUILD_NUMBER --use-dist -m 2 -save -b ) check_env(){ - ./node_modules/@alfresco/adf-cli/bin/adf-cli init-aae-env --host "$E2E_HOST" --oauth "$E2E_HOST" --modelerUsername "$E2E_MODELER_USERNAME" --modelerPassword "$E2E_MODELER_PASSWORD" --devopsUsername "$E2E_DEVOPS_USERNAME" --devopsPassword "$E2E_DEVOPS_PASSWORD" --clientId 'activiti' || exit 1 + ./node_modules/@alfresco/adf-cli/bin/adf-cli init-aae-env --host "$E2E_HOST" --oauth "$HOST_SSO" --modelerUsername "$E2E_MODELER_USERNAME" --modelerPassword "$E2E_MODELER_PASSWORD" --devopsUsername "$E2E_DEVOPS_USERNAME" --devopsPassword "$E2E_DEVOPS_PASSWORD" --clientId 'activiti' || exit 1 ./node_modules/@alfresco/adf-cli/bin/adf-cli check-cs-env --host "$E2E_HOST" -u "$E2E_ADMIN_EMAIL_IDENTITY" -p "$E2E_ADMIN_PASSWORD_IDENTITY" || exit 1 } diff --git a/scripts/travis/e2e/process-services-e2e.sh b/scripts/travis/e2e/process-services-e2e.sh index 2ca62a6da6..3e155f75e4 100755 --- a/scripts/travis/e2e/process-services-e2e.sh +++ b/scripts/travis/e2e/process-services-e2e.sh @@ -6,7 +6,7 @@ cd $DIR/../../../ export CONTEXT_ENV="process-services" export PROVIDER='BPM' -export AUTH_TYPE='BASIC' +export AUTH_TYPE="OAUTH" echo "Start process services e2e" @@ -19,8 +19,9 @@ if [ "${TRAVIS_EVENT_TYPE}" == "pull_request" ]; then echo "Affected e2e ${AFFECTED_E2E}" fi; -./node_modules/@alfresco/adf-cli/bin/adf-cli check-ps-env --host "$E2E_HOST" -u "$E2E_USERNAME" -p "$E2E_PASSWORD" || exit 1 -RUN_E2E=$(echo ./scripts/test-e2e-lib.sh -host http://localhost:4200 -proxy "$E2E_HOST" -u "$E2E_USERNAME" -p "$E2E_PASSWORD" -e "$E2E_EMAIL" --use-dist -m 2 || exit 1) +#./node_modules/@alfresco/adf-cli/bin/adf-cli check-ps-env --host "$E2E_HOST" -u "$E2E_USERNAME" -p "$E2E_PASSWORD" || exit 1 TODO chanvge login check SSO + +RUN_E2E=$(echo ./scripts/test-e2e-lib.sh -host http://localhost:4200 -proxy "$E2E_HOST" -host_sso "$HOST_SSO" -identity_admin_email "$E2E_ADMIN_EMAIL_IDENTITY" -identity_admin_password "$E2E_ADMIN_PASSWORD_IDENTITY" -u "$E2E_USERNAME" -p "$E2E_PASSWORD" --use-dist -m 2 -b || exit 1) if [[ $AFFECTED_LIBS =~ "testing" || $AFFECTED_LIBS =~ "$CONTEXT_ENV" || "${TRAVIS_EVENT_TYPE}" == "push" ]]; then echo "Run all e2e $CONTEXT_ENV" diff --git a/scripts/travis/e2e/search-e2e.sh b/scripts/travis/e2e/search-e2e.sh index bc336df348..2fac7ca55e 100755 --- a/scripts/travis/e2e/search-e2e.sh +++ b/scripts/travis/e2e/search-e2e.sh @@ -20,7 +20,7 @@ if [ "${TRAVIS_EVENT_TYPE}" == "pull_request" ];then fi; ./node_modules/@alfresco/adf-cli/bin/adf-cli check-cs-env --host "$E2E_HOST" -u "$E2E_USERNAME" -p "$E2E_PASSWORD" || exit 1 -RUN_E2E=$(echo ./scripts/test-e2e-lib.sh -host http://localhost:4200 -proxy "$E2E_HOST" -u "$E2E_USERNAME" -p "$E2E_PASSWORD" -e "$E2E_EMAIL" --use-dist -m 2 || exit 1) +RUN_E2E=$(echo ./scripts/test-e2e-lib.sh -host http://localhost:4200 -proxy "$E2E_HOST" -u "$E2E_USERNAME" -p "$E2E_PASSWORD" --use-dist -m 2 || exit 1) if [[ $AFFECTED_LIBS =~ "testing" || $AFFECTED_LIBS =~ "content-services" || "${TRAVIS_EVENT_TYPE}" == "push" ]]; then diff --git a/scripts/travis/deploy/get-docker-image-tag-name.sh b/scripts/travis/release/get-docker-image-tag-name.sh old mode 100755 new mode 100644 similarity index 100% rename from scripts/travis/deploy/get-docker-image-tag-name.sh rename to scripts/travis/release/get-docker-image-tag-name.sh diff --git a/scripts/travis/release/release-docker.sh b/scripts/travis/release/release-docker.sh new file mode 100644 index 0000000000..b524f9c9d0 --- /dev/null +++ b/scripts/travis/release/release-docker.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash + +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + +if [[ $TRAVIS_BRANCH == "develop" || $TRAVIS_BRANCH == "master" ]]; +then + + cd $DIR/../../../ + + # Get Tag Image + TAG_VERSION=$(./scripts/travis/release/get-docker-image-tag-name.sh) + echo "Running the docker with tag" $TAG_VERSION + + # Publish Image to docker + ./node_modules/@alfresco/adf-cli/bin/adf-cli docker-publish --loginCheck --loginUsername "$DOCKER_REPOSITORY_USER" --loginPassword "$DOCKER_REPOSITORY_PASSWORD" --loginRepo "$DOCKER_REPOSITORY_DOMAIN" --dockerRepo "$DOCKER_REPOSITORY" --dockerTags "$TAG_VERSION,$TRAVIS_BRANCH" --pathProject "$(pwd)" + +fi;