This commit is contained in:
parent
896ae23ff2
commit
025cd4bf22
19 changed files with 1336 additions and 1 deletions
|
|
@ -176,6 +176,23 @@ export function validateSiteContent(site) {
|
|||
"toolboxes.modules.calculator.useEntryTitle",
|
||||
"toolboxes.modules.calculator.renameTitle",
|
||||
"toolboxes.modules.calculator.deleteTitle",
|
||||
"toolboxes.modules.table.toolbarLabel",
|
||||
"toolboxes.modules.table.gridLabel",
|
||||
"toolboxes.modules.table.cellLabel",
|
||||
"toolboxes.modules.table.rowActionsLabel",
|
||||
"toolboxes.modules.table.columnActionsLabel",
|
||||
"toolboxes.modules.table.sizeLabel",
|
||||
"toolboxes.modules.table.addRowTitle",
|
||||
"toolboxes.modules.table.removeRowTitle",
|
||||
"toolboxes.modules.table.addColumnTitle",
|
||||
"toolboxes.modules.table.removeColumnTitle",
|
||||
"toolboxes.modules.table.copyTitle",
|
||||
"toolboxes.modules.table.copiedTitle",
|
||||
"toolboxes.modules.table.errorTitle",
|
||||
"toolboxes.modules.table.columnLabel",
|
||||
"toolboxes.modules.table.rowLabel",
|
||||
"toolboxes.modules.table.columnFormulaTitle",
|
||||
"toolboxes.modules.table.rowFormulaTitle",
|
||||
"toolboxes.modules.timer.stopwatchTab",
|
||||
"toolboxes.modules.timer.countdownTab",
|
||||
"toolboxes.modules.timer.stopwatchTitle",
|
||||
|
|
|
|||
|
|
@ -44,7 +44,12 @@ test("style entrypoint and theme layers are wired", async () => {
|
|||
assert.match(styleShell, /\.sidebar-about-link/);
|
||||
assert.match(styleToolboxes, /\.module-icon-abacus/);
|
||||
assert.match(styleToolboxes, /\.module-icon-calculator/);
|
||||
assert.match(styleToolboxes, /\.module-icon-columns/);
|
||||
assert.match(styleToolboxes, /\.module-icon-table/);
|
||||
assert.match(styleToolboxes, /\.calculator-module/);
|
||||
assert.match(styleToolboxes, /\.table-module/);
|
||||
assert.match(styleToolboxes, /\.table-cell-input\.is-selected/);
|
||||
assert.match(styleToolboxes, /\.notepad-toolbar-button\.danger/);
|
||||
assert.match(styleToolboxes, /\.toolbox-icon-picker/);
|
||||
assert.match(styleToolboxes, /\.toolbox-game-icon/);
|
||||
assert.match(styleToolboxes, /\.toolbox-hero-link/);
|
||||
|
|
@ -64,6 +69,11 @@ test("style entrypoint and theme layers are wired", async () => {
|
|||
assert.match(styleIcons, /ui-icon-chevron-up/);
|
||||
assert.match(styleIcons, /ui-icon-flip/);
|
||||
assert.match(styleIcons, /ui-icon-add/);
|
||||
assert.match(styleIcons, /ui-icon-add-row/);
|
||||
assert.match(styleIcons, /ui-icon-add-column/);
|
||||
assert.match(styleIcons, /ui-icon-remove-row/);
|
||||
assert.match(styleIcons, /ui-icon-remove-column/);
|
||||
assert.match(styleIcons, /ui-icon-table/);
|
||||
assert.match(styleIcons, /ui-icon-map/);
|
||||
assert.match(styleGames, /\.game-home-card/);
|
||||
assert.match(styleGames, /\.game-layout/);
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ test("toolbox storage, cards and pages are wired", async () => {
|
|||
assert.match(toolboxStorage, /export function normalizeLinksData/);
|
||||
assert.match(toolboxStorage, /export function normalizeCountersData/);
|
||||
assert.match(toolboxStorage, /export function normalizeCalculatorData/);
|
||||
assert.match(toolboxStorage, /export function normalizeTableData/);
|
||||
assert.match(toolboxStorage, /export function normalizeTimerData/);
|
||||
assert.match(toolboxStorage, /export function normalizeTaskPlannerData/);
|
||||
assert.match(toolboxStorage, /export function normalizeImageAnnotationData/);
|
||||
|
|
@ -77,6 +78,8 @@ test("toolbox module registry and modules expose expected behavior", async () =>
|
|||
const moduleRegistry = await readFile("website/src/features/toolboxes/modules/index.jsx", "utf8");
|
||||
const notepadModule = await readFile("website/src/features/toolboxes/modules/NotepadModule.jsx", "utf8");
|
||||
const calculatorModule = await readFile("website/src/features/toolboxes/modules/CalculatorModule.jsx", "utf8");
|
||||
const tableModule = await readFile("website/src/features/toolboxes/modules/TableModule.jsx", "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 taskPlannerModule = await readFile("website/src/features/toolboxes/modules/TaskPlannerModule.jsx", "utf8");
|
||||
const checklistModule = await readFile("website/src/features/toolboxes/modules/ChecklistModule.jsx", "utf8");
|
||||
|
|
@ -98,6 +101,8 @@ test("toolbox module registry and modules expose expected behavior", async () =>
|
|||
assert.match(moduleRegistry, /links:/);
|
||||
assert.match(moduleRegistry, /counters:/);
|
||||
assert.match(moduleRegistry, /calculator:/);
|
||||
assert.match(moduleRegistry, /table:/);
|
||||
assert.match(moduleRegistry, /table: \{ label: "Tableau", icon: "table", Component: TableModule, editable: false, scrollable: true \}/);
|
||||
assert.match(moduleRegistry, /timer:/);
|
||||
assert.match(moduleRegistry, /taskPlanner:/);
|
||||
assert.match(moduleRegistry, /imageAnnotation:/);
|
||||
|
|
@ -132,6 +137,32 @@ test("toolbox module registry and modules expose expected behavior", async () =>
|
|||
assert.match(calculatorModule, /ResizeObserver/);
|
||||
assert.match(calculatorModule, /--calculator-scroll-height/);
|
||||
assert.match(calculatorModule, /CalculatorEntries/);
|
||||
assert.match(tableModule, /export function TableModule/);
|
||||
assert.match(tableModule, /Icon name="add-row"/);
|
||||
assert.match(tableModule, /Icon name="add-column"/);
|
||||
assert.match(tableModule, /Icon name="remove-row"/);
|
||||
assert.match(tableModule, /Icon name="remove-column"/);
|
||||
assert.match(tableModule, /insertFormulaReferenceSelection/);
|
||||
assert.match(tableModule, /startFormulaSelection/);
|
||||
assert.match(tableModule, /copyTsv/);
|
||||
assert.match(tableModule, /notepad-toolbar table-toolbar/);
|
||||
assert.match(tableModule, /notepad-toolbar-group/);
|
||||
assert.match(tableModule, /notepad-toolbar-button/);
|
||||
assert.match(tableModule, /selection/);
|
||||
assert.match(tableModule, /draggingSelectionRef/);
|
||||
assert.match(tableModule, /isMultiCellSelection/);
|
||||
assert.match(tableModule, /handleCellKeyDown/);
|
||||
assert.match(tableModule, /clearSelection/);
|
||||
assert.match(tableModule, /event\.key === "Delete"/);
|
||||
assert.match(tableModule, /ArrowUp/);
|
||||
assert.match(tableModule, /ArrowDown/);
|
||||
assert.match(tableModule, /ArrowLeft/);
|
||||
assert.match(tableModule, /ArrowRight/);
|
||||
assert.match(tableModule, /function HeaderLabelInput/);
|
||||
assert.match(tableModule, /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/);
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
// Rôle : teste les parseurs d'import texte utilisés par les outils toolbox.
|
||||
import { test } from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import { evaluateTableCell } from "../website/src/features/toolboxes/modules/tableFormulaEngine.js";
|
||||
import { parseColonImportLines } from "../website/src/features/toolboxes/modules/textImport.js";
|
||||
import { getTimePatternRecurrenceMs, getTimePatternTargetMs } from "../website/src/features/toolboxes/modules/timerUtils.js";
|
||||
import { compactModuleDataForStorage, createToolboxExportPayload, normalizeNotepadData, normalizeTaskPlannerData } from "../website/src/features/toolboxes/storage/toolboxStorage.js";
|
||||
import { compactModuleDataForStorage, createToolboxExportPayload, normalizeNotepadData, normalizeTableData, normalizeTaskPlannerData } from "../website/src/features/toolboxes/storage/toolboxStorage.js";
|
||||
|
||||
test("colon text import keeps urls intact after the first separator", () => {
|
||||
assert.deepEqual(parseColonImportLines("Build:https://example.com/build?class=rogue\nPotion:10"), [
|
||||
|
|
@ -28,6 +29,72 @@ test("time pattern recurrence uses configured frequency instead of next remainin
|
|||
assert.equal(getTimePatternRecurrenceMs("X:X:30"), 60 * 1000);
|
||||
});
|
||||
|
||||
test("table formulas evaluate arithmetic, parentheses and cell references", () => {
|
||||
const cells = {
|
||||
A1: "100",
|
||||
A2: "20",
|
||||
B1: "=A1+A2*2",
|
||||
B2: "=(A1+A2)/2",
|
||||
C1: "=B1-B2/3"
|
||||
};
|
||||
const dimensions = { rows: 10, columns: 6 };
|
||||
|
||||
assert.equal(evaluateTableCell("B1", cells, dimensions).display, "140");
|
||||
assert.equal(evaluateTableCell("B2", cells, dimensions).display, "60");
|
||||
assert.equal(evaluateTableCell("C1", cells, dimensions).display, "120");
|
||||
});
|
||||
|
||||
test("table formulas report invalid references, division by zero and circular references", () => {
|
||||
const dimensions = { rows: 10, columns: 6 };
|
||||
|
||||
assert.equal(evaluateTableCell("A1", { A1: "=Z99+1" }, dimensions).error, "reference");
|
||||
assert.equal(evaluateTableCell("A1", { A1: "=10/0" }, dimensions).error, "division");
|
||||
assert.equal(evaluateTableCell("A1", { A1: "=B1", B1: "=A1" }, dimensions).error, "cycle");
|
||||
assert.deepEqual(evaluateTableCell("A1", { A1: "texte", B1: "=A1+1" }, dimensions), { value: null, display: "texte", error: "" });
|
||||
assert.equal(evaluateTableCell("B1", { A1: "texte", B1: "=A1+1" }, dimensions).error, "reference");
|
||||
});
|
||||
|
||||
test("table storage clamps dimensions and compacts non-empty cells", () => {
|
||||
const normalized = normalizeTableData({
|
||||
rows: 200,
|
||||
columns: 40,
|
||||
cells: {
|
||||
A1: "Boss",
|
||||
T50: "=A1+1",
|
||||
U51: "Ignored",
|
||||
B1: " "
|
||||
},
|
||||
rowLabels: {
|
||||
0: "Boss final ",
|
||||
1: "2",
|
||||
50: "Ignored"
|
||||
},
|
||||
columnLabels: {
|
||||
0: "Item final ",
|
||||
1: "B",
|
||||
20: "Ignored"
|
||||
}
|
||||
});
|
||||
|
||||
assert.equal(normalized.rows, 50);
|
||||
assert.equal(normalized.columns, 20);
|
||||
assert.deepEqual(normalized.cells, { A1: "Boss", T50: "=A1+1" });
|
||||
assert.deepEqual(normalized.rowLabels, { 0: "Boss final " });
|
||||
assert.deepEqual(normalized.columnLabels, { 0: "Item final " });
|
||||
|
||||
const compact = compactModuleDataForStorage("table", normalized);
|
||||
assert.equal(compact.rows, 50);
|
||||
assert.equal(compact.columns, 20);
|
||||
assert.deepEqual(compact.cells, { A1: "Boss", T50: "=A1+1" });
|
||||
assert.deepEqual(compact.rowLabels, { 0: "Boss final " });
|
||||
assert.deepEqual(compact.columnLabels, { 0: "Item final " });
|
||||
assert.deepEqual(compactModuleDataForStorage("table", { rows: 10, columns: 6, cells: {}, columnLabels: { 0: "Item " } }), {
|
||||
cells: {},
|
||||
columnLabels: { 0: "Item " }
|
||||
});
|
||||
assert.equal(compactModuleDataForStorage("table", { rows: 10, columns: 6, cells: {} }), null);
|
||||
});
|
||||
|
||||
test("notepad data migrates text, sanitizes html and compacts drawings", () => {
|
||||
const migrated = normalizeNotepadData({ text: "Boss\nPhase 2" });
|
||||
assert.equal(migrated.text, "Boss\nPhase 2");
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue