All files / src/utils/helpers assignmentAvailability.js

100% Statements 30/30
100% Branches 32/32
100% Functions 3/3
100% Lines 21/21

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82                                      74x   68x 68x 68x   68x   67x                 77x   67x 67x 67x   67x   66x 61x 50x                                                 22x   18x 18x 18x   16x 16x 11x    
/**
 * Pure assignment-availability helpers for the student View Assignment page.
 *
 * Extracted from ViewAssignment.jsx (EI-2386) so the logic can be unit-tested
 * directly — previously the tests re-implemented copies of these functions, so a
 * real regression in the component could not fail them.
 *
 * Epoch-based `new Date()` comparison so the open/close window is evaluated
 * correctly regardless of timezone (the EI-2386 C4 fix). Missing/invalid dates
 * are treated conservatively as "not available".
 */
 
/**
 * @param {string|undefined|null} dateOpenUTC  ISO/epoch-parseable open date
 * @param {string|undefined|null} dateCloseUTC ISO/epoch-parseable close date
 * @returns {boolean} true when "now" is outside the [open, close] window, or when
 *                    either date is missing/invalid.
 */
export function isNotAvailable(dateOpenUTC, dateCloseUTC) {
    if (!dateOpenUTC || !dateCloseUTC) return true;
 
    const nowUTC = new Date();
    const open = new Date(dateOpenUTC);
    const close = new Date(dateCloseUTC);
 
    if (isNaN(open.getTime()) || isNaN(close.getTime())) return true;
 
    return !(nowUTC >= open && nowUTC <= close);
}
 
/**
 * @param {string|undefined|null} dateOpen  ISO/epoch-parseable open date
 * @param {string|undefined|null} dateClose ISO/epoch-parseable close date
 * @returns {{label: string, color: string}} the status chip label + MUI color.
 */
export function getAssignmentStatus(dateOpen, dateClose) {
    if (!dateOpen || !dateClose) return { label: "Not Yet Available", color: "default" };
 
    const nowUTC = new Date();
    const open = new Date(dateOpen);
    const close = new Date(dateClose);
 
    if (isNaN(open.getTime()) || isNaN(close.getTime())) return { label: "Not Yet Available", color: "default" };
 
    if (nowUTC < open) return { label: "Not Yet Available", color: "default" };
    if (nowUTC > close) return { label: "Closed", color: "error" };
    return { label: "Available", color: "success" };
}
 
/**
 * WHY `isNotAvailable(dateOpen, dateClose)` is true — lets the UI give the
 * student a specific, actionable reason ("opens on X" / "closed on Y") instead
 * of a generic "not currently available", without changing what the backend
 * sends: `GET /v1/student/assignment/{assignment_uuid}/fetch` already returns
 * `date_open`/`date_close` on every assignment (see `fetch_specific_assignment`
 * in the services repo), so no API change was needed to support this.
 *
 * Only meaningful when `isNotAvailable(dateOpen, dateClose)` is true — callers
 * should gate on that first; this does not itself confirm unavailability.
 *
 * @param {string|undefined|null} dateOpenUTC  ISO/epoch-parseable open date
 * @param {string|undefined|null} dateCloseUTC ISO/epoch-parseable close date
 * @returns {"not_yet_open"|"closed"|"not_scheduled"}
 *   "not_scheduled" — no open/close window is set at all, or one of the dates
 *     is unparseable. Distinguished from "not_yet_open" so the UI never has to
 *     render a message built around a missing/invalid date.
 *   "not_yet_open" — the window hasn't started yet.
 *   "closed" — the window has ended. The only remaining case once the above two
 *     are ruled out and dateOpen <= now, so no explicit close-date check needed.
 */
export function getUnavailabilityReason(dateOpenUTC, dateCloseUTC) {
    if (!dateOpenUTC || !dateCloseUTC) return "not_scheduled";
 
    const open = new Date(dateOpenUTC);
    const close = new Date(dateCloseUTC);
    if (isNaN(open.getTime()) || isNaN(close.getTime())) return "not_scheduled";
 
    const nowUTC = new Date();
    if (nowUTC < open) return "not_yet_open";
    return "closed";
}