Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[#12588] Added unit tests for SessionEditFormComponent #12627

Merged
merged 12 commits into from
Dec 5, 2023
Merged
Original file line number Diff line number Diff line change
@@ -1,14 +1,26 @@
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { TimeFormat } from 'src/web/types/datetime-const';
import moment from 'moment-timezone';
import SpyInstance = jest.SpyInstance;
import { DateTimeService } from '../../../services/datetime.service';
import { SimpleModalService } from '../../../services/simple-modal.service';
import { createMockNgbModalRef } from '../../../test-helpers/mock-ngb-modal-ref';
import { Course, ResponseVisibleSetting, SessionVisibleSetting } from '../../../types/api-output';
import { DateFormat, TimeFormat, getDefaultDateFormat, getDefaultTimeFormat } from '../../../types/datetime-const';
import { SimpleModalType } from '../simple-modal/simple-modal-type';
import { TeammatesRouterModule } from '../teammates-router/teammates-router.module';
import { SessionEditFormMode } from './session-edit-form-model';
import { SessionEditFormComponent } from './session-edit-form.component';
import { SessionEditFormModule } from './session-edit-form.module';

describe('SessionEditFormComponent', () => {
let component: SessionEditFormComponent;
let fixture: ComponentFixture<SessionEditFormComponent>;
let simpleModalService: SimpleModalService;
let service: DateTimeService;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: let's rename this dateTimeService for better readability


const submissionStartDateField = 'submissionStartDate';

beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
Expand All @@ -24,6 +36,8 @@ describe('SessionEditFormComponent', () => {

beforeEach(() => {
fixture = TestBed.createComponent(SessionEditFormComponent);
simpleModalService = TestBed.inject(SimpleModalService);
service = TestBed.inject(DateTimeService);
component = fixture.componentInstance;
fixture.detectChanges();
});
Expand Down Expand Up @@ -53,4 +67,312 @@ describe('SessionEditFormComponent', () => {
expect(time.minute).toEqual(0);
});

it('should set the submission start time correctly when the date is same as'
+ ' earliest date and time is earlier than earliest possible time', () => {
const date: DateFormat = component.minDateForSubmissionStart;
const minTime: TimeFormat = component.minTimeForSubmissionStart;
const time: TimeFormat = { hour: minTime.hour - 1, minute: minTime.minute };
const configureSubmissionOpeningTimeSpy = jest.spyOn(component, 'configureSubmissionOpeningTime');
const triggerModelChangeSpy = jest.spyOn(component, 'triggerModelChange');
component.model.submissionStartTime = time;
component.triggerSubmissionOpeningDateModelChange(submissionStartDateField, date);
component.configureSubmissionOpeningTime(minTime);
expect(component.model.submissionStartTime).toStrictEqual(minTime);
expect(configureSubmissionOpeningTimeSpy).toHaveBeenCalledWith(minTime);
weiquu marked this conversation as resolved.
Show resolved Hide resolved
expect(triggerModelChangeSpy).toHaveBeenCalledWith(submissionStartDateField, date);
});

it('should trigger the change of the model when the submission opening date changes', () => {
const date: DateFormat = component.minDateForSubmissionStart;
const minTime: TimeFormat = component.minTimeForSubmissionStart;
const time: TimeFormat = { hour: minTime.hour + 1, minute: minTime.minute };
const triggerModelChangeSpy = jest.spyOn(component, 'triggerModelChange');
component.model.submissionStartTime = time;
component.triggerSubmissionOpeningDateModelChange(submissionStartDateField, date);
expect(triggerModelChangeSpy).toHaveBeenCalledWith(submissionStartDateField, date);
weiquu marked this conversation as resolved.
Show resolved Hide resolved
});

it('should emit a modelChange event with the updated field when triggerModelChange is called', () => {
const field = 'courseId';
const data = 'testId';
const modelChangeSpy = jest.spyOn(component.modelChange, 'emit');
component.triggerModelChange(field, data);
expect(modelChangeSpy).toHaveBeenCalledWith({
...component.model,
[field]: data,
});
});

it('should emit modelChange event when a valid course ID is provided', () => {
const newCourseId = 'testId1';
const courseCandidates: Course[] = [
{
courseId: 'testId1',
courseName: 'testCourse1',
timeZone: 'Asia/Singapore',
institute: 'Institute 1',
creationTimestamp: 1000000000000,
deletionTimestamp: 1500000000000,
},
];
component.courseCandidates = courseCandidates;
const modelChangeSpy = jest.spyOn(component.modelChange, 'emit');
component.courseIdChangeHandler(newCourseId);
expect(modelChangeSpy).toHaveBeenCalledWith({
...component.model,
courseId: newCourseId,
courseName: 'testCourse1',
timeZone: 'Asia/Singapore',
});
});

it('should not emit a modelChange event when no candidates are found', () => {
const newCourseId = 'testId1';
const courseCandidates: Course[] = [];
component.courseCandidates = courseCandidates;
const modelChangeSpy = jest.spyOn(component.modelChange, 'emit');
component.courseIdChangeHandler(newCourseId);
expect(modelChangeSpy).not.toHaveBeenCalled();
});

it('should return the minimum session closing datetime as the session opening datetime '
+ 'if it is later than one hour before now', () => {
const now = moment().tz(component.model.timeZone);
const date = service.getDateInstance(now.add(1, 'days'));
const time = service.getTimeInstance(now);
component.model.submissionStartDate = date;
component.model.submissionStartTime = time;
const minTimeForSubmissionEnd = component.minTimeForSubmissionEnd;
expect(minTimeForSubmissionEnd).toStrictEqual(time);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: let's just use component.minTimeForSubmissionEnd here instead of the const

});

it('should return the minimum session closing datetime as one hour before now '
+ 'if it is later than the session opening datetime', () => {
const now = moment().tz(component.model.timeZone);
const date = service.getDateInstance(now.subtract(1, 'days'));
const time = service.getTimeInstance(now);
const oneHourBeforeNow = service.getTimeInstance(now.subtract(1, 'hours'));
component.model.submissionStartDate = date;
component.model.submissionStartTime = time;
const minTimeForSubmissionEnd = component.minTimeForSubmissionEnd;
expect(minTimeForSubmissionEnd).toStrictEqual(oneHourBeforeNow);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here, use component.minTimeForSubmissionEnd directly, do make the changes for all the others below

});

it('should return the minimum date for session visibility as 30 days before session opening datetime', () => {
const expectedMinDateForSessionVisible = service.getDateInstance(
service.getMomentInstanceFromDate(component.model.submissionStartDate).subtract(30, 'days'),
);
const minDateForSessionVisible = component.minDateForSessionVisible;
expect(minDateForSessionVisible).toEqual(expectedMinDateForSessionVisible);
});

it('should return the minimum time for session visibility as 30 days before session opening datetime', () => {
const expectedMinTimeForSessionVisible = service.getTimeInstance(
service.getMomentInstanceFromDate(component.model.submissionStartDate).subtract(30, 'days'),
);
const minTimeForSessionVisible = component.minTimeForSessionVisible;
expect(minTimeForSessionVisible).toEqual(expectedMinTimeForSessionVisible);
});

it('should return the submissionStartDate as the maximum date for session visibility '
+ 'when response visible setting is LATER', () => {
component.model.responseVisibleSetting = ResponseVisibleSetting.LATER;
component.model.submissionStartDate =
service.getDateInstance(moment().tz(component.model.timeZone));
const maxDateForSessionVisible = component.maxDateForSessionVisible;
expect(maxDateForSessionVisible).toEqual(component.model.submissionStartDate);
});

it('should return the submissionStartDate as the maximum date for session visibility '
+ 'when response visible setting is AT_VISIBLE', () => {
component.model.responseVisibleSetting = ResponseVisibleSetting.AT_VISIBLE;
component.model.submissionStartDate =
service.getDateInstance(moment().tz(component.model.timeZone));
const maxDateForSessionVisible = component.maxDateForSessionVisible;
expect(maxDateForSessionVisible).toEqual(component.model.submissionStartDate);
});

it('should return the submissionStartDate as the maximum date for session visibility '
+ 'when response visible setting is CUSTOM and submissionStartDate is before customResponseVisibleDate', () => {
component.model.responseVisibleSetting = ResponseVisibleSetting.CUSTOM;
component.model.submissionStartDate =
service.getDateInstance(moment().tz(component.model.timeZone));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's save moment().tz(component.model.timeZone) into a const here, as unlikely as it may be, there might be a case where the date changes while the test runs and thus it might end up as different days

component.model.customResponseVisibleDate =
service.getDateInstance(moment().tz(component.model.timeZone).add(1, 'days'));
const maxDateForSessionVisible = component.maxDateForSessionVisible;
expect(maxDateForSessionVisible).toEqual(component.model.submissionStartDate);
});

it('should return the customResponseVisibleDate as the maximum date for session visibility '
+ 'when response visible setting is CUSTOM and submissionStartDate is after customResponseVisibleDate', () => {
component.model.responseVisibleSetting = ResponseVisibleSetting.CUSTOM;
component.model.submissionStartDate =
service.getDateInstance(moment().tz(component.model.timeZone).add(1, 'days'));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as above here

component.model.customResponseVisibleDate =
service.getDateInstance(moment().tz(component.model.timeZone));
const maxDateForSessionVisible = component.maxDateForSessionVisible;
expect(maxDateForSessionVisible).toEqual(component.model.customResponseVisibleDate);
});

it('should return the default date format if response visible setting is not defined', () => {
const maxDateForSessionVisible = component.maxDateForSessionVisible;
expect(maxDateForSessionVisible).toEqual(getDefaultDateFormat());
});

it('should return the submissionStartTime as the maximum time for session visibility '
+ 'when response visible setting is LATER', () => {
component.model.responseVisibleSetting = ResponseVisibleSetting.LATER;
component.model.submissionStartTime = service.getTimeInstance(moment());
const maxTimeForSessionVisible = component.maxTimeForSessionVisible;
expect(maxTimeForSessionVisible).toEqual(component.model.submissionStartTime);
});

it('should return the submissionStartTime as the maximum time for session visibility '
+ 'when response visible setting is AT_VISIBLE', () => {
component.model.responseVisibleSetting = ResponseVisibleSetting.AT_VISIBLE;
component.model.submissionStartTime = service.getTimeInstance(moment());
const maxTimeForSessionVisible = component.maxTimeForSessionVisible;
expect(maxTimeForSessionVisible).toEqual(component.model.submissionStartTime);
});

it('should return submissionStartTime as the maximum time for session visibility '
+ 'when response visible setting is CUSTOM and submissionStartDate is before customResponseVisibleDate', () => {
component.model.responseVisibleSetting = ResponseVisibleSetting.CUSTOM;
component.model.submissionStartTime =
service.getTimeInstance(moment());
component.model.customResponseVisibleTime =
service.getTimeInstance(moment());
component.model.submissionStartDate =
service.getDateInstance(moment().tz(component.model.timeZone));
component.model.customResponseVisibleDate =
service.getDateInstance(moment().tz(component.model.timeZone).add(1, 'days'));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here, let's save all the moment() calls into a const instead

const maxTimeForSessionVisible = component.maxTimeForSessionVisible;
expect(maxTimeForSessionVisible).toEqual(component.model.submissionStartTime);
});

it('should return customResponseVisibleTime as the maximum time for session visibility '
+ 'when response visible setting is CUSTOM and submissionStartDate is after customResponseVisibleDate', () => {
component.model.responseVisibleSetting = ResponseVisibleSetting.CUSTOM;
component.model.submissionStartTime =
service.getTimeInstance(moment());
component.model.customResponseVisibleTime =
service.getTimeInstance(moment());
component.model.submissionStartDate =
service.getDateInstance(moment().tz(component.model.timeZone).add(1, 'days'));
component.model.customResponseVisibleDate =
service.getDateInstance(moment().tz(component.model.timeZone));
const maxTimeForSessionVisible = component.maxTimeForSessionVisible;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as above here

expect(maxTimeForSessionVisible).toEqual(component.model.customResponseVisibleTime);
});

it('should return the default time format if response visible setting is not recognized', () => {
const maxTimeForSessionVisible = component.maxTimeForSessionVisible;
expect(maxTimeForSessionVisible).toEqual(getDefaultTimeFormat());
});

it('should return submissionStartDate as the minimum date for response visibility'
+ ' when session visible setting is AT_OPEN', () => {
component.model.sessionVisibleSetting = SessionVisibleSetting.AT_OPEN;
component.model.submissionStartDate =
service.getDateInstance(moment().tz(component.model.timeZone));
const minDateForResponseVisible = component.minDateForResponseVisible;
expect(minDateForResponseVisible).toEqual(component.model.submissionStartDate);
});

it('should return customSessionVisibleDate as the minimum date for response visibility '
+ 'when session visible setting is CUSTOM', () => {
component.model.sessionVisibleSetting = SessionVisibleSetting.CUSTOM;
component.model.submissionStartDate =
service.getDateInstance(moment().tz(component.model.timeZone));
component.model.customSessionVisibleDate =
service.getDateInstance(moment().tz(component.model.timeZone));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should make customerSessionVisibleDate different from submissionStartDate here

const minDateForResponseVisible = component.minDateForResponseVisible;
expect(minDateForResponseVisible).toEqual(component.model.customSessionVisibleDate);
});

it('should return the default date format if session visible setting is not recognized', () => {
const minDateForResponseVisible = component.minDateForResponseVisible;
expect(minDateForResponseVisible).toEqual(getDefaultDateFormat());
});

it('should return submissionStartTime as the minimum time for response visibility '
+ 'when session visible setting is AT_OPEN', () => {
component.model.sessionVisibleSetting = SessionVisibleSetting.AT_OPEN;
component.model.submissionStartTime =
service.getTimeInstance(moment().tz(component.model.timeZone));
const minTimeForResponseVisible = component.minTimeForResponseVisible;
expect(minTimeForResponseVisible).toEqual(component.model.submissionStartTime);
});

it('should return customSessionVisibleTime as the minimum time for response visibility '
+ 'when session visible setting is CUSTOM', () => {
component.model.sessionVisibleSetting = SessionVisibleSetting.CUSTOM;
component.model.submissionStartTime =
service.getTimeInstance(moment().tz(component.model.timeZone));
component.model.customSessionVisibleTime =
service.getTimeInstance(moment().tz(component.model.timeZone));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here, let's make the customSessionVisibleTime different from submissionStartTime

const minTimeForResponseVisible = component.minTimeForResponseVisible;
expect(minTimeForResponseVisible).toEqual(component.model.customSessionVisibleTime);
});

it('should return the default time format if session visible setting is not defined', () => {
const minTimeForResponseVisible = component.minTimeForResponseVisible;
expect(minTimeForResponseVisible).toEqual(getDefaultTimeFormat());
});

it('should emit addNewSessionEvent when session edit form mode is ADD', () => {
component.formMode = SessionEditFormMode.ADD;
const addNewSessionSpy = jest.spyOn(component.addNewSessionEvent, 'emit');
component.submitFormHandler();
expect(addNewSessionSpy).toHaveBeenCalled();
});

it('should display warning when discarding edit to current feedback session', async () => {
const promise: Promise<void> = Promise.resolve();
const modalSpy: SpyInstance = jest.spyOn(simpleModalService, 'openConfirmationModal')
.mockReturnValue(createMockNgbModalRef({}, promise));
component.cancelHandler();
await promise;
expect(modalSpy).toHaveBeenCalledTimes(1);
expect(modalSpy).toHaveBeenLastCalledWith('Discard unsaved edit?',
SimpleModalType.WARNING, 'Warning: Any unsaved changes will be lost.');
});

it('should display warning when deleting the current feedback session', async () => {
const promise: Promise<void> = Promise.resolve();
const modalSpy: SpyInstance = jest.spyOn(simpleModalService, 'openConfirmationModal')
.mockReturnValue(createMockNgbModalRef({}, promise));
component.deleteHandler();
await promise;
expect(modalSpy).toHaveBeenCalledTimes(1);
expect(modalSpy)
.toHaveBeenLastCalledWith(`Delete the session <strong>${component.model.feedbackSessionName}</strong>?`,
SimpleModalType.WARNING, 'The session will be moved to the recycle bin. This action can be reverted '
+ 'by going to the "Sessions" tab and restoring the desired session(s).');
});

it('should emit editExistingSessionEvent when session edit form Mode is EDIT', () => {
component.formMode = SessionEditFormMode.EDIT;
const editExistingSessionSpy = jest.spyOn(component.editExistingSessionEvent, 'emit');
component.submitFormHandler();
expect(editExistingSessionSpy).toHaveBeenCalled();
});

it('should emit copyCurrentSessionEvent when copyHandler is called', () => {
const copyCurrentSessionSpy = jest.spyOn(component.copyCurrentSessionEvent, 'emit');
component.copyHandler();
expect(copyCurrentSessionSpy).toHaveBeenCalled();
});

it('should emit copyOtherSessionsEvent when copyOthersHandler is called', () => {
const copyOtherSessionsSpy = jest.spyOn(component.copyOtherSessionsEvent, 'emit');
component.copyOthersHandler();
expect(copyOtherSessionsSpy).toHaveBeenCalled();
});

it('should emit closeEditFormEvent when closeEditFormHandler is called', () => {
const closeEditFormSpy = jest.spyOn(component.closeEditFormEvent, 'emit');
component.closeEditFormHandler();
expect(closeEditFormSpy).toHaveBeenCalled();
});
});