745 lines
48 KiB
JavaScript
745 lines
48 KiB
JavaScript
// Rôle : vérifie les invariants statiques des toolboxes et de leurs outils.
|
|
import { readFile } from "node:fs/promises";
|
|
import { test } from "node:test";
|
|
import assert from "node:assert/strict";
|
|
import { SOCKET_SHAPES, formatValue, getSocketItemName, setItemCategory } from "../website/src/features/toolboxes/modules/equipmentPlannerOptions.js";
|
|
import { applyDueResets, getTaskParentMap, getTaskTreeIds, isTaskResetSoon, moveTask } from "../website/src/features/toolboxes/modules/taskPlannerUtils.js";
|
|
import { createInput, getInputLabel, normalizeCustomKey, parseHoldSeconds } from "../website/src/features/toolboxes/modules/combosUtils.js";
|
|
import { getBoundsFromStartAndSize, getSelectionBounds, moveSelectionCells, parseTsvBlock, pasteCellBlock } from "../website/src/features/toolboxes/modules/tableUtils.js";
|
|
import { canToggleAutoRefresh, getCountdownValidationError, isAlertGuarded } from "../website/src/features/toolboxes/modules/timerOptions.js";
|
|
import { getCalendarOccurrences, getCurrentCalendarOccurrence, getMonthEnd, getMonthStart, getNextCalendarAlertTime, getWeekStart } from "../website/src/features/toolboxes/modules/calendarUtils.js";
|
|
import { exportModuleText, importModuleText } from "../website/src/features/toolboxes/modules/textImport.js";
|
|
import { compactCalendarDataForStorage, normalizeCalendarData } from "../website/src/features/toolboxes/storage/modules/calendar.js";
|
|
|
|
function countLines(source) {
|
|
return source.trimEnd().split("\n").length;
|
|
}
|
|
|
|
test("toolbox cards and pages retain their public UI conventions", async () => {
|
|
const toolboxCard = await readFile("website/src/features/toolboxes/ToolboxCard.jsx", "utf8");
|
|
const toolboxPages = await readFile("website/src/features/toolboxes/ToolboxPages.jsx", "utf8");
|
|
const toolboxCoverEditor = await readFile("website/src/features/toolboxes/ToolboxCoverEditor.jsx", "utf8");
|
|
const toolboxActions = await readFile("website/src/features/toolboxes/useToolboxActions.js", "utf8");
|
|
const toolboxStorage = await readFile("website/src/features/toolboxes/storage/toolboxStorage.js", "utf8");
|
|
const checklistStorage = await readFile("website/src/features/toolboxes/storage/modules/checklist.js", "utf8");
|
|
const timerStorage = await readFile("website/src/features/toolboxes/storage/modules/timer.js", "utf8");
|
|
const calendarStorage = await readFile("website/src/features/toolboxes/storage/modules/calendar.js", "utf8");
|
|
const resetUtils = await readFile("website/src/features/toolboxes/modules/resetUtils.js", "utf8");
|
|
const equipmentStorage = await readFile("website/src/features/toolboxes/storage/modules/equipmentPlanner.js", "utf8");
|
|
const storageQuota = await readFile("website/src/components/StorageQuota.jsx", "utf8");
|
|
const importButton = await readFile("website/src/components/ImportButton.jsx", "utf8");
|
|
const guidedTooltipTour = await readFile("website/src/components/GuidedTooltipTour.jsx", "utf8");
|
|
|
|
assert.match(toolboxActions, /creationTemplate/);
|
|
assert.match(toolboxActions, /type: "memos"/);
|
|
assert.match(toolboxActions, /type: "links"/);
|
|
assert.match(toolboxActions, /#\/library/);
|
|
assert.match(toolboxStorage, /TOOLBOX_ICON_FILES/);
|
|
assert.match(toolboxStorage, /export function normalizeToolboxIcon/);
|
|
assert.match(toolboxStorage, /export function normalizeToolboxCustomCover/);
|
|
assert.match(toolboxStorage, /customCover: normalizeToolboxCustomCover/);
|
|
assert.match(toolboxStorage, /compact\.customCover = normalized\.customCover/);
|
|
assert.match(checklistStorage, /qtyTarget/);
|
|
assert.match(checklistStorage, /qtyCurrent/);
|
|
assert.match(checklistStorage, /hideCompletedSections/);
|
|
assert.match(checklistStorage, /hideCompletedSectionsFully/);
|
|
assert.match(checklistStorage, /hideWhenComplete/);
|
|
assert.match(equipmentStorage, /collapsed/);
|
|
assert.match(toolboxStorage, /module\.scrollable/);
|
|
assert.match(timerStorage, /scrollResults/);
|
|
assert.match(calendarStorage, /normalizeCalendarData/);
|
|
assert.match(resetUtils, /export function getLatestDailyReset/);
|
|
assert.match(resetUtils, /export function getLatestWeeklyReset/);
|
|
assert.match(toolboxStorage, /nextId\("calendar"\)/);
|
|
assert.match(toolboxCard, /export function ToolboxCard/);
|
|
assert.match(toolboxCard, /export function ToolboxIconPicker/);
|
|
assert.match(toolboxCard, /export function ToolboxGameIcon/);
|
|
assert.match(toolboxCard, /toolbox-card-drag-handle/);
|
|
assert.match(toolboxCard, /reorder\.getItemProps/);
|
|
assert.match(toolboxCard, /toolbox-card-cover-link/);
|
|
assert.match(toolboxCard, /toolbox-icon-cover/);
|
|
assert.match(toolboxPages, /\.\/ToolboxCard\.jsx/);
|
|
assert.match(toolboxPages, /updateToolboxOrder/);
|
|
assert.match(toolboxPages, /useGroupedReorder/);
|
|
assert.match(toolboxPages, /useInlineEdit/);
|
|
assert.match(toolboxPages, /getContentEditableProps/);
|
|
assert.match(toolboxPages, /orientation: "horizontal"/);
|
|
assert.match(toolboxPages, /components\/StorageQuota\.jsx/);
|
|
assert.match(toolboxPages, /components\/ImportButton\.jsx/);
|
|
assert.match(toolboxPages, /import-icon-button/);
|
|
assert.match(toolboxPages, /getToolboxGame\(toolbox\)/);
|
|
assert.match(toolboxPages, /Vers la page de jeu/);
|
|
assert.match(toolboxPages, /Vers la toolbox complète/);
|
|
assert.match(toolboxPages, /#\/games\/\$\{toolboxGame\.id\}/);
|
|
assert.match(toolboxPages, /#\/toolbox\/\$\{toolbox\.id\}/);
|
|
assert.match(toolboxPages, /setDrawerGameId\(""\)/);
|
|
assert.match(toolboxPages, /moduleColumns/);
|
|
assert.match(toolboxPages, /normalizeCombosData/);
|
|
assert.match(guidedTooltipTour, /--tutorial-arrow-left/);
|
|
assert.match(guidedTooltipTour, /--tutorial-arrow-top/);
|
|
assert.match(guidedTooltipTour, /data-placement/);
|
|
assert.doesNotMatch(toolboxPages, /usePointerReorder/);
|
|
assert.match(storageQuota, /export function StorageQuota/);
|
|
assert.match(storageQuota, /role="progressbar"/);
|
|
assert.match(importButton, /export function ImportButton/);
|
|
assert.match(importButton, /accept="application\/json"/);
|
|
assert.doesNotMatch(toolboxCoverEditor, /react-easy-crop|react-image-crop/);
|
|
});
|
|
|
|
test("toolbox module registry and modules expose expected behavior", async () => {
|
|
const source = await readFile("website/src/main.jsx", "utf8");
|
|
const moduleRegistry = await readFile("website/src/features/toolboxes/modules/index.jsx", "utf8");
|
|
const moduleComponents = await readFile("website/src/features/toolboxes/modules/toolboxModuleComponents.jsx", "utf8");
|
|
const addToolControls = await readFile("website/src/features/toolboxes/modules/AddToolControls.jsx", "utf8");
|
|
const toolboxModules = await readFile("website/src/features/toolboxes/modules/ToolboxModules.jsx", "utf8");
|
|
const moduleRegistryBundle = `${moduleRegistry}\n${moduleComponents}\n${addToolControls}\n${toolboxModules}`;
|
|
const moduleMetadataRegistry = await readFile("website/src/features/toolboxes/toolboxModuleRegistry.js", "utf8");
|
|
const notepadModule = await readFile("website/src/features/toolboxes/modules/NotepadModule.jsx", "utf8");
|
|
const notepadToolbar = await readFile("website/src/features/toolboxes/modules/NotepadToolbar.jsx", "utf8");
|
|
const notepadUtils = await readFile("website/src/features/toolboxes/modules/notepadUtils.js", "utf8");
|
|
const combosModule = await readFile("website/src/features/toolboxes/modules/CombosModule.jsx", "utf8");
|
|
const combosEditor = await readFile("website/src/features/toolboxes/modules/CombosEditor.jsx", "utf8");
|
|
const combosParts = await readFile("website/src/features/toolboxes/modules/CombosParts.jsx", "utf8");
|
|
const combosList = await readFile("website/src/features/toolboxes/modules/CombosList.jsx", "utf8");
|
|
const combosEditorState = await readFile("website/src/features/toolboxes/modules/useCombosEditorState.js", "utf8");
|
|
const combosReorder = await readFile("website/src/features/toolboxes/modules/useCombosReorder.js", "utf8");
|
|
const combosBundle = `${combosModule}\n${combosEditor}\n${combosParts}\n${combosList}\n${combosEditorState}\n${combosReorder}`;
|
|
const combosUtils = await readFile("website/src/features/toolboxes/modules/combosUtils.js", "utf8");
|
|
const calculatorModule = await readFile("website/src/features/toolboxes/modules/CalculatorModule.jsx", "utf8");
|
|
const calculatorEntries = await readFile("website/src/features/toolboxes/modules/CalculatorEntries.jsx", "utf8");
|
|
const calculatorUtils = await readFile("website/src/features/toolboxes/modules/calculatorUtils.js", "utf8");
|
|
const calculatorBundle = `${calculatorModule}\n${calculatorEntries}\n${calculatorUtils}`;
|
|
const tableModule = await readFile("website/src/features/toolboxes/modules/TableModule.jsx", "utf8");
|
|
const tableGrid = await readFile("website/src/features/toolboxes/modules/TableGrid.jsx", "utf8");
|
|
const tableGridParts = await readFile("website/src/features/toolboxes/modules/TableGridParts.jsx", "utf8");
|
|
const tableToolbar = await readFile("website/src/features/toolboxes/modules/TableToolbar.jsx", "utf8");
|
|
const tableInteractions = await readFile("website/src/features/toolboxes/modules/useTableInteractions.js", "utf8");
|
|
const tableClipboard = await readFile("website/src/features/toolboxes/modules/useTableClipboard.js", "utf8");
|
|
const tableBundle = `${tableModule}\n${tableGrid}\n${tableGridParts}\n${tableToolbar}\n${tableInteractions}\n${tableClipboard}`;
|
|
const tableUtils = await readFile("website/src/features/toolboxes/modules/tableUtils.js", "utf8");
|
|
const tableFormulaEngine = await readFile("website/src/features/toolboxes/modules/tableFormulaEngine.js", "utf8");
|
|
const timerModule = await readFile("website/src/features/toolboxes/modules/TimerModule.jsx", "utf8");
|
|
const timerCountdown = await readFile("website/src/features/toolboxes/modules/TimerCountdown.jsx", "utf8");
|
|
const timerSharedControls = await readFile("website/src/features/toolboxes/modules/TimerSharedControls.jsx", "utf8");
|
|
const timerStopwatch = await readFile("website/src/features/toolboxes/modules/TimerStopwatch.jsx", "utf8");
|
|
const timerBundle = `${timerModule}\n${timerCountdown}\n${timerSharedControls}\n${timerStopwatch}`;
|
|
const timerOptions = await readFile("website/src/features/toolboxes/modules/timerOptions.js", "utf8");
|
|
const calendarModule = await readFile("website/src/features/toolboxes/modules/CalendarModule.jsx", "utf8");
|
|
const calendarController = await readFile("website/src/features/toolboxes/modules/useCalendarController.js", "utf8");
|
|
const calendarDraftControls = await readFile("website/src/features/toolboxes/modules/useCalendarDraftControls.js", "utf8");
|
|
const calendarEventActions = await readFile("website/src/features/toolboxes/modules/calendarEventActions.js", "utf8");
|
|
const calendarLifecycle = await readFile("website/src/features/toolboxes/modules/useCalendarLifecycle.js", "utf8");
|
|
const calendarModeChoice = await readFile("website/src/features/toolboxes/modules/CalendarModeChoice.jsx", "utf8");
|
|
const calendarContent = await readFile("website/src/features/toolboxes/modules/CalendarContent.jsx", "utf8");
|
|
const calendarDraft = await readFile("website/src/features/toolboxes/modules/calendarDraft.js", "utf8");
|
|
const calendarEventForm = await readFile("website/src/features/toolboxes/modules/CalendarEventForm.jsx", "utf8");
|
|
const calendarEventControls = await readFile("website/src/features/toolboxes/modules/CalendarEventControls.jsx", "utf8");
|
|
const calendarMonthDatePicker = await readFile("website/src/features/toolboxes/modules/CalendarMonthDatePicker.jsx", "utf8");
|
|
const calendarViews = await readFile("website/src/features/toolboxes/modules/CalendarViews.jsx", "utf8");
|
|
const calendarBundle = `${calendarModule}\n${calendarController}\n${calendarDraftControls}\n${calendarEventActions}\n${calendarLifecycle}\n${calendarModeChoice}\n${calendarContent}\n${calendarDraft}\n${calendarEventForm}\n${calendarEventControls}\n${calendarMonthDatePicker}\n${calendarViews}`;
|
|
const calendarUtils = await readFile("website/src/features/toolboxes/modules/calendarUtils.js", "utf8");
|
|
const taskPlannerModule = await readFile("website/src/features/toolboxes/modules/TaskPlannerModule.jsx", "utf8");
|
|
const taskPlannerBranch = await readFile("website/src/features/toolboxes/modules/TaskPlannerBranch.jsx", "utf8");
|
|
const taskPlannerItem = await readFile("website/src/features/toolboxes/modules/TaskPlannerItem.jsx", "utf8");
|
|
const taskPlannerItemPanels = await readFile("website/src/features/toolboxes/modules/TaskPlannerItemPanels.jsx", "utf8");
|
|
const taskPlannerReorder = await readFile("website/src/features/toolboxes/modules/useTaskPlannerReorder.js", "utf8");
|
|
const taskPlannerTopbar = await readFile("website/src/features/toolboxes/modules/TaskPlannerTopbar.jsx", "utf8");
|
|
const taskPlannerSettings = await readFile("website/src/features/toolboxes/modules/TaskPlannerSettings.jsx", "utf8");
|
|
const taskPlannerBundle = `${taskPlannerBranch}\n${taskPlannerItem}\n${taskPlannerItemPanels}\n${taskPlannerReorder}\n${taskPlannerTopbar}\n${taskPlannerSettings}`;
|
|
const taskPlannerUtils = await readFile("website/src/features/toolboxes/modules/taskPlannerUtils.js", "utf8");
|
|
const goalTreeModule = await readFile("website/src/features/toolboxes/modules/GoalTreeModule.jsx", "utf8");
|
|
const goalTreeEditor = await readFile("website/src/features/toolboxes/modules/GoalTreeEditor.jsx", "utf8");
|
|
const goalTreeParts = await readFile("website/src/features/toolboxes/modules/GoalTreeParts.jsx", "utf8");
|
|
const goalTreeBranch = await readFile("website/src/features/toolboxes/modules/GoalTreeBranch.jsx", "utf8");
|
|
const goalTreeEditorFields = await readFile("website/src/features/toolboxes/modules/GoalTreeEditorFields.jsx", "utf8");
|
|
const goalTreeNodeContent = await readFile("website/src/features/toolboxes/modules/GoalTreeNodeContent.jsx", "utf8");
|
|
const goalTreeController = await readFile("website/src/features/toolboxes/modules/useGoalTreeController.js", "utf8");
|
|
const goalTreeView = await readFile("website/src/features/toolboxes/modules/GoalTreeView.jsx", "utf8");
|
|
const goalTreeBundle = `${goalTreeModule}\n${goalTreeEditor}\n${goalTreeParts}\n${goalTreeBranch}\n${goalTreeEditorFields}\n${goalTreeNodeContent}\n${goalTreeController}\n${goalTreeView}`;
|
|
const goalTreeDisplay = await readFile("website/src/features/toolboxes/modules/goalTreeDisplay.js", "utf8");
|
|
const goalTreeUtils = await readFile("website/src/features/toolboxes/modules/goalTreeUtils.js", "utf8");
|
|
const equipmentPlannerModule = await readFile("website/src/features/toolboxes/modules/EquipmentPlannerModule.jsx", "utf8");
|
|
const equipmentPlannerEditors = await readFile("website/src/features/toolboxes/modules/EquipmentPlannerEditors.jsx", "utf8");
|
|
const equipmentPlannerCharacteristics = await readFile("website/src/features/toolboxes/modules/EquipmentPlannerCharacteristics.jsx", "utf8");
|
|
const equipmentPlannerMaterials = await readFile("website/src/features/toolboxes/modules/EquipmentPlannerMaterials.jsx", "utf8");
|
|
const equipmentPlannerSockets = await readFile("website/src/features/toolboxes/modules/EquipmentPlannerSockets.jsx", "utf8");
|
|
const equipmentPlannerBundle = `${equipmentPlannerEditors}\n${equipmentPlannerCharacteristics}\n${equipmentPlannerMaterials}\n${equipmentPlannerSockets}`;
|
|
const equipmentPlannerItem = await readFile("website/src/features/toolboxes/modules/EquipmentPlannerItem.jsx", "utf8");
|
|
const equipmentPlannerPickers = await readFile("website/src/features/toolboxes/modules/EquipmentPlannerPickers.jsx", "utf8");
|
|
const equipmentPlannerSummary = await readFile("website/src/features/toolboxes/modules/EquipmentPlannerSummary.jsx", "utf8");
|
|
const equipmentPlannerOptions = await readFile("website/src/features/toolboxes/modules/equipmentPlannerOptions.js", "utf8");
|
|
const checklistModule = await readFile("website/src/features/toolboxes/modules/ChecklistModule.jsx", "utf8");
|
|
const checklistParts = await readFile("website/src/features/toolboxes/modules/ChecklistParts.jsx", "utf8");
|
|
const checklistUtils = await readFile("website/src/features/toolboxes/modules/checklistUtils.js", "utf8");
|
|
const checklistBundle = `${checklistModule}\n${checklistParts}\n${checklistUtils}`;
|
|
const imagesModule = await readFile("website/src/features/toolboxes/modules/ImagesModule.jsx", "utf8");
|
|
const imageAnnotationModule = await readFile("website/src/features/toolboxes/modules/ImageAnnotationModule.jsx", "utf8");
|
|
const linksModule = await readFile("website/src/features/toolboxes/modules/LinksModule.jsx", "utf8");
|
|
const textImportModal = await readFile("website/src/features/toolboxes/modules/TextImportModal.jsx", "utf8");
|
|
const textExchangeActions = await readFile("website/src/features/toolboxes/modules/TextExchangeActions.jsx", "utf8");
|
|
const textExchangeHook = await readFile("website/src/features/toolboxes/modules/useTextExchange.js", "utf8");
|
|
const countersModule = await readFile("website/src/features/toolboxes/modules/CountersModule.jsx", "utf8");
|
|
const textImport = await readFile("website/src/features/toolboxes/modules/textImport.js", "utf8");
|
|
const textImportShared = await readFile("website/src/features/toolboxes/modules/textImport/shared.js", "utf8");
|
|
const textImportPlanning = await readFile("website/src/features/toolboxes/modules/textImport/planning.js", "utf8");
|
|
const textImportTime = await readFile("website/src/features/toolboxes/modules/textImport/time.js", "utf8");
|
|
const imageViewer = await readFile("website/src/components/ImageViewer.jsx", "utf8");
|
|
const randomizerModule = await readFile("website/src/features/toolboxes/modules/RandomizerModule.jsx", "utf8");
|
|
const randomizerDice = await readFile("website/src/features/toolboxes/modules/randomizer/RandomizerDice.jsx", "utf8");
|
|
const randomizerWheel = await readFile("website/src/features/toolboxes/modules/randomizer/RandomizerWheel.jsx", "utf8");
|
|
const randomizerTeams = await readFile("website/src/features/toolboxes/modules/randomizer/RandomizerTeams.jsx", "utf8");
|
|
const randomizerShared = await readFile("website/src/features/toolboxes/modules/randomizer/RandomizerShared.jsx", "utf8");
|
|
const randomizerUiUtils = await readFile("website/src/features/toolboxes/modules/randomizer/randomizerUiUtils.js", "utf8");
|
|
const randomizerBundle = `${randomizerModule}\n${randomizerDice}\n${randomizerWheel}\n${randomizerTeams}\n${randomizerShared}\n${randomizerUiUtils}`;
|
|
|
|
[
|
|
["CalendarModule.jsx", calendarModule],
|
|
["modules/index.jsx", moduleRegistry],
|
|
["GoalTreeParts.jsx", goalTreeParts],
|
|
["RandomizerModule.jsx", randomizerModule]
|
|
].forEach(([fileName, fileSource]) => {
|
|
assert.ok(countLines(fileSource) <= 220, `${fileName} must remain a short orchestrator/barrel`);
|
|
});
|
|
|
|
[
|
|
["AddToolControls.jsx", addToolControls],
|
|
["ToolboxModules.jsx", toolboxModules],
|
|
["useCalendarController.js", calendarController],
|
|
["useCalendarDraftControls.js", calendarDraftControls],
|
|
["calendarEventActions.js", calendarEventActions],
|
|
["useCalendarLifecycle.js", calendarLifecycle],
|
|
["CalendarModeChoice.jsx", calendarModeChoice],
|
|
["GoalTreeBranch.jsx", goalTreeBranch],
|
|
["GoalTreeEditorFields.jsx", goalTreeEditorFields],
|
|
["GoalTreeNodeContent.jsx", goalTreeNodeContent],
|
|
["TimerCountdown.jsx", timerCountdown],
|
|
["TimerSharedControls.jsx", timerSharedControls],
|
|
["TimerStopwatch.jsx", timerStopwatch],
|
|
["TaskPlannerBranch.jsx", taskPlannerBranch],
|
|
["TaskPlannerItem.jsx", taskPlannerItem]
|
|
].forEach(([fileName, fileSource]) => {
|
|
assert.ok(countLines(fileSource) <= 300, `${fileName} must stay focused; split by responsibility before growing further`);
|
|
});
|
|
|
|
assert.match(moduleMetadataRegistry, /export const TOOLBOX_MODULES/);
|
|
assert.match(moduleMetadataRegistry, /export const TOOLBOX_MODULE_DEFINITIONS/);
|
|
assert.match(moduleMetadataRegistry, /export function getDefaultModuleTitle/);
|
|
assert.match(moduleRegistry, /export \{ AddToolControls \}/);
|
|
assert.match(moduleRegistry, /export \{ ToolboxModules \}/);
|
|
assert.match(moduleComponents, /MODULE_COMPONENTS/);
|
|
assert.match(toolboxModules, /Suspense fallback=\{<ModuleLoadingFallback label=\{label\} \/>\}/);
|
|
assert.match(moduleMetadataRegistry, /Planificateur d'équipements/);
|
|
assert.match(moduleMetadataRegistry, /Annotation d'images/);
|
|
assert.match(toolboxModules, /module-edit-button/);
|
|
assert.match(toolboxModules, /itemReorder\.startDrag/);
|
|
assert.match(moduleRegistryBundle, /onPointerDown/);
|
|
assert.match(toolboxModules, /useGroupedReorder/);
|
|
assert.match(toolboxModules, /useInlineEdit/);
|
|
assert.match(toolboxModules, /getContentEditableProps/);
|
|
assert.match(addToolControls, /tool-add-card/);
|
|
assert.match(addToolControls, /tool-quick-add-button/);
|
|
assert.match(addToolControls, /tool-add-quick-toggle/);
|
|
assert.match(moduleMetadataRegistry, /scrollable: true/);
|
|
assert.match(toolboxModules, /module-content/);
|
|
assert.match(toolboxModules, /module-scroll-button/);
|
|
assert.match(addToolControls, /Icon name="dropdown"/);
|
|
assert.match(addToolControls, /CompactDropdown/);
|
|
assert.doesNotMatch(source, /<select value="" onChange=\{\(event\) => addModule/);
|
|
assert.match(notepadModule, /export function NotepadModule/);
|
|
assert.match(notepadModule, /TextExchangeActions/);
|
|
assert.match(notepadModule, /type="notepad"/);
|
|
assert.match(notepadToolbar, /export function NotepadToolbar/);
|
|
assert.match(notepadToolbar, /FormattingColorDropdown/);
|
|
assert.match(notepadToolbar, /ListTypeDropdown/);
|
|
assert.match(notepadUtils, /export function getPlainText/);
|
|
assert.match(combosModule, /export function CombosModule/);
|
|
assert.match(combosUtils, /keyboardMouse/);
|
|
assert.match(combosUtils, /Nintendo 64/);
|
|
assert.match(combosParts, /export function ComboInputToken/);
|
|
assert.match(combosParts, /export function ComboSequence/);
|
|
assert.match(combosBundle, /keyboardLayout/);
|
|
assert.match(combosBundle, /simultaneousMode/);
|
|
assert.match(combosUtils, /SPECIAL_INPUT_GROUP/);
|
|
assert.match(combosUtils, /grab/);
|
|
assert.match(combosUtils, /protect/);
|
|
assert.match(combosUtils, /shoot/);
|
|
assert.match(combosBundle, /holdMs/);
|
|
assert.match(combosBundle, /toggleSelectedInputHold/);
|
|
assert.match(combosEditor, /combo-hold-field/);
|
|
assert.match(combosParts, /useInlineEdit/);
|
|
assert.match(combosBundle, /useGroupedReorder/);
|
|
assert.match(combosBundle, /reorderFeatures/);
|
|
assert.match(combosBundle, /namespace: `combos-\$\{moduleId\}`/);
|
|
assert.match(combosBundle, /getGroupBoundaryProps/);
|
|
assert.match(combosBundle, /combos-category-boundary-drop-zone/);
|
|
assert.match(combosUtils, /function setComboItemCategory/);
|
|
assert.match(combosBundle, /setItemGroup: setComboItemCategory/);
|
|
assert.doesNotMatch(combosModule, /canMoveItem:/);
|
|
assert.doesNotMatch(combosModule, /canMoveGroup:/);
|
|
assert.match(calculatorModule, /export function CalculatorModule/);
|
|
assert.match(calculatorUtils, /export function calculateExpression/);
|
|
assert.match(calculatorModule, /activeParentId/);
|
|
assert.match(calculatorModule, /parentId/);
|
|
assert.match(calculatorModule, /MixTagsInput/);
|
|
assert.match(calculatorModule, /useGroupedReorder/);
|
|
assert.match(moduleMetadataRegistry, /label: "Mémos"/);
|
|
assert.match(moduleMetadataRegistry, /defaultTitle: "Mémos"/);
|
|
assert.match(moduleMetadataRegistry, /label: "Bloc Note"/);
|
|
assert.match(moduleMetadataRegistry, /defaultTitle: "Bloc Note"/);
|
|
assert.match(randomizerModule, /RandomizerDice\.jsx/);
|
|
assert.match(randomizerModule, /RandomizerWheel\.jsx/);
|
|
assert.match(randomizerModule, /RandomizerTeams\.jsx/);
|
|
assert.match(randomizerDice, /export function DiceRandomizer/);
|
|
assert.match(randomizerWheel, /export function WheelRandomizer/);
|
|
assert.match(randomizerTeams, /export function TeamsRandomizer/);
|
|
assert.match(randomizerShared, /export function RandomizerHistory/);
|
|
assert.match(randomizerUiUtils, /export function hasReducedMotion/);
|
|
assert.match(randomizerBundle, /type="randomizer"/);
|
|
assert.match(calculatorEntries, /calculator-drag-handle/);
|
|
assert.match(calculatorModule, /scrollResults/);
|
|
assert.match(calculatorModule, /tool-split-scroll-toggle/);
|
|
assert.match(calculatorModule, /copyChecklistImport/);
|
|
assert.match(calculatorModule, /resetCalculator/);
|
|
assert.match(calculatorModule, /returnToRoot/);
|
|
assert.match(calculatorModule, /createChildEntry/);
|
|
assert.match(calculatorModule, /const childId = context\.uid\("calc"\)/);
|
|
assert.match(calculatorModule, /parentId: entry\.id/);
|
|
assert.match(calculatorModule, /formula: BASE_TOKEN/);
|
|
assert.match(calculatorModule, /pendingExpressionFocusRef/);
|
|
assert.match(calculatorModule, /setExpression\(BASE_TOKEN\)/);
|
|
assert.match(calculatorModule, /editingEntryId/);
|
|
assert.match(calculatorModule, /updateButton/);
|
|
assert.match(calculatorEntries, /onAddChild/);
|
|
assert.match(calculatorEntries, /entry\.label \|\| formatResult\(entry\.value\)/);
|
|
assert.match(calculatorModule, /ResizeObserver/);
|
|
assert.match(calculatorModule, /--calculator-scroll-height/);
|
|
assert.match(calculatorBundle, /CalculatorEntries/);
|
|
assert.match(tableModule, /export function TableModule/);
|
|
assert.match(tableBundle, /Icon name="add-row"/);
|
|
assert.match(tableBundle, /Icon name="add-column"/);
|
|
assert.match(tableBundle, /Icon name="remove-row"/);
|
|
assert.match(tableBundle, /Icon name="remove-column"/);
|
|
assert.match(tableBundle, /insertFormulaReferenceSelection/);
|
|
assert.match(tableBundle, /startFormulaSelection/);
|
|
assert.match(tableBundle, /copyTsv/);
|
|
assert.match(tableBundle, /notepad-toolbar table-toolbar/);
|
|
assert.match(tableBundle, /notepad-toolbar-group/);
|
|
assert.match(tableBundle, /notepad-toolbar-button/);
|
|
assert.match(tableBundle, /selection/);
|
|
assert.match(tableBundle, /draggingSelectionRef/);
|
|
assert.match(tableBundle, /movingSelectionRef/);
|
|
assert.match(tableBundle, /copiedSelectionRef/);
|
|
assert.match(tableUtils, /moveSelectionCells/);
|
|
assert.match(tableUtils, /pasteCellBlock/);
|
|
assert.match(tableUtils, /parseTsvBlock/);
|
|
assert.match(tableGridParts, /is-move-target/);
|
|
assert.match(tableUtils, /isMultiCellSelection/);
|
|
assert.match(tableGrid, /export function TableGrid/);
|
|
assert.match(tableBundle, /handleCellKeyDown/);
|
|
assert.match(tableBundle, /clearSelection/);
|
|
assert.match(tableBundle, /event\.key === "Delete"/);
|
|
assert.match(tableBundle, /ArrowUp/);
|
|
assert.match(tableBundle, /ArrowDown/);
|
|
assert.match(tableBundle, /ArrowLeft/);
|
|
assert.match(tableBundle, /ArrowRight/);
|
|
assert.match(tableGridParts, /export function HeaderLabelInput/);
|
|
assert.match(tableGridParts, /useInlineEdit/);
|
|
assert.match(tableModule, /TextExchangeActions/);
|
|
assert.match(tableGridParts, /event\.target\.select/);
|
|
assert.match(tableFormulaEngine, /export function evaluateTableCell/);
|
|
assert.doesNotMatch(tableFormulaEngine, /Function\(/);
|
|
assert.doesNotMatch(tableFormulaEngine, /\beval\(/);
|
|
assert.match(timerModule, /export function TimerModule/);
|
|
assert.match(timerModule, /Tabs/);
|
|
assert.match(timerModule, /stopwatch/);
|
|
assert.match(timerModule, /countdown/);
|
|
assert.match(timerBundle, /useInlineEdit/);
|
|
assert.match(timerModule, /useDraftForm/);
|
|
assert.match(timerModule, /normalizeTimerData/);
|
|
assert.match(timerModule, /TextExchangeActions/);
|
|
assert.match(calendarModule, /export function CalendarModule/);
|
|
assert.match(calendarModule, /normalizeCalendarData/);
|
|
assert.match(calendarBundle, /calendar-mode-choice/);
|
|
assert.match(calendarBundle, /calendar-week-grid/);
|
|
assert.match(calendarBundle, /calendar-month-grid/);
|
|
assert.match(calendarBundle, /getMonthOccurrenceSegments/);
|
|
assert.match(calendarBundle, /calendar-month-bar/);
|
|
assert.match(calendarBundle, /MonthDateDropdown/);
|
|
assert.match(calendarBundle, /MonthEventRepeatSwitch/);
|
|
assert.match(calendarBundle, /monthlyRepeatBy/);
|
|
assert.match(calendarController, /displayedMonth/);
|
|
assert.match(calendarController, /deleteOccurrence/);
|
|
assert.match(calendarBundle, /onPointerDown/);
|
|
assert.match(calendarController, /copyEvent/);
|
|
assert.match(calendarBundle, /calendar-repeat-switch/);
|
|
assert.match(calendarBundle, /layout-switch calendar-month-repeat-switch/);
|
|
assert.match(calendarBundle, /WEEK_TIME_INPUT_STEP_MINUTES \* 60/);
|
|
assert.match(calendarBundle, /naturalHeight/);
|
|
assert.match(calendarBundle, /data-size=\{eventSurfaceSize\}/);
|
|
assert.match(calendarBundle, /is-compact/);
|
|
assert.match(calendarController, /handleKeyDown/);
|
|
assert.match(calendarContent, /TextExchangeActions/);
|
|
assert.match(calendarContent, /type="calendar"/);
|
|
assert.match(calendarBundle, /repeatLabel/);
|
|
assert.match(calendarBundle, /CalendarAlertModeSwitch/);
|
|
assert.match(calendarDraft, /export function createDefaultDraft/);
|
|
assert.match(calendarDraft, /export function getDraftSelection/);
|
|
assert.match(calendarDraft, /export function normalizeDraftAlertOffsets/);
|
|
assert.doesNotMatch(moduleRegistry, /calendarInitOpen/);
|
|
assert.doesNotMatch(moduleRegistry, /calendar-init-modal/);
|
|
assert.match(calendarUtils, /export function getCalendarOccurrences/);
|
|
assert.match(calendarUtils, /CALENDAR_VIEW_MODES = \["week", "month"\]/);
|
|
assert.match(calendarUtils, /export function getNextCalendarAlertTime/);
|
|
assert.match(taskPlannerModule, /export function TaskPlannerModule/);
|
|
assert.match(taskPlannerModule, /normalizeTaskPlannerData/);
|
|
assert.match(taskPlannerModule, /applyDueResets/);
|
|
assert.match(taskPlannerModule, /TaskPlannerBranch/);
|
|
assert.match(taskPlannerBundle, /useGroupedReorder/);
|
|
assert.match(taskPlannerModule, /useDraftForm/);
|
|
assert.match(taskPlannerBundle, /TextExchangeActions/);
|
|
assert.match(taskPlannerBundle, /reorderFeatures/);
|
|
assert.match(taskPlannerBranch, /export function TaskPlannerBranch/);
|
|
assert.match(taskPlannerItem, /export function TaskPlannerItem/);
|
|
assert.match(taskPlannerBundle, /task-planner-warning/);
|
|
assert.match(taskPlannerBundle, /useInlineEdit/);
|
|
assert.match(taskPlannerBundle, /commitOnEnter: false/);
|
|
assert.match(taskPlannerBundle, /categoryLabel/);
|
|
assert.match(taskPlannerBundle, /prerequisite/);
|
|
assert.match(taskPlannerUtils, /export function getTaskParentMap/);
|
|
assert.match(taskPlannerUtils, /export function applyDueResets/);
|
|
assert.doesNotMatch(taskPlannerModule, /canMoveItem:/);
|
|
assert.doesNotMatch(taskPlannerModule, /canMoveGroup:/);
|
|
assert.match(goalTreeModule, /export function GoalTreeModule/);
|
|
assert.match(goalTreeBundle, /normalizeGoalTreeData/);
|
|
assert.match(goalTreeBundle, /applyGoalTreeDueResets/);
|
|
assert.match(goalTreeModule, /TextExchangeActions/);
|
|
assert.match(goalTreeBundle, /DiagramConnectors/);
|
|
assert.match(goalTreeEditor, /export function GoalTreeEditor/);
|
|
assert.match(goalTreeEditor, /Tabs/);
|
|
assert.match(goalTreeEditor, /GoalTreeFormulaInput/);
|
|
assert.match(goalTreeEditor, /GoalTreePrerequisitePicker/);
|
|
assert.match(goalTreeBranch, /Tooltip/);
|
|
assert.match(goalTreeEditorFields, /export function GoalTreeIconPicker/);
|
|
assert.match(goalTreeEditorFields, /export function GoalTreeContentEditor/);
|
|
assert.match(goalTreeNodeContent, /export function GoalTreeNodeContentPreview/);
|
|
assert.doesNotMatch(goalTreeBundle, /\.slice\(0, 3\)/);
|
|
assert.match(goalTreeNodeContent, /GoalTreeChecklistInlineControl/);
|
|
assert.match(goalTreeNodeContent, /GoalTreeStepLeafControl/);
|
|
assert.match(goalTreeEditorFields, /export function GoalTreePrerequisitePicker/);
|
|
assert.match(goalTreeBundle, /function toggleBranch/);
|
|
assert.match(goalTreeBundle, /function editNode/);
|
|
assert.match(goalTreeBundle, /function addNodeContentItem/);
|
|
assert.match(goalTreeBundle, /closedSiblingIds/);
|
|
assert.match(goalTreeBundle, /useGroupedReorder/);
|
|
assert.match(goalTreeBundle, /const treeOrientation = data\.orientation \|\| "horizontal"/);
|
|
assert.match(goalTreeBundle, /const reorderOrientation = treeOrientation === "horizontal" \? "vertical" : "horizontal"/);
|
|
assert.match(goalTreeBundle, /orientation: reorderOrientation/);
|
|
assert.match(goalTreeView, /orientation={treeOrientation}/);
|
|
assert.match(goalTreeView, /module-scroll-button goal-tree-orientation-switch/);
|
|
assert.match(goalTreeView, /Icon name="rotate"/);
|
|
assert.match(goalTreeBundle, /function toggleLinkedParent/);
|
|
assert.match(goalTreeDisplay, /grouped: false/);
|
|
assert.match(goalTreeBundle, /getGoalTreeConvergenceKeepOpenIds/);
|
|
assert.match(goalTreeDisplay, /progress\.current/);
|
|
assert.match(goalTreeDisplay, /label: textContent\.itemLabelPlaceholder \|\| "Élément"/);
|
|
assert.doesNotMatch(goalTreeUtils, /"calculator"/);
|
|
assert.match(goalTreeBranch, /getItemProps\(\{ itemId: node\.id, parentId \}\)/);
|
|
assert.match(goalTreeUtils, /export function calculateGoalTreeFormula/);
|
|
assert.match(goalTreeUtils, /export function evaluateGoalTreeNodes/);
|
|
assert.match(equipmentPlannerModule, /export function EquipmentPlannerModule/);
|
|
assert.match(equipmentPlannerModule, /normalizeEquipmentPlannerData/);
|
|
assert.match(equipmentPlannerModule, /summarizeEquipmentPlannerData/);
|
|
assert.match(equipmentPlannerModule, /EquipmentItem/);
|
|
assert.match(equipmentPlannerModule, /EquipmentPlannerSummary/);
|
|
assert.match(equipmentPlannerBundle, /socketItems/);
|
|
assert.match(equipmentPlannerSockets, /socketLinks/);
|
|
assert.match(equipmentPlannerModule, /useGroupedReorder/);
|
|
assert.match(equipmentPlannerCharacteristics, /reorderFeatures/);
|
|
assert.match(equipmentPlannerEditors, /export \{ CharacteristicsEditor \}/);
|
|
assert.match(equipmentPlannerEditors, /export \{ MaterialsEditor \}/);
|
|
assert.match(equipmentPlannerEditors, /export \{ SocketItemsEditor \}/);
|
|
assert.match(equipmentPlannerModule, /TextExchangeActions/);
|
|
assert.match(equipmentPlannerItem, /export function EquipmentItem/);
|
|
assert.match(equipmentPlannerPickers, /export function IconChoiceDropdown/);
|
|
assert.match(equipmentPlannerSummary, /export function EquipmentPlannerSummary/);
|
|
assert.match(equipmentPlannerOptions, /export const EQUIPMENT_ICONS/);
|
|
assert.match(equipmentPlannerOptions, /export function formatValue/);
|
|
assert.match(checklistModule, /export function ChecklistModule/);
|
|
assert.match(checklistModule, /TextExchangeActions/);
|
|
assert.match(checklistModule, /editing &&/);
|
|
assert.match(checklistParts, /ChecklistItem/);
|
|
assert.match(checklistParts, /export function ChecklistSection/);
|
|
assert.match(checklistParts, /chevron-down/);
|
|
assert.match(checklistParts, /chevron-up/);
|
|
assert.match(checklistParts, /checklist-qty-current/);
|
|
assert.match(checklistParts, /checklist-delete-button nds-icon-button/);
|
|
assert.match(checklistModule, /useGroupedReorder/);
|
|
assert.match(checklistBundle, /getItemProps/);
|
|
assert.match(checklistParts, /getGroupProps/);
|
|
assert.match(checklistModule, /getGroupBoundaryProps/);
|
|
assert.match(checklistModule, /checklist-category-boundary-drop-zone/);
|
|
assert.match(checklistParts, /itemReorder\.startDrag/);
|
|
assert.match(checklistParts, /groupReorder\.startDrag/);
|
|
assert.match(checklistParts, /useInlineEdit/);
|
|
assert.match(checklistUtils, /export function moveChecklistItemToCategory/);
|
|
assert.match(textImportModal, /createPortal/);
|
|
assert.match(textImportModal, /lockBodyScroll/);
|
|
assert.match(textImportModal, /has-error/);
|
|
assert.match(textExchangeActions, /export function TextExchangeActions/);
|
|
assert.match(textExchangeActions, /Icon name="import"/);
|
|
assert.match(textExchangeActions, /Icon name="export"/);
|
|
assert.match(textExchangeActions, /useTextExchange/);
|
|
assert.match(textExchangeHook, /export function useTextExchange/);
|
|
assert.match(textExchangeHook, /importModuleText/);
|
|
assert.match(textExchangeHook, /exportModuleText/);
|
|
assert.match(imagesModule, /export function ImagesModule/);
|
|
assert.match(imagesModule, /useGroupedReorder/);
|
|
assert.match(imagesModule, /orientation: "horizontal"/);
|
|
assert.match(imagesModule, /editing &&/);
|
|
assert.match(imagesModule, /clipboardData/);
|
|
assert.match(imagesModule, /createImageAnnotationModule/);
|
|
assert.match(imagesModule, /TextExchangeActions/);
|
|
assert.match(imagesModule, /image-annotate-button/);
|
|
assert.match(imagesModule, /dataTransfer\.files/);
|
|
assert.match(imageAnnotationModule, /export function ImageAnnotationModule/);
|
|
assert.match(imageAnnotationModule, /annotation-marker/);
|
|
assert.match(imageAnnotationModule, /setImageFromFiles/);
|
|
assert.match(imageAnnotationModule, /context\.compressImageFile/);
|
|
assert.match(imageAnnotationModule, /context\.setImage/);
|
|
assert.match(imageAnnotationModule, /createMarkerId/);
|
|
assert.match(imageAnnotationModule, /<Icon name="zoom"/);
|
|
assert.match(imageAnnotationModule, /TextExchangeActions/);
|
|
assert.match(linksModule, /export function LinksModule/);
|
|
assert.match(linksModule, /TextExchangeActions/);
|
|
assert.match(linksModule, /editing &&/);
|
|
assert.match(linksModule, /normalizeUrl/);
|
|
assert.match(linksModule, /copyText/);
|
|
assert.match(linksModule, /useGroupedReorder/);
|
|
assert.match(linksModule, /useDraftForm/);
|
|
assert.match(linksModule, /linkDraft\.getFieldProps/);
|
|
assert.match(linksModule, /link-drag-handle/);
|
|
assert.match(countersModule, /export function CountersModule/);
|
|
assert.match(countersModule, /editing &&/);
|
|
assert.match(countersModule, /counter-actions/);
|
|
assert.match(countersModule, /useGroupedReorder/);
|
|
assert.match(countersModule, /useDraftForm/);
|
|
assert.match(countersModule, /counterDraft\.getFieldProps/);
|
|
assert.match(countersModule, /orientation: "horizontal"/);
|
|
assert.match(countersModule, /counter-drag-handle/);
|
|
assert.match(countersModule, /TextExchangeActions/);
|
|
assert.match(timerModule, /useGroupedReorder/);
|
|
assert.match(timerModule, /reorderEnabled={!data\.sortResults}/);
|
|
assert.match(timerModule, /parentId=\{moduleId\}/);
|
|
assert.match(timerCountdown, /timer-drag-handle/);
|
|
assert.match(timerStopwatch, /export function StopwatchControls/);
|
|
assert.match(timerCountdown, /export function CountdownControls/);
|
|
assert.match(timerCountdown, /export function CountdownList/);
|
|
assert.match(timerOptions, /export const ALERT_MIN_DELAY_MS/);
|
|
assert.match(textImport, /export \{ parseColonImportLines \}/);
|
|
assert.match(textImport, /export function exportModuleText/);
|
|
assert.match(textImport, /export function importModuleText/);
|
|
assert.match(textImport, /export function hasTextExchangeContent/);
|
|
assert.match(textImport, /TEXT_EXCHANGE_HANDLERS/);
|
|
assert.match(textImportShared, /export function parseColonImportLines/);
|
|
assert.match(textImportShared, /line\.indexOf\(":"\)/);
|
|
assert.match(textImportTime, /export function exportCalendarText/);
|
|
assert.match(textImportTime, /export function importCalendarText/);
|
|
assert.match(textImportPlanning, /export function exportGoalTreeText/);
|
|
assert.match(textImportPlanning, /export function importGoalTreeText/);
|
|
assert.match(textImportTime, /CALENDAR_WEEKDAYS/);
|
|
assert.match(imageViewer, /image-viewer-media/);
|
|
assert.match(imageViewer, /image-viewer-image-frame/);
|
|
assert.match(imageViewer, /image-viewer-marker/);
|
|
assert.match(imageViewer, /function openImageInNewTab/);
|
|
assert.match(imageViewer, /!hasMarkers &&/);
|
|
assert.match(imageViewer, /<Icon name="zoom"/);
|
|
assert.match(imageViewer, /annotation-marker-list themed-scrollbar/);
|
|
});
|
|
|
|
test("task planner helpers keep hierarchy, reorder and reset behavior stable", () => {
|
|
const tasks = [
|
|
{ id: "a", title: "A", type: "daily", checked: true, checkedAt: Date.UTC(2026, 0, 1, 7) },
|
|
{ id: "b", title: "B", type: "unique", checked: false },
|
|
{ id: "c", title: "C", type: "weekly", checked: true, checkedAt: Date.UTC(2026, 0, 1, 7) }
|
|
];
|
|
const relations = [{ id: "r1", fromTaskId: "b", toTaskId: "a", prerequisite: false }];
|
|
const parentMap = getTaskParentMap(tasks, relations);
|
|
|
|
assert.equal(parentMap.get("b"), "a");
|
|
assert.deepEqual(getTaskTreeIds(["a"], tasks, parentMap), ["a", "b"]);
|
|
assert.deepEqual(moveTask(tasks, "c", "a").map((task) => task.id), ["c", "a", "b"]);
|
|
|
|
const resetData = applyDueResets({
|
|
tasks,
|
|
relations,
|
|
resetTime: "06:00",
|
|
weeklyResetDay: 4,
|
|
categoryOrder: [],
|
|
lastResetAt: Date.UTC(2026, 0, 1, 5)
|
|
}, Date.UTC(2026, 0, 2, 8));
|
|
assert.equal(resetData.tasks.find((task) => task.id === "a").checked, false);
|
|
assert.equal(isTaskResetSoon({ id: "d", title: "D", type: "daily", checked: false }, resetData, new Date(2026, 0, 2, 0, 1).getTime()), true);
|
|
assert.equal(isTaskResetSoon({ id: "e", title: "E", type: "daily", checked: false }, resetData, new Date(2026, 0, 1, 20, 0).getTime()), false);
|
|
assert.equal(isTaskResetSoon({ id: "f", title: "F", type: "weekly", checked: false, weeklyResetDay: 4 }, resetData, new Date(2026, 0, 6, 7, 0).getTime()), true);
|
|
assert.equal(isTaskResetSoon({ id: "g", title: "G", type: "weekly", checked: true, weeklyResetDay: 4 }, resetData, new Date(2026, 0, 6, 7, 0).getTime()), false);
|
|
assert.equal(isTaskResetSoon({ id: "h", title: "H", type: "unique", checked: false }, resetData, new Date(2026, 0, 6, 7, 0).getTime()), false);
|
|
});
|
|
|
|
test("equipment planner options expose stable formatting and socket helpers", () => {
|
|
assert.deepEqual(SOCKET_SHAPES, ["ball", "jewel"]);
|
|
assert.equal(formatValue(1.23456), "1.235");
|
|
assert.deepEqual(setItemCategory({ id: "item" }, "DPS"), { id: "item", category: "DPS" });
|
|
assert.deepEqual(setItemCategory({ id: "item", category: "DPS" }, ""), { id: "item" });
|
|
assert.equal(getSocketItemName({ socketItems: [{ id: "gem", name: "Ruby" }] }, "gem"), "Ruby");
|
|
});
|
|
|
|
test("combos helpers keep labels, hold parsing and custom keys stable", () => {
|
|
assert.deepEqual(createInput("button", "a", 1500), { kind: "button", value: "a", holdMs: 1500 });
|
|
assert.deepEqual(createInput("button", "a"), { kind: "button", value: "a" });
|
|
assert.equal(getInputLabel({ kind: "key", value: "q" }), "Q");
|
|
assert.equal(getInputLabel({ kind: "mouse", value: "left" }), "Clic G");
|
|
assert.equal(parseHoldSeconds("120s"), 99);
|
|
assert.equal(parseHoldSeconds("abc"), 0);
|
|
assert.equal(normalizeCustomKey(" Ctrl Alt Shift "), "Ctrl Alt Shift");
|
|
});
|
|
|
|
test("table helpers keep selection, movement and TSV behavior stable", () => {
|
|
const selection = {
|
|
anchor: { rowIndex: 2, columnIndex: 3 },
|
|
focus: { rowIndex: 1, columnIndex: 1 }
|
|
};
|
|
assert.deepEqual(getSelectionBounds(selection), {
|
|
startRow: 1,
|
|
endRow: 2,
|
|
startColumn: 1,
|
|
endColumn: 3
|
|
});
|
|
assert.deepEqual(parseTsvBlock("A\tB\nC\tD"), {
|
|
height: 2,
|
|
width: 2,
|
|
values: [["A", "B"], ["C", "D"]]
|
|
});
|
|
|
|
const moved = moveSelectionCells({ A1: "a", B1: "b", A2: "c" }, getBoundsFromStartAndSize(0, 0, 1, 2), getBoundsFromStartAndSize(1, 1, 1, 2));
|
|
assert.deepEqual(moved, { A2: "c", B2: "a", C2: "b" });
|
|
|
|
const pasted = pasteCellBlock({}, parseTsvBlock("A\t\nC\tD"), 0, 0, 2, 2);
|
|
assert.deepEqual(pasted.cells, { A1: "A", A2: "C", B2: "D" });
|
|
});
|
|
|
|
test("timer options keep alert guards and countdown validation stable", () => {
|
|
assert.equal(isAlertGuarded({ type: "interval", intervalMs: 60_000 }), true);
|
|
assert.equal(isAlertGuarded({ type: "interval", intervalMs: 600_000 }), false);
|
|
assert.equal(canToggleAutoRefresh({ type: "daily_time" }), true);
|
|
assert.equal(canToggleAutoRefresh({ type: "interval", intervalMs: 60_000 }), false);
|
|
assert.equal(getCountdownValidationError("duration", { duration: { hours: "", minutes: "", seconds: "" } }, {}), "Valeur requise.");
|
|
assert.equal(getCountdownValidationError("daily_time", { time: { hours: "12", minutes: "30", seconds: "" } }, {}), "");
|
|
});
|
|
|
|
test("calendar helpers keep normalization, occurrences and alerts stable", () => {
|
|
const data = normalizeCalendarData({
|
|
events: [
|
|
{
|
|
id: "h1",
|
|
title: "Raid",
|
|
dayIndex: 4,
|
|
startMinutes: 1080,
|
|
endMinutes: 1200,
|
|
repeatable: false,
|
|
alertMode: "site",
|
|
alertOffsetMinutes: [10, 5, 0],
|
|
color: "gold"
|
|
},
|
|
{ title: "", dayIndex: 9, startMinutes: -1 }
|
|
]
|
|
});
|
|
assert.equal(data.events.length, 1);
|
|
assert.equal(data.initialized, true);
|
|
assert.equal(data.events[0].repeat, "none");
|
|
assert.equal(data.events[0].repeatable, false);
|
|
const compactCalendar = compactCalendarDataForStorage(data);
|
|
assert.equal(compactCalendar.initialized, true);
|
|
assert.deepEqual(compactCalendar.events[0], {
|
|
id: "h1",
|
|
title: "Raid",
|
|
dayIndex: 4,
|
|
startMinutes: 1080,
|
|
endMinutes: 1200,
|
|
repeatable: false,
|
|
alertMode: "site",
|
|
alertOffsetMinutes: [10, 5, 0],
|
|
color: "gold"
|
|
});
|
|
|
|
const weekStart = getWeekStart(new Date("2026-08-17T12:00:00.000Z"), 1);
|
|
const occurrences = getCalendarOccurrences(data.events, weekStart, new Date(weekStart.getTime() + 7 * 24 * 60 * 60 * 1000));
|
|
assert.equal(occurrences.length, 1);
|
|
assert.equal(new Date(occurrences[0].startMs).getHours(), 18);
|
|
assert.equal(getNextCalendarAlertTime(data.events[0], new Date(2026, 7, 14, 17, 49).getTime()), new Date(2026, 7, 14, 17, 50).getTime());
|
|
assert.equal(getCurrentCalendarOccurrence(data.events[0], new Date(2026, 7, 14, 19, 0).getTime()).event.id, "h1");
|
|
|
|
const monthly = normalizeCalendarData({
|
|
viewMode: "month",
|
|
events: [
|
|
{ id: "h2", title: "Prime", startDate: "2026-08-14", endDate: "2026-08-16", repeat: "monthly", alertMode: "visible", alertOffsetMinutes: [0] },
|
|
{ id: "h3", title: "Premier lundi", startDate: "2026-08-03", endDate: "2026-08-05", repeat: "monthly", monthlyRepeatBy: "weekday" },
|
|
{ id: "h6", title: "Hebdo", startDate: "2026-08-03", endDate: "2026-08-05", repeat: "weekly", monthlyRepeatBy: "weekday", excludedOccurrences: ["2026-08-10"] },
|
|
{ id: "bad", title: "Sans cible", startMinutes: 720 }
|
|
]
|
|
});
|
|
assert.equal(monthly.viewMode, "month");
|
|
assert.equal(monthly.events.length, 3);
|
|
assert.equal(monthly.events[0].repeat, "monthly");
|
|
assert.equal(monthly.events[0].monthlyRepeatBy, "monthDay");
|
|
assert.equal(monthly.events[1].monthlyRepeatBy, "weekday");
|
|
assert.equal(monthly.events[2].repeat, "weekly");
|
|
const compactMonthly = compactCalendarDataForStorage(monthly);
|
|
assert.equal(compactMonthly.viewMode, "month");
|
|
assert.equal(compactMonthly.events[0].startDate, "2026-08-14");
|
|
assert.equal(compactMonthly.events[0].endDate, "2026-08-16");
|
|
assert.equal("startMinutes" in compactMonthly.events[0], false);
|
|
assert.equal(compactMonthly.events[1].monthlyRepeatBy, "weekday");
|
|
assert.equal(compactMonthly.events[2].repeat, "weekly");
|
|
assert.deepEqual(compactMonthly.events[2].excludedOccurrences, ["2026-08-10"]);
|
|
|
|
const augustStart = getMonthStart(new Date(2026, 7, 14));
|
|
const augustOccurrences = getCalendarOccurrences(monthly.events, augustStart, getMonthEnd(augustStart));
|
|
assert.equal(augustOccurrences.filter((occurrence) => occurrence.event.id === "h2").length, 1);
|
|
assert.equal(augustOccurrences.filter((occurrence) => occurrence.event.id === "h3").length, 1);
|
|
assert.equal(augustOccurrences.filter((occurrence) => occurrence.event.id === "h6").length, 4);
|
|
assert.equal(new Date(augustOccurrences.find((occurrence) => occurrence.event.id === "h2").endMs).getDate(), 17);
|
|
assert.equal(getNextCalendarAlertTime(monthly.events[0], new Date(2026, 7, 13, 23, 59).getTime()), new Date(2026, 7, 14, 0, 0).getTime());
|
|
|
|
const thirtyFirst = normalizeCalendarData({
|
|
viewMode: "month",
|
|
events: [{ id: "h4", title: "31", startDate: "2026-08-31", repeat: "monthly" }]
|
|
});
|
|
const septemberStart = getMonthStart(new Date(2026, 8, 1));
|
|
assert.equal(getCalendarOccurrences(thirtyFirst.events, septemberStart, getMonthEnd(septemberStart)).length, 0);
|
|
});
|
|
|
|
test("calendar text exchange imports and exports weekly and monthly calendars", () => {
|
|
let uidIndex = 0;
|
|
const context = {
|
|
uid: (prefix) => `${prefix}${uidIndex += 1}`,
|
|
normalizeCalendarData
|
|
};
|
|
const weekly = importModuleText("calendar", "@viewMode: week\nevent | title=Raid | day=vendredi | start=18:00 | end=20:00 | color=gold | alertMode=site | alerts=10,5", context);
|
|
assert.equal(weekly.ok, true);
|
|
assert.equal(weekly.data.viewMode, "week");
|
|
assert.equal(weekly.data.events[0].dayIndex, 4);
|
|
assert.equal(weekly.data.events[0].startMinutes, 1080);
|
|
assert.deepEqual(weekly.data.events[0].alertOffsetMinutes, [10, 5]);
|
|
assert.match(exportModuleText("calendar", weekly.data), /day=vendredi/);
|
|
|
|
const monthly = importModuleText("calendar", "@viewMode: month\nevent | title=Maintenance | repeatBy=weekday | repeat=monthly | startDate=2026-08-03 | endDate=2026-08-05 | color=pink\nevent | title=Event | repeatBy=monthDay | repeat=none | startDate=2026-08-20 | endDate=2026-08-27", context);
|
|
assert.equal(monthly.ok, true);
|
|
assert.equal(monthly.data.viewMode, "month");
|
|
assert.equal(monthly.data.events[0].monthlyRepeatBy, "weekday");
|
|
assert.equal(monthly.data.events[0].startDate, "2026-08-03");
|
|
assert.equal(monthly.data.events[0].repeat, "monthly");
|
|
assert.equal(monthly.data.events[1].startDate, "2026-08-20");
|
|
assert.equal(monthly.data.events[1].repeat, "none");
|
|
assert.match(exportModuleText("calendar", monthly.data), /startDate=2026-08-03/);
|
|
assert.match(exportModuleText("calendar", monthly.data), /repeatBy=monthDay/);
|
|
});
|