diff --git a/docs/extending/rules-list.md b/docs/extending/rules-list.md index 54dc41d15..6a2da09a0 100644 --- a/docs/extending/rules-list.md +++ b/docs/extending/rules-list.md @@ -60,6 +60,7 @@ Application related evaluators which can be used to check various different aspe | 6.1.0 | canPrintFile | Checks if current file can be printed or not (media files such as audio/video cannot be printed). | | 7.4.0 | app.selection.isCheckedOut | Checks if selected node contains `cm:checkedOut` aspect. | | 8.0.0 | app.isKnowledgeDiscoveryUrlPresent | Checks if the Knowledge Discovery URL is specified in the app config. | +| 8.1.0 | app.selection.file.isVersionable | Checks if the selected file has the `cm:versionable` aspect. | #### Navigation Rules/Evaluators diff --git a/e2e/playwright/favorite-actions/exclude.tests.json b/e2e/playwright/favorite-actions/exclude.tests.json index 19ead4630..0713f8a80 100644 --- a/e2e/playwright/favorite-actions/exclude.tests.json +++ b/e2e/playwright/favorite-actions/exclude.tests.json @@ -1,3 +1,13 @@ { - "all": {} + "all": { + "XAT-5042": "https://hyland.atlassian.net/browse/ACS-12214", + "XAT-5043": "https://hyland.atlassian.net/browse/ACS-12214", + "XAT-5044": "https://hyland.atlassian.net/browse/ACS-12214", + "XAT-5045": "https://hyland.atlassian.net/browse/ACS-12214", + "XAT-5046": "https://hyland.atlassian.net/browse/ACS-12214", + "XAT-5047": "https://hyland.atlassian.net/browse/ACS-12214", + "XAT-5048": "https://hyland.atlassian.net/browse/ACS-12214", + "XAT-5049": "https://hyland.atlassian.net/browse/ACS-12214", + "XAT-5050": "https://hyland.atlassian.net/browse/ACS-12214" + } } \ No newline at end of file diff --git a/e2e/playwright/favorite-actions/project.json b/e2e/playwright/favorite-actions/project.json index e73535c70..1637c997d 100644 --- a/e2e/playwright/favorite-actions/project.json +++ b/e2e/playwright/favorite-actions/project.json @@ -8,7 +8,7 @@ "executor": "nx:run-commands", "options": { "commands": [ - "playwright test --config=e2e/playwright/favorite-actions/playwright.config.ts" + "playwright test --config=e2e/playwright/favorite-actions/playwright.config.ts --pass-with-no-tests" ] }, "configurations": { diff --git a/e2e/playwright/library-actions/exclude.tests.json b/e2e/playwright/library-actions/exclude.tests.json index 7507194dc..c06227771 100644 --- a/e2e/playwright/library-actions/exclude.tests.json +++ b/e2e/playwright/library-actions/exclude.tests.json @@ -1,3 +1,9 @@ { - "all": {} + "all": { + "XAT-5140": "https://hyland.atlassian.net/browse/ACS-12214", + "XAT-5141": "https://hyland.atlassian.net/browse/ACS-12214", + "XAT-5142": "https://hyland.atlassian.net/browse/ACS-12214", + "XAT-5143": "https://hyland.atlassian.net/browse/ACS-12214", + "XAT-5144": "https://hyland.atlassian.net/browse/ACS-12214" + } } diff --git a/e2e/playwright/list-views/exclude.tests.json b/e2e/playwright/list-views/exclude.tests.json index 214ed1e99..77181717d 100644 --- a/e2e/playwright/list-views/exclude.tests.json +++ b/e2e/playwright/list-views/exclude.tests.json @@ -1,6 +1,8 @@ { "all": { - "XAT-4525": "https://hyland.atlassian.net/browse/ACS-9741" + "XAT-4525": "https://hyland.atlassian.net/browse/ACS-9741", + "XAT-4403": "https://hyland.atlassian.net/browse/ACS-12214", + "XAT-4406": "https://hyland.atlassian.net/browse/ACS-12214" }, "webkit": { "XAT-4455": "https://hyland.atlassian.net/browse/ACS-12214" diff --git a/e2e/playwright/special-permissions-actions-available/exclude.tests.json b/e2e/playwright/special-permissions-actions-available/exclude.tests.json index f3436f249..94f5c5512 100644 --- a/e2e/playwright/special-permissions-actions-available/exclude.tests.json +++ b/e2e/playwright/special-permissions-actions-available/exclude.tests.json @@ -1,6 +1,68 @@ { "all": { "XAT-4845": "https://hyland.atlassian.net/browse/ACS-12214", - "XAT-4835": "https://hyland.atlassian.net/browse/ACS-12214" + "XAT-4835": "https://hyland.atlassian.net/browse/ACS-12214", + "XAT-4846": "https://hyland.atlassian.net/browse/ACS-12385", + "XAT-4847": "https://hyland.atlassian.net/browse/ACS-12385", + "XAT-4853": "https://hyland.atlassian.net/browse/ACS-12385", + "XAT-4854": "https://hyland.atlassian.net/browse/ACS-12385", + "XAT-4855": "https://hyland.atlassian.net/browse/ACS-12385", + "XAT-4861": "https://hyland.atlassian.net/browse/ACS-12385", + "XAT-4862": "https://hyland.atlassian.net/browse/ACS-12385", + "XAT-4863": "https://hyland.atlassian.net/browse/ACS-12385", + "XAT-4637": "https://hyland.atlassian.net/browse/ACS-12385", + "XAT-4638": "https://hyland.atlassian.net/browse/ACS-12385", + "XAT-4642": "https://hyland.atlassian.net/browse/ACS-12385", + "XAT-4643": "https://hyland.atlassian.net/browse/ACS-12385", + "XAT-4808": "https://hyland.atlassian.net/browse/ACS-12385", + "XAT-4809": "https://hyland.atlassian.net/browse/ACS-12385", + "XAT-4810": "https://hyland.atlassian.net/browse/ACS-12385", + "XAT-4811": "https://hyland.atlassian.net/browse/ACS-12385", + "XAT-4814": "https://hyland.atlassian.net/browse/ACS-12385", + "XAT-4815": "https://hyland.atlassian.net/browse/ACS-12385", + "XAT-4825": "https://hyland.atlassian.net/browse/ACS-12385", + "XAT-4826": "https://hyland.atlassian.net/browse/ACS-12385", + "XAT-4827": "https://hyland.atlassian.net/browse/ACS-12385", + "XAT-4828": "https://hyland.atlassian.net/browse/ACS-12385", + "XAT-4829": "https://hyland.atlassian.net/browse/ACS-12385", + "XAT-4830": "https://hyland.atlassian.net/browse/ACS-12385", + "XAT-4831": "https://hyland.atlassian.net/browse/ACS-12385", + "XAT-4843": "https://hyland.atlassian.net/browse/ACS-12385", + "XAT-4844": "https://hyland.atlassian.net/browse/ACS-12385", + "XAT-4848": "https://hyland.atlassian.net/browse/ACS-12385", + "XAT-4849": "https://hyland.atlassian.net/browse/ACS-12385", + "XAT-4850": "https://hyland.atlassian.net/browse/ACS-12385", + "XAT-4851": "https://hyland.atlassian.net/browse/ACS-12385", + "XAT-4816": "https://hyland.atlassian.net/browse/ACS-12385", + "XAT-4817": "https://hyland.atlassian.net/browse/ACS-12385", + "XAT-4812": "https://hyland.atlassian.net/browse/ACS-12385", + "XAT-4813": "https://hyland.atlassian.net/browse/ACS-12385", + "XAT-4818": "https://hyland.atlassian.net/browse/ACS-12385", + "XAT-4819": "https://hyland.atlassian.net/browse/ACS-12385", + "XAT-4820": "https://hyland.atlassian.net/browse/ACS-12385", + "XAT-4821": "https://hyland.atlassian.net/browse/ACS-12385", + "XAT-4823": "https://hyland.atlassian.net/browse/ACS-12385", + "XAT-4824": "https://hyland.atlassian.net/browse/ACS-12385", + "XAT-4822": "https://hyland.atlassian.net/browse/ACS-12385", + "XAT-4832": "https://hyland.atlassian.net/browse/ACS-12385", + "XAT-4833": "https://hyland.atlassian.net/browse/ACS-12385", + "XAT-4834": "https://hyland.atlassian.net/browse/ACS-12385", + "XAT-4838": "https://hyland.atlassian.net/browse/ACS-12385", + "XAT-4839": "https://hyland.atlassian.net/browse/ACS-12385", + "XAT-4840": "https://hyland.atlassian.net/browse/ACS-12385", + "XAT-4841": "https://hyland.atlassian.net/browse/ACS-12385", + "XAT-4836": "https://hyland.atlassian.net/browse/ACS-12385", + "XAT-4837": "https://hyland.atlassian.net/browse/ACS-12385", + "XAT-4842": "https://hyland.atlassian.net/browse/ACS-12385", + "XAT-4852": "https://hyland.atlassian.net/browse/ACS-12385", + "XAT-4856": "https://hyland.atlassian.net/browse/ACS-12385", + "XAT-4857": "https://hyland.atlassian.net/browse/ACS-12385", + "XAT-4858": "https://hyland.atlassian.net/browse/ACS-12385", + "XAT-4859": "https://hyland.atlassian.net/browse/ACS-12385", + "XAT-4860": "https://hyland.atlassian.net/browse/ACS-12385", + "XAT-4864": "https://hyland.atlassian.net/browse/ACS-12385", + "XAT-4865": "https://hyland.atlassian.net/browse/ACS-12385", + "XAT-4866": "https://hyland.atlassian.net/browse/ACS-12385", + "XAT-4867": "https://hyland.atlassian.net/browse/ACS-12385" } } diff --git a/e2e/playwright/special-permissions-actions-available/src/tests/special-permissions-actions.e2e.ts b/e2e/playwright/special-permissions-actions-available/src/tests/special-permissions-actions.e2e.ts index 0b5c9a9a7..ddaf6bd70 100644 --- a/e2e/playwright/special-permissions-actions-available/src/tests/special-permissions-actions.e2e.ts +++ b/e2e/playwright/special-permissions-actions-available/src/tests/special-permissions-actions.e2e.ts @@ -104,28 +104,33 @@ test.describe('Special permissions : ', () => { const consumerFavoritesTotalItems = await consumerFavoritesActions.getFavoritesTotalItems(userConsumer); const managerSearchTotalItems = await managerSearchActions.getTotalItems(userManager); const collaboratorFavoritesTotalItems = await collaboratorFavoritesActions.getFavoritesTotalItems(userCollaborator); + const demotedUserFavoritesTotalItems = await demotedUserFavoritesActions.getFavoritesTotalItems(userDemoted); await managerSiteActions.createSite(sitePrivate, Site.VisibilityEnum.PRIVATE); docLibId = await managerSiteActions.getDocLibId(sitePrivate); await managerSiteActions.addSiteMember(sitePrivate, userConsumer, Site.RoleEnum.SiteConsumer); await managerSiteActions.addSiteMember(sitePrivate, userCollaborator, Site.RoleEnum.SiteCollaborator); await managerSiteActions.addSiteMember(sitePrivate, userDemoted, Site.RoleEnum.SiteManager); + const managerFavoritesTotalItems = await managerFavoritesActions.getFavoritesTotalItems(userManager); await managerFileActions.uploadFileWithRename(TEST_FILES.DOCX.path, testData.fileDocx.name, docLibId); fileDocxFavId = (await managerFileActions.uploadFileWithRename(TEST_FILES.DOCX.path, testData.fileDocxFav.name, docLibId)).entry.id; - await managerNodeActions.createFile(testData.file.name, docLibId); - fileFavId = (await managerNodeActions.createFile(testData.fileFav.name, docLibId)).entry.id; + await managerNodeActions.createFile(testData.file.name, docLibId, '', '', '', true, ['cm:versionable']); + fileFavId = (await managerNodeActions.createFile(testData.fileFav.name, docLibId, '', '', '', true, ['cm:versionable'])).entry.id; fileDocxSharedId = (await managerFileActions.uploadFileWithRename(TEST_FILES.DOCX.path, testData.fileDocxShared.name, docLibId)).entry.id; fileDocxSharedFavId = (await managerFileActions.uploadFileWithRename(TEST_FILES.DOCX.path, testData.fileDocxSharedFav.name, docLibId)).entry.id; - fileSharedId = (await managerNodeActions.createFile(testData.fileShared.name, docLibId)).entry.id; - fileSharedFavId = (await managerNodeActions.createFile(testData.fileSharedFav.name, docLibId)).entry.id; - fileLockedId = (await managerNodeActions.createFile(testData.fileLocked.name, docLibId)).entry.id; - fileFavLockedId = (await managerNodeActions.createFile(testData.fileFavLocked.name, docLibId)).entry.id; - fileSharedLockedId = (await managerNodeActions.createFile(testData.fileSharedLocked.name, docLibId)).entry.id; - fileSharedFavLockedId = (await managerNodeActions.createFile(testData.fileSharedFavLocked.name, docLibId)).entry.id; - fileGranularPermissionId = (await managerNodeActions.createFile(testData.fileGranularPermission, docLibId)).entry.id; + fileSharedId = (await managerNodeActions.createFile(testData.fileShared.name, docLibId, '', '', '', true, ['cm:versionable'])).entry.id; + fileSharedFavId = (await managerNodeActions.createFile(testData.fileSharedFav.name, docLibId, '', '', '', true, ['cm:versionable'])).entry.id; + fileLockedId = (await managerNodeActions.createFile(testData.fileLocked.name, docLibId, '', '', '', true, ['cm:versionable'])).entry.id; + fileFavLockedId = (await managerNodeActions.createFile(testData.fileFavLocked.name, docLibId, '', '', '', true, ['cm:versionable'])).entry.id; + fileSharedLockedId = (await managerNodeActions.createFile(testData.fileSharedLocked.name, docLibId, '', '', '', true, ['cm:versionable'])).entry + .id; + fileSharedFavLockedId = (await managerNodeActions.createFile(testData.fileSharedFavLocked.name, docLibId, '', '', '', true, ['cm:versionable'])) + .entry.id; + fileGranularPermissionId = (await managerNodeActions.createFile(testData.fileGranularPermission, docLibId, '', '', '', true, ['cm:versionable'])) + .entry.id; - fileLockedByUserId = (await managerNodeActions.createFile(testData.fileLockedByUser, docLibId)).entry.id; + fileLockedByUserId = (await managerNodeActions.createFile(testData.fileLockedByUser, docLibId, '', '', '', true, ['cm:versionable'])).entry.id; await demotedUserActions.lockNodes([fileLockedByUserId]); await demotedUserFavoritesActions.addFavoriteById('file', fileLockedByUserId); await demotedUserShareActions.shareFileById(fileLockedByUserId); @@ -163,6 +168,8 @@ test.describe('Special permissions : ', () => { await managerNodeActions.setGranularPermission(fileGranularPermissionId, userConsumer, Site.RoleEnum.SiteManager, false); await collaboratorFavoritesActions.isFavoriteWithRetry(userCollaborator, fileSharedFavId, { expect: true }); + await demotedUserFavoritesActions.isFavoriteWithRetry(userDemoted, fileLockedByUserId, { expect: true }); + await managerFavoritesActions.isFavoriteWithRetry(userManager, fileLockedByUserId, { expect: true }); await Promise.all([ consumerFavoritesActions.waitForApi(userConsumer, { expect: consumerFavoritesTotalItems + 9 }), managerUserShareActions.waitForFilesToBeShared([ @@ -176,7 +183,9 @@ test.describe('Special permissions : ', () => { fileLockedByUserId ]), managerSearchActions.waitForApi(userManager, { expect: managerSearchTotalItems + 14 }), - collaboratorFavoritesActions.waitForApi(userCollaborator, { expect: collaboratorFavoritesTotalItems + 2 }) + collaboratorFavoritesActions.waitForApi(userCollaborator, { expect: collaboratorFavoritesTotalItems + 2 }), + demotedUserFavoritesActions.waitForApi(userDemoted, { expect: demotedUserFavoritesTotalItems + 1 }), + managerFavoritesActions.waitForApi(userManager, { expect: managerFavoritesTotalItems + 1 }) ]); }); diff --git a/projects/aca-content/assets/app.extensions.json b/projects/aca-content/assets/app.extensions.json index dfc1382a0..f39d54cad 100644 --- a/projects/aca-content/assets/app.extensions.json +++ b/projects/aca-content/assets/app.extensions.json @@ -763,6 +763,7 @@ "rules": { "visible": [ "app.selection.file", + "app.selection.file.isVersionable", "!app.navigation.isTrashcan", "!app.selection.file.isLocked", "!app.selection.isNodeLink" @@ -1129,6 +1130,7 @@ "rules": { "visible": [ "app.selection.file", + "app.selection.file.isVersionable", "!app.navigation.isTrashcan", "!app.selection.file.isLocked", "!app.selection.isNodeLink" @@ -1505,6 +1507,7 @@ "rules": { "visible": [ "app.selection.file", + "app.selection.file.isVersionable", "!app.navigation.isTrashcan", "!app.selection.file.isLocked", "!app.selection.isNodeLink" @@ -1687,7 +1690,7 @@ "default": true, "filterWithContains": true, "app:fields": ["cm:name", "cm:title", "cm:description", "TEXT", "TAG"], - "include": ["path", "allowableOperations", "properties"], + "include": ["path", "allowableOperations", "properties", "aspectNames"], "sorting": { "options": [ { @@ -1935,7 +1938,7 @@ "default": false, "filterWithContains": true, "app:fields": ["cm:name", "cm:title", "cm:description", "cm:identifier", "cm:contributor", "cm:coverage", "cm:dcsource", "cm:publisher", "cm:rights", "cm:subject", "cm:type", "TEXT", "TAG"], - "include": ["path", "allowableOperations", "properties"], + "include": ["path", "allowableOperations", "properties", "aspectNames"], "sorting": { "options": [ { @@ -2110,7 +2113,7 @@ "default": false, "filterWithContains": true, "app:fields": ["cm:name", "cm:title", "cm:description", "cm:from", "cm:contributor", "cm:to", "TEXT", "TAG"], - "include": ["path", "allowableOperations", "properties"], + "include": ["path", "allowableOperations", "properties", "aspectNames"], "sorting": { "options": [ { diff --git a/projects/aca-content/src/lib/aca-content.module.ts b/projects/aca-content/src/lib/aca-content.module.ts index c4b79b005..b04d4acb0 100644 --- a/projects/aca-content/src/lib/aca-content.module.ts +++ b/projects/aca-content/src/lib/aca-content.module.ts @@ -148,6 +148,7 @@ import { IsFeatureSupportedInCurrentAcsPipe } from './pipes/is-feature-supported 'app.selection.file': rules.hasFileSelected, 'app.selection.file.isLocked': rules.hasLockedFiles, 'app.selection.file.canUploadVersion': rules.canUploadVersion, + 'app.selection.file.isVersionable': rules.isVersionableFile, 'app.selection.library': rules.hasLibrarySelected, 'app.selection.hasLibraryRole': rules.hasLibraryRole, 'app.selection.folder': rules.hasFolderSelected, diff --git a/projects/aca-playwright-shared/src/page-objects/components/dataTable/mat-menu.component.ts b/projects/aca-playwright-shared/src/page-objects/components/dataTable/mat-menu.component.ts index d73d245e7..9ff9f1a8f 100644 --- a/projects/aca-playwright-shared/src/page-objects/components/dataTable/mat-menu.component.ts +++ b/projects/aca-playwright-shared/src/page-objects/components/dataTable/mat-menu.component.ts @@ -67,7 +67,10 @@ export class MatMenuComponent extends BaseComponent { }) ); for (const action of expectedToolbarMore) { - expect(actualMoreActions.includes(action), `Expected to contain ${action} ${actualMoreActions}`).toBe(true); + expect( + actualMoreActions.includes(action), + `Expected menu to contain "${action}", but actual menu items were: [${actualMoreActions.join(', ')}]` + ).toBe(true); } } } diff --git a/projects/aca-shared/rules/src/app.rules.spec.ts b/projects/aca-shared/rules/src/app.rules.spec.ts index 37b7908a0..019737ba5 100644 --- a/projects/aca-shared/rules/src/app.rules.spec.ts +++ b/projects/aca-shared/rules/src/app.rules.spec.ts @@ -250,6 +250,38 @@ describe('app.evaluators', () => { }); }); + describe('isVersionableFile', () => { + it('should return true when the selected file has the cm:versionable aspect', () => { + context.selection.file = { entry: { aspectNames: ['cm:versionable'] } } as NodeEntry; + + expect(app.isVersionableFile(context)).toBe(true); + }); + + it('should return false when the selected file lacks the cm:versionable aspect', () => { + context.selection.file = { entry: { aspectNames: ['cm:auditable'] } } as NodeEntry; + + expect(app.isVersionableFile(context)).toBe(false); + }); + + it('should return false when the selected file has no entry', () => { + context.selection.file = {} as NodeEntry; + + expect(app.isVersionableFile(context)).toBe(false); + }); + + it('should return false when the selected file has no aspectNames', () => { + context.selection.file = { entry: {} } as NodeEntry; + + expect(app.isVersionableFile(context)).toBe(false); + }); + + it('should return false when there is no file selected', () => { + context.selection.file = undefined; + + expect(app.isVersionableFile(context)).toBe(false); + }); + }); + describe('isShared', () => { it('should return true if route is shared files and single selection', () => { context.navigation.url = '/shared'; diff --git a/projects/aca-shared/rules/src/app.rules.ts b/projects/aca-shared/rules/src/app.rules.ts index 2359bcf50..454f5df26 100644 --- a/projects/aca-shared/rules/src/app.rules.ts +++ b/projects/aca-shared/rules/src/app.rules.ts @@ -529,6 +529,20 @@ export function createVersionRule(minimalVersion: string): (context: RuleContext }; } +/** + * Checks if the selected file has the `cm:versionable` aspect. + * JSON ref: `app.selection.file.isVersionable` + * + * @param context Rule execution context + */ +export const isVersionableFile = (context: RuleContext): boolean => { + const file = context?.selection?.file; + if (!file?.entry) { + return false; + } + return (file.entry.aspectNames ?? []).includes('cm:versionable'); +}; + function isVersionCompatible(currentVersion: string, minimalVersion: string): boolean { if (!currentVersion || !minimalVersion) { return false;