diff --git a/src/app/features/tasks/short-syntax.spec.ts b/src/app/features/tasks/short-syntax.spec.ts index 6173bafff6..6282833fb5 100644 --- a/src/app/features/tasks/short-syntax.spec.ts +++ b/src/app/features/tasks/short-syntax.spec.ts @@ -1,6 +1,10 @@ import { TaskCopy } from './task.model'; import { shortSyntax } from './short-syntax'; import { getWorklogStr } from '../../util/get-work-log-str'; +import { + MONTH_SHORT_NAMES, + oneDayInMilliseconds, +} from '../../util/month-time-conversion'; import { Tag } from '../tag/tag.model'; import { DEFAULT_TAG } from '../tag/tag.const'; import { Project } from '../project/project.model'; @@ -109,6 +113,28 @@ const checkIfCorrectDateAndTime = ( return isDayCorrect && isHourCorrect && isMinuteCorrect; }; +const checkIfCorrectDateMonthAndYear = ( + timestamp: number, + givenDate: number, + givenMonth: number, + givenYear: number, + hour?: number, + minute?: number, +): boolean => { + const date = new Date(timestamp); + const correctDateMonthYear = + date.getDate() === givenDate && + date.getMonth() + 1 === givenMonth && + date.getFullYear() === givenYear; + if (!hour) { + return correctDateMonthYear; + } + if (!minute) { + return correctDateMonthYear && date.getHours() === hour; + } + return correctDateMonthYear && date.getHours() === hour && date.getMinutes() === minute; +}; + describe('shortSyntax', () => { it('should ignore for no short syntax', () => { const r = shortSyntax(TASK, CONFIG); @@ -991,4 +1017,61 @@ describe('shortSyntax', () => { expect(r).toEqual(undefined); }); }); + + // This group of tests address Chrono's parsing the format " " as year + // This will cause unintended parsing result when the date syntax is used together with the time estimate syntax + // https://github.com/johannesjo/super-productivity/issues/4194 + // The focus of this test group will be the ability of the parser to get the correct year and time estimate + describe('should not parse time estimate syntax as year', () => { + const today = new Date(); + const minuteEstimate = 90; + + it('should correctly parse year and time estimate when the input date only has month and day of the month', () => { + const tomorrow = new Date(today.getTime() + oneDayInMilliseconds); + const inputMonth = tomorrow.getMonth() + 1; + const inputMonthName = MONTH_SHORT_NAMES[tomorrow.getMonth()]; + const inputDayOfTheMonth = tomorrow.getDate(); + const t = { + ...TASK, + title: `Test @${inputMonthName} ${inputDayOfTheMonth} ${minuteEstimate}m`, + }; + const parsedTaskInfo = shortSyntax(t, CONFIG, []); + const taskChanges = parsedTaskInfo?.taskChanges; + const plannedAt = taskChanges?.plannedAt as number; + expect( + checkIfCorrectDateMonthAndYear( + plannedAt, + inputDayOfTheMonth, + inputMonth, + tomorrow.getFullYear(), + ), + ).toBeTrue(); + expect(taskChanges?.timeEstimate).toEqual(minuteEstimate * 60 * 1000); + }); + + it('should correctly parse year and time estimate when the input date contains month, day of the month and time', () => { + const time = '4pm'; + const tomorrow = new Date(today.getTime() + oneDayInMilliseconds); + const inputMonth = tomorrow.getMonth() + 1; + const inputMonthName = MONTH_SHORT_NAMES[tomorrow.getMonth()]; + const inputDayOfTheMonth = tomorrow.getDate(); + const t = { + ...TASK, + title: `Test @${inputMonthName} ${inputDayOfTheMonth} ${time} ${minuteEstimate}m`, + }; + const parsedTaskInfo = shortSyntax(t, CONFIG, []); + const taskChanges = parsedTaskInfo?.taskChanges; + const plannedAt = taskChanges?.plannedAt as number; + expect( + checkIfCorrectDateMonthAndYear( + plannedAt, + inputDayOfTheMonth, + inputMonth, + tomorrow.getFullYear(), + 16, + ), + ).toBeTrue(); + expect(taskChanges?.timeEstimate).toEqual(minuteEstimate * 60 * 1000); + }); + }); });