From 8f28408607204547e627c90b62277b7e86f5b3d1 Mon Sep 17 00:00:00 2001
From: Thomas Hunter <thomas.hunter@hyland.com>
Date: Wed, 4 Jan 2023 16:14:03 +0000
Subject: [PATCH] [ACA-4070] Add unit tests for linking rule sets (#2878)

* Commit before rebase

* Added some tests in the rule set picker

* Add unit tests for manage rules component
---
 .../manage-rules.smart-component.html         |   1 +
 .../manage-rules.smart-component.spec.ts      |  34 ++++-
 .../src/lib/mock/node.mock.ts                 |  20 ++-
 .../src/lib/mock/rule-sets.mock.ts            |  22 +++-
 .../edit-rule-dialog.smart-component.ts       |   2 +-
 .../rule-set-picker.smart-component.spec.ts   | 116 ++++++++++++++++++
 .../rule-set-picker.smart-component.ts        |   7 +-
 .../services/folder-rule-sets.service.spec.ts |  12 ++
 8 files changed, 199 insertions(+), 15 deletions(-)
 create mode 100644 projects/aca-folder-rules/src/lib/rule-set-picker/rule-set-picker.smart-component.spec.ts

diff --git a/projects/aca-folder-rules/src/lib/manage-rules/manage-rules.smart-component.html b/projects/aca-folder-rules/src/lib/manage-rules/manage-rules.smart-component.html
index d1b5b133a..a5451562c 100644
--- a/projects/aca-folder-rules/src/lib/manage-rules/manage-rules.smart-component.html
+++ b/projects/aca-folder-rules/src/lib/manage-rules/manage-rules.smart-component.html
@@ -40,6 +40,7 @@
             <div class="aca-manage-rules__actions-bar__buttons">
               <button
                 *ngIf="!(mainRuleSet$ | async)"
+                data-automation-id="manage-rules-link-button"
                 mat-stroked-button
                 (click)="openLinkRulesDialog()">
                 {{ 'ACA_FOLDER_RULES.MANAGE_RULES.TOOLBAR.ACTIONS.LINK_RULES' | translate }}
diff --git a/projects/aca-folder-rules/src/lib/manage-rules/manage-rules.smart-component.spec.ts b/projects/aca-folder-rules/src/lib/manage-rules/manage-rules.smart-component.spec.ts
index ebc87905e..27a84799d 100644
--- a/projects/aca-folder-rules/src/lib/manage-rules/manage-rules.smart-component.spec.ts
+++ b/projects/aca-folder-rules/src/lib/manage-rules/manage-rules.smart-component.spec.ts
@@ -25,7 +25,7 @@
 
 import { ComponentFixture, TestBed } from '@angular/core/testing';
 import { AcaFolderRulesModule, ManageRulesSmartComponent } from '@alfresco/aca-folder-rules';
-import { DebugElement } from '@angular/core';
+import { DebugElement, Predicate } from '@angular/core';
 import { CoreTestingModule } from '@alfresco/adf-core';
 import { FolderRulesService } from '../services/folder-rules.service';
 import { ActivatedRoute } from '@angular/router';
@@ -223,27 +223,41 @@ describe('ManageRulesSmartComponent', () => {
     expect(deleteRuleBtn).toBeTruthy();
   });
 
-  describe('Create rule button visibility', () => {
+  describe('Create rule & link rules buttons visibility', () => {
+    let createButtonPredicate: Predicate<DebugElement>;
+    let linkButtonPredicate: Predicate<DebugElement>;
+
     beforeEach(() => {
       folderRuleSetsService.folderInfo$ = of(owningFolderMock);
       folderRuleSetsService.inheritedRuleSets$ = of([]);
       folderRuleSetsService.isLoading$ = of(false);
       actionsService.loading$ = of(false);
+
+      createButtonPredicate = By.css(`[data-automation-id="manage-rules-create-button"]`);
+      linkButtonPredicate = By.css(`[data-automation-id="manage-rules-link-button"]`);
     });
 
     it('should show the create rule button if there is no main rule set', () => {
       folderRuleSetsService.mainRuleSet$ = of(null);
       fixture.detectChanges();
 
-      const createButton = debugElement.query(By.css(`[data-automation-id="manage-rules-create-button"]`));
+      const createButton = debugElement.query(createButtonPredicate);
       expect(createButton).toBeTruthy();
     });
 
+    it('should show the link rules button if there is no main rule set', () => {
+      folderRuleSetsService.mainRuleSet$ = of(null);
+      fixture.detectChanges();
+
+      const linkButton = debugElement.query(linkButtonPredicate);
+      expect(linkButton).toBeTruthy();
+    });
+
     it('should show the create rule button if the main rule set is owned', () => {
       folderRuleSetsService.mainRuleSet$ = of(ownedRuleSetMock);
       fixture.detectChanges();
 
-      const createButton = debugElement.query(By.css(`[data-automation-id="manage-rules-create-button"]`));
+      const createButton = debugElement.query(createButtonPredicate);
       expect(createButton).toBeTruthy();
     });
 
@@ -251,12 +265,20 @@ describe('ManageRulesSmartComponent', () => {
       folderRuleSetsService.mainRuleSet$ = of(ruleSetWithLinkMock);
       fixture.detectChanges();
 
-      const createButton = debugElement.query(By.css(`[data-automation-id="manage-rules-create-button"]`));
+      const createButton = debugElement.query(createButtonPredicate);
       expect(createButton).toBeFalsy();
     });
+
+    it('should not show the link rules button if the folder has a main rule set', () => {
+      folderRuleSetsService.mainRuleSet$ = of(ownedRuleSetMock);
+      fixture.detectChanges();
+
+      const linkButton = debugElement.query(linkButtonPredicate);
+      expect(linkButton).toBeFalsy();
+    });
   });
 
-  describe('Rule inheritance toggle  button', () => {
+  describe('Rule inheritance toggle button', () => {
     beforeEach(() => {
       folderRuleSetsService.folderInfo$ = of(owningFolderMock);
       folderRuleSetsService.inheritedRuleSets$ = of([]);
diff --git a/projects/aca-folder-rules/src/lib/mock/node.mock.ts b/projects/aca-folder-rules/src/lib/mock/node.mock.ts
index 1648e6144..f820859c8 100644
--- a/projects/aca-folder-rules/src/lib/mock/node.mock.ts
+++ b/projects/aca-folder-rules/src/lib/mock/node.mock.ts
@@ -24,7 +24,7 @@
  */
 
 import { NodeInfo } from '@alfresco/aca-shared/store';
-import { NodeEntry } from '@alfresco/js-api';
+import { Node, NodeEntry } from '@alfresco/js-api';
 
 export const getOwningFolderEntryMock: NodeEntry = {
   entry: {
@@ -52,3 +52,21 @@ export const otherFolderMock: NodeInfo = {
   id: otherFolderIdMock,
   name: 'other-folder-name'
 };
+
+export const folderToLinkMock: Node = {
+  id: 'folder-1-id',
+  name: 'folder-1-name',
+  nodeType: 'folder',
+  isFolder: true,
+  isFile: false,
+  modifiedAt: new Date(),
+  modifiedByUser: {
+    id: 'user-id',
+    displayName: 'user-name'
+  },
+  createdAt: new Date(),
+  createdByUser: {
+    id: 'user-id',
+    displayName: 'user-name'
+  }
+};
diff --git a/projects/aca-folder-rules/src/lib/mock/rule-sets.mock.ts b/projects/aca-folder-rules/src/lib/mock/rule-sets.mock.ts
index e5acb3336..21d771a20 100644
--- a/projects/aca-folder-rules/src/lib/mock/rule-sets.mock.ts
+++ b/projects/aca-folder-rules/src/lib/mock/rule-sets.mock.ts
@@ -24,7 +24,7 @@
  */
 
 import { RuleSet } from '../model/rule-set.model';
-import { otherFolderIdMock, otherFolderMock, owningFolderIdMock, owningFolderMock } from './node.mock';
+import { folderToLinkMock, otherFolderIdMock, otherFolderMock, owningFolderIdMock, owningFolderMock } from './node.mock';
 import { Rule } from '../model/rule.model';
 import { inheritedRulesMock, linkedRulesMock, ownedRulesMock, ruleMock } from './rules.mock';
 
@@ -139,3 +139,23 @@ export const inheritedRuleSetWithOnlyDisabledRulesMock: RuleSet = {
 };
 
 export const ruleSetsMock: RuleSet[] = [inheritedRuleSetMock, ownedRuleSetMock, ruleSetWithLinkMock];
+
+export const ruleSetWithNoRulesToLinkMock: RuleSet = {
+  id: 'rule-set-to-link-with-no-rules',
+  isLinkedTo: false,
+  owningFolder: folderToLinkMock,
+  linkedToBy: [],
+  rules: [],
+  hasMoreRules: false,
+  loadingRules: false
+};
+
+export const ruleSetWithOwnedRulesToLinkMock: RuleSet = {
+  id: 'rule-set-to-link-with-no-rules',
+  isLinkedTo: false,
+  owningFolder: folderToLinkMock,
+  linkedToBy: [],
+  rules: ownedRulesMock,
+  hasMoreRules: false,
+  loadingRules: false
+};
diff --git a/projects/aca-folder-rules/src/lib/rule-details/edit-rule-dialog.smart-component.ts b/projects/aca-folder-rules/src/lib/rule-details/edit-rule-dialog.smart-component.ts
index eda6a2adc..b3a89061f 100644
--- a/projects/aca-folder-rules/src/lib/rule-details/edit-rule-dialog.smart-component.ts
+++ b/projects/aca-folder-rules/src/lib/rule-details/edit-rule-dialog.smart-component.ts
@@ -39,7 +39,7 @@ export interface EditRuleDialogOptions {
   styleUrls: ['./edit-rule-dialog.smart-component.scss'],
   encapsulation: ViewEncapsulation.None,
   host: { class: 'aca-edit-rule-dialog' },
-  providers: [{ provide: ActionsService, useClass: ActionsService }]
+  providers: [ActionsService]
 })
 export class EditRuleDialogSmartComponent implements OnInit, OnDestroy {
   formValid = false;
diff --git a/projects/aca-folder-rules/src/lib/rule-set-picker/rule-set-picker.smart-component.spec.ts b/projects/aca-folder-rules/src/lib/rule-set-picker/rule-set-picker.smart-component.spec.ts
new file mode 100644
index 000000000..f76dfd1e2
--- /dev/null
+++ b/projects/aca-folder-rules/src/lib/rule-set-picker/rule-set-picker.smart-component.spec.ts
@@ -0,0 +1,116 @@
+/*!
+ * @license
+ * Alfresco Example Content Application
+ *
+ * Copyright (C) 2005 - 2020 Alfresco Software Limited
+ *
+ * This file is part of the Alfresco Example Content Application.
+ * If the software was purchased under a paid Alfresco license, the terms of
+ * the paid license agreement will prevail.  Otherwise, the software is
+ * provided under the following open source license terms:
+ *
+ * The Alfresco Example Content Application is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * The Alfresco Example Content Application is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { RuleSetPickerOptions, RuleSetPickerSmartComponent } from './rule-set-picker.smart-component';
+import { CoreTestingModule } from '@alfresco/adf-core';
+import { folderToLinkMock } from '../mock/node.mock';
+import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
+import { FolderRuleSetsService } from '../services/folder-rule-sets.service';
+import { of } from 'rxjs';
+import { ruleSetWithLinkMock, ruleSetWithNoRulesToLinkMock, ruleSetWithOwnedRulesToLinkMock } from '../mock/rule-sets.mock';
+import { By } from '@angular/platform-browser';
+
+describe('RuleSetPickerSmartComponent', () => {
+  let fixture: ComponentFixture<RuleSetPickerSmartComponent>;
+  let component: RuleSetPickerSmartComponent;
+  let folderRuleSetsService: FolderRuleSetsService;
+
+  let loadRuleSetsSpy: jasmine.Spy;
+
+  const dialogRef = {
+    close: jasmine.createSpy('close'),
+    open: jasmine.createSpy('open')
+  };
+
+  const dialogOptions: RuleSetPickerOptions = {
+    nodeId: 'folder-1-id',
+    defaultNodeId: 'folder-1-id'
+  };
+
+  beforeEach(() => {
+    TestBed.configureTestingModule({
+      imports: [CoreTestingModule],
+      providers: [
+        { provide: MatDialogRef, useValue: dialogRef },
+        { provide: MAT_DIALOG_DATA, useValue: dialogOptions }
+      ]
+    });
+
+    folderRuleSetsService = TestBed.inject(FolderRuleSetsService);
+    fixture = TestBed.createComponent(RuleSetPickerSmartComponent);
+    component = fixture.componentInstance;
+    component['folderRuleSetsService'] = folderRuleSetsService;
+
+    loadRuleSetsSpy = spyOn(folderRuleSetsService, 'loadRuleSets');
+  });
+
+  it('should load the rule sets of a node once it has been selected', () => {
+    expect(loadRuleSetsSpy).not.toHaveBeenCalled();
+    component.onNodeSelect([folderToLinkMock]);
+    expect(loadRuleSetsSpy).toHaveBeenCalledWith(folderToLinkMock.id, false);
+    component.onNodeSelect([folderToLinkMock]);
+    expect(loadRuleSetsSpy).toHaveBeenCalledTimes(1);
+  });
+
+  it('should show an empty list message if a selected folder has no rules', () => {
+    component.mainRuleSet$ = of(ruleSetWithNoRulesToLinkMock);
+    component.rulesLoading$ = of(false);
+    component.onNodeSelect([folderToLinkMock]);
+    fixture.detectChanges();
+
+    const items = fixture.debugElement.queryAll(By.css('.aca-rule-set-picker__content__rule-list aca-rule-list-item'));
+    expect(items.length).toBe(0);
+
+    const emptyList = fixture.debugElement.query(By.css('adf-empty-content'));
+    expect(emptyList).not.toBeNull();
+  });
+
+  it('should show an empty list message if a selected folder has linked rules', () => {
+    component.mainRuleSet$ = of(ruleSetWithLinkMock);
+    component.rulesLoading$ = of(false);
+    component.onNodeSelect([folderToLinkMock]);
+    fixture.detectChanges();
+
+    const items = fixture.debugElement.queryAll(By.css('.aca-rule-set-picker__content__rule-list aca-rule-list-item'));
+    expect(items.length).toBe(0);
+
+    const emptyList = fixture.debugElement.query(By.css('adf-empty-content'));
+    expect(emptyList).not.toBeNull();
+  });
+
+  it('should show a list of items if a selected folder has owned rules', () => {
+    component.mainRuleSet$ = of(ruleSetWithOwnedRulesToLinkMock);
+    component.rulesLoading$ = of(false);
+    component.onNodeSelect([folderToLinkMock]);
+    fixture.detectChanges();
+
+    const items = fixture.debugElement.queryAll(By.css('.aca-rule-set-picker__content__rule-list aca-rule-list-item'));
+    expect(items.length).toBe(2);
+
+    const emptyList = fixture.debugElement.query(By.css('adf-empty-content'));
+    expect(emptyList).toBeNull();
+  });
+});
diff --git a/projects/aca-folder-rules/src/lib/rule-set-picker/rule-set-picker.smart-component.ts b/projects/aca-folder-rules/src/lib/rule-set-picker/rule-set-picker.smart-component.ts
index bdc3492be..b9d2ed5c2 100644
--- a/projects/aca-folder-rules/src/lib/rule-set-picker/rule-set-picker.smart-component.ts
+++ b/projects/aca-folder-rules/src/lib/rule-set-picker/rule-set-picker.smart-component.ts
@@ -44,12 +44,7 @@ export interface RuleSetPickerOptions {
   styleUrls: ['./rule-set-picker.smart-component.scss'],
   encapsulation: ViewEncapsulation.None,
   host: { class: 'aca-rule-set-picker' },
-  providers: [
-    {
-      provide: FolderRuleSetsService,
-      useClass: FolderRuleSetsService
-    }
-  ]
+  providers: [FolderRuleSetsService]
 })
 export class RuleSetPickerSmartComponent {
   nodeId = '-root-';
diff --git a/projects/aca-folder-rules/src/lib/services/folder-rule-sets.service.spec.ts b/projects/aca-folder-rules/src/lib/services/folder-rule-sets.service.spec.ts
index b4f95550e..9950e050b 100644
--- a/projects/aca-folder-rules/src/lib/services/folder-rule-sets.service.spec.ts
+++ b/projects/aca-folder-rules/src/lib/services/folder-rule-sets.service.spec.ts
@@ -138,4 +138,16 @@ describe('FolderRuleSetsService', () => {
 
     expect(selectRuleSpy).toHaveBeenCalledWith(ruleMock('inherited-rule-1'));
   });
+
+  it('should send a POST request to create a new link between two folders', () => {
+    folderRuleSetsService.createRuleSetLink('folder-1-id', 'folder-2-id');
+    expect(callApiSpy).toHaveBeenCalledWith('/nodes/folder-1-id/rule-set-links', 'POST', {
+      id: 'folder-2-id'
+    });
+  });
+
+  it('should send a DELETE request to delete a link between two folders', () => {
+    folderRuleSetsService.deleteRuleSetLink('folder-1-id', 'rule-set-1-id');
+    expect(callApiSpy).toHaveBeenCalledWith('/nodes/folder-1-id/rule-set-links/rule-set-1-id', 'DELETE');
+  });
 });