mirror of
https://github.com/Alfresco/alfresco-content-app.git
synced 2025-05-12 17:04:46 +00:00
* code cleanup * rollback changes * remove dead code * remove debug app config * cleanup unused locales * no-unused-vars rule * test fixes and cleanup * remove unnecessary translate modules
793 lines
25 KiB
TypeScript
793 lines
25 KiB
TypeScript
/*!
|
|
* Copyright © 2005-2023 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 { Router, ActivatedRoute } from '@angular/router';
|
|
import { TestBed, ComponentFixture, fakeAsync, tick } from '@angular/core/testing';
|
|
import {
|
|
UserPreferencesService,
|
|
AlfrescoApiService,
|
|
AlfrescoApiServiceMock,
|
|
AuthenticationService,
|
|
TranslationMock,
|
|
TranslationService,
|
|
PipeModule
|
|
} from '@alfresco/adf-core';
|
|
import { UploadService, NodesApiService, DiscoveryApiService } from '@alfresco/adf-content-services';
|
|
import { AppState, ClosePreviewAction } from '@alfresco/aca-shared/store';
|
|
import { PreviewComponent } from './preview.component';
|
|
import { BehaviorSubject, Observable, of, throwError } from 'rxjs';
|
|
import { ContentApiService, AppHookService, DocumentBasePageService } from '@alfresco/aca-shared';
|
|
import { Store, StoreModule } from '@ngrx/store';
|
|
import { Node, NodePaging, FavoritePaging, SharedLinkPaging, PersonEntry, ResultSetPaging, RepositoryInfo } from '@alfresco/js-api';
|
|
import { PreviewModule } from '../preview.module';
|
|
import { TranslateModule } from '@ngx-translate/core';
|
|
import { RouterTestingModule } from '@angular/router/testing';
|
|
import { HttpClientModule } from '@angular/common/http';
|
|
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
|
import { EffectsModule } from '@ngrx/effects';
|
|
|
|
class DocumentBasePageServiceMock extends DocumentBasePageService {
|
|
canUpdateNode(): boolean {
|
|
return true;
|
|
}
|
|
canUploadContent(): boolean {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
export const INITIAL_APP_STATE: AppState = {
|
|
appName: 'Alfresco Content Application',
|
|
logoPath: 'assets/images/alfresco-logo-white.svg',
|
|
customCssPath: '',
|
|
webFontPath: '',
|
|
sharedUrl: '',
|
|
user: {
|
|
isAdmin: null,
|
|
id: null,
|
|
firstName: '',
|
|
lastName: ''
|
|
},
|
|
selection: {
|
|
nodes: [],
|
|
libraries: [],
|
|
isEmpty: true,
|
|
count: 0
|
|
},
|
|
navigation: {
|
|
currentFolder: null
|
|
},
|
|
currentNodeVersion: null,
|
|
infoDrawerOpened: false,
|
|
infoDrawerPreview: false,
|
|
infoDrawerMetadataAspect: '',
|
|
showFacetFilter: true,
|
|
fileUploadingDialog: true,
|
|
documentDisplayMode: 'list',
|
|
showLoader: false,
|
|
repository: {
|
|
status: {
|
|
isQuickShareEnabled: true
|
|
}
|
|
} as any
|
|
};
|
|
|
|
describe('PreviewComponent', () => {
|
|
let fixture: ComponentFixture<PreviewComponent>;
|
|
let component: PreviewComponent;
|
|
let router: Router;
|
|
let route: ActivatedRoute;
|
|
let preferences: UserPreferencesService;
|
|
let contentApi: ContentApiService;
|
|
let uploadService: UploadService;
|
|
let nodesApiService: NodesApiService;
|
|
let appHookService: AppHookService;
|
|
let store: Store<any>;
|
|
|
|
beforeEach(() => {
|
|
TestBed.configureTestingModule({
|
|
imports: [
|
|
PreviewModule,
|
|
NoopAnimationsModule,
|
|
HttpClientModule,
|
|
RouterTestingModule,
|
|
TranslateModule.forRoot(),
|
|
StoreModule.forRoot(
|
|
{ app: (state) => state },
|
|
{
|
|
initialState: {
|
|
app: INITIAL_APP_STATE
|
|
},
|
|
runtimeChecks: {
|
|
strictStateImmutability: false,
|
|
strictActionImmutability: false
|
|
}
|
|
}
|
|
),
|
|
EffectsModule.forRoot([]),
|
|
PipeModule
|
|
],
|
|
providers: [
|
|
{ provide: AlfrescoApiService, useClass: AlfrescoApiServiceMock },
|
|
{ provide: TranslationService, useClass: TranslationMock },
|
|
{ provide: DocumentBasePageService, useVale: new DocumentBasePageServiceMock() },
|
|
{
|
|
provide: DiscoveryApiService,
|
|
useValue: {
|
|
ecmProductInfo$: new BehaviorSubject<RepositoryInfo | null>(null),
|
|
getEcmProductInfo: (): Observable<RepositoryInfo> => of(new RepositoryInfo({ version: '10.0.0' }))
|
|
}
|
|
},
|
|
{
|
|
provide: AuthenticationService,
|
|
useValue: {
|
|
isEcmLoggedIn: (): boolean => true,
|
|
getRedirect: (): string | null => null,
|
|
setRedirect() {},
|
|
isOauth: (): boolean => false,
|
|
isOAuthWithoutSilentLogin: (): boolean => false
|
|
}
|
|
}
|
|
]
|
|
});
|
|
|
|
fixture = TestBed.createComponent(PreviewComponent);
|
|
component = fixture.componentInstance;
|
|
|
|
router = TestBed.inject(Router);
|
|
route = TestBed.inject(ActivatedRoute);
|
|
preferences = TestBed.inject(UserPreferencesService);
|
|
contentApi = TestBed.inject(ContentApiService);
|
|
uploadService = TestBed.inject(UploadService);
|
|
nodesApiService = TestBed.inject(NodesApiService);
|
|
appHookService = TestBed.inject(AppHookService);
|
|
store = TestBed.inject(Store);
|
|
});
|
|
|
|
it('should extract the property path root', () => {
|
|
expect(component.getRootField('some.property.path')).toBe('some');
|
|
expect(component.getRootField('some')).toBe('some');
|
|
expect(component.getRootField('')).toBe('');
|
|
expect(component.getRootField(null)).toBe(null);
|
|
});
|
|
|
|
it('should navigate to previous node in sub-folder', () => {
|
|
spyOn(router, 'navigate').and.stub();
|
|
const clickEvent = new MouseEvent('click');
|
|
|
|
component.previewLocation = 'personal-files';
|
|
component.folderId = 'folder1';
|
|
component.previousNodeId = 'previous1';
|
|
component.onNavigateBefore(clickEvent);
|
|
|
|
expect(router.navigate).toHaveBeenCalledWith(['personal-files', 'folder1', 'preview', 'previous1']);
|
|
});
|
|
|
|
it('should navigate back to previous node in the root path', () => {
|
|
spyOn(router, 'navigate').and.stub();
|
|
const clickEvent = new MouseEvent('click');
|
|
|
|
component.previewLocation = 'personal-files';
|
|
component.folderId = null;
|
|
component.previousNodeId = 'previous1';
|
|
component.onNavigateBefore(clickEvent);
|
|
|
|
expect(router.navigate).toHaveBeenCalledWith(['personal-files', 'preview', 'previous1']);
|
|
});
|
|
|
|
it('should not navigate back if node unset', () => {
|
|
spyOn(router, 'navigate').and.stub();
|
|
const clickEvent = new MouseEvent('click');
|
|
|
|
component.previousNodeId = null;
|
|
component.onNavigateBefore(clickEvent);
|
|
|
|
expect(router.navigate).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('should navigate to next node in sub-folder', () => {
|
|
spyOn(router, 'navigate').and.stub();
|
|
const clickEvent = new MouseEvent('click');
|
|
|
|
component.previewLocation = 'personal-files';
|
|
component.folderId = 'folder1';
|
|
component.nextNodeId = 'next1';
|
|
component.onNavigateNext(clickEvent);
|
|
|
|
expect(router.navigate).toHaveBeenCalledWith(['personal-files', 'folder1', 'preview', 'next1']);
|
|
});
|
|
|
|
it('should navigate to next node in the root path', () => {
|
|
spyOn(router, 'navigate').and.stub();
|
|
const clickEvent = new MouseEvent('click');
|
|
|
|
component.previewLocation = 'personal-files';
|
|
component.folderId = null;
|
|
component.nextNodeId = 'next1';
|
|
component.onNavigateNext(clickEvent);
|
|
|
|
expect(router.navigate).toHaveBeenCalledWith(['personal-files', 'preview', 'next1']);
|
|
});
|
|
|
|
it('should not navigate back if node unset', () => {
|
|
spyOn(router, 'navigate').and.stub();
|
|
const clickEvent = new MouseEvent('click');
|
|
|
|
component.nextNodeId = null;
|
|
component.onNavigateNext(clickEvent);
|
|
|
|
expect(router.navigate).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('should generate preview path for a folder only', () => {
|
|
component.previewLocation = 'personal-files';
|
|
|
|
expect(component.getPreviewPath('folder1', null)).toEqual(['personal-files', 'folder1']);
|
|
});
|
|
|
|
it('should generate preview path for a folder and a node', () => {
|
|
component.previewLocation = 'personal-files';
|
|
|
|
expect(component.getPreviewPath('folder1', 'node1')).toEqual(['personal-files', 'folder1', 'preview', 'node1']);
|
|
});
|
|
|
|
it('should generate preview path for a node only', () => {
|
|
component.previewLocation = 'personal-files';
|
|
|
|
expect(component.getPreviewPath(null, 'node1')).toEqual(['personal-files', 'preview', 'node1']);
|
|
});
|
|
|
|
it('should generate preview for the location only', () => {
|
|
component.previewLocation = 'personal-files';
|
|
|
|
expect(component.getPreviewPath(null, null)).toEqual(['personal-files']);
|
|
});
|
|
|
|
it('should navigate back to root path upon close', () => {
|
|
spyOn(router, 'navigate').and.stub();
|
|
|
|
component.routesSkipNavigation = [];
|
|
component.previewLocation = 'libraries';
|
|
component.folderId = null;
|
|
|
|
component.onVisibilityChanged(false);
|
|
|
|
expect(router.navigate).toHaveBeenCalledWith(['libraries', {}]);
|
|
});
|
|
|
|
it('should navigate back to folder path upon close', () => {
|
|
spyOn(router, 'navigate').and.stub();
|
|
|
|
component.routesSkipNavigation = [];
|
|
component.previewLocation = 'libraries';
|
|
component.folderId = 'site1';
|
|
|
|
component.onVisibilityChanged(false);
|
|
|
|
expect(router.navigate).toHaveBeenCalledWith(['libraries', {}, 'site1']);
|
|
});
|
|
|
|
it('should not navigate to root path for certain routes upon close', () => {
|
|
spyOn(router, 'navigate').and.stub();
|
|
|
|
component.routesSkipNavigation = ['shared'];
|
|
component.previewLocation = 'shared';
|
|
component.folderId = 'folder1';
|
|
|
|
component.onVisibilityChanged(false);
|
|
|
|
expect(router.navigate).toHaveBeenCalledWith(['shared', {}]);
|
|
});
|
|
|
|
it('should not navigate back if viewer is still visible', () => {
|
|
spyOn(router, 'navigate').and.stub();
|
|
|
|
component.routesSkipNavigation = [];
|
|
component.previewLocation = 'shared';
|
|
|
|
component.onVisibilityChanged(true);
|
|
|
|
expect(router.navigate).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('should enable multiple document navigation from route data', () => {
|
|
route.snapshot.data = {
|
|
navigateMultiple: true
|
|
};
|
|
|
|
component.ngOnInit();
|
|
|
|
expect(component.navigateMultiple).toBeTruthy();
|
|
});
|
|
|
|
it('should not enable multiple document navigation from route data', () => {
|
|
route.snapshot.data = {};
|
|
|
|
component.ngOnInit();
|
|
|
|
expect(component.navigateMultiple).toBeFalsy();
|
|
});
|
|
|
|
it('should fetch navigation source from route', () => {
|
|
route.snapshot.data = {
|
|
navigateSource: 'personal-files'
|
|
};
|
|
|
|
component.ngOnInit();
|
|
|
|
expect(component.navigateSource).toBe('personal-files');
|
|
});
|
|
|
|
it('should fetch case-insensitive source from route', () => {
|
|
route.snapshot.data = {
|
|
navigateSource: 'PERSONAL-FILES'
|
|
};
|
|
|
|
component.navigationSources = ['personal-files'];
|
|
component.ngOnInit();
|
|
|
|
expect(component.navigateSource).toBe('PERSONAL-FILES');
|
|
});
|
|
|
|
it('should fetch only permitted navigation source from route', () => {
|
|
route.snapshot.data = {
|
|
navigateSource: 'personal-files'
|
|
};
|
|
|
|
component.navigationSources = ['shared'];
|
|
component.ngOnInit();
|
|
|
|
expect(component.navigateSource).toBeNull();
|
|
});
|
|
|
|
it('should display document upon init', () => {
|
|
route.params = of({
|
|
folderId: 'folder1',
|
|
nodeId: 'node1'
|
|
});
|
|
|
|
spyOn(component, 'displayNode').and.stub();
|
|
|
|
component.ngOnInit();
|
|
|
|
expect(component.folderId).toBe('folder1');
|
|
expect(component.displayNode).toHaveBeenCalledWith('node1');
|
|
});
|
|
|
|
it('should return empty nearest nodes for missing node id', async () => {
|
|
const nearest = await component.getNearestNodes(null, 'folder1');
|
|
|
|
expect(nearest).toEqual({ left: null, right: null });
|
|
});
|
|
|
|
it('should return empty nearest nodes for missing folder id', async () => {
|
|
const nearest = await component.getNearestNodes('node1', null);
|
|
|
|
expect(nearest).toEqual({ left: null, right: null });
|
|
});
|
|
|
|
it('should return empty nearest nodes for crashed fields id request', async () => {
|
|
spyOn(component, 'getFileIds').and.returnValue(Promise.reject('err'));
|
|
|
|
const nearest = await component.getNearestNodes('node1', 'folder1');
|
|
|
|
expect(nearest).toEqual({ left: null, right: null });
|
|
});
|
|
|
|
it('should return nearest nodes', async () => {
|
|
spyOn(component, 'getFileIds').and.returnValue(Promise.resolve(['node1', 'node2', 'node3', 'node4', 'node5']));
|
|
|
|
let nearest = await component.getNearestNodes('node1', 'folder1');
|
|
expect(nearest).toEqual({ left: null, right: 'node2' });
|
|
|
|
nearest = await component.getNearestNodes('node3', 'folder1');
|
|
expect(nearest).toEqual({ left: 'node2', right: 'node4' });
|
|
|
|
nearest = await component.getNearestNodes('node5', 'folder1');
|
|
expect(nearest).toEqual({ left: 'node4', right: null });
|
|
});
|
|
|
|
it('should return empty nearest nodes if node is already deleted', async () => {
|
|
spyOn(component, 'getFileIds').and.returnValue(Promise.resolve(['node1', 'node2', 'node3', 'node4', 'node5']));
|
|
|
|
const nearest = await component.getNearestNodes('node9', 'folder1');
|
|
expect(nearest).toEqual({ left: null, right: null });
|
|
});
|
|
|
|
it('should not display node when id is missing', async () => {
|
|
spyOn(router, 'navigate').and.stub();
|
|
spyOn(contentApi, 'getNodeInfo').and.returnValue(of(null));
|
|
|
|
await component.displayNode(null);
|
|
|
|
expect(contentApi.getNodeInfo).not.toHaveBeenCalled();
|
|
expect(router.navigate).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('should navigate to original location if node not found', async () => {
|
|
spyOn(router, 'navigate').and.stub();
|
|
spyOn(contentApi, 'getNodeInfo').and.returnValue(throwError('error'));
|
|
|
|
component.previewLocation = 'personal-files';
|
|
await component.displayNode('folder1');
|
|
|
|
expect(contentApi.getNodeInfo).toHaveBeenCalledWith('folder1');
|
|
expect(router.navigate).toHaveBeenCalledWith(['personal-files', 'folder1']);
|
|
});
|
|
|
|
it('should navigate to original location if node is not a File', async () => {
|
|
spyOn(router, 'navigate').and.stub();
|
|
spyOn(contentApi, 'getNodeInfo').and.returnValue(
|
|
of({
|
|
isFile: false
|
|
} as Node)
|
|
);
|
|
|
|
component.previewLocation = 'personal-files';
|
|
await component.displayNode('folder1');
|
|
|
|
expect(contentApi.getNodeInfo).toHaveBeenCalledWith('folder1');
|
|
expect(router.navigate).toHaveBeenCalledWith(['personal-files', 'folder1']);
|
|
});
|
|
|
|
it('should navigate to original location in case of Alfresco API errors', async () => {
|
|
spyOn(router, 'navigate').and.stub();
|
|
spyOn(contentApi, 'getNodeInfo').and.returnValue(throwError('error'));
|
|
|
|
component.previewLocation = 'personal-files';
|
|
await component.displayNode('folder1');
|
|
|
|
expect(contentApi.getNodeInfo).toHaveBeenCalledWith('folder1');
|
|
expect(router.navigate).toHaveBeenCalledWith(['personal-files', 'folder1']);
|
|
});
|
|
|
|
it('should fetch and sort file ids for personal-files', async () => {
|
|
preferences.set('personal-files.sorting.key', 'name');
|
|
preferences.set('personal-files.sorting.direction', 'desc');
|
|
|
|
spyOn(contentApi, 'getNodeChildren').and.returnValue(
|
|
of({
|
|
list: {
|
|
entries: [{ entry: { id: 'node1', name: 'node 1' } }, { entry: { id: 'node2', name: 'node 2' } }]
|
|
}
|
|
} as NodePaging)
|
|
);
|
|
|
|
const ids = await component.getFileIds('personal-files', 'folder1');
|
|
expect(ids).toEqual(['node2', 'node1']);
|
|
});
|
|
|
|
it('should fetch file ids for personal-files with default sorting for missing key', async () => {
|
|
preferences.set('personal-files.sorting.key', 'missing');
|
|
preferences.set('personal-files.sorting.direction', 'desc');
|
|
|
|
spyOn(contentApi, 'getNodeChildren').and.returnValue(
|
|
of({
|
|
list: {
|
|
entries: [{ entry: { id: 'node1', name: 'node 1' } }, { entry: { id: 'node2', name: 'node 2' } }]
|
|
}
|
|
} as NodePaging)
|
|
);
|
|
|
|
const ids = await component.getFileIds('personal-files', 'folder1');
|
|
expect(ids).toEqual(['node1', 'node2']);
|
|
});
|
|
|
|
it('should sort file ids for personal-files with [modifiedAt desc]', async () => {
|
|
spyOn(preferences, 'get').and.returnValue(null);
|
|
|
|
spyOn(contentApi, 'getNodeChildren').and.returnValue(
|
|
of({
|
|
list: {
|
|
entries: [
|
|
{ entry: { id: 'node1', name: 'node 1', modifiedAt: new Date(1) } },
|
|
{ entry: { id: 'node2', name: 'node 2', modifiedAt: new Date(2) } }
|
|
]
|
|
}
|
|
} as NodePaging)
|
|
);
|
|
|
|
const ids = await component.getFileIds('personal-files', 'folder1');
|
|
expect(ids).toEqual(['node2', 'node1']);
|
|
});
|
|
|
|
it('should fetch and sort file ids for libraries', async () => {
|
|
preferences.set('personal-files.sorting.key', 'name');
|
|
preferences.set('personal-files.sorting.direction', 'desc');
|
|
|
|
spyOn(contentApi, 'getNodeChildren').and.returnValue(
|
|
of({
|
|
list: {
|
|
entries: [{ entry: { id: 'node1', name: 'node 1' } }, { entry: { id: 'node2', name: 'node 2' } }]
|
|
}
|
|
} as NodePaging)
|
|
);
|
|
|
|
const ids = await component.getFileIds('libraries', 'site1');
|
|
expect(ids).toEqual(['node2', 'node1']);
|
|
});
|
|
|
|
it('should require folder id to fetch ids for libraries', async () => {
|
|
const ids = await component.getFileIds('libraries', null);
|
|
expect(ids).toEqual([]);
|
|
});
|
|
|
|
it('should sort file ids for libraries with [modifiedAt desc]', async () => {
|
|
spyOn(preferences, 'get').and.returnValue(null);
|
|
|
|
spyOn(contentApi, 'getNodeChildren').and.returnValue(
|
|
of({
|
|
list: {
|
|
entries: [
|
|
{ entry: { id: 'node1', name: 'node 1', modifiedAt: new Date(1) } },
|
|
{ entry: { id: 'node2', name: 'node 2', modifiedAt: new Date(2) } }
|
|
]
|
|
}
|
|
} as NodePaging)
|
|
);
|
|
|
|
const ids = await component.getFileIds('libraries', 'folder1');
|
|
expect(ids).toEqual(['node2', 'node1']);
|
|
});
|
|
|
|
it('should fetch and sort ids for favorites', async () => {
|
|
preferences.set('favorites.sorting.key', 'name');
|
|
preferences.set('favorites.sorting.direction', 'desc');
|
|
|
|
spyOn(contentApi, 'getFavorites').and.returnValue(
|
|
of({
|
|
list: {
|
|
entries: [
|
|
{ entry: { target: { file: { id: 'file3', name: 'file 3' } } } },
|
|
{ entry: { target: { file: { id: 'file1', name: 'file 1' } } } },
|
|
{ entry: { target: { file: { id: 'file2', name: 'file 2' } } } }
|
|
]
|
|
}
|
|
} as FavoritePaging)
|
|
);
|
|
|
|
const ids = await component.getFileIds('favorites');
|
|
expect(ids).toEqual(['file3', 'file2', 'file1']);
|
|
});
|
|
|
|
it('should sort file ids for favorites with [modifiedAt desc]', async () => {
|
|
spyOn(preferences, 'get').and.returnValue(null);
|
|
|
|
spyOn(contentApi, 'getFavorites').and.returnValue(
|
|
of({
|
|
list: {
|
|
entries: [
|
|
{
|
|
entry: {
|
|
target: { file: { id: 'file3', modifiedAt: new Date(3) } }
|
|
}
|
|
},
|
|
{
|
|
entry: {
|
|
target: { file: { id: 'file1', modifiedAt: new Date(1) } }
|
|
}
|
|
},
|
|
{
|
|
entry: {
|
|
target: { file: { id: 'file2', modifiedAt: new Date(2) } }
|
|
}
|
|
}
|
|
]
|
|
}
|
|
} as FavoritePaging)
|
|
);
|
|
|
|
const ids = await component.getFileIds('favorites');
|
|
expect(ids).toEqual(['file3', 'file2', 'file1']);
|
|
});
|
|
|
|
it('should fetch and sort file ids for shared files', async () => {
|
|
preferences.set('shared.sorting.key', 'name');
|
|
preferences.set('shared.sorting.direction', 'asc');
|
|
|
|
spyOn(contentApi, 'findSharedLinks').and.returnValue(
|
|
of({
|
|
list: {
|
|
entries: [
|
|
{
|
|
entry: {
|
|
nodeId: 'node2',
|
|
name: 'node 2',
|
|
modifiedAt: new Date(2)
|
|
}
|
|
},
|
|
{
|
|
entry: {
|
|
nodeId: 'node1',
|
|
name: 'node 1',
|
|
modifiedAt: new Date(1)
|
|
}
|
|
}
|
|
]
|
|
}
|
|
} as SharedLinkPaging)
|
|
);
|
|
|
|
const ids = await component.getFileIds('shared');
|
|
expect(ids).toEqual(['node1', 'node2']);
|
|
});
|
|
|
|
it('should sort file ids for favorites with [modifiedAt desc]', async () => {
|
|
spyOn(preferences, 'get').and.returnValue(null);
|
|
|
|
spyOn(contentApi, 'findSharedLinks').and.returnValue(
|
|
of({
|
|
list: {
|
|
entries: [
|
|
{
|
|
entry: {
|
|
nodeId: 'node2',
|
|
name: 'node 2',
|
|
modifiedAt: new Date(2)
|
|
}
|
|
},
|
|
{
|
|
entry: {
|
|
nodeId: 'node1',
|
|
name: 'node 1',
|
|
modifiedAt: new Date(1)
|
|
}
|
|
}
|
|
]
|
|
}
|
|
} as SharedLinkPaging)
|
|
);
|
|
|
|
const ids = await component.getFileIds('shared');
|
|
expect(ids).toEqual(['node2', 'node1']);
|
|
});
|
|
|
|
it('should fetch and sort ids for recent-files', async () => {
|
|
preferences.set('recent-files.sorting.key', 'name');
|
|
preferences.set('recent-files.sorting.direction', 'asc');
|
|
|
|
spyOn(contentApi, 'getPerson').and.returnValue(
|
|
of({
|
|
entry: { id: 'user' }
|
|
} as PersonEntry)
|
|
);
|
|
|
|
spyOn(contentApi, 'search').and.returnValue(
|
|
of({
|
|
list: {
|
|
entries: [
|
|
{ entry: { id: 'node2', name: 'node 2', modifiedAt: new Date(2) } },
|
|
{ entry: { id: 'node1', name: 'node 1', modifiedAt: new Date(1) } }
|
|
]
|
|
}
|
|
} as ResultSetPaging)
|
|
);
|
|
|
|
const ids = await component.getFileIds('recent-files');
|
|
expect(ids).toEqual(['node1', 'node2']);
|
|
});
|
|
|
|
it('should sort file ids for favorites with [modifiedAt desc]', async () => {
|
|
spyOn(preferences, 'get').and.returnValue(null);
|
|
|
|
spyOn(contentApi, 'getPerson').and.returnValue(
|
|
of({
|
|
entry: { id: 'user' }
|
|
} as PersonEntry)
|
|
);
|
|
|
|
spyOn(contentApi, 'search').and.returnValue(
|
|
of({
|
|
list: {
|
|
entries: [
|
|
{ entry: { id: 'node2', name: 'node 2', modifiedAt: new Date(2) } },
|
|
{ entry: { id: 'node1', name: 'node 1', modifiedAt: new Date(1) } }
|
|
]
|
|
}
|
|
} as ResultSetPaging)
|
|
);
|
|
|
|
const ids = await component.getFileIds('recent-files');
|
|
expect(ids).toEqual(['node2', 'node1']);
|
|
});
|
|
|
|
it('should return to parent folder on nodesDeleted event', async () => {
|
|
spyOn(component, 'navigateToFileLocation');
|
|
fixture.detectChanges();
|
|
await fixture.whenStable();
|
|
appHookService.nodesDeleted.next();
|
|
|
|
expect(component.navigateToFileLocation).toHaveBeenCalled();
|
|
});
|
|
|
|
it('should return to parent folder on fileUploadDeleted event', async () => {
|
|
spyOn(component, 'navigateToFileLocation');
|
|
fixture.detectChanges();
|
|
await fixture.whenStable();
|
|
uploadService.fileUploadDeleted.next();
|
|
|
|
expect(component.navigateToFileLocation).toHaveBeenCalled();
|
|
});
|
|
|
|
it('should emit nodeUpdated event on fileUploadComplete event', fakeAsync(() => {
|
|
spyOn(nodesApiService.nodeUpdated, 'next');
|
|
fixture.detectChanges();
|
|
uploadService.fileUploadComplete.next({ data: { entry: {} } } as any);
|
|
tick(300);
|
|
|
|
expect(nodesApiService.nodeUpdated.next).toHaveBeenCalled();
|
|
}));
|
|
|
|
it('should return to parent folder when event emitted from extension', async () => {
|
|
spyOn(component, 'navigateToFileLocation');
|
|
fixture.detectChanges();
|
|
await fixture.whenStable();
|
|
store.dispatch(new ClosePreviewAction());
|
|
expect(component.navigateToFileLocation).toHaveBeenCalled();
|
|
});
|
|
|
|
describe('Keyboard navigation', () => {
|
|
beforeEach(() => {
|
|
component.nextNodeId = 'nextNodeId';
|
|
component.previousNodeId = 'previousNodeId';
|
|
spyOn(router, 'navigate').and.stub();
|
|
});
|
|
|
|
afterEach(() => {
|
|
fixture.destroy();
|
|
});
|
|
|
|
it('should not navigate on keyboard event if target is child of sidebar container', () => {
|
|
const parent = document.createElement('div');
|
|
parent.className = 'adf-viewer__sidebar';
|
|
|
|
const child = document.createElement('button');
|
|
child.addEventListener('keyup', function (e) {
|
|
component.onNavigateNext(e);
|
|
});
|
|
parent.appendChild(child);
|
|
document.body.appendChild(parent);
|
|
|
|
child.dispatchEvent(new KeyboardEvent('keyup', { key: 'ArrowLeft' }));
|
|
|
|
expect(router.navigate).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('should not navigate on keyboard event if target is child of cdk overlay', () => {
|
|
const parent = document.createElement('div');
|
|
parent.className = 'cdk-overlay-container';
|
|
|
|
const child = document.createElement('button');
|
|
child.addEventListener('keyup', function (e) {
|
|
component.onNavigateNext(e);
|
|
});
|
|
parent.appendChild(child);
|
|
document.body.appendChild(parent);
|
|
|
|
child.dispatchEvent(new KeyboardEvent('keyup', { key: 'ArrowLeft' }));
|
|
|
|
expect(router.navigate).not.toHaveBeenCalled();
|
|
});
|
|
});
|
|
});
|