S9 OR Tools basic scheduling
All checks were successful
Build, test and publish / verify (push) Successful in 1m48s
All checks were successful
Build, test and publish / verify (push) Successful in 1m48s
This commit is contained in:
@@ -3,7 +3,7 @@ import { HttpErrorResponse } from '@angular/common/http';
|
||||
import { of, throwError } from 'rxjs';
|
||||
import { SchoolApi } from '../core/school-api';
|
||||
import { EntityEditor } from './entity-editor';
|
||||
import { EditorTarget } from '../core/api-models';
|
||||
import { EditorTarget, Show } from '../core/api-models';
|
||||
|
||||
describe('EntityEditor', () => {
|
||||
const api = { saveSchool: vi.fn(), saveGroup: vi.fn(), saveDancer: vi.fn(), saveShow: vi.fn() };
|
||||
@@ -14,10 +14,11 @@ describe('EntityEditor', () => {
|
||||
providers: [{ provide: SchoolApi, useValue: api }],
|
||||
});
|
||||
});
|
||||
function editor(target: EditorTarget) {
|
||||
function editor(target: EditorTarget, shows: Show[] = []) {
|
||||
const fixture = TestBed.createComponent(EntityEditor);
|
||||
fixture.componentRef.setInput('target', target);
|
||||
fixture.componentRef.setInput('schoolId', 7);
|
||||
fixture.componentRef.setInput('shows', shows);
|
||||
fixture.componentInstance.ngOnInit();
|
||||
return fixture.componentInstance;
|
||||
}
|
||||
@@ -66,4 +67,107 @@ describe('EntityEditor', () => {
|
||||
expect(component.error()).toContain('niet bereikbaar');
|
||||
expect(component.busy()).toBe(false);
|
||||
});
|
||||
const show: Show = {
|
||||
id: 3,
|
||||
name: 'Summer',
|
||||
dayParts: [
|
||||
{ date: '2027-06-01', type: 'AFTERNOON', danceGroupIds: [4] },
|
||||
{ date: '2027-06-02', type: 'AFTERNOON', danceGroupIds: [4] },
|
||||
],
|
||||
appearances: { REGULAR: 2, SELECTION: 2, PRE_SCHOOLER: 1, SHOW_CASE: 2, PRE_EDUCATION: 2 },
|
||||
};
|
||||
|
||||
it('initializes defaults for every type and rejects duplicate dated dayparts and fractional counts', () => {
|
||||
const component = editor({ kind: 'show' });
|
||||
component.name = 'Show';
|
||||
expect(component.validDetails()).toBe(true);
|
||||
component.addPart();
|
||||
component.dayParts[0].date = '2027-06-01';
|
||||
component.addPart();
|
||||
component.dayParts[1].date = '2027-06-02';
|
||||
component.addPart();
|
||||
component.dayParts[2].date = '2027-06-03';
|
||||
expect(component.appearances).toEqual({
|
||||
REGULAR: 2,
|
||||
SELECTION: 2,
|
||||
PRE_SCHOOLER: 1,
|
||||
SHOW_CASE: 3,
|
||||
PRE_EDUCATION: 3,
|
||||
});
|
||||
expect(component.validDetails()).toBe(true);
|
||||
component.dayParts[2].date = '2027-06-02';
|
||||
expect(component.validDetails()).toBe(false);
|
||||
component.dayParts[2].date = '2027-06-03';
|
||||
component.appearances.PRE_SCHOOLER = 1.5;
|
||||
expect(component.validDetails()).toBe(false);
|
||||
});
|
||||
|
||||
it('preserves saved counts and requires explicit correction when reducing to one daypart', () => {
|
||||
const component = editor({ kind: 'show', value: show });
|
||||
component.addPart();
|
||||
component.dayParts[2].date = '2027-06-03';
|
||||
expect(component.appearances.SHOW_CASE).toBe(2);
|
||||
component.removePart(2);
|
||||
component.removePart(1);
|
||||
expect(component.appearances.REGULAR).toBe(2);
|
||||
expect(component.validDetails()).toBe(false);
|
||||
component.setSingleCounts();
|
||||
expect(component.validDetails()).toBe(true);
|
||||
});
|
||||
|
||||
it('allows cancelling an invalidating edit and skips warnings for name-only edits', async () => {
|
||||
api.saveShow.mockReturnValue(of(show));
|
||||
const confirm = vi.spyOn(window, 'confirm').mockReturnValue(false);
|
||||
try {
|
||||
const component = editor({ kind: 'show', value: show });
|
||||
component.appearances.REGULAR = 1;
|
||||
await component.save();
|
||||
expect(confirm).toHaveBeenCalledWith(expect.stringContaining('Summer'));
|
||||
expect(api.saveShow).not.toHaveBeenCalled();
|
||||
component.appearances.REGULAR = 2;
|
||||
confirm.mockClear();
|
||||
component.name = 'Renamed';
|
||||
await component.save();
|
||||
expect(confirm).not.toHaveBeenCalled();
|
||||
expect(api.saveShow).toHaveBeenCalledOnce();
|
||||
} finally {
|
||||
confirm.mockRestore();
|
||||
}
|
||||
});
|
||||
it('names affected shows before adding a group and allows cancellation', async () => {
|
||||
const confirm = vi.spyOn(window, 'confirm').mockReturnValue(false);
|
||||
try {
|
||||
const component = editor({ kind: 'group' }, [show]);
|
||||
component.name = 'New group';
|
||||
await component.save();
|
||||
expect(confirm).toHaveBeenCalledWith(expect.stringContaining('Summer'));
|
||||
expect(api.saveGroup).not.toHaveBeenCalled();
|
||||
} finally {
|
||||
confirm.mockRestore();
|
||||
}
|
||||
});
|
||||
|
||||
it('displays one disabled appearance field for every type with one daypart', async () => {
|
||||
// jsdom does not implement native dialog opening.
|
||||
const descriptor = Object.getOwnPropertyDescriptor(HTMLDialogElement.prototype, 'showModal');
|
||||
Object.defineProperty(HTMLDialogElement.prototype, 'showModal', {
|
||||
configurable: true,
|
||||
value: () => undefined,
|
||||
});
|
||||
try {
|
||||
const fixture = TestBed.createComponent(EntityEditor);
|
||||
fixture.componentRef.setInput('target', { kind: 'show' });
|
||||
fixture.componentInstance.addPart();
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
fixture.detectChanges();
|
||||
const element = fixture.nativeElement as HTMLElement;
|
||||
const inputs = Array.from(element.querySelectorAll<HTMLInputElement>('input[type="number"]'));
|
||||
expect(inputs).toHaveLength(5);
|
||||
expect(inputs.every((input) => input.disabled && input.value === '1')).toBe(true);
|
||||
} finally {
|
||||
if (descriptor) Object.defineProperty(HTMLDialogElement.prototype, 'showModal', descriptor);
|
||||
else Reflect.deleteProperty(HTMLDialogElement.prototype, 'showModal');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user