mirror of
https://github.com/Alfresco/alfresco-content-app.git
synced 2025-09-17 14:21:14 +00:00
[ACS-10035] Ensure ADW handles no PUT for preferences API method in ACS below 25.x v. [PoC] (#4750)
* [ACS-10035]: adds evaluator and helper functions * [ACS-10035]: disables navbar savedSearch for non-supported versions * [ACS-10035]: extends app extension service to allow manual rule evaluation; clean up * [ACS-10035]: introduces pipe as an alternative option for compatibility check * [ACS-10035]: disables save search feature if is not supported * [ACS-10035]: adds test for new method * [ACS-10035]: sonarQube issue * [10035]: adds unit tests for evaluators and helper fns * [ACS-10035]: fixes failed test * [ACS-10035]: fixes naming * [ACS-10035]: fixes race condition issue on direct page refresh * [ACS-10035]: fixes import * [ACS-10035]: sonarQube issues * [ACS-10035]: fixes sonarQube with fake versions * [ACS-10035]: fixes tests * [ACS-10035]: improves pipe logic stream * [ACS-10035]: fixes sonarQube; adjusts tests * [ACS-10035]: adds documentation * [ACS-10035]: exposes isFeatureSupportedInCurrentAcs from aca-content lib * [ACS-10035]: minor fixes * [ACS-10035]: typo fix
This commit is contained in:
@@ -76,4 +76,10 @@ or not.
|
|||||||
| 1.7.0 | app.navigation.isPreview | Current page is **Preview**. |
|
| 1.7.0 | app.navigation.isPreview | Current page is **Preview**. |
|
||||||
| 5.1.1 | app.navigation.isDetails | User is currently on the **Folder Details** page. |
|
| 5.1.1 | app.navigation.isDetails | User is currently on the **Folder Details** page. |
|
||||||
|
|
||||||
|
#### ACS Versions compatibility Rules/Evaluators
|
||||||
|
|
||||||
|
Rules/Evaluators created for specific features in ADW to be checked if supported in current ACS version. Evaluators are created using **createVersionRule** helper function locking specific version number into the rule.
|
||||||
|
|
||||||
|
| Version | Key | Description |
|
||||||
|
|---------|---------------------------------|---------------------------------------------------------------------------|
|
||||||
|
| 8.1.0 | isSavedSearchAvailable | Checks whether current ACS version supports PUT method in Preferences API |
|
@@ -212,7 +212,10 @@
|
|||||||
"items": [
|
"items": [
|
||||||
{
|
{
|
||||||
"id": "app.search.navbar",
|
"id": "app.search.navbar",
|
||||||
"component": "app.search.navbar"
|
"component": "app.search.navbar",
|
||||||
|
"rules": {
|
||||||
|
"visible": "isSavedSearchAvailable"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@@ -134,6 +134,7 @@ import { SaveSearchSidenavComponent } from './components/search/search-save/side
|
|||||||
isSmartFolder: rules.isSmartFolder,
|
isSmartFolder: rules.isSmartFolder,
|
||||||
isMultiSelection: rules.isMultiselection,
|
isMultiSelection: rules.isMultiselection,
|
||||||
canPrintFile: rules.canPrintFile,
|
canPrintFile: rules.canPrintFile,
|
||||||
|
isSavedSearchAvailable: rules.isSavedSearchAvailable,
|
||||||
|
|
||||||
'app.selection.canDelete': rules.canDeleteSelection,
|
'app.selection.canDelete': rules.canDeleteSelection,
|
||||||
'app.selection.canDownload': rules.canDownloadSelection,
|
'app.selection.canDownload': rules.canDownloadSelection,
|
||||||
|
@@ -26,6 +26,7 @@
|
|||||||
<div class="aca-content__advanced-filters--header">
|
<div class="aca-content__advanced-filters--header">
|
||||||
<p>{{ 'APP.BROWSE.SEARCH.ADVANCED_FILTERS' | translate }}</p>
|
<p>{{ 'APP.BROWSE.SEARCH.ADVANCED_FILTERS' | translate }}</p>
|
||||||
<div class="aca-content__advanced-filters--header--action-buttons">
|
<div class="aca-content__advanced-filters--header--action-buttons">
|
||||||
|
@if('isSavedSearchAvailable' | isFeatureSupportedInCurrentAcs | async) {
|
||||||
<button
|
<button
|
||||||
*ngIf="initialSavedSearch !== undefined else saveSearchButton"
|
*ngIf="initialSavedSearch !== undefined else saveSearchButton"
|
||||||
mat-button
|
mat-button
|
||||||
@@ -66,6 +67,7 @@
|
|||||||
{{ 'APP.BROWSE.SEARCH.SAVE_SEARCH.ACTION_BUTTON' | translate }}
|
{{ 'APP.BROWSE.SEARCH.SAVE_SEARCH.ACTION_BUTTON' | translate }}
|
||||||
</button>
|
</button>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
}
|
||||||
<button
|
<button
|
||||||
mat-button
|
mat-button
|
||||||
adf-reset-search
|
adf-reset-search
|
||||||
|
@@ -24,13 +24,14 @@
|
|||||||
|
|
||||||
import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing';
|
import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing';
|
||||||
import { SearchResultsComponent } from './search-results.component';
|
import { SearchResultsComponent } from './search-results.component';
|
||||||
|
import { Pipe, PipeTransform } from '@angular/core';
|
||||||
import { AppConfigService, NotificationService, TranslationService } from '@alfresco/adf-core';
|
import { AppConfigService, NotificationService, TranslationService } from '@alfresco/adf-core';
|
||||||
import { Store } from '@ngrx/store';
|
import { Store } from '@ngrx/store';
|
||||||
import { NavigateToFolder } from '@alfresco/aca-shared/store';
|
import { NavigateToFolder } from '@alfresco/aca-shared/store';
|
||||||
import { Pagination, SearchRequest } from '@alfresco/js-api';
|
import { Pagination, SearchRequest } from '@alfresco/js-api';
|
||||||
import { SavedSearchesService, SearchQueryBuilderService } from '@alfresco/adf-content-services';
|
import { SavedSearchesService, SearchQueryBuilderService } from '@alfresco/adf-content-services';
|
||||||
import { ActivatedRoute, Event, NavigationStart, Params, Router } from '@angular/router';
|
import { ActivatedRoute, Event, NavigationStart, Params, Router } from '@angular/router';
|
||||||
import { BehaviorSubject, of, Subject, throwError } from 'rxjs';
|
import { BehaviorSubject, Observable, of, Subject, throwError } from 'rxjs';
|
||||||
import { AppTestingModule } from '../../../testing/app-testing.module';
|
import { AppTestingModule } from '../../../testing/app-testing.module';
|
||||||
import { AppService } from '@alfresco/aca-shared';
|
import { AppService } from '@alfresco/aca-shared';
|
||||||
import { MatSnackBarModule, MatSnackBarRef } from '@angular/material/snack-bar';
|
import { MatSnackBarModule, MatSnackBarRef } from '@angular/material/snack-bar';
|
||||||
@@ -42,6 +43,13 @@ import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
|||||||
import { MatMenuModule } from '@angular/material/menu';
|
import { MatMenuModule } from '@angular/material/menu';
|
||||||
import { MatMenuHarness } from '@angular/material/menu/testing';
|
import { MatMenuHarness } from '@angular/material/menu/testing';
|
||||||
|
|
||||||
|
@Pipe({ name: 'isFeatureSupportedInCurrentAcs' })
|
||||||
|
class MockIsFeatureSupportedInCurrentAcsPipe implements PipeTransform {
|
||||||
|
transform(): Observable<boolean> {
|
||||||
|
return of(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
describe('SearchComponent', () => {
|
describe('SearchComponent', () => {
|
||||||
let component: SearchResultsComponent;
|
let component: SearchResultsComponent;
|
||||||
let fixture: ComponentFixture<SearchResultsComponent>;
|
let fixture: ComponentFixture<SearchResultsComponent>;
|
||||||
@@ -112,6 +120,12 @@ describe('SearchComponent', () => {
|
|||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|
||||||
|
TestBed.overrideComponent(SearchResultsComponent, {
|
||||||
|
add: {
|
||||||
|
imports: [MockIsFeatureSupportedInCurrentAcsPipe]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
config = TestBed.inject(AppConfigService);
|
config = TestBed.inject(AppConfigService);
|
||||||
store = TestBed.inject(Store);
|
store = TestBed.inject(Store);
|
||||||
queryBuilder = TestBed.inject(SearchQueryBuilderService);
|
queryBuilder = TestBed.inject(SearchQueryBuilderService);
|
||||||
|
@@ -88,6 +88,7 @@ import { SaveSearchDirective } from '../search-save/directive/save-search.direct
|
|||||||
import { combineLatest, of } from 'rxjs';
|
import { combineLatest, of } from 'rxjs';
|
||||||
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
||||||
import { MatMenuModule } from '@angular/material/menu';
|
import { MatMenuModule } from '@angular/material/menu';
|
||||||
|
import { IsFeatureSupportedInCurrentAcsPipe } from '../../../pipes/is-feature-supported.pipe';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
imports: [
|
imports: [
|
||||||
@@ -121,7 +122,8 @@ import { MatMenuModule } from '@angular/material/menu';
|
|||||||
ViewerToolbarComponent,
|
ViewerToolbarComponent,
|
||||||
BulkActionsDropdownComponent,
|
BulkActionsDropdownComponent,
|
||||||
SearchAiInputContainerComponent,
|
SearchAiInputContainerComponent,
|
||||||
SaveSearchDirective
|
SaveSearchDirective,
|
||||||
|
IsFeatureSupportedInCurrentAcsPipe
|
||||||
],
|
],
|
||||||
selector: 'aca-search-results',
|
selector: 'aca-search-results',
|
||||||
templateUrl: './search-results.component.html',
|
templateUrl: './search-results.component.html',
|
||||||
|
@@ -0,0 +1,55 @@
|
|||||||
|
/*!
|
||||||
|
* Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||||
|
*
|
||||||
|
* Alfresco Example Content Application
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { of } from 'rxjs';
|
||||||
|
import { IsFeatureSupportedInCurrentAcsPipe } from './is-feature-supported.pipe';
|
||||||
|
import { TestBed } from '@angular/core/testing';
|
||||||
|
import { AppExtensionService } from '@alfresco/aca-shared';
|
||||||
|
import { AppStore } from '@alfresco/aca-shared/store';
|
||||||
|
import { Store } from '@ngrx/store';
|
||||||
|
|
||||||
|
describe('IsFeatureSupportedInCurrentAcsPipe', () => {
|
||||||
|
let serviceSpy: jasmine.SpyObj<AppExtensionService>;
|
||||||
|
let storeSpy: jasmine.SpyObj<Store<AppStore>>;
|
||||||
|
let pipe: IsFeatureSupportedInCurrentAcsPipe;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
serviceSpy = jasmine.createSpyObj('AppExtensionService', ['isFeatureSupported']);
|
||||||
|
storeSpy = jasmine.createSpyObj('Store', ['dispatch', 'select']);
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
providers: [IsFeatureSupportedInCurrentAcsPipe, { provide: AppExtensionService, useValue: serviceSpy }, { provide: Store, useValue: storeSpy }]
|
||||||
|
});
|
||||||
|
pipe = TestBed.inject(IsFeatureSupportedInCurrentAcsPipe);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call isFeatureSupported in AppExtensionService', (done) => {
|
||||||
|
serviceSpy.isFeatureSupported.and.returnValue(false);
|
||||||
|
storeSpy.select.and.returnValue(of('7.4.0'));
|
||||||
|
pipe.transform('someFeature').subscribe((result) => {
|
||||||
|
expect(result).toBe(false);
|
||||||
|
expect(serviceSpy.isFeatureSupported).toHaveBeenCalledWith('someFeature');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@@ -0,0 +1,43 @@
|
|||||||
|
/*!
|
||||||
|
* Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||||
|
*
|
||||||
|
* Alfresco Example Content Application
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { AppExtensionService } from '@alfresco/aca-shared';
|
||||||
|
import { Pipe, PipeTransform } from '@angular/core';
|
||||||
|
import { AppStore, getRepositoryStatus } from '@alfresco/aca-shared/store';
|
||||||
|
import { Store } from '@ngrx/store';
|
||||||
|
import { map, Observable } from 'rxjs';
|
||||||
|
|
||||||
|
@Pipe({
|
||||||
|
name: 'isFeatureSupportedInCurrentAcs'
|
||||||
|
})
|
||||||
|
export class IsFeatureSupportedInCurrentAcsPipe implements PipeTransform {
|
||||||
|
constructor(
|
||||||
|
private readonly appExtensionsService: AppExtensionService,
|
||||||
|
private readonly store: Store<AppStore>
|
||||||
|
) {}
|
||||||
|
|
||||||
|
transform(evaluatorId: string): Observable<boolean> {
|
||||||
|
return this.store.select(getRepositoryStatus).pipe(map(() => this.appExtensionsService.isFeatureSupported(evaluatorId)));
|
||||||
|
}
|
||||||
|
}
|
@@ -33,3 +33,4 @@ export * from './lib/services/content-url.service';
|
|||||||
export * from './lib/services/content-management.service';
|
export * from './lib/services/content-management.service';
|
||||||
export * from './lib/components/info-drawer/comments-tab/external-node-permission-comments-tab.service';
|
export * from './lib/components/info-drawer/comments-tab/external-node-permission-comments-tab.service';
|
||||||
export * from './lib/utils/aca-search-utils';
|
export * from './lib/utils/aca-search-utils';
|
||||||
|
export * from './lib/pipes/is-feature-supported.pipe';
|
||||||
|
@@ -23,10 +23,10 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import * as app from './app.rules';
|
import * as app from './app.rules';
|
||||||
import { getFileExtension } from './app.rules';
|
import { createVersionRule, getFileExtension, isSavedSearchAvailable } from './app.rules';
|
||||||
import { TestRuleContext } from './test-rule-context';
|
import { TestRuleContext } from './test-rule-context';
|
||||||
import { NodeEntry, RepositoryInfo, StatusInfo } from '@alfresco/js-api';
|
import { NodeEntry, RepositoryInfo, StatusInfo } from '@alfresco/js-api';
|
||||||
import { ProfileState } from '@alfresco/adf-extensions';
|
import { ProfileState, RuleContext } from '@alfresco/adf-extensions';
|
||||||
import { AppConfigService } from '@alfresco/adf-core';
|
import { AppConfigService } from '@alfresco/adf-core';
|
||||||
|
|
||||||
describe('app.evaluators', () => {
|
describe('app.evaluators', () => {
|
||||||
@@ -1198,6 +1198,71 @@ describe('app.evaluators', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('Versions compatibility', () => {
|
||||||
|
function makeContext(versionDisplay?: string): RuleContext {
|
||||||
|
return {
|
||||||
|
repository: {
|
||||||
|
version: versionDisplay ? { display: versionDisplay } : undefined
|
||||||
|
}
|
||||||
|
} as RuleContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('isSavedSearchAvailable', () => {
|
||||||
|
it('should return true if ACS version is equal to minimal version', () => {
|
||||||
|
expect(isSavedSearchAvailable(makeContext('25.1.0'))).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return true if ACS version is greater than minimal version', () => {
|
||||||
|
expect(isSavedSearchAvailable(makeContext('25.2.0'))).toBe(true);
|
||||||
|
expect(isSavedSearchAvailable(makeContext('26.0.0'))).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return false if ACS version is less than minimal version', () => {
|
||||||
|
expect(isSavedSearchAvailable(makeContext('24.4.0'))).toBe(false);
|
||||||
|
expect(isSavedSearchAvailable(makeContext('25.0.9'))).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return false if ACS version is missing', () => {
|
||||||
|
expect(isSavedSearchAvailable(makeContext())).toBe(false);
|
||||||
|
expect(isSavedSearchAvailable({ repository: {} } as any)).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('createVersionRule', () => {
|
||||||
|
it('should return true if version is equal to minimal version', () => {
|
||||||
|
const rule = createVersionRule('25.1.0');
|
||||||
|
expect(rule(makeContext('25.1.0'))).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return true if version is greater than minimal version', () => {
|
||||||
|
const rule = createVersionRule('25.1.0');
|
||||||
|
expect(rule(makeContext('25.2.0'))).toBe(true);
|
||||||
|
expect(rule(makeContext('26.0.0'))).toBe(true);
|
||||||
|
expect(rule(makeContext('25.1.1'))).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return false if version is less than minimal version', () => {
|
||||||
|
const rule = createVersionRule('25.1.0');
|
||||||
|
expect(rule(makeContext('25.0.9'))).toBe(false);
|
||||||
|
expect(rule(makeContext('24.9.0'))).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return false if version is missing', () => {
|
||||||
|
const rule = createVersionRule('25.1.0');
|
||||||
|
expect(rule(makeContext())).toBe(false);
|
||||||
|
expect(rule({ repository: {} } as any)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle versions with different number of segments', () => {
|
||||||
|
const rule = createVersionRule('25.1.0');
|
||||||
|
expect(rule(makeContext('25.1'))).toBe(true);
|
||||||
|
expect(rule(makeContext('25.1.1'))).toBe(true);
|
||||||
|
expect(rule(makeContext('25.1.0.1-beta'))).toBe(true);
|
||||||
|
expect(rule(makeContext('25.0.1.1-rc'))).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
function createTestContext(): TestRuleContext {
|
function createTestContext(): TestRuleContext {
|
||||||
const context = new TestRuleContext();
|
const context = new TestRuleContext();
|
||||||
context.repository = {
|
context.repository = {
|
||||||
|
@@ -483,6 +483,48 @@ export function canOpenWithOffice(context: AcaRuleContext): boolean {
|
|||||||
return context.permissions.check(file, ['update']);
|
return context.permissions.check(file, ['update']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if user savedSearches are supported by current ACS version.
|
||||||
|
* JSON ref: `isSavedSearchAvailable`
|
||||||
|
*/
|
||||||
|
export const isSavedSearchAvailable = createVersionRule('25.1.0');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Partially applies minimal version of a feature against a core compatibility evaluation.
|
||||||
|
* @param minimalVersion The minimal version to check against.
|
||||||
|
*/
|
||||||
|
export function createVersionRule(minimalVersion: string): (context: RuleContext) => boolean {
|
||||||
|
return (context: RuleContext): boolean => {
|
||||||
|
const acsVersion = context.repository.version?.display?.split(' ')[0];
|
||||||
|
return isVersionCompatible(acsVersion, minimalVersion);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function isVersionCompatible(currentVersion: string, minimalVersion: string): boolean {
|
||||||
|
if (!currentVersion || !minimalVersion) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentParts = currentVersion.split('.').map(Number);
|
||||||
|
const minimalParts = minimalVersion.split('.').map(Number);
|
||||||
|
const maxLength = Math.max(currentParts.length, minimalParts.length);
|
||||||
|
|
||||||
|
for (let i = 0; i < maxLength; i++) {
|
||||||
|
const currentSegment = currentParts[i] ?? 0;
|
||||||
|
const minimalSegment = minimalParts[i] ?? 0;
|
||||||
|
|
||||||
|
if (currentSegment > minimalSegment) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentSegment < minimalSegment) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
export function isSmartFolder(context: RuleContext): boolean {
|
export function isSmartFolder(context: RuleContext): boolean {
|
||||||
if (!context.selection?.isEmpty) {
|
if (!context.selection?.isEmpty) {
|
||||||
const node = context.selection.first;
|
const node = context.selection.first;
|
||||||
|
@@ -1697,4 +1697,10 @@ describe('AppExtensionService', () => {
|
|||||||
|
|
||||||
service.bulkActionExecuted();
|
service.bulkActionExecuted();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should call evaluateRule on isFeatureSupported', () => {
|
||||||
|
const evaluateRuleSpy = spyOn(extensions, 'evaluateRule').and.returnValue(true);
|
||||||
|
service.isFeatureSupported('someFeature');
|
||||||
|
expect(evaluateRuleSpy).toHaveBeenCalledWith('someFeature', service);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@@ -592,4 +592,8 @@ export class AppExtensionService implements RuleContext {
|
|||||||
bulkActionExecuted(): void {
|
bulkActionExecuted(): void {
|
||||||
this.bulkActionExecuted$.next();
|
this.bulkActionExecuted$.next();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isFeatureSupported(feature: string): boolean {
|
||||||
|
return this.extensions.evaluateRule(feature, this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user