From 8b5e45f4eb07b4263ab8b08c016184a38c4a188c Mon Sep 17 00:00:00 2001 From: Dharan <14145706+dhrn@users.noreply.github.com> Date: Thu, 29 Apr 2021 18:10:49 +0530 Subject: [PATCH] [ADF-5366] initialize discovery and version compatibility service for oauth based session (#6864) * [ADF-5366] initialize discovery and version compatibility service for oauth based session * better error message * * fix spaces * * revert the search service * * fix build * * check properties after type update * * check properties after type update * * check properties after type update * * fix infinite loop * * fix test without title * * wait for options * * wait for session * Update metadata-content-type.e2e.ts * Update metadata-content-type.e2e.ts * Update protractor.excludes.json * Update protractor.excludes.json --- .../components/permissions-component.e2e.ts | 56 +-- .../components/site-permissions.e2e.ts | 126 +++--- .../metadata/metadata-content-type.e2e.ts | 95 +++-- e2e/core/pages/metadata-view.page.ts | 69 +++- e2e/protractor.conf.js | 37 +- e2e/protractor.excludes.json | 3 +- .../card-view-selectitem.component.html | 1 + .../node-download.directive.spec.ts | 8 +- lib/core/services/authentication.service.ts | 23 +- .../services/discovery-api.service.spec.ts | 368 ++++++++++-------- lib/core/services/discovery-api.service.ts | 8 +- .../services/external-alfresco-api.service.ts | 5 +- .../app-list-cloud.component.spec.ts | 3 +- .../apps-process-cloud.service.spec.ts | 3 +- .../form/services/form-cloud.service.spec.ts | 2 +- ...-definition-selector-cloud.service.spec.ts | 2 +- .../components/group-cloud.component.spec.ts | 3 +- .../components/people-cloud.component.spec.ts | 3 +- ...dit-process-filter-cloud.component.spec.ts | 3 +- .../notification-cloud.service.spec.ts | 3 +- .../user-preference-cloud.service.spec.ts | 3 +- .../start-task-cloud.component.spec.ts | 3 +- .../edit-task-filter-cloud.component.spec.ts | 3 +- .../task-header-cloud.component.spec.ts | 3 +- .../service-task-list-cloud.service.spec.ts | 3 +- .../services/task-list-cloud.service.spec.ts | 3 +- .../content-services/actions/model.actions.ts | 33 +- .../dialog/add-permissions-dialog.page.ts | 5 +- 28 files changed, 532 insertions(+), 345 deletions(-) diff --git a/e2e/content-services/components/permissions-component.e2e.ts b/e2e/content-services/components/permissions-component.e2e.ts index 1bdd5fd854..35253e5937 100644 --- a/e2e/content-services/components/permissions-component.e2e.ts +++ b/e2e/content-services/components/permissions-component.e2e.ts @@ -90,34 +90,41 @@ describe('Permissions Component', () => { let roleConsumerFolder, roleCoordinatorFolder, roleContributorFolder, roleCollaboratorFolder, roleEditorFolder; beforeAll(async () => { - await apiService.loginWithProfile('admin'); - await usersActions.createUser(fileOwnerUser); - await usersActions.createUser(filePermissionUser); - await apiService.getInstance().core.groupsApi.createGroup(groupBody); + try { + await apiService.loginWithProfile('admin'); + await usersActions.createUser(fileOwnerUser); + await usersActions.createUser(filePermissionUser); + await apiService.getInstance().core.groupsApi.createGroup(groupBody); - // to sync user in acs - await searchService.isUserSearchable(filePermissionUser); + await apiService.login(fileOwnerUser.username, fileOwnerUser.password); + roleConsumerFolder = await uploadActions.createFolder(roleConsumerFolderModel.name, '-my-'); + roleCoordinatorFolder = await uploadActions.createFolder(roleCoordinatorFolderModel.name, '-my-'); + roleContributorFolder = await uploadActions.createFolder(roleContributorFolderModel.name, '-my-'); + roleCollaboratorFolder = await uploadActions.createFolder(roleCollaboratorFolderModel.name, '-my-'); + roleEditorFolder = await uploadActions.createFolder(roleEditorFolderModel.name, '-my-'); - await apiService.login(fileOwnerUser.username, fileOwnerUser.password); - roleConsumerFolder = await uploadActions.createFolder(roleConsumerFolderModel.name, '-my-'); - roleCoordinatorFolder = await uploadActions.createFolder(roleCoordinatorFolderModel.name, '-my-'); - roleContributorFolder = await uploadActions.createFolder(roleContributorFolderModel.name, '-my-'); - roleCollaboratorFolder = await uploadActions.createFolder(roleCollaboratorFolderModel.name, '-my-'); - roleEditorFolder = await uploadActions.createFolder(roleEditorFolderModel.name, '-my-'); + await uploadActions.uploadFile(fileModel.location, 'RoleConsumer' + fileModel.name, roleConsumerFolder.entry.id); + await uploadActions.uploadFile(fileModel.location, 'RoleContributor' + fileModel.name, roleContributorFolder.entry.id); + await uploadActions.uploadFile(fileModel.location, 'RoleCoordinator' + fileModel.name, roleCoordinatorFolder.entry.id); + await uploadActions.uploadFile(fileModel.location, 'RoleCollaborator' + fileModel.name, roleCollaboratorFolder.entry.id); + await uploadActions.uploadFile(fileModel.location, 'RoleEditor' + fileModel.name, roleEditorFolder.entry.id); - await uploadActions.uploadFile(fileModel.location, 'RoleConsumer' + fileModel.name, roleConsumerFolder.entry.id); - await uploadActions.uploadFile(fileModel.location, 'RoleContributor' + fileModel.name, roleContributorFolder.entry.id); - await uploadActions.uploadFile(fileModel.location, 'RoleCoordinator' + fileModel.name, roleCoordinatorFolder.entry.id); - await uploadActions.uploadFile(fileModel.location, 'RoleCollaborator' + fileModel.name, roleCollaboratorFolder.entry.id); - await uploadActions.uploadFile(fileModel.location, 'RoleEditor' + fileModel.name, roleEditorFolder.entry.id); + await permissionActions.addRoleForUser(filePermissionUser.username, 'Consumer', roleConsumerFolder); + await permissionActions.addRoleForUser(filePermissionUser.username, 'Collaborator', roleCollaboratorFolder); + await permissionActions.addRoleForUser(filePermissionUser.username, 'Coordinator', roleCoordinatorFolder); + await permissionActions.addRoleForUser(filePermissionUser.username, 'Contributor', roleContributorFolder); + await permissionActions.addRoleForUser(filePermissionUser.username, 'Editor', roleEditorFolder); - await permissionActions.addRoleForUser(filePermissionUser.username, 'Consumer', roleConsumerFolder); - await permissionActions.addRoleForUser(filePermissionUser.username, 'Collaborator', roleCollaboratorFolder); - await permissionActions.addRoleForUser(filePermissionUser.username, 'Coordinator', roleCoordinatorFolder); - await permissionActions.addRoleForUser(filePermissionUser.username, 'Contributor', roleContributorFolder); - await permissionActions.addRoleForUser(filePermissionUser.username, 'Editor', roleEditorFolder); - - await browser.sleep(browser.params.testConfig.timeouts.index_search); // wait search index previous file/folder uploaded + // to sync user in acs + try { + await searchService.isUserSearchable(filePermissionUser); + } catch (e) { + console.error(`*****\n Failed to sync user \n*****`); + } + await browser.sleep(browser.params.testConfig.timeouts.index_search); // wait search index previous file/folder uploaded + } catch (e) { + fail('Failed to set up permission : \n' + JSON.stringify(e, null, 2)); + } }); describe('Inherit and assigning permissions', () => { @@ -209,6 +216,7 @@ describe('Permissions Component', () => { await permissionsPage.addPermissionsDialog.checkAddPermissionDialogIsDisplayed(); await permissionsPage.addPermissionsDialog.checkSearchUserInputIsDisplayed(); await permissionsPage.addPermissionsDialog.searchUserOrGroup(filePermissionUser.firstName); + await permissionsPage.addPermissionsDialog.checkResultListIsDisplayed(); await permissionsPage.addPermissionsDialog.clickUserOrGroup(filePermissionUser.firstName); await permissionsPage.addPermissionsDialog.selectRole(filePermissionUser.fullName, 'Contributor'); await expect(await permissionsPage.addPermissionsDialog.addButtonIsEnabled()).toBe(true, 'button should be enabled'); diff --git a/e2e/content-services/components/site-permissions.e2e.ts b/e2e/content-services/components/site-permissions.e2e.ts index 8ef969b5b9..77fa6b4d36 100644 --- a/e2e/content-services/components/site-permissions.e2e.ts +++ b/e2e/content-services/components/site-permissions.e2e.ts @@ -20,7 +20,7 @@ import { ApiService, BrowserActions, LoginPage, - NotificationHistoryPage, + NotificationHistoryPage, SearchService, StringUtil, UploadActions, UserModel, @@ -42,6 +42,7 @@ describe('Permissions Component', () => { const apiService = new ApiService(); const uploadActions = new UploadActions(apiService); + const searchService = new SearchService(apiService); const loginPage = new LoginPage(); const contentServicesPage = new ContentServicesPage(); @@ -89,71 +90,81 @@ describe('Permissions Component', () => { const usersActions = new UsersActions(apiService); beforeAll(async () => { - await apiService.loginWithProfile('admin'); - await usersActions.createUser(folderOwnerUser); - await usersActions.createUser(siteConsumerUser); - await usersActions.createUser(consumerUser); - await usersActions.createUser(contributorUser); - await usersActions.createUser(collaboratorUser); - await usersActions.createUser(managerUser); - await apiService.login(folderOwnerUser.username, folderOwnerUser.password); + try { + await apiService.loginWithProfile('admin'); + await usersActions.createUser(folderOwnerUser); + await usersActions.createUser(siteConsumerUser); + await usersActions.createUser(consumerUser); + await usersActions.createUser(contributorUser); + await usersActions.createUser(collaboratorUser); + await usersActions.createUser(managerUser); + await apiService.login(folderOwnerUser.username, folderOwnerUser.password); - await browser.sleep(browser.params.testConfig.timeouts.index_search); + const publicSiteName = `PUBLIC_TEST_SITE_${StringUtil.generateRandomString(5)}`; - const publicSiteName = `PUBLIC_TEST_SITE_${StringUtil.generateRandomString(5)}`; + const privateSiteName = `PRIVATE_TEST_SITE_${StringUtil.generateRandomString(5)}`; - const privateSiteName = `PRIVATE_TEST_SITE_${StringUtil.generateRandomString(5)}`; + folderName = `MEESEEKS_${StringUtil.generateRandomString(5)}`; - folderName = `MEESEEKS_${StringUtil.generateRandomString(5)}`; + const publicSiteBody = { visibility: 'PUBLIC', title: publicSiteName }; + const privateSiteBody = { visibility: 'PRIVATE', title: privateSiteName }; - const publicSiteBody = { visibility: 'PUBLIC', title: publicSiteName }; - const privateSiteBody = { visibility: 'PRIVATE', title: privateSiteName }; + const sitesApi = new SitesApi(apiService.getInstance()); - const sitesApi = new SitesApi(apiService.getInstance()); + publicSite = await sitesApi.createSite(publicSiteBody); + privateSite = await sitesApi.createSite(privateSiteBody); - publicSite = await sitesApi.createSite(publicSiteBody); - privateSite = await sitesApi.createSite(privateSiteBody); - - await sitesApi.createSiteMembership(publicSite.entry.id, { - id: siteConsumerUser.username, - role: CONSTANTS.CS_USER_ROLES.CONSUMER - }); - - await sitesApi.createSiteMembership(publicSite.entry.id, { - id: collaboratorUser.username, - role: CONSTANTS.CS_USER_ROLES.COLLABORATOR - }); - - await sitesApi.createSiteMembership(publicSite.entry.id, { - id: contributorUser.username, - role: CONSTANTS.CS_USER_ROLES.CONTRIBUTOR - }); - - await sitesApi.createSiteMembership(publicSite.entry.id, { - id: managerUser.username, - role: CONSTANTS.CS_USER_ROLES.MANAGER - }); - - await sitesApi.createSiteMembership(privateSite.entry.id, { - id: managerUser.username, - role: CONSTANTS.CS_USER_ROLES.MANAGER - }); - - siteFolder = await uploadActions.createFolder(folderName, publicSite.entry.guid); - privateSiteFile = await uploadActions.uploadFile(fileModel.location, 'privateSite' + fileModel.name, privateSite.entry.guid); - - await apiService.getInstance().core.nodesApi.updateNode(privateSiteFile.entry.id, - { - permissions: { - locallySet: [{ - authorityId: managerUser.username, - name: 'SiteConsumer', - accessStatus: 'ALLOWED' - }] - } + await sitesApi.createSiteMembership(publicSite.entry.id, { + id: siteConsumerUser.username, + role: CONSTANTS.CS_USER_ROLES.CONSUMER }); - await uploadActions.uploadFile(fileModel.location, 'Site' + fileModel.name, siteFolder.entry.id); + await sitesApi.createSiteMembership(publicSite.entry.id, { + id: collaboratorUser.username, + role: CONSTANTS.CS_USER_ROLES.COLLABORATOR + }); + + await sitesApi.createSiteMembership(publicSite.entry.id, { + id: contributorUser.username, + role: CONSTANTS.CS_USER_ROLES.CONTRIBUTOR + }); + + await sitesApi.createSiteMembership(publicSite.entry.id, { + id: managerUser.username, + role: CONSTANTS.CS_USER_ROLES.MANAGER + }); + + await sitesApi.createSiteMembership(privateSite.entry.id, { + id: managerUser.username, + role: CONSTANTS.CS_USER_ROLES.MANAGER + }); + + siteFolder = await uploadActions.createFolder(folderName, publicSite.entry.guid); + privateSiteFile = await uploadActions.uploadFile(fileModel.location, 'privateSite' + fileModel.name, privateSite.entry.guid); + + await apiService.getInstance().core.nodesApi.updateNode(privateSiteFile.entry.id, + { + permissions: { + locallySet: [{ + authorityId: managerUser.username, + name: 'SiteConsumer', + accessStatus: 'ALLOWED' + }] + } + }); + + await uploadActions.uploadFile(fileModel.location, 'Site' + fileModel.name, siteFolder.entry.id); + + // to sync user in acs + try { + await searchService.isUserSearchable(consumerUser); + } catch (e) { + console.error(`*****\n Failed to sync user \n*****`); + } + await browser.sleep(browser.params.testConfig.timeouts.index_search); + } catch (error) { + fail('Failed to setup site permission : ' + JSON.stringify(error, null, 2)); + } }); afterAll(async () => { @@ -192,6 +203,7 @@ describe('Permissions Component', () => { await permissionsPage.addPermissionsDialog.checkSearchUserInputIsDisplayed(); await permissionsPage.addPermissionsDialog.searchUserOrGroup(consumerUser.username); + await permissionsPage.addPermissionsDialog.checkResultListIsDisplayed(); await permissionsPage.addPermissionsDialog.clickUserOrGroup(consumerUser.firstName); await permissionsPage.addPermissionsDialog.selectRole(consumerUser.fullName, 'Site Collaborator'); diff --git a/e2e/content-services/metadata/metadata-content-type.e2e.ts b/e2e/content-services/metadata/metadata-content-type.e2e.ts index 4c1434ac48..5579d4e7f7 100644 --- a/e2e/content-services/metadata/metadata-content-type.e2e.ts +++ b/e2e/content-services/metadata/metadata-content-type.e2e.ts @@ -20,6 +20,7 @@ import { BrowserActions, LoginPage, ModelActions, + StringUtil, UploadActions, UserModel, UsersActions, @@ -30,26 +31,31 @@ import { FileModel } from '../../models/ACS/file.model'; import { browser } from 'protractor'; import { MetadataViewPage } from '../../core/pages/metadata-view.page'; import { NavigationBarPage } from '../../core/pages/navigation-bar.page'; +import { ContentServicesPage } from '../../core/pages/content-services.page'; describe('content type', () => { const apiService = new ApiService(); const usersActions = new UsersActions(apiService); const modelActions = new ModelActions(apiService); + const uploadActions = new UploadActions(apiService); const viewerPage = new ViewerPage(); const metadataViewPage = new MetadataViewPage(); const navigationBarPage = new NavigationBarPage(); + const contentServicesPage = new ContentServicesPage(); const loginPage = new LoginPage(); + const randomString = StringUtil.generateRandomString(); const model: CustomModel = { - name: 'test', - namespaceUri: 'http://www.customModel.com/model/e2e/1.0', - namespacePrefix: 'e2e', + name: `test-${randomString}`, + namespaceUri: `http://www.customModel.com/model/${randomString}/1.0`, + namespacePrefix: `e2e-${randomString}`, author: 'E2e Automation User', description: 'Custom type e2e model', status: 'DRAFT' }; - const type: CustomType = { name: 'test', parentName: 'cm:content', title: 'Test type' }; + const type: CustomType = { name: `test-type-${randomString}`, parentName: 'cm:content', title: `Test type - ${randomString}` }; + const property = { name: `test-property-${randomString}`, title: `Test property - ${randomString}`, dataType: 'd:text', defaultValue: randomString }; const pdfFile = new FileModel({ name: browser.params.resources.Files.ADF_DOCUMENTS.PDF.file_name }); const docxFileModel = new FileModel({ name: browser.params.resources.Files.ADF_DOCUMENTS.TEST.file_name, @@ -60,81 +66,105 @@ describe('content type', () => { beforeAll( async () => { try { await apiService.loginWithProfile('admin'); - - const typePaging = await modelActions.listTypes({where: `(namespaceUri matches('http://www.customModel.*'))`}); - if (typePaging.list.pagination.count === 0) { - await modelActions.createModel(model); - await modelActions.createType(model.name, type); - await modelActions.activateCustomModel(model.name); - } + await modelActions.createModel(model); + await modelActions.createType(model.name, type); + await modelActions.addPropertyToType(model.name, type.name, [property]); + await modelActions.activateCustomModel(model.name); + await modelActions.isCustomTypeSearchable(type.title); acsUser = await usersActions.createUser(); await apiService.login(acsUser.username, acsUser.password); - const uploadActions = new UploadActions(apiService); const filePdfNode = await uploadActions.uploadFile(pdfFile.location, pdfFile.name, '-my-'); pdfFile.id = filePdfNode.entry.id; const docsNode = await uploadActions.uploadFile(docxFileModel.location, docxFileModel.name, '-my-'); docxFileModel.id = docsNode.entry.id; - await loginPage.login(acsUser.username, acsUser.password); } catch (e) { - console.error('Failed to setup custom types', JSON.stringify(e, null, 2)); - fail(); + fail('Failed to setup custom types :: ' + JSON.stringify(e, null, 2)); } }); + afterAll(async () => { + await apiService.login(acsUser.username, acsUser.password); + await uploadActions.deleteFileOrFolder(pdfFile.id); + await uploadActions.deleteFileOrFolder(docxFileModel.id); + try { + await apiService.loginWithProfile('admin'); + await modelActions.deactivateCustomModel(model.name); + await modelActions.deleteCustomModel(model.name); + } catch (e) { + console.error('failed to delete the model', e); + } + }); + + beforeEach( async () => { + await loginPage.login(acsUser.username, acsUser.password); + await navigationBarPage.navigateToContentServices(); + await contentServicesPage.contentList.dataTablePage().waitTillContentLoaded(); + await browser.sleep(8000); // wait for sso session to initiate + }); + + afterEach( async () => { + await navigationBarPage.clickLogoutButton(); + }); + it('[C593560] Should the user be able to select a new content type and save it only after the confirmation dialog', async () => { await BrowserActions.getUrl(browser.baseUrl + `/(overlay:files/${pdfFile.id}/view)`); - + await viewerPage.checkFileIsLoaded(pdfFile.name); await viewerPage.clickInfoButton(); await viewerPage.checkInfoSideBarIsDisplayed(); await metadataViewPage.clickOnPropertiesTab(); await metadataViewPage.editIconIsDisplayed(); await expect(await viewerPage.getActiveTab()).toEqual('PROPERTIES'); - await metadataViewPage.hasContentType('Content'); + const defaultType = (await metadataViewPage.hasContentType('Content')) || (await metadataViewPage.hasContentType('cm:content')); + await expect(defaultType).toBe(true, 'Content type not found'); await metadataViewPage.editIconClick(); - await metadataViewPage.changeContentType(type.title); + await expect(await metadataViewPage.changeContentType(type.title)).toBe(true, 'Failed to update node type.'); await metadataViewPage.clickSaveMetadata(); - await metadataViewPage.checkConfirmDialogDisplayed(); await metadataViewPage.applyNodeProperties(); + await expect(await metadataViewPage.checkPropertyDisplayed(`properties.${model.namespacePrefix}:${property.name}`)) + .toContain(property.defaultValue, 'Custom property not found'); + await navigationBarPage.clickLogoutButton(); await loginPage.login(acsUser.username, acsUser.password); await navigationBarPage.navigateToContentServices(); - - await viewerPage.viewFile(pdfFile.name); - await viewerPage.checkFileIsLoaded(); - + await contentServicesPage.contentList.dataTablePage().waitTillContentLoaded(); + await BrowserActions.getUrl(browser.baseUrl + `/(overlay:files/${pdfFile.id}/view)`); + await viewerPage.checkFileIsLoaded(pdfFile.name); await viewerPage.clickInfoButton(); await viewerPage.checkInfoSideBarIsDisplayed(); await metadataViewPage.clickOnPropertiesTab(); await metadataViewPage.editIconIsDisplayed(); await expect(await viewerPage.getActiveTab()).toEqual('PROPERTIES'); - await metadataViewPage.hasContentType(type.title); + const customType = (await metadataViewPage.hasContentType(type.title)) || (await metadataViewPage.hasContentType(`${model.namespacePrefix}:${type.name}`)); + await expect(customType).toBe(true, 'Custom type not found'); + await expect(await metadataViewPage.getPropertyText(`properties.${model.namespacePrefix}:${property.name}`)).toContain(property.defaultValue); await viewerPage.clickCloseButton(); }); it('[C593559] Should the user be able to select a new content type and not save it when press cancel in the confirmation dialog', async () => { await BrowserActions.getUrl(browser.baseUrl + `/(overlay:files/${docxFileModel.id}/view)`); - + await viewerPage.checkFileIsLoaded(docxFileModel.name); await viewerPage.clickInfoButton(); await viewerPage.checkInfoSideBarIsDisplayed(); await metadataViewPage.clickOnPropertiesTab(); await metadataViewPage.editIconIsDisplayed(); await expect(await viewerPage.getActiveTab()).toEqual('PROPERTIES'); - await metadataViewPage.hasContentType('Content'); + let defaultType = (await metadataViewPage.hasContentType('Content')) || (await metadataViewPage.hasContentType('cm:content')); + await expect(defaultType).toBe(true, 'Content type not found'); await metadataViewPage.editIconClick(); - await metadataViewPage.changeContentType(type.title); + await expect(await metadataViewPage.changeContentType(type.title)).toBe(true, 'Failed to update node type.'); await metadataViewPage.clickSaveMetadata(); await metadataViewPage.checkConfirmDialogDisplayed(); @@ -143,18 +173,17 @@ describe('content type', () => { await navigationBarPage.clickLogoutButton(); await loginPage.login(acsUser.username, acsUser.password); await navigationBarPage.navigateToContentServices(); - - await viewerPage.viewFile(docxFileModel.name); - await viewerPage.checkFileIsLoaded(); - + await contentServicesPage.contentList.dataTablePage().waitTillContentLoaded(); + await BrowserActions.getUrl(browser.baseUrl + `/(overlay:files/${docxFileModel.id}/view)`); + await viewerPage.checkFileIsLoaded(docxFileModel.name); await viewerPage.clickInfoButton(); await viewerPage.checkInfoSideBarIsDisplayed(); await metadataViewPage.clickOnPropertiesTab(); await metadataViewPage.editIconIsDisplayed(); await expect(await viewerPage.getActiveTab()).toEqual('PROPERTIES'); - await metadataViewPage.hasContentType('Content'); - + defaultType = (await metadataViewPage.hasContentType('Content')) || (await metadataViewPage.hasContentType('cm:content')); + await expect(defaultType).toBe(true, 'Content type not found'); await viewerPage.clickCloseButton(); }); }); diff --git a/e2e/core/pages/metadata-view.page.ts b/e2e/core/pages/metadata-view.page.ts index 4eabcac9d7..501f7e0b2c 100644 --- a/e2e/core/pages/metadata-view.page.ts +++ b/e2e/core/pages/metadata-view.page.ts @@ -15,8 +15,8 @@ * limitations under the License. */ -import { Locator, by, element, Key, protractor } from 'protractor'; -import { BrowserVisibility, BrowserActions, DropdownPage, TestElement } from '@alfresco/adf-testing'; +import { by, element, Key, Locator, protractor } from 'protractor'; +import { BrowserActions, BrowserVisibility, DropdownPage, TestElement, Logger } from '@alfresco/adf-testing'; export class MetadataViewPage { @@ -216,18 +216,60 @@ export class MetadataViewPage { await BrowserVisibility.waitUntilElementIsVisible(property); } - async hasContentType(contentType: string): Promise { - const nodeType = TestElement.byText('div[data-automation-id="header-nodeType"] .adf-property-value', contentType); - return nodeType.waitVisible(); + async hasContentType(contentType: string, attempt = 0, maxAttempt = 3): Promise { + const contentTypeSelector = '[data-automation-id="select-readonly-value-nodeType"]'; + const type = TestElement.byText(contentTypeSelector, contentType); + try { + if (attempt > maxAttempt) { + return false; + } + await type.waitVisible(); + const isPresent = type.isPresent(); + if (isPresent) { + return true; + } + return this.hasContentType(contentType, attempt + 1, maxAttempt); + } catch (e) { + Logger.log(`re trying content type attempt :: ${attempt}`); + return this.hasContentType(contentType, attempt + 1, maxAttempt); + } } - async changeContentType(option: string): Promise { - const nodeType = TestElement.byCss('div[data-automation-id="header-nodeType"] mat-form-field'); - await nodeType.waitPresent(); - await nodeType.click(); - const typesDropDownPage = new DropdownPage(nodeType.elementFinder); - await typesDropDownPage.checkOptionIsDisplayed(option); - await typesDropDownPage.selectOption(option); + async checkPropertyDisplayed(propertyName: string, type?: string, attempt = 0, maxAttempt = 3): Promise { + try { + if (attempt > maxAttempt) { + return ''; + } + const propertyType = type || 'textitem'; + await TestElement.byCss('[data-automation-id="card-' + propertyType + '-value-' + propertyName + '"]').waitVisible(); + return this.getPropertyText(propertyName); + } catch (e) { + Logger.log(`re trying custom property attempt :: ${attempt}`); + return this.checkPropertyDisplayed(propertyName, type, attempt + 1, maxAttempt); + } + } + + async changeContentType(option: string, attempt = 0, maxAttempt = 3): Promise { + const nodeType = TestElement.byCss('div[data-automation-id="header-nodeType"] .mat-select-trigger'); + if (attempt > maxAttempt) { + console.error(`content type select option not found. check acs version may be lesser than 7.0.0`); + return false; + } + try { + await nodeType.waitVisible(); + if (await nodeType.isPresent()) { + await nodeType.click(); + const typesDropDownPage = new DropdownPage(nodeType.elementFinder); + await typesDropDownPage.checkOptionIsDisplayed(option); + await typesDropDownPage.selectOption(option); + return true; + } + return this.changeContentType(option, attempt + 1, maxAttempt); + } catch (error) { + Logger.log(`re trying content type options attempt :: ${attempt}`); + await BrowserActions.closeMenuAndDialogs(); + return this.changeContentType(option, attempt + 1, maxAttempt); + } } async checkConfirmDialogDisplayed(): Promise { @@ -246,8 +288,7 @@ export class MetadataViewPage { } async checkPropertyIsNotVisible(propertyName: string, type: string): Promise { - const property = element(by.css('div[data-automation-id="card-' + type + '-label-' + propertyName + '"]')); - await BrowserVisibility.waitUntilElementIsNotVisible(property); + await TestElement.byCss('div[data-automation-id="card-' + type + '-label-' + propertyName + '"]').waitNotVisible(); } async clickCloseButton(): Promise { diff --git a/e2e/protractor.conf.js b/e2e/protractor.conf.js index cc31ecf407..cef63f1b77 100644 --- a/e2e/protractor.conf.js +++ b/e2e/protractor.conf.js @@ -250,13 +250,30 @@ exports.config = { }) ); + function disableCSSAnimation() { + const css = '* {' + + '-webkit-transition-duration: 0s !important;' + + 'transition-duration: 0s !important;' + + '-webkit-animation-duration: 0s !important;' + + 'animation-duration: 0s !important;' + + '}'; + const head = document.head || document.getElementsByTagName('head')[0]; + const style = document.createElement('style'); + + style.type = 'text/css'; + style.appendChild(document.createTextNode(css)); + head.appendChild(style); + } + // @ts-ignore await browser.driver.executeScript(disableCSSAnimation); // @ts-ignore + await browser.waitForAngularEnabled(false); await browser.get(`${HOST}/#/settings`); + await browser.waitForAngularEnabled(true); - if (typeof LocalStorageUtil.clearStorage === "function") { + try { await LocalStorageUtil.clearStorage(); // @ts-ignore await LocalStorageUtil.setStorageItem('ecmHost', browser.params.testConfig.appConfig.ecmHost); @@ -280,26 +297,10 @@ exports.config = { await LocalStorageUtil.apiReset(); - } else { + } catch (error) { Logger.error(`====== Demo shell not able to start ======`); process.exit(); } - - function disableCSSAnimation() { - const css = '* {' + - '-webkit-transition-duration: 0s !important;' + - 'transition-duration: 0s !important;' + - '-webkit-animation-duration: 0s !important;' + - 'animation-duration: 0s !important;' + - '}'; - const head = document.head || document.getElementsByTagName('head')[0]; - const style = document.createElement('style'); - - style.type = 'text/css'; - style.appendChild(document.createTextNode(css)); - head.appendChild(style); - } - }, afterLaunch: async function (statusCode) { diff --git a/e2e/protractor.excludes.json b/e2e/protractor.excludes.json index bf3f9244f0..005c954acf 100644 --- a/e2e/protractor.excludes.json +++ b/e2e/protractor.excludes.json @@ -4,7 +4,8 @@ "C279932": "login problem APS not basic", "C279931": "login problem APS not basic", "C279930": "login problem APS not basic", - "C593560": "https://alfresco.atlassian.net/browse/ADF-5366", + "C593560": "https://alfresco.atlassian.net/browse/ADF-5388", + "C593559": "https://alfresco.atlassian.net/browse/ADF-5388", "C269081": "https://alfresco.atlassian.net/browse/ADF-5385", "C272819": "https://alfresco.atlassian.net/browse/ADF-5385", "C290069": "https://alfresco.atlassian.net/browse/ADF-5387" diff --git a/lib/core/card-view/components/card-view-selectitem/card-view-selectitem.component.html b/lib/core/card-view/components/card-view-selectitem/card-view-selectitem.component.html index 6825ea3857..dcab4a4d0f 100644 --- a/lib/core/card-view/components/card-view-selectitem/card-view-selectitem.component.html +++ b/lib/core/card-view/components/card-view-selectitem/card-view-selectitem.component.html @@ -4,6 +4,7 @@
{{ (property.displayValue | async) | translate }}
diff --git a/lib/core/directives/node-download.directive.spec.ts b/lib/core/directives/node-download.directive.spec.ts index 68800aa8a1..bec3ada383 100755 --- a/lib/core/directives/node-download.directive.spec.ts +++ b/lib/core/directives/node-download.directive.spec.ts @@ -41,6 +41,12 @@ describe('NodeDownloadDirective', () => { let apiService: AlfrescoApiService; let contentService; let dialogSpy; + const mockOauth2Auth = { + oauth2Auth: { + callCustomApi: () => Promise.resolve(), + on: jasmine.createSpy('on') + } + }; setupTestBed({ imports: [ @@ -63,7 +69,7 @@ describe('NodeDownloadDirective', () => { }); it('should not download node when selection is empty', () => { - spyOn(apiService, 'getInstance'); + spyOn(apiService, 'getInstance').and.returnValue(mockOauth2Auth); component.selection = []; fixture.detectChanges(); diff --git a/lib/core/services/authentication.service.ts b/lib/core/services/authentication.service.ts index 9a0e1050bd..d45aa2b557 100644 --- a/lib/core/services/authentication.service.ts +++ b/lib/core/services/authentication.service.ts @@ -16,7 +16,7 @@ */ import { Injectable } from '@angular/core'; -import { Observable, from, throwError, Observer, ReplaySubject } from 'rxjs'; +import { Observable, from, throwError, Observer, ReplaySubject, Subject } from 'rxjs'; import { AlfrescoApiService } from './alfresco-api.service'; import { CookieService } from './cookie.service'; import { LogService } from './log.service'; @@ -39,7 +39,19 @@ export class AuthenticationService { private bearerExcludedUrls: string[] = ['auth/realms', 'resources/', 'assets/']; + /** + * Emits oAuth token exchange event + */ + onTokenExchange: Subject = new Subject(); + + /** + * Emits Basic auth login event + */ onLogin: ReplaySubject = new ReplaySubject(1); + + /** + * Emits logout event + */ onLogout: ReplaySubject = new ReplaySubject(1); constructor( @@ -48,6 +60,15 @@ export class AuthenticationService { private alfrescoApi: AlfrescoApiService, private cookie: CookieService, private logService: LogService) { + this.listenForOauthTokenExchange(); + } + + private listenForOauthTokenExchange() { + this.alfrescoApi.alfrescoApiInitialized.subscribe(() => { + this.alfrescoApi.getInstance().oauth2Auth?.on('token_issued', () => { + this.onTokenExchange.next(this.alfrescoApi.getInstance().oauth2Auth.token); + }); + }); } /** diff --git a/lib/core/services/discovery-api.service.spec.ts b/lib/core/services/discovery-api.service.spec.ts index 1b75aba53f..d25a219c73 100644 --- a/lib/core/services/discovery-api.service.spec.ts +++ b/lib/core/services/discovery-api.service.spec.ts @@ -17,64 +17,64 @@ import { TestBed } from '@angular/core/testing'; import { BpmProductVersionModel, EcmProductVersionModel } from '../models/product-version.model'; -import { AppConfigService } from '../app-config/app-config.service'; import { DiscoveryApiService } from './discovery-api.service'; +import { AlfrescoApiService } from './alfresco-api.service'; +import { AuthenticationService } from './authentication.service'; import { setupTestBed } from '../testing/setup-test-bed'; import { CoreTestingModule } from '../testing/core.testing.module'; import { SystemPropertiesRepresentation } from '@alfresco/js-api'; import { TranslateModule } from '@ngx-translate/core'; - -declare let jasmine: any; +import { of, throwError } from 'rxjs'; const fakeEcmDiscoveryResponse: any = { - 'entry': { - 'repository': { - 'edition': 'FAKE', - 'version': { - 'major': '5', - 'minor': '2', - 'patch': '0', - 'hotfix': '0', - 'schema': 999999, - 'label': 'r134899-b26', - 'display': '5.2.0.0 (r134899-b26) schema 10005' + entry: { + repository: { + edition: 'FAKE', + version: { + major: '5', + minor: '2', + patch: '0', + hotfix: '0', + schema: 999999, + label: 'r134899-b26', + display: '5.2.0.0 (r134899-b26) schema 10005' }, - 'license': { - 'issuedAt': '2017-06-22T10:56:45.796+0000', - 'expiresAt': '2017-07-22T00:00:00.000+0000', - 'remainingDays': 4, - 'holder': 'Trial User', - 'mode': 'ENTERPRISE', - 'entitlements': { - 'isClusterEnabled': false, - 'isCryptodocEnabled': false + license: { + issuedAt: '2017-06-22T10:56:45.796+0000', + expiresAt: '2017-07-22T00:00:00.000+0000', + remainingDays: 4, + holder: 'Trial User', + mode: 'ENTERPRISE', + entitlements: { + isClusterEnabled: false, + isCryptodocEnabled: false } }, - 'status': { - 'isReadOnly': false, - 'isAuditEnabled': true, - 'isQuickShareEnabled': true, - 'isThumbnailGenerationEnabled': true + status: { + isReadOnly: false, + isAuditEnabled: true, + isQuickShareEnabled: true, + isThumbnailGenerationEnabled: true }, - 'modules': [ + modules: [ { - 'id': 'alfresco-fake-services', - 'title': 'Alfresco Share Services AMP', - 'description': 'Module to be applied to alfresco.war, containing APIs for Alfresco Share', - 'version': '5.2.0', - 'installDate': '2017-03-07T08:48:14.161+0000', - 'installState': 'INSTALLED', - 'versionMin': '5.1', - 'versionMax': '999' + id: 'alfresco-fake-services', + title: 'Alfresco Share Services AMP', + description: 'Module to be applied to alfresco.war, containing APIs for Alfresco Share', + version: '5.2.0', + installDate: '2017-03-07T08:48:14.161+0000', + installState: 'INSTALLED', + versionMin: '5.1', + versionMax: '999' }, { - 'id': 'alfresco-trashcan-fake', - 'title': 'alfresco-trashcan-cleaner project', - 'description': 'The Alfresco Trash Can Cleaner (Alfresco Module)', - 'version': '2.2', - 'installState': 'UNKNOWN', - 'versionMin': '0', - 'versionMax': '999' + id: 'alfresco-trashcan-fake', + title: 'alfresco-trashcan-cleaner project', + description: 'The Alfresco Trash Can Cleaner (Alfresco Module)', + version: '2.2', + installState: 'UNKNOWN', + versionMin: '0', + versionMax: '999' } ] } @@ -82,149 +82,177 @@ const fakeEcmDiscoveryResponse: any = { }; const fakeBPMDiscoveryResponse: any = { - 'revisionVersion': '2', - 'edition': 'SUPER FAKE EDITION', - 'type': 'bpmSuite', - 'majorVersion': '1', - 'minorVersion': '6' - }; + revisionVersion: '2', + edition: 'SUPER FAKE EDITION', + type: 'bpmSuite', + majorVersion: '1', + minorVersion: '6' +}; const fakeBPMDiscoverySystemPropertyResponse: any = { - 'allowInvolveByEmail': true, - 'disableJavaScriptEventsInFormEditor': false, - 'logoutDisabled': false, - 'authConfiguration': { - 'authUrl': 'fakeAuthUrl', - 'realm': 'fakeRealm', - 'clientId': 'fakeClient', - 'useBrowserLogout': true + allowInvolveByEmail: true, + disableJavaScriptEventsInFormEditor: false, + logoutDisabled: false, + authConfiguration: { + authUrl: 'fakeAuthUrl', + realm: 'fakeRealm', + clientId: 'fakeClient', + useBrowserLogout: true } }; describe('Discovery Api Service', () => { - let service: DiscoveryApiService; + let apiService: AlfrescoApiService; + let authenticationService: AuthenticationService; - setupTestBed({ - imports: [ - TranslateModule.forRoot(), - CoreTestingModule - ] - }); + describe('Basic auth', () => { + setupTestBed({ + imports: [TranslateModule.forRoot(), CoreTestingModule] + }); - beforeEach(() => { - const appConfig: AppConfigService = TestBed.inject(AppConfigService); - appConfig.config = { - ecmHost: 'http://localhost:9876/ecm' - }; - service = TestBed.inject(DiscoveryApiService); - jasmine.Ajax.install(); - }); + beforeEach(() => { + service = TestBed.inject(DiscoveryApiService); + apiService = TestBed.inject(AlfrescoApiService); + }); - afterEach(() => { - jasmine.Ajax.uninstall(); - }); + describe('For ECM', () => { + it('Should retrieve the info about the product for ECM', done => { + spyOn(apiService.getInstance().discovery.discoveryApi, 'getRepositoryInformation') + .and.returnValue(of(fakeEcmDiscoveryResponse)); - describe('For ECM', () => { - it('Should retrieve the info about the product for ECM', (done) => { - service.getEcmProductInfo().subscribe((data: EcmProductVersionModel) => { - expect(data).not.toBeNull(); - expect(data.edition).toBe('FAKE'); - expect(data.version.schema).toBe(999999); - expect(data.license.isClusterEnabled).toBeFalsy(); - expect(data.status.isQuickShareEnabled).toBeTruthy(); - expect(data.modules.length).toBe(2); - expect(data.modules[0].id).toBe('alfresco-fake-services'); - expect(data.modules[1].id).toBe('alfresco-trashcan-fake'); - done(); + service.getEcmProductInfo() + .subscribe((data: EcmProductVersionModel) => { + expect(data).not.toBeNull(); + expect(data.edition).toBe('FAKE'); + expect(data.version.schema).toBe(999999); + expect(data.license.isClusterEnabled).toBeFalsy(); + expect(data.status.isQuickShareEnabled).toBeTruthy(); + expect(data.modules.length).toBe(2); + expect(data.modules[0].id).toBe('alfresco-fake-services'); + expect(data.modules[1].id).toBe('alfresco-trashcan-fake'); + done(); + }); }); - jasmine.Ajax.requests.mostRecent().respondWith({ - status: 200, - contentType: 'json', - responseText: fakeEcmDiscoveryResponse + it('getEcmProductInfo catch errors call', done => { + spyOn(apiService.getInstance().discovery.discoveryApi, 'getRepositoryInformation') + .and.returnValue(throwError({ status: 403 })); + + service.getEcmProductInfo().subscribe( + () => {}, + () => { + done(); + } + ); }); }); - it('getEcmProductInfo catch errors call', (done) => { - service.getEcmProductInfo().subscribe( - () => { - }, - () => { + describe('For BPM', () => { + it('Should retrieve the info about the product for BPM', done => { + spyOn(apiService.getInstance().activiti.aboutApi, 'getAppVersion') + .and.returnValue(of(fakeBPMDiscoveryResponse)); + + service.getBpmProductInfo().subscribe((data: BpmProductVersionModel) => { + expect(data).not.toBeNull(); + expect(data.edition).toBe('SUPER FAKE EDITION'); + expect(data.revisionVersion).toBe('2'); + expect(data.type).toBe('bpmSuite'); + done(); + }); + }); + + it('getBpmProductInfo catch errors call', done => { + spyOn(apiService.getInstance().activiti.aboutApi, 'getAppVersion') + .and.returnValue(throwError({ status: 403 })); + + service.getBpmProductInfo().subscribe( + () => {}, + () => { + done(); + } + ); + }); + + it('Should retrieve the system properties for BPM', done => { + spyOn(apiService.getInstance().activiti.systemPropertiesApi, 'getProperties') + .and.returnValue(of(fakeBPMDiscoverySystemPropertyResponse)); + + service.getBPMSystemProperties().subscribe((data: SystemPropertiesRepresentation) => { + expect(data).not.toBeNull(); + expect(data.allowInvolveByEmail).toBe(true); + expect(data.disableJavaScriptEventsInFormEditor).toBe(false); + expect(data.logoutDisabled).toBe(false); + expect(data.authConfiguration.authUrl).toBe('fakeAuthUrl'); + expect(data.authConfiguration.realm).toBe('fakeRealm'); + expect(data.authConfiguration.clientId).toBe('fakeClient'); + expect(data.authConfiguration.useBrowserLogout).toBe(true); + done(); + }); + }); + + it('Should retrieve the system properties for BPM', done => { + spyOn( + apiService.getInstance().activiti.systemPropertiesApi, + 'getProperties' + ).and.returnValue( + throwError({ + error: { + response: { + statusCode: 404, + statusText: 'Not Found' + } + } + }) + ); + + service.getBPMSystemProperties().subscribe( + () => { + fail('expected an error, bpm not running'); + }, + error => { + expect(error.response.statusCode).toEqual(404); + expect(error.response.statusText).toEqual('Not Found'); + done(); + } + ); + }); + }); + }); + + describe('Oauth', () => { + setupTestBed({ + imports: [ + TranslateModule.forRoot(), + CoreTestingModule + ] + }); + + beforeEach(() => { + service = TestBed.inject(DiscoveryApiService); + apiService = TestBed.inject(AlfrescoApiService); + authenticationService = TestBed.inject(AuthenticationService); + }); + + it('Should retrieve the info about the product for Oauth', done => { + spyOn(apiService.getInstance(), 'isEcmLoggedIn').and.returnValue(true); + spyOn(service, 'getEcmProductInfo').and.returnValue(of(new EcmProductVersionModel(fakeEcmDiscoveryResponse))); + + const subscription = service.ecmProductInfo$.subscribe( + (data: EcmProductVersionModel) => { + expect(data).not.toBeNull(); + expect(data.edition).toBe('FAKE'); + expect(data.version.schema).toBe(999999); + expect(data.license.isClusterEnabled).toBeFalsy(); + expect(data.status.isQuickShareEnabled).toBeTruthy(); + expect(data.modules.length).toBe(2); + expect(data.modules[0].id).toBe('alfresco-fake-services'); + expect(data.modules[1].id).toBe('alfresco-trashcan-fake'); + subscription.unsubscribe(); done(); - }); - - jasmine.Ajax.requests.mostRecent().respondWith({ - status: 403 - }); - }); - }); - - describe('For BPM', () => { - it('Should retrieve the info about the product for BPM', (done) => { - service.getBpmProductInfo().subscribe((data: BpmProductVersionModel) => { - expect(data).not.toBeNull(); - expect(data.edition).toBe('SUPER FAKE EDITION'); - expect(data.revisionVersion).toBe('2'); - expect(data.type).toBe('bpmSuite'); - done(); - }); - - jasmine.Ajax.requests.mostRecent().respondWith({ - status: 200, - contentType: 'json', - responseText: fakeBPMDiscoveryResponse - }); - }); - - it('getBpmProductInfo catch errors call', (done) => { - service.getBpmProductInfo().subscribe( - () => { - }, - () => { - done(); - }); - - jasmine.Ajax.requests.mostRecent().respondWith({ - status: 403 - }); - }); - - it('Should retrieve the system properties for BPM', (done) => { - service.getBPMSystemProperties().subscribe((data: SystemPropertiesRepresentation) => { - expect(data).not.toBeNull(); - expect(data.allowInvolveByEmail).toBe(true); - expect(data.disableJavaScriptEventsInFormEditor).toBe(false); - expect(data.logoutDisabled).toBe(false); - expect(data.authConfiguration.authUrl).toBe('fakeAuthUrl'); - expect(data.authConfiguration.realm).toBe('fakeRealm'); - expect(data.authConfiguration.clientId).toBe('fakeClient'); - expect(data.authConfiguration.useBrowserLogout).toBe(true); - done(); - }); - - jasmine.Ajax.requests.mostRecent().respondWith({ - status: 200, - contentType: 'json', - responseText: fakeBPMDiscoverySystemPropertyResponse - }); - }); - - it('Should retrieve the system properties for BPM', (done) => { - service.getBPMSystemProperties().subscribe( - () => { - fail('expected an error, bpm not running'); - }, - (error) => { - expect(error.response.statusCode).toEqual(404); - expect(error.response.statusText).toEqual('Not Found'); - done(); - }); - - jasmine.Ajax.requests.mostRecent().respondWith({ - status: 404, - statusText: 'Not Found' - }); + } + ); + authenticationService.onTokenExchange.next(''); }); }); }); diff --git a/lib/core/services/discovery-api.service.ts b/lib/core/services/discovery-api.service.ts index c13828a0fc..cefee675b0 100644 --- a/lib/core/services/discovery-api.service.ts +++ b/lib/core/services/discovery-api.service.ts @@ -16,10 +16,10 @@ */ import { Injectable } from '@angular/core'; -import { from, merge, Observable, BehaviorSubject, throwError } from 'rxjs'; +import { from, merge, Observable, throwError, Subject } from 'rxjs'; import { BpmProductVersionModel, EcmProductVersionModel } from '../models/product-version.model'; import { AlfrescoApiService } from './alfresco-api.service'; -import { catchError, filter, map, switchMap } from 'rxjs/operators'; +import { catchError, filter, map, switchMap, take } from 'rxjs/operators'; import { Activiti, SystemPropertiesRepresentation } from '@alfresco/js-api'; import { AuthenticationService } from './authentication.service'; @@ -31,13 +31,13 @@ export class DiscoveryApiService { /** * Gets product information for Content Services. */ - ecmProductInfo$ = new BehaviorSubject(null); + ecmProductInfo$ = new Subject(); constructor( private apiService: AlfrescoApiService, private authenticationService: AuthenticationService) { - merge(this.apiService.alfrescoApiInitialized, this.authenticationService.onLogin) + merge(this.authenticationService.onTokenExchange.pipe(take(1)), this.authenticationService.onLogin) .pipe( filter(() => this.apiService.getInstance()?.isEcmLoggedIn()), switchMap(() => this.getEcmProductInfo()) diff --git a/lib/core/services/external-alfresco-api.service.ts b/lib/core/services/external-alfresco-api.service.ts index 83352cabfa..bad530f739 100644 --- a/lib/core/services/external-alfresco-api.service.ts +++ b/lib/core/services/external-alfresco-api.service.ts @@ -22,7 +22,7 @@ import { Core, Node } from '@alfresco/js-api'; -import { Subject } from 'rxjs'; +import { ReplaySubject, Subject } from 'rxjs'; @Injectable({ providedIn: 'root' @@ -34,6 +34,8 @@ export class ExternalAlfrescoApiService { */ nodeUpdated = new Subject(); + alfrescoApiInitialized: ReplaySubject = new ReplaySubject(1); + protected alfrescoApi: AlfrescoApiCompatibility; getInstance(): AlfrescoApiCompatibility { @@ -60,6 +62,7 @@ export class ExternalAlfrescoApiService { domainPrefix }; this.initAlfrescoApi(config); + this.alfrescoApiInitialized.next(true); } protected initAlfrescoApi(config) { diff --git a/lib/process-services-cloud/src/lib/app/components/app-list-cloud.component.spec.ts b/lib/process-services-cloud/src/lib/app/components/app-list-cloud.component.spec.ts index 00b5781d22..880da85756 100644 --- a/lib/process-services-cloud/src/lib/app/components/app-list-cloud.component.spec.ts +++ b/lib/process-services-cloud/src/lib/app/components/app-list-cloud.component.spec.ts @@ -36,7 +36,8 @@ describe('AppListCloudComponent', () => { const mock = { oauth2Auth: { - callCustomApi: () => Promise.resolve(fakeApplicationInstance) + callCustomApi: () => Promise.resolve(fakeApplicationInstance), + on: jasmine.createSpy('on') }, isEcmLoggedIn() { return false; diff --git a/lib/process-services-cloud/src/lib/app/services/apps-process-cloud.service.spec.ts b/lib/process-services-cloud/src/lib/app/services/apps-process-cloud.service.spec.ts index 7c4c9cb05e..217f7dd8a9 100644 --- a/lib/process-services-cloud/src/lib/app/services/apps-process-cloud.service.spec.ts +++ b/lib/process-services-cloud/src/lib/app/services/apps-process-cloud.service.spec.ts @@ -32,7 +32,8 @@ describe('AppsProcessCloudService', () => { const apiMock = { oauth2Auth: { - callCustomApi: () => Promise.resolve({list : { entries: [ {entry: fakeApplicationInstance[0]}, {entry: fakeApplicationInstance[1]}] }}) + callCustomApi: () => Promise.resolve({list : { entries: [ {entry: fakeApplicationInstance[0]}, {entry: fakeApplicationInstance[1]}] }}), + on: jasmine.createSpy('on') }, isEcmLoggedIn() { return false; diff --git a/lib/process-services-cloud/src/lib/form/services/form-cloud.service.spec.ts b/lib/process-services-cloud/src/lib/form/services/form-cloud.service.spec.ts index d856fdbed0..d1300ada80 100644 --- a/lib/process-services-cloud/src/lib/form/services/form-cloud.service.spec.ts +++ b/lib/process-services-cloud/src/lib/form/services/form-cloud.service.spec.ts @@ -29,7 +29,7 @@ const responseBody = { { id: 'id', name: 'name', formKey: 'form-key' } }; -const oauth2Auth = jasmine.createSpyObj('oauth2Auth', ['callCustomApi']); +const oauth2Auth = jasmine.createSpyObj('oauth2Auth', ['callCustomApi', 'on']); describe('Form Cloud service', () => { diff --git a/lib/process-services-cloud/src/lib/form/services/form-definition-selector-cloud.service.spec.ts b/lib/process-services-cloud/src/lib/form/services/form-definition-selector-cloud.service.spec.ts index 5588c09c19..ab7c47a20e 100644 --- a/lib/process-services-cloud/src/lib/form/services/form-definition-selector-cloud.service.spec.ts +++ b/lib/process-services-cloud/src/lib/form/services/form-definition-selector-cloud.service.spec.ts @@ -52,7 +52,7 @@ const responseBody = [ } ]; -const oauth2Auth = jasmine.createSpyObj('oauth2Auth', ['callCustomApi']); +const oauth2Auth = jasmine.createSpyObj('oauth2Auth', ['callCustomApi', 'on']); describe('Form Definition Selector Cloud Service', () => { diff --git a/lib/process-services-cloud/src/lib/group/components/group-cloud.component.spec.ts b/lib/process-services-cloud/src/lib/group/components/group-cloud.component.spec.ts index bd2a566d66..b20976fee9 100644 --- a/lib/process-services-cloud/src/lib/group/components/group-cloud.component.spec.ts +++ b/lib/process-services-cloud/src/lib/group/components/group-cloud.component.spec.ts @@ -42,7 +42,8 @@ describe('GroupCloudComponent', () => { const mock = { oauth2Auth: { - callCustomApi: () => Promise.resolve(mockIdentityGroups) + callCustomApi: () => Promise.resolve(mockIdentityGroups), + on: jasmine.createSpy('on') }, isEcmLoggedIn() { return false; diff --git a/lib/process-services-cloud/src/lib/people/components/people-cloud.component.spec.ts b/lib/process-services-cloud/src/lib/people/components/people-cloud.component.spec.ts index c40868368b..1ff068065b 100644 --- a/lib/process-services-cloud/src/lib/people/components/people-cloud.component.spec.ts +++ b/lib/process-services-cloud/src/lib/people/components/people-cloud.component.spec.ts @@ -42,7 +42,8 @@ describe('PeopleCloudComponent', () => { const mock = { oauth2Auth: { - callCustomApi: () => Promise.resolve(mockUsers) + callCustomApi: () => Promise.resolve(mockUsers), + on: jasmine.createSpy('on') }, isEcmLoggedIn() { return false; diff --git a/lib/process-services-cloud/src/lib/process/process-filters/components/edit-process-filter-cloud.component.spec.ts b/lib/process-services-cloud/src/lib/process/process-filters/components/edit-process-filter-cloud.component.spec.ts index cfa1010b45..e25f6b07d2 100644 --- a/lib/process-services-cloud/src/lib/process/process-filters/components/edit-process-filter-cloud.component.spec.ts +++ b/lib/process-services-cloud/src/lib/process/process-filters/components/edit-process-filter-cloud.component.spec.ts @@ -66,7 +66,8 @@ describe('EditProcessFilterCloudComponent', () => { const mock = { oauth2Auth: { - callCustomApi: () => Promise.resolve(fakeApplicationInstance) + callCustomApi: () => Promise.resolve(fakeApplicationInstance), + on: jasmine.createSpy('on') }, isEcmLoggedIn() { return false; diff --git a/lib/process-services-cloud/src/lib/services/notification-cloud.service.spec.ts b/lib/process-services-cloud/src/lib/services/notification-cloud.service.spec.ts index 6b488cf33a..576ea59907 100644 --- a/lib/process-services-cloud/src/lib/services/notification-cloud.service.spec.ts +++ b/lib/process-services-cloud/src/lib/services/notification-cloud.service.spec.ts @@ -45,7 +45,8 @@ describe('NotificationCloudService', () => { const apiServiceMock = { oauth2Auth: { - token: '1234567' + token: '1234567', + on: jasmine.createSpy('on') }, isEcmLoggedIn() { return false; diff --git a/lib/process-services-cloud/src/lib/services/user-preference-cloud.service.spec.ts b/lib/process-services-cloud/src/lib/services/user-preference-cloud.service.spec.ts index a336d3f755..725bcdb7d6 100644 --- a/lib/process-services-cloud/src/lib/services/user-preference-cloud.service.spec.ts +++ b/lib/process-services-cloud/src/lib/services/user-preference-cloud.service.spec.ts @@ -38,7 +38,8 @@ describe('PreferenceService', () => { oauth2Auth: { callCustomApi: () => { return Promise.resolve(mockResponse); - } + }, + on: jasmine.createSpy('on') }, isEcmLoggedIn() { return false; diff --git a/lib/process-services-cloud/src/lib/task/start-task/components/start-task-cloud.component.spec.ts b/lib/process-services-cloud/src/lib/task/start-task/components/start-task-cloud.component.spec.ts index 4825afa6b4..e36dd605f4 100644 --- a/lib/process-services-cloud/src/lib/task/start-task/components/start-task-cloud.component.spec.ts +++ b/lib/process-services-cloud/src/lib/task/start-task/components/start-task-cloud.component.spec.ts @@ -40,7 +40,8 @@ describe('StartTaskCloudComponent', () => { const mock = { oauth2Auth: { - callCustomApi: () => Promise.resolve(taskDetailsMock) + callCustomApi: () => Promise.resolve(taskDetailsMock), + on: jasmine.createSpy('on') }, isEcmLoggedIn() { return false; diff --git a/lib/process-services-cloud/src/lib/task/task-filters/components/edit-task-filters/edit-task-filter-cloud.component.spec.ts b/lib/process-services-cloud/src/lib/task/task-filters/components/edit-task-filters/edit-task-filter-cloud.component.spec.ts index 6e445ebf8a..e0b161da26 100644 --- a/lib/process-services-cloud/src/lib/task/task-filters/components/edit-task-filters/edit-task-filter-cloud.component.spec.ts +++ b/lib/process-services-cloud/src/lib/task/task-filters/components/edit-task-filters/edit-task-filter-cloud.component.spec.ts @@ -53,7 +53,8 @@ describe('EditTaskFilterCloudComponent', () => { const mock = { oauth2Auth: { - callCustomApi: () => Promise.resolve(fakeApplicationInstance) + callCustomApi: () => Promise.resolve(fakeApplicationInstance), + on: jasmine.createSpy('on') }, isEcmLoggedIn() { return false; diff --git a/lib/process-services-cloud/src/lib/task/task-header/components/task-header-cloud.component.spec.ts b/lib/process-services-cloud/src/lib/task/task-header/components/task-header-cloud.component.spec.ts index a06ab2185b..64a6c7d033 100644 --- a/lib/process-services-cloud/src/lib/task/task-header/components/task-header-cloud.component.spec.ts +++ b/lib/process-services-cloud/src/lib/task/task-header/components/task-header-cloud.component.spec.ts @@ -51,7 +51,8 @@ describe('TaskHeaderCloudComponent', () => { const mock = { oauth2Auth: { - callCustomApi: () => Promise.resolve({}) + callCustomApi: () => Promise.resolve({}), + on: jasmine.createSpy('on') }, isEcmLoggedIn() { return false; diff --git a/lib/process-services-cloud/src/lib/task/task-list/services/service-task-list-cloud.service.spec.ts b/lib/process-services-cloud/src/lib/task/task-list/services/service-task-list-cloud.service.spec.ts index 83e31a668f..7bf016633c 100644 --- a/lib/process-services-cloud/src/lib/task/task-list/services/service-task-list-cloud.service.spec.ts +++ b/lib/process-services-cloud/src/lib/task/task-list/services/service-task-list-cloud.service.spec.ts @@ -31,7 +31,8 @@ describe('Activiti ServiceTaskList Cloud Service', () => { oauth2Auth: { callCustomApi: (_queryUrl, _operation, _context, queryParams) => { return Promise.resolve(queryParams); - } + }, + on: jasmine.createSpy('on') }, isEcmLoggedIn() { return false; diff --git a/lib/process-services-cloud/src/lib/task/task-list/services/task-list-cloud.service.spec.ts b/lib/process-services-cloud/src/lib/task/task-list/services/task-list-cloud.service.spec.ts index 430e138ef8..933f4957fc 100644 --- a/lib/process-services-cloud/src/lib/task/task-list/services/task-list-cloud.service.spec.ts +++ b/lib/process-services-cloud/src/lib/task/task-list/services/task-list-cloud.service.spec.ts @@ -32,7 +32,8 @@ describe('TaskListCloudService', () => { oauth2Auth: { callCustomApi : (_queryUrl, _operation, _context, queryParams) => { return Promise.resolve(queryParams); - } + }, + on: jasmine.createSpy('on') }, isEcmLoggedIn() { return false; diff --git a/lib/testing/src/lib/content-services/actions/model.actions.ts b/lib/testing/src/lib/content-services/actions/model.actions.ts index 1f79fe2c5f..a99280c273 100644 --- a/lib/testing/src/lib/content-services/actions/model.actions.ts +++ b/lib/testing/src/lib/content-services/actions/model.actions.ts @@ -15,17 +15,18 @@ * limitations under the License. */ +import { CustomModel, CustomModelApi, CustomModelProperty, CustomType, TypePaging, TypesApi } from '@alfresco/js-api'; import { ApiService } from '../../core/actions/api.service'; -import { CustomModel, CustomModelApi, CustomType, TypePaging, TypesApi } from '@alfresco/js-api'; +import { ApiUtil } from '../../core/actions/api.util'; +import { Logger } from '../../core/utils/logger'; export class ModelActions { - customModelApi: CustomModelApi; typesApi: TypesApi; - constructor(api: ApiService) { - this.customModelApi = new CustomModelApi(api.getInstance()); - this.typesApi = new TypesApi(api.getInstance()); + constructor(apiService: ApiService) { + this.customModelApi = new CustomModelApi(apiService.getInstance()); + this.typesApi = new TypesApi(apiService.getInstance()); } async createModel({status, description, name, namespaceUri, namespacePrefix, author}: CustomModel): Promise<{ entry: CustomModel }> { @@ -36,6 +37,10 @@ export class ModelActions { return this.customModelApi.createCustomType(modelName, name, parentName, title, description); } + async addPropertyToType(modelName: string, typeName: string, properties?: CustomModelProperty[]): Promise { + return this.customModelApi.addPropertyToType(modelName , typeName, properties); + } + async activateCustomModel(modelName: string): Promise<{ entry: CustomModel }> { return this.customModelApi.activateCustomModel(modelName); } @@ -44,7 +49,25 @@ export class ModelActions { return this.customModelApi.deactivateCustomModel(modelName); } + async deleteCustomModel(modelName: string): Promise<{ entry: CustomModel }> { + return this.customModelApi.deleteCustomModel(modelName); + } + async listTypes(opts?: any): Promise { return this.typesApi.listTypes(opts); } + + async isCustomTypeSearchable(title: string): Promise { + const predicate = (result: TypePaging) => !!result.list.entries.find(({entry}) => entry.title === title); + + const apiCall = async () => { + try { + return this.listTypes({where: `(not namespaceUri matches('http://www.alfresco.*'))`}); + } catch (error) { + Logger.error('Failed to list types', error); + return null; + } + }; + return ApiUtil.waitForApi(apiCall, predicate); + } } diff --git a/lib/testing/src/lib/content-services/dialog/add-permissions-dialog.page.ts b/lib/testing/src/lib/content-services/dialog/add-permissions-dialog.page.ts index a7bf3d18f5..e478ca4958 100644 --- a/lib/testing/src/lib/content-services/dialog/add-permissions-dialog.page.ts +++ b/lib/testing/src/lib/content-services/dialog/add-permissions-dialog.page.ts @@ -20,6 +20,7 @@ import { BrowserActions } from '../../core/utils/browser-actions'; import { DataTableComponentPage } from '../../core/pages/data-table-component.page'; import { BrowserVisibility } from '../../core/utils/browser-visibility'; import { DropdownPage } from '../../core/pages/material/dropdown.page'; +import { TestElement } from '../../core/test-element'; const column = { role: 'Role' @@ -102,10 +103,10 @@ export class AddPermissionsDialogPage { await BrowserActions.click(this.addButton); } - async selectRole(name: string, role) { + async selectRole(name: string, role: string) { const row = this.userRoleDataTableComponentPage.getRow('Users and Groups', name); await BrowserActions.click(row.element(by.css('[id="adf-select-role-permission"] .mat-select-trigger'))); - await this.getRoleDropdownOptions(); + await TestElement.byCss('.mat-select-panel').waitVisible(); await this.selectOption(role); } }