diff --git a/nuxeo-retention-web/elements/nuxeo-retain-button.js b/nuxeo-retention-web/elements/nuxeo-retain-button.js index cb6d4104..e72eb0a3 100644 --- a/nuxeo-retention-web/elements/nuxeo-retain-button.js +++ b/nuxeo-retention-web/elements/nuxeo-retain-button.js @@ -24,6 +24,7 @@ import '@nuxeo/nuxeo-ui-elements/widgets/nuxeo-tooltip.js'; import '@polymer/paper-button/paper-button.js'; import '@polymer/paper-dialog-scrollable/paper-dialog-scrollable.js'; import '@polymer/paper-icon-button/paper-icon-button.js'; +import moment from '@nuxeo/moment'; /** `nuxeo-retain-button` diff --git a/nuxeo-retention-web/elements/nuxeo-retention-behavior.js b/nuxeo-retention-web/elements/nuxeo-retention-behavior.js index 0b50086c..b854b5c8 100644 --- a/nuxeo-retention-web/elements/nuxeo-retention-behavior.js +++ b/nuxeo-retention-web/elements/nuxeo-retention-behavior.js @@ -14,7 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ -const Nuxeo = window.Nuxeo || {}; +// eslint-disable-next-line import/prefer-default-export +export const Nuxeo = window.Nuxeo || {}; let fetcher; diff --git a/nuxeo-retention-web/elements/nuxeo-retention-events.js b/nuxeo-retention-web/elements/nuxeo-retention-events.js index 76e50673..65c803db 100644 --- a/nuxeo-retention-web/elements/nuxeo-retention-events.js +++ b/nuxeo-retention-web/elements/nuxeo-retention-events.js @@ -27,6 +27,7 @@ import '@nuxeo/nuxeo-ui-elements/widgets/nuxeo-directory-suggestion.js'; import '@nuxeo/nuxeo-ui-elements/widgets/nuxeo-input.js'; import '@nuxeo/nuxeo-ui-elements/widgets/nuxeo-user-tag.js'; import '@polymer/paper-button/paper-button.js'; +import moment from '@nuxeo/moment'; /** `nuxeo-retention-events` diff --git a/nuxeo-retention-web/package.json b/nuxeo-retention-web/package.json index 4c4a2abc..befe5eb4 100644 --- a/nuxeo-retention-web/package.json +++ b/nuxeo-retention-web/package.json @@ -31,6 +31,7 @@ "sinon": "^17.0.1" }, "dependencies": { + "@nuxeo/moment": "^2.24.0-nx.0", "@nuxeo/nuxeo-elements": "~3.0.2-rc.0", "@nuxeo/nuxeo-ui-elements": "~3.0.2-rc.0", "@polymer/paper-button": "^3.0.0", diff --git a/nuxeo-retention-web/test/nuxeo-hold-toggle-button.test.js b/nuxeo-retention-web/test/nuxeo-hold-toggle-button.test.js new file mode 100644 index 00000000..14a44fa0 --- /dev/null +++ b/nuxeo-retention-web/test/nuxeo-hold-toggle-button.test.js @@ -0,0 +1,341 @@ +/** +@license +©2023 Hyland Software, Inc. and its affiliates. All rights reserved. +All Hyland product names are registered or unregistered trademarks of Hyland Software, Inc. or its affiliates. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use attachEl 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 { fixture, html } from '@nuxeo/testing-helpers'; +import '../elements/nuxeo-hold-toggle-button.js'; +import sinon from 'sinon'; +import { expect } from 'chai'; + +const document = { + 'entity-type': 'document', + contextParameters: { + attachEl: { + entries: [ + { + path: '/default-domain', + title: 'Domain', + type: 'Domain', + uid: '1', + }, + { + path: '/default-domain/workspaces', + title: 'Workspaces', + type: 'WorkspaceRoot', + uid: '2', + }, + { + path: '/default-domain/workspaces/my workspace', + title: 'my workspace', + type: 'Workspace', + uid: '3', + }, + { + path: '/default-domain/workspaces/my workspace/folder 1', + title: 'folder 1', + type: 'Folder', + uid: '4', + }, + { + path: '/default-domain/workspaces/my workspace/folder 1/folder 2', + title: 'folder 2', + type: 'Folder', + uid: '5', + }, + { + path: '/default-domain/workspaces/my workspace/folder 1/folder 2/folder 3', + title: 'folder 3', + type: 'Folder', + uid: '6', + }, + { + path: '/default-domain/workspaces/my workspace/folder 1/folder 2/folder 3/my file', + title: 'my file', + type: 'File', + uid: '7', + }, + ], + }, + }, + path: '/default-domain/workspaces/my workspace/folder 1/folder 2/folder 3/my file', + title: 'my file', + type: 'File', + uid: '7', +}; + +window.nuxeo.I18n.language = 'en'; +window.nuxeo.I18n.en = window.nuxeo.I18n.en || {}; +window.nuxeo.I18n.en['retention.holdToggleButton.tooltip.hold'] = 'Legal Hold'; +window.nuxeo.I18n.en['retention.holdToggleButton.tooltip.unhold'] = 'Unhold'; +window.nuxeo.I18n.en['retention.holdToggleButton.bulk.hold.poll'] = 'Setting legal hold'; +window.nuxeo.I18n.en['retention.holdToggleButton.bulk.hold'] = 'Legal hold set'; +window.nuxeo.I18n.en['retention.holdToggleButton.bulk.unhold'] = 'Legal hold unset'; +window.nuxeo.I18n.en['retention.holdToggleButton.bulk.unhold.poll'] = 'Unsetting legal hold'; + +suite('nuxeo-hold-toggle-button', () => { + let attachEl; + + setup(async () => { + attachEl = await fixture(html` `); + }); + + suite('test _isAvailable', () => { + test('Should return provider object if provider is available', async () => { + const providerObj = { + provider: true, + }; + attachEl.provider = providerObj; + sinon.stub(attachEl, 'canSetLegalHold').returns(true); + expect(attachEl._isAvailable()).equal(providerObj); + }); + + test('Should return true if provider is not available and canSetLegalHold permission is present', async () => { + attachEl.provider = null; + sinon.stub(attachEl, 'canSetLegalHold').returns(true); + expect(attachEl._isAvailable()).equal(true); + }); + + test('Should return false if provider is not available and canSetLegalHold permission is not present', async () => { + attachEl.provider = null; + sinon.stub(attachEl, 'canSetLegalHold').returns(false); + expect(attachEl._isAvailable()).equal(false); + }); + }); + + suite('test _hold', () => { + const windowStub = sinon.stub(window, 'confirm'); + test('Should execute Bulk.RunAction if user confirms to put document under legal hold and provider is present', async () => { + const providerObj = { + provider: true, + }; + attachEl.provider = providerObj; + attachEl.document.isFlexibleRecord = true; + attachEl.document.isUnderRetentionOrLegalHold = true; + attachEl.description = 'some text'; + windowStub.returns(true); + sinon.spy(attachEl, '_toggleDialog'); + sinon.stub(attachEl.$.opHold, 'execute').resolves(); + sinon.spy(attachEl, 'dispatchEvent'); + attachEl._hold(); + expect(attachEl.$.opHold.op).equal('Bulk.RunAction'); + expect(attachEl.$.opHold.input).equal(providerObj); + expect(attachEl.$.opHold.async).equal(true); + setTimeout(() => { + expect(attachEl._toggleDialog.calledOnce).to.equal(true); + }, 0); + }); + + test('Should execute Document.Hold if user confirms to put document under legal hold and provider is not present', async () => { + attachEl.provider = null; + attachEl.document.isFlexibleRecord = true; + attachEl.document.isUnderRetentionOrLegalHold = true; + attachEl.description = 'some text'; + sinon.spy(attachEl, '_toggleDialog'); + sinon.stub(attachEl.$.opHold, 'execute').resolves(); + sinon.spy(attachEl, 'dispatchEvent'); + attachEl._hold(); + expect(attachEl.$.opHold.op).equal('Document.Hold'); + expect(attachEl.$.opHold.input).equal(attachEl.document); + expect(attachEl.$.opHold.async).equal(false); + expect(attachEl.$.opHold.params).to.deep.equal({ description: 'some text' }); + setTimeout(() => { + expect(attachEl._toggleDialog.calledOnce).to.equal(true); + expect(attachEl.dispatchEvent.calledOnce).to.equal(true); + }, 0); + }); + + test('Should not execute either Bulk.RunAction or Document.Hold if user cancels legal hold on document', async () => { + sinon.spy(attachEl, '_toggleDialog'); + sinon.stub(attachEl.$.opHold, 'execute'); + attachEl.document.isFlexibleRecord = true; + attachEl.document.isUnderRetentionOrLegalHold = true; + windowStub.returns(false); + attachEl._hold(); + expect(attachEl._toggleDialog.calledOnce).to.equal(false); + expect(attachEl.$.opHold.execute.calledOnce).to.equal(false); + }); + }); + + suite('test _unhold', () => { + test('Should execute Bulk.RunAction if provider is present', async () => { + const providerObj = { + provider: true, + }; + attachEl.provider = providerObj; + sinon.stub(attachEl.$.opUnhold, 'execute'); + attachEl._unhold(); + expect(attachEl.$.opUnhold.op).equal('Bulk.RunAction'); + expect(attachEl.$.opUnhold.input).equal(providerObj); + expect(attachEl.$.opUnhold.async).equal(true); + expect(attachEl.$.opUnhold.params).to.deep.equal({ action: 'unholdDocumentsAction' }); + expect(attachEl.$.opUnhold.execute.calledOnce).to.equal(true); + }); + + test('Should execute Document.Unhold if provider is not present', async () => { + attachEl.provider = null; + sinon.stub(attachEl.$.opUnhold, 'execute').resolves(); + sinon.spy(attachEl, 'dispatchEvent'); + attachEl._unhold(); + expect(attachEl.$.opUnhold.op).equal('Document.Unhold'); + expect(attachEl.$.opUnhold.input).equal(attachEl.document); + expect(attachEl.$.opUnhold.async).equal(false); + expect(attachEl.$.opUnhold.params).to.deep.equal({}); + setTimeout(() => { + expect(attachEl.dispatchEvent.calledOnce).to.equal(true); + }, 0); + }); + }); + + suite('test _unhold', () => { + test('Should toggle dialog if hold is set to false', async () => { + attachEl.hold = false; + sinon.spy(attachEl, '_toggleDialog'); + attachEl._toggle(); + expect(attachEl._toggleDialog.calledOnce).to.equal(true); + }); + + test('Should perform unhold operation if hold is set to true', async () => { + attachEl.hold = true; + sinon.spy(attachEl, '_unhold'); + attachEl._toggle(); + expect(attachEl._unhold.calledOnce).to.equal(true); + }); + }); + + suite('test _toggleDialog', () => { + test('Should reset popup and toggle dialog', async () => { + sinon.spy(attachEl, '_resetPopup'); + sinon.stub(attachEl.$.dialog, 'toggle'); + attachEl._toggleDialog(); + expect(attachEl._resetPopup.calledOnce).to.equal(true); + expect(attachEl.$.dialog.toggle.calledOnce).to.equal(true); + }); + }); + + suite('test _resetPopup', () => { + test('Should set description to null', async () => { + sinon.spy(attachEl, 'set'); + attachEl._resetPopup(); + expect(attachEl.set.calledWith('description', null)).to.equal(true); + }); + }); + + suite('test _computeTooltip', () => { + test('Should return tooltip text for hold', async () => { + attachEl.hold = true; + expect(attachEl._computeTooltip()).equal('Unhold'); + }); + + test('Should return tooltip text for unhold', async () => { + attachEl.hold = false; + expect(attachEl._computeTooltip()).equal('Legal Hold'); + }); + }); + + suite('test _computeLabel', () => { + test('Should return label for hold', async () => { + attachEl.hold = true; + expect(attachEl._computeLabel()).equal('Unhold'); + }); + + test('Should return label for unhold', async () => { + attachEl.hold = false; + expect(attachEl._computeLabel()).equal('Legal Hold'); + }); + }); + + suite('test _computeIcon', () => { + test('Should return label for hold', async () => { + attachEl.hold = true; + expect(attachEl._computeIcon()).equal('nuxeo:hold'); + }); + + test('Should return label for unhold', async () => { + attachEl.hold = false; + expect(attachEl._computeIcon()).equal('nuxeo:unhold'); + }); + }); + + suite('test _documentChanged', () => { + test('Should set hold property to true if document is present and has legal hold', async () => { + attachEl.document = document; + attachEl.document.hasLegalHold = true; + attachEl._documentChanged(); + expect(attachEl.hold).equal(true); + }); + + test('Should set hold property to true if document is present but does not have legal hold', async () => { + attachEl.document = document; + attachEl.document.hasLegalHold = false; + attachEl._documentChanged(); + expect(attachEl.hold).equal(false); + }); + }); + + suite('test _onHoldPollStart', () => { + test('Should dispatch notify event', async () => { + sinon.spy(attachEl, 'dispatchEvent'); + attachEl._onHoldPollStart(); + expect( + attachEl.dispatchEvent.calledWith( + new CustomEvent('notify', { + composed: true, + bubbles: true, + detail: { message: 'Setting legal hold' }, + }), + ), + ).to.equal(true); + }); + }); + + suite('test _onUnholdPollStart', () => { + test('Should dispatch notify event', async () => { + sinon.spy(attachEl, 'dispatchEvent'); + attachEl._onUnholdPollStart(); + expect( + attachEl.dispatchEvent.calledWith( + new CustomEvent('notify', { + composed: true, + bubbles: true, + detail: { message: 'Unsetting legal hold' }, + }), + ), + ).to.equal(true); + }); + }); + + suite('test _onHoldResponse', () => { + test('Should dispatch notify and refresh event', async () => { + sinon.stub(attachEl.$.waitEs, 'execute').resolves(); + sinon.spy(attachEl, 'dispatchEvent'); + attachEl._onHoldResponse(); + setTimeout(() => { + expect(attachEl.dispatchEvent.calledTwice).to.equal(true); + }, 0); + }); + }); + + suite('test _onUnholdResponse', () => { + test('Should dispatch notify and refresh event', async () => { + sinon.stub(attachEl.$.waitEs, 'execute').resolves(); + sinon.spy(attachEl, 'dispatchEvent'); + attachEl._onUnholdResponse(); + setTimeout(() => { + expect(attachEl.dispatchEvent.calledTwice).to.equal(true); + }, 0); + }); + }); +}); diff --git a/nuxeo-retention-web/test/nuxeo-retain-button.test.js b/nuxeo-retention-web/test/nuxeo-retain-button.test.js new file mode 100644 index 00000000..5acce481 --- /dev/null +++ b/nuxeo-retention-web/test/nuxeo-retain-button.test.js @@ -0,0 +1,188 @@ +/** +@license +©2023 Hyland Software, Inc. and its affiliates. All rights reserved. +All Hyland product names are registered or unregistered trademarks of Hyland Software, Inc. or its affiliates. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use attachEl 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 { fixture, html } from '@nuxeo/testing-helpers'; +import moment from '@nuxeo/moment'; +import '../elements/nuxeo-retain-button.js'; +import sinon from 'sinon'; +import { expect } from 'chai'; + +const document = { + 'entity-type': 'document', + contextParameters: { + attachEl: { + entries: [ + { + path: '/default-domain', + title: 'Domain', + type: 'Domain', + uid: '1', + }, + { + path: '/default-domain/workspaces', + title: 'Workspaces', + type: 'WorkspaceRoot', + uid: '2', + }, + { + path: '/default-domain/workspaces/my workspace', + title: 'my workspace', + type: 'Workspace', + uid: '3', + }, + { + path: '/default-domain/workspaces/my workspace/folder 1', + title: 'folder 1', + type: 'Folder', + uid: '4', + }, + { + path: '/default-domain/workspaces/my workspace/folder 1/folder 2', + title: 'folder 2', + type: 'Folder', + uid: '5', + }, + { + path: '/default-domain/workspaces/my workspace/folder 1/folder 2/folder 3', + title: 'folder 3', + type: 'Folder', + uid: '6', + }, + { + path: '/default-domain/workspaces/my workspace/folder 1/folder 2/folder 3/my file', + title: 'my file', + type: 'File', + uid: '7', + }, + ], + }, + }, + path: '/default-domain/workspaces/my workspace/folder 1/folder 2/folder 3/my file', + title: 'my file', + type: 'File', + uid: '7', +}; + +window.nuxeo.I18n.language = 'en'; +window.nuxeo.I18n.en = window.nuxeo.I18n.en || {}; +window.nuxeo.I18n.en['retention.action.retain'] = 'Extend retention'; + +suite('nuxeo-retain-button', () => { + let attachEl; + + setup(async () => { + attachEl = await fixture(html` `); + }); + + suite('test _isAvailable', () => { + test('Should return true if canSetRetention permission is available', async () => { + sinon.stub(attachEl, 'canSetRetention').returns(true); + expect(attachEl._isAvailable()).equal(true); + }); + + test('Should return false if canSetRetention permission is not available', async () => { + sinon.stub(attachEl, 'canSetRetention').returns(false); + expect(attachEl._isAvailable()).equal(false); + }); + }); + + suite('test _computeLabel', () => { + test('Should return i18n label for extend retention button', async () => { + expect(attachEl._computeLabel()).equal('Extend retention'); + }); + }); + + suite('test _toggleDialog', () => { + test('Should toggle dialog', async () => { + sinon.stub(attachEl.$.dialog, 'toggle'); + attachEl._toggleDialog(); + expect(attachEl.$.dialog.toggle.calledOnce).to.equal(true); + }); + }); + + suite('test _retain', () => { + test('Should toggle dialog', async () => { + sinon.stub(attachEl.$.retainOp, 'execute').resolves(); + sinon.spy(attachEl, '_toggleDialog'); + sinon.spy(attachEl, 'dispatchEvent'); + attachEl.until = '2025-05-09'; + attachEl._retain(); + expect(attachEl.$.retainOp.params).to.deep.equal({ until: '2025-05-09' }); + setTimeout(() => { + expect(attachEl.dispatchEvent.calledOnce).to.equal(true); + expect(attachEl._toggleDialog.calledOnce).to.equal(true); + }, 0); + }); + }); + + suite('test _computeMinDate', () => { + setup(async () => { + sinon.spy(attachEl, 'set'); + }); + test('Should return min date as retain until date of document if document has retain until date & retention date is not indeterminate', async () => { + attachEl.document.retainUntil = '2025-05-09'; + sinon.stub(attachEl, 'isRetentionDateIndeterminate').returns(false); + expect(attachEl._computeMinDate()).equal('2025-05-09'); + expect(attachEl.set.calledWith('until', '2025-05-09')).to.equal(true); + }); + + test("Should return min date as tomorrow's date if document has retain until date but retention date is indeterminate", async () => { + attachEl.document.retainUntil = '2025-05-09'; + const tomorrow = moment().add(1, 'days'); + sinon.stub(attachEl, 'isRetentionDateIndeterminate').returns(true); + expect(attachEl._computeMinDate()).equal(moment(tomorrow.toJSON()).format('YYYY-MM-DD')); + expect(attachEl.set.calledWith('until', undefined)).to.equal(true); + }); + + test("Should return min date as tomorrow's date if document does not have retain until date though retention date is not indeterminate", async () => { + attachEl.document.retainUntil = null; + const tomorrow = moment().add(1, 'days'); + sinon.stub(attachEl, 'isRetentionDateIndeterminate').returns(false); + expect(attachEl._computeMinDate()).equal(moment(tomorrow.toJSON()).format('YYYY-MM-DD')); + expect(attachEl.set.calledWith('until', undefined)).to.equal(true); + }); + + test("Should return min date as tomorrow's date if document does not have retain until date and retention date is indeterminate", async () => { + attachEl.document.retainUntil = null; + const tomorrow = moment().add(1, 'days'); + sinon.stub(attachEl, 'isRetentionDateIndeterminate').returns(true); + expect(attachEl._computeMinDate()).equal(moment(tomorrow.toJSON()).format('YYYY-MM-DD')); + expect(attachEl.set.calledWith('until', undefined)).to.equal(true); + }); + }); + + suite('test _isValid', () => { + test('Should return true if document is available & retainUntil property is valid in document', async () => { + attachEl.document = document; + attachEl.document.retainUntil = '2025-05-09'; + attachEl.until = '2025-05-09'; + expect(attachEl._isValid()).equal(true); + }); + + test('Should return false if document is available & retainUntil property is valid in document but until is undefined', async () => { + attachEl.document = document; + attachEl.document.retainUntil = '2025-05-09'; + expect(attachEl._isValid()).equal(false); + }); + + test('Should return true if document is available but retainUntil property is not available in document', async () => { + attachEl.document = document; + attachEl.until = '2025-05-09'; + expect(attachEl._isValid()).equal(true); + }); + }); +}); diff --git a/nuxeo-retention-web/test/nuxeo-retention-behavior.test.js b/nuxeo-retention-web/test/nuxeo-retention-behavior.test.js new file mode 100644 index 00000000..cc6829fd --- /dev/null +++ b/nuxeo-retention-web/test/nuxeo-retention-behavior.test.js @@ -0,0 +1,271 @@ +/** +@license +©2023 Hyland Software, Inc. and its affiliates. All rights reserved. +All Hyland product names are registered or unregistered trademarks of Hyland Software, Inc. or its affiliates. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use retentionBehaviorInstance 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 sinon from 'sinon'; +import { expect } from 'chai'; +import { Nuxeo } from '../elements/nuxeo-retention-behavior.js'; + +suite('nuxeo-retention-behavior', () => { + const document = { + 'entity-type': 'document', + contextParameters: { + attachEl: { + entries: [ + { + path: '/default-domain', + title: 'Domain', + type: 'Domain', + uid: '1', + }, + { + path: '/default-domain/workspaces', + title: 'Workspaces', + type: 'WorkspaceRoot', + uid: '2', + }, + { + path: '/default-domain/workspaces/my workspace', + title: 'my workspace', + type: 'Workspace', + uid: '3', + }, + { + path: '/default-domain/workspaces/my workspace/folder 1', + title: 'folder 1', + type: 'Folder', + uid: '4', + }, + { + path: '/default-domain/workspaces/my workspace/folder 1/folder 2', + title: 'folder 2', + type: 'Folder', + uid: '5', + }, + { + path: '/default-domain/workspaces/my workspace/folder 1/folder 2/folder 3', + title: 'folder 3', + type: 'Folder', + uid: '6', + }, + { + path: '/default-domain/workspaces/my workspace/folder 1/folder 2/folder 3/my file', + title: 'my file', + type: 'File', + uid: '7', + }, + ], + }, + }, + path: '/default-domain/workspaces/my workspace/folder 1/folder 2/folder 3/my file', + title: 'my file', + type: 'File', + uid: '7', + properties: {}, + }; + let sandbox; + let retentionBehaviorInstance; + const originalRetentionBehaviorObj = Nuxeo.RetentionBehavior; + + setup(async () => { + retentionBehaviorInstance = Object.create(Nuxeo.RetentionBehavior); + sandbox = sinon.createSandbox(); + }); + + teardown(() => { + Nuxeo.RetentionBehavior = originalRetentionBehaviorObj; + sandbox.restore(); + }); + + suite('test _isAuto', () => { + test('Should return true if document is available & document.properties is available & document.properties["retention_rule:applicationPolicy"] = auto', async () => { + retentionBehaviorInstance.document = document; + retentionBehaviorInstance.document.properties['retention_rule:applicationPolicy'] = 'auto'; + expect(retentionBehaviorInstance._isAuto()).equal(true); + }); + + test('Should return false if document is available & document.properties is available but document.properties["retention_rule:applicationPolicy"] != auto', async () => { + retentionBehaviorInstance.document = document; + retentionBehaviorInstance.document.properties['retention_rule:applicationPolicy'] = 'custom'; + expect(retentionBehaviorInstance._isAuto()).equal(false); + }); + + test('Should return false if document is available but document.properties is not available', async () => { + retentionBehaviorInstance.document = document; + expect(retentionBehaviorInstance._isAuto()).equal(false); + }); + + test('Should return false if document is not available', async () => { + retentionBehaviorInstance.document = {}; + expect(retentionBehaviorInstance._isAuto()).equal(undefined); + }); + }); + + suite('test _isImmediate', () => { + test('Should return true if document is available & document.properties is available & document.properties["retention_def:startingPointPolicy"] = immediate', async () => { + retentionBehaviorInstance.document = document; + retentionBehaviorInstance.document.properties['retention_def:startingPointPolicy'] = 'immediate'; + expect(retentionBehaviorInstance._isImmediate()).equal(true); + }); + + test('Should return false if document is available & document.properties is available but document.properties["retention_def:startingPointPolicy"] != immediate', async () => { + retentionBehaviorInstance.document = document; + retentionBehaviorInstance.document.properties['retention_def:startingPointPolicy'] = 'custom'; + expect(retentionBehaviorInstance._isImmediate()).equal(false); + }); + + test('Should return false if document is available but document.properties is not available', async () => { + retentionBehaviorInstance.document = document; + expect(retentionBehaviorInstance._isImmediate()).equal(false); + }); + + test('Should return false if document is not available', async () => { + retentionBehaviorInstance.document = {}; + expect(retentionBehaviorInstance._isImmediate()).equal(undefined); + }); + }); + + suite('test _isEventBased', () => { + test('Should return true if document is available & document.properties is available & document.properties["retention_def:startingPointPolicy"] = event_based', async () => { + retentionBehaviorInstance.document = document; + retentionBehaviorInstance.document.properties['retention_def:startingPointPolicy'] = 'event_based'; + expect(retentionBehaviorInstance._isEventBased()).equal(true); + }); + + test('Should return false if document is available & document.properties is available but document.properties["retention_def:startingPointPolicy"] != event_based', async () => { + retentionBehaviorInstance.document = document; + retentionBehaviorInstance.document.properties['retention_def:startingPointPolicy'] = 'custom'; + expect(retentionBehaviorInstance._isEventBased()).equal(false); + }); + + test('Should return false if document is available but document.properties is not available', async () => { + retentionBehaviorInstance.document = document; + expect(retentionBehaviorInstance._isEventBased()).equal(false); + }); + + test('Should return false if document is not available', async () => { + retentionBehaviorInstance.document = {}; + expect(retentionBehaviorInstance._isEventBased()).equal(undefined); + }); + }); + + suite('test _isMetadataBased', () => { + test('Should return true if document is available & document.properties is available & document.properties["retention_def:startingPointPolicy"] = metadata_based', async () => { + retentionBehaviorInstance.document = document; + retentionBehaviorInstance.document.properties['retention_def:startingPointPolicy'] = 'metadata_based'; + expect(retentionBehaviorInstance._isMetadataBased()).equal(true); + }); + + test('Should return false if document is available & document.properties is available but document.properties["retention_def:startingPointPolicy"] != metadata_based', async () => { + retentionBehaviorInstance.document = document; + retentionBehaviorInstance.document.properties['retention_def:startingPointPolicy'] = 'custom'; + expect(retentionBehaviorInstance._isMetadataBased()).equal(false); + }); + + test('Should return false if document is available but document.properties is not available', async () => { + retentionBehaviorInstance.document = document; + expect(retentionBehaviorInstance._isMetadataBased()).equal(false); + }); + + test('Should return false if document is not available', async () => { + retentionBehaviorInstance.document = {}; + expect(retentionBehaviorInstance._isMetadataBased()).equal(undefined); + }); + }); + + suite('test _isAfterDelay', () => { + test('Should return true if document is available & document.properties is available & document.properties["retention_def:startingPointPolicy"] = after_delay', async () => { + retentionBehaviorInstance.document = document; + retentionBehaviorInstance.document.properties['retention_def:startingPointPolicy'] = 'after_delay'; + expect(retentionBehaviorInstance._isAfterDelay()).equal(true); + }); + + test('Should return false if document is available & document.properties is available but document.properties["retention_def:startingPointPolicy"] != after_delay', async () => { + retentionBehaviorInstance.document = document; + retentionBehaviorInstance.document.properties['retention_def:startingPointPolicy'] = 'custom'; + expect(retentionBehaviorInstance._isAfterDelay()).equal(false); + }); + + test('Should return false if document is available but document.properties is not available', async () => { + retentionBehaviorInstance.document = document; + expect(retentionBehaviorInstance._isAfterDelay()).equal(false); + }); + + test('Should return false if document is not available', async () => { + retentionBehaviorInstance.document = {}; + expect(retentionBehaviorInstance._isAfterDelay()).equal(undefined); + }); + }); + + suite('test _isManual', () => { + test('Should return true if document is available & document.properties is available & document.properties["retention_rule:applicationPolicy"] = manual', async () => { + retentionBehaviorInstance.document = document; + retentionBehaviorInstance.document.properties['retention_rule:applicationPolicy'] = 'manual'; + expect(retentionBehaviorInstance._isManual()).equal(true); + }); + + test('Should return false if document is available & document.properties is available but document.properties["retention_rule:applicationPolicy"] != manual', async () => { + retentionBehaviorInstance.document = document; + retentionBehaviorInstance.document.properties['retention_rule:applicationPolicy'] = 'custom'; + expect(retentionBehaviorInstance._isManual()).equal(false); + }); + + test('Should return false if document is available but document.properties is not available', async () => { + retentionBehaviorInstance.document = document; + expect(retentionBehaviorInstance._isManual()).equal(false); + }); + + test('Should return false if document is not available', async () => { + retentionBehaviorInstance.document = {}; + expect(retentionBehaviorInstance._isManual()).equal(undefined); + }); + }); + + suite('test _computeApplicationPolicyLabel', () => { + test('Should return empty string if document is not available', async () => { + expect(retentionBehaviorInstance._computeApplicationPolicyLabel()).equal(''); + }); + }); + + suite('test _computeStartPolicyLabel', () => { + test('Should return empty string if document is not available', async () => { + expect(retentionBehaviorInstance._computeStartPolicyLabel()).equal(''); + }); + }); + + suite('test ready', () => { + test('should set docTypes and dateFields when fetcher resolves', async () => { + const resObj = Promise.resolve({ + docTypes: [ + { + doc: { + schemas: ['file', 'folder', 'picture'], + facets: ['record'], + }, + }, + ], + }); + + sandbox.stub(window.document, 'createElement').returns({ get: sandbox.stub().resolves(resObj) }); + sandbox.stub(window.document.body, 'appendChild'); + sandbox.stub(window.document.body, 'removeChild'); + await retentionBehaviorInstance.ready(); + expect(retentionBehaviorInstance.properties.docTypes).to.be.an.instanceof(Function); + expect(retentionBehaviorInstance.properties.dateFields).to.be.an.instanceof(Function); + }); + }); +}); diff --git a/nuxeo-retention-web/test/nuxeo-retention-events.test.js b/nuxeo-retention-web/test/nuxeo-retention-events.test.js new file mode 100644 index 00000000..3de972e2 --- /dev/null +++ b/nuxeo-retention-web/test/nuxeo-retention-events.test.js @@ -0,0 +1,223 @@ +/** +@license +©2023 Hyland Software, Inc. and its affiliates. All rights reserved. +All Hyland product names are registered or unregistered trademarks of Hyland Software, Inc. or its affiliates. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use attachEl 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 { fixture, html } from '@nuxeo/testing-helpers'; +import '../elements/nuxeo-retention-events.js'; +import sinon from 'sinon'; +import { expect } from 'chai'; + +const document = { + 'entity-type': 'document', + contextParameters: { + attachEl: { + entries: [ + { + path: '/default-domain', + title: 'Domain', + type: 'Domain', + uid: '1', + }, + { + path: '/default-domain/workspaces', + title: 'Workspaces', + type: 'WorkspaceRoot', + uid: '2', + }, + { + path: '/default-domain/workspaces/my workspace', + title: 'my workspace', + type: 'Workspace', + uid: '3', + }, + { + path: '/default-domain/workspaces/my workspace/folder 1', + title: 'folder 1', + type: 'Folder', + uid: '4', + }, + { + path: '/default-domain/workspaces/my workspace/folder 1/folder 2', + title: 'folder 2', + type: 'Folder', + uid: '5', + }, + { + path: '/default-domain/workspaces/my workspace/folder 1/folder 2/folder 3', + title: 'folder 3', + type: 'Folder', + uid: '6', + }, + { + path: '/default-domain/workspaces/my workspace/folder 1/folder 2/folder 3/my file', + title: 'my file', + type: 'File', + uid: '7', + }, + ], + }, + }, + path: '/default-domain/workspaces/my workspace/folder 1/folder 2/folder 3/my file', + title: 'my file', + type: 'File', + uid: '7', +}; + +window.nuxeo.I18n.language = 'en'; +window.nuxeo.I18n.en = window.nuxeo.I18n.en || {}; +window.nuxeo.I18n.en['retention.events.empty'] = 'No past events'; +window.nuxeo.I18n.en['retention.events.fired.success'] = 'Event successfully fired'; + +suite('nuxeo-retention-events', () => { + let attachEl; + + setup(async () => { + attachEl = await fixture(html` `); + }); + + suite('test _observeStartDate', () => { + test('Should set valid start and end date & referesh history if start date & end date are available', () => { + sinon.spy(attachEl, '_refreshHistory'); + attachEl.startDate = '2024-02-20'; + attachEl._observeStartDate(); + expect(attachEl.$.provider.params.startDate).equal('2024-02-20'); + expect(attachEl._refreshHistory.calledTwice).to.equal(true); + }); + + test('Should delete provider start date if it is available & refresh history', async () => { + attachEl.$.provider.params.startDate = '2024-02-19'; + sinon.spy(attachEl, '_refreshHistory'); + attachEl._observeStartDate(); + expect(attachEl.$.provider.params.startDate).equal(undefined); + expect(attachEl._refreshHistory.calledOnce).to.equal(true); + }); + }); + + suite('test _observeEndDate', () => { + test('Should set valid start and end date & referesh history if start date & end date are available', () => { + sinon.spy(attachEl, '_refreshHistory'); + attachEl.endDate = '2024-02-20'; + attachEl._observeEndDate(); + expect(attachEl.$.provider.params.endDate).equal('2024-02-20'); + expect(attachEl._refreshHistory.calledTwice).to.equal(true); + }); + + test('Should delete provider start date if it is available & refresh history', async () => { + attachEl.$.provider.params.endDate = '2024-02-19'; + sinon.spy(attachEl, '_refreshHistory'); + attachEl._observeEndDate(); + expect(attachEl.$.provider.params.endDate).equal(undefined); + expect(attachEl._refreshHistory.calledOnce).to.equal(true); + }); + }); + + suite('test _refreshHistory', () => { + test('Should fetch retention events table if visible is true', async () => { + attachEl.visible = true; + sinon.stub(attachEl.$.table, 'reset'); + sinon.stub(attachEl.$.table, 'fetch').resolves(); + attachEl._refreshHistory(100); + expect(attachEl.$.provider.page).equal(1); + expect(attachEl.$.table.reset.calledOnce).to.equal(true); + setTimeout(() => { + expect(attachEl.$.table.emptyLabel).equal('No past events'); + }, 100); + }); + + test('Should not fetch retention events table if visible is false', async () => { + attachEl.visible = false; + sinon.stub(attachEl.$.table, 'reset'); + sinon.stub(attachEl.$.table, 'fetch').resolves(); + attachEl._refreshHistory(100); + expect(attachEl.$.table.reset.calledOnce).to.equal(false); + setTimeout(() => { + expect(attachEl.$.table.emptyLabel).equal('No past events'); + }, 100); + }); + }); + + suite('test _fire', () => { + test('Should dispatch notify event and referesh history when operation is executed', async () => { + sinon.stub(attachEl.$.op, 'execute').resolves(); + sinon.spy(attachEl, 'dispatchEvent'); + sinon.spy(attachEl, '_refreshHistory'); + attachEl._event = 'fire'; + attachEl._eventInput = 'fire-input'; + attachEl._fire(); + expect(attachEl.$.op.params).to.deep.equal({ name: 'fire' }); + expect(attachEl.$.op.input).equal('fire-input'); + setTimeout(() => { + expect(attachEl.dispatchEvent.calledOnce).to.equal(true); + expect(attachEl._event).equal(null); + expect(attachEl._eventInput).equal(null); + expect(attachEl._refreshHistory.calledWith(100)).to.equal(false); + }, 0); + }); + }); + + suite('test _canFire', () => { + test('Should return true if firingEvent = false & _event is valid', async () => { + attachEl.firingEvent = false; + attachEl._event = 'some event'; + expect(attachEl._canFire()).equal(true); + }); + + test('Should return false if firingEvent = true & _event is valid', async () => { + attachEl.firingEvent = true; + attachEl._event = 'some event'; + expect(attachEl._canFire()).equal(false); + }); + + test('Should return false if firingEvent = false & _event is invalid', async () => { + attachEl.firingEvent = false; + attachEl._event = ''; + expect(attachEl._canFire()).equal(false); + }); + + test('Should return false if firingEvent = true & _event is invalid', async () => { + attachEl.firingEvent = true; + attachEl._event = ''; + expect(attachEl._canFire()).equal(false); + }); + }); + + suite('test _filterEvents', () => { + test('Should return true if evt is valid & evt.id is present & evt.id has the string "Retention."', async () => { + const evt = { + id: 'Retention.AttachRule', + }; + expect(attachEl._filterEvents(evt)).equal(true); + }); + + test('Should return false if evt is valid & evt.id is present but evt.id does not have the string "Retention."', async () => { + const evt = { + id: 'label', + }; + expect(attachEl._filterEvents(evt)).equal(false); + }); + + test('Should return false if evt is valid but evt.id is not present', async () => { + const evt = { + label: 'Retention', + }; + expect(attachEl._filterEvents(evt)).equal(undefined); + }); + + test('Should return false if evt is invalid', async () => { + expect(attachEl._filterEvents()).equal(undefined); + }); + }); +}); diff --git a/nuxeo-retention-web/test/nuxeo-unattach-rule-button.test.js b/nuxeo-retention-web/test/nuxeo-unattach-rule-button.test.js new file mode 100644 index 00000000..1f1fc6ff --- /dev/null +++ b/nuxeo-retention-web/test/nuxeo-unattach-rule-button.test.js @@ -0,0 +1,159 @@ +/** +@license +©2023 Hyland Software, Inc. and its affiliates. All rights reserved. +All Hyland product names are registered or unregistered trademarks of Hyland Software, Inc. or its affiliates. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use attachEl 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 { fixture, html } from '@nuxeo/testing-helpers'; +import '../elements/nuxeo-unattach-rule-button.js'; +import sinon from 'sinon'; +import { expect } from 'chai'; + +const document = { + 'entity-type': 'document', + contextParameters: { + attachEl: { + entries: [ + { + path: '/default-domain', + title: 'Domain', + type: 'Domain', + uid: '1', + }, + { + path: '/default-domain/workspaces', + title: 'Workspaces', + type: 'WorkspaceRoot', + uid: '2', + }, + { + path: '/default-domain/workspaces/my workspace', + title: 'my workspace', + type: 'Workspace', + uid: '3', + }, + { + path: '/default-domain/workspaces/my workspace/folder 1', + title: 'folder 1', + type: 'Folder', + uid: '4', + }, + { + path: '/default-domain/workspaces/my workspace/folder 1/folder 2', + title: 'folder 2', + type: 'Folder', + uid: '5', + }, + { + path: '/default-domain/workspaces/my workspace/folder 1/folder 2/folder 3', + title: 'folder 3', + type: 'Folder', + uid: '6', + }, + { + path: '/default-domain/workspaces/my workspace/folder 1/folder 2/folder 3/my file', + title: 'my file', + type: 'File', + uid: '7', + }, + ], + }, + }, + path: '/default-domain/workspaces/my workspace/folder 1/folder 2/folder 3/my file', + title: 'my file', + type: 'File', + uid: '7', +}; + +window.nuxeo.I18n.language = 'en'; +window.nuxeo.I18n.en = window.nuxeo.I18n.en || {}; +window.nuxeo.I18n.en['retention.rule.label.undeclare'] = 'Undeclare Record'; +window.nuxeo.I18n.en['retention.rule.label.undeclared.notify'] = 'Record undeclared'; + +suite('nuxeo-unattach-rule-button', () => { + let attachEl; + + setup(async () => { + attachEl = await fixture(html` `); + }); + + suite('test _isAvailable', () => { + test('Should return true if document is available & isFlexibleRecord property is true & hasFacet = true & has WriteProperties & UnsetRetention permission', async () => { + const doc = document; + doc.isFlexibleRecord = true; + const permissionStub = sinon.stub(attachEl, 'hasPermission'); + sinon.stub(attachEl, 'hasFacet').returns(true); + permissionStub.withArgs(doc, 'WriteProperties').returns(true); + permissionStub.withArgs(doc, 'UnsetRetention').returns(true); + expect(attachEl._isAvailable(doc)).equal(true); + }); + + test('Should return false if document is available & isFlexibleRecord property is true & hasFacet = true & has WriteProperties but no UnsetRetention permission', async () => { + const doc = document; + doc.isFlexibleRecord = true; + const permissionStub = sinon.stub(attachEl, 'hasPermission'); + sinon.stub(attachEl, 'hasFacet').returns(true); + permissionStub.withArgs(doc, 'WriteProperties').returns(true); + permissionStub.withArgs(doc, 'UnsetRetention').returns(false); + expect(attachEl._isAvailable(doc)).equal(false); + }); + + test('Should return false if document is available & isFlexibleRecord property is true & hasFacet = true but no WriteProperties & UnsetRetention permission', async () => { + const doc = document; + doc.isFlexibleRecord = true; + const permissionStub = sinon.stub(attachEl, 'hasPermission'); + sinon.stub(attachEl, 'hasFacet').returns(true); + permissionStub.withArgs(doc, 'WriteProperties').returns(false); + permissionStub.withArgs(doc, 'UnsetRetention').returns(false); + expect(attachEl._isAvailable(doc)).equal(false); + }); + + test('Should return false if document is available & isFlexibleRecord property is true but hasFacet != true & has WriteProperties & UnsetRetention permission', async () => { + const doc = document; + doc.isFlexibleRecord = true; + const permissionStub = sinon.stub(attachEl, 'hasPermission'); + sinon.stub(attachEl, 'hasFacet').returns(false); + permissionStub.withArgs(doc, 'WriteProperties').returns(true); + permissionStub.withArgs(doc, 'UnsetRetention').returns(true); + expect(attachEl._isAvailable(doc)).equal(false); + }); + + test('Should return false if document is available & isFlexibleRecord property is false but hasFacet = true & has WriteProperties & UnsetRetention permission', async () => { + const doc = document; + doc.isFlexibleRecord = false; + const permissionStub = sinon.stub(attachEl, 'hasPermission'); + sinon.stub(attachEl, 'hasFacet').returns(true); + permissionStub.withArgs(doc, 'WriteProperties').returns(true); + permissionStub.withArgs(doc, 'UnsetRetention').returns(true); + expect(attachEl._isAvailable(doc)).equal(false); + }); + }); + + suite('test _computeLabel', () => { + test('Should return label for undeclare record', async () => { + expect(attachEl._computeLabel()).equal('Undeclare Record'); + }); + }); + + suite('test _unretain', () => { + test('Should dispatch notify and document-updated event', async () => { + sinon.stub(attachEl.$.unretainOp, 'execute').resolves(); + sinon.spy(attachEl, 'dispatchEvent'); + attachEl._unretain(); + setTimeout(() => { + expect(attachEl.dispatchEvent.calledTwice).to.equal(true); + }, 0); + }); + }); +});