mirror of
https://gitlab.com/openstapps/openstapps.git
synced 2026-01-19 16:13:06 +00:00
180 lines
5.7 KiB
TypeScript
180 lines
5.7 KiB
TypeScript
/*
|
|
* Copyright (C) 2025 StApps
|
|
* This program is free software: you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License as published by the Free
|
|
* Software Foundation, version 3.
|
|
*
|
|
* This program 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 General Public License for
|
|
* more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along with
|
|
* this program. If not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
/* eslint-disable @typescript-eslint/no-non-null-assertion,@typescript-eslint/no-explicit-any */
|
|
import {ComponentFixture, TestBed} from '@angular/core/testing';
|
|
import {FormsModule} from '@angular/forms';
|
|
import {CommonModule} from '@angular/common';
|
|
import {TranslateModule} from '@ngx-translate/core';
|
|
import {IonicModule} from '@ionic/angular';
|
|
import {SCThingType} from '@openstapps/core';
|
|
import {ContextMenuModalComponent} from './context-menu-modal.component';
|
|
import {ContextMenuService} from './context-menu.service';
|
|
import {FilterContext, SortContext} from './context-type';
|
|
import {provideIonicAngular, ModalController} from '@ionic/angular/standalone';
|
|
import {BehaviorSubject, of} from 'rxjs';
|
|
import {addIcons} from 'ionicons';
|
|
import {swapVertical, trash} from 'ionicons/icons';
|
|
|
|
describe('ContextMenuModalComponent', () => {
|
|
let fixture: ComponentFixture<ContextMenuModalComponent>;
|
|
let component: ContextMenuModalComponent;
|
|
let modalControllerSpy: jasmine.SpyObj<ModalController>;
|
|
let contextMenuServiceMock: Partial<ContextMenuService>;
|
|
|
|
// Register used icons (suppress warnings)
|
|
addIcons({
|
|
delete: trash,
|
|
sort: swapVertical,
|
|
});
|
|
|
|
beforeEach(async () => {
|
|
modalControllerSpy = jasmine.createSpyObj<ModalController>('ModalController', ['dismiss']);
|
|
|
|
contextMenuServiceMock = {
|
|
filterOptions: new BehaviorSubject<FilterContext | undefined>(getFilterContextType()),
|
|
sortOptions: new BehaviorSubject<SortContext | undefined>(getSortContextType()),
|
|
filterContextChanged$: of(getFilterContextType()),
|
|
sortContextChanged$: of(getSortContextType()),
|
|
contextFilterChanged: jasmine.createSpy(),
|
|
contextSortChanged: jasmine.createSpy(),
|
|
};
|
|
|
|
await TestBed.configureTestingModule({
|
|
declarations: [ContextMenuModalComponent],
|
|
imports: [CommonModule, FormsModule, TranslateModule.forRoot(), IonicModule.forRoot()],
|
|
providers: [
|
|
provideIonicAngular(),
|
|
{
|
|
provide: ModalController,
|
|
useValue: modalControllerSpy,
|
|
},
|
|
{
|
|
provide: ContextMenuService,
|
|
useValue: contextMenuServiceMock,
|
|
},
|
|
],
|
|
}).compileComponents();
|
|
|
|
fixture = TestBed.createComponent(ContextMenuModalComponent);
|
|
component = fixture.componentInstance;
|
|
|
|
component.contextMenuService = contextMenuServiceMock as ContextMenuService;
|
|
component.translator = {
|
|
translatedPropertyValue: () => 'translated',
|
|
} as any;
|
|
|
|
fixture.detectChanges();
|
|
});
|
|
|
|
it('should create the component', () => {
|
|
expect(component).toBeTruthy();
|
|
});
|
|
|
|
it('should load initial sort and filter context', () => {
|
|
expect(component.sortOption?.value).toBe('relevance');
|
|
expect(component.filterOption?.options?.length).toBeGreaterThan(0);
|
|
});
|
|
|
|
it('should display sort items', () => {
|
|
const sortItems = fixture.nativeElement.querySelectorAll('.sort-item');
|
|
expect(sortItems.length).toBe(component.sortOption.values.length);
|
|
});
|
|
|
|
it('should update and reverse sort value on click', () => {
|
|
const value = component.sortOption.values[1]; // "name", reversible
|
|
component.sortChanged(component.sortOption, value);
|
|
expect(component.sortOption.value).toBe('name');
|
|
expect(component.sortOption.reversed).toBeFalse();
|
|
|
|
component.sortChanged(component.sortOption, value);
|
|
expect(component.sortOption.reversed).toBeTrue();
|
|
});
|
|
|
|
it('should call contextFilterChanged when filter is reset', () => {
|
|
component.filterOption.options[0].buckets[0].checked = true;
|
|
component.resetFilter(component.filterOption);
|
|
const allUnchecked = component.filterOption.options.every(opt =>
|
|
opt.buckets.every(bucket => !bucket.checked),
|
|
);
|
|
expect(allUnchecked).toBeTrue();
|
|
expect(contextMenuServiceMock.contextFilterChanged).toHaveBeenCalled();
|
|
});
|
|
|
|
it('should dismiss the modal', () => {
|
|
component.dismiss();
|
|
expect(modalControllerSpy.dismiss).toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
/**
|
|
*
|
|
*/
|
|
function getSortContextType(): SortContext {
|
|
return {
|
|
name: 'sort',
|
|
reversed: false,
|
|
value: 'relevance',
|
|
values: [
|
|
{value: 'relevance', reversible: false},
|
|
{value: 'name', reversible: true},
|
|
{value: 'date', reversible: true},
|
|
{value: 'type', reversible: true},
|
|
],
|
|
};
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
function getFilterContextType(): FilterContext {
|
|
return {
|
|
name: 'filter',
|
|
compact: false,
|
|
options: facetsMock
|
|
.filter(facet => facet.buckets.length > 0)
|
|
.map((facet, i) => ({
|
|
buckets: facet.buckets.map(bucket => ({
|
|
count: bucket.count,
|
|
key: bucket.key,
|
|
checked: false,
|
|
})),
|
|
compact: false,
|
|
field: facet.field,
|
|
onlyOnType: facet.onlyOnType,
|
|
info: {
|
|
onlyOnType: facet.onlyOnType,
|
|
field: facet.field,
|
|
sortOrder: i,
|
|
},
|
|
})),
|
|
};
|
|
}
|
|
|
|
const facetsMock = [
|
|
{
|
|
buckets: [
|
|
{count: 10, key: 'lecture'},
|
|
{count: 5, key: 'seminar'},
|
|
],
|
|
field: 'type',
|
|
onlyOnType: SCThingType.AcademicEvent,
|
|
},
|
|
{
|
|
buckets: [{count: 7, key: 'research'}],
|
|
field: 'categories',
|
|
onlyOnType: SCThingType.AcademicEvent,
|
|
},
|
|
];
|