diff --git a/packages/doenetml-worker/__mocks__/hyperformula.ts b/packages/doenetml-worker/__mocks__/hyperformula.ts new file mode 100644 index 000000000..59bbfd81e --- /dev/null +++ b/packages/doenetml-worker/__mocks__/hyperformula.ts @@ -0,0 +1,7 @@ +import { vi } from "vitest"; + +/** Mock the call to hyperformula that occurs inside the evaluatedCells of spreadsheet. + * The evaluatedCells variable will always be empty, but at least tests can run.*/ +export const HyperFormula = { + buildFromArray: vi.fn(() => ({ getSheetValues: vi.fn() })), +}; diff --git a/packages/doenetml-worker/src/Core.js b/packages/doenetml-worker/src/Core.js index 5783e3930..50ffcbb41 100644 --- a/packages/doenetml-worker/src/Core.js +++ b/packages/doenetml-worker/src/Core.js @@ -12068,7 +12068,7 @@ export default class Core { } if (stateVarObj.additionalStateVariablesDefined) { - // combine workspaces of additional state varibles into one + // combine workspaces of additional state variables into one for (let varName2 of stateVarObj.additionalStateVariablesDefined) { let stateVariableForWorkspace2 = varName2; let stateVarObj2 = component.state[varName2]; diff --git a/packages/doenetml-worker/src/components/BooleanList.js b/packages/doenetml-worker/src/components/BooleanList.js index da07a5c68..09f5db3d7 100644 --- a/packages/doenetml-worker/src/components/BooleanList.js +++ b/packages/doenetml-worker/src/components/BooleanList.js @@ -379,6 +379,7 @@ export default class BooleanList extends CompositeComponent { warnings.push(...processResult.warnings); workspace.componentsCopied = componentsCopied; + workspace.numComponents = numComponents; return { replacements: processResult.serializedComponents, @@ -397,26 +398,30 @@ export default class BooleanList extends CompositeComponent { let errors = []; let warnings = []; - let componentsToCopy = []; + let numComponents = await component.stateValues.numComponents; - let childNameByComponent = - await component.stateValues.childNameByComponent; + if (numComponents === workspace.numComponents) { + let componentsToCopy = []; - for (let childName of childNameByComponent) { - let replacementSource = components[childName]; + let childNameByComponent = + await component.stateValues.childNameByComponent; - if (replacementSource) { - componentsToCopy.push(replacementSource.componentName); + for (let childName of childNameByComponent) { + let replacementSource = components[childName]; + + if (replacementSource) { + componentsToCopy.push(replacementSource.componentName); + } } - } - if ( - componentsToCopy.length == workspace.componentsCopied.length && - workspace.componentsCopied.every( - (x, i) => x === componentsToCopy[i], - ) - ) { - return []; + if ( + componentsToCopy.length == workspace.componentsCopied.length && + workspace.componentsCopied.every( + (x, i) => x === componentsToCopy[i], + ) + ) { + return []; + } } // for now, just recreate diff --git a/packages/doenetml-worker/src/components/ChoiceInput.js b/packages/doenetml-worker/src/components/ChoiceInput.js index 734f7d2de..58d640de2 100644 --- a/packages/doenetml-worker/src/components/ChoiceInput.js +++ b/packages/doenetml-worker/src/components/ChoiceInput.js @@ -223,7 +223,7 @@ export default class Choiceinput extends Input { (x) => x + 1, ); } else { - // if desiredIndices is specfied, use those + // if desiredIndices is specified, use those let desiredChoiceOrder = dependencyValues.variants?.desiredVariant?.indices; if (desiredChoiceOrder !== undefined) { @@ -1048,6 +1048,14 @@ export default class Choiceinput extends Input { globalDependencyValues, stateValues, }) { + let desiredSelectedValues = + desiredStateVariableValues.selectedValues; + + // if attempt to set to a string, set it as the first desired value + if (typeof desiredSelectedValues === "string") { + desiredSelectedValues = [desiredSelectedValues]; + } + let selectMultiple = await stateValues.selectMultiple; let componentType = globalDependencyValues.componentType; @@ -1058,9 +1066,8 @@ export default class Choiceinput extends Input { ]; let foundValue = false; - for (let key in desiredStateVariableValues.selectedValues) { - let desiredVal = - desiredStateVariableValues.selectedValues[key]; + for (let key in desiredSelectedValues) { + let desiredVal = desiredSelectedValues[key]; let ind = getIndOfDesiredValue(desiredVal); if (ind !== -1) { newSelectedIndices[key] = ind + 1; // 1-indexed @@ -1095,8 +1102,7 @@ export default class Choiceinput extends Input { }; } } else { - let desiredVal = - desiredStateVariableValues.selectedValues[0]; + let desiredVal = desiredSelectedValues[0]; let ind = getIndOfDesiredValue(desiredVal); if (ind !== -1) { @@ -1130,9 +1136,9 @@ export default class Choiceinput extends Input { } } } else { - ind = globalDependencyValues.choiceTexts.indexOf( - desiredVal?.toLowerCase().trim(), - ); + ind = globalDependencyValues.choiceTexts + .map((v) => v.toLowerCase().trim()) + .indexOf(desiredVal?.toLowerCase().trim()); } return ind; } @@ -1439,12 +1445,18 @@ export default class Choiceinput extends Input { skipRendererUpdate = false, }) { if (!(await this.stateValues.disabled)) { + let choicesDisabled = await this.stateValues.choicesDisabled; + + let effectiveSelectedIndices = selectedIndices.filter( + (v) => !choicesDisabled[v - 1], + ); + let updateInstructions = [ { updateType: "updateValue", componentName: this.componentName, stateVariable: "allSelectedIndices", - value: selectedIndices, + value: effectiveSelectedIndices, }, ]; @@ -1456,8 +1468,8 @@ export default class Choiceinput extends Input { componentType: this.componentType, }, result: { - response: selectedIndices, - responseText: selectedIndices.map( + response: effectiveSelectedIndices, + responseText: effectiveSelectedIndices.map( (i) => choiceTexts[i - 1], ), }, diff --git a/packages/doenetml-worker/src/components/MathInput.js b/packages/doenetml-worker/src/components/MathInput.js index 77e592ed0..16f21718f 100644 --- a/packages/doenetml-worker/src/components/MathInput.js +++ b/packages/doenetml-worker/src/components/MathInput.js @@ -126,6 +126,7 @@ export default class MathInput extends Input { createComponentOfType: "integer", createStateVariable: "minWidth", defaultValue: 50, + clamp: [0, Infinity], public: true, forRenderer: true, }; diff --git a/packages/doenetml-worker/src/components/MathList.js b/packages/doenetml-worker/src/components/MathList.js index 6f5f5dbaa..ff7acc0be 100644 --- a/packages/doenetml-worker/src/components/MathList.js +++ b/packages/doenetml-worker/src/components/MathList.js @@ -642,6 +642,7 @@ export default class MathList extends CompositeComponent { warnings.push(...processResult.warnings); workspace.componentsCopied = componentsCopied; + workspace.numComponents = numComponents; return { replacements: processResult.serializedComponents, @@ -660,30 +661,36 @@ export default class MathList extends CompositeComponent { let errors = []; let warnings = []; - let componentsToCopy = []; + let numComponents = await component.stateValues.numComponents; - let childInfoByComponent = - await component.stateValues.childInfoByComponent; + if (numComponents === workspace.numComponents) { + let componentsToCopy = []; - for (let childInfo of childInfoByComponent) { - let replacementSource = components[childInfo.childName]; + let childInfoByComponent = + await component.stateValues.childInfoByComponent; - if (childInfo.nComponents !== undefined) { - componentsToCopy.push( - replacementSource.componentName + ":" + childInfo.component, - ); - } else { - componentsToCopy.push(replacementSource.componentName); + for (let childInfo of childInfoByComponent) { + let replacementSource = components[childInfo.childName]; + + if (childInfo.nComponents !== undefined) { + componentsToCopy.push( + replacementSource.componentName + + ":" + + childInfo.component, + ); + } else { + componentsToCopy.push(replacementSource.componentName); + } } - } - if ( - componentsToCopy.length == workspace.componentsCopied.length && - workspace.componentsCopied.every( - (x, i) => x === componentsToCopy[i], - ) - ) { - return []; + if ( + componentsToCopy.length == workspace.componentsCopied.length && + workspace.componentsCopied.every( + (x, i) => x === componentsToCopy[i], + ) + ) { + return []; + } } // for now, just recreate diff --git a/packages/doenetml-worker/src/components/MatrixInput.js b/packages/doenetml-worker/src/components/MatrixInput.js index 37c5d5730..551eb7b03 100644 --- a/packages/doenetml-worker/src/components/MatrixInput.js +++ b/packages/doenetml-worker/src/components/MatrixInput.js @@ -142,6 +142,7 @@ export class MatrixInput extends Input { createComponentOfType: "integer", createStateVariable: "minComponentWidth", defaultValue: 0, + clamp: [0, Infinity], }; return attributes; @@ -1226,6 +1227,9 @@ export class MatrixInput extends Input { let numRows = globalDependencyValues.numRows; let numColumns = globalDependencyValues.numColumns; + let accumulatedComponents = + globalDependencyValues.accumulatedComponents; + let originalIsColumnVector = false; let originalIsRowVector = false; let originalIsMatrix = false; @@ -1263,14 +1267,19 @@ export class MatrixInput extends Input { .slice(1, 1 + numRows) .map((x) => [x]); if (valueData.length < numRows) { - // pad first column with blanks + // pad first column with accumulated components or blanks for ( let rowInd = valueData.length; rowInd < numRows; rowInd++ ) { + let accumRow = + accumulatedComponents[rowInd] ?? []; + valueData.push([ - globalDependencyValues.defaultEntry.tree, + accumRow[0] ?? + globalDependencyValues.defaultEntry + .tree, ]); } } @@ -1292,12 +1301,20 @@ export class MatrixInput extends Input { valueData = [originalTree[1].slice(1, 1 + numColumns)]; if (valueData[0].length < numColumns) { - // pad first row with blanks - valueData[0].push( - ...Array(numColumns - valueData[0].length).fill( - globalDependencyValues.defaultEntry.tree, - ), - ); + // pad first row with accumulated components or blanks + for ( + let colInd = valueData[0].length; + colInd < numColumns; + colInd++ + ) { + let accumRow = accumulatedComponents[0] ?? []; + + valueData[0].push( + accumRow[colInd] ?? + globalDependencyValues.defaultEntry + .tree, + ); + } } if (numRows > 1) { @@ -1318,35 +1335,54 @@ export class MatrixInput extends Input { .map((x) => x.slice(1, numColumns + 1)); if (valueData[0].length < numColumns) { - // pad existing rows with blanks + // pad existing rows with accumulated components or blanks for ( let rowInd = 0; rowInd < valueData.length; rowInd++ ) { - valueData[rowInd].push( - ...Array( - numColumns - valueData[rowInd].length, - ).fill( - globalDependencyValues.defaultEntry - .tree, - ), - ); + let row = valueData[rowInd]; + let accumRow = + accumulatedComponents[rowInd] ?? []; + + for ( + let colInd = row.length; + colInd < numColumns; + colInd++ + ) { + row.push( + accumRow[colInd] ?? + globalDependencyValues.defaultEntry + .tree, + ); + } } } if (valueData.length < numRows) { + // pad with rows of accumulated components or blanks for ( let rowInd = valueData.length; rowInd < numRows; rowInd++ ) { - valueData.push( - Array(numColumns).fill( - globalDependencyValues.defaultEntry - .tree, - ), - ); + let row = []; + let accumRow = + accumulatedComponents[rowInd] ?? []; + + for ( + let colInd = 0; + colInd < numColumns; + colInd++ + ) { + row.push( + accumRow[colInd] ?? + globalDependencyValues.defaultEntry + .tree, + ); + } + + valueData.push(row); } } } else { @@ -1753,6 +1789,9 @@ export class MatrixInput extends Input { // workspace.immediateValueData, // creating it from immediateValueOriginal if not defined yet + let accumulatedComponents = + globalDependencyValues.accumulatedComponents; + let numRows = globalDependencyValues.numRows; let numColumns = globalDependencyValues.numColumns; @@ -1796,14 +1835,19 @@ export class MatrixInput extends Input { .slice(1, 1 + numRows) .map((x) => [x]); if (immediateValueData.length < numRows) { - // pad first column with blanks + // pad first column with accumulated components or blanks for ( let rowInd = immediateValueData.length; rowInd < numRows; rowInd++ ) { + let accumRow = + accumulatedComponents[rowInd] ?? []; + immediateValueData.push([ - globalDependencyValues.defaultEntry.tree, + accumRow[0] ?? + globalDependencyValues.defaultEntry + .tree, ]); } } @@ -1827,14 +1871,20 @@ export class MatrixInput extends Input { ]; if (immediateValueData[0].length < numColumns) { - // pad first row with blanks - immediateValueData[0].push( - ...Array( - numColumns - immediateValueData[0].length, - ).fill( - globalDependencyValues.defaultEntry.tree, - ), - ); + // pad first row with accumulated components or blanks + for ( + let colInd = immediateValueData[0].length; + colInd < numColumns; + colInd++ + ) { + let accumRow = accumulatedComponents[0] ?? []; + + immediateValueData[0].push( + accumRow[colInd] ?? + globalDependencyValues.defaultEntry + .tree, + ); + } } if (numRows > 1) { @@ -1855,36 +1905,54 @@ export class MatrixInput extends Input { .map((x) => x.slice(1, numColumns + 1)); if (immediateValueData[0].length < numColumns) { - // pad existing rows with blanks + // pad existing rows with accumulated components or blanks for ( let rowInd = 0; rowInd < immediateValueData.length; rowInd++ ) { - immediateValueData[rowInd].push( - ...Array( - numColumns - - immediateValueData[rowInd].length, - ).fill( - globalDependencyValues.defaultEntry - .tree, - ), - ); + let row = immediateValueData[rowInd]; + let accumRow = + accumulatedComponents[rowInd] ?? []; + + for ( + let colInd = row.length; + colInd < numColumns; + colInd++ + ) { + row.push( + accumRow[colInd] ?? + globalDependencyValues.defaultEntry + .tree, + ); + } } } if (immediateValueData.length < numRows) { + // pad with rows of accumulated components or blanks for ( let rowInd = immediateValueData.length; rowInd < numRows; rowInd++ ) { - immediateValueData.push( - Array(numColumns).fill( - globalDependencyValues.defaultEntry - .tree, - ), - ); + let row = []; + let accumRow = + accumulatedComponents[rowInd] ?? []; + + for ( + let colInd = 0; + colInd < numColumns; + colInd++ + ) { + row.push( + accumRow[colInd] ?? + globalDependencyValues.defaultEntry + .tree, + ); + } + + immediateValueData.push(row); } } } else { @@ -2390,6 +2458,26 @@ export class MatrixInput extends Input { }, }; + stateVariableDefinitions.text = { + public: true, + shadowingInstructions: { + createComponentOfType: "text", + }, + returnDependencies: () => ({ + valueForDisplay: { + dependencyType: "stateVariable", + variableName: "valueForDisplay", + }, + }), + definition: function ({ dependencyValues }) { + return { + setValue: { + text: dependencyValues.valueForDisplay.toString(), + }, + }; + }, + }; + stateVariableDefinitions.matrix = { public: true, shadowingInstructions: { diff --git a/packages/doenetml-worker/src/components/NumberList.js b/packages/doenetml-worker/src/components/NumberList.js index 7e03523c1..b09d798a3 100644 --- a/packages/doenetml-worker/src/components/NumberList.js +++ b/packages/doenetml-worker/src/components/NumberList.js @@ -649,6 +649,7 @@ export default class NumberList extends CompositeComponent { warnings.push(...processResult.warnings); workspace.componentsCopied = componentsCopied; + workspace.numComponents = numComponents; return { replacements: processResult.serializedComponents, @@ -667,30 +668,36 @@ export default class NumberList extends CompositeComponent { let errors = []; let warnings = []; - let componentsToCopy = []; + let numComponents = await component.stateValues.numComponents; - let childInfoByComponent = - await component.stateValues.childInfoByComponent; + if (numComponents === workspace.numComponents) { + let componentsToCopy = []; - for (let childInfo of childInfoByComponent) { - let replacementSource = components[childInfo.childName]; + let childInfoByComponent = + await component.stateValues.childInfoByComponent; - if (childInfo.nComponents !== undefined) { - componentsToCopy.push( - replacementSource.componentName + ":" + childInfo.component, - ); - } else { - componentsToCopy.push(replacementSource.componentName); + for (let childInfo of childInfoByComponent) { + let replacementSource = components[childInfo.childName]; + + if (childInfo.nComponents !== undefined) { + componentsToCopy.push( + replacementSource.componentName + + ":" + + childInfo.component, + ); + } else { + componentsToCopy.push(replacementSource.componentName); + } } - } - if ( - componentsToCopy.length == workspace.componentsCopied.length && - workspace.componentsCopied.every( - (x, i) => x === componentsToCopy[i], - ) - ) { - return []; + if ( + componentsToCopy.length == workspace.componentsCopied.length && + workspace.componentsCopied.every( + (x, i) => x === componentsToCopy[i], + ) + ) { + return []; + } } // for now, just recreate diff --git a/packages/doenetml-worker/src/components/Spreadsheet.ts b/packages/doenetml-worker/src/components/Spreadsheet.js similarity index 98% rename from packages/doenetml-worker/src/components/Spreadsheet.ts rename to packages/doenetml-worker/src/components/Spreadsheet.js index 0ee0bd30a..3831f35c5 100644 --- a/packages/doenetml-worker/src/components/Spreadsheet.ts +++ b/packages/doenetml-worker/src/components/Spreadsheet.js @@ -1,10 +1,3 @@ -//@ts-nocheck - -// Note: changed this .js file to a .ts file in order to prevent an error -// from HyperFormula when loading Core directly from vitest. -// No idea why this stops the error, but found this workaround via trial-and-error. -// This file has not been converted to typescript, hence the ts-nocheck line at the beginning. - import { normalizeIndex } from "../utils/table"; import { textToAst } from "../utils/math"; import BlockComponent from "./abstract/BlockComponent"; @@ -1033,23 +1026,23 @@ export default class Spreadsheet extends BlockComponent { // console.log(`array definition of evaluatedCells`) // console.log(globalDependencyValues) - // let hf = HyperFormula.buildFromArray( - // globalDependencyValues.cells, - // { - // licenseKey: "gpl-v3", - // }, - // ); + let hf = HyperFormula.buildFromArray( + globalDependencyValues.cells, + { + licenseKey: "gpl-v3", + }, + ); let allEvaluated = hf.getSheetValues(0); let evaluatedCells = {}; - // for (let ind1 in allEvaluated) { - // let row = allEvaluated[ind1]; - // for (let ind2 in row) { - // evaluatedCells[[ind1, ind2]] = row[ind2]; - // } - // } + for (let ind1 in allEvaluated) { + let row = allEvaluated[ind1]; + for (let ind2 in row) { + evaluatedCells[[ind1, ind2]] = row[ind2]; + } + } return { setValue: { evaluatedCells } }; }, diff --git a/packages/doenetml-worker/src/components/Text.js b/packages/doenetml-worker/src/components/Text.js index bc1e9908d..fd0d382c1 100644 --- a/packages/doenetml-worker/src/components/Text.js +++ b/packages/doenetml-worker/src/components/Text.js @@ -32,9 +32,8 @@ export default class Text extends InlineComponent { static variableForImplicitProp = "value"; static implicitPropReturnsSameType = true; - // even if inside a component that turned on descendantCompositesMustHaveAReplacement - // don't required composite replacements - static descendantCompositesMustHaveAReplacement = false; + static descendantCompositesMustHaveAReplacement = true; + static descendantCompositesDefaultReplacementType = "text"; static createAttributesObject() { let attributes = super.createAttributesObject(); diff --git a/packages/doenetml-worker/src/components/TextList.js b/packages/doenetml-worker/src/components/TextList.js index 859b8c19e..bc87a3101 100644 --- a/packages/doenetml-worker/src/components/TextList.js +++ b/packages/doenetml-worker/src/components/TextList.js @@ -380,6 +380,7 @@ export default class TextList extends CompositeComponent { warnings.push(...processResult.warnings); workspace.componentsCopied = componentsCopied; + workspace.numComponents = numComponents; return { replacements: processResult.serializedComponents, @@ -398,26 +399,30 @@ export default class TextList extends CompositeComponent { let errors = []; let warnings = []; - let componentsToCopy = []; + let numComponents = await component.stateValues.numComponents; - let childNameByComponent = - await component.stateValues.childNameByComponent; + if (numComponents === workspace.numComponents) { + let componentsToCopy = []; - for (let childName of childNameByComponent) { - let replacementSource = components[childName]; + let childNameByComponent = + await component.stateValues.childNameByComponent; - if (replacementSource) { - componentsToCopy.push(replacementSource.componentName); + for (let childName of childNameByComponent) { + let replacementSource = components[childName]; + + if (replacementSource) { + componentsToCopy.push(replacementSource.componentName); + } } - } - if ( - componentsToCopy.length == workspace.componentsCopied.length && - workspace.componentsCopied.every( - (x, i) => x === componentsToCopy[i], - ) - ) { - return []; + if ( + componentsToCopy.length == workspace.componentsCopied.length && + workspace.componentsCopied.every( + (x, i) => x === componentsToCopy[i], + ) + ) { + return []; + } } // for now, just recreate diff --git a/packages/doenetml-worker/src/test/tagSpecific/angle.test.ts b/packages/doenetml-worker/src/test/tagSpecific/angle.test.ts new file mode 100644 index 000000000..9e661fdee --- /dev/null +++ b/packages/doenetml-worker/src/test/tagSpecific/angle.test.ts @@ -0,0 +1,1230 @@ +import { describe, expect, it, vi } from "vitest"; +import { createTestCore, returnAllStateVariables } from "../utils/test-core"; +import { + movePoint, + updateBooleanInputValue, + updateMathInputValue, + updateTextInputValue, +} from "../utils/actions"; +import Core from "../../Core"; +import me from "math-expressions"; + +const Mock = vi.fn(); +vi.stubGlobal("postMessage", Mock); +vi.mock("hyperformula"); + +describe("Angle tag tests", async () => { + it("angle determined by three points, 45-45-90 triangle", async () => { + let core = await createTestCore({ + doenetML: ` + $angle1.angle{assignNames="angle2"} +

Angle again: $angle1{name="angle3"}

+ + + + + + + (2,4) + (4,2) + + + `, + }); + + async function check_items(angle: number, ps: number[][]) { + let stateVariables = await returnAllStateVariables(core); + + expect(stateVariables["/angle1"].stateValues.radians.tree).closeTo( + angle, + 1e-12, + ); + expect(stateVariables["/angle2"].stateValues.value.tree).closeTo( + angle, + 1e-12, + ); + expect(stateVariables["/angle3"].stateValues.radians.tree).closeTo( + angle, + 1e-12, + ); + expect( + stateVariables["/angle1"].stateValues.points.map((v) => + v.map((x) => x.tree), + ), + ).eqls(ps); + } + + let ps = [ + [2, 2], + [2, 4], + [4, 2], + ]; + + await check_items(Math.PI / 4, ps); + + await updateMathInputValue({ latex: "4", name: "/mi1", core }); + await updateMathInputValue({ latex: "4", name: "/mi2", core }); + ps[0] = [4, 4]; + await check_items((7 * Math.PI) / 4, ps); + + await updateMathInputValue({ latex: "0", name: "/mi1", core }); + await updateMathInputValue({ latex: "2", name: "/mi2", core }); + ps[0] = [0, 2]; + await check_items(Math.PI / 2, ps); + + await updateMathInputValue({ latex: "4", name: "/mi1", core }); + await updateMathInputValue({ latex: "6", name: "/mi2", core }); + ps[0] = [4, 6]; + await check_items((3 * Math.PI) / 2, ps); + }); + + it("angle determined by two lines", async () => { + let core = await createTestCore({ + doenetML: ` + $angle1.angle{assignNames="angle2"} +

Angle again: $angle1{name="angle3"}

+ + + + + + + + + + + + + + + + + `, + }); + + async function check_items(angle: number) { + let stateVariables = await returnAllStateVariables(core); + + expect(stateVariables["/angle1"].stateValues.radians.tree).closeTo( + angle, + 1e-12, + ); + expect(stateVariables["/angle2"].stateValues.value.tree).closeTo( + angle, + 1e-12, + ); + expect(stateVariables["/angle3"].stateValues.radians.tree).closeTo( + angle, + 1e-12, + ); + } + + let theta1 = -2; + let theta2 = 2; + + await check_items(theta2 - theta1); + + theta1 = 4; + theta2 = 6; + await updateMathInputValue({ latex: "-3", name: "/mi1", core }); + await updateMathInputValue({ latex: "7", name: "/mi2", core }); + await updateMathInputValue({ + latex: theta1.toString(), + name: "/theta1", + core, + }); + await updateMathInputValue({ + latex: theta2.toString(), + name: "/theta2", + core, + }); + await check_items(theta2 - theta1); + + theta1 = 3; + theta2 = 3; + await updateMathInputValue({ latex: "5", name: "/mi1", core }); + await updateMathInputValue({ latex: "-3", name: "/mi2", core }); + await updateMathInputValue({ + latex: theta1.toString(), + name: "/theta1", + core, + }); + await updateMathInputValue({ + latex: theta2.toString(), + name: "/theta2", + core, + }); + await check_items(theta2 - theta1); + + theta1 = Math.PI / 4; + theta2 = (5 * Math.PI) / 4; + await updateMathInputValue({ latex: "2", name: "/mi1", core }); + await updateMathInputValue({ latex: "-1", name: "/mi2", core }); + await updateMathInputValue({ + latex: "\\pi/4", + name: "/theta1", + core, + }); + await updateMathInputValue({ + latex: "5\\pi/4", + name: "/theta2", + core, + }); + await check_items(theta2 - theta1); + }); + + it("angle warning when determined by three lines", async () => { + let core = await createTestCore({ + doenetML: ` + + + + + + + + `, + }); + + let errorWarnings = core.errorWarnings; + + expect(errorWarnings.errors.length).eq(0); + expect(errorWarnings.warnings.length).eq(1); + + expect(errorWarnings.warnings[0].message).contain( + "Cannot define an angle between 3 lines", + ); + expect(errorWarnings.warnings[0].level).eq(2); + expect(errorWarnings.warnings[0].doenetMLrange.lineBegin).eq(7); + expect(errorWarnings.warnings[0].doenetMLrange.charBegin).eq(5); + expect(errorWarnings.warnings[0].doenetMLrange.lineEnd).eq(7); + expect(errorWarnings.warnings[0].doenetMLrange.charEnd).eq(40); + }); + + it("parallel and undefined lines", async () => { + let core = await createTestCore({ + doenetML: ` + + + $angle1.angle{assignNames="angle2"} +

Angle again: $angle1{name="angle3"}

+ + + + + + + + `, + }); + + async function check_items(angle: number | string) { + let stateVariables = await returnAllStateVariables(core); + + if (typeof angle === "number") { + expect( + stateVariables["/angle1"].stateValues.radians.tree, + ).closeTo(angle, 1e-12); + expect( + stateVariables["/angle2"].stateValues.value.tree, + ).closeTo(angle, 1e-12); + expect( + stateVariables["/angle3"].stateValues.radians.tree, + ).closeTo(angle, 1e-12); + } else { + expect(stateVariables["/angle1"].stateValues.radians.tree).eq( + angle, + ); + expect(stateVariables["/angle2"].stateValues.value.tree).eq( + angle, + ); + expect(stateVariables["/angle3"].stateValues.radians.tree).eq( + angle, + ); + } + } + + await check_items("_"); + + await updateMathInputValue({ latex: "0", name: "/mi2", core }); + await check_items(Math.PI / 2); + + await updateMathInputValue({ latex: "1", name: "/mi1", core }); + await updateMathInputValue({ latex: "2", name: "/mi2", core }); + await check_items("_"); + }); + + it("changing radius", async () => { + let core = await createTestCore({ + doenetML: ` + + + (5,0) + (0,0) + + + + $angle1.radius{assignNames="radius2"} + `, + }); + + async function check_items(angle: number, radius: number | string) { + let stateVariables = await returnAllStateVariables(core); + + expect(stateVariables["/angle1"].stateValues.radians.tree).closeTo( + angle, + 1e-12, + ); + expect(stateVariables["/radius2"].stateValues.value.tree).eq( + radius, + ); + } + + await check_items(1, "\uff3f"); + + await updateMathInputValue({ latex: "1", name: "/mi1", core }); + await check_items(1, 1); + + await updateMathInputValue({ latex: "2", name: "/mi1", core }); + await check_items(1, 2); + + await updateMathInputValue({ latex: "-3", name: "/mi1", core }); + await check_items(1, -3); + + await updateMathInputValue({ latex: "x", name: "/mi1", core }); + await check_items(1, "x"); + + await updateMathInputValue({ latex: "4", name: "/mi1", core }); + await check_items(1, 4); + }); + + it("systematically vary angle", async () => { + let core = await createTestCore({ + doenetML: ` + + + (5,0) + (0,0) + + + +

$angle1.angle{assignNames="alpha"}

+

$angle1.degrees{assignNames="alphadeg"}

+

Angle again: $angle1{name="angle2"}

+ `, + }); + + async function check_items(angle: number | string) { + let stateVariables = await returnAllStateVariables(core); + + if (typeof angle === "number") { + expect( + stateVariables["/angle1"].stateValues.radians.tree, + ).closeTo(angle, 1e-12); + expect( + stateVariables["/angle2"].stateValues.radians.tree, + ).closeTo(angle, 1e-12); + expect(stateVariables["/alpha"].stateValues.value.tree).closeTo( + angle, + 1e-12, + ); + expect( + stateVariables["/alphadeg"].stateValues.value.tree, + ).closeTo((180 * angle) / Math.PI, 1e-12); + } else { + expect(stateVariables["/angle1"].stateValues.radians.tree).eq( + angle, + ); + expect(stateVariables["/angle2"].stateValues.radians.tree).eq( + angle, + ); + expect(stateVariables["/alpha"].stateValues.value.tree).eq( + angle, + ); + expect(stateVariables["/alphadeg"].stateValues.value.tree).eq( + angle, + ); + } + } + + await check_items("\uff3f"); + + let angles = [ + { latex: "\\pi/4", number: Math.PI / 4 }, + { latex: "1", number: 1 }, + { latex: "\\pi/3", number: Math.PI / 3 }, + { latex: "2\\pi/3", number: (2 * Math.PI) / 3 }, + { latex: "\\pi", number: Math.PI }, + { latex: "4", number: 4 }, + { latex: "3\\pi/2", number: (3 * Math.PI) / 2 }, + { latex: "11\\pi/6", number: (11 * Math.PI) / 6 }, + { latex: "2\\pi", number: 2 * Math.PI }, + { latex: "2\\pi+0.00001", number: 0.00001 }, + ]; + + for (let a of angles) { + await updateMathInputValue({ + latex: a.latex, + name: "/theta", + core, + }); + await check_items(a.number); + } + }); + + async function check_rightangle(core: Core, numerical: boolean = false) { + const stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/angle1"].stateValues.radians.tree).eqls( + numerical ? Math.PI / 2 : ["/", "pi", 2], + ); + expect(stateVariables["/angle1"].stateValues.degrees.tree).eq(90); + expect( + stateVariables["/angle1"].stateValues.points[0].map((x) => x.tree), + ).eqls([1, 0]); + expect( + stateVariables["/angle1"].stateValues.points[1].map((x) => x.tree), + ).eqls([0, 0]); + expect(stateVariables["/angle1"].stateValues.points[2][0].tree).closeTo( + 0, + 1e-14, + ); + expect(stateVariables["/angle1"].stateValues.points[2][1].tree).eq(1); + expect(stateVariables["/m1"].stateValues.value.tree).eq( + numerical ? Math.PI : "pi", + ); + expect(stateVariables["/m2"].stateValues.value.tree).eq( + numerical ? Math.PI : "pi", + ); + expect(stateVariables["/m3"].stateValues.value.tree).eq(180); + } + + it("angle from number sugar", async () => { + let core = await createTestCore({ + doenetML: ` + pi/2 + 2$angle1 + 2$angle1.angle + 2$angle1.degrees + `, + }); + await check_rightangle(core); + }); + + it("angle from radians number", async () => { + let core = await createTestCore({ + doenetML: ` + + 2$angle1 + 2$angle1.angle + 2$angle1.degrees + `, + }); + + await check_rightangle(core); + }); + + it("angle from degrees number", async () => { + let core = await createTestCore({ + doenetML: ` + + 2$angle1 + 2$angle1.angle + 2$angle1.degrees + `, + }); + + await check_rightangle(core); + }); + + it("angle from sugar with macro and string", async () => { + let core = await createTestCore({ + doenetML: ` + $pi/2 + 2$angle1 + 2$angle1.angle + 2$angle1.degrees + pi + `, + }); + + await check_rightangle(core); + }); + + it("empty angle", async () => { + let core = await createTestCore({ + doenetML: ` + +2$angle1 +2$angle1.angle +2$angle1.degrees +`, + }); + + await check_rightangle(core, true); + }); + + async function check_alphaangle(core: Core) { + const stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/angle1"].stateValues.radians.tree).eq("alpha"); + expect(stateVariables["/angle1"].stateValues.degrees.tree).eqls([ + "/", + ["*", 180, "alpha"], + "pi", + ]); + expect( + stateVariables["/angle1"].stateValues.points[0].map((x) => x.tree), + ).eqls([1, 0]); + expect( + stateVariables["/angle1"].stateValues.points[1].map((x) => x.tree), + ).eqls([0, 0]); + expect( + stateVariables["/angle1"].stateValues.points[2].map((x) => x.tree), + ).eqls(["\uff3f", "\uff3f"]); + expect(stateVariables["/m1"].stateValues.value.tree).eqls([ + "*", + 2, + "alpha", + ]); + expect(stateVariables["/m2"].stateValues.value.tree).eqls([ + "*", + 2, + "alpha", + ]); + expect(stateVariables["/m3"].stateValues.value.tree).eqls([ + "/", + ["*", 360, "alpha"], + "pi", + ]); + } + + it("angle from variable sugar", async () => { + let core = await createTestCore({ + doenetML: ` + alpha + 2$angle1 + 2$angle1.angle + 2$angle1.degrees + `, + }); + + await check_alphaangle(core); + }); + + it("angle from variable radians", async () => { + let core = await createTestCore({ + doenetML: ` + + 2$angle1 + 2$angle1.angle + 2$angle1.degrees + `, + }); + + await check_alphaangle(core); + }); + + it("angle from variable degrees", async () => { + let core = await createTestCore({ + doenetML: ` + + 2$angle1 + 2$angle1.angle + 2$angle1.degrees + `, + }); + + const stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/angle1"].stateValues.radians.tree).eqls([ + "/", + ["*", "alpha", "pi"], + 180, + ]); + expect(stateVariables["/angle1"].stateValues.degrees.tree).eqls( + "alpha", + ); + expect( + stateVariables["/angle1"].stateValues.points[0].map((x) => x.tree), + ).eqls([1, 0]); + expect( + stateVariables["/angle1"].stateValues.points[1].map((x) => x.tree), + ).eqls([0, 0]); + expect( + stateVariables["/angle1"].stateValues.points[2].map((x) => x.tree), + ).eqls(["\uff3f", "\uff3f"]); + // TODO: once can simplify fractions, these should be: ["/", ["*", "alpha", "pi"], 90] + expect(stateVariables["/m1"].stateValues.value.tree).eqls([ + "/", + ["*", 2, "alpha", "pi"], + 180, + ]); + expect(stateVariables["/m2"].stateValues.value.tree).eqls([ + "/", + ["*", 2, "alpha", "pi"], + 180, + ]); + expect(stateVariables["/m3"].stateValues.value.tree).eqls([ + "*", + 2, + "alpha", + ]); + }); + + it("choose reflex angle", async () => { + let core = await createTestCore({ + doenetML: ` +

choose reflex angle:

+ $alpha1.chooseReflexAngle{assignNames="ra2"} + + (-6,5) + (0,0) + (4,2) + + + `, + }); + + function angleFromPs(ps: number[][], reflex?: number) { + let angle = + Math.atan2(ps[2][1] - ps[1][1], ps[2][0] - ps[1][0]) - + Math.atan2(ps[0][1] - ps[1][1], ps[0][0] - ps[1][0]); + if (angle < 0) { + angle += 2 * Math.PI; + } + if (angle > Math.PI) { + if (reflex === -1) { + angle = 2 * Math.PI - angle; + } + } else if (reflex === 1) { + angle = 2 * Math.PI - angle; + } + return angle; + } + + // not sure how to test this + // but at least make sure we don't throw any errors. + + let points = [ + [-6, 5], + [0, 0], + [4, 2], + ]; + + // should now be > pi if no modifications + + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/ra2"].stateValues.value).eq("never"); + expect(stateVariables["/alpha1"].stateValues.radians.tree).eq( + angleFromPs(points, -1), + ); + + await updateTextInputValue({ text: "allowed", name: "/ra", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/ra2"].stateValues.value).eq("allowed"); + expect(stateVariables["/alpha1"].stateValues.radians.tree).eq( + angleFromPs(points), + ); + + await updateTextInputValue({ text: "always", name: "/ra", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/ra2"].stateValues.value).eq("always"); + expect(stateVariables["/alpha1"].stateValues.radians.tree).eq( + angleFromPs(points), + ); + + await movePoint({ name: "/A", x: 1, y: -3, core }); + + points[0] = [1, -3]; + // should now be < pi if no modifications + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/ra2"].stateValues.value).eq("always"); + expect(stateVariables["/alpha1"].stateValues.radians.tree).eq( + angleFromPs(points, 1), + ); + + await updateTextInputValue({ text: "never", name: "/ra", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/ra2"].stateValues.value).eq("never"); + expect(stateVariables["/alpha1"].stateValues.radians.tree).eq( + angleFromPs(points), + ); + + await updateTextInputValue({ text: "allowed", name: "/ra", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/ra2"].stateValues.value).eq("allowed"); + expect(stateVariables["/alpha1"].stateValues.radians.tree).eq( + angleFromPs(points), + ); + + await movePoint({ name: "/C", x: -1, y: -5, core }); + + points[2] = [-1, -5]; + // should now be > pi if no modifications + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/ra2"].stateValues.value).eq("allowed"); + expect(stateVariables["/alpha1"].stateValues.radians.tree).eq( + angleFromPs(points), + ); + + await updateTextInputValue({ text: "never", name: "/ra", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/ra2"].stateValues.value).eq("never"); + expect(stateVariables["/alpha1"].stateValues.radians.tree).eq( + angleFromPs(points, -1), + ); + + await updateTextInputValue({ text: "always", name: "/ra", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/ra2"].stateValues.value).eq("always"); + expect(stateVariables["/alpha1"].stateValues.radians.tree).eq( + angleFromPs(points), + ); + }); + + async function test_angle_through_points({ + core, + initialRadians, + radiansName, + degreesName, + numPoints, + }: { + core: Core; + initialRadians: number | string | (number | string)[]; + radiansName?: string; + degreesName?: string; + numPoints: number; + }) { + async function check_items( + radians: + | number + | string + | (number | string)[] + | (number | string | (number | string)[])[], + x1: number, + y1: number, + x2: number, + y2: number, + ) { + let stateVariables = await returnAllStateVariables(core); + let radiansNumber: number; + if (typeof radians === "number") { + radiansNumber = radians; + expect( + stateVariables["/angle1"].stateValues.radians.tree, + ).closeTo(radians, 1e-14); + } else { + radiansNumber = me.fromAst(radians).evaluate_to_constant(); + expect(stateVariables["/angle1"].stateValues.radians.tree).eqls( + radians, + ); + } + expect( + stateVariables["/angle1"].stateValues.points[0].map( + (x) => x.tree, + ), + ).eqls([x1, y1]); + expect( + stateVariables["/angle1"].stateValues.points[1].map( + (x) => x.tree, + ), + ).eqls([x2, y2]); + + if (Number.isFinite(radiansNumber)) { + let theta = Math.atan2(y1 - y2, x1 - x2) + radiansNumber; + expect( + stateVariables["/angle1"].stateValues.points[2][0].tree, + ).closeTo(x2 + Math.cos(theta), 1e-14); + expect( + stateVariables["/angle1"].stateValues.points[2][1].tree, + ).closeTo(y2 + Math.sin(theta), 1e-14); + } else { + expect( + stateVariables["/angle1"].stateValues.points[2].map( + (x) => x.tree, + ), + ).eqls(["\uff3f", "\uff3f"]); + } + } + + let x1 = 3, + y1 = 5, + x2 = 0, + y2 = 0; + + if (numPoints == 2) { + x2 = 6; + y2 = 1; + } + + let radians = initialRadians; + await check_items(radians, x1, y1, x2, y2); + + // move points + x1 = 1; + y1 = 7; + await movePoint({ name: "/A", x: x1, y: y1, core }); + if (numPoints === 2) { + x2 = -3; + y2 = -2; + await movePoint({ name: "/B", x: x2, y: y2, core }); + } + await check_items(radians, x1, y1, x2, y2); + + if (radiansName) { + // change desired radians + await updateMathInputValue({ + latex: "2\\pi/5", + name: radiansName, + core, + }); + await check_items(["/", ["*", 2, "pi"], 5], x1, y1, x2, y2); + + // change desired radians to variable + await updateMathInputValue({ + latex: "\\theta", + name: radiansName, + core, + }); + await check_items("theta", x1, y1, x2, y2); + } + + if (degreesName) { + // change desired degrees + await updateMathInputValue({ + latex: "180", + name: degreesName, + core, + }); + await check_items("pi", x1, y1, x2, y2); + + // change desired degrees to variable + await updateMathInputValue({ + latex: "\\theta", + name: degreesName, + core, + }); + await check_items(["/", ["*", "pi", "theta"], 180], x1, y1, x2, y2); + } + } + + it("angle through 1 point", async () => { + let core = await createTestCore({ + doenetML: ` + + (3,5) + + + `, + }); + + await test_angle_through_points({ + core, + initialRadians: Math.PI / 2, + numPoints: 1, + }); + }); + + it("angle through 1 point, specify radians", async () => { + let core = await createTestCore({ + doenetML: ` +

Desired radians:

+ + (3,5) + + + `, + }); + + await test_angle_through_points({ + core, + initialRadians: ["/", "pi", 3], + radiansName: "/desiredRadians", + numPoints: 1, + }); + }); + + it("angle through 1 point, specify degrees", async () => { + let core = await createTestCore({ + doenetML: ` +

Desired degrees:

+ + (3,5) + + + `, + }); + + await test_angle_through_points({ + core, + initialRadians: ["/", "pi", 2], + degreesName: "/desiredDegrees", + numPoints: 1, + }); + }); + + it("angle through 2 points", async () => { + let core = await createTestCore({ + doenetML: ` + + (3,5) + (6,1) + + + `, + }); + + await test_angle_through_points({ + core, + initialRadians: Math.PI / 2, + numPoints: 2, + }); + }); + + it("angle through 2 points, specify radians", async () => { + let core = await createTestCore({ + doenetML: ` +

Desired radians:

+ + (3,5) + (6,1) + + + `, + }); + + await test_angle_through_points({ + core, + initialRadians: ["/", "pi", 3], + radiansName: "/desiredRadians", + numPoints: 2, + }); + }); + + it("angle through 2 points, specify degrees", async () => { + let core = await createTestCore({ + doenetML: ` +

Desired radians:

+ + (3,5) + (6,1) + + + `, + }); + + await test_angle_through_points({ + core, + initialRadians: ["/", "pi", 2], + degreesName: "/desiredDegrees", + numPoints: 2, + }); + }); + + async function test_angle_with_one_line({ + core, + initialRadians, + radiansName, + degreesName, + }: { + core: Core; + initialRadians: number | string | (number | string)[]; + radiansName?: string; + degreesName?: string; + }) { + async function check_items( + radians: + | number + | string + | (number | string)[] + | (number | string | (number | string)[])[], + point: number[], + slope: number, + ) { + let stateVariables = await returnAllStateVariables(core); + let radiansNumber: number; + if (typeof radians === "number") { + radiansNumber = radians; + expect( + stateVariables["/angle1"].stateValues.radians.tree, + ).closeTo(radians, 1e-14); + } else { + radiansNumber = me.fromAst(radians).evaluate_to_constant(); + expect(stateVariables["/angle1"].stateValues.radians.tree).eqls( + radians, + ); + } + let mag = Math.sqrt(1 + slope ** 2); + let d = [1 / mag, slope / mag]; + expect( + stateVariables["/angle1"].stateValues.points[0][0].tree, + ).closeTo(point[0] + d[0], 1e-14); + expect( + stateVariables["/angle1"].stateValues.points[0][1].tree, + ).closeTo(point[1] + d[1], 1e-14); + expect( + stateVariables["/angle1"].stateValues.points[1].map( + (x) => x.tree, + ), + ).eqls(point); + + if (Number.isFinite(radiansNumber)) { + let theta = Math.atan2(slope, 1) + radiansNumber; + expect( + stateVariables["/angle1"].stateValues.points[2][0].tree, + ).closeTo(point[0] + Math.cos(theta), 1e-14); + expect( + stateVariables["/angle1"].stateValues.points[2][1].tree, + ).closeTo(point[1] + Math.sin(theta), 1e-14); + } else { + expect( + stateVariables["/angle1"].stateValues.points[2].map( + (x) => x.tree, + ), + ).eqls(["\uff3f", "\uff3f"]); + } + } + + // y = 2x + 1 + let pointNearestOrigin = [-2 / 5, 1 / 5]; + let lineSlope = 2; + + let radians = initialRadians; + await check_items(radians, pointNearestOrigin, lineSlope); + + // change line + await updateMathInputValue({ + latex: "y=-1/2x+3", + name: "/equation", + core, + }); + pointNearestOrigin = [6 / 5, 12 / 5]; + lineSlope = -1 / 2; + await check_items(radians, pointNearestOrigin, lineSlope); + + if (radiansName) { + // change desired radians + await updateMathInputValue({ + latex: "2\\pi/5", + name: radiansName, + core, + }); + await check_items( + ["/", ["*", 2, "pi"], 5], + pointNearestOrigin, + lineSlope, + ); + + // change desired radians to variable + await updateMathInputValue({ + latex: "\\theta", + name: radiansName, + core, + }); + await check_items("theta", pointNearestOrigin, lineSlope); + } + + if (degreesName) { + // change desired degrees + await updateMathInputValue({ + latex: "180", + name: degreesName, + core, + }); + await check_items("pi", pointNearestOrigin, lineSlope); + + // change desired degrees to variable + await updateMathInputValue({ + latex: "\\theta", + name: degreesName, + core, + }); + await check_items( + ["/", ["*", "pi", "theta"], 180], + pointNearestOrigin, + lineSlope, + ); + } + } + + it("angle with one line", async () => { + let core = await createTestCore({ + doenetML: ` +

Equation of line:

+ + $equation + + + `, + }); + + await test_angle_with_one_line({ + core, + initialRadians: Math.PI / 2, + }); + }); + + it("angle with one line, specify radians", async () => { + let core = await createTestCore({ + doenetML: ` +

Equation of line:

+

Desired radians:

+ + $equation + + + `, + }); + + await test_angle_with_one_line({ + core, + initialRadians: ["/", "pi", 3], + radiansName: "/desiredRadians", + }); + }); + + it("angle with one line, specify degrees", async () => { + let core = await createTestCore({ + doenetML: ` +

Equation of line:

+

Desired degrees:

+ + $equation + + + `, + }); + + await test_angle_with_one_line({ + core, + initialRadians: ["/", "pi", 2], + degreesName: "/desiredDegrees", + }); + }); + + it("angle with label", async () => { + let core = await createTestCore({ + doenetML: ` + + + + + + + + + + `, + }); + + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/a"].stateValues.label).eq("\\(\\alpha^2\\)"); + expect(stateVariables["/b"].stateValues.label).eq( + "This is \\(\\frac{m}{2}\\)", + ); + }); + + it("display digits and decimals, overwrite in copies", async () => { + let core = await createTestCore({ + doenetML: ` + 1.39372582305929123842034823 + + + + + + + + 1.39372582305929123842034823 + 1.39372582305929123842034823 + + + + + + `, + }); + + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/a"].stateValues.latexForRenderer).eq("1.39"); + + expect(stateVariables["/aDig5a"].stateValues.latexForRenderer).eq( + "1.3937", + ); + expect(stateVariables["/aDec6a"].stateValues.latexForRenderer).eq( + "1.393726", + ); + expect(stateVariables["/aDig5b"].stateValues.latexForRenderer).eq( + "1.3937", + ); + expect(stateVariables["/aDec6b"].stateValues.latexForRenderer).eq( + "1.393726", + ); + expect(stateVariables["/aDig5c"].stateValues.latexForRenderer).eq( + "1.3937", + ); + expect(stateVariables["/aDec6c"].stateValues.latexForRenderer).eq( + "1.393726", + ); + expect(stateVariables["/aDig5d"].stateValues.latexForRenderer).eq( + "1.3937", + ); + expect(stateVariables["/aDec6d"].stateValues.latexForRenderer).eq( + "1.393726", + ); + expect(stateVariables["/aDig5e"].stateValues.latexForRenderer).eq( + "1.3937", + ); + expect(stateVariables["/aDec6e"].stateValues.latexForRenderer).eq( + "1.393726", + ); + expect(stateVariables["/aDig5f"].stateValues.latexForRenderer).eq( + "1.3937", + ); + expect(stateVariables["/aDec6f"].stateValues.latexForRenderer).eq( + "1.393726", + ); + }); + + it("emphasize right angle", async () => { + let core = await createTestCore({ + doenetML: ` + + + + + + +

Emphasize right angle 1:

+

Emphasize right angle 2:

+

Emphasize right angle 3:

+ +

Emphasize right angle: $a1.emphasizeRightAngle, $a2.emphasizeRightAngle, $a3.emphasizeRightAngle

+ + `, + }); + + // TODO: How to check renderer itself? + + let stateVariables = await returnAllStateVariables(core); + + expect(stateVariables["/emphasize"].stateValues.text).eq( + "Emphasize right angle: true, false, false", + ); + expect(stateVariables["/a1"].stateValues.emphasizeRightAngle).eq(true); + expect(stateVariables["/a2"].stateValues.emphasizeRightAngle).eq(false); + expect(stateVariables["/a3"].stateValues.emphasizeRightAngle).eq(false); + + await updateBooleanInputValue({ boolean: false, name: "/bi1", core }); + await updateBooleanInputValue({ boolean: true, name: "/bi2", core }); + await updateBooleanInputValue({ boolean: true, name: "/bi3", core }); + stateVariables = await returnAllStateVariables(core); + + expect(stateVariables["/emphasize"].stateValues.text).eq( + "Emphasize right angle: false, true, true", + ); + expect(stateVariables["/a1"].stateValues.emphasizeRightAngle).eq(false); + expect(stateVariables["/a2"].stateValues.emphasizeRightAngle).eq(true); + expect(stateVariables["/a3"].stateValues.emphasizeRightAngle).eq(true); + }); +}); diff --git a/packages/doenetml-worker/src/test/tagSpecific/answer.test.ts b/packages/doenetml-worker/src/test/tagSpecific/answer.test.ts index a8a3cdc26..1ab271e1b 100644 --- a/packages/doenetml-worker/src/test/tagSpecific/answer.test.ts +++ b/packages/doenetml-worker/src/test/tagSpecific/answer.test.ts @@ -2,19 +2,23 @@ import { describe, expect, it, vi } from "vitest"; import { createTestCore, returnAllStateVariables } from "../utils/test-core"; import { cleanLatex } from "../utils/math"; import { + submitAnswer, updateBooleanInputValue, updateMathInputImmediateValue, updateMathInputValue, updateMatrixInputValue, + updateSelectedIndices, updateTextInputValue, } from "../utils/actions"; import { getLatexToMathConverter, normalizeLatexString, } from "../../utils/math"; +import Core from "../../Core"; const Mock = vi.fn(); vi.stubGlobal("postMessage", Mock); +vi.mock("hyperformula"); async function test_math_answer({ doenetML, @@ -27,7 +31,7 @@ async function test_math_answer({ latex: string; credit: number; preAction?: { - componentName: string; + name: string; value: string; type: "math" | "text" | "boolean" | "choice"; }; @@ -78,29 +82,26 @@ async function test_math_answer({ if (response.preAction.type === "math") { await updateMathInputValue({ latex: response.preAction.value, - componentName: response.preAction.componentName, + name: response.preAction.name, core, }); } else if (response.preAction.type === "text") { await updateTextInputValue({ text: response.preAction.value, - componentName: response.preAction.componentName, + name: response.preAction.name, core, }); } else if (response.preAction.type === "boolean") { await updateBooleanInputValue({ boolean: response.preAction.value === "true", - componentName: response.preAction.componentName, + name: response.preAction.name, core, }); } else { - await core.requestAction({ - componentName: response.preAction.componentName, - actionName: "updateSelectedIndices", - args: { - selectedIndices: [parseInt(response.preAction.value)], - }, - event: null, + await updateSelectedIndices({ + name: response.preAction.name, + selectedIndices: [parseInt(response.preAction.value)], + core, }); } } @@ -116,7 +117,7 @@ async function test_math_answer({ await updateMathInputValue({ latex, - componentName: mathInputName, + name: mathInputName, core, }); stateVariables = await returnAllStateVariables(core); @@ -147,12 +148,7 @@ async function test_math_answer({ } // submit - await core.requestAction({ - componentName: answerName, - actionName: "submitAnswer", - args: {}, - event: null, - }); + await submitAnswer({ name: answerName, core }); if (!response.submissionPrevented) { submittedResponses = [currentResponse]; numSubmissions++; @@ -231,7 +227,7 @@ async function test_text_answer({ await updateTextInputValue({ text: currentResponse, - componentName: textInputName, + name: textInputName, core, }); stateVariables = await returnAllStateVariables(core); @@ -253,12 +249,7 @@ async function test_text_answer({ ); // submit - await core.requestAction({ - componentName: answerName, - actionName: "submitAnswer", - args: {}, - event: null, - }); + await submitAnswer({ name: answerName, core }); submittedResponses = [currentResponse]; submittedCredit = credit; numSubmissions++; @@ -331,7 +322,7 @@ async function test_boolean_answer({ await updateBooleanInputValue({ boolean: currentResponse, - componentName: booleanInputName, + name: booleanInputName, core, }); stateVariables = await returnAllStateVariables(core); @@ -353,12 +344,7 @@ async function test_boolean_answer({ ); // submit - await core.requestAction({ - componentName: answerName, - actionName: "submitAnswer", - args: {}, - event: null, - }); + await submitAnswer({ name: answerName, core }); submittedResponses = [currentResponse]; submittedCredit = credit; numSubmissions++; @@ -397,7 +383,7 @@ async function test_choice_answer({ choices: string[]; credit: number; preAction?: { - componentName: string; + name: string; value: string; recomputeIndices: boolean; type: "math" | "text"; @@ -457,12 +443,7 @@ async function test_choice_answer({ if (submitFirst) { // submit no answer - await core.requestAction({ - componentName: answerName, - actionName: "submitAnswer", - args: {}, - event: null, - }); + await submitAnswer({ name: answerName, core }); submittedResponses = selectedValues; submittedCredit = 0; numSubmissions++; @@ -494,13 +475,13 @@ async function test_choice_answer({ if (response.preAction.type === "math") { await updateMathInputValue({ latex: response.preAction.value, - componentName: response.preAction.componentName, + name: response.preAction.name, core, }); } else { await updateTextInputValue({ text: response.preAction.value, - componentName: response.preAction.componentName, + name: response.preAction.name, core, }); } @@ -522,11 +503,10 @@ async function test_choice_answer({ // select responses - await core.requestAction({ - componentName: choiceInputName, - actionName: "updateSelectedIndices", - args: { selectedIndices }, - event: null, + await updateSelectedIndices({ + name: choiceInputName, + selectedIndices, + core, }); stateVariables = await returnAllStateVariables(core); @@ -551,12 +531,7 @@ async function test_choice_answer({ ).eqls(selectedIndices); // submit - await core.requestAction({ - componentName: answerName, - actionName: "submitAnswer", - args: {}, - event: null, - }); + await submitAnswer({ name: answerName, core }); submittedResponses = selectedValues; submittedCredit = credit; numSubmissions++; @@ -596,7 +571,7 @@ async function test_matrix_answer({ entries: { latex: string; rowInd: number; colInd: number }[]; credit: number; preAction?: { - componentName: string; + name: string; value: string; type: "math" | "text" | "boolean"; }; @@ -646,19 +621,19 @@ async function test_matrix_answer({ if (response.preAction.type === "math") { await updateMathInputValue({ latex: response.preAction.value, - componentName: response.preAction.componentName, + name: response.preAction.name, core, }); } else if (response.preAction.type === "text") { await updateTextInputValue({ text: response.preAction.value, - componentName: response.preAction.componentName, + name: response.preAction.name, core, }); } else { await updateBooleanInputValue({ boolean: response.preAction.value === "true", - componentName: response.preAction.componentName, + name: response.preAction.name, core, }); } @@ -676,7 +651,7 @@ async function test_matrix_answer({ latex: entry.latex, rowInd: entry.rowInd, colInd: entry.colInd, - componentName: matrixInputName, + name: matrixInputName, stateVariables, core, }); @@ -710,12 +685,7 @@ async function test_matrix_answer({ } // submit - await core.requestAction({ - componentName: answerName, - actionName: "submitAnswer", - args: {}, - event: null, - }); + await submitAnswer({ name: answerName, core }); if (!response.submissionPrevented) { submittedResponses = [currentResponse]; numSubmissions++; @@ -760,7 +730,7 @@ async function test_action_answer({ effectiveResponses: any[]; credit: number; preAction?: { - componentName: string; + name: string; value: string; type: "math" | "text"; }; @@ -795,13 +765,13 @@ async function test_action_answer({ if (response.preAction.type === "math") { await updateMathInputValue({ latex: response.preAction.value, - componentName: response.preAction.componentName, + name: response.preAction.name, core, }); } else { await updateTextInputValue({ text: response.preAction.value, - componentName: response.preAction.componentName, + name: response.preAction.name, core, }); } @@ -835,12 +805,7 @@ async function test_action_answer({ ), ).eqls(submittedResponses); // submit - await core.requestAction({ - componentName: answerName, - actionName: "submitAnswer", - args: {}, - event: null, - }); + await submitAnswer({ name: answerName, core }); submittedResponses = currentResponses; submittedCredit = credit; @@ -873,7 +838,7 @@ async function test_answer_multiple_inputs({ values: string[]; credit: number; preAction?: { - componentName: string; + name: string; value: string; type: "math" | "text"; }; @@ -968,13 +933,13 @@ async function test_answer_multiple_inputs({ if (response.preAction.type === "math") { await updateMathInputValue({ latex: response.preAction.value, - componentName: response.preAction.componentName, + name: response.preAction.name, core, }); } else { await updateTextInputValue({ text: response.preAction.value, - componentName: response.preAction.componentName, + name: response.preAction.name, core, }); } @@ -991,19 +956,19 @@ async function test_answer_multiple_inputs({ if (input.type === "math" || input.type === "number") { await updateMathInputValue({ latex: values[ind], - componentName: inputNames[ind], + name: inputNames[ind], core, }); } else if (input.type === "text") { await updateTextInputValue({ text: values[ind], - componentName: inputNames[ind], + name: inputNames[ind], core, }); } else { await updateBooleanInputValue({ boolean: values[ind] === "true", - componentName: inputNames[ind], + name: inputNames[ind], core, }); } @@ -1033,12 +998,7 @@ async function test_answer_multiple_inputs({ } // submit - await core.requestAction({ - componentName: answerName, - actionName: "submitAnswer", - args: {}, - event: null, - }); + await submitAnswer({ name: answerName, core }); submittedResponses = currentResponses; submittedCredit = credit; @@ -1316,17 +1276,12 @@ describe("Answer tag tests", async () => { await updateTextInputValue({ text: " hello there ", - componentName: textInputName, + name: textInputName, core, }); // submit - await core.requestAction({ - componentName: "/answer1", - actionName: "submitAnswer", - args: {}, - event: null, - }); + await submitAnswer({ name: "/answer1", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/sr1"].stateValues.value).eq(" hello there "); @@ -1440,15 +1395,10 @@ describe("Answer tag tests", async () => { await updateMathInputValue({ latex: "x", - componentName: mathInputName, + name: mathInputName, core, }); - await core.requestAction({ - componentName: "/answer1", - actionName: "submitAnswer", - args: {}, - event: null, - }); + await submitAnswer({ name: "/answer1", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/answer1"].stateValues.creditAchieved).eq(1); }); @@ -1478,7 +1428,7 @@ describe("Answer tag tests", async () => { ]; async function check_award_based_on_submitted_response( - core: any, + core: Core, eventually_correct = true, ) { let errorWarnings = core.errorWarnings; @@ -1503,15 +1453,10 @@ describe("Answer tag tests", async () => { // have to submit the correct answer twice before it is marked correct await updateMathInputValue({ latex: "5", - componentName: mathInputName, + name: mathInputName, core, }); - await core.requestAction({ - componentName: "/ans", - actionName: "submitAnswer", - args: {}, - event: null, - }); + await submitAnswer({ name: "/ans", core }); stateVariables = await returnAllStateVariables(core); // answer is not correct because the submitted response was initially blank @@ -1520,12 +1465,7 @@ describe("Answer tag tests", async () => { // change when the answer is submitted expect(stateVariables["/ans"].stateValues.justSubmitted).eq(false); - await core.requestAction({ - componentName: "/ans", - actionName: "submitAnswer", - args: {}, - event: null, - }); + await submitAnswer({ name: "/ans", core }); stateVariables = await returnAllStateVariables(core); // if `eventually correct` is set to `true`, then @@ -2705,7 +2645,7 @@ The animal is a choices: ["Get 1"], credit: 1, preAction: { - componentName: "/num", + name: "/num", value: "4", recomputeIndices: true, type: "math", @@ -2718,7 +2658,7 @@ The animal is a choices: ["Get 1"], credit: 1, preAction: { - componentName: "/num", + name: "/num", value: "3", recomputeIndices: true, type: "math", @@ -2730,7 +2670,7 @@ The animal is a choices: ["Get 1"], credit: 1, preAction: { - componentName: "/num", + name: "/num", value: "6", recomputeIndices: true, type: "math", @@ -2768,7 +2708,7 @@ The animal is a choices: ["dog"], credit: 1, preAction: { - componentName: "/catCredit", + name: "/catCredit", value: "0.4", recomputeIndices: true, type: "math", @@ -2779,7 +2719,7 @@ The animal is a choices: ["cat"], credit: 0.4, preAction: { - componentName: "/last", + name: "/last", value: "mouse", recomputeIndices: true, type: "text", @@ -2790,7 +2730,7 @@ The animal is a choices: ["cat"], credit: 0.2, preAction: { - componentName: "/catCredit", + name: "/catCredit", value: "0.2", recomputeIndices: true, type: "math", @@ -2801,7 +2741,7 @@ The animal is a choices: ["rabbit"], credit: 0, preAction: { - componentName: "/last", + name: "/last", value: "rabbit", recomputeIndices: true, type: "text", @@ -2858,7 +2798,7 @@ Enter any letter: latex: "c", credit: 1, preAction: { - componentName: "/set", + name: "/set", value: "\\{a,b,c,d,e,f,g\\}", type: "math", }, @@ -2869,7 +2809,7 @@ Enter any letter: latex: "2", credit: 0, preAction: { - componentName: "/set", + name: "/set", value: "\\{(x+y)/2, e^{x^2+y}, (1,2,3) \\}", type: "math", }, @@ -2976,7 +2916,7 @@ Enter any letter: { preAction: { type: "math", - componentName: "/mi", + name: "/mi", value: "4", }, actionArgs: { x: 3, y: -3 }, @@ -3027,7 +2967,7 @@ Enter any letter: latex: "", credit: 0, preAction: { - componentName: "/min", + name: "/min", value: "2", type: "math", }, @@ -3038,7 +2978,7 @@ Enter any letter: latex: "2", credit: 1, preAction: { - componentName: "/min", + name: "/min", value: "1.9", type: "math", }, @@ -3227,19 +3167,13 @@ Enter any letter: for (let ind2 = 1; ind2 <= 4; ind2++) { let selectedIndices = [indexByName[options[ind2 - 1]]]; - await core.requestAction({ - componentName: "/ci", - actionName: "updateSelectedIndices", - args: { selectedIndices }, - event: null, + await updateSelectedIndices({ + name: "/ci", + selectedIndices, + core, }); - await core.requestAction({ - componentName: "/ans", - actionName: "submitAnswer", - args: {}, - event: null, - }); + await submitAnswer({ name: "/ans", core }); stateVariables = await returnAllStateVariables(core); @@ -3299,16 +3233,11 @@ Enter any letter: for (let ind2 = 1; ind2 <= 4; ind2++) { await updateTextInputValue({ text: options[ind2 - 1], - componentName: textInputName, + name: textInputName, core, }); - await core.requestAction({ - componentName: "/ans", - actionName: "submitAnswer", - args: {}, - event: null, - }); + await submitAnswer({ name: "/ans", core }); stateVariables = await returnAllStateVariables(core); @@ -3354,7 +3283,7 @@ Enter any letter: credit: 0, preAction: { type: "math", - componentName: "/nAwards", + name: "/nAwards", value: "3", }, awardsUsed: ["/FirstNumber", "/SecondNumber"], @@ -3373,7 +3302,7 @@ Enter any letter: credit: 0.4, preAction: { type: "math", - componentName: "/nAwards", + name: "/nAwards", value: "1", }, awardsUsed: ["/FirstGreater3"], @@ -3388,7 +3317,7 @@ Enter any letter: credit: 0.8, preAction: { type: "math", - componentName: "/nAwards", + name: "/nAwards", value: "3", }, awardsUsed: [ @@ -3411,7 +3340,7 @@ Enter any letter: credit: 1, preAction: { type: "math", - componentName: "/nAwards", + name: "/nAwards", value: "1", }, awardsUsed: ["/DistinctGreater3"], @@ -3421,7 +3350,7 @@ Enter any letter: credit: 0.4, preAction: { type: "math", - componentName: "/creditForCombined", + name: "/creditForCombined", value: "0.2", }, awardsUsed: ["/FirstGreater3"], @@ -3431,7 +3360,7 @@ Enter any letter: credit: 0.8, preAction: { type: "math", - componentName: "/nAwards", + name: "/nAwards", value: "2", }, awardsUsed: ["/FirstGreater3", "/SecondGreater3"], @@ -3441,7 +3370,7 @@ Enter any letter: credit: 1, preAction: { type: "math", - componentName: "/nAwards", + name: "/nAwards", value: "3", }, awardsUsed: [ @@ -3881,7 +3810,7 @@ Enter any letter: preAction: { type: "boolean", value: "true", - componentName: "/split", + name: "/split", }, }, { latex: "xyz", credit: 1 }, @@ -3892,7 +3821,7 @@ Enter any letter: preAction: { type: "boolean", value: "false", - componentName: "/split", + name: "/split", }, overrideResponse: "xyzb", }, @@ -3918,7 +3847,7 @@ Enter any letter: preAction: { type: "boolean", value: "true", - componentName: "/split", + name: "/split", }, }, { latex: "xyz", credit: 1 }, @@ -3929,7 +3858,7 @@ Enter any letter: preAction: { type: "boolean", value: "false", - componentName: "/split", + name: "/split", }, overrideResponse: "xyzb", }, @@ -3957,7 +3886,7 @@ Enter any letter: preAction: { type: "boolean", value: "true", - componentName: "/split", + name: "/split", }, }, { latex: "xyz", credit: 1 }, @@ -3968,7 +3897,7 @@ Enter any letter: preAction: { type: "boolean", value: "false", - componentName: "/split", + name: "/split", }, overrideResponse: "xyzb", }, @@ -3997,7 +3926,7 @@ Enter any letter: preAction: { type: "boolean", value: "true", - componentName: "/split", + name: "/split", }, }, { latex: "xyz", credit: 1 }, @@ -4008,7 +3937,7 @@ Enter any letter: preAction: { type: "boolean", value: "false", - componentName: "/split", + name: "/split", }, overrideResponse: "xyzb", }, @@ -4025,15 +3954,10 @@ Enter any letter: `, }); - await updateMathInputValue({ latex: "x", componentName: "/mi", core }); + await updateMathInputValue({ latex: "x", name: "/mi", core }); // submit - await core.requestAction({ - componentName: "/a", - actionName: "submitAnswer", - args: {}, - event: null, - }); + await submitAnswer({ name: "/a", core }); let stateVariables = await returnAllStateVariables(core); @@ -4058,12 +3982,7 @@ Enter any letter: expect(stateVariables["/ans"].stateValues.creditAchieved).eq(0); // submit - await core.requestAction({ - componentName: "/ans", - actionName: "submitAnswer", - args: {}, - event: null, - }); + await submitAnswer({ name: "/ans", core }); stateVariables = await returnAllStateVariables(core); @@ -4078,19 +3997,14 @@ Enter any letter: expect(stateVariables["/ans"].stateValues.justSubmitted).eq(true); expect(stateVariables["/ans"].stateValues.creditAchieved).eq(0); - await updateMathInputValue({ latex: "1", componentName: "/mi", core }); + await updateMathInputValue({ latex: "1", name: "/mi", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/cond"].replacementsToWithhold).eq(1); expect(stateVariables["/ans"].stateValues.justSubmitted).eq(false); expect(stateVariables["/ans"].stateValues.creditAchieved).eq(0); // submit - await core.requestAction({ - componentName: "/ans", - actionName: "submitAnswer", - args: {}, - event: null, - }); + await submitAnswer({ name: "/ans", core }); stateVariables = await returnAllStateVariables(core); @@ -4106,12 +4020,12 @@ Enter any letter: await updateMathInputImmediateValue({ latex: "0", - componentName: "/mi", + name: "/mi", core, }); await updateMathInputImmediateValue({ latex: "1", - componentName: "/mi", + name: "/mi", core, }); stateVariables = await returnAllStateVariables(core); @@ -4153,30 +4067,10 @@ Enter any letter: `, }); - await core.requestAction({ - componentName: "/ans1", - actionName: "submitAnswer", - args: {}, - event: null, - }); - await core.requestAction({ - componentName: "/ans2", - actionName: "submitAnswer", - args: {}, - event: null, - }); - await core.requestAction({ - componentName: "/ans3", - actionName: "submitAnswer", - args: {}, - event: null, - }); - await core.requestAction({ - componentName: "/ans4", - actionName: "submitAnswer", - args: {}, - event: null, - }); + await submitAnswer({ name: "/ans1", core }); + await submitAnswer({ name: "/ans2", core }); + await submitAnswer({ name: "/ans3", core }); + await submitAnswer({ name: "/ans4", core }); let stateVariables = await returnAllStateVariables(core); @@ -4206,12 +4100,12 @@ Enter any letter: await updateMathInputValue({ latex: "y", - componentName: "/micr", + name: "/micr", core, }); await updateMathInputValue({ latex: "z", - componentName: "/misr", + name: "/misr", core, }); @@ -4220,13 +4114,8 @@ Enter any letter: expect(stateVariables["/sr"]).eq(undefined); // submit response - await updateMathInputValue({ latex: "x", componentName: "/mia", core }); - await core.requestAction({ - componentName: "/a", - actionName: "submitAnswer", - args: {}, - event: null, - }); + await updateMathInputValue({ latex: "x", name: "/mia", core }); + await submitAnswer({ name: "/a", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/cr"].stateValues.value.tree).eq("x"); @@ -4236,12 +4125,12 @@ Enter any letter: await updateMathInputValue({ latex: "y", - componentName: "/micr", + name: "/micr", core, }); await updateMathInputValue({ latex: "z", - componentName: "/misr", + name: "/misr", core, }); @@ -4349,7 +4238,7 @@ Enter any letter: // Type correct answer in first blank await updateMathInputValue({ latex: "x+y", - componentName: mathInput1Name, + name: mathInput1Name, core, }); stateVariables = await returnAllStateVariables(core); @@ -4372,12 +4261,7 @@ Enter any letter: expect(stateVariables["/ans2"].stateValues.submittedResponses).eqls([]); // submit second answer - await core.requestAction({ - componentName: "/ans2", - actionName: "submitAnswer", - args: {}, - event: null, - }); + await submitAnswer({ name: "/ans2", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/ans1"].stateValues.justSubmitted).eq(true); @@ -4408,7 +4292,7 @@ Enter any letter: // type incorrect answer into second blank await updateMathInputValue({ latex: "x", - componentName: mathInput2Name, + name: mathInput2Name, core, }); stateVariables = await returnAllStateVariables(core); @@ -4439,12 +4323,7 @@ Enter any letter: ).eqls([["+", "x", "y"]]); // submit first answer - await core.requestAction({ - componentName: "/ans1", - actionName: "submitAnswer", - args: {}, - event: null, - }); + await submitAnswer({ name: "/ans1", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/ans1"].stateValues.justSubmitted).eq(true); @@ -4509,7 +4388,7 @@ Enter any letter: // Type correct answer in first blank await updateMathInputValue({ latex: "x+y", - componentName: mathInput1Name, + name: mathInput1Name, core, }); stateVariables = await returnAllStateVariables(core); @@ -4532,12 +4411,7 @@ Enter any letter: expect(stateVariables["/ans2"].stateValues.submittedResponses).eqls([]); // submit first answer - await core.requestAction({ - componentName: "/ans1", - actionName: "submitAnswer", - args: {}, - event: null, - }); + await submitAnswer({ name: "/ans1", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/ans1"].stateValues.justSubmitted).eq(true); @@ -4564,7 +4438,7 @@ Enter any letter: // type correct answer into second blank await updateMathInputValue({ latex: "x+y", - componentName: mathInput2Name, + name: mathInput2Name, core, }); stateVariables = await returnAllStateVariables(core); @@ -4591,12 +4465,7 @@ Enter any letter: expect(stateVariables["/ans2"].stateValues.submittedResponses).eqls([]); // submit second answer - await core.requestAction({ - componentName: "/ans2", - actionName: "submitAnswer", - args: {}, - event: null, - }); + await submitAnswer({ name: "/ans2", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/ans1"].stateValues.justSubmitted).eq(true); @@ -4627,7 +4496,7 @@ Enter any letter: // type incorrect answer into second blank await updateMathInputValue({ latex: "x", - componentName: mathInput2Name, + name: mathInput2Name, core, }); stateVariables = await returnAllStateVariables(core); @@ -4658,12 +4527,7 @@ Enter any letter: ).eqls([["+", "x", "y"]]); // submit second answer - await core.requestAction({ - componentName: "/ans2", - actionName: "submitAnswer", - args: {}, - event: null, - }); + await submitAnswer({ name: "/ans2", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/ans1"].stateValues.justSubmitted).eq(true); @@ -4694,7 +4558,7 @@ Enter any letter: // type incorrect answer into first blank await updateMathInputValue({ latex: "x", - componentName: mathInput1Name, + name: mathInput1Name, core, }); stateVariables = await returnAllStateVariables(core); @@ -4725,12 +4589,7 @@ Enter any letter: ).eqls(["x"]); // submit first answer - await core.requestAction({ - componentName: "/ans1", - actionName: "submitAnswer", - args: {}, - event: null, - }); + await submitAnswer({ name: "/ans1", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/ans1"].stateValues.justSubmitted).eq(true); @@ -4812,7 +4671,7 @@ Enter any letter: // type correct answer await updateMathInputValue({ latex: "x^2-2x+3", - componentName: mathInputName, + name: mathInputName, core, }); @@ -4841,12 +4700,7 @@ Enter any letter: // submit - await core.requestAction({ - componentName: "/ans", - actionName: "submitAnswer", - args: {}, - event: null, - }); + await submitAnswer({ name: "/ans", core }); expect(components["/ans"].stateValues.justSubmitted).eq(true); expect(components["/ans"].stateValues.creditAchieved).eq(1); @@ -4893,7 +4747,7 @@ Enter any letter: // type partially correct answer await updateMathInputValue({ latex: "x^2-2x-3", - componentName: mathInputName, + name: mathInputName, core, }); @@ -4924,12 +4778,7 @@ Enter any letter: // submit - await core.requestAction({ - componentName: "/ans", - actionName: "submitAnswer", - args: {}, - event: null, - }); + await submitAnswer({ name: "/ans", core }); expect(components["/ans"].stateValues.justSubmitted).eq(true); expect(components["/ans"].stateValues.creditAchieved).eq(0.5); @@ -4996,7 +4845,7 @@ Enter any letter: { preAction: { type: "math", - componentName: "/mi", + name: "/mi", value: "x^2", }, actionArgs: { x: 0, y: 0 }, @@ -5015,7 +4864,7 @@ Enter any letter: { preAction: { type: "math", - componentName: "/mi", + name: "/mi", value: "y^2", }, actionArgs: { x: 2, y: -7 }, @@ -5249,18 +5098,13 @@ What is the derivative of x^2? expect(stateVariables["/xsr"].stateValues.value.tree).eq("\uff3f"); expect(stateVariables["/xcr"].stateValues.value.tree).eq("\uff3f"); - await updateMathInputValue({ latex: "x", componentName: "/mi", core }); + await updateMathInputValue({ latex: "x", name: "/mi", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/xsr"].stateValues.value.tree).eq("\uff3f"); expect(stateVariables["/xcr"].stateValues.value.tree).eq("x"); - await core.requestAction({ - componentName: "/x", - actionName: "submitAnswer", - args: {}, - event: null, - }); + await submitAnswer({ name: "/x", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/xsr"].stateValues.value.tree).eq("x"); expect(stateVariables["/xcr"].stateValues.value.tree).eq("x"); @@ -5270,7 +5114,7 @@ What is the derivative of x^2? await updateTextInputValue({ text: "hello", - componentName: "/ti", + name: "/ti", core, }); stateVariables = await returnAllStateVariables(core); @@ -5278,12 +5122,7 @@ What is the derivative of x^2? expect(stateVariables["/hellosr"].stateValues.value).eq(""); expect(stateVariables["/hellocr"].stateValues.value).eq("hello"); - await core.requestAction({ - componentName: "/hello", - actionName: "submitAnswer", - args: {}, - event: null, - }); + await submitAnswer({ name: "/hello", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/hellosr"].stateValues.value).eq("hello"); @@ -5294,7 +5133,7 @@ What is the derivative of x^2? await updateBooleanInputValue({ boolean: true, - componentName: "/bi", + name: "/bi", core, }); stateVariables = await returnAllStateVariables(core); @@ -5302,12 +5141,7 @@ What is the derivative of x^2? expect(stateVariables["/bsr"].stateValues.value).eq(false); expect(stateVariables["/bcr"].stateValues.value).eq(true); - await core.requestAction({ - componentName: "/b", - actionName: "submitAnswer", - args: {}, - event: null, - }); + await submitAnswer({ name: "/b", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/bsr"].stateValues.value).eq(true); @@ -5646,28 +5480,18 @@ What is the derivative of x^2? await updateMathInputValue({ latex: "y", - componentName: "/miDefault", + name: "/miDefault", core, }); await updateMathInputValue({ latex: "y", - componentName: "/miLong", + name: "/miLong", core, }); - await core.requestAction({ - componentName: "/default", - actionName: "submitAnswer", - args: {}, - event: null, - }); + await submitAnswer({ name: "/default", core }); - await core.requestAction({ - componentName: "/long", - actionName: "submitAnswer", - args: {}, - event: null, - }); + await submitAnswer({ name: "/long", core }); let stateVariables = await returnAllStateVariables(core); @@ -5703,12 +5527,12 @@ What is the derivative of x^2? await updateMathInputValue({ latex: "1.23456789", - componentName: "/miDefault", + name: "/miDefault", core, }); await updateMathInputValue({ latex: "1.23456789", - componentName: "/miShort", + name: "/miShort", core, }); @@ -5727,19 +5551,9 @@ What is the derivative of x^2? "\uff3f", ); - await core.requestAction({ - componentName: "/default", - actionName: "submitAnswer", - args: {}, - event: null, - }); + await submitAnswer({ name: "/default", core }); - await core.requestAction({ - componentName: "/short", - actionName: "submitAnswer", - args: {}, - event: null, - }); + await submitAnswer({ name: "/short", core }); stateVariables = await returnAllStateVariables(core); @@ -5781,12 +5595,12 @@ What is the derivative of x^2? await updateMathInputValue({ latex: "1.23456789", - componentName: "/miDefault", + name: "/miDefault", core, }); await updateMathInputValue({ latex: "1.23456789", - componentName: "/miShort", + name: "/miShort", core, }); @@ -5805,19 +5619,9 @@ What is the derivative of x^2? NaN, ); - await core.requestAction({ - componentName: "/default", - actionName: "submitAnswer", - args: {}, - event: null, - }); + await submitAnswer({ name: "/default", core }); - await core.requestAction({ - componentName: "/short", - actionName: "submitAnswer", - args: {}, - event: null, - }); + await submitAnswer({ name: "/short", core }); stateVariables = await returnAllStateVariables(core); @@ -5870,18 +5674,12 @@ What is the derivative of x^2? } async function submit_selection(name: string) { - await core.requestAction({ - componentName: "/ci", - actionName: "updateSelectedIndices", - args: { selectedIndices: [indexByName[name]] }, - event: null, - }); - await core.requestAction({ - componentName: "/a", - actionName: "submitAnswer", - args: {}, - event: null, + await updateSelectedIndices({ + name: "/ci", + selectedIndices: [indexByName[name]], + core, }); + await submitAnswer({ name: "/a", core }); } await submit_selection("stable"); @@ -5891,7 +5689,7 @@ What is the derivative of x^2? stateVariables = await returnAllStateVariables(core); expect(stateVariables["/a"].stateValues.creditAchieved).eq(0); - await updateMathInputValue({ latex: "3", componentName: "/m", core }); + await updateMathInputValue({ latex: "3", name: "/m", core }); await submit_selection("stable"); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/a"].stateValues.creditAchieved).eq(0); @@ -5901,7 +5699,7 @@ What is the derivative of x^2? await updateMathInputValue({ latex: "-0.8", - componentName: "/m", + name: "/m", core, }); await submit_selection("stable"); @@ -5911,7 +5709,7 @@ What is the derivative of x^2? stateVariables = await returnAllStateVariables(core); expect(stateVariables["/a"].stateValues.creditAchieved).eq(0); - await updateMathInputValue({ latex: "1/3", componentName: "/m", core }); + await updateMathInputValue({ latex: "1/3", name: "/m", core }); await submit_selection("stable"); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/a"].stateValues.creditAchieved).eq(1); @@ -5921,7 +5719,7 @@ What is the derivative of x^2? await updateMathInputValue({ latex: "-7/5", - componentName: "/m", + name: "/m", core, }); await submit_selection("stable"); @@ -5931,7 +5729,7 @@ What is the derivative of x^2? stateVariables = await returnAllStateVariables(core); expect(stateVariables["/a"].stateValues.creditAchieved).eq(1); - await updateMathInputValue({ latex: "1", componentName: "/m", core }); + await updateMathInputValue({ latex: "1", name: "/m", core }); await submit_selection("stable"); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/a"].stateValues.creditAchieved).eq(0); @@ -5973,7 +5771,7 @@ What is the derivative of x^2? latex: "x < 0", credit: 1, preAction: { - componentName: "/c", + name: "/c", type: "choice", value: "2", }, @@ -5983,7 +5781,7 @@ What is the derivative of x^2? latex: "x > 0", credit: 1, preAction: { - componentName: "/c", + name: "/c", type: "choice", value: "1", }, diff --git a/packages/doenetml-worker/src/test/tagSpecific/bestfitline.test.ts b/packages/doenetml-worker/src/test/tagSpecific/bestfitline.test.ts new file mode 100644 index 000000000..38983ee95 --- /dev/null +++ b/packages/doenetml-worker/src/test/tagSpecific/bestfitline.test.ts @@ -0,0 +1,292 @@ +import { describe, expect, it, vi } from "vitest"; +import { createTestCore, returnAllStateVariables } from "../utils/test-core"; +import { movePoint } from "../utils/actions"; +import me from "math-expressions"; + +const Mock = vi.fn(); +vi.stubGlobal("postMessage", Mock); +vi.mock("hyperformula"); + +describe("BestFitLine tag tests", async () => { + it("fit line to 4 points", async () => { + let core = await createTestCore({ + doenetML: ` + + + + + + (1,2) + (1,6) + + (7,3) + (7,-1) + + + + + + $l.equation{assignNames="eq"} + `, + }); + + let stateVariables = await returnAllStateVariables(core); + let eqTree = me.fromText("y=-0.5x+4.5").simplify().tree; + expect(stateVariables["/l"].stateValues.equation.tree).eqls(eqTree); + expect(stateVariables["/eq"].stateValues.value.tree).eqls(eqTree); + + // move points + + await movePoint({ name: "/P1", x: -5, y: -8, core }); + await movePoint({ name: "/P2", x: 3, y: 5, core }); + await movePoint({ name: "/P3", x: -5, y: -10, core }); + await movePoint({ name: "/P4", x: 3, y: 9, core }); + + stateVariables = await returnAllStateVariables(core); + eqTree = me.fromText("y=2x+1").simplify().tree; + expect(stateVariables["/l"].stateValues.equation.tree).eqls(eqTree); + expect(stateVariables["/eq"].stateValues.value.tree).eqls(eqTree); + }); + + it("no arguments", async () => { + let core = await createTestCore({ + doenetML: ` + + + + + + $l.equation{assignNames="eq"} + `, + }); + + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/l"].stateValues.equation.tree).eqls("_"); + expect(stateVariables["/eq"].stateValues.value.tree).eqls("_"); + }); + + it("fit line to 0 points", async () => { + let core = await createTestCore({ + doenetML: ` + + + + + + + + + $l.equation{assignNames="eq"} + `, + }); + + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/l"].stateValues.equation.tree).eqls("_"); + expect(stateVariables["/eq"].stateValues.value.tree).eqls("_"); + }); + + it("fit line to 1 point", async () => { + let core = await createTestCore({ + doenetML: ` + + + + + + (3,4) + + + + $l.equation{assignNames="eq"} + `, + }); + + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/l"].stateValues.equation.tree).eqls([ + "=", + "y", + 4, + ]); + expect(stateVariables["/eq"].stateValues.value.tree).eqls([ + "=", + "y", + 4, + ]); + + // move point + await movePoint({ name: "/P1", x: -5, y: -8, core }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/l"].stateValues.equation.tree).eqls([ + "=", + "y", + -8, + ]); + expect(stateVariables["/eq"].stateValues.value.tree).eqls([ + "=", + "y", + -8, + ]); + }); + + it("fit line to 2 points, change variables", async () => { + let core = await createTestCore({ + doenetML: ` + + + + + + (3,4) + (-5,0) + + + + $l.equation{assignNames="eq"} + `, + }); + + let stateVariables = await returnAllStateVariables(core); + let eqTree = me.fromText("z=0.5t+2.5").simplify().tree; + expect(stateVariables["/l"].stateValues.equation.tree).eqls(eqTree); + expect(stateVariables["/eq"].stateValues.value.tree).eqls(eqTree); + + // move points to be vertical + await movePoint({ name: "/P1", x: -5, y: -8, core }); + + stateVariables = await returnAllStateVariables(core); + eqTree = me.fromText("z=-4").tree; + expect(stateVariables["/l"].stateValues.equation.tree).eqls(eqTree); + expect(stateVariables["/eq"].stateValues.value.tree).eqls(eqTree); + + // move points + await movePoint({ name: "/P2", x: -4, y: -6, core }); + stateVariables = await returnAllStateVariables(core); + eqTree = me.fromText("z=2t+2").simplify().tree; + expect(stateVariables["/l"].stateValues.equation.tree).eqls(eqTree); + expect(stateVariables["/eq"].stateValues.value.tree).eqls(eqTree); + }); + + it("fit line to points of different dimensions", async () => { + let core = await createTestCore({ + doenetML: ` + + + + + + (1,2) + (1,6, a) + (7,3,3,1,5) + (7,-1,5,x) + + + + + $l.equation{assignNames="eq"} + + `, + }); + + let stateVariables = await returnAllStateVariables(core); + let eqTree = me.fromText("y=-0.5x+4.5").simplify().tree; + expect(stateVariables["/l"].stateValues.equation.tree).eqls(eqTree); + expect(stateVariables["/eq"].stateValues.value.tree).eqls(eqTree); + + // move points + + await movePoint({ name: "/P1", x: -5, y: -8, core }); + await movePoint({ name: "/P2", x: 3, y: 5, core }); + await movePoint({ name: "/P3", x: -5, y: -10, core }); + await movePoint({ name: "/P4", x: 3, y: 9, core }); + + stateVariables = await returnAllStateVariables(core); + eqTree = me.fromText("y=2x+1").simplify().tree; + expect(stateVariables["/l"].stateValues.equation.tree).eqls(eqTree); + expect(stateVariables["/eq"].stateValues.value.tree).eqls(eqTree); + }); + + it("fit line to 4 points, ignore non-numerical points", async () => { + let core = await createTestCore({ + doenetML: ` + + + + + + (a,b) + (1,2) + (c,2) + (1,6) + (1,d) + (7,3) + (7+f,3+g) + (7,-1) + (,-1) + + + + + $l.equation{assignNames="eq"} + `, + }); + + let stateVariables = await returnAllStateVariables(core); + let eqTree = me.fromText("y=-0.5x+4.5").simplify().tree; + expect(stateVariables["/l"].stateValues.equation.tree).eqls(eqTree); + expect(stateVariables["/eq"].stateValues.value.tree).eqls(eqTree); + + // move points + + await movePoint({ name: "/P2", x: -5, y: -8, core }); + await movePoint({ name: "/P4", x: 3, y: 5, core }); + await movePoint({ name: "/P6", x: -5, y: -10, core }); + await movePoint({ name: "/P8", x: 3, y: 9, core }); + + stateVariables = await returnAllStateVariables(core); + eqTree = me.fromText("y=2x+1").simplify().tree; + expect(stateVariables["/l"].stateValues.equation.tree).eqls(eqTree); + expect(stateVariables["/eq"].stateValues.value.tree).eqls(eqTree); + }); + + it("rounding", async () => { + let core = await createTestCore({ + doenetML: ` + + + + + + (1.2345678,2.3456789) + (1.2345678,6.7891234) + (7.8912345,3.4567891) + (7.8912345,-1.2345678) + + + + + + + $l.equation{assignNames="eq"} +

data: $l.data

+ + $l2.equation{assignNames="eq2"} +

data2: $l2.data

+ `, + }); + + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/eq"].stateValues.text).eq( + "y = -0.519 x + 5.21", + ); + expect(stateVariables["/data"].stateValues.text).eq( + "data: ( 1.23, 2.35 ), ( 1.23, 6.79 ), ( 7.89, 3.46 ), ( 7.89, -1.23 )", + ); + expect(stateVariables["/eq2"].stateValues.text).eq( + "y = -0.51922 x + 5.2084", + ); + expect(stateVariables["/data2"].stateValues.text).eq( + "data2: ( 1.2346, 2.3457 ), ( 1.2346, 6.7891 ), ( 7.8912, 3.4568 ), ( 7.8912, -1.2346 )", + ); + }); +}); diff --git a/packages/doenetml-worker/src/test/tagSpecific/boolean.test.ts b/packages/doenetml-worker/src/test/tagSpecific/boolean.test.ts index 26188e1e2..bf1b7dc9c 100644 --- a/packages/doenetml-worker/src/test/tagSpecific/boolean.test.ts +++ b/packages/doenetml-worker/src/test/tagSpecific/boolean.test.ts @@ -3,11 +3,13 @@ import { createTestCore, returnAllStateVariables } from "../utils/test-core"; import { updateBooleanInputValue, updateMathInputValue, + updateSelectedIndices, updateTextInputValue, } from "../utils/actions"; const Mock = vi.fn(); vi.stubGlobal("postMessage", Mock); +vi.mock("hyperformula"); describe("Boolean tag tests", async () => { it("basic boolean evaluation", async () => { @@ -123,17 +125,17 @@ describe("Boolean tag tests", async () => { let stateVariables = await returnAllStateVariables(core); expect(stateVariables[`/b`].stateValues.value).to.be.false; - await updateMathInputValue({ latex: "3", componentName: "/mi", core }); + await updateMathInputValue({ latex: "3", name: "/mi", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables[`/b`].stateValues.value).to.be.true; - await updateMathInputValue({ latex: "2x", componentName: "/mi", core }); + await updateMathInputValue({ latex: "2x", name: "/mi", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables[`/b`].stateValues.value).to.be.false; await updateMathInputValue({ latex: "2x-x-x", - componentName: "/mi", + name: "/mi", core, }); stateVariables = await returnAllStateVariables(core); @@ -204,21 +206,21 @@ describe("Boolean tag tests", async () => { expect(stateVariables[`/b3`].stateValues.value).to.be.false; expect(stateVariables[`/b4`].stateValues.value).to.be.false; - await updateMathInputValue({ latex: "4", componentName: "/i", core }); + await updateMathInputValue({ latex: "4", name: "/i", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables[`/b1`].stateValues.value).to.be.false; expect(stateVariables[`/b2`].stateValues.value).to.be.false; expect(stateVariables[`/b3`].stateValues.value).to.be.true; expect(stateVariables[`/b4`].stateValues.value).to.be.true; - await updateMathInputValue({ latex: "-7", componentName: "/i", core }); + await updateMathInputValue({ latex: "-7", name: "/i", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables[`/b1`].stateValues.value).to.be.true; expect(stateVariables[`/b2`].stateValues.value).to.be.true; expect(stateVariables[`/b3`].stateValues.value).to.be.false; expect(stateVariables[`/b4`].stateValues.value).to.be.false; - await updateMathInputValue({ latex: "0", componentName: "/i", core }); + await updateMathInputValue({ latex: "0", name: "/i", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables[`/b1`].stateValues.value).to.be.false; expect(stateVariables[`/b2`].stateValues.value).to.be.false; @@ -1252,12 +1254,7 @@ describe("Boolean tag tests", async () => { expect(stateVariables[`/three`].stateValues.value).to.be.false; expect(stateVariables[`/four`].stateValues.value).to.be.false; - await core.requestAction({ - actionName: "updateSelectedIndices", - componentName: "/c", - args: { selectedIndices: [1] }, - event: null, - }); + await updateSelectedIndices({ name: "/c", selectedIndices: [1], core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables[`/one`].stateValues.value).to.be.true; @@ -1265,12 +1262,7 @@ describe("Boolean tag tests", async () => { expect(stateVariables[`/three`].stateValues.value).to.be.false; expect(stateVariables[`/four`].stateValues.value).to.be.false; - await core.requestAction({ - actionName: "updateSelectedIndices", - componentName: "/c", - args: { selectedIndices: [2] }, - event: null, - }); + await updateSelectedIndices({ name: "/c", selectedIndices: [2], core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables[`/one`].stateValues.value).to.be.false; @@ -1278,12 +1270,7 @@ describe("Boolean tag tests", async () => { expect(stateVariables[`/three`].stateValues.value).to.be.false; expect(stateVariables[`/four`].stateValues.value).to.be.false; - await core.requestAction({ - actionName: "updateSelectedIndices", - componentName: "/c", - args: { selectedIndices: [3] }, - event: null, - }); + await updateSelectedIndices({ name: "/c", selectedIndices: [3], core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables[`/one`].stateValues.value).to.be.false; @@ -1291,12 +1278,7 @@ describe("Boolean tag tests", async () => { expect(stateVariables[`/three`].stateValues.value).to.be.true; expect(stateVariables[`/four`].stateValues.value).to.be.false; - await core.requestAction({ - actionName: "updateSelectedIndices", - componentName: "/c", - args: { selectedIndices: [4] }, - event: null, - }); + await updateSelectedIndices({ name: "/c", selectedIndices: [4], core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables[`/one`].stateValues.value).to.be.false; @@ -1323,7 +1305,7 @@ describe("Boolean tag tests", async () => { await updateBooleanInputValue({ boolean: true, - componentName: "/bi", + name: "/bi", core, }); stateVariables = await returnAllStateVariables(core); @@ -1335,7 +1317,7 @@ describe("Boolean tag tests", async () => { await updateTextInputValue({ text: "false", - componentName: "/ti", + name: "/ti", core, }); stateVariables = await returnAllStateVariables(core); @@ -1347,7 +1329,7 @@ describe("Boolean tag tests", async () => { await updateTextInputValue({ text: "tRuE", - componentName: "/ti", + name: "/ti", core, }); stateVariables = await returnAllStateVariables(core); @@ -1357,7 +1339,7 @@ describe("Boolean tag tests", async () => { ); expect(stateVariables[`/ti`].stateValues.value).eq("true"); - await updateTextInputValue({ text: "0", componentName: "/ti", core }); + await updateTextInputValue({ text: "0", name: "/ti", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables[`/t`].stateValues.value).eq( @@ -1365,7 +1347,7 @@ describe("Boolean tag tests", async () => { ); expect(stateVariables[`/ti`].stateValues.value).eq("true"); - await updateTextInputValue({ text: "1=0", componentName: "/ti", core }); + await updateTextInputValue({ text: "1=0", name: "/ti", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables[`/t`].stateValues.value).eq( @@ -1373,7 +1355,7 @@ describe("Boolean tag tests", async () => { ); expect(stateVariables[`/ti`].stateValues.value).eq("true"); - await updateTextInputValue({ text: "f", componentName: "/ti", core }); + await updateTextInputValue({ text: "f", name: "/ti", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables[`/t`].stateValues.value).eq( @@ -1383,7 +1365,7 @@ describe("Boolean tag tests", async () => { await updateTextInputValue({ text: "FALSE", - componentName: "/ti", + name: "/ti", core, }); stateVariables = await returnAllStateVariables(core); @@ -1393,7 +1375,7 @@ describe("Boolean tag tests", async () => { ); expect(stateVariables[`/ti`].stateValues.value).eq("false"); - await updateTextInputValue({ text: "1", componentName: "/ti", core }); + await updateTextInputValue({ text: "1", name: "/ti", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables[`/t`].stateValues.value).eq( @@ -1401,7 +1383,7 @@ describe("Boolean tag tests", async () => { ); expect(stateVariables[`/ti`].stateValues.value).eq("false"); - await updateTextInputValue({ text: "t", componentName: "/ti", core }); + await updateTextInputValue({ text: "t", name: "/ti", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables[`/t`].stateValues.value).eq( @@ -1411,7 +1393,7 @@ describe("Boolean tag tests", async () => { await updateBooleanInputValue({ boolean: true, - componentName: "/bi", + name: "/bi", core, }); stateVariables = await returnAllStateVariables(core); @@ -1423,7 +1405,7 @@ describe("Boolean tag tests", async () => { await updateBooleanInputValue({ boolean: false, - componentName: "/bi", + name: "/bi", core, }); stateVariables = await returnAllStateVariables(core); diff --git a/packages/doenetml-worker/src/test/tagSpecific/booleaninput.test.ts b/packages/doenetml-worker/src/test/tagSpecific/booleaninput.test.ts index 311bb3516..b03f79105 100644 --- a/packages/doenetml-worker/src/test/tagSpecific/booleaninput.test.ts +++ b/packages/doenetml-worker/src/test/tagSpecific/booleaninput.test.ts @@ -2,12 +2,17 @@ import { describe, expect, it, vi } from "vitest"; import { createTestCore, returnAllStateVariables } from "../utils/test-core"; import { cleanLatex } from "../utils/math"; import { + moveInput, updateBooleanInputValue, updateMathInputValue, + updateSelectedIndices, + updateValue, } from "../utils/actions"; +import { test_in_graph } from "../utils/in-graph"; const Mock = vi.fn(); vi.stubGlobal("postMessage", Mock); +vi.mock("hyperformula"); describe("BooleanInput tag tests", async () => { it("single boolean input", async () => { @@ -30,7 +35,7 @@ describe("BooleanInput tag tests", async () => { // check the box await updateBooleanInputValue({ boolean: true, - componentName: "/bi1", + name: "/bi1", core, }); stateVariables = await returnAllStateVariables(core); @@ -41,7 +46,7 @@ describe("BooleanInput tag tests", async () => { // uncheck the box await updateBooleanInputValue({ boolean: false, - componentName: "/bi1", + name: "/bi1", core, }); stateVariables = await returnAllStateVariables(core); @@ -65,7 +70,7 @@ describe("BooleanInput tag tests", async () => { // uncheck the box await updateBooleanInputValue({ boolean: false, - componentName: "/bi1", + name: "/bi1", core, }); stateVariables = await returnAllStateVariables(core); @@ -75,7 +80,7 @@ describe("BooleanInput tag tests", async () => { // recheck the box await updateBooleanInputValue({ boolean: true, - componentName: "/bi1", + name: "/bi1", core, }); stateVariables = await returnAllStateVariables(core); @@ -134,7 +139,7 @@ describe("BooleanInput tag tests", async () => { bi1 = false; await updateBooleanInputValue({ boolean: bi1, - componentName: "/bi1", + name: "/bi1", core, }); await check_items(bi1, bi2); @@ -143,7 +148,7 @@ describe("BooleanInput tag tests", async () => { bi1 = true; await updateBooleanInputValue({ boolean: bi1, - componentName: "/bi1a", + name: "/bi1a", core, }); await check_items(bi1, bi2); @@ -152,7 +157,7 @@ describe("BooleanInput tag tests", async () => { bi2 = true; await updateBooleanInputValue({ boolean: bi2, - componentName: "/bi2", + name: "/bi2", core, }); await check_items(bi1, bi2); @@ -177,7 +182,7 @@ describe("BooleanInput tag tests", async () => { // change value await updateBooleanInputValue({ boolean: false, - componentName: "/bi1", + name: "/bi1", core, }); @@ -205,7 +210,7 @@ describe("BooleanInput tag tests", async () => { // change value await updateBooleanInputValue({ boolean: false, - componentName: "/bi1", + name: "/bi1", core, }); stateVariables = await returnAllStateVariables(core); @@ -229,7 +234,7 @@ describe("BooleanInput tag tests", async () => { // attempt to change value, but it reverts await updateBooleanInputValue({ boolean: true, - componentName: "/bi1", + name: "/bi1", core, }); @@ -257,7 +262,7 @@ describe("BooleanInput tag tests", async () => { // change value await updateBooleanInputValue({ boolean: false, - componentName: "/bi1", + name: "/bi1", core, }); @@ -285,7 +290,7 @@ describe("BooleanInput tag tests", async () => { // change value await updateBooleanInputValue({ boolean: false, - componentName: "/bi1", + name: "/bi1", core, }); stateVariables = await returnAllStateVariables(core); @@ -309,7 +314,7 @@ describe("BooleanInput tag tests", async () => { // attempt to change value, but it reverts await updateBooleanInputValue({ boolean: true, - componentName: "/bi1", + name: "/bi1", core, }); @@ -339,7 +344,7 @@ describe("BooleanInput tag tests", async () => { // change value await updateBooleanInputValue({ boolean: false, - componentName: "/bi1", + name: "/bi1", core, }); @@ -365,7 +370,7 @@ describe("BooleanInput tag tests", async () => { await updateBooleanInputValue({ boolean: true, - componentName: "/bi", + name: "/bi", core, }); stateVariables = await returnAllStateVariables(core); @@ -373,7 +378,7 @@ describe("BooleanInput tag tests", async () => { await updateBooleanInputValue({ boolean: false, - componentName: "/bi", + name: "/bi", core, }); stateVariables = await returnAllStateVariables(core); @@ -395,22 +400,12 @@ describe("BooleanInput tag tests", async () => { ); // hide label - await core.requestAction({ - componentName: "/toggleLabel", - actionName: "updateValue", - args: {}, - event: null, - }); + await updateValue({ name: "/toggleLabel", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/bi"].stateValues.label).eq(""); // show label again - await core.requestAction({ - componentName: "/toggleLabel", - actionName: "updateValue", - args: {}, - event: null, - }); + await updateValue({ name: "/toggleLabel", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/bi"].stateValues.label).eq( "It is \\(\\int_a^b f(x)\\,dx\\)", @@ -430,484 +425,15 @@ describe("BooleanInput tag tests", async () => { }); it("boolean input in graph", async () => { - let core = await createTestCore({ - doenetML: ` + const doenetMLsnippet = ` - - + + - -

Anchor 1 coordinates:

-

Anchor 2 coordinates:

-

Change anchor 1 coordinates:

-

Change anchor 2 coordinates:

-

Position from anchor 1: $booleanInput1.positionFromAnchor

-

Position from anchor 2: $booleanInput2.positionFromAnchor

-

Change position from anchor 1 - - upperRight - upperLeft - lowerRight - lowerLeft - left - right - top - bottom - center - -

-

Change position from anchor 2 - - upperRight - upperLeft - lowerRight - lowerLeft - left - right - top - bottom - center - -

-

Draggable 1: $draggable1

-

Draggable 2: $draggable2

-

Change draggable 1

-

Change draggable 2

-

Disabled 1: $disabled1

-

Disabled 2: $disabled2

-

Change disabled 1

-

Change disabled 2

-

Fixed 1: $fixed1

-

Fixed 2: $fixed2

-

Change fixed 1

-

Change fixed 2

-

FixLocation 1: $fixLocation1

-

FixLocation 2: $fixLocation2

-

Change fixLocation 1

-

Change fixLocation 2

- - `, - }); - - // TODO: how to click on the checkboxes and test if they are disabled? - - let stateVariables = await returnAllStateVariables(core); - - expect( - cleanLatex( - stateVariables["/booleanInput1anchor"].stateValues.latex, - ), - ).eq("(1,3)"); - expect( - cleanLatex( - stateVariables["/booleanInput2anchor"].stateValues.latex, - ), - ).eq("(0,0)"); - - expect(stateVariables["/pPositionFromAnchor1"].stateValues.text).eq( - "Position from anchor 1: upperright", - ); - expect(stateVariables["/pPositionFromAnchor2"].stateValues.text).eq( - "Position from anchor 2: center", - ); - expect( - stateVariables["/positionFromAnchor1"].stateValues.selectedIndices, - ).eqls([1]); - expect( - stateVariables["/positionFromAnchor2"].stateValues.selectedIndices, - ).eqls([9]); - - expect(stateVariables["/pDraggable1"].stateValues.text).eq( - "Draggable 1: true", - ); - expect(stateVariables["/pDraggable2"].stateValues.text).eq( - "Draggable 2: true", - ); - expect(stateVariables["/pDisabled1"].stateValues.text).eq( - "Disabled 1: true", - ); - expect(stateVariables["/pDisabled2"].stateValues.text).eq( - "Disabled 2: false", - ); - expect(stateVariables["/pFixed1"].stateValues.text).eq( - "Fixed 1: false", - ); - expect(stateVariables["/pFixed2"].stateValues.text).eq( - "Fixed 2: false", - ); - expect(stateVariables["/pFixLocation1"].stateValues.text).eq( - "FixLocation 1: false", - ); - expect(stateVariables["/pFixLocation2"].stateValues.text).eq( - "FixLocation 2: false", - ); - - // move booleanInputs by dragging - await core.requestAction({ - actionName: "moveInput", - componentName: "/booleanInput1", - args: { x: -2, y: 3 }, - event: null, - }); - await core.requestAction({ - actionName: "moveInput", - componentName: "/booleanInput2", - args: { x: 4, y: -5 }, - event: null, - }); - - stateVariables = await returnAllStateVariables(core); - - expect( - cleanLatex( - stateVariables["/booleanInput1anchor"].stateValues.latex, - ), - ).eq("(-2,3)"); - expect( - cleanLatex( - stateVariables["/booleanInput2anchor"].stateValues.latex, - ), - ).eq("(4,-5)"); - - // move booleanInputs by entering coordinates - - await updateMathInputValue({ - latex: "(6,7)", - componentName: "/anchorCoords1", - core, - }); - await updateMathInputValue({ - latex: "(8,9)", - componentName: "/anchorCoords2", - core, - }); - - stateVariables = await returnAllStateVariables(core); - - expect( - cleanLatex( - stateVariables["/booleanInput1anchor"].stateValues.latex, - ), - ).eq("(6,7)"); - expect( - cleanLatex( - stateVariables["/booleanInput2anchor"].stateValues.latex, - ), - ).eq("(8,9)"); - - // change position from anchor - await core.requestAction({ - actionName: "updateSelectedIndices", - componentName: "/positionFromAnchor1", - args: { selectedIndices: [4] }, - event: null, - }); - await core.requestAction({ - actionName: "updateSelectedIndices", - componentName: "/positionFromAnchor2", - args: { selectedIndices: [3] }, - event: null, - }); - - stateVariables = await returnAllStateVariables(core); - - expect(stateVariables["/pPositionFromAnchor1"].stateValues.text).eq( - "Position from anchor 1: lowerleft", - ); - expect(stateVariables["/pPositionFromAnchor2"].stateValues.text).eq( - "Position from anchor 2: lowerright", - ); - - // make not draggable - await updateBooleanInputValue({ - boolean: false, - componentName: "/draggable1", - core, - }); - await updateBooleanInputValue({ - boolean: false, - componentName: "/draggable2", - core, - }); - stateVariables = await returnAllStateVariables(core); - - expect(stateVariables["/pDraggable1"].stateValues.text).eq( - "Draggable 1: false", - ); - expect(stateVariables["/pDraggable2"].stateValues.text).eq( - "Draggable 2: false", - ); - - // cannot move booleanInputs by dragging - await core.requestAction({ - actionName: "moveInput", - componentName: "/booleanInput1", - args: { x: -10, y: -9 }, - event: null, - }); - await core.requestAction({ - actionName: "moveInput", - componentName: "/booleanInput2", - args: { x: -8, y: -7 }, - event: null, - }); - - stateVariables = await returnAllStateVariables(core); - - expect( - cleanLatex( - stateVariables["/booleanInput1anchor"].stateValues.latex, - ), - ).eq("(6,7)"); - expect( - cleanLatex( - stateVariables["/booleanInput2anchor"].stateValues.latex, - ), - ).eq("(8,9)"); - - // make draggable again - await updateBooleanInputValue({ - boolean: true, - componentName: "/draggable1", - core, - }); - await updateBooleanInputValue({ - boolean: true, - componentName: "/draggable2", - core, - }); - stateVariables = await returnAllStateVariables(core); - - expect(stateVariables["/pDraggable1"].stateValues.text).eq( - "Draggable 1: true", - ); - expect(stateVariables["/pDraggable2"].stateValues.text).eq( - "Draggable 2: true", - ); - - await core.requestAction({ - actionName: "moveInput", - componentName: "/booleanInput1", - args: { x: -10, y: -9 }, - event: null, - }); - await core.requestAction({ - actionName: "moveInput", - componentName: "/booleanInput2", - args: { x: -8, y: -7 }, - event: null, - }); - stateVariables = await returnAllStateVariables(core); - - expect( - cleanLatex( - stateVariables["/booleanInput1anchor"].stateValues.latex, - ), - ).eq("(-10,-9)"); - expect( - cleanLatex( - stateVariables["/booleanInput2anchor"].stateValues.latex, - ), - ).eq("(-8,-7)"); - - // fix location - await updateBooleanInputValue({ - boolean: true, - componentName: "/fixLocation1", - core, - }); - await updateBooleanInputValue({ - boolean: true, - componentName: "/fixLocation2", - core, - }); - stateVariables = await returnAllStateVariables(core); - - expect(stateVariables["/pFixLocation1"].stateValues.text).eq( - "FixLocation 1: true", - ); - expect(stateVariables["/pFixLocation2"].stateValues.text).eq( - "FixLocation 2: true", - ); - - // can change coordinates entering coordinates only for input 1 - await updateMathInputValue({ - latex: "(3,4)", - componentName: "/anchorCoords2", - core, - }); - await updateMathInputValue({ - latex: "(1,2)", - componentName: "/anchorCoords1", - core, - }); - - stateVariables = await returnAllStateVariables(core); - - expect( - cleanLatex( - stateVariables["/booleanInput1anchor"].stateValues.latex, - ), - ).eq("(1,2)"); - expect( - cleanLatex( - stateVariables["/booleanInput2anchor"].stateValues.latex, - ), - ).eq("(-8,-7)"); - - // cannot move booleanInputs by dragging - await core.requestAction({ - actionName: "moveInput", - componentName: "/booleanInput1", - args: { x: 4, y: 6 }, - event: null, - }); - await core.requestAction({ - actionName: "moveInput", - componentName: "/booleanInput2", - args: { x: 7, y: 8 }, - event: null, - }); - stateVariables = await returnAllStateVariables(core); - - expect( - cleanLatex( - stateVariables["/booleanInput1anchor"].stateValues.latex, - ), - ).eq("(1,2)"); - expect( - cleanLatex( - stateVariables["/booleanInput2anchor"].stateValues.latex, - ), - ).eq("(-8,-7)"); - - // can change position from anchor only for input 1 - await core.requestAction({ - actionName: "updateSelectedIndices", - componentName: "/positionFromAnchor2", - args: { selectedIndices: [8] }, - event: null, - }); - await core.requestAction({ - actionName: "updateSelectedIndices", - componentName: "/positionFromAnchor1", - args: { selectedIndices: [7] }, - event: null, - }); - - stateVariables = await returnAllStateVariables(core); - - expect(stateVariables["/pPositionFromAnchor1"].stateValues.text).eq( - "Position from anchor 1: top", - ); - expect(stateVariables["/pPositionFromAnchor2"].stateValues.text).eq( - "Position from anchor 2: lowerright", - ); - - // can change disabled attribute - await updateBooleanInputValue({ - boolean: false, - componentName: "/disabled1", - core, - }); - await updateBooleanInputValue({ - boolean: true, - componentName: "/disabled2", - core, - }); - stateVariables = await returnAllStateVariables(core); - - expect(stateVariables["/pDisabled1"].stateValues.text).eq( - "Disabled 1: false", - ); - expect(stateVariables["/pDisabled2"].stateValues.text).eq( - "Disabled 2: true", - ); + `; + // TODO: how to click on the buttons and test if they are disabled? - // make completely fixed - await updateBooleanInputValue({ - boolean: true, - componentName: "/fixed1", - core, - }); - await updateBooleanInputValue({ - boolean: true, - componentName: "/fixed2", - core, - }); - stateVariables = await returnAllStateVariables(core); - - expect(stateVariables["/pFixed1"].stateValues.text).eq("Fixed 1: true"); - expect(stateVariables["/pFixed2"].stateValues.text).eq("Fixed 2: true"); - - // can change coordinates entering coordinates only for input 1 - await updateMathInputValue({ - latex: "(7,8)", - componentName: "/anchorCoords2", - core, - }); - await updateMathInputValue({ - latex: "(5,6)", - componentName: "/anchorCoords1", - core, - }); - - stateVariables = await returnAllStateVariables(core); - - expect( - cleanLatex( - stateVariables["/booleanInput1anchor"].stateValues.latex, - ), - ).eq("(5,6)"); - expect( - cleanLatex( - stateVariables["/booleanInput2anchor"].stateValues.latex, - ), - ).eq("(-8,-7)"); - - // can change position from anchor only for math 1 - await core.requestAction({ - actionName: "updateSelectedIndices", - componentName: "/positionFromAnchor2", - args: { selectedIndices: [5] }, - event: null, - }); - await core.requestAction({ - actionName: "updateSelectedIndices", - componentName: "/positionFromAnchor1", - args: { selectedIndices: [6] }, - event: null, - }); - - stateVariables = await returnAllStateVariables(core); - - expect(stateVariables["/pPositionFromAnchor1"].stateValues.text).eq( - "Position from anchor 1: right", - ); - expect(stateVariables["/pPositionFromAnchor2"].stateValues.text).eq( - "Position from anchor 2: lowerright", - ); - - // can change disabled attribute only for input 1 - - await updateBooleanInputValue({ - boolean: true, - componentName: "/disabled1", - core, - }); - await updateBooleanInputValue({ - boolean: false, - componentName: "/disabled2", - core, - }); - stateVariables = await returnAllStateVariables(core); - - expect(stateVariables["/pDisabled1"].stateValues.text).eq( - "Disabled 1: true", - ); - expect(stateVariables["/pDisabled2"].stateValues.text).eq( - "Disabled 2: true", - ); + await test_in_graph(doenetMLsnippet, moveInput); }); it("valueChanged", async () => { @@ -976,7 +502,7 @@ describe("BooleanInput tag tests", async () => { bi1changed = true; await updateBooleanInputValue({ boolean: bi1, - componentName: "/bi1", + name: "/bi1", core, }); await check_items( @@ -990,7 +516,7 @@ describe("BooleanInput tag tests", async () => { bi2changed = true; await updateBooleanInputValue({ boolean: bi2, - componentName: "/bi2", + name: "/bi2", core, }); await check_items( @@ -1006,12 +532,12 @@ describe("BooleanInput tag tests", async () => { bi4changed = true; await updateBooleanInputValue({ boolean: bi1, - componentName: "/bi3", + name: "/bi3", core, }); await updateBooleanInputValue({ boolean: bi2, - componentName: "/bi4", + name: "/bi4", core, }); await check_items( @@ -1043,7 +569,7 @@ describe("BooleanInput tag tests", async () => { bi3changed = true; await updateBooleanInputValue({ boolean: bi1, - componentName: "/bi3", + name: "/bi3", core, }); await check_items( @@ -1058,7 +584,7 @@ describe("BooleanInput tag tests", async () => { bi4changed = true; await updateBooleanInputValue({ boolean: bi2, - componentName: "/bi4", + name: "/bi4", core, }); await check_items( @@ -1072,12 +598,12 @@ describe("BooleanInput tag tests", async () => { await updateBooleanInputValue({ boolean: bi1, - componentName: "/bi1", + name: "/bi1", core, }); await updateBooleanInputValue({ boolean: bi2, - componentName: "/bi2", + name: "/bi2", core, }); await check_items( diff --git a/packages/doenetml-worker/src/test/tagSpecific/booleanlist.test.ts b/packages/doenetml-worker/src/test/tagSpecific/booleanlist.test.ts index e50cb3852..05ed32aa6 100644 --- a/packages/doenetml-worker/src/test/tagSpecific/booleanlist.test.ts +++ b/packages/doenetml-worker/src/test/tagSpecific/booleanlist.test.ts @@ -4,9 +4,11 @@ import { updateBooleanInputValue, updateMathInputValue, } from "../utils/actions"; +import Core from "../../Core"; const Mock = vi.fn(); vi.stubGlobal("postMessage", Mock); +vi.mock("hyperformula"); describe("BooleanList tag tests", async () => { async function test_booleanList({ @@ -16,11 +18,11 @@ describe("BooleanList tag tests", async () => { text, booleans, }: { - core: any; + core: Core; name?: string; pName?: string; text?: string; - booleans?: any[]; + booleans?: boolean[]; }) { const stateVariables = await returnAllStateVariables(core); @@ -102,7 +104,7 @@ describe("BooleanList tag tests", async () => { }); }); - async function test_nested_and_inverse(core: any) { + async function test_nested_and_inverse(core: Core) { await test_booleanList({ core, name: "/bl1", @@ -150,47 +152,47 @@ describe("BooleanList tag tests", async () => { // change values await updateBooleanInputValue({ - componentName: "/mi1", + name: "/mi1", boolean: false, core, }); await updateBooleanInputValue({ - componentName: "/mi2", + name: "/mi2", boolean: false, core, }); await updateBooleanInputValue({ - componentName: "/mi3", + name: "/mi3", boolean: true, core, }); await updateBooleanInputValue({ - componentName: "/mi4", + name: "/mi4", boolean: false, core, }); await updateBooleanInputValue({ - componentName: "/mi5", + name: "/mi5", boolean: true, core, }); await updateBooleanInputValue({ - componentName: "/mi6", + name: "/mi6", boolean: true, core, }); await updateBooleanInputValue({ - componentName: "/mi7", + name: "/mi7", boolean: false, core, }); await updateBooleanInputValue({ - componentName: "/mi8", + name: "/mi8", boolean: false, core, }); await updateBooleanInputValue({ - componentName: "/mi9", + name: "/mi9", boolean: true, core, }); @@ -465,13 +467,13 @@ describe("BooleanList tag tests", async () => { await check_items(max1, max2); max1 = Infinity; - await updateMathInputValue({ latex: "", componentName: "/mn1", core }); + await updateMathInputValue({ latex: "", name: "/mn1", core }); await check_items(max1, max2); max2 = 3; await updateMathInputValue({ latex: max2.toString(), - componentName: "/mn2", + name: "/mn2", core, }); await check_items(max1, max2); @@ -479,7 +481,7 @@ describe("BooleanList tag tests", async () => { max1 = 4; await updateMathInputValue({ latex: max1.toString(), - componentName: "/mn1", + name: "/mn1", core, }); await check_items(max1, max2); @@ -487,7 +489,7 @@ describe("BooleanList tag tests", async () => { max1 = 1; await updateMathInputValue({ latex: max1.toString(), - componentName: "/mn1", + name: "/mn1", core, }); await check_items(max1, max2); @@ -495,7 +497,7 @@ describe("BooleanList tag tests", async () => { max2 = 10; await updateMathInputValue({ latex: max2.toString(), - componentName: "/mn2", + name: "/mn2", core, }); await check_items(max1, max2); diff --git a/packages/doenetml-worker/src/test/tagSpecific/booleanoperators.test.ts b/packages/doenetml-worker/src/test/tagSpecific/booleanoperators.test.ts index b5b09a673..4ac9a34fb 100644 --- a/packages/doenetml-worker/src/test/tagSpecific/booleanoperators.test.ts +++ b/packages/doenetml-worker/src/test/tagSpecific/booleanoperators.test.ts @@ -4,9 +4,11 @@ import { updateBooleanInputValue, updateMathInputValue, } from "../utils/actions"; +import Core from "../../Core"; const Mock = vi.fn(); vi.stubGlobal("postMessage", Mock); +vi.mock("hyperformula"); describe("Boolean Operator tag tests", async () => { it("not", async () => { @@ -28,7 +30,7 @@ describe("Boolean Operator tag tests", async () => { await updateBooleanInputValue({ boolean: true, - componentName: "/bi", + name: "/bi", core, }); stateVariables = await returnAllStateVariables(core); @@ -50,14 +52,14 @@ describe("Boolean Operator tag tests", async () => { expect(stateVariables["/mv"].stateValues.value.tree).eq("\uff3f"); expect(stateVariables["/op"].stateValues.value).eq(true); - await updateMathInputValue({ latex: "2", componentName: "/mi", core }); + await updateMathInputValue({ latex: "2", name: "/mi", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/mi"].stateValues.value.tree).eq(2); expect(stateVariables["/mv"].stateValues.value.tree).eq(2); expect(stateVariables["/op"].stateValues.value).eq(false); - await updateMathInputValue({ latex: "1", componentName: "/mi", core }); + await updateMathInputValue({ latex: "1", name: "/mi", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/mi"].stateValues.value.tree).eq(1); expect(stateVariables["/mv"].stateValues.value.tree).eq(1); @@ -65,7 +67,7 @@ describe("Boolean Operator tag tests", async () => { }); async function test_three_operators( - core: any, + core: Core, operator: (args: boolean[]) => boolean, ) { async function check_items(booleans: boolean[]) { @@ -94,7 +96,7 @@ describe("Boolean Operator tag tests", async () => { booleans[0] = true; await updateBooleanInputValue({ boolean: booleans[0], - componentName: "/bi1", + name: "/bi1", core, }); await check_items(booleans); @@ -102,7 +104,7 @@ describe("Boolean Operator tag tests", async () => { booleans[1] = true; await updateBooleanInputValue({ boolean: booleans[1], - componentName: "/bi2", + name: "/bi2", core, }); await check_items(booleans); @@ -110,7 +112,7 @@ describe("Boolean Operator tag tests", async () => { booleans[2] = true; await updateBooleanInputValue({ boolean: booleans[2], - componentName: "/bi3", + name: "/bi3", core, }); await check_items(booleans); @@ -254,7 +256,7 @@ describe("Boolean Operator tag tests", async () => { await updateBooleanInputValue({ boolean: true, - componentName: "/bi", + name: "/bi", core, }); stateVariables = await returnAllStateVariables(core); diff --git a/packages/doenetml-worker/src/test/tagSpecific/booleanoperatorsonmath.test.ts b/packages/doenetml-worker/src/test/tagSpecific/booleanoperatorsonmath.test.ts index 3f19e2a4d..0e7b701ff 100644 --- a/packages/doenetml-worker/src/test/tagSpecific/booleanoperatorsonmath.test.ts +++ b/packages/doenetml-worker/src/test/tagSpecific/booleanoperatorsonmath.test.ts @@ -7,6 +7,7 @@ import { const Mock = vi.fn(); vi.stubGlobal("postMessage", Mock); +vi.mock("hyperformula"); describe("Boolean Operator tag tests", async () => { it("isinteger, is number", async () => { @@ -65,13 +66,13 @@ describe("Boolean Operator tag tests", async () => { await check_items(isNumber, isInteger, isEven); - await updateMathInputValue({ latex: "36", componentName: "/n", core }); + await updateMathInputValue({ latex: "36", name: "/n", core }); isNumber = true; isInteger = true; isEven = true; await check_items(isNumber, isInteger, isEven); - await updateMathInputValue({ latex: "37", componentName: "/n", core }); + await updateMathInputValue({ latex: "37", name: "/n", core }); isNumber = true; isInteger = true; isEven = false; @@ -79,7 +80,7 @@ describe("Boolean Operator tag tests", async () => { await updateMathInputValue({ latex: "37.1", - componentName: "/n", + name: "/n", core, }); isNumber = true; @@ -89,7 +90,7 @@ describe("Boolean Operator tag tests", async () => { await updateMathInputValue({ latex: "42/3", - componentName: "/n", + name: "/n", core, }); isNumber = true; @@ -99,7 +100,7 @@ describe("Boolean Operator tag tests", async () => { await updateMathInputValue({ latex: "-39.6/3.3", - componentName: "/n", + name: "/n", core, }); isNumber = true; @@ -107,7 +108,7 @@ describe("Boolean Operator tag tests", async () => { isEven = true; await check_items(isNumber, isInteger, isEven); - await updateMathInputValue({ latex: "x", componentName: "/n", core }); + await updateMathInputValue({ latex: "x", name: "/n", core }); isNumber = false; isInteger = false; isEven = false; @@ -115,7 +116,7 @@ describe("Boolean Operator tag tests", async () => { await updateMathInputValue({ latex: "\\sqrt{4}", - componentName: "/n", + name: "/n", core, }); isNumber = true; @@ -125,7 +126,7 @@ describe("Boolean Operator tag tests", async () => { await updateMathInputValue({ latex: "2\\sin(\\pi/4)^2", - componentName: "/n", + name: "/n", core, }); isNumber = true; @@ -135,7 +136,7 @@ describe("Boolean Operator tag tests", async () => { await updateMathInputValue({ latex: "1E-300", - componentName: "/n", + name: "/n", core, }); isNumber = true; @@ -143,13 +144,13 @@ describe("Boolean Operator tag tests", async () => { isEven = false; await check_items(isNumber, isInteger, isEven); - await updateMathInputValue({ latex: "-0", componentName: "/n", core }); + await updateMathInputValue({ latex: "-0", name: "/n", core }); isNumber = true; isInteger = true; isEven = true; await check_items(isNumber, isInteger, isEven); - await updateMathInputValue({ latex: "0/0", componentName: "/n", core }); + await updateMathInputValue({ latex: "0/0", name: "/n", core }); isNumber = false; isInteger = false; isEven = false; @@ -157,7 +158,7 @@ describe("Boolean Operator tag tests", async () => { await updateMathInputValue({ latex: "10/0", - componentName: "/n", + name: "/n", core, }); isNumber = false; @@ -167,7 +168,7 @@ describe("Boolean Operator tag tests", async () => { await updateMathInputValue({ latex: "10/-0", - componentName: "/n", + name: "/n", core, }); isNumber = false; @@ -216,17 +217,17 @@ describe("Boolean Operator tag tests", async () => { x2 = 3; await updateMathInputValue({ latex: x.toString(), - componentName: "/x", + name: "/x", core, }); await updateMathInputValue({ latex: x1.toString(), - componentName: "/x1", + name: "/x1", core, }); await updateMathInputValue({ latex: x2.toString(), - componentName: "/x2", + name: "/x2", core, }); await check_items(x, x1, x2, strict); @@ -235,7 +236,7 @@ describe("Boolean Operator tag tests", async () => { strict = true; await updateBooleanInputValue({ boolean: strict, - componentName: "/strict", + name: "/strict", core, }); await check_items(x, x1, x2, strict); @@ -244,7 +245,7 @@ describe("Boolean Operator tag tests", async () => { x2 = 5; await updateMathInputValue({ latex: x2.toString(), - componentName: "/x2", + name: "/x2", core, }); await check_items(x, x1, x2, strict); @@ -253,7 +254,7 @@ describe("Boolean Operator tag tests", async () => { strict = false; await updateBooleanInputValue({ boolean: strict, - componentName: "/strict", + name: "/strict", core, }); await check_items(x, x1, x2, strict); @@ -262,7 +263,7 @@ describe("Boolean Operator tag tests", async () => { x = 4; await updateMathInputValue({ latex: x.toString(), - componentName: "/x", + name: "/x", core, }); await check_items(x, x1, x2, strict); @@ -271,7 +272,7 @@ describe("Boolean Operator tag tests", async () => { strict = true; await updateBooleanInputValue({ boolean: strict, - componentName: "/strict", + name: "/strict", core, }); await check_items(x, x1, x2, strict); @@ -280,7 +281,7 @@ describe("Boolean Operator tag tests", async () => { x1 = 8; await updateMathInputValue({ latex: x1.toString(), - componentName: "/x1", + name: "/x1", core, }); await check_items(x, x1, x2, strict); @@ -289,7 +290,7 @@ describe("Boolean Operator tag tests", async () => { x = 7; await updateMathInputValue({ latex: x.toString(), - componentName: "/x", + name: "/x", core, }); await check_items(x, x1, x2, strict); @@ -298,7 +299,7 @@ describe("Boolean Operator tag tests", async () => { x2 = 8; await updateMathInputValue({ latex: x2.toString(), - componentName: "/x2", + name: "/x2", core, }); await check_items(x, x1, x2, strict); @@ -307,7 +308,7 @@ describe("Boolean Operator tag tests", async () => { strict = false; await updateBooleanInputValue({ boolean: strict, - componentName: "/strict", + name: "/strict", core, }); await check_items(x, x1, x2, strict); diff --git a/packages/doenetml-worker/src/test/tagSpecific/callAction.test.ts b/packages/doenetml-worker/src/test/tagSpecific/callAction.test.ts index 4d6910e7d..d1e68c026 100644 --- a/packages/doenetml-worker/src/test/tagSpecific/callAction.test.ts +++ b/packages/doenetml-worker/src/test/tagSpecific/callAction.test.ts @@ -2,12 +2,22 @@ import { describe, expect, it, vi } from "vitest"; import { createTestCore, returnAllStateVariables } from "../utils/test-core"; import { cleanLatex } from "../utils/math"; import { + callAction, + moveButton, + movePoint, + clickPoint, + focusPoint, + triggerActions, updateBooleanInputValue, updateMathInputValue, + updateSelectedIndices, + updateValue, } from "../utils/actions"; +import { test_in_graph } from "../utils/in-graph"; const Mock = vi.fn(); vi.stubGlobal("postMessage", Mock); +vi.mock("hyperformula"); describe("callAction tag tests", async () => { async function test_resample(core) { @@ -27,12 +37,7 @@ describe("callAction tag tests", async () => { expect(stateVariables["/sum"].stateValues.value).eq(sum); - await core.requestAction({ - componentName: "/rs", - actionName: "callAction", - args: {}, - event: null, - }); + await callAction({ name: "/rs", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/sum"].stateValues.value).not.eq(sum); @@ -108,12 +113,7 @@ describe("callAction tag tests", async () => { expect(g.stateValues.graphicalDescendants.length).eq(1); } - await core.requestAction({ - componentName: "/addPoint", - actionName: "callAction", - args: {}, - event: null, - }); + await callAction({ name: "/addPoint", core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/p2"].stateValues.latex)).eq("(3,4)"); @@ -137,11 +137,11 @@ describe("callAction tag tests", async () => { ).eqls([3, 4]); } - await core.requestAction({ - actionName: "movePoint", - componentName: g1.stateValues.graphicalDescendants[1].componentName, - args: { x: -2, y: 5 }, - event: null, + await movePoint({ + name: g1.stateValues.graphicalDescendants[1].componentName, + x: -2, + y: 5, + core, }); stateVariables = await returnAllStateVariables(core); @@ -164,12 +164,7 @@ describe("callAction tag tests", async () => { ).eqls([-2, 5]); } - await core.requestAction({ - componentName: "/addPoint", - actionName: "callAction", - args: {}, - event: null, - }); + await callAction({ name: "/addPoint", core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/p3"].stateValues.latex)).eq("(3,4)"); @@ -196,11 +191,11 @@ describe("callAction tag tests", async () => { ).eqls([3, 4]); } - await core.requestAction({ - actionName: "movePoint", - componentName: g2.stateValues.graphicalDescendants[2].componentName, - args: { x: 7, y: -9 }, - event: null, + await movePoint({ + name: g2.stateValues.graphicalDescendants[2].componentName, + x: 7, + y: -9, + core, }); stateVariables = await returnAllStateVariables(core); @@ -224,12 +219,7 @@ describe("callAction tag tests", async () => { ).eqls([7, -9]); } - await core.requestAction({ - componentName: "/deletePoint", - actionName: "callAction", - args: {}, - event: null, - }); + await callAction({ name: "/deletePoint", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/p3"]).eq(undefined); @@ -253,11 +243,11 @@ describe("callAction tag tests", async () => { ).eqls([-2, 5]); } - await core.requestAction({ - actionName: "movePoint", - componentName: g3.stateValues.graphicalDescendants[1].componentName, - args: { x: 1, y: 0 }, - event: null, + await movePoint({ + name: g3.stateValues.graphicalDescendants[1].componentName, + x: 1, + y: 0, + core, }); stateVariables = await returnAllStateVariables(core); @@ -279,12 +269,7 @@ describe("callAction tag tests", async () => { ).eqls([1, 0]); } - await core.requestAction({ - componentName: "/deletePoint", - actionName: "callAction", - args: {}, - event: null, - }); + await callAction({ name: "/deletePoint", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/p2"]).eq(undefined); @@ -305,12 +290,7 @@ describe("callAction tag tests", async () => { ).eqls([1, 2]); } - await core.requestAction({ - componentName: "/deletePoint", - actionName: "callAction", - args: {}, - event: null, - }); + await callAction({ name: "/deletePoint", core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/p1"].stateValues.latex)).eq("(1,2)"); @@ -331,12 +311,7 @@ describe("callAction tag tests", async () => { ).eqls([1, 2]); } - await core.requestAction({ - componentName: "/addPoint", - actionName: "callAction", - args: {}, - event: null, - }); + await callAction({ name: "/addPoint", core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/p2"].stateValues.latex)).eq("(3,4)"); @@ -379,12 +354,7 @@ describe("callAction tag tests", async () => { expect(num).lte(6); } - await core.requestAction({ - componentName: "/rs", - actionName: "callAction", - args: {}, - event: null, - }); + await callAction({ name: "/rs", core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/p2"].stateValues.latex)).eq("(3,4)"); @@ -400,12 +370,7 @@ describe("callAction tag tests", async () => { stateVariables[pointNames[1]].stateValues.xs.map((x) => x.tree), ).eqls([3, 4]); - await core.requestAction({ - actionName: "movePoint", - componentName: pointNames[1], - args: { x: -2, y: 5 }, - event: null, - }); + await movePoint({ name: pointNames[1], x: -2, y: 5, core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/p2"].stateValues.latex)).eq( @@ -531,12 +496,7 @@ describe("callAction tag tests", async () => { expect(num).lte(6); } - await core.requestAction({ - componentName: `/set${ind}/rs`, - actionName: "callAction", - args: {}, - event: null, - }); + await callAction({ name: `/set${ind}/rs`, core }); stateVariables = await returnAllStateVariables(core); expect( @@ -554,12 +514,7 @@ describe("callAction tag tests", async () => { stateVariables[pointNames[1]].stateValues.xs.map((x) => x.tree), ).eqls([3, 4]); - await core.requestAction({ - actionName: "movePoint", - componentName: pointNames[1], - args: { x: -2, y: 5 }, - event: null, - }); + await movePoint({ name: pointNames[1], x: -2, y: 5, core }); stateVariables = await returnAllStateVariables(core); @@ -634,12 +589,7 @@ describe("callAction tag tests", async () => { expect(stateVariables["/n"].stateValues.value).eq(1); - await core.requestAction({ - componentName: "/rs", - actionName: "callAction", - args: {}, - event: null, - }); + await callAction({ name: "/rs", core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/p2"].stateValues.latex)).eq("(3,4)"); @@ -655,12 +605,7 @@ describe("callAction tag tests", async () => { stateVariables[pointNames[1]].stateValues.xs.map((x) => x.tree), ).eqls([3, 4]); - await core.requestAction({ - actionName: "movePoint", - componentName: pointNames[1], - args: { x: -2, y: 5 }, - event: null, - }); + await movePoint({ name: pointNames[1], x: -2, y: 5, core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/p2"].stateValues.latex)).eq( @@ -688,12 +633,7 @@ describe("callAction tag tests", async () => { expect(stateVariables["/n"].stateValues.value).eq(1); - await core.requestAction({ - componentName: "/in", - actionName: "updateValue", - args: {}, - event: null, - }); + await updateValue({ name: "/in", core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/p3"].stateValues.latex)).eq("(3,4)"); @@ -712,12 +652,7 @@ describe("callAction tag tests", async () => { stateVariables[pointNames[2]].stateValues.xs.map((x) => x.tree), ).eqls([3, 4]); - await core.requestAction({ - actionName: "movePoint", - componentName: pointNames[2], - args: { x: 7, y: -9 }, - event: null, - }); + await movePoint({ name: pointNames[2], x: 7, y: -9, core }); stateVariables = await returnAllStateVariables(core); @@ -768,12 +703,7 @@ describe("callAction tag tests", async () => { "(-1,2)", ); - await core.requestAction({ - actionName: "movePoint", - componentName: "/P", - args: { x: -1, y: -7 }, - event: null, - }); + await movePoint({ name: "/P", x: -1, y: -7, core }); stateVariables = await returnAllStateVariables(core); @@ -787,12 +717,7 @@ describe("callAction tag tests", async () => { expect(numbers2).eqls(numbers); - await core.requestAction({ - actionName: "movePoint", - componentName: "/P", - args: { x: 3, y: -4 }, - event: null, - }); + await movePoint({ name: "/P", x: 3, y: -4, core }); stateVariables = await returnAllStateVariables(core); @@ -806,12 +731,7 @@ describe("callAction tag tests", async () => { expect(numbers2).eqls(numbers); - await core.requestAction({ - actionName: "movePoint", - componentName: "/P", - args: { x: 1, y: 7 }, - event: null, - }); + await movePoint({ name: "/P", x: 1, y: 7, core }); stateVariables = await returnAllStateVariables(core); @@ -830,12 +750,7 @@ describe("callAction tag tests", async () => { expect(numbers2).not.eqls(numbers); numbers = numbers2; - await core.requestAction({ - actionName: "movePoint", - componentName: "/P", - args: { x: 5, y: 9 }, - event: null, - }); + await movePoint({ name: "/P", x: 5, y: 9, core }); stateVariables = await returnAllStateVariables(core); @@ -847,12 +762,7 @@ describe("callAction tag tests", async () => { expect(numbers2).eqls(numbers); - await core.requestAction({ - actionName: "movePoint", - componentName: "/P", - args: { x: -3, y: 4 }, - event: null, - }); + await movePoint({ name: "/P", x: -3, y: 4, core }); stateVariables = await returnAllStateVariables(core); @@ -866,12 +776,7 @@ describe("callAction tag tests", async () => { expect(numbers2).eqls(numbers); - await core.requestAction({ - actionName: "movePoint", - componentName: "/P", - args: { x: -6, y: 5 }, - event: null, - }); + await movePoint({ name: "/P", x: -6, y: 5, core }); stateVariables = await returnAllStateVariables(core); @@ -885,12 +790,7 @@ describe("callAction tag tests", async () => { expect(numbers2).eqls(numbers); - await core.requestAction({ - actionName: "movePoint", - componentName: "/P", - args: { x: 4, y: 2 }, - event: null, - }); + await movePoint({ name: "/P", x: 4, y: 2, core }); stateVariables = await returnAllStateVariables(core); @@ -909,12 +809,7 @@ describe("callAction tag tests", async () => { expect(numbers2).not.eqls(numbers); numbers = numbers2; - await core.requestAction({ - actionName: "movePoint", - componentName: "/P", - args: { x: 9, y: 7 }, - event: null, - }); + await movePoint({ name: "/P", x: 9, y: 7, core }); stateVariables = await returnAllStateVariables(core); @@ -960,12 +855,7 @@ describe("callAction tag tests", async () => { "(-1,2)", ); - await core.requestAction({ - actionName: "movePoint", - componentName: "/P", - args: { x: 3, y: -4 }, - event: null, - }); + await movePoint({ name: "/P", x: 3, y: -4, core }); stateVariables = await returnAllStateVariables(core); @@ -979,12 +869,7 @@ describe("callAction tag tests", async () => { expect(numbers2).eqls(numbers); - await core.requestAction({ - actionName: "pointClicked", - componentName: "/P", - args: { name: "/P" }, - event: null, - }); + await clickPoint({ name: "/P", core }); stateVariables = await returnAllStateVariables(core); @@ -1001,12 +886,7 @@ describe("callAction tag tests", async () => { expect(numbers2).not.eqls(numbers); numbers = numbers2; - await core.requestAction({ - actionName: "movePoint", - componentName: "/P", - args: { x: 5, y: 9 }, - event: null, - }); + await movePoint({ name: "/P", x: 5, y: 9, core }); stateVariables = await returnAllStateVariables(core); @@ -1018,12 +898,7 @@ describe("callAction tag tests", async () => { expect(numbers2).eqls(numbers); - await core.requestAction({ - actionName: "pointClicked", - componentName: "/P", - args: { name: "/P" }, - event: null, - }); + await clickPoint({ name: "/P", core }); stateVariables = await returnAllStateVariables(core); @@ -1040,12 +915,7 @@ describe("callAction tag tests", async () => { expect(numbers2).not.eqls(numbers); numbers = numbers2; - await core.requestAction({ - actionName: "movePoint", - componentName: "/P", - args: { x: 9, y: 7 }, - event: null, - }); + await movePoint({ name: "/P", x: 9, y: 7, core }); stateVariables = await returnAllStateVariables(core); @@ -1107,12 +977,7 @@ describe("callAction tag tests", async () => { "(-1,2)", ); - await core.requestAction({ - actionName: "movePoint", - componentName: PName, - args: { x: 3, y: -4 }, - event: null, - }); + await movePoint({ name: PName, x: 3, y: -4, core }); stateVariables = await returnAllStateVariables(core); @@ -1126,12 +991,7 @@ describe("callAction tag tests", async () => { expect(numbers2).eqls(numbers); - await core.requestAction({ - actionName: "pointClicked", - componentName: PName, - args: { name: PName }, - event: null, - }); + await clickPoint({ name: PName, core }); stateVariables = await returnAllStateVariables(core); @@ -1148,12 +1008,7 @@ describe("callAction tag tests", async () => { expect(numbers2).not.eqls(numbers); numbers = numbers2; - await core.requestAction({ - actionName: "movePoint", - componentName: PName, - args: { x: 5, y: 9 }, - event: null, - }); + await movePoint({ name: PName, x: 5, y: 9, core }); stateVariables = await returnAllStateVariables(core); @@ -1167,12 +1022,7 @@ describe("callAction tag tests", async () => { expect(numbers2).eqls(numbers); - await core.requestAction({ - actionName: "pointClicked", - componentName: PName, - args: { name: PName }, - event: null, - }); + await clickPoint({ name: PName, core }); stateVariables = await returnAllStateVariables(core); @@ -1189,12 +1039,7 @@ describe("callAction tag tests", async () => { expect(numbers2).not.eqls(numbers); numbers = numbers2; - await core.requestAction({ - actionName: "movePoint", - componentName: PName, - args: { x: 9, y: 7 }, - event: null, - }); + await movePoint({ name: PName, x: 9, y: 7, core }); stateVariables = await returnAllStateVariables(core); @@ -1243,12 +1088,7 @@ describe("callAction tag tests", async () => { "(-1,2)", ); - await core.requestAction({ - actionName: "movePoint", - componentName: "/P", - args: { x: 3, y: -4 }, - event: null, - }); + await movePoint({ name: "/P", x: 3, y: -4, core }); stateVariables = await returnAllStateVariables(core); @@ -1262,12 +1102,7 @@ describe("callAction tag tests", async () => { expect(numbers2).eqls(numbers); - await core.requestAction({ - actionName: "pointFocused", - componentName: "/P", - args: { name: "/P" }, - event: null, - }); + await focusPoint({ name: "/P", core }); stateVariables = await returnAllStateVariables(core); @@ -1284,12 +1119,7 @@ describe("callAction tag tests", async () => { expect(numbers2).not.eqls(numbers); numbers = numbers2; - await core.requestAction({ - actionName: "movePoint", - componentName: "/P", - args: { x: 5, y: 9 }, - event: null, - }); + await movePoint({ name: "/P", x: 5, y: 9, core }); stateVariables = await returnAllStateVariables(core); @@ -1301,12 +1131,7 @@ describe("callAction tag tests", async () => { expect(numbers2).eqls(numbers); - await core.requestAction({ - actionName: "pointFocused", - componentName: "/P", - args: { name: "/P" }, - event: null, - }); + await focusPoint({ name: "/P", core }); stateVariables = await returnAllStateVariables(core); @@ -1323,12 +1148,7 @@ describe("callAction tag tests", async () => { expect(numbers2).not.eqls(numbers); numbers = numbers2; - await core.requestAction({ - actionName: "movePoint", - componentName: "/P", - args: { x: 9, y: 7 }, - event: null, - }); + await movePoint({ name: "/P", x: 9, y: 7, core }); stateVariables = await returnAllStateVariables(core); @@ -1383,12 +1203,7 @@ describe("callAction tag tests", async () => { expect(num).lte(6); } - await core.requestAction({ - actionName: "movePoint", - componentName: "/P", - args: { x: -1, y: -7 }, - event: null, - }); + await movePoint({ name: "/P", x: -1, y: -7, core }); stateVariables = await returnAllStateVariables(core); @@ -1405,12 +1220,7 @@ describe("callAction tag tests", async () => { .map(Number); expect(numbers2).eqls(numbers); - await core.requestAction({ - actionName: "movePoint", - componentName: "/P", - args: { x: 3, y: -4 }, - event: null, - }); + await movePoint({ name: "/P", x: 3, y: -4, core }); stateVariables = await returnAllStateVariables(core); @@ -1427,12 +1237,7 @@ describe("callAction tag tests", async () => { .map(Number); expect(numbers2).eqls(numbers); - await core.requestAction({ - actionName: "movePoint", - componentName: "/P", - args: { x: 1, y: 7 }, - event: null, - }); + await movePoint({ name: "/P", x: 1, y: 7, core }); stateVariables = await returnAllStateVariables(core); @@ -1455,12 +1260,7 @@ describe("callAction tag tests", async () => { expect(numbers2).not.eqls(numbers); numbers = numbers2; - await core.requestAction({ - actionName: "movePoint", - componentName: "/P", - args: { x: 5, y: 9 }, - event: null, - }); + await movePoint({ name: "/P", x: 5, y: 9, core }); stateVariables = await returnAllStateVariables(core); @@ -1475,12 +1275,7 @@ describe("callAction tag tests", async () => { .map(Number); expect(numbers2).eqls(numbers); - await core.requestAction({ - actionName: "movePoint", - componentName: "/P", - args: { x: -3, y: 4 }, - event: null, - }); + await movePoint({ name: "/P", x: -3, y: 4, core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/P2"].stateValues.latex)).eq( @@ -1496,12 +1291,7 @@ describe("callAction tag tests", async () => { .map(Number); expect(numbers2).eqls(numbers); - await core.requestAction({ - actionName: "movePoint", - componentName: "/P", - args: { x: -6, y: 5 }, - event: null, - }); + await movePoint({ name: "/P", x: -6, y: 5, core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/P2"].stateValues.latex)).eq( @@ -1517,12 +1307,7 @@ describe("callAction tag tests", async () => { .map(Number); expect(numbers2).eqls(numbers); - await core.requestAction({ - actionName: "movePoint", - componentName: "/P", - args: { x: 4, y: 2 }, - event: null, - }); + await movePoint({ name: "/P", x: 4, y: 2, core }); stateVariables = await returnAllStateVariables(core); @@ -1545,12 +1330,7 @@ describe("callAction tag tests", async () => { expect(numbers2).not.eqls(numbers); numbers = numbers2; - await core.requestAction({ - actionName: "movePoint", - componentName: "/P", - args: { x: 9, y: 7 }, - event: null, - }); + await movePoint({ name: "/P", x: 9, y: 7, core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/P2"].stateValues.latex)).eq("(9,7)"); @@ -1607,12 +1387,7 @@ describe("callAction tag tests", async () => { expect(num).lte(6); } - await core.requestAction({ - actionName: "movePoint", - componentName: "/P", - args: { x: -1, y: -7 }, - event: null, - }); + await movePoint({ name: "/P", x: -1, y: -7, core }); stateVariables = await returnAllStateVariables(core); @@ -1636,12 +1411,7 @@ describe("callAction tag tests", async () => { expect(numbers2).not.eqls(numbers); numbers = numbers2; - await core.requestAction({ - actionName: "movePoint", - componentName: "/P", - args: { x: 3, y: -4 }, - event: null, - }); + await movePoint({ name: "/P", x: 3, y: -4, core }); stateVariables = await returnAllStateVariables(core); @@ -1658,12 +1428,7 @@ describe("callAction tag tests", async () => { .map(Number); expect(numbers2).eqls(numbers); - await core.requestAction({ - actionName: "movePoint", - componentName: "/P", - args: { x: 1, y: 7 }, - event: null, - }); + await movePoint({ name: "/P", x: 1, y: 7, core }); stateVariables = await returnAllStateVariables(core); @@ -1678,12 +1443,7 @@ describe("callAction tag tests", async () => { .map(Number); expect(numbers2).eqls(numbers); - await core.requestAction({ - actionName: "movePoint", - componentName: "/P", - args: { x: 5, y: 9 }, - event: null, - }); + await movePoint({ name: "/P", x: 5, y: 9, core }); stateVariables = await returnAllStateVariables(core); @@ -1698,12 +1458,7 @@ describe("callAction tag tests", async () => { .map(Number); expect(numbers2).eqls(numbers); - await core.requestAction({ - actionName: "movePoint", - componentName: "/P", - args: { x: -3, y: -4 }, - event: null, - }); + await movePoint({ name: "/P", x: -3, y: -4, core }); stateVariables = await returnAllStateVariables(core); @@ -1727,12 +1482,7 @@ describe("callAction tag tests", async () => { expect(numbers2).not.eqls(numbers); numbers = numbers2; - await core.requestAction({ - actionName: "movePoint", - componentName: "/P", - args: { x: -6, y: -5 }, - event: null, - }); + await movePoint({ name: "/P", x: -6, y: -5, core }); stateVariables = await returnAllStateVariables(core); @@ -1749,12 +1499,7 @@ describe("callAction tag tests", async () => { .map(Number); expect(numbers2).eqls(numbers); - await core.requestAction({ - actionName: "movePoint", - componentName: "/P", - args: { x: 4, y: 2 }, - event: null, - }); + await movePoint({ name: "/P", x: 4, y: 2, core }); stateVariables = await returnAllStateVariables(core); @@ -1769,12 +1514,7 @@ describe("callAction tag tests", async () => { .map(Number); expect(numbers2).eqls(numbers); - await core.requestAction({ - actionName: "movePoint", - componentName: "/P", - args: { x: 9, y: 7 }, - event: null, - }); + await movePoint({ name: "/P", x: 9, y: 7, core }); stateVariables = await returnAllStateVariables(core); @@ -1833,12 +1573,7 @@ describe("callAction tag tests", async () => { expect(num).lte(6); } - await core.requestAction({ - actionName: "triggerActions", - componentName: "/tset", - args: {}, - event: null, - }); + await triggerActions({ name: "/tset", core }); stateVariables = await returnAllStateVariables(core); @@ -1855,12 +1590,7 @@ describe("callAction tag tests", async () => { stateVariables[pointNames[1]].stateValues.xs.map((x) => x.tree), ).eqls([3, 4]); - await core.requestAction({ - actionName: "movePoint", - componentName: pointNames[1], - args: { x: -2, y: 5 }, - event: null, - }); + await movePoint({ name: pointNames[1], x: -2, y: 5, core }); stateVariables = await returnAllStateVariables(core); @@ -1927,7 +1657,7 @@ describe("callAction tag tests", async () => { await updateMathInputValue({ latex: "x", - componentName: mathInputName, + name: mathInputName, core, }); stateVariables = await returnAllStateVariables(core); @@ -1948,12 +1678,7 @@ describe("callAction tag tests", async () => { expect(num).lte(6); } - await core.requestAction({ - actionName: "triggerActions", - componentName: "/tset", - args: {}, - event: null, - }); + await triggerActions({ name: "/tset", core }); stateVariables = await returnAllStateVariables(core); @@ -1970,12 +1695,7 @@ describe("callAction tag tests", async () => { stateVariables[pointNames[1]].stateValues.xs.map((x) => x.tree), ).eqls([3, 4]); - await core.requestAction({ - actionName: "movePoint", - componentName: pointNames[1], - args: { x: -2, y: 5 }, - event: null, - }); + await movePoint({ name: pointNames[1], x: -2, y: 5, core }); stateVariables = await returnAllStateVariables(core); @@ -2051,12 +1771,7 @@ describe("callAction tag tests", async () => { expect(stateVariables["/n"].stateValues.value).eq(1); - await core.requestAction({ - componentName: "/rs", - actionName: "callAction", - args: {}, - event: null, - }); + await callAction({ name: "/rs", core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/p2"].stateValues.latex)).eq("(3,4)"); @@ -2072,12 +1787,7 @@ describe("callAction tag tests", async () => { stateVariables[pointNames[1]].stateValues.xs.map((x) => x.tree), ).eqls([3, 4]); - await core.requestAction({ - actionName: "movePoint", - componentName: pointNames[1], - args: { x: -2, y: 5 }, - event: null, - }); + await movePoint({ name: pointNames[1], x: -2, y: 5, core }); stateVariables = await returnAllStateVariables(core); @@ -2152,465 +1862,21 @@ describe("callAction tag tests", async () => { }); it("callAction in graph", async () => { - let core = await createTestCore({ - doenetML: ` + const doenetMLsnippet = ` - + (3,4) - + (-3,-4) - -

Anchor 1 coordinates: $callAction1.anchor

-

Anchor 2 coordinates: $callAction2.anchor

-

Change anchor 1 coordinates:

-

Change anchor 2 coordinates:

-

Position from anchor 1: $callAction1.positionFromAnchor

-

Position from anchor 2: $callAction2.positionFromAnchor

-

Change position from anchor 1 - - upperRight - upperLeft - lowerRight - lowerLeft - left - right - top - bottom - center - -

-

Change position from anchor 2 - - upperRight - upperLeft - lowerRight - lowerLeft - left - right - top - bottom - center - -

-

Draggable 1: $draggable1

-

Draggable 2: $draggable2

-

Change draggable 1

-

Change draggable 2

-

Disabled 1: $disabled1

-

Disabled 2: $disabled2

-

Change disabled 1

-

Change disabled 2

-

Fixed 1: $fixed1

-

Fixed 2: $fixed2

-

Change fixed 1

-

Change fixed 2

-

FixLocation 1: $fixLocation1

-

FixLocation 2: $fixLocation2

-

Change fixLocation 1

-

Change fixLocation 2

- - `, - }); - + `; // TODO: how to click on the buttons and test if they are disabled? - let stateVariables = await returnAllStateVariables(core); - expect(stateVariables["/pAnchor1"].stateValues.text).eq( - "Anchor 1 coordinates: ( 1, 3 )", - ); - expect(stateVariables["/pAnchor2"].stateValues.text).eq( - "Anchor 2 coordinates: ( 0, 0 )", - ); - expect(stateVariables["/pPositionFromAnchor1"].stateValues.text).eq( - "Position from anchor 1: upperright", - ); - expect(stateVariables["/pPositionFromAnchor2"].stateValues.text).eq( - "Position from anchor 2: center", - ); - expect( - stateVariables["/positionFromAnchor1"].stateValues.selectedIndices, - ).eqls([1]); - expect( - stateVariables["/positionFromAnchor2"].stateValues.selectedIndices, - ).eqls([9]); - expect(stateVariables["/pDraggable1"].stateValues.text).eq( - "Draggable 1: true", - ); - expect(stateVariables["/pDraggable2"].stateValues.text).eq( - "Draggable 2: true", - ); - expect(stateVariables["/pDisabled1"].stateValues.text).eq( - "Disabled 1: true", - ); - expect(stateVariables["/pDisabled2"].stateValues.text).eq( - "Disabled 2: false", - ); - expect(stateVariables["/pFixed1"].stateValues.text).eq( - "Fixed 1: false", - ); - expect(stateVariables["/pFixed2"].stateValues.text).eq( - "Fixed 2: false", - ); - expect(stateVariables["/pFixLocation1"].stateValues.text).eq( - "FixLocation 1: false", - ); - expect(stateVariables["/pFixLocation2"].stateValues.text).eq( - "FixLocation 2: false", - ); - - // move callActions by dragging - - await core.requestAction({ - actionName: "moveButton", - componentName: "/callAction1", - args: { x: -2, y: 3 }, - event: null, - }); - await core.requestAction({ - actionName: "moveButton", - componentName: "/callAction2", - args: { x: 4, y: -5 }, - event: null, - }); - - stateVariables = await returnAllStateVariables(core); - expect(stateVariables["/pAnchor1"].stateValues.text).eq( - "Anchor 1 coordinates: ( -2, 3 )", - ); - expect(stateVariables["/pAnchor2"].stateValues.text).eq( - "Anchor 2 coordinates: ( 4, -5 )", - ); - - // move callActions by entering coordinates - - await updateMathInputValue({ - latex: "(6,7)", - componentName: "/anchorCoords1", - core, - }); - await updateMathInputValue({ - latex: "(8,9)", - componentName: "/anchorCoords2", - core, - }); - - stateVariables = await returnAllStateVariables(core); - expect(stateVariables["/pAnchor1"].stateValues.text).eq( - "Anchor 1 coordinates: ( 6, 7 )", - ); - expect(stateVariables["/pAnchor2"].stateValues.text).eq( - "Anchor 2 coordinates: ( 8, 9 )", - ); - - // change position from anchor - await core.requestAction({ - componentName: "/positionFromAnchor1", - actionName: "updateSelectedIndices", - args: { - selectedIndices: [4], - }, - event: null, - }); - await core.requestAction({ - componentName: "/positionFromAnchor2", - actionName: "updateSelectedIndices", - args: { - selectedIndices: [3], - }, - event: null, - }); - stateVariables = await returnAllStateVariables(core); - - expect(stateVariables["/pPositionFromAnchor1"].stateValues.text).eq( - "Position from anchor 1: lowerleft", - ); - expect(stateVariables["/pPositionFromAnchor2"].stateValues.text).eq( - "Position from anchor 2: lowerright", - ); - - // make not draggable - await updateBooleanInputValue({ - boolean: false, - componentName: "/draggable1", - core, - }); - await updateBooleanInputValue({ - boolean: false, - componentName: "/draggable2", - core, - }); - stateVariables = await returnAllStateVariables(core); - - expect(stateVariables["/pDraggable1"].stateValues.text).eq( - "Draggable 1: false", - ); - expect(stateVariables["/pDraggable2"].stateValues.text).eq( - "Draggable 2: false", - ); - - // cannot move callActions by dragging - await core.requestAction({ - actionName: "moveButton", - componentName: "/callAction1", - args: { x: -10, y: -9 }, - event: null, - }); - await core.requestAction({ - actionName: "moveButton", - componentName: "/callAction2", - args: { x: -8, y: -7 }, - event: null, - }); - stateVariables = await returnAllStateVariables(core); - - expect(stateVariables["/pAnchor1"].stateValues.text).eq( - "Anchor 1 coordinates: ( 6, 7 )", - ); - expect(stateVariables["/pAnchor2"].stateValues.text).eq( - "Anchor 2 coordinates: ( 8, 9 )", - ); - - // make draggable again - await updateBooleanInputValue({ - boolean: true, - componentName: "/draggable1", - core, - }); - await updateBooleanInputValue({ - boolean: true, - componentName: "/draggable2", - core, - }); - stateVariables = await returnAllStateVariables(core); - - expect(stateVariables["/pDraggable1"].stateValues.text).eq( - "Draggable 1: true", - ); - expect(stateVariables["/pDraggable2"].stateValues.text).eq( - "Draggable 2: true", - ); - - await core.requestAction({ - actionName: "moveButton", - componentName: "/callAction1", - args: { x: -10, y: -9 }, - event: null, - }); - await core.requestAction({ - actionName: "moveButton", - componentName: "/callAction2", - args: { x: -8, y: -7 }, - event: null, - }); - stateVariables = await returnAllStateVariables(core); - - expect(stateVariables["/pAnchor1"].stateValues.text).eq( - "Anchor 1 coordinates: ( -10, -9 )", - ); - expect(stateVariables["/pAnchor2"].stateValues.text).eq( - "Anchor 2 coordinates: ( -8, -7 )", - ); - - // fix location - await updateBooleanInputValue({ - boolean: true, - componentName: "/fixLocation1", - core, - }); - await updateBooleanInputValue({ - boolean: true, - componentName: "/fixLocation2", - core, - }); - stateVariables = await returnAllStateVariables(core); - - expect(stateVariables["/pFixLocation1"].stateValues.text).eq( - "FixLocation 1: true", - ); - expect(stateVariables["/pFixLocation2"].stateValues.text).eq( - "FixLocation 2: true", - ); - - // can change coordinates entering coordinates only for button 1 - await updateMathInputValue({ - latex: "(1,2)", - componentName: "/anchorCoords1", - core, - }); - await updateMathInputValue({ - latex: "(3,4)", - componentName: "/anchorCoords2", - core, - }); - - stateVariables = await returnAllStateVariables(core); - - expect(stateVariables["/pAnchor1"].stateValues.text).eq( - "Anchor 1 coordinates: ( 1, 2 )", - ); - expect(stateVariables["/pAnchor2"].stateValues.text).eq( - "Anchor 2 coordinates: ( -8, -7 )", - ); - - // cannot move callActions by dragging - await core.requestAction({ - actionName: "moveButton", - componentName: "/callAction1", - args: { x: 4, y: 6 }, - event: null, - }); - await core.requestAction({ - actionName: "moveButton", - componentName: "/callAction2", - args: { x: 7, y: 8 }, - event: null, - }); - - stateVariables = await returnAllStateVariables(core); - - expect(stateVariables["/pAnchor1"].stateValues.text).eq( - "Anchor 1 coordinates: ( 1, 2 )", - ); - expect(stateVariables["/pAnchor2"].stateValues.text).eq( - "Anchor 2 coordinates: ( -8, -7 )", - ); - - // can change position from anchor only for button 1 - await core.requestAction({ - componentName: "/positionFromAnchor1", - actionName: "updateSelectedIndices", - args: { - selectedIndices: [7], - }, - event: null, - }); - await core.requestAction({ - componentName: "/positionFromAnchor2", - actionName: "updateSelectedIndices", - args: { - selectedIndices: [8], - }, - event: null, - }); - stateVariables = await returnAllStateVariables(core); - - expect(stateVariables["/pPositionFromAnchor1"].stateValues.text).eq( - "Position from anchor 1: top", - ); - expect(stateVariables["/pPositionFromAnchor2"].stateValues.text).eq( - "Position from anchor 2: lowerright", - ); - - // can change disabled attribute - - await updateBooleanInputValue({ - boolean: false, - componentName: "/disabled1", - core, - }); - await updateBooleanInputValue({ - boolean: true, - componentName: "/disabled2", - core, - }); - stateVariables = await returnAllStateVariables(core); - - expect(stateVariables["/pDisabled1"].stateValues.text).eq( - "Disabled 1: false", - ); - expect(stateVariables["/pDisabled2"].stateValues.text).eq( - "Disabled 2: true", - ); - - // make completely fixed - - await updateBooleanInputValue({ - boolean: true, - componentName: "/fixed1", - core, - }); - await updateBooleanInputValue({ - boolean: true, - componentName: "/fixed2", - core, - }); - stateVariables = await returnAllStateVariables(core); - - expect(stateVariables["/pFixed1"].stateValues.text).eq("Fixed 1: true"); - expect(stateVariables["/pFixed2"].stateValues.text).eq("Fixed 2: true"); - - // can change coordinates entering coordinates only for button 1 - await updateMathInputValue({ - latex: "(5,6)", - componentName: "/anchorCoords1", - core, - }); - await updateMathInputValue({ - latex: "(7,8)", - componentName: "/anchorCoords2", - core, - }); - - stateVariables = await returnAllStateVariables(core); - - expect(stateVariables["/pAnchor1"].stateValues.text).eq( - "Anchor 1 coordinates: ( 5, 6 )", - ); - expect(stateVariables["/pAnchor2"].stateValues.text).eq( - "Anchor 2 coordinates: ( -8, -7 )", - ); - - // can change position from anchor only for button 1 - await core.requestAction({ - componentName: "/positionFromAnchor1", - actionName: "updateSelectedIndices", - args: { - selectedIndices: [6], - }, - event: null, - }); - await core.requestAction({ - componentName: "/positionFromAnchor2", - actionName: "updateSelectedIndices", - args: { - selectedIndices: [5], - }, - event: null, - }); - stateVariables = await returnAllStateVariables(core); - - expect(stateVariables["/pPositionFromAnchor1"].stateValues.text).eq( - "Position from anchor 1: right", - ); - expect(stateVariables["/pPositionFromAnchor2"].stateValues.text).eq( - "Position from anchor 2: lowerright", - ); - - // can change disabled attribute only for button 1 - - await updateBooleanInputValue({ - boolean: true, - componentName: "/disabled1", - core, - }); - await updateBooleanInputValue({ - boolean: false, - componentName: "/disabled2", - core, - }); - stateVariables = await returnAllStateVariables(core); - - expect(stateVariables["/pDisabled1"].stateValues.text).eq( - "Disabled 1: true", - ); - expect(stateVariables["/pDisabled2"].stateValues.text).eq( - "Disabled 2: true", - ); + await test_in_graph(doenetMLsnippet, moveButton); }); it("buttons can be styled", async () => { diff --git a/packages/doenetml-worker/src/test/tagSpecific/choiceinput.test.ts b/packages/doenetml-worker/src/test/tagSpecific/choiceinput.test.ts new file mode 100644 index 000000000..7e504a85a --- /dev/null +++ b/packages/doenetml-worker/src/test/tagSpecific/choiceinput.test.ts @@ -0,0 +1,3089 @@ +import { describe, expect, it, vi } from "vitest"; +import { createTestCore, returnAllStateVariables } from "../utils/test-core"; +import { + updateMathInputValue, + updateSelectedIndices, + updateTextInputValue, + updateValue, +} from "../utils/actions"; +import Core from "../../Core"; +import { V } from "vitest/dist/chunks/reporters.WnPwkmgA.js"; + +const Mock = vi.fn(); +vi.stubGlobal("postMessage", Mock); +vi.mock("hyperformula"); + +describe("ChoiceInput tag tests", async () => { + async function test_animal_choice_input( + core: Core, + inline: boolean, + shuffleOrder: boolean, + ) { + let originalChoices = ["cat", "dog", "monkey", "mouse"]; + const stateVariables = await returnAllStateVariables(core); + const choiceTexts: string[] = + stateVariables["/ci"].stateValues.choiceTexts; + + if (!shuffleOrder) { + expect(choiceTexts).eqls(originalChoices); + } + expect([...choiceTexts].sort()).eqls(originalChoices); + + expect(stateVariables["/ci"].stateValues.inline).eq(inline); + expect(stateVariables["/ci"].stateValues.shuffleOrder).eq(shuffleOrder); + + async function check_items( + selectedIndex?: number, + selectedValue?: string, + ) { + const stateVariables = await returnAllStateVariables(core); + + expect(stateVariables["/psv"].stateValues.text).eq( + `Selected value: ${selectedValue ?? ""}`, + ); + expect(stateVariables["/psi"].stateValues.text).eq( + `Selected index: ${selectedIndex ?? ""}`, + ); + expect(stateVariables["/pCat"].stateValues.text).eq( + `Selected cat: ${selectedValue === "cat"}`, + ); + expect(stateVariables["/pDog"].stateValues.text).eq( + `Selected dog: ${selectedValue === "dog"}`, + ); + expect(stateVariables["/pMonkey"].stateValues.text).eq( + `Selected monkey: ${selectedValue === "monkey"}`, + ); + expect(stateVariables["/pMouse"].stateValues.text).eq( + `Selected mouse: ${selectedValue === "mouse"}`, + ); + + expect(stateVariables["/ci"].stateValues.selectedValues).eqls( + selectedValue ? [selectedValue] : [], + ); + expect(stateVariables["/ci"].stateValues.selectedIndices).eqls( + selectedIndex ? [selectedIndex] : [], + ); + expect(stateVariables["/choice1"].stateValues.selected).eq( + selectedValue === "cat", + ); + expect(stateVariables["/choice2"].stateValues.selected).eq( + selectedValue === "dog", + ); + expect(stateVariables["/choice3"].stateValues.selected).eq( + selectedValue === "monkey", + ); + expect(stateVariables["/choice4"].stateValues.selected).eq( + selectedValue === "mouse", + ); + } + + await check_items(); + + // select options in order + + for (let i = 0; i < 4; i++) { + let selectedValue = originalChoices[i]; + let selectedIndex = choiceTexts.indexOf(selectedValue) + 1; + await updateSelectedIndices({ + name: "/ci", + selectedIndices: [selectedIndex], + core, + }); + await check_items(selectedIndex, selectedValue); + } + } + + it("default is block format, not shuffled", async () => { + let core = await createTestCore({ + doenetML: ` + + cat + dog + monkey + mouse + + +

Selected value: $ci.selectedValue

+

Selected index: $ci.selectedIndex

+ +

Selected cat: $choice1.selected

+

Selected dog: $choice2.selected

+

Selected monkey: $choice3.selected

+

Selected mouse: $choice4.selected

+ `, + requestedVariantIndex: 8, + }); + + await test_animal_choice_input(core, false, false); + }); + + it("shuffleOrder", async () => { + let core = await createTestCore({ + doenetML: ` + + cat + dog + monkey + mouse + + +

Selected value: $ci.selectedValue

+

Selected index: $ci.selectedIndex

+ +

Selected cat: $choice1.selected

+

Selected dog: $choice2.selected

+

Selected monkey: $choice3.selected

+

Selected mouse: $choice4.selected

+ `, + requestedVariantIndex: 8, + }); + + await test_animal_choice_input(core, false, true); + }); + + it("inline", async () => { + let core = await createTestCore({ + doenetML: ` + + cat + dog + monkey + mouse + + +

Selected value: $ci.selectedValue

+

Selected index: $ci.selectedIndex

+ +

Selected cat: $choice1.selected

+

Selected dog: $choice2.selected

+

Selected monkey: $choice3.selected

+

Selected mouse: $choice4.selected

+ `, + requestedVariantIndex: 8, + }); + + await test_animal_choice_input(core, true, false); + }); + + it("inline, shuffle order", async () => { + let core = await createTestCore({ + doenetML: ` + + cat + dog + monkey + mouse + + +

Selected value: $ci.selectedValue

+

Selected index: $ci.selectedIndex

+ +

Selected cat: $choice1.selected

+

Selected dog: $choice2.selected

+

Selected monkey: $choice3.selected

+

Selected mouse: $choice4.selected

+ `, + requestedVariantIndex: 8, + }); + + await test_animal_choice_input(core, true, true); + }); + + it("choiceInput references", async () => { + let core = await createTestCore({ + doenetML: ` + + a + b + c + d + e + f + + + + + +

Selected values: + $ci1.selectedValue + $ci2.selectedValue + $ci3.selectedValue + $ci4.selectedValue +

+

Selected indices: + $ci1.selectedIndex + $ci2.selectedIndex + $ci3.selectedIndex + $ci4.selectedIndex +

+ + `, + }); + + let originalChoices = ["a", "b", "c", "d", "e", "f"]; + + const stateVariables = await returnAllStateVariables(core); + const choiceTexts: string[][] = [ + stateVariables["/ci1"].stateValues.choiceTexts, + stateVariables["/ci2"].stateValues.choiceTexts, + stateVariables["/ci3"].stateValues.choiceTexts, + stateVariables["/ci4"].stateValues.choiceTexts, + ]; + + expect([...choiceTexts[0]].sort()).eqls(originalChoices); + + for (let i = 1; i < 4; i++) { + expect(choiceTexts[i]).eqls(choiceTexts[0]); + } + + expect(stateVariables["/ci1"].stateValues.inline).eq(true); + expect(stateVariables["/ci2"].stateValues.inline).eq(true); + expect(stateVariables["/ci3"].stateValues.inline).eq(false); + expect(stateVariables["/ci4"].stateValues.inline).eq(false); + expect(stateVariables["/ci1"].stateValues.shuffleOrder).eq(true); + expect(stateVariables["/ci2"].stateValues.shuffleOrder).eq(true); + expect(stateVariables["/ci3"].stateValues.shuffleOrder).eq(true); + expect(stateVariables["/ci4"].stateValues.shuffleOrder).eq(true); + + async function check_items( + selectedIndex?: number, + selectedValue?: string, + ) { + const stateVariables = await returnAllStateVariables(core); + + expect(stateVariables["/psv"].stateValues.text).eq( + `Selected values: ${selectedValue ? Array(4).fill(selectedValue).join(", ") : ""}`, + ); + expect(stateVariables["/psi"].stateValues.text).eq( + `Selected indices: ${selectedIndex ? Array(4).fill(selectedIndex).join(", ") : ""}`, + ); + + for (let i = 1; i <= 4; i++) { + expect( + stateVariables[`/ci${i}`].stateValues.selectedValues, + ).eqls(selectedValue ? [selectedValue] : []); + expect( + stateVariables[`/ci${i}`].stateValues.selectedIndices, + ).eqls(selectedIndex ? [selectedIndex] : []); + } + } + + await check_items(); + + // select options in order from each input in turn + + for (let inputInd = 0; inputInd < 4; inputInd++) { + for (let choiceInd = 0; choiceInd < 6; choiceInd++) { + let selectedValue = originalChoices[choiceInd]; + let selectedIndex = + choiceTexts[inputInd].indexOf(selectedValue) + 1; + await updateSelectedIndices({ + name: `/ci${inputInd + 1}`, + selectedIndices: [selectedIndex], + core, + }); + await check_items(selectedIndex, selectedValue); + } + } + }); + + it("math inside choices", async () => { + let core = await createTestCore({ + doenetML: ` + + The function is f(\\xi)=\\sin(\\xi). + The sum of lambda^2 and 2 lambda^2 is $lambda2+$twice. + The sequence is . + Can't convert this latex: \\bar{x}^i. + + + + +

Selected values: + $ci1.selectedValue + $ci2.selectedValue +

+

Selected indices: + $ci1.selectedIndex + $ci2.selectedIndex +

+ + `, + }); + + let originalChoices = [ + "The function is f(ξ) = sin(ξ).", + "The sum of λ² and 2 λ² is 3 λ².", + "The sequence is 1, 2, 3, 4, 5.", + "Can't convert this latex: \\bar{x}^i.", + ]; + + const stateVariables = await returnAllStateVariables(core); + const choiceTexts: string[][] = [ + stateVariables["/ci1"].stateValues.choiceTexts, + stateVariables["/ci2"].stateValues.choiceTexts, + ]; + + expect([...choiceTexts[0]].sort()).eqls([...originalChoices].sort()); + expect(choiceTexts[1]).eqls(choiceTexts[0]); + + expect(stateVariables["/ci1"].stateValues.inline).eq(false); + expect(stateVariables["/ci2"].stateValues.inline).eq(true); + expect(stateVariables["/ci1"].stateValues.shuffleOrder).eq(true); + expect(stateVariables["/ci2"].stateValues.shuffleOrder).eq(true); + + async function check_items( + selectedIndex?: number, + selectedValue?: string, + ) { + const stateVariables = await returnAllStateVariables(core); + + expect(stateVariables["/psv"].stateValues.text).eq( + `Selected values: ${selectedValue ? Array(2).fill(selectedValue).join(", ") : ""}`, + ); + expect(stateVariables["/psi"].stateValues.text).eq( + `Selected indices: ${selectedIndex ? Array(2).fill(selectedIndex).join(", ") : ""}`, + ); + + for (let i = 1; i <= 2; i++) { + expect( + stateVariables[`/ci${i}`].stateValues.selectedValues, + ).eqls(selectedValue ? [selectedValue] : []); + expect( + stateVariables[`/ci${i}`].stateValues.selectedIndices, + ).eqls(selectedIndex ? [selectedIndex] : []); + } + } + + await check_items(); + + // select options in order from each input in turn + + for (let inputInd = 0; inputInd < 2; inputInd++) { + for (let choiceInd = 0; choiceInd < 4; choiceInd++) { + let selectedValue = originalChoices[choiceInd]; + let selectedIndex = + choiceTexts[inputInd].indexOf(selectedValue) + 1; + await updateSelectedIndices({ + name: `/ci${inputInd + 1}`, + selectedIndices: [selectedIndex], + core, + }); + await check_items(selectedIndex, selectedValue); + } + } + }); + + it("bind value to textInput", async () => { + let core = await createTestCore({ + doenetML: ` + + caT + dog + Monkey + + +

Select by typing:

+ + + +

Selected values: + $ci1.selectedValue + $ci2.selectedValue +

+

Selected indices: + $ci1.selectedIndex + $ci2.selectedIndex +

+ + `, + }); + + let originalChoices = ["caT", " dog ", "Monkey"]; + + const stateVariables = await returnAllStateVariables(core); + const choiceTexts: string[][] = [ + stateVariables["/ci1"].stateValues.choiceTexts, + stateVariables["/ci2"].stateValues.choiceTexts, + ]; + + expect([...choiceTexts[0]].sort()).eqls([...originalChoices].sort()); + expect(choiceTexts[1]).eqls(choiceTexts[0]); + + expect(stateVariables["/ci1"].stateValues.inline).eq(false); + expect(stateVariables["/ci2"].stateValues.inline).eq(true); + expect(stateVariables["/ci1"].stateValues.shuffleOrder).eq(true); + expect(stateVariables["/ci2"].stateValues.shuffleOrder).eq(true); + + async function check_items( + selectedIndex: number | null, + selectedValue: string | null, + inputText: string, + ) { + const stateVariables = await returnAllStateVariables(core); + + expect(stateVariables["/psv"].stateValues.text).eq( + `Selected values: ${selectedValue ? Array(2).fill(selectedValue).join(", ") : ""}`, + ); + expect(stateVariables["/psi"].stateValues.text).eq( + `Selected indices: ${selectedIndex ? Array(2).fill(selectedIndex).join(", ") : ""}`, + ); + + for (let i = 1; i <= 2; i++) { + expect( + stateVariables[`/ci${i}`].stateValues.selectedValues, + ).eqls(selectedValue ? [selectedValue] : []); + expect( + stateVariables[`/ci${i}`].stateValues.selectedIndices, + ).eqls(selectedIndex ? [selectedIndex] : []); + } + + expect(stateVariables["/ti"].stateValues.value).eq(inputText); + } + + let selectedValue: string | null = "Monkey"; + let selectedIndex: number | null = + choiceTexts[0].indexOf(selectedValue) + 1; + let inputText = "monkey"; + + await check_items(selectedIndex, selectedValue, inputText); + + // select cat from first input + selectedValue = "caT"; + selectedIndex = choiceTexts[0].indexOf(selectedValue) + 1; + await updateSelectedIndices({ + name: `/ci1`, + selectedIndices: [selectedIndex], + core, + }); + + inputText = selectedValue; + await check_items(selectedIndex, selectedValue, inputText); + + // Type Dog + selectedValue = " dog "; + selectedIndex = choiceTexts[0].indexOf(selectedValue) + 1; + inputText = "Dog"; + await updateTextInputValue({ text: inputText, name: "/ti", core }); + await check_items(selectedIndex, selectedValue, inputText); + + // select monkey from second input + selectedValue = "Monkey"; + selectedIndex = choiceTexts[0].indexOf(selectedValue) + 1; + inputText = selectedValue; + await updateSelectedIndices({ + name: `/ci2`, + selectedIndices: [selectedIndex], + core, + }); + await check_items(selectedIndex, selectedValue, inputText); + + // type no cat + selectedValue = null; + selectedIndex = null; + inputText = "no cat"; + await updateTextInputValue({ text: inputText, name: "/ti", core }); + await check_items(selectedIndex, selectedValue, inputText); + + // select cat from second input + selectedValue = "caT"; + selectedIndex = choiceTexts[0].indexOf(selectedValue) + 1; + inputText = selectedValue; + await updateSelectedIndices({ + name: `/ci2`, + selectedIndices: [selectedIndex], + core, + }); + await check_items(selectedIndex, selectedValue, inputText); + + // type no dog + selectedValue = null; + selectedIndex = null; + inputText = "no dog"; + await updateTextInputValue({ text: inputText, name: "/ti", core }); + await check_items(selectedIndex, selectedValue, inputText); + + // select dog from first input + selectedValue = " dog "; + selectedIndex = choiceTexts[0].indexOf(selectedValue) + 1; + inputText = selectedValue; + await updateSelectedIndices({ + name: `/ci1`, + selectedIndices: [selectedIndex], + core, + }); + await check_items(selectedIndex, selectedValue, inputText); + + // type no monkey + selectedValue = null; + selectedIndex = null; + inputText = "no monkey"; + await updateTextInputValue({ text: inputText, name: "/ti", core }); + await check_items(selectedIndex, selectedValue, inputText); + + // type monKey + selectedValue = "Monkey"; + inputText = " monKey "; + selectedIndex = choiceTexts[0].indexOf(selectedValue) + 1; + await updateTextInputValue({ text: inputText, name: "/ti", core }); + await check_items(selectedIndex, selectedValue, inputText); + }); + + it("bind value to textInput, select multiple", async () => { + let core = await createTestCore({ + doenetML: ` + + caT + dog + Monkey + + +

Select by typing:

+ + + +

Selected values: + $ci1.selectedValues + $ci2.selectedValues +

+

Selected indices: + $ci1.selectedIndices + $ci2.selectedIndices +

+ + `, + requestedVariantIndex: 1, + }); + + // TODO: determine why the on change handler of ci2 + // is not invoked when selecting monkey, dog for these variants + // bad variants: 3 (dog, cat, monkey), 4 (dog, monkey, cat) + + let originalChoices = ["caT", " dog ", "Monkey"]; + + const stateVariables = await returnAllStateVariables(core); + const choiceTexts: string[][] = [ + stateVariables["/ci1"].stateValues.choiceTexts, + stateVariables["/ci2"].stateValues.choiceTexts, + ]; + + expect([...choiceTexts[0]].sort()).eqls([...originalChoices].sort()); + expect(choiceTexts[1]).eqls(choiceTexts[0]); + + expect(stateVariables["/ci1"].stateValues.inline).eq(false); + expect(stateVariables["/ci2"].stateValues.inline).eq(true); + expect(stateVariables["/ci1"].stateValues.shuffleOrder).eq(true); + expect(stateVariables["/ci2"].stateValues.shuffleOrder).eq(true); + + async function check_items( + selectedIndices: number[], + selectedValues: string[], + inputText: string, + ) { + let selectedValuesString = [ + ...selectedValues, + ...selectedValues, + ].join(", "); + let selectedIndicesString = [ + ...selectedIndices, + ...selectedIndices, + ].join(", "); + + const stateVariables = await returnAllStateVariables(core); + + expect(stateVariables["/psv"].stateValues.text).eq( + `Selected values: ${selectedValuesString}`, + ); + expect(stateVariables["/psi"].stateValues.text).eq( + `Selected indices: ${selectedIndicesString}`, + ); + + for (let i = 1; i <= 2; i++) { + expect( + stateVariables[`/ci${i}`].stateValues.selectedValues, + ).eqls(selectedValues); + expect( + stateVariables[`/ci${i}`].stateValues.selectedIndices, + ).eqls(selectedIndices); + } + + expect(stateVariables["/ti"].stateValues.value).eq(inputText); + } + + let selectedValues = ["Monkey"]; + let selectedIndices = selectedValues.map( + (v) => choiceTexts[0].indexOf(v) + 1, + ); + let inputText = "monkey"; + + await check_items(selectedIndices, selectedValues, inputText); + + // select cat from first input + selectedValues = ["caT", "Monkey"]; + selectedValues.sort( + (a, b) => choiceTexts[0].indexOf(a) - choiceTexts[0].indexOf(b), + ); + selectedIndices = selectedValues.map( + (v) => choiceTexts[0].indexOf(v) + 1, + ); + inputText = selectedValues.join(", "); + await updateSelectedIndices({ name: "/ci1", selectedIndices, core }); + await check_items(selectedIndices, selectedValues, inputText); + + // Type Dog + selectedValues = [" dog "]; + inputText = "Dog"; + await updateTextInputValue({ text: inputText, name: "/ti", core }); + selectedIndices = selectedValues.map( + (v) => choiceTexts[0].indexOf(v) + 1, + ); + await check_items(selectedIndices, selectedValues, inputText); + + // Type cat ,DOG + selectedValues = [" dog ", "caT"]; + selectedValues.sort( + (a, b) => choiceTexts[0].indexOf(a) - choiceTexts[0].indexOf(b), + ); + inputText = "cat ,DOG"; + await updateTextInputValue({ text: inputText, name: "/ti", core }); + selectedIndices = selectedValues.map( + (v) => choiceTexts[0].indexOf(v) + 1, + ); + await check_items(selectedIndices, selectedValues, inputText); + + // TODO: for + // bad variants: 3 (dog, cat, monkey), 4 (dog, monkey, cat) + // this selection is not triggering the on-change handler + // select monkey, dog from second input + selectedValues = [" dog ", "Monkey"].sort( + (a, b) => choiceTexts[0].indexOf(a) - choiceTexts[0].indexOf(b), + ); + selectedIndices = selectedValues.map( + (v) => choiceTexts[0].indexOf(v) + 1, + ); + inputText = selectedValues.join(", "); + await updateSelectedIndices({ name: "/ci2", selectedIndices, core }); + await check_items(selectedIndices, selectedValues, inputText); + + // type no cat + selectedValues = []; + inputText = "no cat"; + await updateTextInputValue({ text: inputText, name: "/ti", core }); + selectedIndices = selectedValues.map( + (v) => choiceTexts[0].indexOf(v) + 1, + ); + await check_items(selectedIndices, selectedValues, inputText); + + // type cat, no dog + selectedValues = ["caT"]; + inputText = "cat, no dog"; + await updateTextInputValue({ text: inputText, name: "/ti", core }); + selectedIndices = selectedValues.map( + (v) => choiceTexts[0].indexOf(v) + 1, + ); + await check_items(selectedIndices, selectedValues, inputText); + + // type dog, no monkey, CAT + selectedValues = [" dog ", "caT"].sort( + (a, b) => choiceTexts[0].indexOf(a) - choiceTexts[0].indexOf(b), + ); + inputText = "dog, no monkey, CAT "; + await updateTextInputValue({ text: inputText, name: "/ti", core }); + selectedIndices = selectedValues.map( + (v) => choiceTexts[0].indexOf(v) + 1, + ); + await check_items(selectedIndices, selectedValues, inputText); + + // select all from second input + selectedValues = ["Monkey", " dog ", "caT"].sort( + (a, b) => choiceTexts[0].indexOf(a) - choiceTexts[0].indexOf(b), + ); + selectedIndices = selectedValues.map( + (v) => choiceTexts[0].indexOf(v) + 1, + ); + inputText = selectedValues.join(", "); + await updateSelectedIndices({ name: "/ci2", selectedIndices, core }); + await check_items(selectedIndices, selectedValues, inputText); + + // type no dog at end + inputText += ", no dog"; + await updateTextInputValue({ text: inputText, name: "/ti", core }); + await check_items(selectedIndices, selectedValues, inputText); + + // type dog, DOG + selectedValues = [" dog "]; + inputText = "dog, DOG"; + await updateTextInputValue({ text: inputText, name: "/ti", core }); + selectedIndices = selectedValues.map( + (v) => choiceTexts[0].indexOf(v) + 1, + ); + await check_items(selectedIndices, selectedValues, inputText); + + // select cat from first input + selectedValues = [" dog ", "caT"].sort( + (a, b) => choiceTexts[0].indexOf(a) - choiceTexts[0].indexOf(b), + ); + selectedIndices = selectedValues.map( + (v) => choiceTexts[0].indexOf(v) + 1, + ); + inputText = selectedValues.join(", "); + await updateSelectedIndices({ name: "/ci2", selectedIndices, core }); + await check_items(selectedIndices, selectedValues, inputText); + + // deselect dog from first input + selectedValues = ["caT"]; + selectedIndices = selectedValues.map( + (v) => choiceTexts[0].indexOf(v) + 1, + ); + inputText = selectedValues.join(", "); + await updateSelectedIndices({ name: "/ci1", selectedIndices, core }); + await check_items(selectedIndices, selectedValues, inputText); + }); + + it("bind value to fixed text, choiceInput reverts to fixed value", async () => { + let core = await createTestCore({ + doenetML: ` + + cat + dog + monkey + + +

Fixed to be: monkey

+ + + +

Selected values: + $ci1.selectedValue + $ci2.selectedValue +

+

Selected indices: + $ci1.selectedIndex + $ci2.selectedIndex +

+ + `, + }); + + async function check_still_monkey() { + let selectedIndex = 3; + let selectedValue = "monkey"; + + let selectedValuesString = [selectedValue, selectedValue].join( + ", ", + ); + let selectedIndicesString = [selectedIndex, selectedIndex].join( + ", ", + ); + + const stateVariables = await returnAllStateVariables(core); + + expect(stateVariables["/psv"].stateValues.text).eq( + `Selected values: ${selectedValuesString}`, + ); + expect(stateVariables["/psi"].stateValues.text).eq( + `Selected indices: ${selectedIndicesString}`, + ); + + for (let i = 1; i <= 2; i++) { + expect( + stateVariables[`/ci${i}`].stateValues.selectedValues, + ).eqls([selectedValue]); + expect( + stateVariables[`/ci${i}`].stateValues.selectedIndices, + ).eqls([selectedIndex]); + } + } + + await check_still_monkey(); + + await updateSelectedIndices({ + name: "/ci1", + selectedIndices: [1], + core, + }); + await check_still_monkey(); + + await updateSelectedIndices({ + name: "/ci1", + selectedIndices: [2], + core, + }); + await check_still_monkey(); + + await updateSelectedIndices({ + name: "/ci2", + selectedIndices: [1], + core, + }); + await check_still_monkey(); + + await updateSelectedIndices({ + name: "/ci2", + selectedIndices: [2], + core, + }); + await check_still_monkey(); + }); + + it("bind value to mathInput", async () => { + let core = await createTestCore({ + doenetML: ` + + x^2/2 + y + \\frac{\\partial f}{\\partial x} + 3 + 1/(e^x) + + +

Select by typing:

+ + + +

Selected values: + $ci1.selectedValue + $ci2.selectedValue +

+

Selected indices: + $ci1.selectedIndex + $ci2.selectedIndex +

+ + `, + }); + + let originalChoices = ["(x²)/2", "y", "∂f/∂x", "3", "1/(e^x)"]; + + const stateVariables = await returnAllStateVariables(core); + const choiceTexts: string[][] = [ + stateVariables["/ci1"].stateValues.choiceTexts, + stateVariables["/ci2"].stateValues.choiceTexts, + ]; + + expect(choiceTexts[0]).eqls(originalChoices); + expect(choiceTexts[1]).eqls(originalChoices); + + expect(stateVariables["/ci1"].stateValues.inline).eq(false); + expect(stateVariables["/ci2"].stateValues.inline).eq(true); + expect(stateVariables["/ci1"].stateValues.shuffleOrder).eq(false); + expect(stateVariables["/ci2"].stateValues.shuffleOrder).eq(false); + + async function check_items( + selectedIndex: number | null, + selectedValue: string | null, + inputMath: any, + ) { + const stateVariables = await returnAllStateVariables(core); + + expect(stateVariables["/psv"].stateValues.text).eq( + `Selected values: ${selectedValue ? Array(2).fill(selectedValue).join(", ") : ""}`, + ); + expect(stateVariables["/psi"].stateValues.text).eq( + `Selected indices: ${selectedIndex ? Array(2).fill(selectedIndex).join(", ") : ""}`, + ); + + for (let i = 1; i <= 2; i++) { + expect( + stateVariables[`/ci${i}`].stateValues.selectedValues, + ).eqls(selectedValue ? [selectedValue] : []); + expect( + stateVariables[`/ci${i}`].stateValues.selectedIndices, + ).eqls(selectedIndex ? [selectedIndex] : []); + } + + expect(stateVariables["/mi"].stateValues.value.tree).eqls( + inputMath, + ); + } + + let selectedValue: string | null = "y"; + let selectedIndex: number | null = + choiceTexts[0].indexOf(selectedValue) + 1; + let inputMath: any = "y"; + + await check_items(selectedIndex, selectedValue, inputMath); + + // select x^2/2 from first input + selectedIndex = 1; + selectedValue = originalChoices[selectedIndex - 1]; + inputMath = ["/", ["^", "x", 2], 2]; + await updateSelectedIndices({ + name: "/ci1", + selectedIndices: [selectedIndex], + core, + }); + await check_items(selectedIndex, selectedValue, inputMath); + + // Type 3 + selectedIndex = 4; + selectedValue = originalChoices[selectedIndex - 1]; + inputMath = 3; + await updateMathInputValue({ latex: "3", name: "/mi", core }); + await check_items(selectedIndex, selectedValue, inputMath); + + // select ∂f/∂x from second input + selectedIndex = 3; + selectedValue = originalChoices[selectedIndex - 1]; + inputMath = ["partial_derivative_leibniz", "f", ["tuple", "x"]]; + await updateSelectedIndices({ + name: "/ci2", + selectedIndices: [selectedIndex], + core, + }); + await check_items(selectedIndex, selectedValue, inputMath); + + // type e^{-x} + selectedIndex = null; + selectedValue = null; + inputMath = ["^", "e", ["-", "x"]]; + await updateMathInputValue({ latex: "e^{-x}", name: "/mi", core }); + await check_items(selectedIndex, selectedValue, inputMath); + + // type 1/e^{x} + selectedIndex = 5; + selectedValue = originalChoices[selectedIndex - 1]; + inputMath = ["/", 1, ["^", "e", "x"]]; + await updateMathInputValue({ latex: "1/e^x", name: "/mi", core }); + await check_items(selectedIndex, selectedValue, inputMath); + + // select y from second input + selectedIndex = 2; + selectedValue = originalChoices[selectedIndex - 1]; + inputMath = "y"; + await updateSelectedIndices({ + name: "/ci2", + selectedIndices: [selectedIndex], + core, + }); + await check_items(selectedIndex, selectedValue, inputMath); + }); + + it("preselect choices", async () => { + let core = await createTestCore({ + doenetML: ` + + cat + dog + monkey + mouse + rabbit + emu + giraffe + aardvark + + + + cat + dog + monkey + mouse + rabbit + emu + giraffe + aardvark + + + + cat + dog + monkey + mouse + rabbit + emu + giraffe + aardvark + + + + + cat + dog + monkey + mouse + rabbit + emu + giraffe + aardvark + + + + cat + dog + monkey + mouse + rabbit + emu + giraffe + aardvark + + + + + cat + dog + monkey + mouse + rabbit + emu + giraffe + aardvark + + + + + cat + dog + monkey + mouse + rabbit + emu + giraffe + aardvark + + + + + cat + dog + monkey + mouse + rabbit + emu + giraffe + aardvark + + + + `, + }); + + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/c1"].stateValues.selectedIndices).eqls([2]); + expect(stateVariables["/c2"].stateValues.selectedIndices).eqls([2]); + expect(stateVariables["/c3"].stateValues.selectedValues).eqls([ + "mouse", + ]); + expect(stateVariables["/c4"].stateValues.selectedValues).eqls([ + "mouse", + ]); + expect(stateVariables["/c5"].stateValues.selectedValues).eqls(["dog"]); + + let dogInd6 = + stateVariables["/c6"].stateValues.choiceTexts.indexOf("dog"); + let mouseInd6 = + stateVariables["/c6"].stateValues.choiceTexts.indexOf("mouse"); + let selectedInd6 = Math.min(dogInd6, mouseInd6) + 1; + expect(stateVariables["/c6"].stateValues.selectedIndices).eqls([ + selectedInd6, + ]); + expect(stateVariables["/c7"].stateValues.selectedValues).eqls([ + "mouse", + ]); + expect(stateVariables["/c8"].stateValues.selectedValues).eqls([ + "mouse", + ]); + }); + + it("disabled choice", async () => { + let core = await createTestCore({ + doenetML: ` + + cat + dog + monkey + mouse + + +

Selected value: $ci.selectedValue

+

Selected index: $ci.selectedIndex

+ + `, + }); + + let originalChoices = ["cat", "dog", "monkey", "mouse"]; + + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/ci"].stateValues.choiceTexts).eqls( + originalChoices, + ); + + async function check_items(selectedIndex?: number) { + let selectedValue = selectedIndex + ? originalChoices[selectedIndex - 1] + : undefined; + const stateVariables = await returnAllStateVariables(core); + + expect(stateVariables["/psv"].stateValues.text).eq( + `Selected value: ${selectedValue ?? ""}`, + ); + expect(stateVariables["/psi"].stateValues.text).eq( + `Selected index: ${selectedIndex ?? ""}`, + ); + + expect(stateVariables[`/ci`].stateValues.selectedValues).eqls( + selectedValue ? [selectedValue] : [], + ); + expect(stateVariables[`/ci`].stateValues.selectedIndices).eqls( + selectedIndex ? [selectedIndex] : [], + ); + + for (let i = 1; i <= 4; i++) { + expect(stateVariables[`/choice${i}`].stateValues.selected).eq( + i === selectedIndex, + ); + } + } + + await check_items(); + + // select options in order + + for (let i = 1; i <= 4; i++) { + await updateSelectedIndices({ + name: "/ci", + selectedIndices: [i], + core, + }); + + if (i === 3) { + await check_items(); + } else { + await check_items(i); + } + } + }); + + it("select multiple", async () => { + let core = await createTestCore({ + doenetML: ` + + cat + dog + monkey + mouse + + + +

Selected values: $ci.selectedValues

+

Selected indices: $ci.selectedIndices

+ `, + }); + + let originalChoices = ["cat", "dog", "monkey", "mouse"]; + + let stateVariables = await returnAllStateVariables(core); + let choiceTexts = stateVariables["/ci"].stateValues.choiceTexts; + expect([...choiceTexts].sort()).eqls([...originalChoices].sort()); + + async function check_items(selectedIndices: number[]) { + let selectedValues = selectedIndices.map((v) => choiceTexts[v - 1]); + const stateVariables = await returnAllStateVariables(core); + + expect(stateVariables["/psv"].stateValues.text).eq( + `Selected values: ${selectedValues.join(", ")}`, + ); + expect(stateVariables["/psi"].stateValues.text).eq( + `Selected indices: ${selectedIndices.join(", ")}`, + ); + + expect(stateVariables[`/ci`].stateValues.selectedValues).eqls( + selectedValues, + ); + expect(stateVariables[`/ci`].stateValues.selectedIndices).eqls( + selectedIndices, + ); + + for (let i = 1; i <= 4; i++) { + expect(stateVariables[`/choice${i}`].stateValues.selected).eq( + selectedIndices.includes(i), + ); + } + } + + await check_items([]); + + // select options in order + let selectedIndices: number[] = []; + + for (let i = 1; i <= 4; i++) { + selectedIndices.push(i); + await updateSelectedIndices({ name: "/ci", selectedIndices, core }); + + await check_items(selectedIndices); + } + + // deselect options in order + + while (selectedIndices.length > 0) { + selectedIndices = selectedIndices.slice(1, selectedIndices.length); + await updateSelectedIndices({ name: "/ci", selectedIndices, core }); + + await check_items(selectedIndices); + } + }); + + it("chain update off choiceInput", async () => { + let core = await createTestCore({ + doenetML: ` + + red + orange + yellow + green + blue + + + + + `, + }); + + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/t"].stateValues.value).eq(""); + + await updateSelectedIndices({ + name: "/ci", + selectedIndices: [2], + core, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/t"].stateValues.value).eq(" orange"); + + await updateSelectedIndices({ + name: "/ci", + selectedIndices: [5], + core, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/t"].stateValues.value).eq(" orange blue"); + + await updateSelectedIndices({ + name: "/ci", + selectedIndices: [1], + core, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/t"].stateValues.value).eq(" orange blue red"); + }); + + // verify fixed bug where shuffle order was recalculated + // causing a copy with no link to have a different shuffle order + it("shuffleOrder is not recalculated when copy with no link", async () => { + let core = await createTestCore({ + doenetML: ` + + + + + a + b + c + d + e + + + + $g{name="g2" link="false"} + `, + }); + + let stateVariables = await returnAllStateVariables(core); + + let choiceOrder = stateVariables["/g/ci"].stateValues.choiceOrder; + let choiceOrder2 = stateVariables["/g2/ci"].stateValues.choiceOrder; + + expect(choiceOrder2).eqls(choiceOrder); + }); + + it("math choices", async () => { + let core = await createTestCore({ + doenetML: ` + + x + x + y+y + z+z + u+u + + + +

Selected value: $ci.selectedValue

+

Selected index: $ci.selectedIndex

+

Selected value: $ci

+

Selected value simplified: $ci{simplify}

+ + + + `, + }); + + let choices = ["x", "y", "z", "u"].map((v) => `${v} + ${v}`); + let choicesSimp = ["x", "y", "z", "u"].map((v) => `2 ${v}`); + + async function check_items(selectedIndex?: number) { + let selectedValue = selectedIndex ? choices[selectedIndex - 1] : ""; + let selectedValueSimp = selectedIndex + ? choicesSimp[selectedIndex - 1] + : ""; + + const stateVariables = await returnAllStateVariables(core); + + expect(stateVariables["/psv"].stateValues.text).eq( + `Selected value: ${selectedValue}`, + ); + expect(stateVariables["/psi"].stateValues.text).eq( + `Selected index: ${selectedIndex ?? ""}`, + ); + expect(stateVariables["/psv2"].stateValues.text).eq( + `Selected value: ${selectedValue}`, + ); + expect(stateVariables["/psvs"].stateValues.text).eq( + `Selected value simplified: ${selectedValueSimp}`, + ); + } + + await check_items(); + + for (let i = 1; i <= 4; i++) { + await updateSelectedIndices({ + name: "/ci", + selectedIndices: [i], + core, + }); + await check_items(i); + } + }); + + it("consistent order for n elements for given variant", async () => { + let core = await createTestCore({ + doenetML: ` +

m:

+

n:

+ + + + + + + + + `, + requestedVariantIndex: 1, + }); + + let orders = {}; + + let m = 1, + n = 6; + + let stateVariables = await returnAllStateVariables(core); + let choiceOrder = stateVariables["/ci"].stateValues.choiceOrder; + + expect([...choiceOrder].sort((a, b) => a - b)).eqls( + [...Array(n - m + 1).keys()].map((x) => x + m), + ); + + orders[`${m},${n}`] = choiceOrder; + + // switch n to 8 + + await updateMathInputValue({ latex: "8", name: "/n", core }); + + m = 1; + n = 8; + + stateVariables = await returnAllStateVariables(core); + choiceOrder = stateVariables["/ci"].stateValues.choiceOrder; + + expect([...choiceOrder].sort((a, b) => a - b)).eqls( + [...Array(n - m + 1).keys()].map((x) => x + m), + ); + + orders[`${m},${n}`] = choiceOrder; + + // get another list of length 6 by setting m to 3 + + await updateMathInputValue({ latex: "3", name: "/m", core }); + + m = 3; + n = 8; + + stateVariables = await returnAllStateVariables(core); + choiceOrder = stateVariables["/ci"].stateValues.choiceOrder; + + expect(choiceOrder).eqls(orders[`1,6`]); + + orders[`${m},${n}`] = choiceOrder; + + // get another list of length 8 by setting n to 10 + + await updateMathInputValue({ latex: "10", name: "/n", core }); + + m = 3; + n = 10; + + stateVariables = await returnAllStateVariables(core); + choiceOrder = stateVariables["/ci"].stateValues.choiceOrder; + + expect(choiceOrder).eqls(orders[`1,8`]); + + orders[`${m},${n}`] = choiceOrder; + + // values change with another variant + + core = await createTestCore({ + doenetML: ` +

m:

+

n:

+ + + + + + + + + `, + requestedVariantIndex: 2, + }); + + m = 1; + n = 6; + + stateVariables = await returnAllStateVariables(core); + choiceOrder = stateVariables["/ci"].stateValues.choiceOrder; + + expect(choiceOrder).not.eqls(orders[`${m},${n}`]); + + expect([...choiceOrder].sort((a, b) => a - b)).eqls( + [...Array(n - m + 1).keys()].map((x) => x + m), + ); + + orders[`${m},${n}`] = choiceOrder; + + // switch n to 8 + + await updateMathInputValue({ latex: "8", name: "/n", core }); + + m = 1; + n = 8; + + stateVariables = await returnAllStateVariables(core); + choiceOrder = stateVariables["/ci"].stateValues.choiceOrder; + expect(choiceOrder).not.eqls(orders[`${m},${n}`]); + + expect([...choiceOrder].sort((a, b) => a - b)).eqls( + [...Array(n - m + 1).keys()].map((x) => x + m), + ); + + orders[`${m},${n}`] = choiceOrder; + + // get another list of length 6 by setting m to 3 + + await updateMathInputValue({ latex: "3", name: "/m", core }); + + m = 3; + n = 8; + + stateVariables = await returnAllStateVariables(core); + choiceOrder = stateVariables["/ci"].stateValues.choiceOrder; + + expect(choiceOrder).eqls(orders[`1,6`]); + + orders[`${m},${n}`] = choiceOrder; + + // get another list of length 8 by setting n to 10 + + await updateMathInputValue({ latex: "10", name: "/n", core }); + + m = 3; + n = 10; + + stateVariables = await returnAllStateVariables(core); + choiceOrder = stateVariables["/ci"].stateValues.choiceOrder; + + expect(choiceOrder).eqls(orders[`1,8`]); + + orders[`${m},${n}`] = choiceOrder; + }); + + it("shuffle all but last choice", async () => { + let core = await createTestCore({ + doenetML: ` + + + cat + dog + monkey + + none of the above + + +

Selected value: $ci.selectedValue

+

Selected index: $ci.selectedIndex

+ +

Selected cat: $choice1.selected

+

Selected dog: $choice2.selected

+

Selected monkey: $choice3.selected

+

Selected none of the above: $choice4.selected

+ `, + requestedVariantIndex: 4, + }); + + let originalChoices = ["cat", "dog", "monkey", "none of the above"]; + + const stateVariables = await returnAllStateVariables(core); + const choiceTexts: string[] = + stateVariables["/ci"].stateValues.choiceTexts; + + expect([...choiceTexts].sort()).eqls(originalChoices); + + async function check_items( + selectedIndex?: number, + selectedValue?: string, + ) { + const stateVariables = await returnAllStateVariables(core); + + expect(stateVariables["/psv"].stateValues.text).eq( + `Selected value: ${selectedValue ?? ""}`, + ); + expect(stateVariables["/psi"].stateValues.text).eq( + `Selected index: ${selectedIndex ?? ""}`, + ); + expect(stateVariables["/pCat"].stateValues.text).eq( + `Selected cat: ${selectedValue === "cat"}`, + ); + expect(stateVariables["/pDog"].stateValues.text).eq( + `Selected dog: ${selectedValue === "dog"}`, + ); + expect(stateVariables["/pMonkey"].stateValues.text).eq( + `Selected monkey: ${selectedValue === "monkey"}`, + ); + expect(stateVariables["/pNone"].stateValues.text).eq( + `Selected none of the above: ${selectedValue === "none of the above"}`, + ); + + expect(stateVariables["/ci"].stateValues.selectedValues).eqls( + selectedValue ? [selectedValue] : [], + ); + expect(stateVariables["/ci"].stateValues.selectedIndices).eqls( + selectedIndex ? [selectedIndex] : [], + ); + expect(stateVariables["/choice1"].stateValues.selected).eq( + selectedValue === "cat", + ); + expect(stateVariables["/choice2"].stateValues.selected).eq( + selectedValue === "dog", + ); + expect(stateVariables["/choice3"].stateValues.selected).eq( + selectedValue === "monkey", + ); + expect(stateVariables["/choice4"].stateValues.selected).eq( + selectedValue === "none of the above", + ); + } + + await check_items(); + + // select options in order + + for (let i = 0; i < 4; i++) { + let selectedValue = originalChoices[i]; + let selectedIndex = choiceTexts.indexOf(selectedValue) + 1; + await updateSelectedIndices({ + name: "/ci", + selectedIndices: [selectedIndex], + core, + }); + await check_items(selectedIndex, selectedValue); + } + }); + + it("sorted choices", async () => { + let core = await createTestCore({ + doenetML: ` + + + mouse + dog + cat + monkey + + + +

Selected value: $ci.selectedValue

+

Selected index: $ci.selectedIndex

+ +

Selected mouse: $choice1.selected

+

Selected dog: $choice2.selected

+

Selected cat: $choice3.selected

+

Selected monkey: $choice4.selected

+ `, + }); + + let sortedChoices = ["cat", "dog", "monkey", "mouse"]; + const stateVariables = await returnAllStateVariables(core); + const choiceTexts: string[] = + stateVariables["/ci"].stateValues.choiceTexts; + + expect(choiceTexts).eqls(sortedChoices); + + async function check_items( + selectedIndex?: number, + selectedValue?: string, + ) { + const stateVariables = await returnAllStateVariables(core); + + expect(stateVariables["/psv"].stateValues.text).eq( + `Selected value: ${selectedValue ?? ""}`, + ); + expect(stateVariables["/psi"].stateValues.text).eq( + `Selected index: ${selectedIndex ?? ""}`, + ); + expect(stateVariables["/pCat"].stateValues.text).eq( + `Selected cat: ${selectedValue === "cat"}`, + ); + expect(stateVariables["/pDog"].stateValues.text).eq( + `Selected dog: ${selectedValue === "dog"}`, + ); + expect(stateVariables["/pMonkey"].stateValues.text).eq( + `Selected monkey: ${selectedValue === "monkey"}`, + ); + expect(stateVariables["/pMouse"].stateValues.text).eq( + `Selected mouse: ${selectedValue === "mouse"}`, + ); + + expect(stateVariables["/ci"].stateValues.selectedValues).eqls( + selectedValue ? [selectedValue] : [], + ); + expect(stateVariables["/ci"].stateValues.selectedIndices).eqls( + selectedIndex ? [selectedIndex] : [], + ); + expect(stateVariables["/choice1"].stateValues.selected).eq( + selectedValue === "mouse", + ); + expect(stateVariables["/choice2"].stateValues.selected).eq( + selectedValue === "dog", + ); + expect(stateVariables["/choice3"].stateValues.selected).eq( + selectedValue === "cat", + ); + expect(stateVariables["/choice4"].stateValues.selected).eq( + selectedValue === "monkey", + ); + } + + await check_items(); + + // select options in order + + for (let i = 0; i < 4; i++) { + let selectedValue = sortedChoices[i]; + let selectedIndex = choiceTexts.indexOf(selectedValue) + 1; + await updateSelectedIndices({ + name: "/ci", + selectedIndices: [selectedIndex], + core, + }); + await check_items(selectedIndex, selectedValue); + } + }); + + it("copy choices", async () => { + let core = await createTestCore({ + doenetML: ` + + cat + + + + dog + monkey + + + + + + monkey + + +

Selected value 1: $ci1.selectedValue

+

Selected index 1: $ci1.selectedIndex

+ +

Selected value 2: $ci2.selectedValue

+

Selected index 2: $ci2.selectedIndex

+ +

Selected cat0: $cat0.selected

+ +

Selected cat1: $cat1.selected

+

Selected dog1: $dog1.selected

+

Selected monkey1: $monkey1.selected

+ +

Selected cat2: $cat2.selected

+

Selected dog2: $dog2.selected

+

Selected monkey2: $monkey2.selected

+ + `, + }); + + let choices = ["cat", "dog", "monkey"]; + + async function check_items( + selectedIndex1?: number, + selectedIndex2?: number, + ) { + let selectedValue1 = selectedIndex1 + ? choices[selectedIndex1 - 1] + : ""; + let selectedValue2 = selectedIndex2 + ? choices[selectedIndex2 - 1] + : ""; + + const stateVariables = await returnAllStateVariables(core); + + expect(stateVariables["/psv1"].stateValues.text).eq( + `Selected value 1: ${selectedValue1}`, + ); + expect(stateVariables["/psi1"].stateValues.text).eq( + `Selected index 1: ${selectedIndex1 ?? ""}`, + ); + expect(stateVariables["/psv2"].stateValues.text).eq( + `Selected value 2: ${selectedValue2}`, + ); + expect(stateVariables["/psi2"].stateValues.text).eq( + `Selected index 2: ${selectedIndex2 ?? ""}`, + ); + + expect(stateVariables["/pCat0"].stateValues.text).eq( + `Selected cat0: false`, + ); + expect(stateVariables["/pCat1"].stateValues.text).eq( + `Selected cat1: ${selectedValue1 === "cat"}`, + ); + expect(stateVariables["/pCat2"].stateValues.text).eq( + `Selected cat2: ${selectedValue2 === "cat"}`, + ); + expect(stateVariables["/pDog1"].stateValues.text).eq( + `Selected dog1: ${selectedValue1 === "dog"}`, + ); + expect(stateVariables["/pDog2"].stateValues.text).eq( + `Selected dog2: ${selectedValue2 === "dog"}`, + ); + expect(stateVariables["/pMonkey1"].stateValues.text).eq( + `Selected monkey1: ${selectedValue1 === "monkey"}`, + ); + expect(stateVariables["/pMonkey2"].stateValues.text).eq( + `Selected monkey2: ${selectedValue2 === "monkey"}`, + ); + + expect(stateVariables["/ci1"].stateValues.selectedValues).eqls( + selectedValue1 ? [selectedValue1] : [], + ); + expect(stateVariables["/ci1"].stateValues.selectedIndices).eqls( + selectedIndex1 ? [selectedIndex1] : [], + ); + expect(stateVariables["/ci2"].stateValues.selectedValues).eqls( + selectedValue2 ? [selectedValue2] : [], + ); + expect(stateVariables["/ci2"].stateValues.selectedIndices).eqls( + selectedIndex2 ? [selectedIndex2] : [], + ); + } + + await check_items(); + + // select options 1 in order + + for (let i = 1; i <= 3; i++) { + await updateSelectedIndices({ + name: "/ci1", + selectedIndices: [i], + core, + }); + await check_items(i); + } + + // select options 2 in order + + for (let i = 1; i <= 3; i++) { + await updateSelectedIndices({ + name: "/ci2", + selectedIndices: [i], + core, + }); + await check_items(3, i); + } + }); + + it("valueChanged", async () => { + let doenetML = ` +

YesNo +

+

YesNo +

+

YesNo +

+

YesNo

+ + `; + + let core = await createTestCore({ + doenetML, + }); + + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/ci1changed"].stateValues.value).eq(false); + expect(stateVariables["/ci2changed"].stateValues.value).eq(false); + expect(stateVariables["/ci3changed"].stateValues.value).eq(false); + expect(stateVariables["/ci4changed"].stateValues.value).eq(false); + + expect(stateVariables["/ci1a"].stateValues.value).eq(""); + expect(stateVariables["/ci2a"].stateValues.value).eq("No"); + expect(stateVariables["/ci3a"].stateValues.value).eq(""); + expect(stateVariables["/ci4a"].stateValues.value).eq("No"); + + // selecting from first and second marks only them as changed + + await updateSelectedIndices({ + name: "/ci1", + selectedIndices: [1], + core, + }); + await updateSelectedIndices({ + name: "/ci2", + selectedIndices: [1], + core, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/ci1changed"].stateValues.value).eq(true); + expect(stateVariables["/ci2changed"].stateValues.value).eq(true); + expect(stateVariables["/ci3changed"].stateValues.value).eq(false); + expect(stateVariables["/ci4changed"].stateValues.value).eq(false); + + expect(stateVariables["/ci1a"].stateValues.value).eq("Yes"); + expect(stateVariables["/ci2a"].stateValues.value).eq("Yes"); + expect(stateVariables["/ci3a"].stateValues.value).eq("Yes"); + expect(stateVariables["/ci4a"].stateValues.value).eq("Yes"); + + // selecting from third and fourth marks them as changed + + await updateSelectedIndices({ + name: "/ci3", + selectedIndices: [2], + core, + }); + await updateSelectedIndices({ + name: "/ci4", + selectedIndices: [2], + core, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/ci1changed"].stateValues.value).eq(true); + expect(stateVariables["/ci2changed"].stateValues.value).eq(true); + expect(stateVariables["/ci3changed"].stateValues.value).eq(true); + expect(stateVariables["/ci4changed"].stateValues.value).eq(true); + + expect(stateVariables["/ci1a"].stateValues.value).eq("No"); + expect(stateVariables["/ci2a"].stateValues.value).eq("No"); + expect(stateVariables["/ci3a"].stateValues.value).eq("No"); + expect(stateVariables["/ci4a"].stateValues.value).eq("No"); + + // reload + + core = await createTestCore({ + doenetML, + }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/ci1changed"].stateValues.value).eq(false); + expect(stateVariables["/ci2changed"].stateValues.value).eq(false); + expect(stateVariables["/ci3changed"].stateValues.value).eq(false); + expect(stateVariables["/ci4changed"].stateValues.value).eq(false); + + expect(stateVariables["/ci1a"].stateValues.value).eq(""); + expect(stateVariables["/ci2a"].stateValues.value).eq("No"); + expect(stateVariables["/ci3a"].stateValues.value).eq(""); + expect(stateVariables["/ci4a"].stateValues.value).eq("No"); + + // selecting from fourth marks second and fourth as changed + + await updateSelectedIndices({ + name: "/ci4", + selectedIndices: [1], + core, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/ci1changed"].stateValues.value).eq(false); + expect(stateVariables["/ci2changed"].stateValues.value).eq(true); + expect(stateVariables["/ci3changed"].stateValues.value).eq(false); + expect(stateVariables["/ci4changed"].stateValues.value).eq(true); + + expect(stateVariables["/ci1a"].stateValues.value).eq(""); + expect(stateVariables["/ci2a"].stateValues.value).eq("Yes"); + expect(stateVariables["/ci3a"].stateValues.value).eq(""); + expect(stateVariables["/ci4a"].stateValues.value).eq("Yes"); + + // selecting from third marks first and third as changed + + await updateSelectedIndices({ + name: "/ci3", + selectedIndices: [2], + core, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/ci1changed"].stateValues.value).eq(true); + expect(stateVariables["/ci2changed"].stateValues.value).eq(true); + expect(stateVariables["/ci3changed"].stateValues.value).eq(true); + expect(stateVariables["/ci4changed"].stateValues.value).eq(true); + + expect(stateVariables["/ci1a"].stateValues.value).eq("No"); + expect(stateVariables["/ci2a"].stateValues.value).eq("Yes"); + expect(stateVariables["/ci3a"].stateValues.value).eq("No"); + expect(stateVariables["/ci4a"].stateValues.value).eq("Yes"); + }); + + it("label", async () => { + let core = await createTestCore({ + doenetML: ` +

YesNo +

+

YesNo +

+ +

+

+ `, + }); + + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/ci1"].stateValues.label).eq("Select an option"); + expect(stateVariables["/ci1a"].stateValues.label).eq( + "Select an option", + ); + expect(stateVariables["/ci2"].stateValues.label).eq( + "Select another option", + ); + expect(stateVariables["/ci2a"].stateValues.label).eq( + "Select another option", + ); + + // hide labels + await updateValue({ name: "/toggleLabels", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/ci1"].stateValues.label).eq(""); + expect(stateVariables["/ci1a"].stateValues.label).eq(""); + expect(stateVariables["/ci2"].stateValues.label).eq(""); + expect(stateVariables["/ci2a"].stateValues.label).eq(""); + + // show labels again + await updateValue({ name: "/toggleLabels", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/ci1"].stateValues.label).eq("Select an option"); + expect(stateVariables["/ci1a"].stateValues.label).eq( + "Select an option", + ); + expect(stateVariables["/ci2"].stateValues.label).eq( + "Select another option", + ); + expect(stateVariables["/ci2a"].stateValues.label).eq( + "Select another option", + ); + }); + + it("change choice input from copied value, text", async () => { + let doenetML = ` + + yes + no + maybe + + +

Change from text of ci:

+

Change from text of ci.selectedValue:

+

Change from macro of $ci: $ci

+

Change from macro of $ci.selectedValue: $ci.selectedValue

+

Change from math of ci.selectedIndices:

+

Change from math of ci.selectedIndex:

+

Change from macro of $ci.selectedIndices: $ci.selectedIndices

+

Change from macro of $ci.selectedIndex: $ci.selectedIndex

+ +

Selected value:

+ `; + + let core = await createTestCore({ + doenetML, + }); + + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq(`Selected value: `); + + // typing in wrong value doesn't do anything + await updateTextInputValue({ + text: "nothing", + name: "/fromTextCi", + core, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq(`Selected value: `); + + // Select value from text ci + await updateTextInputValue({ + text: "maybe", + name: "/fromTextCi", + core, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq( + `Selected value: maybe`, + ); + + // Change value from text ci.selectedValue + await updateTextInputValue({ + text: "no", + name: "/fromTextSelectedValue", + core, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq(`Selected value: no`); + + // Invalid value into text ci.selectedValue does nothing + await updateTextInputValue({ + text: "bad", + name: "/fromTextSelectedValue", + core, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq(`Selected value: no`); + + // reload + core = await createTestCore({ + doenetML, + }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq(`Selected value: `); + + // Can change value from one macro after starting afresh + await updateTextInputValue({ + text: "yes", + name: "/fromMacroCi", + core, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq(`Selected value: yes`); + + // reload + core = await createTestCore({ + doenetML, + }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq(`Selected value: `); + + // Can change value from other macro after starting afresh + await updateTextInputValue({ + text: "no", + name: "/fromMacroSelectedValue", + core, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq(`Selected value: no`); + + // reload + core = await createTestCore({ + doenetML, + }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq(`Selected value: `); + + // Change value from text ci.selectedValue works after starting afresh + await updateTextInputValue({ + text: "maybe", + name: "/fromTextSelectedValue", + core, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq( + `Selected value: maybe`, + ); + + // We can now change from macros + await updateTextInputValue({ + text: "yes", + name: "/fromMacroCi", + core, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq(`Selected value: yes`); + + await updateTextInputValue({ + text: "no", + name: "/fromMacroSelectedValue", + core, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq(`Selected value: no`); + + // reload + core = await createTestCore({ + doenetML, + }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq(`Selected value: `); + + // Change value from math ci.selectedIndices works after starting afresh + await updateMathInputValue({ + latex: "3", + name: "/fromMathSelectedIndices", + core, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq( + `Selected value: maybe`, + ); + + // Invalid value into from math ci.selectedIndex deselects all option + await updateMathInputValue({ + latex: "4", + name: "/fromMathSelectedIndex", + core, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq(`Selected value: `); + + // Enter valid value into from math ci.selectedIndex + await updateMathInputValue({ + latex: "3", + name: "/fromMathSelectedIndex", + core, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq( + `Selected value: maybe`, + ); + + // Enter value into from macro ci.selectedIndices + await updateMathInputValue({ + latex: "2", + name: "/fromMacroSelectedIndices", + core, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq(`Selected value: no`); + + // Enter value into from macro ci.selectedIndex + await updateMathInputValue({ + latex: "1", + name: "/fromMacroSelectedIndex", + core, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq(`Selected value: yes`); + + // reload + core = await createTestCore({ + doenetML, + }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq(`Selected value: `); + + // Can change value from macros ci.selectedIndices after starting afresh + await updateMathInputValue({ + latex: "2", + name: "/fromMacroSelectedIndices", + core, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq(`Selected value: no`); + + // reload + core = await createTestCore({ + doenetML, + }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq(`Selected value: `); + + // Can change value from macro ci.selectedIndex after starting afresh + await updateMathInputValue({ + latex: "1", + name: "/fromMacroSelectedIndex", + core, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq(`Selected value: yes`); + + // reload + core = await createTestCore({ + doenetML, + }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq(`Selected value: `); + + // can add value into from math ci.selectedIndex even after starting afresh + await updateMathInputValue({ + latex: "2", + name: "/fromMathSelectedIndex", + core, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq(`Selected value: no`); + }); + + it("change choice input from copied value, text, select multiple", async () => { + let doenetML = ` + + yes + no + maybe + + +

Change from text of ci.selectedValue:

+

Change from text of ci.selectedValue2:

+

Change from macro of $ci.selectedValue: $ci.selectedValue

+

Change from macro of $ci.selectedValue2: $ci.selectedValue2

+

Change from math of ci.selectedIndex:

+

Change from math of ci.selectedIndex2:

+

Change from macro of $ci.selectedIndex: $ci.selectedIndex

+

Change from macro of $ci.selectedIndex2: $ci.selectedIndex2

+ +

Selected values: $ci.selectedValues

+ `; + + let core = await createTestCore({ + doenetML, + }); + + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq(`Selected values: `); + + // typing in wrong value doesn't do anything + await updateTextInputValue({ + text: "nothing", + name: "/fromTextSelectedValue", + core, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq(`Selected values: `); + + // Select value from text ci.selectedValue + await updateTextInputValue({ + text: "maybe", + name: "/fromTextSelectedValue", + core, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq( + `Selected values: maybe`, + ); + + // Add second value from text ci.selectedValue2 + await updateTextInputValue({ + text: "no", + name: "/fromTextSelectedValue2", + core, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq( + `Selected values: no, maybe`, + ); + + // Invalid value into text ci.selectedValue does nothing + await updateTextInputValue({ + text: "bad", + name: "/fromTextSelectedValue", + core, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq( + `Selected values: no, maybe`, + ); + + // Repeat first value from text ci.selectedValue2 reduces to one value + await updateTextInputValue({ + text: "no", + name: "/fromTextSelectedValue2", + core, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq(`Selected values: no`); + + // Add second value from text ci.selectedValue2 + await updateTextInputValue({ + text: "yes", + name: "/fromTextSelectedValue2", + core, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq( + `Selected values: yes, no`, + ); + + // Repeat second value from text ci.selectedValue reduces to one value + await updateTextInputValue({ + text: "no", + name: "/fromTextSelectedValue", + core, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq(`Selected values: no`); + + // reload + core = await createTestCore({ + doenetML, + }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq(`Selected values: `); + + // Can change value from macros after starting afresh + await updateTextInputValue({ + text: "yes", + name: "/fromMacroSelectedValue", + core, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq( + `Selected values: yes`, + ); + + // reload + core = await createTestCore({ + doenetML, + }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq(`Selected values: `); + + // Can change value from macros after starting afresh + await updateTextInputValue({ + text: "no", + name: "/fromMacroSelectedValue2", + core, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq(`Selected values: no`); + + // reload + core = await createTestCore({ + doenetML, + }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq(`Selected values: `); + + // Change value from text ci.selectedValue2 works after starting afresh + await updateTextInputValue({ + text: "maybe", + name: "/fromTextSelectedValue2", + core, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq( + `Selected values: maybe`, + ); + + // Add second value from text ci.selectedValue2 + await updateTextInputValue({ + text: "yes", + name: "/fromTextSelectedValue2", + core, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq( + `Selected values: yes, maybe`, + ); + + // We can change from macros + await updateTextInputValue({ + text: "no", + name: "/fromMacroSelectedValue", + core, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq( + `Selected values: no, maybe`, + ); + + await updateTextInputValue({ + text: "yes", + name: "/fromMacroSelectedValue2", + core, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq( + `Selected values: yes, no`, + ); + + // reload + core = await createTestCore({ + doenetML, + }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq(`Selected values: `); + + // Change value from math ci.selectedIndex works after starting afresh + await updateMathInputValue({ + latex: "3", + name: "/fromMathSelectedIndex", + core, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq( + `Selected values: maybe`, + ); + + // Invalid value into from math ci.selectedIndex is reverted + await updateMathInputValue({ + latex: "4", + name: "/fromMathSelectedIndex", + core, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq( + `Selected values: maybe`, + ); + + // Enter valid value into from math ci.selectedIndex2 + await updateMathInputValue({ + latex: "1", + name: "/fromMathSelectedIndex2", + core, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq( + `Selected values: yes, maybe`, + ); + + // Enter value into from macro ci.selectedIndex + await updateMathInputValue({ + latex: "2", + name: "/fromMacroSelectedIndex", + core, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq( + `Selected values: no, maybe`, + ); + + // Enter value into from macro ci.selectedIndex2 + await updateMathInputValue({ + latex: "1", + name: "/fromMacroSelectedIndex2", + core, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq( + `Selected values: yes, no`, + ); + + // Enter repeated value into from macro ci.selectedIndex2 selects just one + await updateMathInputValue({ + latex: "1", + name: "/fromMacroSelectedIndex2", + core, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq( + `Selected values: yes`, + ); + + // reload + core = await createTestCore({ + doenetML, + }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq(`Selected values: `); + + // Can change value from macro ci.selectedIndex after starting afresh + await updateMathInputValue({ + latex: "2", + name: "/fromMacroSelectedIndex", + core, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq(`Selected values: no`); + + // reload + core = await createTestCore({ + doenetML, + }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq(`Selected values: `); + + // Can change value from macro ci.selectedIndex2 after starting afresh + await updateMathInputValue({ + latex: "1", + name: "/fromMacroSelectedIndex2", + core, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq( + `Selected values: yes`, + ); + + // reload + core = await createTestCore({ + doenetML, + }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq(`Selected values: `); + + // can add value into from math ci.selectedIndex2 even after starting afresh + await updateMathInputValue({ + latex: "2", + name: "/fromMathSelectedIndex2", + core, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq(`Selected values: no`); + }); + + it("change choice input from copied value, math", async () => { + let doenetML = ` + + x + y + z + + +

Change from math of ci:

+

Change from math of ci.selectedValue:

+

Change from macro of $ci: $ci

+

Change from macro of $ci.selectedValue: $ci.selectedValue

+

Change from math of ci.selectedIndices:

+

Change from math of ci.selectedIndex:

+

Change from macro of $ci.selectedIndices: $ci.selectedIndices

+

Change from macro of $ci.selectedIndex: $ci.selectedIndex

+ +

Selected value: $ci.selectedValue

+ `; + + let core = await createTestCore({ + doenetML, + }); + + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq("Selected value: "); + + // typing in wrong value doesn't do anything + await updateMathInputValue({ + latex: "a", + name: "/fromMathCi", + core, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq("Selected value: "); + + // Select value from math ci + await updateMathInputValue({ + latex: "z", + name: "/fromMathCi", + core, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq(`Selected value: z`); + + // Change value from math ci.selectedValue + await updateMathInputValue({ + latex: "y", + name: "/fromMathSelectedValue", + core, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq(`Selected value: y`); + + // Invalid value into math ci.selectedValue does nothing + await updateMathInputValue({ + latex: "bad", + name: "/fromMathSelectedValue", + core, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq(`Selected value: y`); + + // reload + core = await createTestCore({ + doenetML, + }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq("Selected value: "); + + // Can change value from one macro after starting afresh + await updateMathInputValue({ + latex: "x", + name: "/fromMacroCi", + core, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq(`Selected value: x`); + + // reload + core = await createTestCore({ + doenetML, + }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq("Selected value: "); + + // Can change value from other macro after starting afresh + await updateMathInputValue({ + latex: "y", + name: "/fromMacroSelectedValue", + core, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq(`Selected value: y`); + + // reload + core = await createTestCore({ + doenetML, + }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq("Selected value: "); + + // Change value from math ci.selectedValue works after starting afresh + await updateMathInputValue({ + latex: "z", + name: "/fromMathSelectedValue", + core, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq(`Selected value: z`); + + // We can change from macros + await updateMathInputValue({ + latex: "x", + name: "/fromMacroCi", + core, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq(`Selected value: x`); + + await updateMathInputValue({ + latex: "y", + name: "/fromMacroSelectedValue", + core, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq(`Selected value: y`); + + // reload + core = await createTestCore({ + doenetML, + }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq("Selected value: "); + + // Change value from math ci.selectedIndices works after starting afresh + await updateMathInputValue({ + latex: "3", + name: "/fromMathSelectedIndices", + core, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq(`Selected value: z`); + + // Invalid value into from math ci.selectedIndex deselects all option + await updateMathInputValue({ + latex: "4", + name: "/fromMathSelectedIndex", + core, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq("Selected value: "); + + // Enter valid value into from math ci.selectedIndex + await updateMathInputValue({ + latex: "3", + name: "/fromMathSelectedIndex", + core, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq(`Selected value: z`); + + // Enter value into from macro ci.selectedIndices + await updateMathInputValue({ + latex: "2", + name: "/fromMacroSelectedIndices", + core, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq(`Selected value: y`); + + // Enter value into from macro ci.selectedIndex + await updateMathInputValue({ + latex: "1", + name: "/fromMacroSelectedIndex", + core, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq(`Selected value: x`); + + // reload + core = await createTestCore({ + doenetML, + }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq("Selected value: "); + + // Can change value from macros ci.selectedIndices after starting afresh + await updateMathInputValue({ + latex: "2", + name: "/fromMacroSelectedIndices", + core, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq(`Selected value: y`); + + // reload + core = await createTestCore({ + doenetML, + }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq("Selected value: "); + + // Can change value from macro ci.selectedIndex after starting afresh + await updateMathInputValue({ + latex: "1", + name: "/fromMacroSelectedIndex", + core, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq(`Selected value: x`); + + // reload + core = await createTestCore({ + doenetML, + }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq("Selected value: "); + + // can add value into from math ci.selectedIndex even after starting afresh + await updateMathInputValue({ + latex: "2", + name: "/fromMathSelectedIndex", + core, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq(`Selected value: y`); + }); + + it("change choice input from copied value, math, select multiple", async () => { + let doenetML = ` + + x + y + z + + +

Change from math of ci.selectedValue:

+

Change from math of ci.selectedValue2:

+

Change from macro of $ci.selectedValue: $ci.selectedValue

+

Change from macro of $ci.selectedValue2: $ci.selectedValue2

+

Change from math of ci.selectedIndex:

+

Change from math of ci.selectedIndex2:

+

Change from macro of $ci.selectedIndex: $ci.selectedIndex

+

Change from macro of $ci.selectedIndex2: $ci.selectedIndex2

+ +

Selected values: $ci.selectedValues

+ `; + + let core = await createTestCore({ + doenetML, + }); + + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq(`Selected values: `); + + // typing in wrong value doesn't do anything + await updateMathInputValue({ + latex: "nothing", + name: "/fromMathSelectedValue", + core, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq(`Selected values: `); + + // Select value from text ci.selectedValue + await updateMathInputValue({ + latex: "z", + name: "/fromMathSelectedValue", + core, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq(`Selected values: z`); + + // Add second value from text ci.selectedValue2 + await updateMathInputValue({ + latex: "y", + name: "/fromMathSelectedValue2", + core, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq( + `Selected values: y, z`, + ); + + // Invalid value into text ci.selectedValue does nothing + await updateMathInputValue({ + latex: "bad", + name: "/fromMathSelectedValue", + core, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq( + `Selected values: y, z`, + ); + + // Repeat first value from text ci.selectedValue2 reduces to one value + await updateMathInputValue({ + latex: "y", + name: "/fromMathSelectedValue2", + core, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq(`Selected values: y`); + + // Add second value from text ci.selectedValue2 + await updateMathInputValue({ + latex: "x", + name: "/fromMathSelectedValue2", + core, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq( + `Selected values: x, y`, + ); + + // Repeat second value from text ci.selectedValue reduces to one value + await updateMathInputValue({ + latex: "y", + name: "/fromMathSelectedValue", + core, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq(`Selected values: y`); + + // reload + core = await createTestCore({ + doenetML, + }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq(`Selected values: `); + + // Can change value from macros after starting afresh + await updateMathInputValue({ + latex: "x", + name: "/fromMacroSelectedValue", + core, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq(`Selected values: x`); + + // reload + core = await createTestCore({ + doenetML, + }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq(`Selected values: `); + + // Can change value from macros after starting afresh + await updateMathInputValue({ + latex: "y", + name: "/fromMacroSelectedValue2", + core, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq(`Selected values: y`); + + // reload + core = await createTestCore({ + doenetML, + }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq(`Selected values: `); + + // Change value from text ci.selectedValue2 works after starting afresh + await updateMathInputValue({ + latex: "z", + name: "/fromMathSelectedValue2", + core, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq(`Selected values: z`); + + // Add second value from text ci.selectedValue2 + await updateMathInputValue({ + latex: "x", + name: "/fromMathSelectedValue2", + core, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq( + `Selected values: x, z`, + ); + + // We can change from macros + await updateMathInputValue({ + latex: "y", + name: "/fromMacroSelectedValue", + core, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq( + `Selected values: y, z`, + ); + + await updateMathInputValue({ + latex: "x", + name: "/fromMacroSelectedValue2", + core, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq( + `Selected values: x, y`, + ); + + // reload + core = await createTestCore({ + doenetML, + }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq(`Selected values: `); + + // Change value from math ci.selectedIndex works after starting afresh + await updateMathInputValue({ + latex: "3", + name: "/fromMathSelectedIndex", + core, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq(`Selected values: z`); + + // Invalid value into from math ci.selectedIndex is reverted + await updateMathInputValue({ + latex: "4", + name: "/fromMathSelectedIndex", + core, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq(`Selected values: z`); + + // Enter valid value into from math ci.selectedIndex2 + await updateMathInputValue({ + latex: "1", + name: "/fromMathSelectedIndex2", + core, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq( + `Selected values: x, z`, + ); + + // Enter value into from macro ci.selectedIndex + await updateMathInputValue({ + latex: "2", + name: "/fromMacroSelectedIndex", + core, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq( + `Selected values: y, z`, + ); + + // Enter value into from macro ci.selectedIndex2 + await updateMathInputValue({ + latex: "1", + name: "/fromMacroSelectedIndex2", + core, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq( + `Selected values: x, y`, + ); + + // Enter repeated value into from macro ci.selectedIndex2 selects just one + await updateMathInputValue({ + latex: "1", + name: "/fromMacroSelectedIndex2", + core, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq(`Selected values: x`); + + // reload + core = await createTestCore({ + doenetML, + }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq(`Selected values: `); + + // Can change value from macro ci.selectedIndex after starting afresh + await updateMathInputValue({ + latex: "2", + name: "/fromMacroSelectedIndex", + core, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq(`Selected values: y`); + + // reload + core = await createTestCore({ + doenetML, + }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq(`Selected values: `); + + // Can change value from macro ci.selectedIndex2 after starting afresh + await updateMathInputValue({ + latex: "1", + name: "/fromMacroSelectedIndex2", + core, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq(`Selected values: x`); + + // reload + core = await createTestCore({ + doenetML, + }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq(`Selected values: `); + + // can add value into from math ci.selectedIndex2 even after starting afresh + await updateMathInputValue({ + latex: "2", + name: "/fromMathSelectedIndex2", + core, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq(`Selected values: y`); + }); +}); diff --git a/packages/doenetml-worker/src/test/tagSpecific/circle.test.ts b/packages/doenetml-worker/src/test/tagSpecific/circle.test.ts index 870f1f9a3..b21369397 100644 --- a/packages/doenetml-worker/src/test/tagSpecific/circle.test.ts +++ b/packages/doenetml-worker/src/test/tagSpecific/circle.test.ts @@ -1,39 +1,16 @@ import { describe, expect, it, vi } from "vitest"; import { createTestCore, returnAllStateVariables } from "../utils/test-core"; -import { cleanLatex } from "../utils/math"; import { + moveCircle, movePoint, movePolygon, - updateBooleanInputValue, updateMathInputValue, - updateMatrixInputValue, - updateTextInputValue, } from "../utils/actions"; import Core from "../../Core"; -import { an, X } from "vitest/dist/chunks/reporters.WnPwkmgA.js"; -import { cdataContent } from "../../../../parser/dist/generated-assets/lezer-doenet.terms"; const Mock = vi.fn(); vi.stubGlobal("postMessage", Mock); - -async function moveCircle({ - name, - cx, - cy, - core, -}: { - name: string; - cx: number; - cy: number; - core: Core; -}) { - await core.requestAction({ - event: null, - actionName: "moveCircle", - componentName: name, - args: { center: [cx, cy] }, - }); -} +vi.mock("hyperformula"); function check_circle1_and_copies_approx({ cx, @@ -215,7 +192,7 @@ describe("Circle tag tests", async () => { // attempt to set radius negative, but becomes zero radius = 0; await updateMathInputValue({ - componentName: "/radiusControl", + name: "/radiusControl", latex: "-4", core, }); @@ -224,7 +201,7 @@ describe("Circle tag tests", async () => { // change radius radius = 5; await updateMathInputValue({ - componentName: "/radiusControl", + name: "/radiusControl", latex: radius.toString(), core, }); @@ -242,7 +219,7 @@ describe("Circle tag tests", async () => { // set defining radius to negative, actual radius becomes zero radius = -3; await updateMathInputValue({ - componentName: definingRadius, + name: definingRadius, latex: radius.toString(), core, }); @@ -251,7 +228,7 @@ describe("Circle tag tests", async () => { // change radius with defining point radius = 7; await updateMathInputValue({ - componentName: definingRadius, + name: definingRadius, latex: radius.toString(), core, }); @@ -450,7 +427,7 @@ describe("Circle tag tests", async () => { cy += radius; radius = 0; await updateMathInputValue({ - componentName: "/radiusControl", + name: "/radiusControl", latex: "-4", core, }); @@ -460,7 +437,7 @@ describe("Circle tag tests", async () => { radius = 5; cy -= 5; await updateMathInputValue({ - componentName: "/radiusControl", + name: "/radiusControl", latex: radius.toString(), core, }); @@ -482,7 +459,7 @@ describe("Circle tag tests", async () => { cy += radius; radius = -3; await updateMathInputValue({ - componentName: definingRadius, + name: definingRadius, latex: radius.toString(), core, }); @@ -492,7 +469,7 @@ describe("Circle tag tests", async () => { radius = 7; cy -= 7; await updateMathInputValue({ - componentName: definingRadius, + name: definingRadius, latex: radius.toString(), core, }); @@ -3645,7 +3622,7 @@ describe("Circle tag tests", async () => { expect(stateVariables["/P3"]).is.undefined; expect(stateVariables["/x"]).is.undefined; - await updateMathInputValue({ componentName: "/n", latex: "1", core }); + await updateMathInputValue({ name: "/n", latex: "1", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/P1"].stateValues.xs.map((v) => v.tree)).eqls([ t1x, @@ -3655,7 +3632,7 @@ describe("Circle tag tests", async () => { expect(stateVariables["/P3"]).is.undefined; expect(stateVariables["/x"].stateValues.value.tree).eqls(t2x); - await updateMathInputValue({ componentName: "/n", latex: "2", core }); + await updateMathInputValue({ name: "/n", latex: "2", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/P1"].stateValues.xs.map((v) => v.tree)).eqls([ t2x, @@ -3665,7 +3642,7 @@ describe("Circle tag tests", async () => { expect(stateVariables["/P3"]).is.undefined; expect(stateVariables["/x"].stateValues.value.tree).eqls(t2y); - await updateMathInputValue({ componentName: "/n", latex: "3", core }); + await updateMathInputValue({ name: "/n", latex: "3", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/P1"].stateValues.xs.map((v) => v.tree)).eqls([ t3x, @@ -3675,7 +3652,7 @@ describe("Circle tag tests", async () => { expect(stateVariables["/P3"]).is.undefined; expect(stateVariables["/x"]).is.undefined; - await updateMathInputValue({ componentName: "/n", latex: "4", core }); + await updateMathInputValue({ name: "/n", latex: "4", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/P1"]).is.undefined; expect(stateVariables["/P2"]).is.undefined; @@ -3926,7 +3903,7 @@ describe("Circle tag tests", async () => { cx = 2; cy = 1; await updateMathInputValue({ - componentName: "/c", + name: "/c", latex: `(${cx},${cy})`, core, }); @@ -3942,7 +3919,7 @@ describe("Circle tag tests", async () => { cx = -7; cy = -4; await updateMathInputValue({ - componentName: "/c", + name: "/c", latex: `(${cx}, ${cy})`, core, }); @@ -3958,7 +3935,7 @@ describe("Circle tag tests", async () => { cx = NaN; cy = NaN; await updateMathInputValue({ - componentName: "/c", + name: "/c", latex: "", core, }); @@ -3968,7 +3945,7 @@ describe("Circle tag tests", async () => { cx = 5; cy = 4; await updateMathInputValue({ - componentName: "/c", + name: "/c", latex: `(${cx}, ${cy})`, core, }); @@ -4096,12 +4073,12 @@ describe("Circle tag tests", async () => { // enter non-numeric radius and center for origin circle await updateMathInputValue({ - componentName: "/rc", + name: "/rc", latex: `1+x`, core, }); await updateMathInputValue({ - componentName: "/cc", + name: "/cc", latex: `(-1,y)`, core, }); @@ -4117,12 +4094,12 @@ describe("Circle tag tests", async () => { // set radius and center for origin circle back to number using other components await updateMathInputValue({ - componentName: "/rc1", + name: "/rc1", latex: `2`, core, }); await updateMathInputValue({ - componentName: "/cc3", + name: "/cc3", latex: `(4,5)`, core, }); @@ -4139,7 +4116,7 @@ describe("Circle tag tests", async () => { // move point P and set radius of second circle await movePoint({ name: "/P", x: -5, y: 2, core }); await updateMathInputValue({ - componentName: "/rc1", + name: "/rc1", latex: `4`, core, }); @@ -4167,7 +4144,7 @@ describe("Circle tag tests", async () => { // set radius of third circle await updateMathInputValue({ - componentName: "/rc2", + name: "/rc2", latex: `5`, core, }); @@ -4183,7 +4160,7 @@ describe("Circle tag tests", async () => { // set center of third circle await updateMathInputValue({ - componentName: "/cc2", + name: "/cc2", latex: `(5,-3)`, core, }); @@ -4199,7 +4176,7 @@ describe("Circle tag tests", async () => { // set radius of fourth circle await updateMathInputValue({ - componentName: "/src3", + name: "/src3", latex: `9`, core, }); @@ -4216,7 +4193,7 @@ describe("Circle tag tests", async () => { // move and change radius of fourth circle await moveCircle({ name: "/c3", cx: 3, cy: 8, core }); await updateMathInputValue({ - componentName: "/rc3", + name: "/rc3", latex: `9`, core, }); @@ -4383,7 +4360,7 @@ $c7.radius // change radius await updateMathInputValue({ - componentName: "/r", + name: "/r", latex: `3`, core, }); @@ -4396,7 +4373,7 @@ $c7.radius // change to symbolic radius await updateMathInputValue({ - componentName: "/r", + name: "/r", latex: `a`, core, }); @@ -4409,7 +4386,7 @@ $c7.radius // back to numeric radius await updateMathInputValue({ - componentName: "/r", + name: "/r", latex: `5`, core, }); diff --git a/packages/doenetml-worker/src/test/tagSpecific/codeditor.test.ts b/packages/doenetml-worker/src/test/tagSpecific/codeditor.test.ts new file mode 100644 index 000000000..6818a9073 --- /dev/null +++ b/packages/doenetml-worker/src/test/tagSpecific/codeditor.test.ts @@ -0,0 +1,551 @@ +import { describe, expect, it, vi } from "vitest"; +import { createTestCore, returnAllStateVariables } from "../utils/test-core"; +import { + updateTextInputImmediateValue, + updateTextInputValueToImmediateValue, +} from "../utils/actions"; + +const Mock = vi.fn(); +vi.stubGlobal("postMessage", Mock); +vi.mock("hyperformula"); + +describe("Code Editor tag tests", async () => { + it("code editor with no arguments", async () => { + let core = await createTestCore({ + doenetML: ` + + +

$editor.immediateValue

+

$editor.value

+ `, + }); + + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p1"].stateValues.text).eq(""); + expect(stateVariables["/p2"].stateValues.text).eq(""); + expect(stateVariables["/editor"].stateValues.immediateValue).eq(""); + expect(stateVariables["/editor"].stateValues.value).eq(""); + + await updateTextInputImmediateValue({ + text: "Hello", + name: "/editor", + core, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p1"].stateValues.text).eq("Hello"); + expect(stateVariables["/p2"].stateValues.text).eq(""); + expect(stateVariables["/editor"].stateValues.immediateValue).eq( + "Hello", + ); + expect(stateVariables["/editor"].stateValues.value).eq(""); + + // No debouncing since that is initiated by the renderer. + // Have to explicitly call update value + await updateTextInputValueToImmediateValue({ name: "/editor", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p1"].stateValues.text).eq("Hello"); + expect(stateVariables["/p2"].stateValues.text).eq("Hello"); + expect(stateVariables["/editor"].stateValues.immediateValue).eq( + "Hello", + ); + expect(stateVariables["/editor"].stateValues.value).eq("Hello"); + + // type more in editor + await updateTextInputImmediateValue({ + text: "Hello\nMore here.", + name: "/editor", + core, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p1"].stateValues.text).eq("Hello\nMore here."); + expect(stateVariables["/p2"].stateValues.text).eq("Hello"); + expect(stateVariables["/editor"].stateValues.immediateValue).eq( + "Hello\nMore here.", + ); + expect(stateVariables["/editor"].stateValues.value).eq("Hello"); + + // update value again + await updateTextInputValueToImmediateValue({ name: "/editor", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p1"].stateValues.text).eq("Hello\nMore here."); + expect(stateVariables["/p2"].stateValues.text).eq("Hello\nMore here."); + expect(stateVariables["/editor"].stateValues.immediateValue).eq( + "Hello\nMore here.", + ); + expect(stateVariables["/editor"].stateValues.value).eq( + "Hello\nMore here.", + ); + }); + + it("bind value to", async () => { + let core = await createTestCore({ + doenetML: ` +

+ +

+ +

value: $ce

+

immediate value: $ce.immediateValue

+

value: $ti

+

immediate value: $ti.immediateValue

+ `, + }); + + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/piv"].stateValues.text).eq( + "immediate value: Hello!", + ); + expect(stateVariables["/pv"].stateValues.text).eq("value: Hello!"); + expect(stateVariables["/piv2"].stateValues.text).eq( + "immediate value: Hello!", + ); + expect(stateVariables["/pv2"].stateValues.text).eq("value: Hello!"); + + await updateTextInputImmediateValue({ + text: "Hello!\nSelam!", + name: "/ti", + core, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/piv"].stateValues.text).eq( + "immediate value: Hello!", + ); + expect(stateVariables["/pv"].stateValues.text).eq("value: Hello!"); + expect(stateVariables["/piv2"].stateValues.text).eq( + "immediate value: Hello!\nSelam!", + ); + expect(stateVariables["/pv2"].stateValues.text).eq("value: Hello!"); + + await updateTextInputValueToImmediateValue({ name: "/ti", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/piv"].stateValues.text).eq( + "immediate value: Hello!\nSelam!", + ); + expect(stateVariables["/pv"].stateValues.text).eq( + "value: Hello!\nSelam!", + ); + expect(stateVariables["/piv2"].stateValues.text).eq( + "immediate value: Hello!\nSelam!", + ); + expect(stateVariables["/pv2"].stateValues.text).eq( + "value: Hello!\nSelam!", + ); + + await updateTextInputImmediateValue({ + text: "Hello!\nSelam!\nKaixo!", + name: "/ce", + core, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/piv"].stateValues.text).eq( + "immediate value: Hello!\nSelam!\nKaixo!", + ); + expect(stateVariables["/pv"].stateValues.text).eq( + "value: Hello!\nSelam!", + ); + expect(stateVariables["/piv2"].stateValues.text).eq( + "immediate value: Hello!\nSelam!", + ); + expect(stateVariables["/pv2"].stateValues.text).eq( + "value: Hello!\nSelam!", + ); + + await updateTextInputValueToImmediateValue({ name: "/ce", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/piv"].stateValues.text).eq( + "immediate value: Hello!\nSelam!\nKaixo!", + ); + expect(stateVariables["/pv"].stateValues.text).eq( + "value: Hello!\nSelam!\nKaixo!", + ); + expect(stateVariables["/piv2"].stateValues.text).eq( + "immediate value: Hello!\nSelam!\nKaixo!", + ); + expect(stateVariables["/pv2"].stateValues.text).eq( + "value: Hello!\nSelam!\nKaixo!", + ); + }); + + it("prefill from children, ignore indenting", async () => { + let core = await createTestCore({ + doenetML: ` + + hello there +

More

+
+ +

value: $ce

+

immediate value: $ce.immediateValue

+ `, + }); + + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/piv"].stateValues.text).eq( + 'immediate value: hello there\n

More

\n', + ); + expect(stateVariables["/pv"].stateValues.text).eq( + 'value: hello there\n

More

\n', + ); + // child components not created + expect(stateVariables["/t1"]).eq(undefined); + expect(stateVariables["/p2"]).eq(undefined); + + await updateTextInputImmediateValue({ + text: 'hello there\n

Change

\n', + name: "/ce", + core, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/piv"].stateValues.text).eq( + 'immediate value: hello there\n

Change

\n', + ); + expect(stateVariables["/pv"].stateValues.text).eq( + 'value: hello there\n

More

\n', + ); + + await updateTextInputValueToImmediateValue({ name: "/ce", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/piv"].stateValues.text).eq( + 'immediate value: hello there\n

Change

\n', + ); + expect(stateVariables["/pv"].stateValues.text).eq( + 'value: hello there\n

Change

\n', + ); + }); + + it("valueChanged", async () => { + let doenetML = ` +

+

+

+

+ + `; + + async function check_items( + [ce1, ce2, ce3, ce4]: [ + ce1: string, + ce2: string, + ce3: string, + ce4: string, + ], + [ce1iv, ce2iv, ce3iv, ce4iv]: [ + ce1iv: string, + ce2iv: string, + ce3iv: string, + ce4iv: string, + ], + [ce1changed, ce2changed, ce3changed, ce4changed]: [ + ce1changed: boolean, + ce2changed: boolean, + ce3changed: boolean, + ce4changed: boolean, + ], + [ce1ivchanged, ce2ivchanged, ce3ivchanged, ce4ivchanged]: [ + ce1ivchanged: boolean, + ce2ivchanged: boolean, + ce3ivchanged: boolean, + ce4ivchanged: boolean, + ], + ) { + const stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/ce1"].stateValues.value).eq(ce1); + expect(stateVariables["/ce2"].stateValues.value).eq(ce2); + expect(stateVariables["/ce3"].stateValues.value).eq(ce3); + expect(stateVariables["/ce4"].stateValues.value).eq(ce4); + + expect(stateVariables["/ce1a"].stateValues.value).eq(ce1); + expect(stateVariables["/ce2a"].stateValues.value).eq(ce2); + expect(stateVariables["/ce3a"].stateValues.value).eq(ce3); + expect(stateVariables["/ce4a"].stateValues.value).eq(ce4); + + expect(stateVariables["/ce1iva"].stateValues.value).eq(ce1iv); + expect(stateVariables["/ce2iva"].stateValues.value).eq(ce2iv); + expect(stateVariables["/ce3iva"].stateValues.value).eq(ce3iv); + expect(stateVariables["/ce4iva"].stateValues.value).eq(ce4iv); + + expect(stateVariables["/ce1changed"].stateValues.value).eq( + ce1changed, + ); + expect(stateVariables["/ce2changed"].stateValues.value).eq( + ce2changed, + ); + expect(stateVariables["/ce3changed"].stateValues.value).eq( + ce3changed, + ); + expect(stateVariables["/ce4changed"].stateValues.value).eq( + ce4changed, + ); + + expect(stateVariables["/ce1ivchanged"].stateValues.value).eq( + ce1ivchanged, + ); + expect(stateVariables["/ce2ivchanged"].stateValues.value).eq( + ce2ivchanged, + ); + expect(stateVariables["/ce3ivchanged"].stateValues.value).eq( + ce3ivchanged, + ); + expect(stateVariables["/ce4ivchanged"].stateValues.value).eq( + ce4ivchanged, + ); + } + + let core = await createTestCore({ + doenetML, + }); + + let ce1 = "", + ce2 = "apple", + ce3 = "", + ce4 = "apple"; + let ce1iv = "", + ce2iv = "apple", + ce3iv = "", + ce4iv = "apple"; + let ce1changed = false, + ce2changed = false, + ce3changed = false, + ce4changed = false; + let ce1ivchanged = false, + ce2ivchanged = false, + ce3ivchanged = false, + ce4ivchanged = false; + + await check_items( + [ce1, ce2, ce3, ce4], + [ce1iv, ce2iv, ce3iv, ce4iv], + [ce1changed, ce2changed, ce3changed, ce4changed], + [ce1ivchanged, ce2ivchanged, ce3ivchanged, ce4ivchanged], + ); + + // type in first marks only first immediate value as changed + ce1iv = "banana"; + ce1ivchanged = true; + await updateTextInputImmediateValue({ + text: ce1iv, + name: "/ce1", + core, + }); + await check_items( + [ce1, ce2, ce3, ce4], + [ce1iv, ce2iv, ce3iv, ce4iv], + [ce1changed, ce2changed, ce3changed, ce4changed], + [ce1ivchanged, ce2ivchanged, ce3ivchanged, ce4ivchanged], + ); + + // update value in first marks only first value as changed + ce1 = ce3 = ce3iv = ce1iv; + ce1changed = true; + await updateTextInputValueToImmediateValue({ + name: "/ce1", + core, + }); + + await check_items( + [ce1, ce2, ce3, ce4], + [ce1iv, ce2iv, ce3iv, ce4iv], + [ce1changed, ce2changed, ce3changed, ce4changed], + [ce1ivchanged, ce2ivchanged, ce3ivchanged, ce4ivchanged], + ); + + // type in second marks only second immediate value as changed + + ce4 = ce4iv = ce2iv = "cherry"; + ce2ivchanged = true; + await updateTextInputImmediateValue({ + text: ce2iv, + name: "/ce2", + core, + }); + await check_items( + [ce1, ce2, ce3, ce4], + [ce1iv, ce2iv, ce3iv, ce4iv], + [ce1changed, ce2changed, ce3changed, ce4changed], + [ce1ivchanged, ce2ivchanged, ce3ivchanged, ce4ivchanged], + ); + + // update value in second marks only second value as changed + ce2 = ce2iv; + ce2changed = true; + await updateTextInputValueToImmediateValue({ + name: "/ce2", + core, + }); + + await check_items( + [ce1, ce2, ce3, ce4], + [ce1iv, ce2iv, ce3iv, ce4iv], + [ce1changed, ce2changed, ce3changed, ce4changed], + [ce1ivchanged, ce2ivchanged, ce3ivchanged, ce4ivchanged], + ); + + // type in third marks third immediate value as changed + ce3iv = "dragonfruit"; + ce3ivchanged = true; + await updateTextInputImmediateValue({ + text: ce3iv, + name: "/ce3", + core, + }); + await check_items( + [ce1, ce2, ce3, ce4], + [ce1iv, ce2iv, ce3iv, ce4iv], + [ce1changed, ce2changed, ce3changed, ce4changed], + [ce1ivchanged, ce2ivchanged, ce3ivchanged, ce4ivchanged], + ); + + // update value in third marks only third value as changed + ce1 = ce1iv = ce3 = ce3iv; + ce3changed = true; + await updateTextInputValueToImmediateValue({ + name: "/ce3", + core, + }); + + await check_items( + [ce1, ce2, ce3, ce4], + [ce1iv, ce2iv, ce3iv, ce4iv], + [ce1changed, ce2changed, ce3changed, ce4changed], + [ce1ivchanged, ce2ivchanged, ce3ivchanged, ce4ivchanged], + ); + + // type in fourth marks fourth immediate value as changed + ce4iv = "eggplant"; + ce4ivchanged = true; + await updateTextInputImmediateValue({ + text: ce4iv, + name: "/ce4", + core, + }); + await check_items( + [ce1, ce2, ce3, ce4], + [ce1iv, ce2iv, ce3iv, ce4iv], + [ce1changed, ce2changed, ce3changed, ce4changed], + [ce1ivchanged, ce2ivchanged, ce3ivchanged, ce4ivchanged], + ); + + // update value in fourth marks only fourth value as changed + ce2 = ce2iv = ce4 = ce4iv; + ce4changed = true; + await updateTextInputValueToImmediateValue({ + name: "/ce4", + core, + }); + + await check_items( + [ce1, ce2, ce3, ce4], + [ce1iv, ce2iv, ce3iv, ce4iv], + [ce1changed, ce2changed, ce3changed, ce4changed], + [ce1ivchanged, ce2ivchanged, ce3ivchanged, ce4ivchanged], + ); + + // reload + core = await createTestCore({ + doenetML, + }); + + ce1 = ""; + ce2 = "apple"; + ce3 = ""; + ce4 = "apple"; + ce1iv = ""; + ce2iv = "apple"; + ce3iv = ""; + ce4iv = "apple"; + ce1changed = false; + ce2changed = false; + ce3changed = false; + ce4changed = false; + ce1ivchanged = false; + ce2ivchanged = false; + ce3ivchanged = false; + ce4ivchanged = false; + + await check_items( + [ce1, ce2, ce3, ce4], + [ce1iv, ce2iv, ce3iv, ce4iv], + [ce1changed, ce2changed, ce3changed, ce4changed], + [ce1ivchanged, ce2ivchanged, ce3ivchanged, ce4ivchanged], + ); + + // type in third marks only third immediate value as changed + ce3iv = "banana"; + ce3ivchanged = true; + await updateTextInputImmediateValue({ + text: ce3iv, + name: "/ce3", + core, + }); + await check_items( + [ce1, ce2, ce3, ce4], + [ce1iv, ce2iv, ce3iv, ce4iv], + [ce1changed, ce2changed, ce3changed, ce4changed], + [ce1ivchanged, ce2ivchanged, ce3ivchanged, ce4ivchanged], + ); + + // update value in third marks first and third value/immediateValue as changed + ce1 = ce1iv = ce3 = ce3iv; + ce1changed = true; + ce1ivchanged = true; + ce3changed = true; + await updateTextInputValueToImmediateValue({ + name: "/ce3", + core, + }); + + await check_items( + [ce1, ce2, ce3, ce4], + [ce1iv, ce2iv, ce3iv, ce4iv], + [ce1changed, ce2changed, ce3changed, ce4changed], + [ce1ivchanged, ce2ivchanged, ce3ivchanged, ce4ivchanged], + ); + + // type in fourth marks only fourth immediate value as changed + ce4iv = "eggplant"; + ce4ivchanged = true; + await updateTextInputImmediateValue({ + text: ce4iv, + name: "/ce4", + core, + }); + await check_items( + [ce1, ce2, ce3, ce4], + [ce1iv, ce2iv, ce3iv, ce4iv], + [ce1changed, ce2changed, ce3changed, ce4changed], + [ce1ivchanged, ce2ivchanged, ce3ivchanged, ce4ivchanged], + ); + + // update value in fourth marks second and fourth value/immediateValue as changed + ce2 = ce2iv = ce4 = ce4iv; + ce2changed = true; + ce2ivchanged = true; + ce4changed = true; + await updateTextInputValueToImmediateValue({ + name: "/ce4", + core, + }); + + await check_items( + [ce1, ce2, ce3, ce4], + [ce1iv, ce2iv, ce3iv, ce4iv], + [ce1changed, ce2changed, ce3changed, ce4changed], + [ce1ivchanged, ce2ivchanged, ce3ivchanged, ce4ivchanged], + ); + }); + + it("ignore variants from children", async () => { + let core = await createTestCore({ + doenetML: ` + + `, + }); + + // Have only one variant despite selectFromSequence child + let stateVariables = await returnAllStateVariables(core); + + expect( + stateVariables["/_document1"].sharedParameters.allPossibleVariants, + ).eqls(["a"]); + }); +}); diff --git a/packages/doenetml-worker/src/test/tagSpecific/collect.test.ts b/packages/doenetml-worker/src/test/tagSpecific/collect.test.ts index fe4a46064..e42dd57a7 100644 --- a/packages/doenetml-worker/src/test/tagSpecific/collect.test.ts +++ b/packages/doenetml-worker/src/test/tagSpecific/collect.test.ts @@ -1,15 +1,15 @@ import { describe, expect, it, vi } from "vitest"; import { createTestCore, returnAllStateVariables } from "../utils/test-core"; -import { cleanLatex } from "../utils/math"; import { + movePoint, + moveVector, updateBooleanInputValue, updateMathInputValue, - updateMatrixInputValue, - updateTextInputValue, } from "../utils/actions"; const Mock = vi.fn(); vi.stubGlobal("postMessage", Mock); +vi.mock("hyperformula"); describe("Collect tag tests", async () => { it("collect points from graphs", async () => { @@ -50,12 +50,7 @@ describe("Collect tag tests", async () => { let x3 = 4, y3 = 2; - let coords1Text = ("(" + x1 + "," + y1 + ")").replace(/-/g, "−"); - let coords2Text = ("(" + x2 + "," + y2 + ")").replace(/-/g, "−"); - let coords3Text = ("(" + x3 + "," + y3 + ")").replace(/-/g, "−"); - let coords2tText = ("(" + y2 + "," + x2 + ")").replace(/-/g, "−"); - - let meany = (y1 + y2 + y1 + y3 + x2) / 5; + let mean_y = (y1 + y2 + y1 + y3 + x2) / 5; let xs = [x1, x2, x1, x3, y2]; let ys = [y1, y2, y1, y3, x2]; @@ -91,21 +86,16 @@ describe("Collect tag tests", async () => { ys[i], ); } - expect(stateVariables["/mean"].stateValues.value.tree).eq(meany); + expect(stateVariables["/mean"].stateValues.value.tree).eq(mean_y); // move point 1 x1 = -8; y1 = 6; xs = [x1, x2, x1, x3, y2]; ys = [y1, y2, y1, y3, x2]; - meany = (y1 + y2 + y1 + y3 + x2) / 5; + mean_y = (y1 + y2 + y1 + y3 + x2) / 5; - await core.requestAction({ - actionName: "movePoint", - componentName: "/_point1", - args: { x: x1, y: y1 }, - event: null, - }); + await movePoint({ name: "/_point1", x: x1, y: y1, core }); stateVariables = await returnAllStateVariables(core); @@ -140,21 +130,16 @@ describe("Collect tag tests", async () => { ys[i], ); } - expect(stateVariables["/mean"].stateValues.value.tree).eq(meany); + expect(stateVariables["/mean"].stateValues.value.tree).eq(mean_y); // move point 1 via copy x1 = 2; y1 = 0; xs = [x1, x2, x1, x3, y2]; ys = [y1, y2, y1, y3, x2]; - meany = (y1 + y2 + y1 + y3 + x2) / 5; + mean_y = (y1 + y2 + y1 + y3 + x2) / 5; - await core.requestAction({ - actionName: "movePoint", - componentName: "/p1a", - args: { x: x1, y: y1 }, - event: null, - }); + await movePoint({ name: "/p1a", x: x1, y: y1, core }); stateVariables = await returnAllStateVariables(core); for (let i = 0; i < 5; i++) { @@ -188,21 +173,16 @@ describe("Collect tag tests", async () => { ys[i], ); } - expect(stateVariables["/mean"].stateValues.value.tree).eq(meany); + expect(stateVariables["/mean"].stateValues.value.tree).eq(mean_y); // move point 2 x2 = 4; y2 = 8; xs = [x1, x2, x1, x3, y2]; ys = [y1, y2, y1, y3, x2]; - meany = (y1 + y2 + y1 + y3 + x2) / 5; + mean_y = (y1 + y2 + y1 + y3 + x2) / 5; - await core.requestAction({ - actionName: "movePoint", - componentName: "/_point2", - args: { x: x2, y: y2 }, - event: null, - }); + await movePoint({ name: "/_point2", x: x2, y: y2, core }); stateVariables = await returnAllStateVariables(core); @@ -237,21 +217,16 @@ describe("Collect tag tests", async () => { ys[i], ); } - expect(stateVariables["/mean"].stateValues.value.tree).eq(meany); + expect(stateVariables["/mean"].stateValues.value.tree).eq(mean_y); // move flipped point 2 x2 = -1; y2 = -3; xs = [x1, x2, x1, x3, y2]; ys = [y1, y2, y1, y3, x2]; - meany = (y1 + y2 + y1 + y3 + x2) / 5; + mean_y = (y1 + y2 + y1 + y3 + x2) / 5; - await core.requestAction({ - actionName: "movePoint", - componentName: "/_point4", - args: { x: y2, y: x2 }, - event: null, - }); + await movePoint({ name: "/_point4", x: y2, y: x2, core }); stateVariables = await returnAllStateVariables(core); @@ -286,21 +261,16 @@ describe("Collect tag tests", async () => { ys[i], ); } - expect(stateVariables["/mean"].stateValues.value.tree).eq(meany); + expect(stateVariables["/mean"].stateValues.value.tree).eq(mean_y); // move point 3 x3 = -5; y3 = 9; xs = [x1, x2, x1, x3, y2]; ys = [y1, y2, y1, y3, x2]; - meany = (y1 + y2 + y1 + y3 + x2) / 5; + mean_y = (y1 + y2 + y1 + y3 + x2) / 5; - await core.requestAction({ - actionName: "movePoint", - componentName: "/_point3", - args: { x: x3, y: y3 }, - event: null, - }); + await movePoint({ name: "/_point3", x: x3, y: y3, core }); stateVariables = await returnAllStateVariables(core); @@ -335,7 +305,7 @@ describe("Collect tag tests", async () => { ys[i], ); } - expect(stateVariables["/mean"].stateValues.value.tree).eq(meany); + expect(stateVariables["/mean"].stateValues.value.tree).eq(mean_y); }); it("collect dynamic points from graphs", async () => { @@ -421,7 +391,7 @@ describe("Collect tag tests", async () => { // increase number of points await updateMathInputValue({ latex: "5", - componentName: "/length", + name: "/length", core, }); @@ -475,7 +445,7 @@ describe("Collect tag tests", async () => { // change multiple await updateMathInputValue({ latex: "0.5", - componentName: "/mult", + name: "/mult", core, }); @@ -529,7 +499,7 @@ describe("Collect tag tests", async () => { // decrease number of points await updateMathInputValue({ latex: "1", - componentName: "/length", + name: "/length", core, }); @@ -583,7 +553,7 @@ describe("Collect tag tests", async () => { // increase number of points back to 4 await updateMathInputValue({ latex: "4", - componentName: "/length", + name: "/length", core, }); @@ -637,7 +607,7 @@ describe("Collect tag tests", async () => { // increase number of points to 6 await updateMathInputValue({ latex: "6", - componentName: "/length", + name: "/length", core, }); @@ -772,7 +742,7 @@ describe("Collect tag tests", async () => { // increase number of points await updateMathInputValue({ latex: "5", - componentName: "/length", + name: "/length", core, }); @@ -826,7 +796,7 @@ describe("Collect tag tests", async () => { // change multiple await updateMathInputValue({ latex: "0.5", - componentName: "/mult", + name: "/mult", core, }); @@ -880,7 +850,7 @@ describe("Collect tag tests", async () => { // decrease number of points await updateMathInputValue({ latex: "1", - componentName: "/length", + name: "/length", core, }); @@ -934,7 +904,7 @@ describe("Collect tag tests", async () => { // increase number of points back to 4 await updateMathInputValue({ latex: "4", - componentName: "/length", + name: "/length", core, }); @@ -988,7 +958,7 @@ describe("Collect tag tests", async () => { // increase number of points to 6 await updateMathInputValue({ latex: "6", - componentName: "/length", + name: "/length", core, }); @@ -1156,11 +1126,11 @@ describe("Collect tag tests", async () => { x2 = 3; y2 = 2; - await core.requestAction({ - actionName: "moveVector", - componentName: "/v3", - args: { tailcoords: [x1, y1], headcoords: [x2, y2] }, - event: null, + await moveVector({ + name: "/v3", + tailcoords: [x1, y1], + headcoords: [x2, y2], + core, }); stateVariables = await returnAllStateVariables(core); @@ -1242,11 +1212,11 @@ describe("Collect tag tests", async () => { x2 = -7; y2 = 5; - await core.requestAction({ - actionName: "moveVector", - componentName: "/v6", - args: { tailcoords: [y1, x1], headcoords: [y2, x2] }, - event: null, + await moveVector({ + name: "/v6", + tailcoords: [y1, x1], + headcoords: [y2, x2], + core, }); stateVariables = await returnAllStateVariables(core); @@ -1407,7 +1377,7 @@ describe("Collect tag tests", async () => { // increase maxnumber await updateMathInputValue({ latex: "5", - componentName: "/maxnumber", + name: "/maxnumber", core, }); @@ -1459,7 +1429,7 @@ describe("Collect tag tests", async () => { // increase maxnumber further await updateMathInputValue({ latex: "10", - componentName: "/maxnumber", + name: "/maxnumber", core, }); @@ -1513,7 +1483,7 @@ describe("Collect tag tests", async () => { // change multiple await updateMathInputValue({ latex: "0.5", - componentName: "/mult", + name: "/mult", core, }); @@ -1567,7 +1537,7 @@ describe("Collect tag tests", async () => { // decrease number of points await updateMathInputValue({ latex: "1", - componentName: "/length", + name: "/length", core, }); @@ -1621,7 +1591,7 @@ describe("Collect tag tests", async () => { // increase number of points back to 4 await updateMathInputValue({ latex: "4", - componentName: "/length", + name: "/length", core, }); @@ -1675,7 +1645,7 @@ describe("Collect tag tests", async () => { // decrease max number to 3 await updateMathInputValue({ latex: "3", - componentName: "/maxnumber", + name: "/maxnumber", core, }); @@ -1804,7 +1774,7 @@ describe("Collect tag tests", async () => { async function set_and_check_items(values, mis_used = "orig") { await updateMathInputValue({ latex: `${values.length}`, - componentName: "/n", + name: "/n", core, }); @@ -1828,7 +1798,7 @@ describe("Collect tag tests", async () => { await updateMathInputValue({ latex: val, - componentName: mi_name, + name: mi_name, core, }); } @@ -2272,7 +2242,7 @@ describe("Collect tag tests", async () => { ); expect(stateVariables["/c2"].stateValues.text).eq("collect 2: "); - await updateMathInputValue({ latex: "6", componentName: "/n", core }); + await updateMathInputValue({ latex: "6", name: "/n", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/c1"].stateValues.text).eq( @@ -2282,12 +2252,12 @@ describe("Collect tag tests", async () => { await updateBooleanInputValue({ boolean: true, - componentName: "/h1", + name: "/h1", core, }); await updateBooleanInputValue({ boolean: false, - componentName: "/h2", + name: "/h2", core, }); stateVariables = await returnAllStateVariables(core); @@ -2297,7 +2267,7 @@ describe("Collect tag tests", async () => { "collect 2: Hello, a! Hello, b! Hello, c! Hello, d! Hello, e! Hello, f! ", ); - await updateMathInputValue({ latex: "8", componentName: "/n", core }); + await updateMathInputValue({ latex: "8", name: "/n", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/c1"].stateValues.text).eq("collect 1: "); @@ -2307,12 +2277,12 @@ describe("Collect tag tests", async () => { await updateBooleanInputValue({ boolean: false, - componentName: "/h1", + name: "/h1", core, }); await updateBooleanInputValue({ boolean: true, - componentName: "/h2", + name: "/h2", core, }); stateVariables = await returnAllStateVariables(core); @@ -2322,7 +2292,7 @@ describe("Collect tag tests", async () => { ); expect(stateVariables["/c2"].stateValues.text).eq("collect 2: "); - await updateMathInputValue({ latex: "3", componentName: "/n", core }); + await updateMathInputValue({ latex: "3", name: "/n", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/c1"].stateValues.text).eq( @@ -2332,12 +2302,12 @@ describe("Collect tag tests", async () => { await updateBooleanInputValue({ boolean: true, - componentName: "/h1", + name: "/h1", core, }); await updateBooleanInputValue({ boolean: false, - componentName: "/h2", + name: "/h2", core, }); stateVariables = await returnAllStateVariables(core); @@ -2347,7 +2317,7 @@ describe("Collect tag tests", async () => { "collect 2: Hello, a! Hello, b! Hello, c! ", ); - await updateMathInputValue({ latex: "4", componentName: "/n", core }); + await updateMathInputValue({ latex: "4", name: "/n", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/c1"].stateValues.text).eq("collect 1: "); @@ -2474,13 +2444,13 @@ describe("Collect tag tests", async () => { await checkAllChildren(); - await updateMathInputValue({ latex: "4", componentName: "/n", core }); + await updateMathInputValue({ latex: "4", name: "/n", core }); await checkAllChildren(); - await updateMathInputValue({ latex: "3", componentName: "/n", core }); + await updateMathInputValue({ latex: "3", name: "/n", core }); await checkAllChildren(); - await updateMathInputValue({ latex: "1", componentName: "/n", core }); + await updateMathInputValue({ latex: "1", name: "/n", core }); await checkAllChildren(); }); @@ -2524,14 +2494,14 @@ describe("Collect tag tests", async () => { await updateBooleanInputValue({ boolean: true, - componentName: "/fixed", + name: "/fixed", core, }); await check_items(true); await updateBooleanInputValue({ boolean: false, - componentName: "/fixed", + name: "/fixed", core, }); await check_items(false); @@ -2621,34 +2591,24 @@ describe("Collect tag tests", async () => { await check_items({ x1, x2, y1, y2 }); // restrict collection to first component - await updateMathInputValue({ latex: "1", componentName: "/n", core }); + await updateMathInputValue({ latex: "1", name: "/n", core }); await check_items({ x1, x2, y1, y2, index: 1 }); // move copied point x1 = 9; y1 = -5; - await core.requestAction({ - actionName: "movePoint", - componentName: "/A2", - args: { x: x1, y: y1 }, - event: null, - }); + await movePoint({ name: "/A2", x: x1, y: y1, core }); await check_items({ x1, x2, y1, y2, index: 1 }); // restrict collection to second component - await updateMathInputValue({ latex: "2", componentName: "/n", core }); + await updateMathInputValue({ latex: "2", name: "/n", core }); await check_items({ x1, x2, y1, y2, index: 2 }); // move double copied point x2 = 0; y2 = 8; - await core.requestAction({ - actionName: "movePoint", - componentName: "/g3/A2", - args: { x: x2, y: y2 }, - event: null, - }); + await movePoint({ name: "/g3/A2", x: x2, y: y2, core }); await check_items({ x1, x2, y1, y2, index: 2 }); }); @@ -2730,68 +2690,58 @@ describe("Collect tag tests", async () => { // set propIndex to 1 let propIndex = 1; - await updateMathInputValue({ latex: "1", componentName: "/n", core }); + await updateMathInputValue({ latex: "1", name: "/n", core }); await check_items({ x1, y1, x2, y2, propIndex }); // move point 1 x1 = 9; y1 = -5; - await core.requestAction({ - actionName: "movePoint", - componentName: "/A", - args: { x: x1, y: y1 }, - event: null, - }); + await movePoint({ name: "/A", x: x1, y: y1, core }); await check_items({ x1, y1, x2, y2, propIndex }); // set componentIndex to 2 let componentIndex = 2; - await updateMathInputValue({ latex: "2", componentName: "/m", core }); + await updateMathInputValue({ latex: "2", name: "/m", core }); await check_items({ x1, y1, x2, y2, propIndex, componentIndex }); // move point2 x2 = 0; y2 = 8; - await core.requestAction({ - actionName: "movePoint", - componentName: "/B", - args: { x: x2, y: y2 }, - event: null, - }); + await movePoint({ name: "/B", x: x2, y: y2, core }); await check_items({ x1, y1, x2, y2, propIndex, componentIndex }); // set propIndex to 2 propIndex = 2; - await updateMathInputValue({ latex: "2", componentName: "/n", core }); + await updateMathInputValue({ latex: "2", name: "/n", core }); await check_items({ x1, y1, x2, y2, propIndex, componentIndex }); // set componentIndex to 1 componentIndex = 1; - await updateMathInputValue({ latex: "1", componentName: "/m", core }); + await updateMathInputValue({ latex: "1", name: "/m", core }); await check_items({ x1, y1, x2, y2, propIndex, componentIndex }); // set propIndex to 3 propIndex = 3; - await updateMathInputValue({ latex: "3", componentName: "/n", core }); + await updateMathInputValue({ latex: "3", name: "/n", core }); await check_items({ x1, y1, x2, y2, propIndex, componentIndex }); // set propIndex to 1 propIndex = 3; - await updateMathInputValue({ latex: "3", componentName: "/n", core }); + await updateMathInputValue({ latex: "3", name: "/n", core }); await check_items({ x1, y1, x2, y2, propIndex, componentIndex }); // set componentIndex to 3 componentIndex = 3; - await updateMathInputValue({ latex: "3", componentName: "/m", core }); + await updateMathInputValue({ latex: "3", name: "/m", core }); await check_items({ x1, y1, x2, y2, propIndex, componentIndex }); // set componentIndex to 2 componentIndex = 2; - await updateMathInputValue({ latex: "2", componentName: "/m", core }); + await updateMathInputValue({ latex: "2", name: "/m", core }); await check_items({ x1, y1, x2, y2, propIndex, componentIndex }); // clear propIndex - await updateMathInputValue({ latex: "", componentName: "/n", core }); + await updateMathInputValue({ latex: "", name: "/n", core }); await check_items({ x1, y1, x2, y2, componentIndex }); }); @@ -2890,7 +2840,7 @@ describe("Collect tag tests", async () => { await updateBooleanInputValue({ boolean: true, - componentName: "/bi", + name: "/bi", core, }); diff --git a/packages/doenetml-worker/src/test/tagSpecific/conditionalcontent.test.ts b/packages/doenetml-worker/src/test/tagSpecific/conditionalcontent.test.ts index 30f949e17..76aefccf5 100644 --- a/packages/doenetml-worker/src/test/tagSpecific/conditionalcontent.test.ts +++ b/packages/doenetml-worker/src/test/tagSpecific/conditionalcontent.test.ts @@ -5,9 +5,11 @@ import { updateMathInputValue, updateTextInputValue, } from "../utils/actions"; +import Core from "../../Core"; const Mock = vi.fn(); vi.stubGlobal("postMessage", Mock); +vi.mock("hyperformula"); describe("Conditional content tag tests", async () => { // tests without cases or else @@ -25,20 +27,20 @@ describe("Conditional content tag tests", async () => { await check_text("something else"); - await updateMathInputValue({ latex: "10", componentName: "/n", core }); + await updateMathInputValue({ latex: "10", name: "/n", core }); await check_text("a positive number"); await updateMathInputValue({ latex: "-5/9", - componentName: "/n", + name: "/n", core, }); await check_text("a negative number"); - await updateMathInputValue({ latex: "5-5", componentName: "/n", core }); + await updateMathInputValue({ latex: "5-5", name: "/n", core }); await check_text("zero"); - await updateMathInputValue({ latex: "x", componentName: "/n", core }); + await updateMathInputValue({ latex: "x", name: "/n", core }); await check_text("something else"); } @@ -138,20 +140,20 @@ describe("Conditional content tag tests", async () => { await check_text(4); - await updateMathInputValue({ latex: "10", componentName: "/n", core }); + await updateMathInputValue({ latex: "10", name: "/n", core }); await check_text(1); await updateMathInputValue({ latex: "-5/9", - componentName: "/n", + name: "/n", core, }); await check_text(2); - await updateMathInputValue({ latex: "5-5", componentName: "/n", core }); + await updateMathInputValue({ latex: "5-5", name: "/n", core }); await check_text(3); - await updateMathInputValue({ latex: "x", componentName: "/n", core }); + await updateMathInputValue({ latex: "x", name: "/n", core }); await check_text(4); }); @@ -175,7 +177,7 @@ describe("Conditional content tag tests", async () => { await updateBooleanInputValue({ boolean: true, - componentName: "/b", + name: "/b", core, }); @@ -229,11 +231,11 @@ describe("Conditional content tag tests", async () => { await check_text([]); // enter 1 - await updateMathInputValue({ latex: "1", componentName: "/n", core }); + await updateMathInputValue({ latex: "1", name: "/n", core }); await check_text(["dog", "mouse", "cat"]); // enter 0 - await updateMathInputValue({ latex: "0", componentName: "/n", core }); + await updateMathInputValue({ latex: "0", name: "/n", core }); await check_text([]); }); @@ -266,7 +268,7 @@ describe("Conditional content tag tests", async () => { await updateBooleanInputValue({ boolean: true, - componentName: "/hide", + name: "/hide", core, }); stateVariables = await returnAllStateVariables(core); @@ -279,7 +281,7 @@ describe("Conditional content tag tests", async () => { await updateBooleanInputValue({ boolean: true, - componentName: "/show_copy", + name: "/show_copy", core, }); stateVariables = await returnAllStateVariables(core); @@ -290,7 +292,7 @@ describe("Conditional content tag tests", async () => { await updateBooleanInputValue({ boolean: false, - componentName: "/hide", + name: "/hide", core, }); stateVariables = await returnAllStateVariables(core); @@ -339,22 +341,22 @@ describe("Conditional content tag tests", async () => { // enter 1 - await updateMathInputValue({ componentName: "/n", latex: "1", core }); + await updateMathInputValue({ name: "/n", latex: "1", core }); await check_text("cat"); // enter 10 - await updateMathInputValue({ componentName: "/n", latex: "10", core }); + await updateMathInputValue({ name: "/n", latex: "10", core }); await check_text("mouse"); // enter -1 - await updateMathInputValue({ componentName: "/n", latex: "-1", core }); + await updateMathInputValue({ name: "/n", latex: "-1", core }); await check_text("dog"); // enter x - await updateMathInputValue({ componentName: "/n", latex: "x", core }); + await updateMathInputValue({ name: "/n", latex: "x", core }); await check_text("mouse"); }); @@ -392,22 +394,22 @@ describe("Conditional content tag tests", async () => { // enter 1 - await updateMathInputValue({ componentName: "/n", latex: "1", core }); + await updateMathInputValue({ name: "/n", latex: "1", core }); await check_text("cat"); // enter 10 - await updateMathInputValue({ componentName: "/n", latex: "10", core }); + await updateMathInputValue({ name: "/n", latex: "10", core }); await check_text("mouse"); // enter -1 - await updateMathInputValue({ componentName: "/n", latex: "-1", core }); + await updateMathInputValue({ name: "/n", latex: "-1", core }); await check_text("dog"); // enter x - await updateMathInputValue({ componentName: "/n", latex: "x", core }); + await updateMathInputValue({ name: "/n", latex: "x", core }); await check_text("mouse"); }); @@ -480,19 +482,19 @@ describe("Conditional content tag tests", async () => { await check_items("mouse", "z"); // enter 1 - await updateMathInputValue({ latex: "1", componentName: "/n", core }); + await updateMathInputValue({ latex: "1", name: "/n", core }); await check_items("cat", "y"); // enter 10 - await updateMathInputValue({ latex: "10", componentName: "/n", core }); + await updateMathInputValue({ latex: "10", name: "/n", core }); await check_items("mouse", "z"); // enter -1 - await updateMathInputValue({ latex: "-1", componentName: "/n", core }); + await updateMathInputValue({ latex: "-1", name: "/n", core }); await check_items("dog", "x", "optional text!"); // enter x - await updateMathInputValue({ latex: "x", componentName: "/n", core }); + await updateMathInputValue({ latex: "x", name: "/n", core }); await check_items("mouse", "z"); } @@ -578,7 +580,7 @@ describe("Conditional content tag tests", async () => { skipSingletons = false, calcNegativeFromContainers, }: { - core: any; + core: Core; namePrefixes: string[]; skipSingletons?: boolean; calcNegativeFromContainers?: string[]; @@ -714,7 +716,7 @@ describe("Conditional content tag tests", async () => { }); // enter 1 - await updateMathInputValue({ latex: "1", componentName: "/n", core }); + await updateMathInputValue({ latex: "1", name: "/n", core }); await check_items({ animal: "cat", @@ -725,7 +727,7 @@ describe("Conditional content tag tests", async () => { }); // enter -1 - await updateMathInputValue({ latex: "-1", componentName: "/n", core }); + await updateMathInputValue({ latex: "-1", name: "/n", core }); await check_items({ animal: "dog", @@ -737,7 +739,7 @@ describe("Conditional content tag tests", async () => { }); // enter 10 - await updateMathInputValue({ latex: "10", componentName: "/n", core }); + await updateMathInputValue({ latex: "10", name: "/n", core }); await check_items({ animal: "mouse", @@ -975,7 +977,7 @@ describe("Conditional content tag tests", async () => { namePrefixes, inputName, }: { - core: any; + core: Core; namePrefixes: string[]; inputName: string; }) { @@ -1019,7 +1021,7 @@ describe("Conditional content tag tests", async () => { await updateTextInputValue({ text: "Fred", - componentName: inputName, + name: inputName, core, }); await check_items({ @@ -1028,7 +1030,7 @@ describe("Conditional content tag tests", async () => { response: "Fred", }); - await updateMathInputValue({ latex: "-1", componentName: "/n", core }); + await updateMathInputValue({ latex: "-1", name: "/n", core }); await check_items({ question: "What is your favorite animal? ", comeback: "I like , too.", @@ -1037,7 +1039,7 @@ describe("Conditional content tag tests", async () => { await updateTextInputValue({ text: "dogs", - componentName: inputName, + name: inputName, core, }); await check_items({ @@ -1046,7 +1048,7 @@ describe("Conditional content tag tests", async () => { response: "dogs", }); - await updateMathInputValue({ latex: "3", componentName: "/n", core }); + await updateMathInputValue({ latex: "3", name: "/n", core }); await check_items({ question: "Anything else? ", comeback: "To repeat: .", @@ -1055,7 +1057,7 @@ describe("Conditional content tag tests", async () => { await updateTextInputValue({ text: "Goodbye", - componentName: inputName, + name: inputName, core, }); await check_items({ @@ -1169,15 +1171,15 @@ describe("Conditional content tag tests", async () => { await check_items("non-positive", "neither"); // enter 1 - await updateMathInputValue({ latex: "1", componentName: "/n", core }); + await updateMathInputValue({ latex: "1", name: "/n", core }); await check_items("positive", "positive"); // enter -1 - await updateMathInputValue({ latex: "-1", componentName: "/n", core }); + await updateMathInputValue({ latex: "-1", name: "/n", core }); await check_items("non-positive", "negative"); // enter 0 - await updateMathInputValue({ latex: "0", componentName: "/n", core }); + await updateMathInputValue({ latex: "0", name: "/n", core }); await check_items("non-positive", "neither"); }); @@ -1216,15 +1218,15 @@ describe("Conditional content tag tests", async () => { await check_items("bye", "bye"); // enter 1 - await updateMathInputValue({ latex: "1", componentName: "/n", core }); + await updateMathInputValue({ latex: "1", name: "/n", core }); await check_items("hello", "oops"); // enter -1 - await updateMathInputValue({ latex: "-1", componentName: "/n", core }); + await updateMathInputValue({ latex: "-1", name: "/n", core }); await check_items("bye", "hello"); // enter 0 - await updateMathInputValue({ latex: "0", componentName: "/n", core }); + await updateMathInputValue({ latex: "0", name: "/n", core }); await check_items("bye", "bye"); }); @@ -1269,33 +1271,33 @@ describe("Conditional content tag tests", async () => { await check_items("mouse", ["b"]); // enter 1 - await updateMathInputValue({ latex: "1", componentName: "/n", core }); + await updateMathInputValue({ latex: "1", name: "/n", core }); await check_items("cat", ["b"]); await updateBooleanInputValue({ boolean: true, - componentName: "/h1", + name: "/h1", core, }); await updateBooleanInputValue({ boolean: false, - componentName: "/h2", + name: "/h2", core, }); await check_items("cat", ["a"]); // enter -3 - await updateMathInputValue({ latex: "-3", componentName: "/n", core }); + await updateMathInputValue({ latex: "-3", name: "/n", core }); await check_items("dog", ["a"]); await updateBooleanInputValue({ boolean: false, - componentName: "/h1", + name: "/h1", core, }); await updateBooleanInputValue({ boolean: true, - componentName: "/h2", + name: "/h2", core, }); @@ -1360,11 +1362,11 @@ describe("Conditional content tag tests", async () => { await check_items("elephant", "trumpets"); // enter 1 - await updateMathInputValue({ latex: "1", componentName: "/n", core }); + await updateMathInputValue({ latex: "1", name: "/n", core }); await check_items("fox", "jumps"); // enter 0 - await updateMathInputValue({ latex: "0", componentName: "/n", core }); + await updateMathInputValue({ latex: "0", name: "/n", core }); await check_items("elephant", "trumpets"); }); @@ -1382,7 +1384,7 @@ describe("Conditional content tag tests", async () => { `, }); - await updateMathInputValue({ latex: "1", componentName: "/n", core }); + await updateMathInputValue({ latex: "1", name: "/n", core }); const stateVariables = await returnAllStateVariables(core); @@ -1495,16 +1497,16 @@ describe("Conditional content tag tests", async () => { await check_items(0); - await updateMathInputValue({ latex: "1", componentName: "/n", core }); + await updateMathInputValue({ latex: "1", name: "/n", core }); await check_items(1); - await updateMathInputValue({ latex: "2", componentName: "/n", core }); + await updateMathInputValue({ latex: "2", name: "/n", core }); await check_items(2); - await updateMathInputValue({ latex: "3", componentName: "/n", core }); + await updateMathInputValue({ latex: "3", name: "/n", core }); await check_items(3); - await updateMathInputValue({ latex: "x", componentName: "/n", core }); + await updateMathInputValue({ latex: "x", name: "/n", core }); await check_items(0); }); @@ -1534,14 +1536,14 @@ describe("Conditional content tag tests", async () => { await updateBooleanInputValue({ boolean: false, - componentName: "/h", + name: "/h", core, }); await check_items(false); await updateBooleanInputValue({ boolean: true, - componentName: "/h", + name: "/h", core, }); await check_items(true); diff --git a/packages/doenetml-worker/src/test/tagSpecific/copy.test.ts b/packages/doenetml-worker/src/test/tagSpecific/copy.test.ts index 8d9686271..5794ec8af 100644 --- a/packages/doenetml-worker/src/test/tagSpecific/copy.test.ts +++ b/packages/doenetml-worker/src/test/tagSpecific/copy.test.ts @@ -2,16 +2,24 @@ import { describe, expect, it, vi } from "vitest"; import { createTestCore, returnAllStateVariables } from "../utils/test-core"; import { cleanLatex } from "../utils/math"; import { + moveCircle, + moveLine, + movePoint, + moveVector, + submitAnswer, updateBooleanInputValue, updateMathInputImmediateValue, updateMathInputValue, updateMathInputValueToImmediateValue, updateTextInputValue, + updateValue, } from "../utils/actions"; import me from "math-expressions"; +import Core from "../../Core"; const Mock = vi.fn(); vi.stubGlobal("postMessage", Mock); +vi.mock("hyperformula"); function isUndefinedOrInactive(comp) { expect( @@ -379,34 +387,19 @@ describe("Copy tag tests", async () => { // move point 1 x = -3; y = 5; - await core.requestAction({ - actionName: "movePoint", - componentName: "/p1", - args: { x, y }, - event: null, - }); + await movePoint({ name: "/p1", x, y, core }); await check_items(x, y); // move point 2 x = 6; y = -9; - await core.requestAction({ - actionName: "movePoint", - componentName: "/p2", - args: { x, y }, - event: null, - }); + await movePoint({ name: "/p2", x, y, core }); await check_items(x, y); // move point 3 x = -7; y = -2; - await core.requestAction({ - actionName: "movePoint", - componentName: "/p3", - args: { x: y, y: x }, - event: null, - }); + await movePoint({ name: "/p3", x: y, y: x, core }); await check_items(x, y); } @@ -501,14 +494,11 @@ describe("Copy tag tests", async () => { v_head = displacement.map((x, i) => x + v_tail[i]); d_head = displacement.map((x, i) => x + d_tail[i]); - await core.requestAction({ - actionName: "moveVector", - componentName: "/vector1", - args: { - tailcoords: v_tail, - headcoords: v_head, - }, - event: null, + await moveVector({ + name: "/vector1", + tailcoords: v_tail, + headcoords: v_head, + core, }); stateVariables = await returnAllStateVariables(core); @@ -549,13 +539,11 @@ describe("Copy tag tests", async () => { v_head = displacement.map((x, i) => x + v_tail[i]); d_head = displacement.map((x, i) => x + d_tail[i]); - await core.requestAction({ - actionName: "moveVector", - componentName: "/d1", - args: { - tailcoords: d_tail, - headcoords: d_head, - }, + await moveVector({ + name: "/d1", + tailcoords: d_tail, + headcoords: d_head, + core, }); stateVariables = await returnAllStateVariables(core); @@ -596,13 +584,11 @@ describe("Copy tag tests", async () => { v_head = displacement.map((x, i) => x + v_tail[i]); d_head = displacement.map((x, i) => x + d_tail[i]); - await core.requestAction({ - actionName: "moveVector", - componentName: "/d2", - args: { - tailcoords: d_tail, - headcoords: d_head, - }, + await moveVector({ + name: "/d2", + tailcoords: d_tail, + headcoords: d_head, + core, }); stateVariables = await returnAllStateVariables(core); @@ -643,13 +629,11 @@ describe("Copy tag tests", async () => { v_head = displacement.map((x, i) => x + v_tail[i]); d_head = displacement.map((x, i) => x + d_tail[i]); - await core.requestAction({ - actionName: "moveVector", - componentName: "/vector1", - args: { - tailcoords: v_tail, - headcoords: v_head, - }, + await moveVector({ + name: "/vector1", + tailcoords: v_tail, + headcoords: v_head, + core, }); stateVariables = await returnAllStateVariables(core); @@ -741,16 +725,16 @@ describe("Copy tag tests", async () => { await check_items(""); - await updateMathInputValue({ latex: "2", componentName: "/mi", core }); + await updateMathInputValue({ latex: "2", name: "/mi", core }); await check_items("a, b"); - await updateMathInputValue({ latex: "5", componentName: "/mi", core }); + await updateMathInputValue({ latex: "5", name: "/mi", core }); await check_items("a, b, c, d, e"); - await updateMathInputValue({ latex: "1", componentName: "/mi", core }); + await updateMathInputValue({ latex: "1", name: "/mi", core }); await check_items("a"); - await updateMathInputValue({ latex: "6", componentName: "/mi", core }); + await updateMathInputValue({ latex: "6", name: "/mi", core }); await check_items("a, b, c, d, e, f"); } @@ -846,9 +830,9 @@ describe("Copy tag tests", async () => { await check_items(5, 2, 3); // Enter new numbers - await updateMathInputValue({ latex: "9", componentName: "/a", core }); - await updateMathInputValue({ latex: "6", componentName: "/b", core }); - await updateMathInputValue({ latex: "7", componentName: "/c", core }); + await updateMathInputValue({ latex: "9", name: "/a", core }); + await updateMathInputValue({ latex: "6", name: "/b", core }); + await updateMathInputValue({ latex: "7", name: "/c", core }); await check_items(9, 6, 7); }); @@ -993,12 +977,12 @@ describe("Copy tag tests", async () => { await updateBooleanInputValue({ boolean: true, - componentName: "/h1", + name: "/h1", core, }); await updateBooleanInputValue({ boolean: false, - componentName: "/h2", + name: "/h2", core, }); stateVariables = await returnAllStateVariables(core); @@ -1008,12 +992,12 @@ describe("Copy tag tests", async () => { await updateBooleanInputValue({ boolean: false, - componentName: "/h1", + name: "/h1", core, }); await updateBooleanInputValue({ boolean: true, - componentName: "/h2", + name: "/h2", core, }); stateVariables = await returnAllStateVariables(core); @@ -1069,7 +1053,7 @@ describe("Copy tag tests", async () => { await updateBooleanInputValue({ boolean: true, - componentName: "/b", + name: "/b", core, }); stateVariables = await returnAllStateVariables(core); @@ -1078,7 +1062,7 @@ describe("Copy tag tests", async () => { await updateBooleanInputValue({ boolean: false, - componentName: "/b", + name: "/b", core, }); stateVariables = await returnAllStateVariables(core); @@ -1087,7 +1071,7 @@ describe("Copy tag tests", async () => { await updateBooleanInputValue({ boolean: true, - componentName: "/b", + name: "/b", core, }); stateVariables = await returnAllStateVariables(core); @@ -1204,7 +1188,7 @@ describe("Copy tag tests", async () => { // simplify copies await updateTextInputValue({ text: "full", - componentName: "/s2", + name: "/s2", core, }); await check_maths(["*", 2, "x"], ["*", 2, "x"], ["*", 2, "x"]); @@ -1212,18 +1196,13 @@ describe("Copy tag tests", async () => { // stop simplifying original await updateTextInputValue({ text: "none", - componentName: "/s1", + name: "/s1", core, }); await check_maths(["+", "x", "x"], ["*", 2, "x"], ["*", 2, "x"]); // double original - await core.requestAction({ - componentName: "/doubleOriginal", - actionName: "updateValue", - args: {}, - event: null, - }); + await updateValue({ name: "/doubleOriginal", core }); await check_maths( ["*", 2, ["+", "x", "x"]], ["*", 2, "x"], @@ -1231,12 +1210,7 @@ describe("Copy tag tests", async () => { ); // double copy1 - await core.requestAction({ - componentName: "/doubleCopy1", - actionName: "updateValue", - args: {}, - event: null, - }); + await updateValue({ name: "/doubleCopy1", core }); await check_maths( ["*", 2, ["+", "x", "x"]], ["*", 4, "x"], @@ -1244,18 +1218,13 @@ describe("Copy tag tests", async () => { ); // double copy2 - await core.requestAction({ - componentName: "/doubleCopy2", - actionName: "updateValue", - args: {}, - event: null, - }); + await updateValue({ name: "/doubleCopy2", core }); await check_maths(["*", 2, 4, "x"], ["*", 4, "x"], ["*", 8, "x"]); // stop simplifying copies await updateTextInputValue({ text: "none", - componentName: "/s2", + name: "/s2", core, }); await check_maths(["*", 2, 4, "x"], ["*", 2, 2, "x"], ["*", 2, 4, "x"]); @@ -1357,122 +1326,70 @@ describe("Copy tag tests", async () => { // move A A = [-9, -3]; - await core.requestAction({ - actionName: "movePoint", - componentName: "/A", - args: { x: A[0], y: A[1] }, - event: null, - }); + await movePoint({ name: "/A", x: A[0], y: A[1], core }); await check_items({ A, B, A2, l2A, l2B, A3, A4, B4, gA, gB, Ax }); // move B B = [-2, 6]; - await core.requestAction({ - actionName: "movePoint", - componentName: "/B", - args: { x: B[0], y: B[1] }, - event: null, - }); + await movePoint({ name: "/B", x: B[0], y: B[1], core }); await check_items({ A, B, A2, l2A, l2B, A3, A4, B4, gA, gB, Ax }); // move l A = [-7, -6]; B = [8, 0]; - await core.requestAction({ - actionName: "moveLine", - componentName: "/l", - args: { - point1coords: A, - point2coords: B, - }, - }); + await moveLine({ name: "/l", point1coords: A, point2coords: B, core }); await check_items({ A, B, A2, l2A, l2B, A3, A4, B4, gA, gB, Ax }); // move A2 A2 = [5, 4]; - await core.requestAction({ - actionName: "movePoint", - componentName: "/A2", - args: { x: A2[0], y: A2[1] }, - event: null, - }); + await movePoint({ name: "/A2", x: A2[0], y: A2[1], core }); await check_items({ A, B, A2, l2A, l2B, A3, A4, B4, gA, gB, Ax }); // move l2 l2A = [-5, 9]; l2B = [-4, -1]; - await core.requestAction({ - actionName: "moveLine", - componentName: "/l2", - args: { - point1coords: l2A, - point2coords: l2B, - }, + await moveLine({ + name: "/l2", + point1coords: l2A, + point2coords: l2B, + core, }); await check_items({ A, B, A2, l2A, l2B, A3, A4, B4, gA, gB, Ax }); // move A3 A3 = [6, -3]; - await core.requestAction({ - actionName: "movePoint", - componentName: "/A3", - args: { x: A3[0], y: A3[1] }, - event: null, - }); + await movePoint({ name: "/A3", x: A3[0], y: A3[1], core }); await check_items({ A, B, A2, l2A, l2B, A3, A4, B4, gA, gB, Ax }); // move A4 A4 = [-2, 7]; - await core.requestAction({ - actionName: "movePoint", - componentName: "/A4", - args: { x: A4[0], y: A4[1] }, - event: null, - }); + await movePoint({ name: "/A4", x: A4[0], y: A4[1], core }); await check_items({ A, B, A2, l2A, l2B, A3, A4, B4, gA, gB, Ax }); // move B4 B4 = [-9, -8]; - await core.requestAction({ - actionName: "movePoint", - componentName: "/B4", - args: { x: B4[0], y: B4[1] }, - event: null, - }); + await movePoint({ name: "/B4", x: B4[0], y: B4[1], core }); await check_items({ A, B, A2, l2A, l2B, A3, A4, B4, gA, gB, Ax }); // move A5 gA = [-10, -9]; - await core.requestAction({ - actionName: "movePoint", - componentName: "/gnolink/A", - args: { x: gA[0], y: gA[1] }, - event: null, - }); + await movePoint({ name: "/gnolink/A", x: gA[0], y: gA[1], core }); await check_items({ A, B, A2, l2A, l2B, A3, A4, B4, gA, gB, Ax }); // move B5 gB = [-8, -7]; - await core.requestAction({ - actionName: "movePoint", - componentName: "/gnolink/B", - args: { x: gB[0], y: gB[1] }, - event: null, - }); + await movePoint({ name: "/gnolink/B", x: gB[0], y: gB[1], core }); await check_items({ A, B, A2, l2A, l2B, A3, A4, B4, gA, gB, Ax }); // move l3 gA = [6, 5]; gB = [4, -3]; - await core.requestAction({ - actionName: "moveLine", - componentName: "/gnolink/l", - args: { - point1coords: gA, - point2coords: gB, - }, - event: null, + await moveLine({ + name: "/gnolink/l", + point1coords: gA, + point2coords: gB, + core, }); await check_items({ A, B, A2, l2A, l2B, A3, A4, B4, gA, gB, Ax }); } @@ -1767,7 +1684,7 @@ describe("Copy tag tests", async () => { simplify1 = "none"; await updateTextInputValue({ text: simplify1, - componentName: "/sim", + name: "/sim", core, }); @@ -1777,7 +1694,7 @@ describe("Copy tag tests", async () => { simplify2 = "none"; await updateTextInputValue({ text: simplify1, - componentName: "/g2/sim", + name: "/g2/sim", core, }); await check_items(simplify1, simplify2, simplify3); @@ -1786,7 +1703,7 @@ describe("Copy tag tests", async () => { simplify3 = "none"; await updateTextInputValue({ text: simplify1, - componentName: "/g3/sim", + name: "/g3/sim", core, }); await check_items(simplify1, simplify2, simplify3); @@ -1868,7 +1785,7 @@ describe("Copy tag tests", async () => { await updateTextInputValue({ text: "hi", - componentName: "/external", + name: "/external", core, }); @@ -1929,12 +1846,12 @@ describe("Copy tag tests", async () => { await updateTextInputValue({ text: "one", - componentName: "/g/ti", + name: "/g/ti", core, }); await updateTextInputValue({ text: "two", - componentName: "/g2/ti", + name: "/g2/ti", core, }); @@ -2212,36 +2129,16 @@ describe("Copy tag tests", async () => { await check_items(2, 2, 2, 2); - await core.requestAction({ - componentName: "/section1/addP", - actionName: "updateValue", - args: {}, - event: null, - }); + await updateValue({ name: "/section1/addP", core }); await check_items(3, 2, 2, 2); - await core.requestAction({ - componentName: "/section7/removeP", - actionName: "updateValue", - args: {}, - event: null, - }); + await updateValue({ name: "/section7/removeP", core }); await check_items(2, 2, 2, 2); - await core.requestAction({ - componentName: "/section4/addP", - actionName: "updateValue", - args: {}, - event: null, - }); + await updateValue({ name: "/section4/addP", core }); await check_items(2, 2, 2, 3); - await core.requestAction({ - componentName: "/section4/removeP", - actionName: "updateValue", - args: {}, - event: null, - }); + await updateValue({ name: "/section4/removeP", core }); await check_items(2, 2, 2, 2); } @@ -2423,59 +2320,29 @@ describe("Copy tag tests", async () => { await check_items(); - await updateMathInputValue({ latex: "1", componentName: "/n", core }); + await updateMathInputValue({ latex: "1", name: "/n", core }); await check_items([1, 2], [3, 4], [5, 6]); - await core.requestAction({ - actionName: "movePoint", - componentName: "/A", - args: { x: 9, y: 0 }, - event: null, - }); - await core.requestAction({ - actionName: "movePoint", - componentName: "/B", - args: { x: 1, y: 8 }, - event: null, - }); - await core.requestAction({ - actionName: "movePoint", - componentName: "/C", - args: { x: 7, y: 2 }, - event: null, - }); + await movePoint({ name: "/A", x: 9, y: 0, core }); + await movePoint({ name: "/B", x: 1, y: 8, core }); + await movePoint({ name: "/C", x: 7, y: 2, core }); await check_items([9, 0], [1, 8], [7, 2]); - await updateMathInputValue({ latex: "2", componentName: "/n", core }); + await updateMathInputValue({ latex: "2", name: "/n", core }); await check_items([9, 0], [1, 8], [7, 2], [2, 3], [4, 5], [6, 7]); - await core.requestAction({ - actionName: "movePoint", - componentName: "/D", - args: { x: 0, y: 10 }, - event: null, - }); - await core.requestAction({ - actionName: "movePoint", - componentName: "/E", - args: { x: 9, y: 1 }, - event: null, - }); - await core.requestAction({ - actionName: "movePoint", - componentName: "/F", - args: { x: 2, y: 8 }, - event: null, - }); + await movePoint({ name: "/D", x: 0, y: 10, core }); + await movePoint({ name: "/E", x: 9, y: 1, core }); + await movePoint({ name: "/F", x: 2, y: 8, core }); await check_items([9, 0], [1, 8], [7, 2], [0, 10], [9, 1], [2, 8]); - await updateMathInputValue({ latex: "0", componentName: "/n", core }); + await updateMathInputValue({ latex: "0", name: "/n", core }); await check_items(); - await updateMathInputValue({ latex: "2", componentName: "/n", core }); + await updateMathInputValue({ latex: "2", name: "/n", core }); await check_items([9, 0], [1, 8], [7, 2], [0, 10], [9, 1], [2, 8]); }); @@ -2498,14 +2365,14 @@ describe("Copy tag tests", async () => { expect(stateVariables["/xval"].stateValues.value.tree).eq("x"); expect(stateVariables["/xvalnl"].stateValues.value.tree).eq("x"); - await updateMathInputValue({ latex: "y", componentName: "/mi1", core }); + await updateMathInputValue({ latex: "y", name: "/mi1", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/x"].stateValues.value.tree).eq("y"); expect(stateVariables["/xval"].stateValues.value.tree).eq("y"); expect(stateVariables["/xvalnl"].stateValues.value.tree).eq("x"); - await updateMathInputValue({ latex: "z", componentName: "/mi2", core }); + await updateMathInputValue({ latex: "z", name: "/mi2", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/x"].stateValues.value.tree).eq("y"); @@ -2570,42 +2437,22 @@ describe("Copy tag tests", async () => { // Move v1 v1 = [2, 3]; - await core.requestAction({ - actionName: "movePoint", - componentName: "/v1", - args: { x: v1[0], y: v1[1] }, - event: null, - }); + await movePoint({ name: "/v1", x: v1[0], y: v1[1], core }); await check_items({ v1, v1nl, v2nl, v3nl }); // Move v1nl v1nl = [3, 4]; - await core.requestAction({ - actionName: "movePoint", - componentName: "/v1nl", - args: { x: v1nl[0], y: v1nl[1] }, - event: null, - }); + await movePoint({ name: "/v1nl", x: v1nl[0], y: v1nl[1], core }); await check_items({ v1, v1nl, v2nl, v3nl }); // Move v2nl v2nl = [4, 5]; - await core.requestAction({ - actionName: "movePoint", - componentName: "/v2nl", - args: { x: v2nl[0], y: v2nl[1] }, - event: null, - }); + await movePoint({ name: "/v2nl", x: v2nl[0], y: v2nl[1], core }); await check_items({ v1, v1nl, v2nl, v3nl }); // Move v3nl v3nl = [5, 6]; - await core.requestAction({ - actionName: "movePoint", - componentName: "/v3nl", - args: { x: v3nl[0], y: v3nl[1] }, - event: null, - }); + await movePoint({ name: "/v3nl", x: v3nl[0], y: v3nl[1], core }); await check_items({ v1, v1nl, v2nl, v3nl }); }); @@ -2646,50 +2493,40 @@ describe("Copy tag tests", async () => { // Add point - await updateMathInputValue({ latex: "1", componentName: "/n", core }); + await updateMathInputValue({ latex: "1", name: "/n", core }); await check_items(`(${A1.join(",")})`, ""); // Move point A1 = [-3, 7]; - await core.requestAction({ - actionName: "movePoint", - componentName: "/t1/A", - args: { x: A1[0], y: A1[1] }, - event: null, - }); + await movePoint({ name: "/t1/A", x: A1[0], y: A1[1], core }); await check_items(`(${A1.join(",")})`, ""); // Remove point - await updateMathInputValue({ latex: "0", componentName: "/n", core }); + await updateMathInputValue({ latex: "0", name: "/n", core }); await check_items("", ""); // Remember coordinates when restore point since copy was maintained - await updateMathInputValue({ latex: "1", componentName: "/n", core }); + await updateMathInputValue({ latex: "1", name: "/n", core }); await check_items(`(${A1.join(",")})`, ""); // Add second point - await updateMathInputValue({ latex: "2", componentName: "/n", core }); + await updateMathInputValue({ latex: "2", name: "/n", core }); await check_items(`(${A1.join(",")})`, `(${A2.join(",")})`); // Move second point A2 = [5, -4]; - await core.requestAction({ - actionName: "movePoint", - componentName: "/t2/A", - args: { x: A2[0], y: A2[1] }, - event: null, - }); + await movePoint({ name: "/t2/A", x: A2[0], y: A2[1], core }); await check_items(`(${A1.join(",")})`, `(${A2.join(",")})`); // Remove both points - await updateMathInputValue({ latex: "0", componentName: "/n", core }); + await updateMathInputValue({ latex: "0", name: "/n", core }); await check_items("", ""); // Remember coordinates of both points - await updateMathInputValue({ latex: "2", componentName: "/n", core }); + await updateMathInputValue({ latex: "2", name: "/n", core }); await check_items(`(${A1.join(",")})`, `(${A2.join(",")})`); }); @@ -3105,19 +2942,19 @@ describe("Copy tag tests", async () => { await test_group("/thegrp", 1, 2, 3); // Change index for thegrp - await updateMathInputValue({ latex: "2", componentName: "/n", core }); + await updateMathInputValue({ latex: "2", name: "/n", core }); await test_group("/thegrp", 4, 5, 6); // Change to invalid index for thegrp - await updateMathInputValue({ latex: "3", componentName: "/n", core }); + await updateMathInputValue({ latex: "3", name: "/n", core }); let stateVariable = await returnAllStateVariables(core); expect(stateVariable["/thegrp"]).eq(undefined); // Change back to index 1 for thegrp - await updateMathInputValue({ latex: "1", componentName: "/n", core }); + await updateMathInputValue({ latex: "1", name: "/n", core }); await test_group("/thegrp", 1, 2, 3); }); @@ -3229,7 +3066,10 @@ describe("Copy tag tests", async () => { ); }); - async function test_copy_component_index(core: any, force_values: boolean) { + async function test_copy_component_index( + core: Core, + force_values: boolean, + ) { async function check_items({ x1, y1, @@ -3288,37 +3128,27 @@ describe("Copy tag tests", async () => { // restrict collection to first component - await updateMathInputValue({ latex: "1", componentName: "/n", core }); + await updateMathInputValue({ latex: "1", name: "/n", core }); await check_items({ x1, y1, x2, y2, comp: 1 }); // move copied point x1 = 9; y1 = -5; - await core.requestAction({ - actionName: "movePoint", - componentName: "/A2", - args: { x: x1, y: y1 }, - event: null, - }); + await movePoint({ name: "/A2", x: x1, y: y1, core }); await check_items({ x1, y1, x2, y2, comp: 1 }); // restrict collection to second component - await updateMathInputValue({ latex: "2", componentName: "/n", core }); + await updateMathInputValue({ latex: "2", name: "/n", core }); await check_items({ x1, y1, x2, y2, comp: 2 }); // move double copied point x2 = 0; y2 = 8; - await core.requestAction({ - actionName: "movePoint", - componentName: "/g3/A2", - args: { x: x2, y: y2 }, - event: null, - }); + await movePoint({ name: "/g3/A2", x: x2, y: y2, core }); await check_items({ x1, y1, x2, y2, comp: 2 }); } @@ -3384,7 +3214,7 @@ describe("Copy tag tests", async () => { }); async function test_copy_prop_component_index( - core: any, + core: Core, force_values: boolean, ) { async function check_items({ @@ -3458,7 +3288,7 @@ describe("Copy tag tests", async () => { propIndex = 1; await updateMathInputValue({ latex: "1", - componentName: "/n", + name: "/n", core, }); @@ -3467,12 +3297,7 @@ describe("Copy tag tests", async () => { // move point 1 x1 = 9; y1 = -5; - await core.requestAction({ - actionName: "movePoint", - componentName: "/A", - args: { x: x1, y: y1 }, - event: null, - }); + await movePoint({ name: "/A", x: x1, y: y1, core }); await check_items({ x1, y1, x2, y2, propIndex, componentIndex }); @@ -3480,7 +3305,7 @@ describe("Copy tag tests", async () => { componentIndex = 2; await updateMathInputValue({ latex: "2", - componentName: "/m", + name: "/m", core, }); @@ -3489,12 +3314,7 @@ describe("Copy tag tests", async () => { // move point2 x2 = 0; y2 = 8; - await core.requestAction({ - actionName: "movePoint", - componentName: "/B", - args: { x: x2, y: y2 }, - event: null, - }); + await movePoint({ name: "/B", x: x2, y: y2, core }); await check_items({ x1, y1, x2, y2, propIndex, componentIndex }); @@ -3502,7 +3322,7 @@ describe("Copy tag tests", async () => { propIndex = 2; await updateMathInputValue({ latex: "2", - componentName: "/n", + name: "/n", core, }); await check_items({ x1, y1, x2, y2, propIndex, componentIndex }); @@ -3511,7 +3331,7 @@ describe("Copy tag tests", async () => { componentIndex = 1; await updateMathInputValue({ latex: "1", - componentName: "/m", + name: "/m", core, }); await check_items({ x1, y1, x2, y2, propIndex, componentIndex }); @@ -3520,7 +3340,7 @@ describe("Copy tag tests", async () => { propIndex = 3; await updateMathInputValue({ latex: "3", - componentName: "/n", + name: "/n", core, }); await check_items({ x1, y1, x2, y2, propIndex, componentIndex }); @@ -3529,7 +3349,7 @@ describe("Copy tag tests", async () => { propIndex = 1; await updateMathInputValue({ latex: "1", - componentName: "/n", + name: "/n", core, }); await check_items({ x1, y1, x2, y2, propIndex, componentIndex }); @@ -3538,7 +3358,7 @@ describe("Copy tag tests", async () => { componentIndex = 3; await updateMathInputValue({ latex: "3", - componentName: "/m", + name: "/m", core, }); await check_items({ x1, y1, x2, y2, propIndex, componentIndex }); @@ -3547,7 +3367,7 @@ describe("Copy tag tests", async () => { componentIndex = 2; await updateMathInputValue({ latex: "2", - componentName: "/m", + name: "/m", core, }); @@ -3555,7 +3375,7 @@ describe("Copy tag tests", async () => { propIndex = undefined; await updateMathInputValue({ latex: "", - componentName: "/n", + name: "/n", core, }); await check_items({ x1, y1, x2, y2, propIndex, componentIndex }); @@ -3789,12 +3609,7 @@ describe("Copy tag tests", async () => { P1 = [4, 5]; - await core.requestAction({ - actionName: "movePoint", - componentName: "/P", - args: { x: P1[0], y: P1[1] }, - event: null, - }); + await movePoint({ name: "/P", x: P1[0], y: P1[1], core }); stateVariables = await returnAllStateVariables(core); @@ -3886,12 +3701,7 @@ describe("Copy tag tests", async () => { // move P2 to (7,0) P2 = [7, 0]; - await core.requestAction({ - actionName: "movePoint", - componentName: "/g2/P", - args: { x: P2[0], y: P2[1] }, - event: null, - }); + await movePoint({ name: "/g2/P", x: P2[0], y: P2[1], core }); stateVariables = await returnAllStateVariables(core); @@ -3984,12 +3794,7 @@ describe("Copy tag tests", async () => { // move P1 via Pa to (2,9) P1 = [2, 0]; - await core.requestAction({ - actionName: "movePoint", - componentName: "/Pa", - args: { x: P1[0], y: P1[1] }, - event: null, - }); + await movePoint({ name: "/Pa", x: P1[0], y: P1[1], core }); stateVariables = await returnAllStateVariables(core); @@ -4081,12 +3886,7 @@ describe("Copy tag tests", async () => { // move P2 via graph 4's Pa to (8, 6) P2 = [8, 6]; - await core.requestAction({ - actionName: "movePoint", - componentName: "/g4/Pa", - args: { x: P2[0], y: P2[1] }, - event: null, - }); + await movePoint({ name: "/g4/Pa", x: P2[0], y: P2[1], core }); stateVariables = await returnAllStateVariables(core); @@ -4326,12 +4126,7 @@ describe("Copy tag tests", async () => { P1 = [4, 5]; - await core.requestAction({ - actionName: "movePoint", - componentName: "/P", - args: { x: P1[0], y: P1[1] }, - event: null, - }); + await movePoint({ name: "/P", x: P1[0], y: P1[1], core }); stateVariables = await returnAllStateVariables(core); @@ -4424,12 +4219,7 @@ describe("Copy tag tests", async () => { // move P2 to (7,0) P2 = [7, 0]; - await core.requestAction({ - actionName: "movePoint", - componentName: "/g2/P", - args: { x: P2[0], y: P2[1] }, - event: null, - }); + await movePoint({ name: "/g2/P", x: P2[0], y: P2[1], core }); stateVariables = await returnAllStateVariables(core); @@ -4523,12 +4313,7 @@ describe("Copy tag tests", async () => { P1 = [2, 0]; - await core.requestAction({ - actionName: "movePoint", - componentName: "/Pa", - args: { x: P1[0], y: P1[1] }, - event: null, - }); + await movePoint({ name: "/Pa", x: P1[0], y: P1[1], core }); stateVariables = await returnAllStateVariables(core); @@ -4621,12 +4406,7 @@ describe("Copy tag tests", async () => { // move P2 via graph 4's Pa to (8, 6) P2 = [8, 6]; - await core.requestAction({ - actionName: "movePoint", - componentName: "/g4/Pa", - args: { x: P2[0], y: P2[1] }, - event: null, - }); + await movePoint({ name: "/g4/Pa", x: P2[0], y: P2[1], core }); stateVariables = await returnAllStateVariables(core); @@ -4758,15 +4538,10 @@ describe("Copy tag tests", async () => { // answer outside answer await updateMathInputValue({ latex: "x", - componentName: mathinputoutsideName, + name: mathinputoutsideName, core, }); - await core.requestAction({ - componentName: "/answer1", - actionName: "submitAnswer", - args: {}, - event: null, - }); + await submitAnswer({ name: "/answer1", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/answer1"].stateValues.justSubmitted).eq(true); @@ -4785,15 +4560,10 @@ describe("Copy tag tests", async () => { // correctly answer first problem await updateMathInputValue({ latex: "y", - componentName: mathinputp1Name, + name: mathinputp1Name, core, }); - await core.requestAction({ - componentName: "/answer2", - actionName: "submitAnswer", - args: {}, - event: null, - }); + await submitAnswer({ name: "/answer2", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/answer1"].stateValues.justSubmitted).eq(true); @@ -4842,15 +4612,10 @@ describe("Copy tag tests", async () => { // correctly answer second problem await updateMathInputValue({ latex: "z", - componentName: mathinputp2Name, + name: mathinputp2Name, core, }); - await core.requestAction({ - componentName: "/p2/answer1", - actionName: "submitAnswer", - args: {}, - event: null, - }); + await submitAnswer({ name: "/p2/answer1", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/answer1"].stateValues.justSubmitted).eq(true); @@ -4901,15 +4666,10 @@ describe("Copy tag tests", async () => { // incorrectly answer third problem await updateMathInputValue({ latex: "a", - componentName: mathinputp3Name, + name: mathinputp3Name, core, }); - await core.requestAction({ - componentName: "/p3/answer2", - actionName: "submitAnswer", - args: {}, - event: null, - }); + await submitAnswer({ name: "/p3/answer2", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/answer1"].stateValues.justSubmitted).eq(true); @@ -4960,15 +4720,10 @@ describe("Copy tag tests", async () => { // incorrectly answer fourth problem await updateMathInputValue({ latex: "b", - componentName: mathinputp4Name, + name: mathinputp4Name, core, }); - await core.requestAction({ - componentName: "/p4/answer1", - actionName: "submitAnswer", - args: {}, - event: null, - }); + await submitAnswer({ name: "/p4/answer1", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/answer1"].stateValues.justSubmitted).eq(true); @@ -5020,7 +4775,7 @@ describe("Copy tag tests", async () => { await updateMathInputValue({ latex: "q", - componentName: "/mi", + name: "/mi", core, }); stateVariables = await returnAllStateVariables(core); @@ -5167,7 +4922,7 @@ describe("Copy tag tests", async () => { // type x in first mathinput await updateMathInputValue({ latex: "x", - componentName: "/p1/x", + name: "/p1/x", core, }); @@ -5176,7 +4931,7 @@ describe("Copy tag tests", async () => { // type y in second mathinput await updateMathInputValue({ latex: "y", - componentName: "/p2b/x", + name: "/p2b/x", core, }); @@ -5185,7 +4940,7 @@ describe("Copy tag tests", async () => { // increase n await updateMathInputValue({ latex: "3", - componentName: "/n", + name: "/n", core, }); @@ -5194,7 +4949,7 @@ describe("Copy tag tests", async () => { // type z in third mathinput await updateMathInputValue({ latex: "z", - componentName: "/p3a/x", + name: "/p3a/x", core, }); @@ -5327,7 +5082,7 @@ describe("Copy tag tests", async () => { ]); // enter a - await updateMathInputValue({ latex: "a", componentName: "/mi", core }); + await updateMathInputValue({ latex: "a", name: "/mi", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/m1"].stateValues.value.tree).eq("a"); @@ -5382,18 +5137,8 @@ describe("Copy tag tests", async () => { // move points - await core.requestAction({ - actionName: "movePoint", - componentName: "/P", - args: { x: 3, y: 5 }, - event: null, - }); - await core.requestAction({ - actionName: "movePoint", - componentName: "/g2/P", - args: { x: 7, y: 6 }, - event: null, - }); + await movePoint({ name: "/P", x: 3, y: 5, core }); + await movePoint({ name: "/g2/P", x: 7, y: 6, core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/P"].stateValues.xs.map((x) => x.tree)).eqls([ @@ -5611,62 +5356,14 @@ describe("Copy tag tests", async () => { // move points - await core.requestAction({ - event: null, - actionName: "movePoint", - componentName: "/P1", - args: { x: 3, y: 5 }, - }); - await core.requestAction({ - event: null, - actionName: "moveVector", - componentName: "/v1", - args: { - headcoords: [8, 7], - }, - }); - await core.requestAction({ - event: null, - actionName: "movePoint", - componentName: "/P2", - args: { x: 6, y: 0 }, - }); - await core.requestAction({ - event: null, - actionName: "moveVector", - componentName: "/g2a/v2", - args: { - headcoords: [9, 1], - }, - }); - await core.requestAction({ - event: null, - actionName: "movePoint", - componentName: "/g3/P3", - args: { x: 5, y: 8 }, - }); - await core.requestAction({ - event: null, - actionName: "moveVector", - componentName: "/g3a/v3", - args: { - headcoords: [8, 6], - }, - }); - await core.requestAction({ - event: null, - actionName: "movePoint", - componentName: "/g4/P4", - args: { x: 0, y: 3 }, - }); - await core.requestAction({ - event: null, - actionName: "moveVector", - componentName: "/v4", - args: { - headcoords: [7, 2], - }, - }); + await movePoint({ name: "/P1", x: 3, y: 5, core }); + await moveVector({ name: "/v1", headcoords: [8, 7], core }); + await movePoint({ name: "/P2", x: 6, y: 0, core }); + await moveVector({ name: "/g2a/v2", headcoords: [9, 1], core }); + await movePoint({ name: "/g3/P3", x: 5, y: 8, core }); + await moveVector({ name: "/g3a/v3", headcoords: [8, 6], core }); + await movePoint({ name: "/g4/P4", x: 0, y: 3, core }); + await moveVector({ name: "/v4", headcoords: [7, 2], core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/P1"].stateValues.xs.map((x) => x.tree)).eqls([ @@ -5770,30 +5467,10 @@ describe("Copy tag tests", async () => { // move shadowed points - await core.requestAction({ - event: null, - actionName: "movePoint", - componentName: P1aName, - args: { x: 2, y: 1 }, - }); - await core.requestAction({ - event: null, - actionName: "movePoint", - componentName: "/g2a/P2", - args: { x: 5, y: 4 }, - }); - await core.requestAction({ - event: null, - actionName: "movePoint", - componentName: "/g3a/P3", - args: { x: 9, y: 7 }, - }); - await core.requestAction({ - event: null, - actionName: "movePoint", - componentName: "/g4a/P4", - args: { x: 7, y: 6 }, - }); + await movePoint({ name: P1aName, x: 2, y: 1, core }); + await movePoint({ name: "/g2a/P2", x: 5, y: 4, core }); + await movePoint({ name: "/g3a/P3", x: 9, y: 7, core }); + await movePoint({ name: "/g4a/P4", x: 7, y: 6, core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/P1"].stateValues.xs.map((x) => x.tree)).eqls([ @@ -6539,30 +6216,17 @@ describe("Copy tag tests", async () => { // move objects P = [3, 5]; - await core.requestAction({ - event: null, - actionName: "movePoint", - componentName: "/g/P", - args: { x: P[0], y: P[1] }, - }); + await movePoint({ name: "/g/P", x: P[0], y: P[1], core }); v = [8, 7]; vH = [5, 1]; - await core.requestAction({ - event: null, - actionName: "moveVector", - componentName: "/v", - args: { - headcoords: vH, - tailcoords: [vH[0] - v[0], vH[1] - v[1]], - }, + await moveVector({ + name: "/v", + headcoords: vH, + tailcoords: [vH[0] - v[0], vH[1] - v[1]], + core, }); c0 = [6, 0]; - await core.requestAction({ - event: null, - actionName: "moveCircle", - componentName: "/g5/c", - args: { center: c0 }, - }); + await moveCircle({ name: "/g5/c", cx: c0[0], cy: c0[1], core }); await check_items({ P, v, vH, c0 }); }); @@ -6624,48 +6288,28 @@ describe("Copy tag tests", async () => { P = [10, 9]; Q = [8, 4]; - await core.requestAction({ - event: null, - actionName: "movePoint", - componentName: "/g1/P", - args: { x: P[0], y: P[1] }, - }); - await core.requestAction({ - event: null, - actionName: "movePoint", - componentName: "/g2/Q", - args: { x: Q[0], y: Q[1] }, - }); + await movePoint({ name: "/g1/P", x: P[0], y: P[1], core }); + await movePoint({ name: "/g2/Q", x: Q[0], y: Q[1], core }); await check_items(P, Q); // switch to second option from conditional content P = [3, 4]; Q = [7, 8]; - await updateMathInputValue({ latex: "2", componentName: "/n", core }); + await updateMathInputValue({ latex: "2", name: "/n", core }); await check_items(P, Q); // move new points P = [6, 1]; Q = [9, 3]; - await core.requestAction({ - event: null, - actionName: "movePoint", - componentName: "/g1/P", - args: { x: P[0], y: P[1] }, - }); - await core.requestAction({ - event: null, - actionName: "movePoint", - componentName: "/g2/Q", - args: { x: Q[0], y: Q[1] }, - }); + await movePoint({ name: "/g1/P", x: P[0], y: P[1], core }); + await movePoint({ name: "/g2/Q", x: Q[0], y: Q[1], core }); await check_items(P, Q); // switch back to first option from conditional content P = [5, 6]; Q = [7, 8]; - await updateMathInputValue({ latex: "0", componentName: "/n", core }); + await updateMathInputValue({ latex: "0", name: "/n", core }); }); async function test_assign_names_group_map(core) { @@ -7012,15 +6656,15 @@ describe("Copy tag tests", async () => { await check_items(1); // change to vertex 2 - await updateMathInputValue({ latex: "2", componentName: "/ind", core }); + await updateMathInputValue({ latex: "2", name: "/ind", core }); await check_items(2); // invalid vertex - await updateMathInputValue({ latex: "", componentName: "/ind", core }); + await updateMathInputValue({ latex: "", name: "/ind", core }); await check_items(0); // change to vertex 3 - await updateMathInputValue({ latex: "3", componentName: "/ind", core }); + await updateMathInputValue({ latex: "3", name: "/ind", core }); await check_items(3); }); @@ -7280,14 +6924,11 @@ describe("Copy tag tests", async () => { // move points - await core.requestAction({ - event: null, - actionName: "moveLine", - componentName: "/l", - args: { - point1coords: [7, 8], - point2coords: [9, 0], - }, + await moveLine({ + name: "/l", + point1coords: [7, 8], + point2coords: [9, 0], + core, }); await check_items({ @@ -7495,14 +7136,11 @@ describe("Copy tag tests", async () => { expect(stateVariables["/p1"].stateValues.text).eq("2.93521"); expect(stateVariables["/p2"].stateValues.text).eq("2.93521"); - await core.requestAction({ - event: null, - actionName: "moveLine", - componentName: "/l", - args: { - point1coords: [1.38527302734, 8.48273402357], - point2coords: [5.9060742037, 2.93520806203104], - }, + await moveLine({ + name: "/l", + point1coords: [1.38527302734, 8.48273402357], + point2coords: [5.9060742037, 2.93520806203104], + core, }); stateVariables = await returnAllStateVariables(core); @@ -7510,14 +7148,11 @@ describe("Copy tag tests", async () => { expect(stateVariables["/p1"].stateValues.text).eq("8.48273"); expect(stateVariables["/p2"].stateValues.text).eq("8.48273"); - await core.requestAction({ - event: null, - actionName: "moveLine", - componentName: "/l", - args: { - point1coords: [1.38527302734, 8.48273402357], - point2coords: [4.482081034234, 7.34828203481], - }, + await moveLine({ + name: "/l", + point1coords: [1.38527302734, 8.48273402357], + point2coords: [4.482081034234, 7.34828203481], + core, }); stateVariables = await returnAllStateVariables(core); @@ -7722,14 +7357,11 @@ describe("Copy tag tests", async () => { }); // move points - await core.requestAction({ - event: null, - actionName: "moveLine", - componentName: "/l", - args: { - point1coords: [7, 8], - point2coords: [9, 0], - }, + await moveLine({ + name: "/l", + point1coords: [7, 8], + point2coords: [9, 0], + core, }); await check_items({ @@ -7804,22 +7436,22 @@ describe("Copy tag tests", async () => { await check_items({ ln: 1, pn: 1, cn: 1 }); - await updateMathInputValue({ latex: "", componentName: "/ln", core }); + await updateMathInputValue({ latex: "", name: "/ln", core }); await check_items({ ln: 0, pn: 1, cn: 1 }); - await updateMathInputValue({ latex: "2", componentName: "/ln", core }); + await updateMathInputValue({ latex: "2", name: "/ln", core }); await check_items({ ln: 2, pn: 1, cn: 1 }); - await updateMathInputValue({ latex: "0", componentName: "/pn", core }); + await updateMathInputValue({ latex: "0", name: "/pn", core }); await check_items({ ln: 2, pn: 0, cn: 1 }); - await updateMathInputValue({ latex: "2", componentName: "/pn", core }); + await updateMathInputValue({ latex: "2", name: "/pn", core }); await check_items({ ln: 2, pn: 2, cn: 1 }); - await updateMathInputValue({ latex: "", componentName: "/cn", core }); + await updateMathInputValue({ latex: "", name: "/cn", core }); await check_items({ ln: 2, pn: 2, cn: 0 }); - await updateMathInputValue({ latex: "2", componentName: "/cn", core }); + await updateMathInputValue({ latex: "2", name: "/cn", core }); await check_items({ ln: 2, pn: 2, cn: 2 }); }); @@ -7946,34 +7578,34 @@ describe("Copy tag tests", async () => { await check_items(2, 1, 1, 1); - await updateMathInputValue({ latex: "2", componentName: "/tn", core }); + await updateMathInputValue({ latex: "2", name: "/tn", core }); await check_items(2, 2, 1, 1); - await updateMathInputValue({ latex: "3", componentName: "/tn", core }); + await updateMathInputValue({ latex: "3", name: "/tn", core }); await check_items(2, 3, 1, 1); - await updateMathInputValue({ latex: "4", componentName: "/n", core }); + await updateMathInputValue({ latex: "4", name: "/n", core }); await check_items(4, 3, 1, 1); - await updateMathInputValue({ latex: "3", componentName: "/pn", core }); + await updateMathInputValue({ latex: "3", name: "/pn", core }); await check_items(4, 3, 3, 1); - await updateMathInputValue({ latex: "2", componentName: "/pn", core }); + await updateMathInputValue({ latex: "2", name: "/pn", core }); await check_items(4, 3, 2, 1); - await updateMathInputValue({ latex: "3", componentName: "/cn", core }); + await updateMathInputValue({ latex: "3", name: "/cn", core }); await check_items(4, 3, 2, 3); - await updateMathInputValue({ latex: "2", componentName: "/cn", core }); + await updateMathInputValue({ latex: "2", name: "/cn", core }); await check_items(4, 3, 2, 2); - await updateMathInputValue({ latex: "3", componentName: "/n", core }); + await updateMathInputValue({ latex: "3", name: "/n", core }); await check_items(3, 3, 2, 2); - await updateMathInputValue({ latex: "1", componentName: "/n", core }); + await updateMathInputValue({ latex: "1", name: "/n", core }); await check_items(1, 3, 2, 2); - await updateMathInputValue({ latex: "1", componentName: "/tn", core }); + await updateMathInputValue({ latex: "1", name: "/tn", core }); await check_items(1, 1, 2, 2); }); @@ -8404,7 +8036,7 @@ describe("Copy tag tests", async () => { // mathinputs change with immediate value await updateMathInputImmediateValue({ latex: "x", - componentName: "/mi", + name: "/mi", core, }); stateVariables = await returnAllStateVariables(core); @@ -8417,7 +8049,7 @@ describe("Copy tag tests", async () => { // maths change with value await updateMathInputValueToImmediateValue({ - componentName: "/mi", + name: "/mi", core, }); stateVariables = await returnAllStateVariables(core); @@ -8429,7 +8061,7 @@ describe("Copy tag tests", async () => { // mathinputs change with immediate value await updateMathInputImmediateValue({ latex: "y", - componentName: "/mi2", + name: "/mi2", core, }); stateVariables = await returnAllStateVariables(core); @@ -8442,7 +8074,7 @@ describe("Copy tag tests", async () => { // maths change with value await updateMathInputValueToImmediateValue({ - componentName: "/mi2", + name: "/mi2", core, }); stateVariables = await returnAllStateVariables(core); @@ -8454,7 +8086,7 @@ describe("Copy tag tests", async () => { // mathinputs change with immediate value await updateMathInputImmediateValue({ latex: "z", - componentName: mi3Name, + name: mi3Name, core, }); stateVariables = await returnAllStateVariables(core); @@ -8467,7 +8099,7 @@ describe("Copy tag tests", async () => { // maths change with value await updateMathInputValueToImmediateValue({ - componentName: mi3Name, + name: mi3Name, core, }); stateVariables = await returnAllStateVariables(core); @@ -8587,27 +8219,17 @@ describe("Copy tag tests", async () => { expect(stateVariables["/num"].stateValues.value).eqls(NaN); - await updateMathInputValue({ latex: "4", componentName: tiName, core }); - await core.requestAction({ - componentName: "/ans", - actionName: "submitAnswer", - args: {}, - event: null, - }); + await updateMathInputValue({ latex: "4", name: tiName, core }); + await submitAnswer({ name: "/ans", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/num"].stateValues.value).eq(4); await updateMathInputValue({ latex: "47", - componentName: tiName, + name: tiName, core, }); - await core.requestAction({ - componentName: "/ans", - actionName: "submitAnswer", - args: {}, - event: null, - }); + await submitAnswer({ name: "/ans", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/num"].stateValues.value).eq(47); }); diff --git a/packages/doenetml-worker/src/test/tagSpecific/document.test.ts b/packages/doenetml-worker/src/test/tagSpecific/document.test.ts new file mode 100644 index 000000000..77ea11cc9 --- /dev/null +++ b/packages/doenetml-worker/src/test/tagSpecific/document.test.ts @@ -0,0 +1,207 @@ +import { describe, expect, it, vi } from "vitest"; +import { createTestCore, returnAllStateVariables } from "../utils/test-core"; +import { submitAnswer, updateMathInputValue } from "../utils/actions"; + +const Mock = vi.fn(); +vi.stubGlobal("postMessage", Mock); +vi.mock("hyperformula"); + +describe("Document tag tests", async () => { + it("get 1 for document credit with nothing", async () => { + let core = await createTestCore({ + doenetML: ` + $_document1.creditAchieved{assignNames="docCa"} + + `, + }); + + const stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/docCa"].stateValues.value).eq(1); + }); + + it("document credit when have problem with nothing", async () => { + let core = await createTestCore({ + doenetML: ` + $_document1.creditAchieved{assignNames="docCa"} +

x

+ + Problem with nothing + + + `, + }); + + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/docCa"].stateValues.value).eq(0.5); + + let mathInputName = + stateVariables["/ans"].stateValues.inputChildren[0].componentName; + + await updateMathInputValue({ + latex: "x", + name: mathInputName, + core, + }); + await submitAnswer({ name: "/ans", core }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/docCa"].stateValues.value).eq(1); + }); + + it("get document credit even when have composites as a siblings", async () => { + let core = await createTestCore({ + doenetML: ` + $_document1.creditAchieved{assignNames="docCa"} + + x + + + $m1 + +

$m2

+ + `, + }); + + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/docCa"].stateValues.value).eq(0); + + let mathInputName = + stateVariables["/ans"].stateValues.inputChildren[0].componentName; + + await updateMathInputValue({ + latex: "x", + name: mathInputName, + core, + }); + await submitAnswer({ name: "/ans", core }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/docCa"].stateValues.value).eq(1); + }); + + it(`item credit achieved, don't skip weight 0`, async () => { + let core = await createTestCore({ + doenetML: ` + $_document1.creditAchieved{assignNames="docCa"} +

x: x

+

a: a

+ +

y: y

+
+ +

b: b

+
+ +

z: z

+
+ + `, + }); + + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/docCa"].stateValues.value).eq(0); + expect( + stateVariables["/_document1"].stateValues.itemCreditAchieved, + ).eqls([0, 0, 0, 0, 0]); + + let mathInputXName = + stateVariables["/x"].stateValues.inputChildren[0].componentName; + let mathInputYName = + stateVariables["/y"].stateValues.inputChildren[0].componentName; + let mathInputZName = + stateVariables["/z"].stateValues.inputChildren[0].componentName; + let mathInputAName = + stateVariables["/a"].stateValues.inputChildren[0].componentName; + let mathInputBName = + stateVariables["/b"].stateValues.inputChildren[0].componentName; + + await updateMathInputValue({ + latex: "x", + name: mathInputXName, + core, + }); + await submitAnswer({ name: "/x", core }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/x"].stateValues.creditAchieved).eq(1); + expect(stateVariables["/docCa"].stateValues.value).eq(1 / 3); + expect( + stateVariables["/_document1"].stateValues.itemCreditAchieved, + ).eqls([1, 0, 0, 0, 0]); + + await updateMathInputValue({ + latex: "a", + name: mathInputAName, + core, + }); + await submitAnswer({ name: "/a", core }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/a"].stateValues.creditAchieved).eq(1); + expect(stateVariables["/docCa"].stateValues.value).eq(1 / 3); + expect( + stateVariables["/_document1"].stateValues.itemCreditAchieved, + ).eqls([1, 1, 0, 0, 0]); + + await updateMathInputValue({ + latex: "y", + name: mathInputYName, + core, + }); + await submitAnswer({ name: "/y", core }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/y"].stateValues.creditAchieved).eq(1); + expect(stateVariables["/docCa"].stateValues.value).eq(2 / 3); + expect( + stateVariables["/_document1"].stateValues.itemCreditAchieved, + ).eqls([1, 1, 1, 0, 0]); + + await updateMathInputValue({ + latex: "b", + name: mathInputBName, + core, + }); + await submitAnswer({ name: "/b", core }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/b"].stateValues.creditAchieved).eq(1); + expect(stateVariables["/docCa"].stateValues.value).eq(2 / 3); + expect( + stateVariables["/_document1"].stateValues.itemCreditAchieved, + ).eqls([1, 1, 1, 1, 0]); + + await updateMathInputValue({ + latex: "z", + name: mathInputZName, + core, + }); + await submitAnswer({ name: "/z", core }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/z"].stateValues.creditAchieved).eq(1); + expect(stateVariables["/docCa"].stateValues.value).eq(1); + expect( + stateVariables["/_document1"].stateValues.itemCreditAchieved, + ).eqls([1, 1, 1, 1, 1]); + }); + + it("explicit document tag, ignore outer blank strings", async () => { + let core = await createTestCore({ + doenetML: ` + + a + + + + `, + }); + + let stateVariables = await returnAllStateVariables(core); + + expect(Object.keys(stateVariables).length).eq(1); + + expect(stateVariables["/_document1"].activeChildren).eqls(["a"]); + }); +}); diff --git a/packages/doenetml-worker/src/test/tagSpecific/evaluate.test.ts b/packages/doenetml-worker/src/test/tagSpecific/evaluate.test.ts index 66b033413..a390096eb 100644 --- a/packages/doenetml-worker/src/test/tagSpecific/evaluate.test.ts +++ b/packages/doenetml-worker/src/test/tagSpecific/evaluate.test.ts @@ -2,15 +2,15 @@ import { describe, expect, it, vi } from "vitest"; import { createTestCore, returnAllStateVariables } from "../utils/test-core"; import { cleanLatex } from "../utils/math"; import { - updateBooleanInputValue, + movePoint, + submitAnswer, updateMathInputValue, - updateMatrixInputValue, - updateTextInputValue, } from "../utils/actions"; import me from "math-expressions"; const Mock = vi.fn(); vi.stubGlobal("postMessage", Mock); +vi.mock("hyperformula"); describe("Evaluate tag tests", async () => { it("evaluate numeric and symbolic", async () => { @@ -102,7 +102,7 @@ describe("Evaluate tag tests", async () => { // evaluate at pi await updateMathInputValue({ latex: "\\pi", - componentName: "/input", + name: "/input", core, }); @@ -145,7 +145,7 @@ describe("Evaluate tag tests", async () => { // change variable await updateMathInputValue({ latex: "y", - componentName: "/variable", + name: "/variable", core, }); @@ -188,7 +188,7 @@ describe("Evaluate tag tests", async () => { // change formula to match variable await updateMathInputValue({ latex: "\\sin(y)", - componentName: "/formula", + name: "/formula", core, }); @@ -251,7 +251,7 @@ describe("Evaluate tag tests", async () => { // change function await updateMathInputValue({ latex: "bx^2", - componentName: "/fformula", + name: "/fformula", core, }); @@ -263,7 +263,7 @@ describe("Evaluate tag tests", async () => { // change u await updateMathInputValue({ latex: "cq^2", - componentName: "/u", + name: "/u", core, }); @@ -275,7 +275,7 @@ describe("Evaluate tag tests", async () => { // change variable await updateMathInputValue({ latex: "y", - componentName: "/x", + name: "/x", core, }); @@ -287,7 +287,7 @@ describe("Evaluate tag tests", async () => { // change function to match variable await updateMathInputValue({ latex: "ay+by^2", - componentName: "/fformula", + name: "/fformula", core, }); @@ -321,15 +321,10 @@ describe("Evaluate tag tests", async () => { // submit answer await updateMathInputValue({ latex: "4", - componentName: "/mi", + name: "/mi", core, }); - await core.requestAction({ - componentName: "/ans1", - actionName: "submitAnswer", - args: {}, - event: null, - }); + await submitAnswer({ name: "/ans1", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/eval"].stateValues.value.tree).eqls([ @@ -531,7 +526,7 @@ describe("Evaluate tag tests", async () => { await updateMathInputValue({ latex: "\\pi", - componentName: "/input", + name: "/input", core, }); stateVariables = await returnAllStateVariables(core); @@ -888,7 +883,7 @@ describe("Evaluate tag tests", async () => { await updateMathInputValue({ latex: "\\pi", - componentName: "/input", + name: "/input", core, }); stateVariables = await returnAllStateVariables(core); @@ -1259,7 +1254,7 @@ describe("Evaluate tag tests", async () => { await updateMathInputValue({ latex: "\\pi", - componentName: "/input", + name: "/input", core, }); stateVariables = await returnAllStateVariables(core); @@ -1438,12 +1433,12 @@ describe("Evaluate tag tests", async () => { // evaluate at (pi, 2pi) await updateMathInputValue({ latex: "\\pi", - componentName: "/input1", + name: "/input1", core, }); await updateMathInputValue({ latex: "2\\pi", - componentName: "/input2", + name: "/input2", core, }); @@ -1480,12 +1475,12 @@ describe("Evaluate tag tests", async () => { // change variable await updateMathInputValue({ latex: "u", - componentName: "/variable1", + name: "/variable1", core, }); await updateMathInputValue({ latex: "v", - componentName: "/variable2", + name: "/variable2", core, }); @@ -1522,7 +1517,7 @@ describe("Evaluate tag tests", async () => { // change formula to use new variables await updateMathInputValue({ latex: "\\sin(u+v)", - componentName: "/formula", + name: "/formula", core, }); @@ -1587,7 +1582,7 @@ describe("Evaluate tag tests", async () => { // evaluate at (pi, pi/2) await updateMathInputValue({ latex: "(\\pi, \\pi/2)", - componentName: "/input", + name: "/input", core, }); @@ -1599,7 +1594,7 @@ describe("Evaluate tag tests", async () => { // change variables to 3D await updateMathInputValue({ latex: "x,y,z", - componentName: "/variablesOrig", + name: "/variablesOrig", core, }); @@ -1611,7 +1606,7 @@ describe("Evaluate tag tests", async () => { // change input to 3D await updateMathInputValue({ latex: "(\\pi, \\pi/2,3)", - componentName: "/input", + name: "/input", core, }); @@ -1623,7 +1618,7 @@ describe("Evaluate tag tests", async () => { // change formula to use all variables await updateMathInputValue({ latex: "z\\sin(x+y)", - componentName: "/formula", + name: "/formula", core, }); @@ -1635,7 +1630,7 @@ describe("Evaluate tag tests", async () => { // add fourth variable to formula await updateMathInputValue({ latex: "z\\sin(x+y/w)", - componentName: "/formula", + name: "/formula", core, }); @@ -1659,7 +1654,7 @@ describe("Evaluate tag tests", async () => { // add 4th input await updateMathInputValue({ latex: "(\\pi, \\pi/2,3,3)", - componentName: "/input", + name: "/input", core, }); @@ -1671,7 +1666,7 @@ describe("Evaluate tag tests", async () => { // add 4th variable await updateMathInputValue({ latex: "x,y,z,w", - componentName: "/variablesOrig", + name: "/variablesOrig", core, }); @@ -1866,37 +1861,27 @@ describe("Evaluate tag tests", async () => { // change inputs, use altvector await updateMathInputValue({ latex: "\\langle -3,5\\rangle", - componentName: "/input1", + name: "/input1", core, }); await updateMathInputValue({ latex: "-3,5", - componentName: "/input2Orig", + name: "/input2Orig", core, }); await updateMathInputValue({ latex: "-3", - componentName: "/input4a", + name: "/input4a", core, }); await updateMathInputValue({ latex: "5", - componentName: "/input4b", + name: "/input4b", core, }); - await core.requestAction({ - actionName: "movePoint", - componentName: "/A", - args: { x: -3, y: 7 }, - event: null, - }); - await core.requestAction({ - actionName: "movePoint", - componentName: "/B", - args: { x: 5, y: -9 }, - event: null, - }); + await movePoint({ name: "/A", x: -3, y: 7, core }); + await movePoint({ name: "/B", x: 5, y: -9, core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/result1a"].stateValues.value.tree).eqls([ @@ -2050,12 +2035,12 @@ describe("Evaluate tag tests", async () => { // evaluate at (pi, 2pi) await updateMathInputValue({ latex: "\\pi", - componentName: "/input1", + name: "/input1", core, }); await updateMathInputValue({ latex: "2\\pi", - componentName: "/input2", + name: "/input2", core, }); @@ -2115,12 +2100,12 @@ describe("Evaluate tag tests", async () => { // change variable await updateMathInputValue({ latex: "u", - componentName: "/variable1", + name: "/variable1", core, }); await updateMathInputValue({ latex: "v", - componentName: "/variable2", + name: "/variable2", core, }); @@ -2182,7 +2167,7 @@ describe("Evaluate tag tests", async () => { // change formula to use new variables await updateMathInputValue({ latex: "(\\sin(u+v), \\cos(u-v))", - componentName: "/formula", + name: "/formula", core, }); @@ -2285,7 +2270,7 @@ describe("Evaluate tag tests", async () => { // evaluate at (7,3) await updateMathInputValue({ latex: "(7,3)", - componentName: "/input", + name: "/input", core, }); @@ -2311,7 +2296,7 @@ describe("Evaluate tag tests", async () => { // change variables to 3D await updateMathInputValue({ latex: "x,y,z", - componentName: "/variablesOrig", + name: "/variablesOrig", core, }); @@ -2325,7 +2310,7 @@ describe("Evaluate tag tests", async () => { // change input to 3D await updateMathInputValue({ latex: "(7,3,2)", - componentName: "/input", + name: "/input", core, }); @@ -2351,7 +2336,7 @@ describe("Evaluate tag tests", async () => { // change formula to use all variables await updateMathInputValue({ latex: "(zx+y, x-yz)", - componentName: "/formula", + name: "/formula", core, }); @@ -2377,7 +2362,7 @@ describe("Evaluate tag tests", async () => { // add third dimension await updateMathInputValue({ latex: "(zx+y, x-yz,xyz)", - componentName: "/formula", + name: "/formula", core, }); @@ -2406,7 +2391,7 @@ describe("Evaluate tag tests", async () => { // add fourth variable and 4th dimension to formula await updateMathInputValue({ latex: "(zx+y, x-yz,xyzw,w)", - componentName: "/formula", + name: "/formula", core, }); @@ -2438,7 +2423,7 @@ describe("Evaluate tag tests", async () => { // add 4th input await updateMathInputValue({ latex: "(7,3,2,5)", - componentName: "/input", + name: "/input", core, }); @@ -2450,7 +2435,7 @@ describe("Evaluate tag tests", async () => { // add 4th variable await updateMathInputValue({ latex: "x,y,z,w", - componentName: "/variablesOrig", + name: "/variablesOrig", core, }); diff --git a/packages/doenetml-worker/src/test/tagSpecific/extract.test.ts b/packages/doenetml-worker/src/test/tagSpecific/extract.test.ts index 5923bfb09..b79172d89 100644 --- a/packages/doenetml-worker/src/test/tagSpecific/extract.test.ts +++ b/packages/doenetml-worker/src/test/tagSpecific/extract.test.ts @@ -1,12 +1,15 @@ import { describe, expect, it, vi } from "vitest"; import { createTestCore, returnAllStateVariables } from "../utils/test-core"; import { + movePoint, + movePolygon, updateBooleanInputValue, updateMathInputValue, } from "../utils/actions"; const Mock = vi.fn(); vi.stubGlobal("postMessage", Mock); +vi.mock("hyperformula"); describe("Extract tag tests", async () => { it("extract copies properties", async () => { @@ -87,12 +90,7 @@ describe("Extract tag tests", async () => { expect(stateVariables["/transformed"].stateValues.xs[1].tree).eq(1); // move original point - await core.requestAction({ - actionName: "movePoint", - componentName: "/original", - args: { x: -3, y: 5 }, - event: null, - }); + await movePoint({ name: "/original", x: -3, y: 5, core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/original"].stateValues.xs[0].tree).eq(-3); expect(stateVariables["/original"].stateValues.xs[1].tree).eq(5); @@ -102,12 +100,7 @@ describe("Extract tag tests", async () => { expect(stateVariables["/transformed"].stateValues.xs[1].tree).eq(-3); // move copy point - await core.requestAction({ - actionName: "movePoint", - componentName: "/copy", - args: { x: 6, y: -9 }, - event: null, - }); + await movePoint({ name: "/copy", x: 6, y: -9, core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/original"].stateValues.xs[0].tree).eq(6); expect(stateVariables["/original"].stateValues.xs[1].tree).eq(-9); @@ -117,12 +110,7 @@ describe("Extract tag tests", async () => { expect(stateVariables["/transformed"].stateValues.xs[1].tree).eq(6); // move transformed point - await core.requestAction({ - actionName: "movePoint", - componentName: "/transformed", - args: { x: -1, y: -7 }, - event: null, - }); + await movePoint({ name: "/transformed", x: -1, y: -7, core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/original"].stateValues.xs[0].tree).eq(-7); expect(stateVariables["/original"].stateValues.xs[1].tree).eq(-1); @@ -161,12 +149,7 @@ describe("Extract tag tests", async () => { // move extracted center - await core.requestAction({ - actionName: "movePoint", - componentName: "/copiedextract", - args: { x: -2, y: -5 }, - event: null, - }); + await movePoint({ name: "/copiedextract", x: -2, y: -5, core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/x1"].stateValues.value.tree).closeTo(-2, 1e-12); expect(stateVariables["/y1"].stateValues.value.tree).closeTo(-5, 1e-12); @@ -191,18 +174,8 @@ describe("Extract tag tests", async () => { // move points 1 and 2 - await core.requestAction({ - actionName: "movePoint", - componentName: "/_point1", - args: { x: 8, y: -1 }, - event: null, - }); - await core.requestAction({ - actionName: "movePoint", - componentName: "/_point2", - args: { x: -6, y: -7 }, - event: null, - }); + await movePoint({ name: "/_point1", x: 8, y: -1, core }); + await movePoint({ name: "/_point2", x: -6, y: -7, core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/x1"].stateValues.value.tree).closeTo(1, 1e-12); expect(stateVariables["/y1"].stateValues.value.tree).closeTo(-4, 1e-12); @@ -253,19 +226,19 @@ describe("Extract tag tests", async () => { await check_items([]); // set to 3 - await updateMathInputValue({ latex: "3", componentName: "/n", core }); + await updateMathInputValue({ latex: "3", name: "/n", core }); await check_items(["1", "2", "3"]); // increase to 4 - await updateMathInputValue({ latex: "4", componentName: "/n", core }); + await updateMathInputValue({ latex: "4", name: "/n", core }); await check_items(["1", "2", "3", "4"]); // decrease to 2 - await updateMathInputValue({ latex: "2", componentName: "/n", core }); + await updateMathInputValue({ latex: "2", name: "/n", core }); await check_items(["1", "2"]); // increase to 5 - await updateMathInputValue({ latex: "5", componentName: "/n", core }); + await updateMathInputValue({ latex: "5", name: "/n", core }); await check_items(["1", "2", "3", "4", "5"]); }); @@ -320,7 +293,7 @@ describe("Extract tag tests", async () => { await check_items([]); // set n to 3 - await updateMathInputValue({ latex: "3", componentName: "/n", core }); + await updateMathInputValue({ latex: "3", name: "/n", core }); await check_items([ ["+", 1, "_"], ["+", 2, "_"], @@ -328,15 +301,15 @@ describe("Extract tag tests", async () => { ]); // set m to 7 - await updateMathInputValue({ latex: "7", componentName: "/m", core }); + await updateMathInputValue({ latex: "7", name: "/m", core }); await check_items([8, 9, 10]); // increase n to 4 - await updateMathInputValue({ latex: "4", componentName: "/n", core }); + await updateMathInputValue({ latex: "4", name: "/n", core }); await check_items([8, 9, 10, 11]); // change m to q - await updateMathInputValue({ latex: "q", componentName: "/m", core }); + await updateMathInputValue({ latex: "q", name: "/m", core }); await check_items([ ["+", "q", 1], ["+", "q", 2], @@ -345,18 +318,18 @@ describe("Extract tag tests", async () => { ]); // decrease n to 2 - await updateMathInputValue({ latex: "2", componentName: "/n", core }); + await updateMathInputValue({ latex: "2", name: "/n", core }); await check_items([ ["+", "q", 1], ["+", "q", 2], ]); // set m to -1 - await updateMathInputValue({ latex: "-1", componentName: "/m", core }); + await updateMathInputValue({ latex: "-1", name: "/m", core }); await check_items([0, 1]); // increase n to 5 - await updateMathInputValue({ latex: "5", componentName: "/n", core }); + await updateMathInputValue({ latex: "5", name: "/n", core }); await check_items([0, 1, 2, 3, 4]); }); @@ -396,12 +369,12 @@ describe("Extract tag tests", async () => { await updateBooleanInputValue({ boolean: true, - componentName: "/h1", + name: "/h1", core, }); await updateBooleanInputValue({ boolean: false, - componentName: "/h2", + name: "/h2", core, }); @@ -411,12 +384,12 @@ describe("Extract tag tests", async () => { await updateBooleanInputValue({ boolean: false, - componentName: "/h1", + name: "/h1", core, }); await updateBooleanInputValue({ boolean: true, - componentName: "/h2", + name: "/h2", core, }); @@ -483,33 +456,23 @@ describe("Extract tag tests", async () => { await check_items({ x1, x2, y1, y2 }); // restrict collection to first component - await updateMathInputValue({ latex: "1", componentName: "/n", core }); + await updateMathInputValue({ latex: "1", name: "/n", core }); await check_items({ x1, x2, y1, y2, Ax: x1 }); // move point x1 = 9; y1 = -5; - await core.requestAction({ - actionName: "movePoint", - componentName: "/A", - args: { x: x1, y: y1 }, - event: null, - }); + await movePoint({ name: "/A", x: x1, y: y1, core }); await check_items({ x1, x2, y1, y2, Ax: x1 }); // restrict collection to second component - await updateMathInputValue({ latex: "2", componentName: "/n", core }); + await updateMathInputValue({ latex: "2", name: "/n", core }); await check_items({ x1, x2, y1, y2, Ax: x2 }); x2 = 0; y2 = 8; - await core.requestAction({ - actionName: "movePoint", - componentName: "/B", - args: { x: x2, y: y2 }, - event: null, - }); + await movePoint({ name: "/B", x: x2, y: y2, core }); await check_items({ x1, x2, y1, y2, Ax: x2 }); }); @@ -578,61 +541,51 @@ describe("Extract tag tests", async () => { // set propIndex to 1 - await updateMathInputValue({ latex: "1", componentName: "/n", core }); + await updateMathInputValue({ latex: "1", name: "/n", core }); await check_items({ x1, x2, y1, y2 }); // move point 1 x1 = 9; y1 = -5; - await core.requestAction({ - actionName: "movePoint", - componentName: "/A", - args: { x: x1, y: y1 }, - event: null, - }); + await movePoint({ name: "/A", x: x1, y: y1, core }); await check_items({ x1, x2, y1, y2 }); // set componentIndex to 2 - await updateMathInputValue({ latex: "2", componentName: "/m", core }); + await updateMathInputValue({ latex: "2", name: "/m", core }); await check_items({ x1, x2, y1, y2, n1: x2 }); // move point2 x2 = 0; y2 = 8; - await core.requestAction({ - actionName: "movePoint", - componentName: "/B", - args: { x: x2, y: y2 }, - event: null, - }); + await movePoint({ name: "/B", x: x2, y: y2, core }); await check_items({ x1, x2, y1, y2, n1: x2 }); // set propIndex to 2 - await updateMathInputValue({ latex: "2", componentName: "/n", core }); + await updateMathInputValue({ latex: "2", name: "/n", core }); await check_items({ x1, x2, y1, y2, n1: y2 }); // set componentIndex to 1 - await updateMathInputValue({ latex: "1", componentName: "/m", core }); + await updateMathInputValue({ latex: "1", name: "/m", core }); await check_items({ x1, x2, y1, y2, n1: y1 }); // set propIndex to 3 - await updateMathInputValue({ latex: "3", componentName: "/n", core }); + await updateMathInputValue({ latex: "3", name: "/n", core }); await check_items({ x1, x2, y1, y2 }); // set propIndex to 1 - await updateMathInputValue({ latex: "1", componentName: "/n", core }); + await updateMathInputValue({ latex: "1", name: "/n", core }); await check_items({ x1, x2, y1, y2, n1: x1 }); // set componentIndex to 3 - await updateMathInputValue({ latex: "3", componentName: "/m", core }); + await updateMathInputValue({ latex: "3", name: "/m", core }); await check_items({ x1, x2, y1, y2 }); // set componentIndex to 2 - await updateMathInputValue({ latex: "2", componentName: "/m", core }); + await updateMathInputValue({ latex: "2", name: "/m", core }); await check_items({ x1, x2, y1, y2, n1: x2 }); // clear propIndex - await updateMathInputValue({ latex: "", componentName: "/n", core }); + await updateMathInputValue({ latex: "", name: "/n", core }); await check_items({ x1, x2, y1, y2 }); }); @@ -704,65 +657,51 @@ describe("Extract tag tests", async () => { await check_items({ x1, y1, x2, y2, x3, y3 }); // set second propIndex to 1 - await updateMathInputValue({ latex: "1", componentName: "/n", core }); + await updateMathInputValue({ latex: "1", name: "/n", core }); await check_items({ x1, y1, x2, y2, x3, y3 }); // move first point x1 = 9; y1 = -5; - await core.requestAction({ - actionName: "movePolygon", - componentName: "/pg", - args: { - pointCoords: { 0: [x1, y1] }, - }, - event: null, - }); + await movePolygon({ name: "/pg", pointCoords: { 0: [x1, y1] }, core }); await check_items({ x1, y1, x2, y2, x3, y3 }); // set first propIndex to 2 - await updateMathInputValue({ latex: "2", componentName: "/m", core }); + await updateMathInputValue({ latex: "2", name: "/m", core }); await check_items({ x1, y1, x2, y2, x3, y3, n1: x2 }); // move second point x2 = 0; y2 = 8; - await core.requestAction({ - actionName: "movePolygon", - componentName: "/pg", - args: { - pointCoords: { 1: [x2, y2] }, - }, - event: null, - }); + await movePolygon({ name: "/pg", pointCoords: { 1: [x2, y2] }, core }); await check_items({ x1, y1, x2, y2, x3, y3, n1: x2 }); // set second propIndex to 2 - await updateMathInputValue({ latex: "2", componentName: "/n", core }); + await updateMathInputValue({ latex: "2", name: "/n", core }); await check_items({ x1, y1, x2, y2, x3, y3, n1: y2 }); // set first propIndex to 1 - await updateMathInputValue({ latex: "1", componentName: "/m", core }); + await updateMathInputValue({ latex: "1", name: "/m", core }); await check_items({ x1, y1, x2, y2, x3, y3, n1: y1 }); // set second propIndex to 3 - await updateMathInputValue({ latex: "3", componentName: "/n", core }); + await updateMathInputValue({ latex: "3", name: "/n", core }); await check_items({ x1, y1, x2, y2, x3, y3 }); // set second propIndex to 1 - await updateMathInputValue({ latex: "1", componentName: "/n", core }); + await updateMathInputValue({ latex: "1", name: "/n", core }); await check_items({ x1, y1, x2, y2, x3, y3, n1: x1 }); // set first propindex to 4 - await updateMathInputValue({ latex: "4", componentName: "/m", core }); + await updateMathInputValue({ latex: "4", name: "/m", core }); await check_items({ x1, y1, x2, y2, x3, y3 }); // set first propIndex to 3 - await updateMathInputValue({ latex: "3", componentName: "/m", core }); + await updateMathInputValue({ latex: "3", name: "/m", core }); await check_items({ x1, y1, x2, y2, x3, y3, n1: x3 }); // clear second propIndex - await updateMathInputValue({ latex: "", componentName: "/n", core }); + await updateMathInputValue({ latex: "", name: "/n", core }); await check_items({ x1, y1, x2, y2, x3, y3 }); }); diff --git a/packages/doenetml-worker/src/test/tagSpecific/feedback.test.ts b/packages/doenetml-worker/src/test/tagSpecific/feedback.test.ts new file mode 100644 index 000000000..ea3a491ad --- /dev/null +++ b/packages/doenetml-worker/src/test/tagSpecific/feedback.test.ts @@ -0,0 +1,1630 @@ +import { describe, expect, it, vi } from "vitest"; +import { createTestCore, returnAllStateVariables } from "../utils/test-core"; +import { + submitAnswer, + updateMathInputValue, + updateSelectedIndices, + updateTextInputValue, +} from "../utils/actions"; +import Core from "../../Core"; + +const Mock = vi.fn(); +vi.stubGlobal("postMessage", Mock); +vi.mock("hyperformula"); + +describe("Feedback tag tests", async () => { + async function test_three_feedbacks(core: Core, showFeedback: boolean) { + async function check_items( + show1: boolean, + show2: boolean, + currentResponse: string, + submittedResponse?: string, + ) { + const stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/pfc"].stateValues.text).eq( + "You got full credit!", + ); + expect(stateVariables["/pra"].stateValues.text).eq( + "You typed the right answer!", + ); + expect(stateVariables["/pba"].stateValues.text).eq( + "That's a bad answer.", + ); + + expect( + stateVariables[ + "/ans" + ].stateValues.currentResponses[0].toString(), + ).eq(currentResponse); + if (submittedResponse === undefined) { + expect( + stateVariables["/ans"].stateValues.submittedResponses, + ).eqls([]); + } else { + expect( + stateVariables[ + "/ans" + ].stateValues.submittedResponses[0].toString(), + ).eq(submittedResponse); + } + + expect(stateVariables["/ffc"].stateValues.hidden).eq(show1); + expect(stateVariables["/fra"].stateValues.hidden).eq(show1); + expect(stateVariables["/fba"].stateValues.hidden).eq(show2); + expect(stateVariables["/pfc"].stateValues.hidden).eq(show1); + expect(stateVariables["/pra"].stateValues.hidden).eq(show1); + expect(stateVariables["/pba"].stateValues.hidden).eq(show2); + } + + let stateVariables = await returnAllStateVariables(core); + let mathInputName = + stateVariables["/ans"].stateValues.inputChildren[0].componentName; + + let hidden1 = true; + let hidden2 = true; + let currentResponse = "\uff3f"; + let submittedResponse: string | undefined = undefined; + + await check_items(hidden1, hidden2, currentResponse, submittedResponse); + + // Type correct answer in + currentResponse = "x + y"; + await updateMathInputValue({ + latex: currentResponse, + name: mathInputName, + core, + }); + + await check_items(hidden1, hidden2, currentResponse, submittedResponse); + + // Submit answer + submittedResponse = currentResponse; + await submitAnswer({ name: "/ans", core }); + + if (showFeedback) { + hidden1 = false; + } + await check_items(hidden1, hidden2, currentResponse, submittedResponse); + + // Type wrong answer + currentResponse = "x"; + await updateMathInputValue({ + latex: currentResponse, + name: mathInputName, + core, + }); + await check_items(hidden1, hidden2, currentResponse, submittedResponse); + + // Submit answer + submittedResponse = currentResponse; + await submitAnswer({ name: "/ans", core }); + + if (showFeedback) { + hidden1 = true; + hidden2 = false; + } + await check_items(hidden1, hidden2, currentResponse, submittedResponse); + + // Enter different wrong answer + currentResponse = "y"; + await updateMathInputValue({ + latex: currentResponse, + name: mathInputName, + core, + }); + await check_items(hidden1, hidden2, currentResponse, submittedResponse); + + // Submit answer + submittedResponse = currentResponse; + await submitAnswer({ name: "/ans", core }); + + if (showFeedback) { + hidden1 = true; + hidden2 = true; + } + await check_items(hidden1, hidden2, currentResponse, submittedResponse); + } + + it("feedback from answer value or credit", async () => { + let core = await createTestCore({ + doenetML: ` +

x+y

+ +

You got full credit!

+
+ +

You typed the right answer!

+
+ +

That's a bad answer.

+
+ `, + }); + + await test_three_feedbacks(core, true); + }); + + it("feedback from answer value or credit, set showFeedback=false", async () => { + let core = await createTestCore({ + doenetML: ` +

x+y

+ +

You got full credit!

+
+ +

You typed the right answer!

+
+ +

That's a bad answer.

+
+ `, + flags: { showFeedback: false }, + }); + + await test_three_feedbacks(core, false); + }); + + it("feedback from award", async () => { + let core = await createTestCore({ + doenetML: ` +

+ x+y + x +

+ +

You got full credit!

+
+ +

You typed the right answer!

+
+ +

That's a bad answer.

+
+ + `, + }); + + await test_three_feedbacks(core, true); + }); + + it("feedback from full awards", async () => { + let core = await createTestCore({ + doenetML: ` +

+ + $mi > 1 + $mi > 10 + $mi > 2 + $mi > 0.9 + $mi < 0 +

+

Credit achieved: $ans.creditAchieved{assignNames="ca"}

+ +

Larger than 1

+
+ +

Larger than 10

+
+ +

Larger than 2

+
+ +

Larger than 0.9

+
+ +

A negative number?

+
+ `, + }); + + async function check_items(submittedResponse: number | undefined) { + let creditAchieved = 0; + let awardReceived: number | null = null; + if (submittedResponse !== undefined) { + if (submittedResponse > 10) { + creditAchieved = 1; + awardReceived = 2; + } else if (submittedResponse > 2) { + creditAchieved = 0.2; + awardReceived = 3; + } else if (submittedResponse > 1) { + creditAchieved = 0.1; + awardReceived = 1; + } else if (submittedResponse > 0.9) { + creditAchieved = 0.1; + awardReceived = 4; + } else if (submittedResponse < 0) { + awardReceived = 5; + } + } + + const stateVariables = await returnAllStateVariables(core); + + expect(stateVariables["/ca"].stateValues.value).eq(creditAchieved); + expect(stateVariables["/ans"].stateValues.creditAchieved).eq( + creditAchieved, + ); + + for (let awardNum = 1; awardNum <= 5; awardNum++) { + if (awardNum === awardReceived) { + expect( + stateVariables[`/f${awardNum}`].stateValues.hidden, + ).eq(false); + } else { + expect( + stateVariables[`/f${awardNum}`].stateValues.hidden, + ).eq(true); + } + } + } + + let submittedResponse: number | undefined = undefined; + + await check_items(submittedResponse); + + submittedResponse = 11; + await updateMathInputValue({ + latex: submittedResponse.toString(), + name: "/mi", + core, + }); + await submitAnswer({ name: "/ans", core }); + await check_items(submittedResponse); + + submittedResponse = 10; + await updateMathInputValue({ + latex: submittedResponse.toString(), + name: "/mi", + core, + }); + await submitAnswer({ name: "/ans", core }); + await check_items(submittedResponse); + + submittedResponse = 2; + await updateMathInputValue({ + latex: submittedResponse.toString(), + name: "/mi", + core, + }); + await submitAnswer({ name: "/ans", core }); + await check_items(submittedResponse); + + submittedResponse = 1; + await updateMathInputValue({ + latex: submittedResponse.toString(), + name: "/mi", + core, + }); + await submitAnswer({ name: "/ans", core }); + await check_items(submittedResponse); + + submittedResponse = 0; + await updateMathInputValue({ + latex: submittedResponse.toString(), + name: "/mi", + core, + }); + await submitAnswer({ name: "/ans", core }); + await check_items(submittedResponse); + + submittedResponse = -1; + await updateMathInputValue({ + latex: submittedResponse.toString(), + name: "/mi", + core, + }); + await submitAnswer({ name: "/ans", core }); + await check_items(submittedResponse); + }); + + it("feedback from copied awards", async () => { + let core = await createTestCore({ + doenetML: ` +

+ + x+y + x +

+ +

You got award 1.

+
+ +

You got award 2.

+
+

+ + + +

+ +

You got award 3.

+
+ +

You got award 4.

+
+ `, + }); + + async function check_items( + ans1Response: string | undefined, + ans2Response: string | undefined, + ) { + let credit1 = 0, + credit2 = 0; + + let award1 = false, + award2 = false, + award3 = false, + award4 = false; + + if (ans1Response === "x+y") { + credit1 = 1; + award1 = true; + } else if (ans1Response === "x") { + credit1 = 0.5; + award2 = true; + } + + if (ans2Response === "x+y") { + credit2 = 0.5; + award3 = true; + } else if (ans2Response === "x") { + credit2 = 1; + award4 = true; + } + + const stateVariables = await returnAllStateVariables(core); + + expect(stateVariables["/ans1"].stateValues.creditAchieved).eq( + credit1, + ); + expect(stateVariables["/ans2"].stateValues.creditAchieved).eq( + credit2, + ); + + expect(stateVariables["/f1"].stateValues.hidden).eq(!award1); + expect(stateVariables["/f2"].stateValues.hidden).eq(!award2); + expect(stateVariables["/f3"].stateValues.hidden).eq(!award3); + expect(stateVariables["/f4"].stateValues.hidden).eq(!award4); + } + + let ans1Response: string | undefined = undefined; + let ans2Response: string | undefined = undefined; + + await check_items(ans1Response, ans2Response); + + // Submit correct answer 1 + ans1Response = "x+y"; + await updateMathInputValue({ + latex: ans1Response, + name: "/mi1", + core, + }); + await submitAnswer({ name: "/ans1", core }); + await check_items(ans1Response, ans2Response); + + // Submit wrong answer 1 + ans1Response = "x"; + await updateMathInputValue({ + latex: ans1Response, + name: "/mi1", + core, + }); + await submitAnswer({ name: "/ans1", core }); + await check_items(ans1Response, ans2Response); + + // Submit wrong answer 2 + ans2Response = "x+y"; + await updateMathInputValue({ + latex: ans2Response, + name: "/mi2", + core, + }); + await submitAnswer({ name: "/ans2", core }); + await check_items(ans1Response, ans2Response); + + // Submit correct answer 2 + ans2Response = "x"; + await updateMathInputValue({ + latex: ans2Response, + name: "/mi2", + core, + }); + await submitAnswer({ name: "/ans2", core }); + await check_items(ans1Response, ans2Response); + + // Enter different wrong answer 1 + ans1Response = "y"; + await updateMathInputValue({ + latex: ans1Response, + name: "/mi1", + core, + }); + await submitAnswer({ name: "/ans1", core }); + await check_items(ans1Response, ans2Response); + + // Enter different wrong answer 2 + ans2Response = "y"; + await updateMathInputValue({ + latex: ans2Response, + name: "/mi2", + core, + }); + await submitAnswer({ name: "/ans2", core }); + await check_items(ans1Response, ans2Response); + }); + + async function test_feedback_from_multiple_choice(core: Core) { + async function check_items(submittedChoice?: string) { + let credit = 0; + if (submittedChoice === "dog") { + credit = 1; + } else if (submittedChoice === "cow") { + credit = 0.2; + } else if ( + submittedChoice === "cat" || + submittedChoice === "mouse" + ) { + credit = 0.1; + } + + const stateVariables = await returnAllStateVariables(core); + + expect(stateVariables["/ca"].stateValues.value).eq(credit); + + for (let animal of ["dog", "cat", "cow", "mouse", "banana"]) { + expect(stateVariables[`/f${animal}`].stateValues.hidden).eq( + submittedChoice !== animal, + ); + } + } + + const stateVariables = await returnAllStateVariables(core); + const choiceTexts: string[] = + stateVariables["/ci"].stateValues.choiceTexts; + + await check_items(); + + // Select dog + let selectedChoice = "dog"; + let selectedIndex = choiceTexts.indexOf(selectedChoice) + 1; + await updateSelectedIndices({ + name: "/ci", + selectedIndices: [selectedIndex], + core, + }); + await check_items(); + + // Submit answer + let submittedChoice = selectedChoice; + await submitAnswer({ name: "/ans", core }); + await check_items(submittedChoice); + + // submit cow + submittedChoice = selectedChoice = "cow"; + selectedIndex = choiceTexts.indexOf(selectedChoice) + 1; + await updateSelectedIndices({ + name: "/ci", + selectedIndices: [selectedIndex], + core, + }); + await submitAnswer({ name: "/ans", core }); + await check_items(submittedChoice); + + // submit cat + submittedChoice = selectedChoice = "cat"; + selectedIndex = choiceTexts.indexOf(selectedChoice) + 1; + await updateSelectedIndices({ + name: "/ci", + selectedIndices: [selectedIndex], + core, + }); + await submitAnswer({ name: "/ans", core }); + await check_items(submittedChoice); + + // submit mouse + submittedChoice = selectedChoice = "mouse"; + selectedIndex = choiceTexts.indexOf(selectedChoice) + 1; + await updateSelectedIndices({ + name: "/ci", + selectedIndices: [selectedIndex], + core, + }); + await submitAnswer({ name: "/ans", core }); + await check_items(submittedChoice); + + // submit banana + submittedChoice = selectedChoice = "banana"; + selectedIndex = choiceTexts.indexOf(selectedChoice) + 1; + await updateSelectedIndices({ + name: "/ci", + selectedIndices: [selectedIndex], + core, + }); + await submitAnswer({ name: "/ans", core }); + await check_items(submittedChoice); + } + + it("feedback from multiple choice", async () => { + let core = await createTestCore({ + doenetML: ` +

+ + cat + dog + cow + mouse + banana + +

+

Credit achieved: $ans.creditAchieved{assignNames="ca"}

+ +

Meow

+
P + +

Ruff

+
+ +

Moo

+
+ +

Squeak

+
+ +

Huh?

+
+ `, + }); + + await test_feedback_from_multiple_choice(core); + }); + + it("feedback from multiple choice, some choices inside shuffle", async () => { + let core = await createTestCore({ + doenetML: ` +

+ + cat + + dog + cow + + mouse + banana + +

+

Credit achieved: $ans.creditAchieved{assignNames="ca"}

+ +

Meow

+
P + +

Ruff

+
+ +

Moo

+
+ +

Squeak

+
+ +

Huh?

+
+ `, + }); + + await test_feedback_from_multiple_choice(core); + }); + + it("feedback from multiple choice, copied choices, some choices inside shuffle", async () => { + let core = await createTestCore({ + doenetML: ` +

+ + cat + + dog + cow + + +

+

Credit achieved 1: $ans1.creditAchieved{assignNames="ca1"}

+ +

Meow

+
+ +

Ruff

+
+ +

Moo

+
+ +

+ + + + + + + +

+

Credit achieved 2: $ans2.creditAchieved{assignNames="ca2"}

+ +

Ruff

+
+ +

Meow

+
+ +

Moo

+
+ `, + requestedVariantIndex: 2, + }); + + async function check_items( + submittedChoice1?: string, + submittedChoice2?: string, + ) { + let credit1 = 0; + if (submittedChoice1 === "dog") { + credit1 = 1; + } else if (submittedChoice1 === "cat") { + credit1 = 0.1; + } + let credit2 = 0; + if (submittedChoice2 === "cat") { + credit2 = 1; + } else if (submittedChoice2 === "dog") { + credit2 = 0.1; + } + + const stateVariables = await returnAllStateVariables(core); + + expect(stateVariables["/ca1"].stateValues.value).eq(credit1); + expect(stateVariables["/ca2"].stateValues.value).eq(credit2); + + for (let animal of ["dog", "cat", "cow"]) { + expect(stateVariables[`/f1${animal}`].stateValues.hidden).eq( + submittedChoice1 !== animal, + ); + expect(stateVariables[`/f2${animal}`].stateValues.hidden).eq( + submittedChoice2 !== animal, + ); + } + } + + const stateVariables = await returnAllStateVariables(core); + const choiceTexts1: string[] = + stateVariables["/ci1"].stateValues.choiceTexts; + const choiceTexts2: string[] = + stateVariables["/ci2"].stateValues.choiceTexts; + + await check_items(); + + // Submit dog1 + let submittedChoice1 = "dog"; + let selectedIndex1 = choiceTexts1.indexOf(submittedChoice1) + 1; + await updateSelectedIndices({ + name: "/ci1", + selectedIndices: [selectedIndex1], + core, + }); + await submitAnswer({ name: "/ans1", core }); + await check_items(submittedChoice1); + + // submit cow1 + submittedChoice1 = "cow"; + selectedIndex1 = choiceTexts1.indexOf(submittedChoice1) + 1; + await updateSelectedIndices({ + name: "/ci1", + selectedIndices: [selectedIndex1], + core, + }); + await submitAnswer({ name: "/ans1", core }); + await check_items(submittedChoice1); + + // Submit dog2 + let submittedChoice2 = "dog"; + let selectedIndex2 = choiceTexts2.indexOf(submittedChoice2) + 1; + await updateSelectedIndices({ + name: "/ci2", + selectedIndices: [selectedIndex2], + core, + }); + await submitAnswer({ name: "/ans2", core }); + await check_items(submittedChoice1, submittedChoice2); + + // Submit cat2 + submittedChoice2 = "cat"; + selectedIndex2 = choiceTexts2.indexOf(submittedChoice2) + 1; + await updateSelectedIndices({ + name: "/ci2", + selectedIndices: [selectedIndex2], + core, + }); + await submitAnswer({ name: "/ans2", core }); + await check_items(submittedChoice1, submittedChoice2); + + // Submit cat1 + submittedChoice1 = "cat"; + selectedIndex1 = choiceTexts1.indexOf(submittedChoice1) + 1; + await updateSelectedIndices({ + name: "/ci1", + selectedIndices: [selectedIndex1], + core, + }); + await submitAnswer({ name: "/ans1", core }); + await check_items(submittedChoice1, submittedChoice2); + + // Submit cow2 + submittedChoice2 = "cow"; + selectedIndex2 = choiceTexts2.indexOf(submittedChoice2) + 1; + await updateSelectedIndices({ + name: "/ci2", + selectedIndices: [selectedIndex2], + core, + }); + await submitAnswer({ name: "/ans2", core }); + await check_items(submittedChoice1, submittedChoice2); + }); + + it("feedback for any incorrect response", async () => { + let core = await createTestCore({ + doenetML: ` +

hello there

+ +

Your response $ans.submittedresponse is incorrect.

+
+ `, + }); + + async function check_items(response?: string) { + const stateVariables = await returnAllStateVariables(core); + + let credit = response === "hello there" ? 1 : 0; + let hidden = response === undefined || response === "hello there"; + + expect(stateVariables["/ans"].stateValues.creditAchieved).eq( + credit, + ); + expect(stateVariables["/f"].stateValues.hidden).eq(hidden); + expect(stateVariables["/p"].stateValues.hidden).eq(hidden); + + if (!hidden) { + expect(stateVariables["/p"].stateValues.text).eq( + `Your response ${response} is incorrect.`, + ); + } + } + + let stateVariables = await returnAllStateVariables(core); + let tiName = + stateVariables["/ans"].stateValues.inputChildren[0].componentName; + + await check_items(); + + // Submit incorrect answer + let response = "wrong"; + await updateTextInputValue({ text: response, name: tiName, core }); + await submitAnswer({ name: "/ans", core }); + await check_items(response); + + // Submit correct answer + response = "hello there"; + await updateTextInputValue({ text: response, name: tiName, core }); + await submitAnswer({ name: "/ans", core }); + await check_items(response); + + // Submit blank answer + response = ""; + await updateTextInputValue({ text: response, name: tiName, core }); + await submitAnswer({ name: "/ans", core }); + await check_items(response); + + // Submit another incorrect answer + response = "bye"; + await updateTextInputValue({ text: response, name: tiName, core }); + await submitAnswer({ name: "/ans", core }); + await check_items(response); + }); + + async function test_feedback_defined_in_awards( + core: Core, + wrongCosCode?: boolean, + ) { + async function check_items(submittedLatex?: string) { + const stateVariables = await returnAllStateVariables(core); + if (submittedLatex === "\\sin(\\pi x)") { + if (stateVariables["/ans"].stateValues.justSubmitted) { + expect( + stateVariables["/ans"].stateValues.creditAchieved, + ).eq(1); + } + expect( + stateVariables["/feedback1"].stateValues.feedbackText, + ).eq("Good job!"); + expect( + stateVariables["/feedback4"].stateValues.feedbackText, + ).eq("Good job!"); + expect(stateVariables["/feedback2"]).eq(undefined); + expect(stateVariables["/feedback3"]).eq(undefined); + } else if (submittedLatex === "\\cos(\\pi x)") { + if (stateVariables["/ans"].stateValues.justSubmitted) { + expect( + stateVariables["/ans"].stateValues.creditAchieved, + ).eq(0.7); + } + if (wrongCosCode) { + expect(stateVariables["/feedback2"]).eq(undefined); + expect(stateVariables["/feedback4"]).eq(undefined); + } else { + expect( + stateVariables["/feedback2"].stateValues.feedbackText, + ).eq("Close, but wrong trigonometric function"); + expect( + stateVariables["/feedback4"].stateValues.feedbackText, + ).eq("Close, but wrong trigonometric function"); + } + expect(stateVariables["/feedback1"]).eq(undefined); + expect(stateVariables["/feedback3"]).eq(undefined); + } else if (submittedLatex === "\\sin(x)") { + if (stateVariables["/ans"].stateValues.justSubmitted) { + expect( + stateVariables["/ans"].stateValues.creditAchieved, + ).eq(0.3); + } + expect( + stateVariables["/feedback3"].stateValues.feedbackText, + ).eq("You lost pi"); + expect( + stateVariables["/feedback4"].stateValues.feedbackText, + ).eq("You lost pi"); + expect(stateVariables["/feedback1"]).eq(undefined); + expect(stateVariables["/feedback2"]).eq(undefined); + } else { + if (stateVariables["/ans"].stateValues.justSubmitted) { + expect( + stateVariables["/ans"].stateValues.creditAchieved, + ).eq(0); + } + expect(stateVariables["/feedback1"]).eq(undefined); + expect(stateVariables["/feedback2"]).eq(undefined); + expect(stateVariables["/feedback3"]).eq(undefined); + expect(stateVariables["/feedback4"]).eq(undefined); + } + } + + await check_items(); + + // submit blank answer + await submitAnswer({ name: "/ans", core }); + await check_items(); + + // Type sin(pi x) + let response = "\\sin(\\pi x)"; + await updateMathInputValue({ latex: response, name: "/mi", core }); + await check_items(); + + // Submit answer + let submittedResponse = response; + await submitAnswer({ name: "/ans", core }); + await check_items(submittedResponse); + + // Type cos(pi x) + response = "\\cos(\\pi x)"; + await updateMathInputValue({ latex: response, name: "/mi", core }); + await check_items(submittedResponse); + + // Submit answer + submittedResponse = response; + await submitAnswer({ name: "/ans", core }); + await check_items(submittedResponse); + + // Submit x + submittedResponse = response = "x"; + await updateMathInputValue({ latex: response, name: "/mi", core }); + await submitAnswer({ name: "/ans", core }); + await check_items(submittedResponse); + + // Submit sin(x) + submittedResponse = response = "\\sin(x)"; + await updateMathInputValue({ latex: response, name: "/mi", core }); + await submitAnswer({ name: "/ans", core }); + await check_items(submittedResponse); + await submitAnswer({ name: "/ans", core }); + } + + it("feedback defined in awards", async () => { + let core = await createTestCore({ + doenetML: ` +

+ + sin(pi x) + cos(pi x) + sin(x) +

+ +

Award 1 feedback:

+ $award1.feedback{assignNames="feedback1"} + +

Award 2 feedback:

+ $award2.feedback{assignNames="feedback2"} + +

Award 3 feedback:

+ $award3.feedback{assignNames="feedback3"} + +

Answer feedbacks:

+ $ans.feedbacks{assignNames="feedback4"} + `, + }); + + await test_feedback_defined_in_awards(core); + }); + + it("feedback defined in awards, new feedback definitions", async () => { + let core = await createTestCore({ + doenetML: ` + + + + + + + +

+ + sin(pi x) + cos(pi x) + sin(x) +

+ +

Award 1 feedback:

+ $award1.feedback{assignNames="feedback1"} + +

Award 2 feedback:

+ $award2.feedback{assignNames="feedback2"} + +

Award 3 feedback:

+ $award3.feedback{assignNames="feedback3"} + +

Answer feedbacks:

+ $ans.feedbacks{assignNames="feedback4"} + + `, + }); + + await test_feedback_defined_in_awards(core); + }); + + it("feedback defined in awards, new feedback definitions in document and section", async () => { + let core = await createTestCore({ + doenetML: ` + + + + + + + +
+ + + + + + +

+ + sin(pi x) + cos(pi x) + sin(x) +

+ +

Award 1 feedback:

+ $award1.feedback{assignNames="feedback1"} + +

Award 2 feedback:

+ $award2.feedback{assignNames="feedback2"} + +

Award 3 feedback:

+ $award3.feedback{assignNames="feedback3"} + +

Answer feedbacks:

+ $ans.feedbacks{assignNames="feedback4"} +
+ `, + }); + + await test_feedback_defined_in_awards(core); + }); + + it("feedback defined in awards, new feedback definitions in document, incorrect codes", async () => { + let core = await createTestCore({ + doenetML: ` + + + + + + + + +

+ + sin(pi x) + cos(pi x) + sin(x) +

+ +

Award 1 feedback:

+ $award1.feedback{assignNames="feedback1"} + +

Award 2 feedback:

+ $award2.feedback{assignNames="feedback2"} + +

Award 3 feedback:

+ $award3.feedback{assignNames="feedback3"} + +

Answer feedbacks:

+ $ans.feedbacks{assignNames="feedback4"} + + `, + }); + + await test_feedback_defined_in_awards(core, true); + }); + + async function test_feedback_defined_in_choices( + core: Core, + feedbacks: Record, + ) { + async function check_items(response?: string) { + const stateVariables = await returnAllStateVariables(core); + + if (response === "cat") { + if (stateVariables["/ans"].stateValues.justSubmitted) { + expect( + stateVariables["/ans"].stateValues.creditAchieved, + ).eq(0.5); + } + for (let i = 1; i <= 2; i++) { + let feedbackText = feedbacks.cat[i - 1]; + if (feedbackText) { + expect( + stateVariables[`/f${i}`].stateValues.feedbackText, + ).eq(feedbackText); + } else { + expect(stateVariables[`/f${i}`]).eq(undefined); + } + } + } else if (response === "dog") { + if (stateVariables["/ans"].stateValues.justSubmitted) { + expect( + stateVariables["/ans"].stateValues.creditAchieved, + ).eq(1); + } + for (let i = 1; i <= 2; i++) { + let feedbackText = feedbacks.dog[i - 1]; + if (feedbackText) { + expect( + stateVariables[`/f${i}`].stateValues.feedbackText, + ).eq(feedbackText); + } else { + expect(stateVariables[`/f${i}`]).eq(undefined); + } + } + } else { + if (stateVariables["/ans"].stateValues.justSubmitted) { + expect( + stateVariables["/ans"].stateValues.creditAchieved, + ).eq(0); + } + expect(stateVariables["/f1"]).eq(undefined); + } + } + + const stateVariables = await returnAllStateVariables(core); + const choiceTexts: string[] = + stateVariables["/ci"].stateValues.choiceTexts; + + await check_items(); + + // Select correct answer + let selectedChoice = "dog"; + let selectedIndex = choiceTexts.indexOf(selectedChoice) + 1; + await updateSelectedIndices({ + name: "/ci", + selectedIndices: [selectedIndex], + core, + }); + await check_items(); + + // submit answer + await submitAnswer({ name: "/ans", core }); + let submittedChoice = selectedChoice; + await check_items(submittedChoice); + + // Select half correct answer + selectedChoice = "cat"; + selectedIndex = choiceTexts.indexOf(selectedChoice) + 1; + await updateSelectedIndices({ + name: "/ci", + selectedIndices: [selectedIndex], + core, + }); + await check_items(submittedChoice); + + // submit answer + await submitAnswer({ name: "/ans", core }); + submittedChoice = selectedChoice; + await check_items(submittedChoice); + + // Select incorrect answer + selectedChoice = "monkey"; + selectedIndex = choiceTexts.indexOf(selectedChoice) + 1; + await updateSelectedIndices({ + name: "/ci", + selectedIndices: [selectedIndex], + core, + }); + await check_items(submittedChoice); + + // submit answer + await submitAnswer({ name: "/ans", core }); + submittedChoice = selectedChoice; + await check_items(submittedChoice); + } + + it("feedback defined in choices", async () => { + let core = await createTestCore({ + doenetML: ` +

+ + + cat + dog + monkey + + +

+ +

Answer feedbacks:

+ $ans.feedbacks{assignNames="f1 f2"} + `, + }); + + let feedbacks = { + dog: ["Good job!"], + cat: ["meow"], + }; + + await test_feedback_defined_in_choices(core, feedbacks); + }); + + it("feedback defined in choices, new feedback definitions in document and section", async () => { + let core = await createTestCore({ + doenetML: ` + + + + + + + +
+ + + + + + + +

+ + + cat + dog + monkey + + +

+ +

Answer feedbacks:

+ $ans.feedbacks{assignNames="f1 f2"} + +
+ `, + }); + + let feedbacks = { + dog: ["Woof", "Grrr"], + cat: ["Meow"], + }; + + await test_feedback_defined_in_choices(core, feedbacks); + }); + + it("feedback updated with target", async () => { + let doenetML = ` + + + + $mi = x + + + +

You typed y!

+ `; + + let core = await createTestCore({ + doenetML, + }); + + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/fback"].stateValues.hidden).eq(true); + + await updateMathInputValue({ latex: "y", name: "/mi", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/fback"].stateValues.hidden).eq(true); + + await submitAnswer({ name: "/ans", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/ans"].stateValues.creditAchieved).eq(0); + expect(stateVariables["/fback"].stateValues.hidden).eq(false); + + await updateMathInputValue({ latex: "x", name: "/mi", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/fback"].stateValues.hidden).eq(false); + + await submitAnswer({ name: "/ans", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/ans"].stateValues.creditAchieved).eq(1); + expect(stateVariables["/fback"].stateValues.hidden).eq(true); + + core = await createTestCore({ + doenetML, + flags: { showFeedback: false }, + }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/fback"].stateValues.hidden).eq(true); + + await updateMathInputValue({ latex: "y", name: "/mi", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/fback"].stateValues.hidden).eq(true); + + await submitAnswer({ name: "/ans", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/ans"].stateValues.creditAchieved).eq(0); + expect(stateVariables["/fback"].stateValues.hidden).eq(true); + + await updateMathInputValue({ latex: "x", name: "/mi", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/fback"].stateValues.hidden).eq(true); + + await submitAnswer({ name: "/ans", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/ans"].stateValues.creditAchieved).eq(1); + expect(stateVariables["/fback"].stateValues.hidden).eq(true); + }); + + it("feedback based on booleans, updated with target", async () => { + let core = await createTestCore({ + doenetML: ` + + + $m1 = x + $m2 = y + + + $got1 and $got2 + + $m1 $m2 + + +

Submitted responses: $ans.submittedResponses{assignNames="r1 r2"}

+ + + Desired feedback behavior +

You got the first; what about the second?

+

You got the second; what about the first?

+
+ + Default feedback behavior +

You got the first; what about the second?

+

You got the second; what about the first?

+
+ + + `, + }); + + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/ans"].stateValues.creditAchieved).eq(0); + expect(stateVariables["/fback1"].stateValues.hidden).eq(true); + expect(stateVariables["/fback2"].stateValues.hidden).eq(true); + expect(stateVariables["/fback1b"].stateValues.hidden).eq(true); + expect(stateVariables["/fback2b"].stateValues.hidden).eq(true); + expect(stateVariables["/r1"]).eq(undefined); + expect(stateVariables["/r2"]).eq(undefined); + + await submitAnswer({ name: "/ans", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/ans"].stateValues.creditAchieved).eq(0); + expect(stateVariables["/fback1"].stateValues.hidden).eq(true); + expect(stateVariables["/fback2"].stateValues.hidden).eq(true); + expect(stateVariables["/fback1b"].stateValues.hidden).eq(true); + expect(stateVariables["/fback2b"].stateValues.hidden).eq(true); + expect(stateVariables["/r1"].stateValues.value.tree).eq("\uff3f"); + expect(stateVariables["/r2"].stateValues.value.tree).eq("\uff3f"); + + await updateMathInputValue({ latex: "y", name: "/m2", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/ans"].stateValues.creditAchieved).eq(0); + expect(stateVariables["/fback1"].stateValues.hidden).eq(true); + expect(stateVariables["/fback2"].stateValues.hidden).eq(true); + expect(stateVariables["/fback1b"].stateValues.hidden).eq(true); + expect(stateVariables["/fback2b"].stateValues.hidden).eq(false); + expect(stateVariables["/r1"].stateValues.value.tree).eq("\uff3f"); + expect(stateVariables["/r2"].stateValues.value.tree).eq("\uff3f"); + + await submitAnswer({ name: "/ans", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/ans"].stateValues.creditAchieved).eq(0); + expect(stateVariables["/fback1"].stateValues.hidden).eq(true); + expect(stateVariables["/fback2"].stateValues.hidden).eq(false); + expect(stateVariables["/fback1b"].stateValues.hidden).eq(true); + expect(stateVariables["/fback2b"].stateValues.hidden).eq(false); + expect(stateVariables["/r1"].stateValues.value.tree).eq("\uff3f"); + expect(stateVariables["/r2"].stateValues.value.tree).eq("y"); + + await updateMathInputValue({ latex: "x", name: "/m1", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/ans"].stateValues.creditAchieved).eq(0); + expect(stateVariables["/fback1"].stateValues.hidden).eq(true); + expect(stateVariables["/fback2"].stateValues.hidden).eq(false); + expect(stateVariables["/fback1b"].stateValues.hidden).eq(true); + expect(stateVariables["/fback2b"].stateValues.hidden).eq(true); + expect(stateVariables["/r1"].stateValues.value.tree).eq("\uff3f"); + expect(stateVariables["/r2"].stateValues.value.tree).eq("y"); + + await submitAnswer({ name: "/ans", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/ans"].stateValues.creditAchieved).eq(1); + expect(stateVariables["/fback1"].stateValues.hidden).eq(true); + expect(stateVariables["/fback2"].stateValues.hidden).eq(true); + expect(stateVariables["/fback1b"].stateValues.hidden).eq(true); + expect(stateVariables["/fback2b"].stateValues.hidden).eq(true); + expect(stateVariables["/r1"].stateValues.value.tree).eq("x"); + expect(stateVariables["/r2"].stateValues.value.tree).eq("y"); + + await updateMathInputValue({ latex: "", name: "/m2", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/ans"].stateValues.creditAchieved).eq(1); + expect(stateVariables["/fback1"].stateValues.hidden).eq(true); + expect(stateVariables["/fback2"].stateValues.hidden).eq(true); + expect(stateVariables["/fback1b"].stateValues.hidden).eq(false); + expect(stateVariables["/fback2b"].stateValues.hidden).eq(true); + expect(stateVariables["/r1"].stateValues.value.tree).eq("x"); + expect(stateVariables["/r2"].stateValues.value.tree).eq("y"); + + await submitAnswer({ name: "/ans", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/ans"].stateValues.creditAchieved).eq(0); + expect(stateVariables["/fback1"].stateValues.hidden).eq(false); + expect(stateVariables["/fback2"].stateValues.hidden).eq(true); + expect(stateVariables["/fback1b"].stateValues.hidden).eq(false); + expect(stateVariables["/fback2b"].stateValues.hidden).eq(true); + expect(stateVariables["/r1"].stateValues.value.tree).eq("x"); + expect(stateVariables["/r2"].stateValues.value.tree).eq("\uff3f"); + }); + + it("feedback based on fractionSatisfied/creditAchieved of award", async () => { + let core = await createTestCore({ + doenetML: ` +

+ + $mi1 < 1 and $mi2 < 1 + $mi1 < 2 and $mi2 < 2 + $mi1 < 3 and $mi2 < 3 +

+ +

A number or two is close but not quite.

+
+ +

One number is good, the other number is close but not quite.

+
+ +

A number or two is starting to get close.

+
+ +

A number is close but not quite; the other number is starting to get close.

+
+ +

One number is good, the other number is starting to get close.

+
+ +

One number is good.

+
+ `, + }); + + let feedbackNames = [ + "close", + "goodAndClose", + "startingClose", + "closeStartingClose", + "goodStartingClose", + "good", + ]; + + async function check_items( + credit: number, + justSubmitted: boolean, + feedbacksChosen: string[] = [], + ) { + const stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/ans"].stateValues.creditAchieved).eq( + credit, + ); + expect(stateVariables["/ans"].stateValues.justSubmitted).eq( + justSubmitted, + ); + + for (let fName of feedbackNames) { + let hidden = !feedbacksChosen.includes(fName); + expect(stateVariables[`/${fName}`].stateValues.hidden).eq( + hidden, + ); + } + } + + await submitAnswer({ name: "/ans", core }); + await check_items(0, true); + + await updateMathInputValue({ latex: "2", name: "/mi1", core }); + await check_items(0, false); + + await submitAnswer({ name: "/ans", core }); + await check_items(0, true, ["startingClose"]); + + await updateMathInputValue({ latex: "1", name: "/mi1", core }); + await check_items(0, false, ["startingClose"]); + + await submitAnswer({ name: "/ans", core }); + await check_items(0.25, true, ["close"]); + + await updateMathInputValue({ latex: "0", name: "/mi1", core }); + await check_items(0.25, false, ["close"]); + + await submitAnswer({ name: "/ans", core }); + await check_items(0.5, true, ["good"]); + + await updateMathInputValue({ latex: "2", name: "/mi2", core }); + await check_items(0.5, false, ["good"]); + + await submitAnswer({ name: "/ans", core }); + await check_items(0.5, true, ["goodStartingClose"]); + + await updateMathInputValue({ latex: "1", name: "/mi2", core }); + await check_items(0.5, false, ["goodStartingClose"]); + + await submitAnswer({ name: "/ans", core }); + await check_items(0.5, true, ["goodAndClose"]); + + await updateMathInputValue({ latex: "0", name: "/mi2", core }); + await check_items(0.5, false, ["goodAndClose"]); + + await submitAnswer({ name: "/ans", core }); + await check_items(1, true); + + await updateMathInputValue({ latex: "1", name: "/mi1", core }); + await updateMathInputValue({ latex: "1", name: "/mi2", core }); + await check_items(1, false); + + await submitAnswer({ name: "/ans", core }); + await check_items(0.5, true, ["close"]); + + await updateMathInputValue({ latex: "2", name: "/mi1", core }); + await check_items(0.5, false, ["close"]); + + await submitAnswer({ name: "/ans", core }); + await check_items(0.25, true, ["close", "closeStartingClose"]); + }); + + it("feedback with no condition", async () => { + let doenetML = ` +

Good job!

+ `; + + let core = await createTestCore({ + doenetML, + }); + + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/fback"].stateValues.hidden).eq(false); + + core = await createTestCore({ + doenetML, + flags: { showFeedback: false }, + }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/fback"].stateValues.hidden).eq(true); + }); + + it("feedback inside invalid children", async () => { + // The following DoenetML was a minimal working example to trigger a bug + // where the children of li1 where not being updated on the second submission + // (due to being marked stale from invalid children in the middle of the first + // time that they were being updated) + let core = await createTestCore({ + doenetML: ` + a +

+ +

    +
  • + x: x + You answered at least twice +
  • +
  • $ans

  • +
+

+ `, + }); + + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/fb"].stateValues.hidden).eq(true); + + await submitAnswer({ name: "/ans", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq("\uff3f"); + expect(stateVariables["/fb"].stateValues.hidden).eq(true); + + await updateMathInputValue({ latex: "y", name: "/mi", core }); + await submitAnswer({ name: "/ans", core }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p"].stateValues.text).eq("y"); + expect(stateVariables["/fb"].stateValues.hidden).eq(false); + }); + + it("feedback from numSubmissions", async () => { + let core = await createTestCore({ + doenetML: ` +

x

+ +

You answered more than once!

+
+ `, + }); + + let stateVariables = await returnAllStateVariables(core); + let mathInputName = + stateVariables["/ans"].stateValues.inputChildren[0].componentName; + + expect(stateVariables["/pSub"].stateValues.hidden).eq(true); + + // Submit first time + await updateMathInputValue({ latex: "x", name: mathInputName, core }); + await submitAnswer({ name: "/ans", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/ans"].stateValues.creditAchieved).eq(1); + expect(stateVariables["/pSub"].stateValues.hidden).eq(true); + + // Submit second time + await updateMathInputValue({ latex: "y", name: mathInputName, core }); + await submitAnswer({ name: "/ans", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/ans"].stateValues.creditAchieved).eq(0); + expect(stateVariables["/pSub"].stateValues.hidden).eq(false); + + // Submit third time + await updateMathInputValue({ latex: "x", name: mathInputName, core }); + await submitAnswer({ name: "/ans", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/ans"].stateValues.creditAchieved).eq(1); + expect(stateVariables["/pSub"].stateValues.hidden).eq(false); + }); +}); diff --git a/packages/doenetml-worker/src/test/tagSpecific/function.test.ts b/packages/doenetml-worker/src/test/tagSpecific/function.test.ts index b8a111023..96eef8081 100644 --- a/packages/doenetml-worker/src/test/tagSpecific/function.test.ts +++ b/packages/doenetml-worker/src/test/tagSpecific/function.test.ts @@ -1,12 +1,14 @@ import { describe, expect, it, vi } from "vitest"; import { createTestCore, returnAllStateVariables } from "../utils/test-core"; import { cleanLatex } from "../utils/math"; -import { updateMathInputValue } from "../utils/actions"; +import { movePoint, updateMathInputValue } from "../utils/actions"; import { createFunctionFromDefinition } from "@doenet/utils"; import me from "math-expressions"; +import Core from "../../Core"; const Mock = vi.fn(); vi.stubGlobal("postMessage", Mock); +vi.mock("hyperformula"); function constantFromAst(tree) { //@ts-ignore @@ -668,7 +670,7 @@ describe("Function tag tests", async () => { expect(f(-12)).closeTo(-1 + 0 * (-12 + 6), 1e-12); expect(f(12)).closeTo(-1 + 0 * (12 + 6), 1e-12); - await updateMathInputValue({ latex: "2", componentName: "/mi", core }); + await updateMathInputValue({ latex: "2", name: "/mi", core }); f = await core.components!["/f"].state.numericalf.value; expect(f(-6)).closeTo(-1, 1e-12); @@ -676,7 +678,7 @@ describe("Function tag tests", async () => { expect(f(-12)).closeTo(-1 + 2 * (-12 + 6), 1e-12); expect(f(12)).closeTo(-1 + 2 * (12 + 6), 1e-12); - await updateMathInputValue({ latex: "-3", componentName: "/mi", core }); + await updateMathInputValue({ latex: "-3", name: "/mi", core }); f = await core.components!["/f"].state.numericalf.value; expect(f(-6)).closeTo(-1, 1e-12); @@ -684,7 +686,7 @@ describe("Function tag tests", async () => { expect(f(-12)).closeTo(-1 - 3 * (-12 + 6), 1e-12); expect(f(12)).closeTo(-1 - 3 * (12 + 6), 1e-12); - await updateMathInputValue({ latex: "", componentName: "/mi", core }); + await updateMathInputValue({ latex: "", name: "/mi", core }); f = await core.components!["/f"].state.numericalf.value; expect(f(-6)).closeTo(-1, 1e-12); @@ -710,7 +712,7 @@ describe("Function tag tests", async () => { expect(f(-12)).closeTo(-1 + 1 * (-12 + 6), 1e-12); expect(f(12)).closeTo(-1 + 1 * (12 + 6), 1e-12); - await updateMathInputValue({ latex: "2", componentName: "/mi", core }); + await updateMathInputValue({ latex: "2", name: "/mi", core }); f = await core.components!["/f"].state.numericalf.value; expect(f(-6 - 0.01)).closeTo(-1 - 0.01 * 2, 1e-3); @@ -725,7 +727,7 @@ describe("Function tag tests", async () => { expect(f(-6 - 3)).closeTo(-1 - 3 * 2, 1e-12); expect(f(3 + 3)).closeTo(8 + 3 * 2, 1e-12); - await updateMathInputValue({ latex: "-3", componentName: "/mi", core }); + await updateMathInputValue({ latex: "-3", name: "/mi", core }); f = await core.components!["/f"].state.numericalf.value; expect(f(-6 - 0.01)).closeTo(-1 - 0.01 * -3, 1e-3); @@ -740,7 +742,7 @@ describe("Function tag tests", async () => { expect(f(-6 - 3)).closeTo(-1 - 3 * -3, 1e-12); expect(f(3 + 3)).closeTo(8 + 3 * -3, 1e-12); - await updateMathInputValue({ latex: "", componentName: "/mi", core }); + await updateMathInputValue({ latex: "", name: "/mi", core }); f = await core.components!["/f"].state.numericalf.value; expect(f(-6)).closeTo(-1, 1e-12); @@ -1075,12 +1077,7 @@ describe("Function tag tests", async () => { expect(6 - ((x - 5) * (x - 5)) / 25).closeTo(y, 1e-5); - await core.requestAction({ - actionName: "movePoint", - componentName: "/P", - args: { x: -8, y: 8 }, - event: null, - }); + await movePoint({ name: "/P", x: -8, y: 8, core }); stateVariables = await returnAllStateVariables(core); @@ -1093,12 +1090,7 @@ describe("Function tag tests", async () => { expect(6 - ((x - 5) * (x - 5)) / 25).closeTo(y, 1e-5); - await core.requestAction({ - actionName: "movePoint", - componentName: "/P", - args: { x: 8, y: 8 }, - event: null, - }); + await movePoint({ name: "/P", x: 8, y: 8, core }); stateVariables = await returnAllStateVariables(core); @@ -1137,12 +1129,7 @@ describe("Function tag tests", async () => { ); expect(constantFromAst(p.stateValues.xs[1])).closeTo(2, 1e-6); - await core.requestAction({ - actionName: "movePoint", - componentName: "/P", - args: { x: -2, y: 2 }, - event: null, - }); + await movePoint({ name: "/P", x: -2, y: 2, core }); stateVariables = await returnAllStateVariables(core); @@ -1176,12 +1163,7 @@ describe("Function tag tests", async () => { expect(x).closeTo(1, 1e-12); expect(6 - ((x - 5) * (x - 5)) / 25).closeTo(y, 1e-5); - await core.requestAction({ - actionName: "movePoint", - componentName: "/P", - args: { x: -8, y: 8 }, - event: null, - }); + await movePoint({ name: "/P", x: -8, y: 8, core }); stateVariables = await returnAllStateVariables(core); @@ -1193,12 +1175,7 @@ describe("Function tag tests", async () => { expect(x).closeTo(-4, 1e-12); expect(6 - ((x - 5) * (x - 5)) / 25).closeTo(y, 1e-5); - await core.requestAction({ - actionName: "movePoint", - componentName: "/P", - args: { x: 6, y: 8 }, - event: null, - }); + await movePoint({ name: "/P", x: 6, y: 8, core }); stateVariables = await returnAllStateVariables(core); @@ -1210,12 +1187,7 @@ describe("Function tag tests", async () => { expect(x).closeTo(6, 1e-12); expect(6 - (x - 5) * (x - 5) * (2 / 9)).closeTo(y, 1e-5); - await core.requestAction({ - actionName: "movePoint", - componentName: "/P", - args: { x: 8, y: -4 }, - event: null, - }); + await movePoint({ name: "/P", x: 8, y: -4, core }); stateVariables = await returnAllStateVariables(core); @@ -1252,12 +1224,7 @@ describe("Function tag tests", async () => { expect(x).closeTo(1, 1e-12); expect(6 - ((x - 5) * (x - 5)) / 25).closeTo(y, 1e-5); - await core.requestAction({ - actionName: "movePoint", - componentName: "/P", - args: { x: -8, y: 8 }, - event: null, - }); + await movePoint({ name: "/P", x: -8, y: 8, core }); stateVariables = await returnAllStateVariables(core); @@ -1270,12 +1237,7 @@ describe("Function tag tests", async () => { expect(x).lessThan(-4 + 1e-3); expect(6 - ((x - 5) * (x - 5)) / 25).closeTo(y, 1e-5); - await core.requestAction({ - actionName: "movePoint", - componentName: "/P", - args: { x: 6, y: 8 }, - event: null, - }); + await movePoint({ name: "/P", x: 6, y: 8, core }); stateVariables = await returnAllStateVariables(core); @@ -1287,12 +1249,7 @@ describe("Function tag tests", async () => { expect(x).closeTo(6, 1e-12); expect(6 - (x - 5) * (x - 5) * (2 / 9)).closeTo(y, 1e-5); - await core.requestAction({ - actionName: "movePoint", - componentName: "/P", - args: { x: 8, y: -4 }, - event: null, - }); + await movePoint({ name: "/P", x: 8, y: -4, core }); stateVariables = await returnAllStateVariables(core); @@ -1315,12 +1272,7 @@ describe("Function tag tests", async () => { expect(constantFromAst(p.stateValues.xs[0])).closeTo(1, 1e-6); expect(constantFromAst(p.stateValues.xs[1])).closeTo(f(1), 1e-6); - await core.requestAction({ - actionName: "movePoint", - componentName: "/P", - args: { x: -1, y: 8 }, - event: null, - }); + await movePoint({ name: "/P", x: -1, y: 8, core }); stateVariables = await returnAllStateVariables(core); @@ -1331,12 +1283,7 @@ describe("Function tag tests", async () => { expect(y).closeTo(f(x), 1e-6); - await core.requestAction({ - actionName: "movePoint", - componentName: "/P", - args: { x: 6, y: 8 }, - event: null, - }); + await movePoint({ name: "/P", x: 6, y: 8, core }); stateVariables = await returnAllStateVariables(core); @@ -1347,12 +1294,7 @@ describe("Function tag tests", async () => { expect(y).closeTo(f(x), 1e-6); - await core.requestAction({ - actionName: "movePoint", - componentName: "/P", - args: { x: 8, y: -4 }, - event: null, - }); + await movePoint({ name: "/P", x: 8, y: -4, core }); stateVariables = await returnAllStateVariables(core); @@ -1363,12 +1305,7 @@ describe("Function tag tests", async () => { expect(y).closeTo(f(x), 1e-6); - await core.requestAction({ - actionName: "movePoint", - componentName: "/P", - args: { x: -1, y: -6 }, - event: null, - }); + await movePoint({ name: "/P", x: -1, y: -6, core }); stateVariables = await returnAllStateVariables(core); @@ -1733,12 +1670,7 @@ describe("Function tag tests", async () => { expect(Math.log(2 * x)).closeTo(y, 1e-5); - await core.requestAction({ - actionName: "movePoint", - componentName: "/P", - args: { x: 8, y: 8 }, - event: null, - }); + await movePoint({ name: "/P", x: 8, y: 8, core }); stateVariables = await returnAllStateVariables(core); @@ -1749,12 +1681,7 @@ describe("Function tag tests", async () => { expect(Math.log(2 * x)).closeTo(y, 1e-5); - await core.requestAction({ - actionName: "movePoint", - componentName: "/P", - args: { x: -8, y: -8 }, - event: null, - }); + await movePoint({ name: "/P", x: -8, y: -8, core }); stateVariables = await returnAllStateVariables(core); @@ -1793,12 +1720,7 @@ describe("Function tag tests", async () => { expect(x).lessThan(0.1 + 1e-3); expect(Math.log(2 * x)).closeTo(y, 1e-5); - await core.requestAction({ - actionName: "movePoint", - componentName: "/P", - args: { x: 4, y: 6 }, - event: null, - }); + await movePoint({ name: "/P", x: 4, y: 6, core }); stateVariables = await returnAllStateVariables(core); @@ -1810,12 +1732,7 @@ describe("Function tag tests", async () => { expect(x).closeTo(4, 1e-12); expect(Math.log(2 * x)).closeTo(y, 1e-5); - await core.requestAction({ - actionName: "movePoint", - componentName: "/P", - args: { x: 8, y: 8 }, - event: null, - }); + await movePoint({ name: "/P", x: 8, y: 8, core }); stateVariables = await returnAllStateVariables(core); @@ -1827,12 +1744,7 @@ describe("Function tag tests", async () => { expect(x).closeTo(6, 1e-12); expect(Math.log(2 * x)).closeTo(y, 1e-5); - await core.requestAction({ - actionName: "movePoint", - componentName: "/P", - args: { x: -8, y: -8 }, - event: null, - }); + await movePoint({ name: "/P", x: -8, y: -8, core }); stateVariables = await returnAllStateVariables(core); @@ -1872,12 +1784,7 @@ describe("Function tag tests", async () => { expect(x).eq(0.1); expect(Math.log(2 * x)).closeTo(y, 1e-5); - await core.requestAction({ - actionName: "movePoint", - componentName: "/P", - args: { x: 4, y: 6 }, - event: null, - }); + await movePoint({ name: "/P", x: 4, y: 6, core }); stateVariables = await returnAllStateVariables(core); @@ -1889,12 +1796,7 @@ describe("Function tag tests", async () => { expect(x).closeTo(4, 1e-12); expect(Math.log(2 * x)).closeTo(y, 1e-5); - await core.requestAction({ - actionName: "movePoint", - componentName: "/P", - args: { x: 8, y: 8 }, - event: null, - }); + await movePoint({ name: "/P", x: 8, y: 8, core }); stateVariables = await returnAllStateVariables(core); @@ -1907,12 +1809,7 @@ describe("Function tag tests", async () => { expect(x).greaterThan(6 - 1e-3); expect(Math.log(2 * x)).closeTo(y, 1e-5); - await core.requestAction({ - actionName: "movePoint", - componentName: "/P", - args: { x: -8, y: -8 }, - event: null, - }); + await movePoint({ name: "/P", x: -8, y: -8, core }); stateVariables = await returnAllStateVariables(core); @@ -1939,7 +1836,7 @@ describe("Function tag tests", async () => { globalinfLocation, fName = "/f", }: { - core: any; + core: Core; maxima: number[][]; minima: number[][]; haveGlobalMax?: boolean; @@ -2075,12 +1972,7 @@ describe("Function tag tests", async () => { globalinf: -Infinity, }); - await core.requestAction({ - actionName: "movePoint", - componentName: "/P1", - args: { x: 2, y: 2 }, - event: null, - }); + await movePoint({ name: "/P1", x: 2, y: 2, core }); await check_extrema({ core, @@ -2096,12 +1988,7 @@ describe("Function tag tests", async () => { globalinf: -Infinity, }); - await core.requestAction({ - actionName: "movePoint", - componentName: "/P1", - args: { x: 3.6, y: 5.1 }, - event: null, - }); + await movePoint({ name: "/P1", x: 3.6, y: 5.1, core }); await check_extrema({ core, @@ -2119,12 +2006,7 @@ describe("Function tag tests", async () => { globalinf: -Infinity, }); - await core.requestAction({ - actionName: "movePoint", - componentName: "/P1", - args: { x: 8, y: 9 }, - event: null, - }); + await movePoint({ name: "/P1", x: 8, y: 9, core }); await check_extrema({ core, @@ -2142,21 +2024,11 @@ describe("Function tag tests", async () => { haveGlobalMin: true, }); - await core.requestAction({ - actionName: "movePoint", - componentName: "/P1", - args: { x: 5, y: 2 }, - event: null, - }); + await movePoint({ name: "/P1", x: 5, y: 2, core }); await check_extrema({ core, maxima: [], minima: [] }); - await core.requestAction({ - actionName: "movePoint", - componentName: "/P1", - args: { x: -9, y: 0 }, - event: null, - }); + await movePoint({ name: "/P1", x: -9, y: 0, core }); await check_extrema({ core, @@ -2174,12 +2046,7 @@ describe("Function tag tests", async () => { globalinf: -Infinity, }); - await core.requestAction({ - actionName: "movePoint", - componentName: "/P4", - args: { x: 8, y: 3 }, - event: null, - }); + await movePoint({ name: "/P4", x: 8, y: 3, core }); await check_extrema({ core, @@ -2190,12 +2057,7 @@ describe("Function tag tests", async () => { haveGlobalMin: true, }); - await core.requestAction({ - actionName: "movePoint", - componentName: "/P4", - args: { x: 8, y: 6 }, - event: null, - }); + await movePoint({ name: "/P4", x: 8, y: 6, core }); await check_extrema({ core, @@ -2240,12 +2102,12 @@ describe("Function tag tests", async () => { await updateMathInputValue({ latex: "-2", - componentName: "/xmin", + name: "/xmin", core, }); await updateMathInputValue({ latex: "4", - componentName: "/xmax", + name: "/xmax", core, }); @@ -2259,12 +2121,7 @@ describe("Function tag tests", async () => { haveGlobalMin: true, }); - await core.requestAction({ - actionName: "movePoint", - componentName: "/P1", - args: { x: 2, y: 2 }, - event: null, - }); + await movePoint({ name: "/P1", x: 2, y: 2, core }); await check_extrema({ core, @@ -2278,12 +2135,12 @@ describe("Function tag tests", async () => { await updateMathInputValue({ latex: "-6", - componentName: "/xmin", + name: "/xmin", core, }); await updateMathInputValue({ latex: "8", - componentName: "/xmax", + name: "/xmax", core, }); @@ -2302,12 +2159,7 @@ describe("Function tag tests", async () => { globalinf: -3, }); - await core.requestAction({ - actionName: "movePoint", - componentName: "/P1", - args: { x: 3.6, y: 5.1 }, - event: null, - }); + await movePoint({ name: "/P1", x: 3.6, y: 5.1, core }); await check_extrema({ core, @@ -2328,12 +2180,12 @@ describe("Function tag tests", async () => { await updateMathInputValue({ latex: "-1", - componentName: "/xmin", + name: "/xmin", core, }); await updateMathInputValue({ latex: "4", - componentName: "/xmax", + name: "/xmax", core, }); @@ -2379,12 +2231,12 @@ describe("Function tag tests", async () => { await updateMathInputValue({ latex: "-2", - componentName: "/xmin", + name: "/xmin", core, }); await updateMathInputValue({ latex: "4", - componentName: "/xmax", + name: "/xmax", core, }); @@ -2397,12 +2249,7 @@ describe("Function tag tests", async () => { haveGlobalMin: true, }); - await core.requestAction({ - actionName: "movePoint", - componentName: "/P1", - args: { x: 2, y: 2 }, - event: null, - }); + await movePoint({ name: "/P1", x: 2, y: 2, core }); await check_extrema({ core, @@ -2416,12 +2263,12 @@ describe("Function tag tests", async () => { await updateMathInputValue({ latex: "-6", - componentName: "/xmin", + name: "/xmin", core, }); await updateMathInputValue({ latex: "8", - componentName: "/xmax", + name: "/xmax", core, }); @@ -2441,12 +2288,7 @@ describe("Function tag tests", async () => { haveGlobalMin: true, }); - await core.requestAction({ - actionName: "movePoint", - componentName: "/P1", - args: { x: 3.6, y: 5.1 }, - event: null, - }); + await movePoint({ name: "/P1", x: 3.6, y: 5.1, core }); await check_extrema({ core, @@ -2468,12 +2310,12 @@ describe("Function tag tests", async () => { await updateMathInputValue({ latex: "-1", - componentName: "/xmin", + name: "/xmin", core, }); await updateMathInputValue({ latex: "4", - componentName: "/xmax", + name: "/xmax", core, }); @@ -2515,24 +2357,9 @@ describe("Function tag tests", async () => { // the first two points is past maximum of the domain // check for bug where this stopped looking for minima - await core.requestAction({ - actionName: "movePoint", - componentName: "/P1", - args: { x: 0, y: -0.35 }, - event: null, - }); - await core.requestAction({ - actionName: "movePoint", - componentName: "/P2", - args: { x: 1.8, y: -1.36 }, - event: null, - }); - await core.requestAction({ - actionName: "movePoint", - componentName: "/P5", - args: { x: 1, y: -0.866 }, - event: null, - }); + await movePoint({ name: "/P1", x: 0, y: -0.35, core }); + await movePoint({ name: "/P2", x: 1.8, y: -1.36, core }); + await movePoint({ name: "/P5", x: 1, y: -0.866, core }); await check_extrema({ core, @@ -2547,30 +2374,10 @@ describe("Function tag tests", async () => { // is past maximum of domain // check for bug where this stopped looking for maxima - await core.requestAction({ - actionName: "movePoint", - componentName: "/P1", - args: { x: 0, y: 0 }, - event: null, - }); - await core.requestAction({ - actionName: "movePoint", - componentName: "/P2", - args: { x: 2, y: 1.8 }, - event: null, - }); - await core.requestAction({ - actionName: "movePoint", - componentName: "/P3", - args: { x: 5, y: 4 }, - event: null, - }); - await core.requestAction({ - actionName: "movePoint", - componentName: "/P5", - args: { x: 8, y: -1 }, - event: null, - }); + await movePoint({ name: "/P1", x: 0, y: 0, core }); + await movePoint({ name: "/P2", x: 2, y: 1.8, core }); + await movePoint({ name: "/P3", x: 5, y: 4, core }); + await movePoint({ name: "/P5", x: 8, y: -1, core }); await check_extrema({ core, @@ -2585,24 +2392,9 @@ describe("Function tag tests", async () => { // the first two points is past maximum of the domain // check for bug where this stopped looking for maximum - await core.requestAction({ - actionName: "movePoint", - componentName: "/P1", - args: { x: 0, y: 0.35 }, - event: null, - }); - await core.requestAction({ - actionName: "movePoint", - componentName: "/P2", - args: { x: 1.8, y: 1.36 }, - event: null, - }); - await core.requestAction({ - actionName: "movePoint", - componentName: "/P5", - args: { x: 1, y: 0.866 }, - event: null, - }); + await movePoint({ name: "/P1", x: 0, y: 0.35, core }); + await movePoint({ name: "/P2", x: 1.8, y: 1.36, core }); + await movePoint({ name: "/P5", x: 1, y: 0.866, core }); await check_extrema({ core, @@ -2636,12 +2428,7 @@ describe("Function tag tests", async () => { haveGlobalMin: true, }); - await core.requestAction({ - actionName: "movePoint", - componentName: "/P2", - args: { x: 3, y: -1 }, - event: null, - }); + await movePoint({ name: "/P2", x: 3, y: -1, core }); await check_extrema({ core, @@ -2653,12 +2440,7 @@ describe("Function tag tests", async () => { haveGlobalMin: true, }); - await core.requestAction({ - actionName: "movePoint", - componentName: "/P1", - args: { x: 0, y: -1 }, - event: null, - }); + await movePoint({ name: "/P1", x: 0, y: -1, core }); await check_extrema({ core, @@ -2703,7 +2485,7 @@ describe("Function tag tests", async () => { let period = 10; await updateMathInputValue({ latex: `${period}`, - componentName: "/period", + name: "/period", core, }); @@ -2722,7 +2504,7 @@ describe("Function tag tests", async () => { period = 5; await updateMathInputValue({ latex: `${period}`, - componentName: "/period", + name: "/period", core, }); @@ -2777,7 +2559,7 @@ describe("Function tag tests", async () => { let period = 10; await updateMathInputValue({ latex: `${period}`, - componentName: "/period", + name: "/period", core, }); @@ -2797,12 +2579,12 @@ describe("Function tag tests", async () => { xmax = 25; await updateMathInputValue({ latex: `${xmin}`, - componentName: "/xmin", + name: "/xmin", core, }); await updateMathInputValue({ latex: `${xmax}`, - componentName: "/xmax", + name: "/xmax", core, }); @@ -2820,7 +2602,7 @@ describe("Function tag tests", async () => { period = 5; await updateMathInputValue({ latex: `${period}`, - componentName: "/period", + name: "/period", core, }); @@ -2839,12 +2621,12 @@ describe("Function tag tests", async () => { xmax = 9; await updateMathInputValue({ latex: `${xmin}`, - componentName: "/xmin", + name: "/xmin", core, }); await updateMathInputValue({ latex: `${xmax}`, - componentName: "/xmax", + name: "/xmax", core, }); @@ -3578,27 +3360,12 @@ describe("Function tag tests", async () => { await check_items({ numMaxf2: 2, numMinf2: 1, maxf1: [2, 1] }); - await core.requestAction({ - actionName: "movePoint", - componentName: "/P1", - args: { x: 2, y: 6 }, - event: null, - }); + await movePoint({ name: "/P1", x: 2, y: 6, core }); await check_items({ numMaxf2: 1, numMinf2: 0, maxf1: [1, 0] }); - await core.requestAction({ - actionName: "movePoint", - componentName: "/P2", - args: { x: 3, y: 7 }, - event: null, - }); - await core.requestAction({ - actionName: "movePoint", - componentName: "/P3", - args: { x: 9, y: 0 }, - event: null, - }); + await movePoint({ name: "/P2", x: 3, y: 7, core }); + await movePoint({ name: "/P3", x: 9, y: 0, core }); await check_items({ numMaxf2: 2, numMinf2: 2, maxf1: [2, 2] }); }); @@ -3628,8 +3395,8 @@ describe("Function tag tests", async () => { "_x^{3}+1", ); - await updateMathInputValue({ latex: "1", componentName: "/mi1", core }); - await updateMathInputValue({ latex: "2", componentName: "/mi2", core }); + await updateMathInputValue({ latex: "1", name: "/mi1", core }); + await updateMathInputValue({ latex: "2", name: "/mi2", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/p1"].stateValues.text).eq("1"); @@ -3644,8 +3411,8 @@ describe("Function tag tests", async () => { let fa = stateVariables["/f1a"].stateValues.numericalf; expect(fa(-2)).eq(2 * (-2) ** 3 + 1); - await updateMathInputValue({ latex: "3", componentName: "/mi1", core }); - await updateMathInputValue({ latex: "4", componentName: "/mi2", core }); + await updateMathInputValue({ latex: "3", name: "/mi1", core }); + await updateMathInputValue({ latex: "4", name: "/mi2", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/p1"].stateValues.text).eq("3"); @@ -3712,7 +3479,7 @@ describe("Function tag tests", async () => { fName: "/f1b", }); - await updateMathInputValue({ latex: "2", componentName: "/mi2", core }); + await updateMathInputValue({ latex: "2", name: "/mi2", core }); maxima = [[0, 1]]; minima = [ @@ -3752,7 +3519,7 @@ describe("Function tag tests", async () => { await updateMathInputValue({ latex: "-1", - componentName: "/mi1", + name: "/mi1", core, }); @@ -4855,7 +4622,7 @@ describe("Function tag tests", async () => { expect(cleanLatex(stateVariables["/ex11"].stateValues.latex)).eq("-5"); // set propIndex to 1 - await updateMathInputValue({ latex: "1", componentName: "/n", core }); + await updateMathInputValue({ latex: "1", name: "/n", core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/mn1"].stateValues.latex)).eq( diff --git a/packages/doenetml-worker/src/test/tagSpecific/functioniterates.test.ts b/packages/doenetml-worker/src/test/tagSpecific/functioniterates.test.ts index 20c9c7edd..a977e2f1f 100644 --- a/packages/doenetml-worker/src/test/tagSpecific/functioniterates.test.ts +++ b/packages/doenetml-worker/src/test/tagSpecific/functioniterates.test.ts @@ -6,6 +6,7 @@ import me from "math-expressions"; const Mock = vi.fn(); vi.stubGlobal("postMessage", Mock); +vi.mock("hyperformula"); describe("FunctionIterates tag tests", async () => { // TODO: test forceNumeric and forceSymbolic? @@ -38,11 +39,11 @@ describe("FunctionIterates tag tests", async () => { // change function, numIterates, and initial await updateMathInputValue({ latex: "bx^2", - componentName: "/fformula", + name: "/fformula", core, }); - await updateMathInputValue({ latex: "4", componentName: "/n", core }); - await updateMathInputValue({ latex: "w", componentName: "/u", core }); + await updateMathInputValue({ latex: "4", name: "/n", core }); + await updateMathInputValue({ latex: "w", name: "/u", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/l1"].stateValues.text).eq("f¹(u) = b w²"); @@ -51,7 +52,7 @@ describe("FunctionIterates tag tests", async () => { expect(stateVariables["/l4"].stateValues.text).eq("f⁴(u) = b¹⁵ w¹⁶"); // change variable - await updateMathInputValue({ latex: "y", componentName: "/x", core }); + await updateMathInputValue({ latex: "y", name: "/x", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/l1"].stateValues.text).eq("f¹(u) = b x²"); @@ -62,10 +63,10 @@ describe("FunctionIterates tag tests", async () => { // change function to match variable await updateMathInputValue({ latex: "y+q", - componentName: "/fformula", + name: "/fformula", core, }); - await updateMathInputValue({ latex: "5", componentName: "/n", core }); + await updateMathInputValue({ latex: "5", name: "/n", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/l1"].stateValues.text).eq("f¹(u) = q + w"); @@ -103,11 +104,11 @@ describe("FunctionIterates tag tests", async () => { // change function, numIterates, and initial await updateMathInputValue({ latex: "2x^2", - componentName: "/fformula", + name: "/fformula", core, }); - await updateMathInputValue({ latex: "4", componentName: "/n", core }); - await updateMathInputValue({ latex: "1/4", componentName: "/u", core }); + await updateMathInputValue({ latex: "4", name: "/n", core }); + await updateMathInputValue({ latex: "1/4", name: "/u", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/l1"].stateValues.text).eq("f¹(u) = 0.125"); @@ -120,7 +121,7 @@ describe("FunctionIterates tag tests", async () => { ); // change variable - await updateMathInputValue({ latex: "y", componentName: "/x", core }); + await updateMathInputValue({ latex: "y", name: "/x", core }); stateVariables = await returnAllStateVariables(core); @@ -134,10 +135,10 @@ describe("FunctionIterates tag tests", async () => { // change function to match variable await updateMathInputValue({ latex: "y+5", - componentName: "/fformula", + name: "/fformula", core, }); - await updateMathInputValue({ latex: "5", componentName: "/n", core }); + await updateMathInputValue({ latex: "5", name: "/n", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/l1"].stateValues.text).eq("f¹(u) = 5.25"); @@ -174,18 +175,18 @@ describe("FunctionIterates tag tests", async () => { await checkIterates({ a: 3, b: -2, c: 1, d: 4, u1: 2, u2: 1, n: 3 }); // change values - await updateMathInputValue({ latex: "q", componentName: "/x", core }); - await updateMathInputValue({ latex: "r", componentName: "/y", core }); - await updateMathInputValue({ latex: "-4", componentName: "/a", core }); - await updateMathInputValue({ latex: "7", componentName: "/b", core }); - await updateMathInputValue({ latex: "6", componentName: "/c", core }); - await updateMathInputValue({ latex: "-1", componentName: "/d", core }); + await updateMathInputValue({ latex: "q", name: "/x", core }); + await updateMathInputValue({ latex: "r", name: "/y", core }); + await updateMathInputValue({ latex: "-4", name: "/a", core }); + await updateMathInputValue({ latex: "7", name: "/b", core }); + await updateMathInputValue({ latex: "6", name: "/c", core }); + await updateMathInputValue({ latex: "-1", name: "/d", core }); await updateMathInputValue({ latex: "(-8,9)", - componentName: "/u", + name: "/u", core, }); - await updateMathInputValue({ latex: "5", componentName: "/n", core }); + await updateMathInputValue({ latex: "5", name: "/n", core }); await checkIterates({ a: -4, b: 7, c: 6, d: -1, u1: -8, u2: 9, n: 5 }); } @@ -282,7 +283,7 @@ describe("FunctionIterates tag tests", async () => { await updateMathInputValue({ latex: "(xy, x+yz, x-z)", - componentName: "/fformula", + name: "/fformula", core, }); @@ -306,7 +307,7 @@ describe("FunctionIterates tag tests", async () => { // add variable to function await updateMathInputValue({ latex: "x, y, z", - componentName: "/vars", + name: "/vars", core, }); @@ -330,7 +331,7 @@ describe("FunctionIterates tag tests", async () => { // add component to initial condition await updateMathInputValue({ latex: "(2,1,-4)", - componentName: "/u", + name: "/u", core, }); stateVariables = await returnAllStateVariables(core); @@ -384,7 +385,7 @@ describe("FunctionIterates tag tests", async () => { // non-numeric initial condition await updateMathInputValue({ latex: "(2,1a)", - componentName: "/u", + name: "/u", core, }); stateVariables = await returnAllStateVariables(core); @@ -408,12 +409,12 @@ describe("FunctionIterates tag tests", async () => { // add component to function await updateMathInputValue({ latex: "(2,1)", - componentName: "/u", + name: "/u", core, }); await updateMathInputValue({ latex: "(xy, x+yz, x-z)", - componentName: "/fformula", + name: "/fformula", core, }); stateVariables = await returnAllStateVariables(core); @@ -436,7 +437,7 @@ describe("FunctionIterates tag tests", async () => { // add variable to function await updateMathInputValue({ latex: "x, y, z", - componentName: "/vars", + name: "/vars", core, }); stateVariables = await returnAllStateVariables(core); @@ -459,7 +460,7 @@ describe("FunctionIterates tag tests", async () => { // add component to initial condition await updateMathInputValue({ latex: "(2,1,-4)", - componentName: "/u", + name: "/u", core, }); stateVariables = await returnAllStateVariables(core); diff --git a/packages/doenetml-worker/src/test/tagSpecific/functionoperators.test.ts b/packages/doenetml-worker/src/test/tagSpecific/functionoperators.test.ts index ffdec8ade..724e076de 100644 --- a/packages/doenetml-worker/src/test/tagSpecific/functionoperators.test.ts +++ b/packages/doenetml-worker/src/test/tagSpecific/functionoperators.test.ts @@ -1,10 +1,12 @@ import { describe, expect, it, vi } from "vitest"; import { createTestCore, returnAllStateVariables } from "../utils/test-core"; -import { updateMathInputValue } from "../utils/actions"; +import { movePoint, updateMathInputValue } from "../utils/actions"; import me from "math-expressions"; +import Core from "../../Core"; const Mock = vi.fn(); vi.stubGlobal("postMessage", Mock); +vi.mock("hyperformula"); describe("Function Operator tag tests", async () => { async function test_function_operator({ @@ -271,18 +273,8 @@ describe("Function Operator tag tests", async () => { x1 = -3; x2 = 5; - await core.requestAction({ - event: null, - actionName: "movePoint", - componentName: "/P1", - args: { x: x1, y: 11 }, - }); - await core.requestAction({ - event: null, - actionName: "movePoint", - componentName: "/P2", - args: { x: x2, y: -9 }, - }); + await movePoint({ name: "/P1", x: x1, y: 11, core }); + await movePoint({ name: "/P2", x: x2, y: -9, core }); await check_items({ a, b, c, x, x1, x2 }); @@ -291,37 +283,27 @@ describe("Function Operator tag tests", async () => { c = "e"; x = "q"; - await updateMathInputValue({ latex: a, componentName: "/a", core }); + await updateMathInputValue({ latex: a, name: "/a", core }); await updateMathInputValue({ latex: "\\pi", - componentName: "/b", + name: "/b", core, }); - await updateMathInputValue({ latex: c, componentName: "/c", core }); - await updateMathInputValue({ latex: x, componentName: "/x", core }); + await updateMathInputValue({ latex: c, name: "/c", core }); + await updateMathInputValue({ latex: x, name: "/x", core }); await check_items({ a, b, c, x, x1, x2 }); x1 = 9; x2 = -7; - await core.requestAction({ - event: null, - actionName: "movePoint", - componentName: "/P1", - args: { x: x1, y: -6 }, - }); - await core.requestAction({ - event: null, - actionName: "movePoint", - componentName: "/P2", - args: { x: x2, y: 0 }, - }); + await movePoint({ name: "/P1", x: x1, y: -6, core }); + await movePoint({ name: "/P2", x: x2, y: 0, core }); await check_items({ a, b, c, x, x1, x2 }); }); - async function checkd_list(core: any) { + async function checkd_list(core: Core) { const stateVariables = await returnAllStateVariables(core); expect( stateVariables["/d1"].stateValues.formula.equals(me.fromText("2x")), @@ -2894,7 +2876,7 @@ describe("Function Operator tag tests", async () => { c1 = 3; await updateMathInputValue({ latex: c1.toString(), - componentName: "/c_1", + name: "/c_1", core, }); await verifyExtrema(c1, c2, c3, c4, c5); @@ -2902,7 +2884,7 @@ describe("Function Operator tag tests", async () => { c2 = -5; await updateMathInputValue({ latex: c2.toString(), - componentName: "/c_2", + name: "/c_2", core, }); await verifyExtrema(c1, c2, c3, c4, c5); @@ -2910,7 +2892,7 @@ describe("Function Operator tag tests", async () => { c3 = 1; await updateMathInputValue({ latex: c3.toString(), - componentName: "/c_3", + name: "/c_3", core, }); await verifyExtrema(c1, c2, c3, c4, c5); @@ -2918,7 +2900,7 @@ describe("Function Operator tag tests", async () => { c4 = -6; await updateMathInputValue({ latex: c4.toString(), - componentName: "/c_4", + name: "/c_4", core, }); await verifyExtrema(c1, c2, c3, c4, c5); @@ -2926,7 +2908,7 @@ describe("Function Operator tag tests", async () => { c5 = 3; await updateMathInputValue({ latex: c5.toString(), - componentName: "/c_5", + name: "/c_5", core, }); await verifyExtrema(c1, c2, c3, c4, c5); @@ -2934,7 +2916,7 @@ describe("Function Operator tag tests", async () => { c6 = 2; await updateMathInputValue({ latex: c6.toString(), - componentName: "/c_6", + name: "/c_6", core, }); await verifyExtrema(c1, c2, c3, c4, c5); @@ -2942,7 +2924,7 @@ describe("Function Operator tag tests", async () => { v = "y"; await updateMathInputValue({ latex: v, - componentName: "/x", + name: "/x", core, }); await verifyExtrema(c1, c2, c3, c4, c5); @@ -2950,7 +2932,7 @@ describe("Function Operator tag tests", async () => { c1 = 2; await updateMathInputValue({ latex: c1.toString(), - componentName: "/c_1", + name: "/c_1", core, }); await verifyExtrema(c1, c2, c3, c4, c5); @@ -2958,7 +2940,7 @@ describe("Function Operator tag tests", async () => { c2 = 4; await updateMathInputValue({ latex: c2.toString(), - componentName: "/c_2", + name: "/c_2", core, }); await verifyExtrema(c1, c2, c3, c4, c5); @@ -2966,7 +2948,7 @@ describe("Function Operator tag tests", async () => { c3 = -8; await updateMathInputValue({ latex: c3.toString(), - componentName: "/c_3", + name: "/c_3", core, }); await verifyExtrema(c1, c2, c3, c4, c5); @@ -2974,7 +2956,7 @@ describe("Function Operator tag tests", async () => { c4 = 9; await updateMathInputValue({ latex: c4.toString(), - componentName: "/c_4", + name: "/c_4", core, }); await verifyExtrema(c1, c2, c3, c4, c5); @@ -2982,7 +2964,7 @@ describe("Function Operator tag tests", async () => { c5 = -2; await updateMathInputValue({ latex: c5.toString(), - componentName: "/c_5", + name: "/c_5", core, }); await verifyExtrema(c1, c2, c3, c4, c5); @@ -2990,7 +2972,7 @@ describe("Function Operator tag tests", async () => { c6 = 6; await updateMathInputValue({ latex: c6.toString(), - componentName: "/c_6", + name: "/c_6", core, }); await verifyExtrema(c1, c2, c3, c4, c5); @@ -2998,7 +2980,7 @@ describe("Function Operator tag tests", async () => { v = "q"; await updateMathInputValue({ latex: v, - componentName: "/x", + name: "/x", core, }); await verifyExtrema(c1, c2, c3, c4, c5); diff --git a/packages/doenetml-worker/src/test/tagSpecific/graph.test.ts b/packages/doenetml-worker/src/test/tagSpecific/graph.test.ts index d6f25553e..cb32d470c 100644 --- a/packages/doenetml-worker/src/test/tagSpecific/graph.test.ts +++ b/packages/doenetml-worker/src/test/tagSpecific/graph.test.ts @@ -1,14 +1,19 @@ import { describe, expect, it, vi } from "vitest"; import { createTestCore, returnAllStateVariables } from "../utils/test-core"; import { + submitAnswer, updateBooleanInputValue, updateMathInputValue, + updateSelectedIndices, updateTextInputValue, + updateValue, } from "../utils/actions"; import { widthsBySize } from "@doenet/utils"; +import Core from "../../Core"; const Mock = vi.fn(); vi.stubGlobal("postMessage", Mock); +vi.mock("hyperformula"); describe("Graph tag tests", async () => { it("functions adapted to curves in graph", async () => { @@ -86,32 +91,29 @@ describe("Graph tag tests", async () => { await updateTextInputValue({ text: "hello", - componentName: "/xlabel", + name: "/xlabel", core, }); await updateTextInputValue({ text: "bye", - componentName: "/ylabel", + name: "/ylabel", core, }); - await core.requestAction({ - componentName: "/xlabelpos", - actionName: "updateSelectedIndices", - args: { selectedIndices: [1] }, - event: null, + await updateSelectedIndices({ + name: "/xlabelpos", + selectedIndices: [1], + core, }); - await core.requestAction({ - componentName: "/ylabelpos", - actionName: "updateSelectedIndices", - args: { selectedIndices: [2] }, - event: null, + await updateSelectedIndices({ + name: "/ylabelpos", + selectedIndices: [2], + core, }); - await core.requestAction({ - componentName: "/ylabelalign", - actionName: "updateSelectedIndices", - args: { selectedIndices: [2] }, - event: null, + await updateSelectedIndices({ + name: "/ylabelalign", + selectedIndices: [2], + core, }); stateVariables = await returnAllStateVariables(core); @@ -136,18 +138,8 @@ describe("Graph tag tests", async () => { expect(stateVariables["/g"].stateValues.xlabel).eq(""); expect(stateVariables["/g"].stateValues.ylabel).eq(""); - await core.requestAction({ - componentName: "/uvx", - actionName: "updateValue", - args: {}, - event: null, - }); - await core.requestAction({ - componentName: "/uvy", - actionName: "updateValue", - args: {}, - event: null, - }); + await updateValue({ name: "/uvx", core }); + await updateValue({ name: "/uvy", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/g"].stateValues.xlabel).eq("s"); @@ -163,7 +155,7 @@ describe("Graph tag tests", async () => { }; async function checkLimits( - core: any, + core: Core, { xmin, xmax, ymin, ymax }: AxisLimits, ) { const stateVariables = await returnAllStateVariables(core); @@ -195,14 +187,14 @@ describe("Graph tag tests", async () => { await updateMathInputValue({ latex: "2", - componentName: "/aspectRatio", + name: "/aspectRatio", core, }); await checkLimits(core, ratio2); await updateMathInputValue({ latex: "1/2", - componentName: "/aspectRatio", + name: "/aspectRatio", core, }); await checkLimits(core, ratio05); @@ -328,7 +320,7 @@ describe("Graph tag tests", async () => { // set xmin to -5 await updateMathInputValue({ latex: "-5", - componentName: "/xminInput", + name: "/xminInput", core, }); await checkLimits(-5, 10, -10, 10); @@ -336,7 +328,7 @@ describe("Graph tag tests", async () => { // set ymax to 0 await updateMathInputValue({ latex: "0", - componentName: "/ymaxInput", + name: "/ymaxInput", core, }); await checkLimits(-5, 10, -10, 0); @@ -387,7 +379,7 @@ describe("Graph tag tests", async () => { await updateBooleanInputValue({ boolean: true, - componentName: "/bi", + name: "/bi", core, }); stateVariables = await returnAllStateVariables(core); @@ -395,7 +387,7 @@ describe("Graph tag tests", async () => { await updateTextInputValue({ text: "true", - componentName: "/ti", + name: "/ti", core, }); stateVariables = await returnAllStateVariables(core); @@ -403,7 +395,7 @@ describe("Graph tag tests", async () => { await updateTextInputValue({ text: "false", - componentName: "/ti", + name: "/ti", core, }); stateVariables = await returnAllStateVariables(core); @@ -411,7 +403,7 @@ describe("Graph tag tests", async () => { await updateTextInputValue({ text: "dense", - componentName: "/ti", + name: "/ti", core, }); stateVariables = await returnAllStateVariables(core); @@ -419,7 +411,7 @@ describe("Graph tag tests", async () => { await updateTextInputValue({ text: "hello", - componentName: "/ti", + name: "/ti", core, }); stateVariables = await returnAllStateVariables(core); @@ -427,7 +419,7 @@ describe("Graph tag tests", async () => { await updateTextInputValue({ text: "medium", - componentName: "/ti", + name: "/ti", core, }); stateVariables = await returnAllStateVariables(core); @@ -435,7 +427,7 @@ describe("Graph tag tests", async () => { await updateTextInputValue({ text: "none", - componentName: "/ti", + name: "/ti", core, }); stateVariables = await returnAllStateVariables(core); @@ -482,28 +474,28 @@ describe("Graph tag tests", async () => { expect(stateVariables["/sg4"].stateValues.numbers).eqls([2, 3]); expect(stateVariables["/sg5"].stateValues.numbers).eqls([2, 3]); - await updateMathInputValue({ latex: "3", componentName: "/g2x", core }); + await updateMathInputValue({ latex: "3", name: "/g2x", core }); await updateMathInputValue({ latex: "1.5", - componentName: "/g2y", + name: "/g2y", core, }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/sg2"].stateValues.numbers).eqls([3, 1.5]); - await updateMathInputValue({ latex: "3", componentName: "/g3x", core }); + await updateMathInputValue({ latex: "3", name: "/g3x", core }); await updateMathInputValue({ latex: "1.5", - componentName: "/g3y", + name: "/g3y", core, }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/sg3"].stateValues.numbers).eqls([3, 1.5]); - await updateMathInputValue({ latex: "3", componentName: "/g4x", core }); + await updateMathInputValue({ latex: "3", name: "/g4x", core }); await updateMathInputValue({ latex: "1.5", - componentName: "/g4y", + name: "/g4y", core, }); stateVariables = await returnAllStateVariables(core); @@ -512,10 +504,10 @@ describe("Graph tag tests", async () => { 1.5 * 3, ]); - await updateMathInputValue({ latex: "3", componentName: "/g5x", core }); + await updateMathInputValue({ latex: "3", name: "/g5x", core }); await updateMathInputValue({ latex: "1.5", - componentName: "/g5y", + name: "/g5y", core, }); stateVariables = await returnAllStateVariables(core); @@ -526,12 +518,12 @@ describe("Graph tag tests", async () => { await updateMathInputValue({ latex: "3e/2", - componentName: "/g2x", + name: "/g2x", core, }); await updateMathInputValue({ latex: "1.5\\pi", - componentName: "/g2y", + name: "/g2y", core, }); stateVariables = await returnAllStateVariables(core); @@ -542,12 +534,12 @@ describe("Graph tag tests", async () => { await updateMathInputValue({ latex: "3e/2", - componentName: "/g3x", + name: "/g3x", core, }); await updateMathInputValue({ latex: "1.5\\pi", - componentName: "/g3y", + name: "/g3y", core, }); stateVariables = await returnAllStateVariables(core); @@ -558,12 +550,12 @@ describe("Graph tag tests", async () => { await updateMathInputValue({ latex: "3\\pi/5", - componentName: "/g4x", + name: "/g4x", core, }); await updateMathInputValue({ latex: "1.5e/6", - componentName: "/g4y", + name: "/g4y", core, }); stateVariables = await returnAllStateVariables(core); @@ -574,12 +566,12 @@ describe("Graph tag tests", async () => { await updateMathInputValue({ latex: "3\\pi/5", - componentName: "/g5x", + name: "/g5x", core, }); await updateMathInputValue({ latex: "1.5e/6", - componentName: "/g5y", + name: "/g5y", core, }); stateVariables = await returnAllStateVariables(core); @@ -639,15 +631,10 @@ describe("Graph tag tests", async () => { await updateMathInputValue({ latex: "x", - componentName: mathinputName, + name: mathinputName, core, }); - await core.requestAction({ - componentName: "/x", - actionName: "submitAnswer", - args: {}, - event: null, - }); + await submitAnswer({ name: "/x", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/graph1"].stateValues.xlabel).eq("\\(x\\)"); @@ -675,7 +662,7 @@ describe("Graph tag tests", async () => { await updateBooleanInputValue({ boolean: true, - componentName: "/b1", + name: "/b1", core, }); stateVariables = await returnAllStateVariables(core); @@ -688,7 +675,7 @@ describe("Graph tag tests", async () => { await updateBooleanInputValue({ boolean: false, - componentName: "/b2", + name: "/b2", core, }); stateVariables = await returnAllStateVariables(core); @@ -701,7 +688,7 @@ describe("Graph tag tests", async () => { await updateBooleanInputValue({ boolean: false, - componentName: "/b1", + name: "/b1", core, }); stateVariables = await returnAllStateVariables(core); @@ -714,7 +701,7 @@ describe("Graph tag tests", async () => { await updateBooleanInputValue({ boolean: true, - componentName: "/b2", + name: "/b2", core, }); stateVariables = await returnAllStateVariables(core); @@ -875,7 +862,7 @@ describe("Graph tag tests", async () => { await updateBooleanInputValue({ boolean: true, - componentName: "/b1", + name: "/b1", core, }); @@ -885,7 +872,7 @@ describe("Graph tag tests", async () => { await updateBooleanInputValue({ boolean: false, - componentName: "/b2", + name: "/b2", core, }); @@ -895,7 +882,7 @@ describe("Graph tag tests", async () => { await updateBooleanInputValue({ boolean: false, - componentName: "/b1", + name: "/b1", core, }); stateVariables = await returnAllStateVariables(core); @@ -904,7 +891,7 @@ describe("Graph tag tests", async () => { await updateBooleanInputValue({ boolean: true, - componentName: "/b2", + name: "/b2", core, }); @@ -928,7 +915,7 @@ describe("Graph tag tests", async () => { await updateBooleanInputValue({ boolean: true, - componentName: "/b", + name: "/b", core, }); @@ -937,7 +924,7 @@ describe("Graph tag tests", async () => { await updateBooleanInputValue({ boolean: false, - componentName: "/b", + name: "/b", core, }); diff --git a/packages/doenetml-worker/src/test/tagSpecific/group.test.ts b/packages/doenetml-worker/src/test/tagSpecific/group.test.ts new file mode 100644 index 000000000..9de08ce52 --- /dev/null +++ b/packages/doenetml-worker/src/test/tagSpecific/group.test.ts @@ -0,0 +1,544 @@ +import { describe, expect, it, vi } from "vitest"; +import { createTestCore, returnAllStateVariables } from "../utils/test-core"; +import { cleanLatex } from "../utils/math"; +import { + moveMath, + movePoint, + moveVector, + updateBooleanInputValue, + updateMathInputValue, + updateMatrixInputValue, + updateSelectedIndices, + updateTextInputValue, + updateValue, +} from "../utils/actions"; +import Core from "../../Core"; + +const Mock = vi.fn(); +vi.stubGlobal("postMessage", Mock); +vi.mock("hyperformula"); + +function isUndefinedOrInactive(comp) { + expect( + comp === undefined || comp.stateValues.isInactiveCompositeReplacement, + ).eq(true); +} + +describe("Group tag tests", async () => { + async function test_nested_groups(core: Core) { + let animal = "fox"; + let plant = "tree"; + let animalSentence = "The animal is a " + animal + "."; + let plantSentence = "The plant is a " + plant + "."; + + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/animalp"].stateValues.text).eq(animalSentence); + expect(stateVariables["/plantp"].stateValues.text).eq(plantSentence); + expect(stateVariables["/animalp2"].stateValues.text).eq(animalSentence); + expect(stateVariables["/plantp2"].stateValues.text).eq(plantSentence); + expect(stateVariables["/plantp3"].stateValues.text).eq(plantSentence); + expect(stateVariables["/plantp4"].stateValues.text).eq(plantSentence); + expect(stateVariables["/animalp3"].stateValues.text).eq(animalSentence); + expect(stateVariables["/plantp5"].stateValues.text).eq(plantSentence); + expect(stateVariables["/plantp6"].stateValues.text).eq(plantSentence); + expect(stateVariables["/animalp4"].stateValues.text).eq(animalSentence); + expect(stateVariables["/plantp7"].stateValues.text).eq(plantSentence); + expect(stateVariables["/animalp5"].stateValues.text).eq(animalSentence); + expect(stateVariables["/plantp8"].stateValues.text).eq(plantSentence); + expect(stateVariables["/plantp9"].stateValues.text).eq(plantSentence); + expect(stateVariables["/plantp10"].stateValues.text).eq(plantSentence); + expect(stateVariables["/animalp6"].stateValues.text).eq(animalSentence); + expect(stateVariables["/plantp11"].stateValues.text).eq(plantSentence); + expect(stateVariables["/plantp12"].stateValues.text).eq(plantSentence); + + await updateTextInputValue({ text: "beetle", name: "/animal", core }); + await updateTextInputValue({ text: "dandelion", name: "/plant", core }); + + let animal2 = "beetle"; + let plant2 = "dandelion"; + let animalSentence2 = "The animal is a " + animal2 + "."; + let plantSentence2 = "The plant is a " + plant2 + "."; + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/animalp"].stateValues.text).eq(animalSentence2); + expect(stateVariables["/plantp"].stateValues.text).eq(plantSentence2); + expect(stateVariables["/animalp2"].stateValues.text).eq( + animalSentence2, + ); + expect(stateVariables["/plantp2"].stateValues.text).eq(plantSentence2); + expect(stateVariables["/plantp3"].stateValues.text).eq(plantSentence2); + expect(stateVariables["/plantp4"].stateValues.text).eq(plantSentence2); + expect(stateVariables["/animalp3"].stateValues.text).eq( + animalSentence2, + ); + expect(stateVariables["/plantp5"].stateValues.text).eq(plantSentence2); + expect(stateVariables["/plantp6"].stateValues.text).eq(plantSentence2); + expect(stateVariables["/animalp4"].stateValues.text).eq( + animalSentence2, + ); + expect(stateVariables["/plantp7"].stateValues.text).eq(plantSentence2); + expect(stateVariables["/animalp5"].stateValues.text).eq( + animalSentence2, + ); + expect(stateVariables["/plantp8"].stateValues.text).eq(plantSentence2); + expect(stateVariables["/plantp9"].stateValues.text).eq(plantSentence2); + expect(stateVariables["/plantp10"].stateValues.text).eq(plantSentence2); + expect(stateVariables["/animalp6"].stateValues.text).eq( + animalSentence2, + ); + expect(stateVariables["/plantp11"].stateValues.text).eq(plantSentence2); + expect(stateVariables["/plantp12"].stateValues.text).eq(plantSentence2); + } + it("nested groups, copied", async () => { + let core = await createTestCore({ + doenetML: ` +

Animal:

+

Plant:

+ + +

The animal is a $animal.value.

+ +

The plant is a $plant.value.

+

+ +

+ + + + + + + `, + }); + + await test_nested_groups(core); + }); + + it("nested groups, initially unresolved, reffed", async () => { + let core = await createTestCore({ + doenetML: ` + + $g1 + +

The animal $animalphrase.

+ +

The plant $plantphrase.

+

+ +

+ + + + + + + + $verb1{name="verb"} + $animalphrase1{name="animalphrase"} + $verb $animal1 + $article $animal.value + $verb2{name="verb1"} + is + $article1 + $article2{name="article1"} + a + $plantphrase1{name="plantphrase"} + $verb $plant1 + $article $plant.value + +

Animal:

+

Plant:

+ + `, + }); + + await test_nested_groups(core); + }); + + it("group with a map that begins zero length, copied multiple times", async () => { + let core = await createTestCore({ + doenetML: ` +

+ + + + +

+ + + + + +

+

+ +

+

+

+ + `, + }); + + async function check_items(count: number, from: number, to: number) { + const dx = count > 1 ? (to - from) / (count - 1) : 0; + const sequence_text = [...Array(count).keys()] + .map((i) => (from + i * dx) ** 2) + .join(", "); + + const stateVariables = await returnAllStateVariables(core); + for (let i = 1; i <= 6; i++) { + expect(stateVariables[`/p${i}`].stateValues.text).eq( + sequence_text, + ); + } + } + + let count = 0, + from = 1, + to = 2; + + // At beginning, nothing shown + await check_items(count, from, to); + + // make sequence length 1 + count = 1; + await updateMathInputValue({ + latex: count.toString(), + name: "/count", + core, + }); + await check_items(count, from, to); + + // make sequence length 0 again + count = 0; + await updateMathInputValue({ + latex: count.toString(), + name: "/count", + core, + }); + await check_items(count, from, to); + + // make sequence length 2 + count = 2; + await updateMathInputValue({ + latex: count.toString(), + name: "/count", + core, + }); + await check_items(count, from, to); + + // change limits + from = 3; + to = 5; + await updateMathInputValue({ + latex: from.toString(), + name: "/from", + core, + }); + await updateMathInputValue({ latex: to.toString(), name: "/to", core }); + await check_items(count, from, to); + + // make sequence length 0 once again + count = 0; + await updateMathInputValue({ + latex: count.toString(), + name: "/count", + core, + }); + await check_items(count, from, to); + + // make sequence length 3 + count = 3; + await updateMathInputValue({ + latex: count.toString(), + name: "/count", + core, + }); + await check_items(count, from, to); + }); + + it("group with mutual references", async () => { + let core = await createTestCore({ + doenetML: ` +

+ $var1 + $y + $var2 + $x +

+ + + + +

$group1$group2

+

$al1

+ +

+

+

+ +

+

+

+ + $var2.value{assignNames="var2b"} + + `, + }); + + async function check_items(var1: string, var2: string) { + let text = `${var1} + ${var2}`; + text = `${text}, ${text}`; + const stateVariables = await returnAllStateVariables(core); + for (let i = 1; i <= 9; i++) { + expect(stateVariables[`/p${i}`].stateValues.text).eq(text); + } + } + + let var1 = "x", + var2 = "y"; + await check_items(var1, var2); + + // change variables + var1 = "u"; + var2 = "v"; + await updateMathInputValue({ latex: var1, name: "/var1", core }); + await updateMathInputValue({ latex: var2, name: "/var2", core }); + await check_items(var1, var2); + }); + + it("fixed propagated when copy group", async () => { + let core = await createTestCore({ + doenetML: ` + + + (1,2) + + + + + + + + + + + + + + + + `, + }); + + // Initial values + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/g"].stateValues.fixed).eq(false); + expect(stateVariables["/g/A"].stateValues.fixed).eq(false); + expect(stateVariables["/g/A"].stateValues.xs.map((x) => x.tree)).eqls([ + 1, 2, + ]); + expect(stateVariables["/g2"].stateValues.fixed).eq(true); + expect(stateVariables["/g2/A"].stateValues.fixed).eq(true); + expect(stateVariables["/g2/A"].stateValues.xs.map((x) => x.tree)).eqls([ + 1, 2, + ]); + expect(stateVariables["/g3"].stateValues.fixed).eq(false); + expect(stateVariables["/g3/A"].stateValues.fixed).eq(false); + expect(stateVariables["/g3/A"].stateValues.xs.map((x) => x.tree)).eqls([ + 1, 2, + ]); + expect(stateVariables["/g4"].stateValues.fixed).eq(false); + expect(stateVariables["/g4/A"].stateValues.fixed).eq(false); + expect(stateVariables["/g4/A"].stateValues.xs.map((x) => x.tree)).eqls([ + 1, 2, + ]); + + // move first point + await movePoint({ name: "/g/A", x: 3, y: 4, core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/g/A"].stateValues.xs.map((x) => x.tree)).eqls([ + 3, 4, + ]); + expect(stateVariables["/g2/A"].stateValues.xs.map((x) => x.tree)).eqls([ + 3, 4, + ]); + expect(stateVariables["/g3/A"].stateValues.xs.map((x) => x.tree)).eqls([ + 3, 4, + ]); + expect(stateVariables["/g4/A"].stateValues.xs.map((x) => x.tree)).eqls([ + 1, 2, + ]); + + // can't move second point as fixed + await movePoint({ name: "/g2/A", x: 5, y: 6, core }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/g/A"].stateValues.xs.map((x) => x.tree)).eqls([ + 3, 4, + ]); + expect(stateVariables["/g2/A"].stateValues.xs.map((x) => x.tree)).eqls([ + 3, 4, + ]); + expect(stateVariables["/g3/A"].stateValues.xs.map((x) => x.tree)).eqls([ + 3, 4, + ]); + expect(stateVariables["/g4/A"].stateValues.xs.map((x) => x.tree)).eqls([ + 1, 2, + ]); + + // TODO: this used to be immobile but not it is + // Do we need to figure out how to make third point immobile again? + // Comment is supposed to be: can't move third point as depends on fixed second point + + // for now, can move third point as depends on directly on xs of first point + await movePoint({ name: "/g3/A", x: 7, y: 8, core }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/g/A"].stateValues.xs.map((x) => x.tree)).eqls([ + 7, 8, + ]); + expect(stateVariables["/g2/A"].stateValues.xs.map((x) => x.tree)).eqls([ + 7, 8, + ]); + expect(stateVariables["/g3/A"].stateValues.xs.map((x) => x.tree)).eqls([ + 7, 8, + ]); + expect(stateVariables["/g4/A"].stateValues.xs.map((x) => x.tree)).eqls([ + 1, 2, + ]); + + // can move fourth point + await movePoint({ name: "/g4/A", x: 9, y: 0, core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/g/A"].stateValues.xs.map((x) => x.tree)).eqls([ + 7, 8, + ]); + expect(stateVariables["/g2/A"].stateValues.xs.map((x) => x.tree)).eqls([ + 7, 8, + ]); + expect(stateVariables["/g3/A"].stateValues.xs.map((x) => x.tree)).eqls([ + 7, 8, + ]); + expect(stateVariables["/g4/A"].stateValues.xs.map((x) => x.tree)).eqls([ + 9, 0, + ]); + }); + + it("disabled propagated when copy group", async () => { + let core = await createTestCore({ + doenetML: ` + + + $ti.value{assignNames="t"} + + + + + + + + + `, + }); + + // Initial values + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/g"].stateValues.disabled).eq(false); + expect(stateVariables["/g/ti"].stateValues.disabled).eq(false); + expect(stateVariables["/g/ti"].stateValues.value).eq("hello"); + expect(stateVariables["/g2"].stateValues.disabled).eq(true); + expect(stateVariables["/g2/ti"].stateValues.disabled).eq(true); + expect(stateVariables["/g2/ti"].stateValues.value).eq("hello"); + expect(stateVariables["/g3"].stateValues.disabled).eq(false); + expect(stateVariables["/g3/ti"].stateValues.disabled).eq(false); + expect(stateVariables["/g3/ti"].stateValues.value).eq("hello"); + expect(stateVariables["/g4"].stateValues.disabled).eq(false); + expect(stateVariables["/g4/ti"].stateValues.disabled).eq(false); + expect(stateVariables["/g4/ti"].stateValues.value).eq("hello"); + + // type in first textInput + await updateTextInputValue({ text: "bye", name: "/g/ti", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/g/ti"].stateValues.value).eq("bye"); + expect(stateVariables["/g2/ti"].stateValues.value).eq("bye"); + expect(stateVariables["/g3/ti"].stateValues.value).eq("bye"); + expect(stateVariables["/g4/ti"].stateValues.value).eq("hello"); + + // attempting to type in second textInput doesn't work + await updateTextInputValue({ text: "nope", name: "/g2/ti", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/g/ti"].stateValues.value).eq("bye"); + expect(stateVariables["/g2/ti"].stateValues.value).eq("bye"); + expect(stateVariables["/g3/ti"].stateValues.value).eq("bye"); + expect(stateVariables["/g4/ti"].stateValues.value).eq("hello"); + + // type in third textInput + await updateTextInputValue({ text: "this", name: "/g3/ti", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/g/ti"].stateValues.value).eq("this"); + expect(stateVariables["/g2/ti"].stateValues.value).eq("this"); + expect(stateVariables["/g3/ti"].stateValues.value).eq("this"); + expect(stateVariables["/g4/ti"].stateValues.value).eq("hello"); + + // type in fourth textInput + await updateTextInputValue({ text: "that", name: "/g4/ti", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/g/ti"].stateValues.value).eq("this"); + expect(stateVariables["/g2/ti"].stateValues.value).eq("this"); + expect(stateVariables["/g3/ti"].stateValues.value).eq("this"); + expect(stateVariables["/g4/ti"].stateValues.value).eq("that"); + }); + + it("change rendered", async () => { + let core = await createTestCore({ + doenetML: ` +

+ +

Hello

+
+ +

+ +

Bye

+
+ + $g1{name="g1a"} + $g2{name="g2a"} + + `, + }); + + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/g1/p"]).eq(undefined); + expect(stateVariables["/g1a/p"]).eq(undefined); + expect(stateVariables["/g2/p"].stateValues.text).eq("Bye"); + expect(stateVariables["/g2a/p"].stateValues.text).eq("Bye"); + + await updateBooleanInputValue({ boolean: true, name: "/ren1", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/g1/p"].stateValues.text).eq("Hello"); + expect(stateVariables["/g1a/p"].stateValues.text).eq("Hello"); + expect(stateVariables["/g2/p"].stateValues.text).eq("Bye"); + expect(stateVariables["/g2a/p"].stateValues.text).eq("Bye"); + + await updateBooleanInputValue({ boolean: false, name: "/ren2", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/g1/p"].stateValues.text).eq("Hello"); + expect(stateVariables["/g1a/p"].stateValues.text).eq("Hello"); + isUndefinedOrInactive(stateVariables["/g2/p"]); + isUndefinedOrInactive(stateVariables["/g2a/p"]); + + await updateBooleanInputValue({ boolean: false, name: "/ren1", core }); + stateVariables = await returnAllStateVariables(core); + isUndefinedOrInactive(stateVariables["/g1/p"]); + isUndefinedOrInactive(stateVariables["/g1a/p"]); + isUndefinedOrInactive(stateVariables["/g2/p"]); + isUndefinedOrInactive(stateVariables["/g2a/p"]); + + await updateBooleanInputValue({ boolean: true, name: "/ren2", core }); + stateVariables = await returnAllStateVariables(core); + isUndefinedOrInactive(stateVariables["/g1/p"]); + isUndefinedOrInactive(stateVariables["/g1a/p"]); + expect(stateVariables["/g2/p"].stateValues.text).eq("Bye"); + expect(stateVariables["/g2a/p"].stateValues.text).eq("Bye"); + }); +}); diff --git a/packages/doenetml-worker/src/test/tagSpecific/hint.test.ts b/packages/doenetml-worker/src/test/tagSpecific/hint.test.ts new file mode 100644 index 000000000..d6e6ea81a --- /dev/null +++ b/packages/doenetml-worker/src/test/tagSpecific/hint.test.ts @@ -0,0 +1,176 @@ +import { describe, expect, it, vi } from "vitest"; +import { createTestCore, returnAllStateVariables } from "../utils/test-core"; + +const Mock = vi.fn(); +vi.stubGlobal("postMessage", Mock); +vi.mock("hyperformula"); + +describe("Hint tag tests", async () => { + it("hints with and without title", async () => { + let core = await createTestCore({ + doenetML: ` + +

Hello

+
+ + + Hint 2 +

Good day!

+
+ + `, + }); + + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/hint1"].stateValues.title).eq("Hint"); + expect(stateVariables["/hint2"].stateValues.title).eq("Hint 2"); + expect(stateVariables["/hint1"].stateValues.open).eq(false); + expect(stateVariables["/hint2"].stateValues.open).eq(false); + + await core.requestAction({ + componentName: "/hint1", + actionName: "revealHint", + args: {}, + event: null, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/hint1"].stateValues.open).eq(true); + expect(stateVariables["/hint2"].stateValues.open).eq(false); + + await core.requestAction({ + componentName: "/hint2", + actionName: "revealHint", + args: {}, + event: null, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/hint1"].stateValues.open).eq(true); + expect(stateVariables["/hint2"].stateValues.open).eq(true); + + await core.requestAction({ + componentName: "/hint1", + actionName: "closeHint", + args: {}, + event: null, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/hint1"].stateValues.open).eq(false); + expect(stateVariables["/hint2"].stateValues.open).eq(true); + + await core.requestAction({ + componentName: "/hint2", + actionName: "closeHint", + args: {}, + event: null, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/hint1"].stateValues.open).eq(false); + expect(stateVariables["/hint2"].stateValues.open).eq(false); + }); + + it("copy and overwrite title", async () => { + let core = await createTestCore({ + doenetML: ` + + Hint 1 +

Hello

+
+ + + Hint 2 +

Good day!

+
+ +

Title of original hint:

+

Title of revised hint:

+ + `, + }); + + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/hint1"].stateValues.title).eq("Hint 1"); + expect(stateVariables["/revised"].stateValues.title).eq("Hint 2"); + expect(stateVariables["/title1"].stateValues.value).eq("Hint 1"); + expect(stateVariables["/title2"].stateValues.value).eq("Hint 2"); + expect(stateVariables["/hint1"].stateValues.open).eq(false); + expect(stateVariables["/revised"].stateValues.open).eq(false); + + await core.requestAction({ + componentName: "/hint1", + actionName: "revealHint", + args: {}, + event: null, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/hint1"].stateValues.open).eq(true); + expect(stateVariables["/revised"].stateValues.open).eq(false); + + await core.requestAction({ + componentName: "/revised", + actionName: "revealHint", + args: {}, + event: null, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/hint1"].stateValues.open).eq(true); + expect(stateVariables["/revised"].stateValues.open).eq(true); + + await core.requestAction({ + componentName: "/hint1", + actionName: "closeHint", + args: {}, + event: null, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/hint1"].stateValues.open).eq(false); + expect(stateVariables["/revised"].stateValues.open).eq(true); + + await core.requestAction({ + componentName: "/revised", + actionName: "closeHint", + args: {}, + event: null, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/hint1"].stateValues.open).eq(false); + expect(stateVariables["/revised"].stateValues.open).eq(false); + }); + + it("Can open hint in read only mode", async () => { + let core = await createTestCore({ + doenetML: ` + + Hello +

Content

+
+ +

+ `, + flags: { readOnly: true }, + }); + + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/hint1"].stateValues.title).eq("Hello"); + expect(stateVariables["/hint1"].stateValues.open).eq(false); + + expect(stateVariables["/ti"].stateValues.disabled).eq(true); + + await core.requestAction({ + componentName: "/hint1", + actionName: "revealHint", + args: {}, + event: null, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/hint1"].stateValues.open).eq(true); + + await core.requestAction({ + componentName: "/hint1", + actionName: "closeHint", + args: {}, + event: null, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/hint1"].stateValues.open).eq(false); + }); +}); diff --git a/packages/doenetml-worker/src/test/tagSpecific/image.test.ts b/packages/doenetml-worker/src/test/tagSpecific/image.test.ts new file mode 100644 index 000000000..4dd0cd3b4 --- /dev/null +++ b/packages/doenetml-worker/src/test/tagSpecific/image.test.ts @@ -0,0 +1,263 @@ +import { describe, expect, it, vi } from "vitest"; +import { createTestCore, returnAllStateVariables } from "../utils/test-core"; +import { updateMathInputValue } from "../utils/actions"; +import { widthsBySize } from "@doenet/utils"; +import { test_in_graph } from "../utils/in-graph"; +import Core from "../../Core"; +import { cleanLatex } from "../utils/math"; + +const Mock = vi.fn(); +vi.stubGlobal("postMessage", Mock); +vi.mock("hyperformula"); + +export async function moveImage({ + name, + x, + y, + core, +}: { + name: string; + x: number; + y: number; + core: Core; +}) { + await core.requestAction({ + componentName: name, + actionName: "moveImage", + args: { x, y }, + event: null, + }); +} + +describe("Image tag tests", async () => { + it("image sizes", async () => { + let core = await createTestCore({ + doenetML: ` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + `, + }); + + let expectedSizes = { + i: "medium", + itiny: "tiny", + ismall: "small", + imedium: "medium", + ilarge: "large", + ifull: "full", + iinvalid: "medium", + ia10: "tiny", + ia100: "tiny", + ia200: "small", + ia300: "small", + ia400: "medium", + ia500: "medium", + ia600: "large", + ia700: "large", + ia800: "full", + ia900: "full", + ia10000: "full", + ip1: "tiny", + ip10: "tiny", + ip20: "small", + ip30: "small", + ip40: "small", + ip50: "medium", + ip60: "medium", + ip70: "large", + ip80: "large", + ip90: "full", + ip100: "full", + ip1000: "full", + ibadwidth: "medium", + }; + + let stateVariables = await returnAllStateVariables(core); + for (let name in expectedSizes) { + expect(stateVariables["/" + name].stateValues.size).eq( + expectedSizes[name], + ); + expect(stateVariables["/" + name].stateValues.width.size).eq( + widthsBySize[expectedSizes[name]], + ); + } + }); + + it("horizontal align", async () => { + let core = await createTestCore({ + doenetML: ` + + + + + + + `, + }); + + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/i"].stateValues.horizontalAlign).eq("center"); + expect(stateVariables["/ileft"].stateValues.horizontalAlign).eq("left"); + expect(stateVariables["/iright"].stateValues.horizontalAlign).eq( + "right", + ); + expect(stateVariables["/icenter"].stateValues.horizontalAlign).eq( + "center", + ); + expect(stateVariables["/iinvalid"].stateValues.horizontalAlign).eq( + "center", + ); + + // TODO: anything to check in the DOM? + }); + + it("displayMode", async () => { + let core = await createTestCore({ + doenetML: ` + + + + + + `, + }); + + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/i"].stateValues.displayMode).eq("block"); + expect(stateVariables["/iinline"].stateValues.displayMode).eq("inline"); + expect(stateVariables["/iblock"].stateValues.displayMode).eq("block"); + expect(stateVariables["/iinvalid"].stateValues.displayMode).eq("block"); + }); + + it("image in graph", async () => { + const doenetMLsnippet = ` + + + + + `; + + await test_in_graph(doenetMLsnippet, moveImage); + }); + + it("rotate image in graph", async () => { + let core = await createTestCore({ + doenetML: ` + + + + +

Change rotate 1

+

Change rotate 1a

+ + + + `, + }); + + // Is there a way to test the rotation of the image in the graph? + + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/image1"].stateValues.rotate).eq(Math.PI / 4); + + await updateMathInputValue({ + latex: "3\\pi/4", + name: "/rotate1", + core, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/image1"].stateValues.rotate).eq( + (3 * Math.PI) / 4, + ); + + await updateMathInputValue({ + latex: "-\\pi", + name: "/rotate1a", + core, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/image1"].stateValues.rotate).eq(-Math.PI); + }); + + it("math in graph, handle bad anchor coordinates", async () => { + let core = await createTestCore({ + doenetML: ` + + + + + +

Anchor 1 coordinates:

+

Change anchor 1 coordinates:

+ + + `, + }); + + let stateVariables = await returnAllStateVariables(core); + + expect( + cleanLatex(stateVariables["/image1anchor"].stateValues.latex), + ).eq("x"); + + // give good anchor coords + await updateMathInputValue({ + latex: "(6,7)", + name: "/anchorCoords1", + core, + }); + + stateVariables = await returnAllStateVariables(core); + + expect( + cleanLatex(stateVariables["/image1anchor"].stateValues.latex), + ).eq("(6,7)"); + + // give bad anchor coords again + await updateMathInputValue({ + latex: "q", + name: "/anchorCoords1", + core, + }); + + stateVariables = await returnAllStateVariables(core); + + expect( + cleanLatex(stateVariables["/image1anchor"].stateValues.latex), + ).eq("q"); + }); +}); diff --git a/packages/doenetml-worker/src/test/tagSpecific/integer.test.ts b/packages/doenetml-worker/src/test/tagSpecific/integer.test.ts index 94b8bfe5d..d97e1441f 100644 --- a/packages/doenetml-worker/src/test/tagSpecific/integer.test.ts +++ b/packages/doenetml-worker/src/test/tagSpecific/integer.test.ts @@ -4,6 +4,7 @@ import { updateMathInputValue } from "../utils/actions"; const Mock = vi.fn(); vi.stubGlobal("postMessage", Mock); +vi.mock("hyperformula"); describe("Integer tag tests", async () => { it("1.2+1.1", async () => { @@ -46,7 +47,7 @@ describe("Integer tag tests", async () => { await updateMathInputValue({ latex: "-6.5", - componentName: "/mi", + name: "/mi", core, }); stateVariables = await returnAllStateVariables(core); @@ -55,7 +56,7 @@ describe("Integer tag tests", async () => { await updateMathInputValue({ latex: "-6.5x", - componentName: "/mi", + name: "/mi", core, }); stateVariables = await returnAllStateVariables(core); @@ -64,7 +65,7 @@ describe("Integer tag tests", async () => { await updateMathInputValue({ latex: "9.5", - componentName: "/mi", + name: "/mi", core, }); stateVariables = await returnAllStateVariables(core); @@ -86,7 +87,7 @@ describe("Integer tag tests", async () => { await updateMathInputValue({ latex: "-6.5", - componentName: "/mi", + name: "/mi", core, }); stateVariables = await returnAllStateVariables(core); @@ -95,7 +96,7 @@ describe("Integer tag tests", async () => { await updateMathInputValue({ latex: "-6.5x", - componentName: "/mi", + name: "/mi", core, }); stateVariables = await returnAllStateVariables(core); @@ -105,7 +106,7 @@ describe("Integer tag tests", async () => { // Note: change to 3 and then 31 to verify bug doesn't reappear await updateMathInputValue({ latex: "3", - componentName: "/mi", + name: "/mi", core, }); stateVariables = await returnAllStateVariables(core); @@ -114,7 +115,7 @@ describe("Integer tag tests", async () => { await updateMathInputValue({ latex: "31", - componentName: "/mi", + name: "/mi", core, }); stateVariables = await returnAllStateVariables(core); @@ -123,7 +124,7 @@ describe("Integer tag tests", async () => { await updateMathInputValue({ latex: "31.5", - componentName: "/mi", + name: "/mi", core, }); stateVariables = await returnAllStateVariables(core); diff --git a/packages/doenetml-worker/src/test/tagSpecific/intersection.test.ts b/packages/doenetml-worker/src/test/tagSpecific/intersection.test.ts new file mode 100644 index 000000000..daf0e6ae4 --- /dev/null +++ b/packages/doenetml-worker/src/test/tagSpecific/intersection.test.ts @@ -0,0 +1,835 @@ +import { describe, expect, it, vi } from "vitest"; +import { createTestCore, returnAllStateVariables } from "../utils/test-core"; +import { + movePoint, + movePolygon, + movePolyline, + updateBooleanInputValue, + updateMathInputValue, +} from "../utils/actions"; +import Core from "../../Core"; + +const Mock = vi.fn(); +vi.stubGlobal("postMessage", Mock); +vi.mock("hyperformula"); + +async function check_intersections( + core: Core, + coords1?: number[], + coords2?: number[], + coords3?: number[], + coords4?: number[], + coords5?: number[], + coords6?: number[], + coords7?: number[], +) { + let stateVariables = await returnAllStateVariables(core); + + if (coords1) { + expect(stateVariables["/int1"].stateValues.xs[0].tree).closeTo( + coords1[0], + 1e-12, + ); + expect(stateVariables["/int1"].stateValues.xs[1].tree).closeTo( + coords1[1], + 1e-12, + ); + } else { + expect(stateVariables["/int1"]).eq(undefined); + } + + if (coords2) { + expect(stateVariables["/int2"].stateValues.xs[0].tree).closeTo( + coords2[0], + 1e-12, + ); + expect(stateVariables["/int2"].stateValues.xs[1].tree).closeTo( + coords2[1], + 1e-12, + ); + } else { + expect(stateVariables["/int2"]).eq(undefined); + } + + if (coords3) { + expect(stateVariables["/int3"].stateValues.xs[0].tree).closeTo( + coords3[0], + 1e-12, + ); + expect(stateVariables["/int3"].stateValues.xs[1].tree).closeTo( + coords3[1], + 1e-12, + ); + } else { + expect(stateVariables["/int3"]).eq(undefined); + } + + if (coords4) { + expect(stateVariables["/int4"].stateValues.xs[0].tree).closeTo( + coords4[0], + 1e-12, + ); + expect(stateVariables["/int4"].stateValues.xs[1].tree).closeTo( + coords4[1], + 1e-12, + ); + } else { + expect(stateVariables["/int4"]).eq(undefined); + } + + if (coords5) { + expect(stateVariables["/int5"].stateValues.xs[0].tree).closeTo( + coords5[0], + 1e-12, + ); + expect(stateVariables["/int5"].stateValues.xs[1].tree).closeTo( + coords5[1], + 1e-12, + ); + } else { + expect(stateVariables["/int5"]).eq(undefined); + } + + if (coords6) { + expect(stateVariables["/int6"].stateValues.xs[0].tree).closeTo( + coords6[0], + 1e-12, + ); + expect(stateVariables["/int6"].stateValues.xs[1].tree).closeTo( + coords6[1], + 1e-12, + ); + } else { + expect(stateVariables["/int6"]).eq(undefined); + } + + if (coords7) { + expect(stateVariables["/int7"].stateValues.xs[0].tree).closeTo( + coords7[0], + 1e-12, + ); + expect(stateVariables["/int7"].stateValues.xs[1].tree).closeTo( + coords7[1], + 1e-12, + ); + } else { + expect(stateVariables["/int7"]).eq(undefined); + } +} + +describe("Intersection tag tests", async () => { + it("intersections between two lines", async () => { + let core = await createTestCore({ + doenetML: ` + + (1,2) + (2,2) + (3,2) + (4,2) + + + + $l1$l2 + + + + + `, + }); + + // no intersection when lines coincide + await check_intersections(core); + + // make first line vertical + await movePoint({ name: "/P1", x: 3, y: 5, core }); + await movePoint({ name: "/P2", x: 3, y: -5, core }); + await check_intersections(core, [3, 2]); + + // make second line vertical + await movePoint({ name: "/P3", x: -4, y: 5, core }); + await movePoint({ name: "/P4", x: -4, y: -5, core }); + await check_intersections(core); + + // make lines intersect again + await movePoint({ name: "/P1", x: -8, y: -7, core }); + await movePoint({ name: "/P2", x: 8, y: 9, core }); + await movePoint({ name: "/P3", x: 4, y: 6, core }); + await movePoint({ name: "/P4", x: -4, y: -6, core }); + await check_intersections(core, [2, 3]); + + // make lines equal again + await movePoint({ name: "/P1", x: 6, y: 9, core }); + await movePoint({ name: "/P2", x: -6, y: -9, core }); + await check_intersections(core); + }); + + it("intersections between three lines gives warning", async () => { + let core = await createTestCore({ + doenetML: ` + + + + + + $l1$l2$l3 + + + `, + }); + + let errorWarnings = core.errorWarnings; + + expect(errorWarnings.errors.length).eq(0); + expect(errorWarnings.warnings.length).eq(1); + + expect(errorWarnings.warnings[0].message).contain( + `Haven't implemented intersection for more than two items`, + ); + expect(errorWarnings.warnings[0].level).eq(1); + expect(errorWarnings.warnings[0].doenetMLrange.lineBegin).eq(7); + expect(errorWarnings.warnings[0].doenetMLrange.charBegin).eq(3); + expect(errorWarnings.warnings[0].doenetMLrange.lineEnd).eq(7); + expect(errorWarnings.warnings[0].doenetMLrange.charEnd).eq(40); + }); + + it("intersection of two lines hides dynamically", async () => { + let core = await createTestCore({ + doenetML: ` + + (1,2) + (2,2) + (3,2) + (4,2) + + + + + + + + + + + +

Intersection 1: $l1$l2

+

Intersection 2: $l1$l2

+ + `, + }); + + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/i1"].stateValues.text).eq("Intersection 1: "); + expect(stateVariables["/i2"].stateValues.text).eq("Intersection 2: "); + + await updateBooleanInputValue({ boolean: true, name: "/h1", core }); + await updateBooleanInputValue({ boolean: false, name: "/h2", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/i1"].stateValues.text).eq("Intersection 1: "); + expect(stateVariables["/i2"].stateValues.text).eq("Intersection 2: "); + + // make first line vertical + await movePoint({ name: "/P1", x: 3, y: 5, core }); + await movePoint({ name: "/P2", x: 3, y: -5, core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/i1"].stateValues.text).eq("Intersection 1: "); + expect(stateVariables["/i2"].stateValues.text).eq( + "Intersection 2: ( 3, 2 )", + ); + + await updateBooleanInputValue({ boolean: false, name: "/h1", core }); + await updateBooleanInputValue({ boolean: true, name: "/h2", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/i1"].stateValues.text).eq( + "Intersection 1: ( 3, 2 )", + ); + expect(stateVariables["/i2"].stateValues.text).eq("Intersection 2: "); + + // make second line vertical + await movePoint({ name: "/P3", x: -4, y: 5, core }); + await movePoint({ name: "/P4", x: -4, y: -5, core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/i1"].stateValues.text).eq("Intersection 1: "); + expect(stateVariables["/i2"].stateValues.text).eq("Intersection 2: "); + + await updateBooleanInputValue({ boolean: true, name: "/h1", core }); + await updateBooleanInputValue({ boolean: false, name: "/h2", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/i1"].stateValues.text).eq("Intersection 1: "); + expect(stateVariables["/i2"].stateValues.text).eq("Intersection 2: "); + + // make lines intersect again + await movePoint({ name: "/P1", x: -8, y: -7, core }); + await movePoint({ name: "/P2", x: 8, y: 9, core }); + await movePoint({ name: "/P3", x: 4, y: 6, core }); + await movePoint({ name: "/P4", x: -4, y: -6, core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/i1"].stateValues.text).eq("Intersection 1: "); + expect(stateVariables["/i2"].stateValues.text).eq( + "Intersection 2: ( 2, 3 )", + ); + + await updateBooleanInputValue({ boolean: false, name: "/h1", core }); + await updateBooleanInputValue({ boolean: true, name: "/h2", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/i1"].stateValues.text).eq( + "Intersection 1: ( 2, 3 )", + ); + expect(stateVariables["/i2"].stateValues.text).eq("Intersection 2: "); + + // make lines equal again + await movePoint({ name: "/P1", x: 6, y: 9, core }); + await movePoint({ name: "/P2", x: -6, y: -9, core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/i1"].stateValues.text).eq("Intersection 1: "); + expect(stateVariables["/i2"].stateValues.text).eq("Intersection 2: "); + }); + + it("intersections between two circles", async () => { + let core = await createTestCore({ + doenetML: ` + + + + + (3,4) + + (3,6) + + + $c1 $c2 + + + `, + }); + + // start with single intersection + await check_intersections(core, [3, 5], undefined); + + // move first circle up + await movePoint({ name: "/P1", x: 3, y: 5, core }); + await check_intersections( + core, + [3 + Math.sqrt(3) / 2, 5.5], + [3 - Math.sqrt(3) / 2, 5.5], + ); + + // increase radius of second circle + await updateMathInputValue({ latex: "2", name: "/r2", core }); + await check_intersections(core, [3, 4], undefined); + + // increase radius of second circle further + await updateMathInputValue({ latex: "4", name: "/r2", core }); + await check_intersections(core, undefined, undefined); + + // increase radius of first circle + await updateMathInputValue({ latex: "2", name: "/r1", core }); + await check_intersections(core, undefined, undefined); + + // make 30-60-90 triangle + await movePoint({ name: "/P2", x: 3 - 2 * Math.sqrt(3), y: 5, core }); + await check_intersections(core, [3, 7], [3, 3]); + + // increase radius of first circle + await updateMathInputValue({ latex: "4", name: "/r1", core }); + let h = Math.sqrt(16 - 3); + await check_intersections( + core, + [3 - Math.sqrt(3), 5 + h], + [3 - Math.sqrt(3), 5 - h], + ); + + // make circles identical + + await movePoint({ name: "/P1", x: 3 - 2 * Math.sqrt(3), y: 5, core }); + await check_intersections(core, undefined, undefined); + }); + + it("intersections between line and circle", async () => { + let core = await createTestCore({ + doenetML: ` + + + + (3,4) + + (4,1) + (4,-4) + + + $l $c + + + `, + }); + + // start with single intersection + await check_intersections(core, [4, 4]); + + // move circle up and to right + await movePoint({ name: "/P", x: 4, y: 5, core }); + await check_intersections(core, [4, 6], [4, 4]); + + // increase radius of circle + await updateMathInputValue({ latex: "2", name: "/r", core }); + await check_intersections(core, [4, 7], [4, 3]); + + // move line point 1 + await movePoint({ name: "/A", x: -2, y: 2, core }); + await check_intersections(core); + + // increase radius of circle to make it tangent to line + await updateMathInputValue({ latex: "9/\\sqrt{2}", name: "/r", core }); + await check_intersections(core, [-0.5, 0.5]); + + // increase radius of circle further + await updateMathInputValue({ latex: "9", name: "/r", core }); + await check_intersections(core, [-5, 5], [4, -4]); + }); + + it("intersection involving zero or one object returns nothing", async () => { + let core = await createTestCore({ + doenetML: ` + + + + + $l + $c + + + + + `, + }); + + await check_intersections(core); + }); + + it("intersections between two line segments", async () => { + let core = await createTestCore({ + doenetML: ` + + (1,2) + (5,2) + (3,2) + (4,2) + + + + $l1$l2 + + + + `, + }); + + //no intersection when line segments overlap over interval + await check_intersections(core); + + //make first line segment vertical + await movePoint({ name: "/P1", x: 3, y: 5, core }); + await movePoint({ name: "/P2", x: 3, y: -5, core }); + await check_intersections(core, [3, 2]); + + // move second line segment to just miss intersection + await movePoint({ name: "/P3", x: 3.01, y: 2, core }); + await check_intersections(core); + + // shift line segment one to intersect again + await movePoint({ name: "/P1", x: 4, y: 5, core }); + await movePoint({ name: "/P2", x: 4, y: -5, core }); + await check_intersections(core, [4, 2]); + + // move second line segment to make it just miss again + await movePoint({ name: "/P4", x: 3.99, y: 2, core }); + await check_intersections(core); + + // flip second line segment to make it intersect again + await movePoint({ name: "/P3", x: 6, y: 2, core }); + await check_intersections(core, [4, 2]); + + // move second line segment to make it just miss again + await movePoint({ name: "/P4", x: 4.01, y: 2, core }); + await check_intersections(core); + + // shift line segment one to intersect again + await movePoint({ name: "/P1", x: 5, y: 3, core }); + await movePoint({ name: "/P2", x: 5, y: 1, core }); + await check_intersections(core, [5, 2]); + + // move second line segment to just miss intersection again + await movePoint({ name: "/P3", x: 4.99, y: 2, core }); + await check_intersections(core); + + //make line segments intersect again + await movePoint({ name: "/P1", x: -8, y: -7, core }); + await movePoint({ name: "/P2", x: 8, y: 9, core }); + await movePoint({ name: "/P3", x: 4, y: 6, core }); + await movePoint({ name: "/P4", x: -4, y: -6, core }); + await check_intersections(core, [2, 3]); + + // move first line segment to make it just miss again + await movePoint({ name: "/P1", x: 2.01, y: 3, core }); + await check_intersections(core); + }); + + it("intersections between line and line segment", async () => { + let core = await createTestCore({ + doenetML: ` + + (1,2) + (2,2) + (3,2) + (4,2) + + + + $l1$l2 + + + + `, + }); + + // no intersection when line and line segment coincide + await check_intersections(core); + + // make first line vertical + await movePoint({ name: "/P1", x: 3, y: 5, core }); + await movePoint({ name: "/P2", x: 3, y: -5, core }); + await check_intersections(core, [3, 2]); + + // move second line segment to just miss intersection + await movePoint({ name: "/P3", x: 3.01, y: 2, core }); + await check_intersections(core); + + // shift line one to intersect again + await movePoint({ name: "/P1", x: 4, y: 5, core }); + await movePoint({ name: "/P2", x: 4, y: -5, core }); + await check_intersections(core, [4, 2]); + + // move second line segment to make it just miss again + await movePoint({ name: "/P4", x: 3.99, y: 2, core }); + await check_intersections(core); + + // flip second line segment to make it intersect again + await movePoint({ name: "/P3", x: 6, y: 2, core }); + await check_intersections(core, [4, 2]); + + // move second line segment to make it just miss again + await movePoint({ name: "/P4", x: 4.01, y: 2, core }); + await check_intersections(core); + + // shift line one to intersect again + await movePoint({ name: "/P1", x: 5, y: 3, core }); + await movePoint({ name: "/P2", x: 5, y: 1, core }); + await check_intersections(core, [5, 2]); + + // move second line segment to just miss intersection again + + await movePoint({ name: "/P3", x: 4.99, y: 2, core }); + await check_intersections(core); + + // make lines intersect again + await movePoint({ name: "/P1", x: -8, y: -7, core }); + await movePoint({ name: "/P2", x: 8, y: 9, core }); + await movePoint({ name: "/P3", x: 4, y: 6, core }); + await movePoint({ name: "/P4", x: -4, y: -6, core }); + await check_intersections(core, [2, 3]); + + // move first line and still intersects + await movePoint({ name: "/P1", x: 2.01, y: 3, core }); + await check_intersections( + core, + [1.9798994974874389, 2.969849246231158], + ); + }); + + it("intersections between line segment and circle", async () => { + let core = await createTestCore({ + doenetML: ` + + + + (3,4) + + (4,3.99) + (4,-4) + + + $l $c + + + `, + }); + + //start with no intersection + await check_intersections(core); + + //move line point 1 to intersect + await movePoint({ name: "/A", x: 4, y: 4, core }); + await check_intersections(core, [4, 4]); + + //move circle up and to right + await movePoint({ name: "/P", x: 4, y: 5, core }); + await check_intersections(core, [4, 4]); + + //extend line segment to intersect twice + await movePoint({ name: "/A", x: 4, y: 6, core }); + await check_intersections(core, [4, 6], [4, 4]); + + //increase radius of circle + await updateMathInputValue({ latex: "2", name: "/r", core }); + await check_intersections(core, [4, 3]); + + //flip line segment to intersect at top + await movePoint({ name: "/B", x: 4, y: 7, core }); + await check_intersections(core, [4, 7]); + + //extend line segment to intersect twice + await movePoint({ name: "/A", x: 4, y: 3, core }); + await check_intersections(core, [4, 7], [4, 3]); + + //move line segment + await movePoint({ name: "/A", x: -0.5, y: 0.5, core }); + await movePoint({ name: "/B", x: 3, y: -3, core }); + await check_intersections(core); + + //increase radius of circle to make it tangent to line + await updateMathInputValue({ latex: "9/\\sqrt{2}", name: "/r", core }); + await check_intersections(core, [-0.5, 0.5]); + + //increase radius of circle further + await updateMathInputValue({ latex: "9", name: "/r", core }); + await check_intersections(core); + + //extend bottom of line segment + await movePoint({ name: "/B", x: 4, y: -4, core }); + await check_intersections(core, [4, -4]); + + //extend top of line segment + await movePoint({ name: "/A", x: -5, y: 5, core }); + await check_intersections(core, [-5, 5], [4, -4]); + }); + + it("intersections between two polygons", async () => { + let core = await createTestCore({ + doenetML: ` + + + + + + $p1$p2 + + + + + `, + }); + + // all three triangle vertices are intersections + await check_intersections(core, [1, 0], [0, 1], [0, 0]); + + // extending rectangle to upper right does not change intersections + await movePolygon({ + name: "/p2", + pointCoords: { 2: [4, 2] }, + core, + }); + await check_intersections(core, [1, 0], [0, 1], [0, 0]); + + // moving bottom of rectangle down removes one intersection + await movePolygon({ + name: "/p2", + pointCoords: { 0: [0, -1] }, + core, + }); + await check_intersections(core, [0, 1], [0, 0]); + + // moving left of rectangle left removes all intersections + await movePolygon({ + name: "/p2", + pointCoords: { 0: [-1, -1] }, + core, + }); + await check_intersections(core); + + // move top of triangle upward + await movePolygon({ + name: "/p1", + pointCoords: { 0: [0, 4] }, + core, + }); + await check_intersections(core, [0.5, 2], [0, 2]); + + // move right of triangle rightward + await movePolygon({ + name: "/p1", + pointCoords: { 1: [6, 0] }, + core, + }); + await check_intersections(core, [4, 4 / 3], [3, 2], [4, 0], [0, 2]); + }); + + it("intersections between two polylines", async () => { + let core = await createTestCore({ + doenetML: ` + + + + + + $p1$p2 + + + + + `, + }); + + // two of three vertices of triangular polyline are intersections + // TODO: why isn't the third? + await check_intersections(core, [1, 0], [0, 1]); + + // extending polyline2 to right does not change intersections + await movePolyline({ + name: "/p2", + pointCoords: { 1: [4, 0] }, + core, + }); + await check_intersections(core, [1, 0], [0, 1]); + + // extending top of polyline2 up removes one intersection + await movePolyline({ + name: "/p2", + pointCoords: { 3: [0, 4] }, + core, + }); + await check_intersections(core, [1, 0]); + + // extending bottom polyline 2 removes all intersections + await movePolyline({ + name: "/p2", + pointCoords: { 0: [0, -2] }, + core, + }); + await check_intersections(core); + + // move top of polyline 1 rightward + await movePolyline({ + name: "/p1", + pointCoords: { 0: [4, 1] }, + core, + }); + await check_intersections(core, [2.5, 0.5]); + + // move middle of polyline1 down + await movePolyline({ + name: "/p1", + pointCoords: { 1: [-1, -4] }, + core, + }); + await check_intersections(core, [2, -1], [3.25, 0.25]); + + // move other end of polyline1 up + await movePolyline({ + name: "/p1", + pointCoords: { 2: [1, 6] }, + core, + }); + await check_intersections(core, [2, -1], [3.25, 0.25], [0.375, 2.875]); + }); + + it("intersections between polygon and circle", async () => { + let core = await createTestCore({ + doenetML: ` + + + + (0,0) + + + + $p$c + + + + `, + }); + + // two of three triangle vertices are intersections + await check_intersections(core, [0, 1], [1, 0]); + + // extend triangle down + await movePolygon({ + name: "/p", + pointCoords: { 2: [0, -10] }, + core, + }); + + await check_intersections( + core, + [0, 1], + [1, 0], + [0.9801980198019802, -0.19801980198019803], + [0, -1], + ); + + // extend triangle right + await movePolygon({ + name: "/p", + pointCoords: { 1: [6, 1] }, + core, + }); + await check_intersections(core, [0, 1], [0, -1]); + + // extend triangle left + await movePolygon({ + name: "/p", + pointCoords: { 0: [-6, 1] }, + core, + }); + await check_intersections(core, [0, 1]); + + // Increase radius of circle to 6 + await updateMathInputValue({ latex: "6", name: "/r", core }); + await check_intersections( + core, + [5.916079783099616, 1], + [-5.916079783099616, 1], + [5.934993883246796, 0.880822119285793], + [2.4726494288551146, -5.46680938043229], + [-5.934993883246796, 0.880822119285793], + [-2.4726494288551146, -5.46680938043229], + ); + + // Increase radius of circle to 10 + await updateMathInputValue({ latex: "10", name: "/r", core }); + await check_intersections(core, [0, -10]); + }); + + it("aslist", async () => { + let core = await createTestCore({ + doenetML: ` + + + + + $p$c + + +

+

+ `, + }); + + const stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/pDefault"].stateValues.text).eq( + "( 0, 1 ), ( 1, 0 )", + ); + expect(stateVariables["/pNoList"].stateValues.text).eq( + "( 0, 1 )( 1, 0 )", + ); + }); +}); diff --git a/packages/doenetml-worker/src/test/tagSpecific/label.test.ts b/packages/doenetml-worker/src/test/tagSpecific/label.test.ts new file mode 100644 index 000000000..932d269b8 --- /dev/null +++ b/packages/doenetml-worker/src/test/tagSpecific/label.test.ts @@ -0,0 +1,312 @@ +import { describe, expect, it, vi } from "vitest"; +import { createTestCore, returnAllStateVariables } from "../utils/test-core"; +import { cleanLatex } from "../utils/math"; +import { moveLabel, updateMathInputValue } from "../utils/actions"; +import { test_in_graph } from "../utils/in-graph"; + +const Mock = vi.fn(); +vi.stubGlobal("postMessage", Mock); +vi.mock("hyperformula"); + +describe("Label tag tests", async () => { + it("label in graph", async () => { + const doenetMLsnippet = ` + + + + + `; + + await test_in_graph(doenetMLsnippet, moveLabel); + }); + + it("label in graph, handle bad anchor coordinates", async () => { + let core = await createTestCore({ + doenetML: ` + + + + + +

Anchor 1 coordinates:

+

Change anchor 1 coordinates:

+ + + `, + }); + + let stateVariables = await returnAllStateVariables(core); + + expect( + cleanLatex(stateVariables["/label1anchor"].stateValues.latex), + ).eq("x"); + + // give good anchor coords + await updateMathInputValue({ + latex: "(6,7)", + name: "/anchorCoords1", + core, + }); + + stateVariables = await returnAllStateVariables(core); + + expect( + cleanLatex(stateVariables["/label1anchor"].stateValues.latex), + ).eq("(6,7)"); + + // give bad anchor coords again + await updateMathInputValue({ + latex: "q", + name: "/anchorCoords1", + core, + }); + + stateVariables = await returnAllStateVariables(core); + + expect( + cleanLatex(stateVariables["/label1anchor"].stateValues.latex), + ).eq("q"); + }); + + it("color label via style", async () => { + let core = await createTestCore({ + doenetML: ` + + + + + + + +

Style number:

+ +

is $no_style.textStyleDescription, i.e., the text color is $no_style.textColor and the background color is $no_style.backgroundColor.

+

is $fixed_style.textStyleDescription, i.e., the text color is $fixed_style.textColor and the background color is $fixed_style.backgroundColor.

+

is $variable_style.textStyleDescription, i.e., the text color is $variable_style.textColor and the background color is $variable_style.backgroundColor.

+ + + $no_style{anchor="(1,2)"} + $fixed_style{anchor="(3,4)"} + $variable_style + + + `, + }); + + let stateVariables = await returnAllStateVariables(core); + + expect(stateVariables["/tsd_no_style"].stateValues.text).eq("black"); + expect(stateVariables["/tc_no_style"].stateValues.text).eq("black"); + expect(stateVariables["/bc_no_style"].stateValues.text).eq("none"); + + expect(stateVariables["/tsd_fixed_style"].stateValues.text).eq("green"); + expect(stateVariables["/tc_fixed_style"].stateValues.text).eq("green"); + expect(stateVariables["/bc_fixed_style"].stateValues.text).eq("none"); + + expect(stateVariables["/tsd_variable_style"].stateValues.text).eq( + "black", + ); + expect(stateVariables["/tc_variable_style"].stateValues.text).eq( + "black", + ); + expect(stateVariables["/bc_variable_style"].stateValues.text).eq( + "none", + ); + + await updateMathInputValue({ latex: "2", name: "/sn", core }); + stateVariables = await returnAllStateVariables(core); + + expect(stateVariables["/tsd_variable_style"].stateValues.text).eq( + "green", + ); + expect(stateVariables["/tc_variable_style"].stateValues.text).eq( + "green", + ); + expect(stateVariables["/bc_variable_style"].stateValues.text).eq( + "none", + ); + + expect(stateVariables["/tsd_no_style"].stateValues.text).eq("black"); + expect(stateVariables["/tc_no_style"].stateValues.text).eq("black"); + expect(stateVariables["/bc_no_style"].stateValues.text).eq("none"); + + expect(stateVariables["/tsd_fixed_style"].stateValues.text).eq("green"); + expect(stateVariables["/tc_fixed_style"].stateValues.text).eq("green"); + expect(stateVariables["/bc_fixed_style"].stateValues.text).eq("none"); + + await updateMathInputValue({ latex: "3", name: "/sn", core }); + stateVariables = await returnAllStateVariables(core); + + expect(stateVariables["/tsd_variable_style"].stateValues.text).eq( + "red with a blue background", + ); + expect(stateVariables["/tc_variable_style"].stateValues.text).eq("red"); + expect(stateVariables["/bc_variable_style"].stateValues.text).eq( + "blue", + ); + + expect(stateVariables["/tsd_no_style"].stateValues.text).eq("black"); + expect(stateVariables["/tc_no_style"].stateValues.text).eq("black"); + expect(stateVariables["/bc_no_style"].stateValues.text).eq("none"); + + expect(stateVariables["/tsd_fixed_style"].stateValues.text).eq("green"); + expect(stateVariables["/tc_fixed_style"].stateValues.text).eq("green"); + expect(stateVariables["/bc_fixed_style"].stateValues.text).eq("none"); + }); + + it("label copied by plain macro, but not value, reflects style and anchor position", async () => { + let core = await createTestCore({ + doenetML: ` + + + + + + + + + + + + + + + + + $m1{name="m1a"} + $m2{name="m2a"} + + + + + + $m1.value{assignNames="m1b"} + $m2.value{assignNames="m2b"} + + + + +

$m1{name="m1c"} $m2{name="m2c"}

+ +

$m1.value{assignNames="m1d"} $m2.value{assignNames="m2d"}

+ + `, + }); + + let stateVariables = await returnAllStateVariables(core); + + expect(stateVariables["/m1"].stateValues.value).eq("one: \\(x^2\\)"); + expect(stateVariables["/m1a"].stateValues.value).eq("one: \\(x^2\\)"); + expect(stateVariables["/m1b"].stateValues.value).eq("one: \\(x^2\\)"); + expect(stateVariables["/m1c"].stateValues.value).eq("one: \\(x^2\\)"); + expect(stateVariables["/m1d"].stateValues.value).eq("one: \\(x^2\\)"); + + expect(stateVariables["/m2"].stateValues.value).eq("two: \\(x^3\\)"); + expect(stateVariables["/m2a"].stateValues.value).eq("two: \\(x^3\\)"); + expect(stateVariables["/m2b"].stateValues.value).eq("two: \\(x^3\\)"); + expect(stateVariables["/m2c"].stateValues.value).eq("two: \\(x^3\\)"); + expect(stateVariables["/m2d"].stateValues.value).eq("two: \\(x^3\\)"); + + expect(stateVariables["/m1"].stateValues.styleNumber).eq(2); + expect(stateVariables["/m1a"].stateValues.styleNumber).eq(2); + expect(stateVariables["/m1b"].stateValues.styleNumber).eq(1); + expect(stateVariables["/m1c"].stateValues.styleNumber).eq(2); + expect(stateVariables["/m1d"].stateValues.styleNumber).eq(1); + expect(stateVariables["/m2"].stateValues.styleNumber).eq(3); + expect(stateVariables["/m2a"].stateValues.styleNumber).eq(3); + expect(stateVariables["/m2b"].stateValues.styleNumber).eq(1); + expect(stateVariables["/m2c"].stateValues.styleNumber).eq(3); + expect(stateVariables["/m2d"].stateValues.styleNumber).eq(1); + + expect(cleanLatex(stateVariables["/m1coords"].stateValues.latex)).eq( + "(0,0)", + ); + expect(cleanLatex(stateVariables["/m2coords"].stateValues.latex)).eq( + "(3,4)", + ); + expect(cleanLatex(stateVariables["/m1acoords"].stateValues.latex)).eq( + "(0,0)", + ); + expect(cleanLatex(stateVariables["/m2acoords"].stateValues.latex)).eq( + "(3,4)", + ); + expect(cleanLatex(stateVariables["/m1bcoords"].stateValues.latex)).eq( + "(0,0)", + ); + expect(cleanLatex(stateVariables["/m2bcoords"].stateValues.latex)).eq( + "(0,0)", + ); + + // move first labels + await moveLabel({ name: "/m1", x: -2, y: 3, core }); + await moveLabel({ name: "/m2", x: 4, y: -5, core }); + stateVariables = await returnAllStateVariables(core); + + expect(cleanLatex(stateVariables["/m1coords"].stateValues.latex)).eq( + "(-2,3)", + ); + expect(cleanLatex(stateVariables["/m2coords"].stateValues.latex)).eq( + "(4,-5)", + ); + expect(cleanLatex(stateVariables["/m1acoords"].stateValues.latex)).eq( + "(-2,3)", + ); + expect(cleanLatex(stateVariables["/m2acoords"].stateValues.latex)).eq( + "(4,-5)", + ); + expect(cleanLatex(stateVariables["/m1bcoords"].stateValues.latex)).eq( + "(0,0)", + ); + expect(cleanLatex(stateVariables["/m2bcoords"].stateValues.latex)).eq( + "(0,0)", + ); + + // move second labels + await moveLabel({ name: "/m1a", x: 7, y: 1, core }); + await moveLabel({ name: "/m2a", x: -8, y: 2, core }); + stateVariables = await returnAllStateVariables(core); + + expect(cleanLatex(stateVariables["/m1coords"].stateValues.latex)).eq( + "(7,1)", + ); + expect(cleanLatex(stateVariables["/m2coords"].stateValues.latex)).eq( + "(-8,2)", + ); + expect(cleanLatex(stateVariables["/m1acoords"].stateValues.latex)).eq( + "(7,1)", + ); + expect(cleanLatex(stateVariables["/m2acoords"].stateValues.latex)).eq( + "(-8,2)", + ); + expect(cleanLatex(stateVariables["/m1bcoords"].stateValues.latex)).eq( + "(0,0)", + ); + expect(cleanLatex(stateVariables["/m2bcoords"].stateValues.latex)).eq( + "(0,0)", + ); + + // move third labels + await moveLabel({ name: "/m1b", x: -6, y: 3, core }); + await moveLabel({ name: "/m2b", x: -5, y: -4, core }); + stateVariables = await returnAllStateVariables(core); + + expect(cleanLatex(stateVariables["/m1coords"].stateValues.latex)).eq( + "(7,1)", + ); + expect(cleanLatex(stateVariables["/m2coords"].stateValues.latex)).eq( + "(-8,2)", + ); + expect(cleanLatex(stateVariables["/m1acoords"].stateValues.latex)).eq( + "(7,1)", + ); + expect(cleanLatex(stateVariables["/m2acoords"].stateValues.latex)).eq( + "(-8,2)", + ); + expect(cleanLatex(stateVariables["/m1bcoords"].stateValues.latex)).eq( + "(-6,3)", + ); + expect(cleanLatex(stateVariables["/m2bcoords"].stateValues.latex)).eq( + "(-5,-4)", + ); + }); +}); diff --git a/packages/doenetml-worker/src/test/tagSpecific/legend.test.ts b/packages/doenetml-worker/src/test/tagSpecific/legend.test.ts new file mode 100644 index 000000000..1be74d3fe --- /dev/null +++ b/packages/doenetml-worker/src/test/tagSpecific/legend.test.ts @@ -0,0 +1,484 @@ +import { describe, expect, it, vi } from "vitest"; +import { createTestCore, returnAllStateVariables } from "../utils/test-core"; +import { + updateBooleanInputValue, + updateMathInputValue, + updateTextInputValue, +} from "../utils/actions"; +import Core from "../../Core"; + +const Mock = vi.fn(); +vi.stubGlobal("postMessage", Mock); +vi.mock("hyperformula"); + +type LineLegendItem = { + swatchType: "line"; + label: string; + hasLatex: boolean; + lineStyle?: string; + lineColor?: string; + lineWidth?: number; + lineOpacity?: number; +}; +type RectangleLegendItem = Omit & { + swatchType: "rectangle"; + fillColor?: string; + filled?: boolean; + fillOpacity?: number; +}; +type MarkerLegendItem = { + swatchType: "marker"; + label: string; + hasLatex: boolean; + markerStyle?: string; + markerColor?: string; + markerSize?: number; +}; + +type LegendItem = LineLegendItem | RectangleLegendItem | MarkerLegendItem; + +async function check_legend({ + core, + legendName = "/legend1", + legendItems, + position, +}: { + core: Core; + legendName?: string; + legendItems: LegendItem[]; + position?: string; +}) { + const stateVariables = await returnAllStateVariables(core); + + if (position) { + expect(stateVariables[legendName].stateValues.position).eq(position); + } + + let numItems = legendItems.length; + let legendElements = stateVariables[legendName].stateValues.legendElements; + expect(legendElements.length).eq(numItems); + + for (let i = 0; i < numItems; i++) { + let element = legendElements[i]; + let item = legendItems[i]; + expect(element.swatchType).eq(item.swatchType); + expect(element.label.value).eq(item.label); + expect(element.label.hasLatex).eq(item.hasLatex); + if (item.swatchType === "marker") { + if (item.markerStyle !== undefined) { + expect(element.markerStyle).eq(item.markerStyle); + expect(element.markerColor).eq(item.markerColor); + expect(element.markerSize).eq(item.markerSize); + } + } else { + if (item.lineStyle !== undefined) { + expect(element.lineStyle).eq(item.lineStyle); + expect(element.lineColor).eq(item.lineColor); + expect(element.lineWidth).eq(item.lineWidth); + expect(element.lineOpacity).eq(item.lineOpacity); + if (item.swatchType === "rectangle") { + expect(element.fillColor).eq(item.fillColor); + expect(element.filled).eq(item.filled); + expect(element.fillOpacity).eq(item.fillOpacity); + } + } + } + } +} + +describe("Legend tag tests", async () => { + it("legend includes unique styles, points separate, closed path not separate", async () => { + let core = await createTestCore({ + doenetML: ` + + (x+5)^2 + (-3,2) + + + (-5,6) + (0,-6) + + + + + + + + + + `, + }); + + let legendItems: LegendItem[] = [ + { + swatchType: "line", + label: "parabola and circle", + hasLatex: false, + }, + { + swatchType: "marker", + label: "\\(\\left( -3, 2 \\right)\\) and \\(\\left( -5, 6 \\right)\\)", + hasLatex: true, + }, + { swatchType: "line", label: "vector", hasLatex: false }, + { swatchType: "marker", label: "\\(r^2\\)", hasLatex: true }, + ]; + + await check_legend({ + core, + legendItems, + position: "upperright", + }); + }); + + it("displayClosedSwatches separates closed path", async () => { + let core = await createTestCore({ + doenetML: ` + + (x+5)^2 + (-3,2) + + + (-5,6) + (0,-6) + + + + + + + + + + + `, + }); + + let legendItems: LegendItem[] = [ + { swatchType: "line", label: "parabola", hasLatex: false }, + { + swatchType: "marker", + label: "\\(\\left( -3, 2 \\right)\\) and \\(\\left( -5, 6 \\right)\\)", + hasLatex: true, + }, + { swatchType: "rectangle", label: "circle", hasLatex: false }, + { swatchType: "line", label: "vector", hasLatex: false }, + { swatchType: "marker", label: "\\(r^2\\)", hasLatex: true }, + ]; + + await check_legend({ + core, + legendItems, + position: "upperright", + }); + }); + + it("legend with dynamical functions", async () => { + let core = await createTestCore({ + doenetML: ` + + + + + + + + + + + + + + + `, + }); + + let possibleLegendItems: LegendItem[] = [ + { swatchType: "line", label: "hi", hasLatex: false }, + { + swatchType: "line", + label: "\\(\\int_a^b f(x) \\,dx\\) is it!", + hasLatex: true, + }, + { swatchType: "line", label: "only this", hasLatex: false }, + { swatchType: "line", label: "\\(x^2\\)", hasLatex: true }, + ]; + + let position = "upperright"; + let numItems = 1; + await check_legend({ + core, + position, + legendItems: possibleLegendItems.slice(0, numItems), + }); + + position = "upperleft"; + await updateTextInputValue({ text: "upperLeft", name: "/pos", core }); + await check_legend({ + core, + position, + legendItems: possibleLegendItems.slice(0, numItems), + }); + + numItems = 2; + await updateMathInputValue({ latex: "3", name: "/n", core }); + await check_legend({ + core, + position, + legendItems: possibleLegendItems.slice(0, numItems), + }); + + await updateMathInputValue({ latex: "4", name: "/n", core }); + await check_legend({ + core, + position, + legendItems: possibleLegendItems.slice(0, numItems), + }); + + numItems = 3; + await updateMathInputValue({ latex: "5", name: "/n", core }); + await check_legend({ + core, + position, + legendItems: possibleLegendItems.slice(0, numItems), + }); + + position = "lowerright"; + await updateTextInputValue({ text: "LowerRight", name: "/pos", core }); + await check_legend({ + core, + position, + legendItems: possibleLegendItems.slice(0, numItems), + }); + + numItems = 4; + await updateMathInputValue({ latex: "8", name: "/n", core }); + await check_legend({ + core, + position, + legendItems: possibleLegendItems.slice(0, numItems), + }); + + numItems = 1; + await updateMathInputValue({ latex: "1", name: "/n", core }); + await check_legend({ + core, + position, + legendItems: possibleLegendItems.slice(0, numItems), + }); + + position = "lowerleft"; + await updateTextInputValue({ text: "lowerleft", name: "/pos", core }); + await check_legend({ + core, + position, + legendItems: possibleLegendItems.slice(0, numItems), + }); + + numItems = 4; + await updateMathInputValue({ latex: "10", name: "/n", core }); + await check_legend({ + core, + position, + legendItems: possibleLegendItems.slice(0, numItems), + }); + }); + + it("legend with forObject", async () => { + let core = await createTestCore({ + doenetML: ` + + + + + + + + + +

display closed swatches:

+ + + + (-3,2) + + + (-5,6) + (0,-6) + (x+5)^2 + + + + + + + + + + + + + + `, + }); + + async function check_items(displayClosedSwatches: boolean) { + let legendItems: LegendItem[] = [ + { + swatchType: "line", + label: "targeted function", + hasLatex: false, + lineStyle: "dashed", + lineColor: "blue", + lineWidth: 2, + lineOpacity: 0.8, + }, + { + swatchType: "line", + label: "first one", + hasLatex: false, + lineStyle: "solid", + lineColor: "cyan", + lineWidth: 3, + lineOpacity: 0.7, + }, + ]; + + if (displayClosedSwatches) { + legendItems.push({ + swatchType: "rectangle", + label: "second one \\(x^2\\)", + hasLatex: true, + lineStyle: "solid", + lineColor: "cyan", + lineWidth: 3, + lineOpacity: 0.7, + fillColor: "magenta", + filled: true, + fillOpacity: 0.5, + }); + + legendItems.push({ + swatchType: "marker", + label: "targeted point \\(B\\)", + hasLatex: true, + markerStyle: "triangle", + markerColor: "green", + markerSize: 5, + }); + legendItems.push({ + swatchType: "marker", + label: "third one", + hasLatex: false, + markerStyle: "square", + markerColor: "blue", + markerSize: 4, + }); + legendItems.push({ + swatchType: "rectangle", + label: "fourth one", + hasLatex: false, + lineStyle: "dashed", + lineColor: "blue", + lineWidth: 2, + lineOpacity: 0.8, + fillColor: "orange", + filled: true, + fillOpacity: 0.4, + }); + } else { + legendItems.push({ + swatchType: "marker", + label: "second one \\(x^2\\)", + hasLatex: true, + markerStyle: "square", + markerColor: "blue", + markerSize: 4, + }); + + legendItems.push({ + swatchType: "marker", + label: "targeted point \\(B\\)", + hasLatex: true, + markerStyle: "triangle", + markerColor: "green", + markerSize: 5, + }); + legendItems.push({ + swatchType: "line", + label: "third one", + hasLatex: false, + lineStyle: "dotted", + lineColor: "black", + lineWidth: 4, + lineOpacity: 0.6, + }); + } + + await check_legend({ + core, + legendItems, + }); + } + + await check_items(false); + + // change displayClosedSwatches to true + await updateBooleanInputValue({ + boolean: true, + name: "/closedSwatches", + core, + }); + await check_items(true); + }); + + it("legend with forObject, use names of shadow sources", async () => { + let core = await createTestCore({ + doenetML: ` + + (3,4) + (4,5) + + + + + + + + + $p + $Q + + + + + + + `, + }); + + let legend1Items: LegendItem[] = [ + { swatchType: "marker", label: "point p", hasLatex: false }, + { swatchType: "marker", label: "point Q", hasLatex: false }, + ]; + + let legend2Items: LegendItem[] = [ + { swatchType: "marker", label: "point Q", hasLatex: false }, + { swatchType: "marker", label: "point p", hasLatex: false }, + ]; + + await check_legend({ + core, + legendName: "/legend1", + legendItems: legend1Items, + }); + + await check_legend({ + core, + legendName: "/legend2", + legendItems: legend2Items, + }); + }); +}); diff --git a/packages/doenetml-worker/src/test/tagSpecific/lorem.test.ts b/packages/doenetml-worker/src/test/tagSpecific/lorem.test.ts index a0506e480..72c1c77c5 100644 --- a/packages/doenetml-worker/src/test/tagSpecific/lorem.test.ts +++ b/packages/doenetml-worker/src/test/tagSpecific/lorem.test.ts @@ -4,6 +4,7 @@ import { updateMathInputValue } from "../utils/actions"; const Mock = vi.fn(); vi.stubGlobal("postMessage", Mock); +vi.mock("hyperformula"); describe("lorem tag tests", async () => { it("paragraphs, sentences, and words", async () => { @@ -96,17 +97,17 @@ describe("lorem tag tests", async () => { await updateMathInputValue({ latex: `${nParagraphs}`, - componentName: "/paragraphs/numPars", + name: "/paragraphs/numPars", core, }); await updateMathInputValue({ latex: `${nSentences}`, - componentName: "/sentences/numSens", + name: "/sentences/numSens", core, }); await updateMathInputValue({ latex: `${nWords}`, - componentName: "/words/numWords", + name: "/words/numWords", core, }); stateVariables = await returnAllStateVariables(core); diff --git a/packages/doenetml-worker/src/test/tagSpecific/map.test.ts b/packages/doenetml-worker/src/test/tagSpecific/map.test.ts index 833030f3f..fdfd5aaf3 100644 --- a/packages/doenetml-worker/src/test/tagSpecific/map.test.ts +++ b/packages/doenetml-worker/src/test/tagSpecific/map.test.ts @@ -1,12 +1,15 @@ import { describe, expect, it, vi } from "vitest"; import { createTestCore, returnAllStateVariables } from "../utils/test-core"; import { + movePoint, updateBooleanInputValue, updateMathInputValue, + updateValue, } from "../utils/actions"; const Mock = vi.fn(); vi.stubGlobal("postMessage", Mock); +vi.mock("hyperformula"); describe("Map tag tests", async () => { it("single map of maths", async () => { @@ -555,12 +558,7 @@ describe("Map tag tests", async () => { expect(stateVariables["/p3"].stateValues.text).eq(pText); // Double the length then test again - await core.requestAction({ - componentName: "/uv", - actionName: "updateValue", - args: {}, - event: null, - }); + await updateValue({ name: "/uv", core }); pText = textForLength(2); stateVariables = await returnAllStateVariables(core); @@ -569,12 +567,7 @@ describe("Map tag tests", async () => { expect(stateVariables["/p3"].stateValues.text).eq(pText); // Double the length again then test one more time - await core.requestAction({ - componentName: "/uv", - actionName: "updateValue", - args: {}, - event: null, - }); + await updateValue({ name: "/uv", core }); pText = textForLength(4); stateVariables = await returnAllStateVariables(core); @@ -814,7 +807,7 @@ describe("Map tag tests", async () => { count = 1; await updateMathInputValue({ latex: count.toString(), - componentName: "/sequenceCount", + name: "/sequenceCount", core, }); await check_items(from, to, count); @@ -823,7 +816,7 @@ describe("Map tag tests", async () => { count = 0; await updateMathInputValue({ latex: count.toString(), - componentName: "/sequenceCount", + name: "/sequenceCount", core, }); await check_items(from, to, count); @@ -832,7 +825,7 @@ describe("Map tag tests", async () => { count = 2; await updateMathInputValue({ latex: count.toString(), - componentName: "/sequenceCount", + name: "/sequenceCount", core, }); await check_items(from, to, count); @@ -842,12 +835,12 @@ describe("Map tag tests", async () => { to = 5; await updateMathInputValue({ latex: from.toString(), - componentName: "/sequenceFrom", + name: "/sequenceFrom", core, }); await updateMathInputValue({ latex: to.toString(), - componentName: "/sequenceTo", + name: "/sequenceTo", core, }); await check_items(from, to, count); @@ -856,7 +849,7 @@ describe("Map tag tests", async () => { count = 0; await updateMathInputValue({ latex: count.toString(), - componentName: "/sequenceCount", + name: "/sequenceCount", core, }); await check_items(from, to, count); @@ -865,7 +858,7 @@ describe("Map tag tests", async () => { count = 3; await updateMathInputValue({ latex: count.toString(), - componentName: "/sequenceCount", + name: "/sequenceCount", core, }); await check_items(from, to, count); @@ -974,12 +967,7 @@ describe("Map tag tests", async () => { let P1s = points_from_pars(q, r).P1s; - await core.requestAction({ - actionName: "movePoint", - componentName: "/a/P1", - args: { x: P1s[0][0], y: P1s[0][1] }, - event: null, - }); + await movePoint({ name: "/a/P1", x: P1s[0][0], y: P1s[0][1], core }); await check_items(q, r); // move point /a/P2 @@ -987,12 +975,7 @@ describe("Map tag tests", async () => { q = 1.8; let P2s = points_from_pars(q, r).P2s; - await core.requestAction({ - actionName: "movePoint", - componentName: "/a/P2", - args: { x: P2s[0][0], y: P2s[0][1] }, - event: null, - }); + await movePoint({ name: "/a/P2", x: P2s[0][0], y: P2s[0][1], core }); await check_items(q, r); // move point /b/P1 @@ -1000,12 +983,7 @@ describe("Map tag tests", async () => { q = 0.3; P1s = points_from_pars(q, r).P1s; - await core.requestAction({ - actionName: "movePoint", - componentName: "/b/P1", - args: { x: P1s[1][0], y: P1s[1][1] }, - event: null, - }); + await movePoint({ name: "/b/P1", x: P1s[1][0], y: P1s[1][1], core }); await check_items(q, r); // move point /b/P2 @@ -1013,12 +991,7 @@ describe("Map tag tests", async () => { q = 0.35; P2s = points_from_pars(q, r).P2s; - await core.requestAction({ - actionName: "movePoint", - componentName: "/b/P2", - args: { x: P2s[1][0], y: P2s[1][1] }, - event: null, - }); + await movePoint({ name: "/b/P2", x: P2s[1][0], y: P2s[1][1], core }); await check_items(q, r); // move point /c/P1 @@ -1026,12 +999,7 @@ describe("Map tag tests", async () => { q = -0.46; P1s = points_from_pars(q, r).P1s; - await core.requestAction({ - actionName: "movePoint", - componentName: "/c/P1", - args: { x: P1s[2][0], y: P1s[2][1] }, - event: null, - }); + await movePoint({ name: "/c/P1", x: P1s[2][0], y: P1s[2][1], core }); await check_items(q, r); // move point /c/P2 @@ -1039,12 +1007,7 @@ describe("Map tag tests", async () => { q = -0.73; P2s = points_from_pars(q, r).P2s; - await core.requestAction({ - actionName: "movePoint", - componentName: "/c/P2", - args: { x: P2s[2][0], y: P2s[2][1] }, - event: null, - }); + await movePoint({ name: "/c/P2", x: P2s[2][0], y: P2s[2][1], core }); await check_items(q, r); }); @@ -1149,7 +1112,7 @@ describe("Map tag tests", async () => { count = 1; await updateMathInputValue({ latex: count.toString(), - componentName: "/sequenceCount", + name: "/sequenceCount", core, }); await check_items(from, to, count); @@ -1158,7 +1121,7 @@ describe("Map tag tests", async () => { count = 0; await updateMathInputValue({ latex: count.toString(), - componentName: "/sequenceCount", + name: "/sequenceCount", core, }); await check_items(from, to, count); @@ -1167,7 +1130,7 @@ describe("Map tag tests", async () => { count = 2; await updateMathInputValue({ latex: count.toString(), - componentName: "/sequenceCount", + name: "/sequenceCount", core, }); await check_items(from, to, count); @@ -1177,12 +1140,12 @@ describe("Map tag tests", async () => { to = 5; await updateMathInputValue({ latex: from.toString(), - componentName: "/sequenceFrom", + name: "/sequenceFrom", core, }); await updateMathInputValue({ latex: to.toString(), - componentName: "/sequenceTo", + name: "/sequenceTo", core, }); await check_items(from, to, count); @@ -1191,7 +1154,7 @@ describe("Map tag tests", async () => { count = 0; await updateMathInputValue({ latex: count.toString(), - componentName: "/sequenceCount", + name: "/sequenceCount", core, }); await check_items(from, to, count); @@ -1200,7 +1163,7 @@ describe("Map tag tests", async () => { count = 3; await updateMathInputValue({ latex: count.toString(), - componentName: "/sequenceCount", + name: "/sequenceCount", core, }); await check_items(from, to, count); @@ -1273,7 +1236,7 @@ describe("Map tag tests", async () => { numPoints = 10; await updateMathInputValue({ latex: numPoints.toString(), - componentName: "/number", + name: "/number", core, }); await check_math_children(numPoints, step); @@ -1281,7 +1244,7 @@ describe("Map tag tests", async () => { step = 1; await updateMathInputValue({ latex: step.toString(), - componentName: "/step", + name: "/step", core, }); await check_math_children(numPoints, step); @@ -1289,7 +1252,7 @@ describe("Map tag tests", async () => { numPoints = 20; await updateMathInputValue({ latex: numPoints.toString(), - componentName: "/number", + name: "/number", core, }); await check_math_children(numPoints, step); @@ -1297,7 +1260,7 @@ describe("Map tag tests", async () => { step = 0.5; await updateMathInputValue({ latex: step.toString(), - componentName: "/step", + name: "/step", core, }); await check_math_children(numPoints, step); @@ -1305,7 +1268,7 @@ describe("Map tag tests", async () => { numPoints = 10; await updateMathInputValue({ latex: numPoints.toString(), - componentName: "/number", + name: "/number", core, }); await check_math_children(numPoints, step); @@ -1314,7 +1277,7 @@ describe("Map tag tests", async () => { step = 0; await updateMathInputValue({ latex: "", - componentName: "/step", + name: "/step", core, }); await check_math_children(numPoints, step); @@ -1322,7 +1285,7 @@ describe("Map tag tests", async () => { numPoints = 5; await updateMathInputValue({ latex: numPoints.toString(), - componentName: "/number", + name: "/number", core, }); await check_math_children(numPoints, step); @@ -1330,7 +1293,7 @@ describe("Map tag tests", async () => { step = -3; await updateMathInputValue({ latex: step.toString(), - componentName: "/step", + name: "/step", core, }); await check_math_children(numPoints, step); @@ -1413,7 +1376,7 @@ describe("Map tag tests", async () => { num = 2; await updateMathInputValue({ latex: num.toString(), - componentName: "/number", + name: "/number", core, }); await check_items(num); @@ -1422,7 +1385,7 @@ describe("Map tag tests", async () => { num = 1; await updateMathInputValue({ latex: num.toString(), - componentName: "/number", + name: "/number", core, }); await check_items(num); @@ -1431,7 +1394,7 @@ describe("Map tag tests", async () => { num = 3; await updateMathInputValue({ latex: num.toString(), - componentName: "/number", + name: "/number", core, }); await check_items(num); @@ -1440,7 +1403,7 @@ describe("Map tag tests", async () => { num = 0; await updateMathInputValue({ latex: num.toString(), - componentName: "/number", + name: "/number", core, }); await check_items(num); @@ -1449,7 +1412,7 @@ describe("Map tag tests", async () => { num = 1; await updateMathInputValue({ latex: num.toString(), - componentName: "/number", + name: "/number", core, }); await check_items(num); @@ -1488,22 +1451,22 @@ describe("Map tag tests", async () => { async function set_items(a: string, b: string, c: string, d: string) { await updateMathInputValue({ latex: a, - componentName: "/a/mi", + name: "/a/mi", core, }); await updateMathInputValue({ latex: b, - componentName: "/b/mi", + name: "/b/mi", core, }); await updateMathInputValue({ latex: c, - componentName: "/c/mi", + name: "/c/mi", core, }); await updateMathInputValue({ latex: d, - componentName: "/d/mi", + name: "/d/mi", core, }); } @@ -1573,12 +1536,12 @@ describe("Map tag tests", async () => { async function set_ns(n1: number, n2: number) { await updateMathInputValue({ latex: n1.toString(), - componentName: "/n1", + name: "/n1", core, }); await updateMathInputValue({ latex: n2.toString(), - componentName: "/n2", + name: "/n2", core, }); } @@ -1586,12 +1549,12 @@ describe("Map tag tests", async () => { async function set_hs(h1: boolean, h2: boolean) { await updateBooleanInputValue({ boolean: h1, - componentName: "/h1", + name: "/h1", core, }); await updateBooleanInputValue({ boolean: h2, - componentName: "/h2", + name: "/h2", core, }); } diff --git a/packages/doenetml-worker/src/test/tagSpecific/matchespattern.test.ts b/packages/doenetml-worker/src/test/tagSpecific/matchespattern.test.ts index 437bdf39d..1a7c07ba7 100644 --- a/packages/doenetml-worker/src/test/tagSpecific/matchespattern.test.ts +++ b/packages/doenetml-worker/src/test/tagSpecific/matchespattern.test.ts @@ -8,6 +8,7 @@ import { const Mock = vi.fn(); vi.stubGlobal("postMessage", Mock); +vi.mock("hyperformula"); describe("MatchesPattern tag tests", async () => { it("match linear pattern", async () => { @@ -177,7 +178,7 @@ describe("MatchesPattern tag tests", async () => { for (let expr in desiredResults) { await updateMathInputValue({ latex: expr, - componentName: "/expr", + name: "/expr", core, }); let stateVariables = await returnAllStateVariables(core); @@ -334,7 +335,7 @@ describe("MatchesPattern tag tests", async () => { for (let expr in desiredResults) { await updateMathInputValue({ latex: expr, - componentName: "/expr", + name: "/expr", core, }); let stateVariables = await returnAllStateVariables(core); @@ -485,7 +486,7 @@ describe("MatchesPattern tag tests", async () => { for (let expr in desiredResults) { await updateMathInputValue({ latex: expr, - componentName: "/expr", + name: "/expr", core, }); let stateVariables = await returnAllStateVariables(core); @@ -644,12 +645,12 @@ describe("MatchesPattern tag tests", async () => { for (let expr in desiredResults) { await updateMathInputValue({ latex: expr, - componentName: "/expr", + name: "/expr", core, }); await updateBooleanInputValue({ boolean: true, - componentName: "/matchBlanks", + name: "/matchBlanks", core, }); @@ -674,7 +675,7 @@ describe("MatchesPattern tag tests", async () => { await updateBooleanInputValue({ boolean: false, - componentName: "/matchBlanks", + name: "/matchBlanks", core, }); diff --git a/packages/doenetml-worker/src/test/tagSpecific/math.test.ts b/packages/doenetml-worker/src/test/tagSpecific/math.test.ts index ce02c9844..7f11833cd 100644 --- a/packages/doenetml-worker/src/test/tagSpecific/math.test.ts +++ b/packages/doenetml-worker/src/test/tagSpecific/math.test.ts @@ -2,14 +2,20 @@ import { describe, expect, it, vi } from "vitest"; import { createTestCore, returnAllStateVariables } from "../utils/test-core"; import { cleanLatex } from "../utils/math"; import { + moveMath, + movePoint, + moveVector, updateBooleanInputValue, updateMathInputValue, updateMatrixInputValue, updateTextInputValue, + updateValue, } from "../utils/actions"; +import { test_in_graph } from "../utils/in-graph"; const Mock = vi.fn(); vi.stubGlobal("postMessage", Mock); +vi.mock("hyperformula"); describe("Math tag tests", async () => { it("1+1", async () => { @@ -182,12 +188,7 @@ describe("Math tag tests", async () => { expect(stateVariables["/P"].stateValues.xs[1].tree).eq(3); // move point to (7,9) - await core.requestAction({ - componentName: "/P", - actionName: "movePoint", - args: { x: 7, y: 9 }, - event: null, - }); + await movePoint({ name: "/P", x: 7, y: 9, core }); stateVariables = await returnAllStateVariables(core); // Since we mark the component to ignore changes to defining children, @@ -267,7 +268,7 @@ describe("Math tag tests", async () => { // change format of second to latex await updateTextInputValue({ - componentName: "/ti2", + name: "/ti2", text: "latex", core, }); @@ -286,7 +287,7 @@ describe("Math tag tests", async () => { // change format of first to text await updateTextInputValue({ - componentName: "/ti1", + name: "/ti1", text: "text", core, }); @@ -301,7 +302,7 @@ describe("Math tag tests", async () => { // change format of second back to text await updateTextInputValue({ - componentName: "/ti2", + name: "/ti2", text: "text", core, }); @@ -318,7 +319,7 @@ describe("Math tag tests", async () => { // change format of first back to latex await updateTextInputValue({ - componentName: "/ti1", + name: "/ti1", text: "latex", core, }); @@ -1626,7 +1627,7 @@ describe("Math tag tests", async () => { // higher decimals await updateMathInputValue({ - componentName: "/nDecimals", + name: "/nDecimals", latex: "5", core, }); @@ -1637,7 +1638,7 @@ describe("Math tag tests", async () => { // lower decimals await updateMathInputValue({ - componentName: "/nDecimals", + name: "/nDecimals", latex: "-3", core, }); @@ -1648,7 +1649,7 @@ describe("Math tag tests", async () => { // increase digits await updateMathInputValue({ - componentName: "/nDigits", + name: "/nDigits", latex: "12", core, }); @@ -1659,7 +1660,7 @@ describe("Math tag tests", async () => { // invalid nDigits, falls back to decimals await updateMathInputValue({ - componentName: "/nDigits", + name: "/nDigits", latex: "x", core, }); @@ -1670,7 +1671,7 @@ describe("Math tag tests", async () => { // invalid both, no rounding await updateMathInputValue({ - componentName: "/nDecimals", + name: "/nDecimals", latex: "y", core, }); @@ -1681,7 +1682,7 @@ describe("Math tag tests", async () => { // only invalid nDecimals, falls back to digits await updateMathInputValue({ - componentName: "/nDigits", + name: "/nDigits", latex: "1", core, }); @@ -1692,7 +1693,7 @@ describe("Math tag tests", async () => { // negative decimals past number magnitude await updateMathInputValue({ - componentName: "/nDecimals", + name: "/nDecimals", latex: "-8", core, }); @@ -1703,7 +1704,7 @@ describe("Math tag tests", async () => { // becomes zero with no digits await updateMathInputValue({ - componentName: "/nDigits", + name: "/nDigits", latex: "0", core, }); @@ -1712,7 +1713,7 @@ describe("Math tag tests", async () => { // get number back with less rounding await updateMathInputValue({ - componentName: "/nDecimals", + name: "/nDecimals", latex: "-6", core, }); @@ -2513,24 +2514,24 @@ describe("Math tag tests", async () => { await check_values(["a", "b", "c"], "tuple"); // change xyz 1 - await updateMathInputValue({ componentName: "/mx", latex: "d", core }); - await updateMathInputValue({ componentName: "/my", latex: "e", core }); - await updateMathInputValue({ componentName: "/mz", latex: "f", core }); + await updateMathInputValue({ name: "/mx", latex: "d", core }); + await updateMathInputValue({ name: "/my", latex: "e", core }); + await updateMathInputValue({ name: "/mz", latex: "f", core }); await check_values(["d", "e", "f"], "tuple"); // change xyz 2 await updateMathInputValue({ - componentName: "/mx_2", + name: "/mx_2", latex: "g", core, }); await updateMathInputValue({ - componentName: "/my_2", + name: "/my_2", latex: "h", core, }); await updateMathInputValue({ - componentName: "/mz_2", + name: "/mz_2", latex: "i", core, }); @@ -2538,41 +2539,41 @@ describe("Math tag tests", async () => { // change xyz 3 await updateMathInputValue({ - componentName: "/mx_3", + name: "/mx_3", latex: "j", core, }); await updateMathInputValue({ - componentName: "/my_3", + name: "/my_3", latex: "k", core, }); await updateMathInputValue({ - componentName: "/mz_3", + name: "/mz_3", latex: "l", core, }); await check_values(["j", "k", "l"], "vector"); // change x1x2x3 1 - await updateMathInputValue({ componentName: "/mx1", latex: "m", core }); - await updateMathInputValue({ componentName: "/mx2", latex: "n", core }); - await updateMathInputValue({ componentName: "/mx3", latex: "o", core }); + await updateMathInputValue({ name: "/mx1", latex: "m", core }); + await updateMathInputValue({ name: "/mx2", latex: "n", core }); + await updateMathInputValue({ name: "/mx3", latex: "o", core }); await check_values(["m", "n", "o"], "vector"); // change x1x2x3 2 await updateMathInputValue({ - componentName: "/mx1_2", + name: "/mx1_2", latex: "p", core, }); await updateMathInputValue({ - componentName: "/mx2_2", + name: "/mx2_2", latex: "q", core, }); await updateMathInputValue({ - componentName: "/mx3_2", + name: "/mx3_2", latex: "r", core, }); @@ -2580,17 +2581,17 @@ describe("Math tag tests", async () => { // change x1x2x3 2 await updateMathInputValue({ - componentName: "/mx1_3", + name: "/mx1_3", latex: "s", core, }); await updateMathInputValue({ - componentName: "/mx2_3", + name: "/mx2_3", latex: "t", core, }); await updateMathInputValue({ - componentName: "/mx3_3", + name: "/mx3_3", latex: "u", core, }); @@ -2598,37 +2599,37 @@ describe("Math tag tests", async () => { // change to 4D list await updateMathInputValue({ - componentName: "/m", + name: "/m", latex: "v,w,x,y", core, }); await check_values(["v", "w", "x", "y"], "list"); // change x1x2x3x4 1 - await updateMathInputValue({ componentName: "/mx1", latex: "z", core }); - await updateMathInputValue({ componentName: "/mx2", latex: "a", core }); - await updateMathInputValue({ componentName: "/mx3", latex: "b", core }); - await updateMathInputValue({ componentName: "/mx4", latex: "c", core }); + await updateMathInputValue({ name: "/mx1", latex: "z", core }); + await updateMathInputValue({ name: "/mx2", latex: "a", core }); + await updateMathInputValue({ name: "/mx3", latex: "b", core }); + await updateMathInputValue({ name: "/mx4", latex: "c", core }); await check_values(["z", "a", "b", "c"], "list"); // change x1x2x3x4 2 await updateMathInputValue({ - componentName: "/mx1_2", + name: "/mx1_2", latex: "d", core, }); await updateMathInputValue({ - componentName: "/mx2_2", + name: "/mx2_2", latex: "e", core, }); await updateMathInputValue({ - componentName: "/mx3_2", + name: "/mx3_2", latex: "f", core, }); await updateMathInputValue({ - componentName: "/mx4_2", + name: "/mx4_2", latex: "g", core, }); @@ -2636,7 +2637,7 @@ describe("Math tag tests", async () => { // change to 3D alt vector await updateMathInputValue({ - componentName: "/m", + name: "/m", latex: "\\langle a,b,c\\rangle", core, }); @@ -2644,17 +2645,17 @@ describe("Math tag tests", async () => { // change xyz 3 await updateMathInputValue({ - componentName: "/mx_3", + name: "/mx_3", latex: "j", core, }); await updateMathInputValue({ - componentName: "/my_3", + name: "/my_3", latex: "k", core, }); await updateMathInputValue({ - componentName: "/mz_3", + name: "/mz_3", latex: "l", core, }); @@ -3006,7 +3007,7 @@ describe("Math tag tests", async () => { await updateBooleanInputValue({ boolean: false, - componentName: "/b1", + name: "/b1", core, }); @@ -3021,7 +3022,7 @@ describe("Math tag tests", async () => { await updateBooleanInputValue({ boolean: true, - componentName: "/b2", + name: "/b2", core, }); @@ -3036,7 +3037,7 @@ describe("Math tag tests", async () => { await updateBooleanInputValue({ boolean: false, - componentName: "/b3", + name: "/b3", core, }); @@ -3066,7 +3067,7 @@ describe("Math tag tests", async () => { await updateMathInputValue({ latex: "(x,y)", - componentName: "/mi", + name: "/mi", core, }); @@ -3096,12 +3097,7 @@ describe("Math tag tests", async () => { ]); // move dependent point - await core.requestAction({ - componentName: "/Q", - actionName: "movePoint", - args: { x: -2, y: 3 }, - event: null, - }); + await movePoint({ name: "/Q", x: -2, y: 3, core }); stateVariables = await returnAllStateVariables(core); @@ -3137,12 +3133,7 @@ describe("Math tag tests", async () => { ]); // move dependent point - await core.requestAction({ - componentName: "/Q", - actionName: "movePoint", - args: { x: -2, y: 3 }, - event: null, - }); + await movePoint({ name: "/Q", x: -2, y: 3, core }); stateVariables = await returnAllStateVariables(core); @@ -3179,12 +3170,7 @@ describe("Math tag tests", async () => { ]); // move dependent point - await core.requestAction({ - componentName: "/Q", - actionName: "movePoint", - args: { x: -2, y: 3 }, - event: null, - }); + await movePoint({ name: "/Q", x: -2, y: 3, core }); stateVariables = await returnAllStateVariables(core); @@ -3199,7 +3185,7 @@ describe("Math tag tests", async () => { // enter value in mathinput await updateMathInputValue({ latex: "(6,9)", - componentName: "/coords2", + name: "/coords2", core, }); @@ -3298,7 +3284,7 @@ describe("Math tag tests", async () => { await updateMathInputValue({ latex: "", - componentName: "/mi", + name: "/mi", core, }); stateVariables = await returnAllStateVariables(core); @@ -3310,7 +3296,7 @@ describe("Math tag tests", async () => { await updateMathInputValue({ latex: "q", - componentName: "/mi", + name: "/mi", core, }); stateVariables = await returnAllStateVariables(core); @@ -3336,7 +3322,7 @@ describe("Math tag tests", async () => { await updateMathInputValue({ latex: "", - componentName: "/mi", + name: "/mi", core, }); stateVariables = await returnAllStateVariables(core); @@ -3348,7 +3334,7 @@ describe("Math tag tests", async () => { await updateMathInputValue({ latex: "q", - componentName: "/mi", + name: "/mi", core, }); stateVariables = await returnAllStateVariables(core); @@ -3878,7 +3864,7 @@ describe("Math tag tests", async () => { await updateBooleanInputValue({ boolean: true, - componentName: "/displayBlanks", + name: "/displayBlanks", core, }); stateVariables = await returnAllStateVariables(core); @@ -6427,102 +6413,18 @@ describe("Math tag tests", async () => { // move vectors - await core.requestAction({ - actionName: "moveVector", - componentName: "/v1vb", - args: { - headcoords: [2, 1], - }, - event: null, - }); - await core.requestAction({ - actionName: "moveVector", - componentName: "/v2vb", - args: { - headcoords: [2, 2], - }, - event: null, - }); - await core.requestAction({ - actionName: "moveVector", - componentName: "/v3vb", - args: { - headcoords: [2, 3], - }, - event: null, - }); - await core.requestAction({ - actionName: "moveVector", - componentName: "/v4vb", - args: { - headcoords: [2, 4], - }, - event: null, - }); - await core.requestAction({ - actionName: "moveVector", - componentName: "/v5vb", - args: { - headcoords: [2, 5], - }, - event: null, - }); - await core.requestAction({ - actionName: "moveVector", - componentName: "/v6vb", - args: { - headcoords: [2, 6], - }, - event: null, - }); - await core.requestAction({ - actionName: "moveVector", - componentName: "/v7vb", - args: { - headcoords: [2, 7], - }, - event: null, - }); - await core.requestAction({ - actionName: "moveVector", - componentName: "/v8vb", - args: { - headcoords: [2, 8], - }, - event: null, - }); - await core.requestAction({ - actionName: "moveVector", - componentName: "/v9vb", - args: { - headcoords: [2, 9], - }, - event: null, - }); - await core.requestAction({ - actionName: "moveVector", - componentName: "/v10vb", - args: { - headcoords: [2, 10], - }, - event: null, - }); - await core.requestAction({ - actionName: "moveVector", - componentName: "/v11vb", - args: { - headcoords: [2, 11], - }, - event: null, - }); - await core.requestAction({ - actionName: "moveVector", - componentName: "/v12vb", - args: { - headcoords: [2, 12], - }, - event: null, - }); + await moveVector({ name: "/v1vb", headcoords: [2, 1], core }); + await moveVector({ name: "/v2vb", headcoords: [2, 2], core }); + await moveVector({ name: "/v3vb", headcoords: [2, 3], core }); + await moveVector({ name: "/v4vb", headcoords: [2, 4], core }); + await moveVector({ name: "/v5vb", headcoords: [2, 5], core }); + await moveVector({ name: "/v6vb", headcoords: [2, 6], core }); + await moveVector({ name: "/v7vb", headcoords: [2, 7], core }); + await moveVector({ name: "/v8vb", headcoords: [2, 8], core }); + await moveVector({ name: "/v9vb", headcoords: [2, 9], core }); + await moveVector({ name: "/v10vb", headcoords: [2, 10], core }); + await moveVector({ name: "/v11vb", headcoords: [2, 11], core }); + await moveVector({ name: "/v12vb", headcoords: [2, 12], core }); stateVariables = await returnAllStateVariables(core); @@ -6560,7 +6462,7 @@ describe("Math tag tests", async () => { // change from matrix inputs await updateMatrixInputValue({ latex: "3", - componentName: "/mi1", + name: "/mi1", rowInd: 0, colInd: 0, core, @@ -6568,7 +6470,7 @@ describe("Math tag tests", async () => { }); await updateMatrixInputValue({ latex: "-1", - componentName: "/mi1", + name: "/mi1", rowInd: 1, colInd: 0, core, @@ -6576,7 +6478,7 @@ describe("Math tag tests", async () => { }); await updateMatrixInputValue({ latex: "3", - componentName: "/mi2", + name: "/mi2", rowInd: 0, colInd: 0, core, @@ -6584,7 +6486,7 @@ describe("Math tag tests", async () => { }); await updateMatrixInputValue({ latex: "-2", - componentName: "/mi2", + name: "/mi2", rowInd: 0, colInd: 1, core, @@ -6592,7 +6494,7 @@ describe("Math tag tests", async () => { }); await updateMatrixInputValue({ latex: "3", - componentName: "/mi3", + name: "/mi3", rowInd: 0, colInd: 0, core, @@ -6600,7 +6502,7 @@ describe("Math tag tests", async () => { }); await updateMatrixInputValue({ latex: "-3", - componentName: "/mi3", + name: "/mi3", rowInd: 1, colInd: 0, core, @@ -6608,7 +6510,7 @@ describe("Math tag tests", async () => { }); await updateMatrixInputValue({ latex: "3", - componentName: "/mi4", + name: "/mi4", rowInd: 0, colInd: 0, core, @@ -6616,7 +6518,7 @@ describe("Math tag tests", async () => { }); await updateMatrixInputValue({ latex: "-4", - componentName: "/mi4", + name: "/mi4", rowInd: 0, colInd: 1, core, @@ -6624,7 +6526,7 @@ describe("Math tag tests", async () => { }); await updateMatrixInputValue({ latex: "3", - componentName: "/mi5", + name: "/mi5", rowInd: 0, colInd: 0, core, @@ -6632,7 +6534,7 @@ describe("Math tag tests", async () => { }); await updateMatrixInputValue({ latex: "-5", - componentName: "/mi5", + name: "/mi5", rowInd: 0, colInd: 1, core, @@ -6640,7 +6542,7 @@ describe("Math tag tests", async () => { }); await updateMatrixInputValue({ latex: "3", - componentName: "/mi6", + name: "/mi6", rowInd: 0, colInd: 0, core, @@ -6648,7 +6550,7 @@ describe("Math tag tests", async () => { }); await updateMatrixInputValue({ latex: "-6", - componentName: "/mi6", + name: "/mi6", rowInd: 1, colInd: 0, core, @@ -6656,7 +6558,7 @@ describe("Math tag tests", async () => { }); await updateMatrixInputValue({ latex: "3", - componentName: "/mi7", + name: "/mi7", rowInd: 0, colInd: 0, core, @@ -6664,7 +6566,7 @@ describe("Math tag tests", async () => { }); await updateMatrixInputValue({ latex: "-7", - componentName: "/mi7", + name: "/mi7", rowInd: 1, colInd: 0, core, @@ -6672,7 +6574,7 @@ describe("Math tag tests", async () => { }); await updateMatrixInputValue({ latex: "3", - componentName: "/mi8", + name: "/mi8", rowInd: 0, colInd: 0, core, @@ -6680,7 +6582,7 @@ describe("Math tag tests", async () => { }); await updateMatrixInputValue({ latex: "-8", - componentName: "/mi8", + name: "/mi8", rowInd: 0, colInd: 1, core, @@ -6688,7 +6590,7 @@ describe("Math tag tests", async () => { }); await updateMatrixInputValue({ latex: "3", - componentName: "/mi9", + name: "/mi9", rowInd: 0, colInd: 0, core, @@ -6696,7 +6598,7 @@ describe("Math tag tests", async () => { }); await updateMatrixInputValue({ latex: "-9", - componentName: "/mi9", + name: "/mi9", rowInd: 0, colInd: 1, core, @@ -6704,7 +6606,7 @@ describe("Math tag tests", async () => { }); await updateMatrixInputValue({ latex: "3", - componentName: "/mi10", + name: "/mi10", rowInd: 0, colInd: 0, core, @@ -6712,7 +6614,7 @@ describe("Math tag tests", async () => { }); await updateMatrixInputValue({ latex: "-10", - componentName: "/mi10", + name: "/mi10", rowInd: 1, colInd: 0, core, @@ -6720,7 +6622,7 @@ describe("Math tag tests", async () => { }); await updateMatrixInputValue({ latex: "3", - componentName: "/mi11", + name: "/mi11", rowInd: 0, colInd: 0, core, @@ -6728,7 +6630,7 @@ describe("Math tag tests", async () => { }); await updateMatrixInputValue({ latex: "-11", - componentName: "/mi11", + name: "/mi11", rowInd: 0, colInd: 1, core, @@ -6736,7 +6638,7 @@ describe("Math tag tests", async () => { }); await updateMatrixInputValue({ latex: "3", - componentName: "/mi12", + name: "/mi12", rowInd: 0, colInd: 0, core, @@ -6744,7 +6646,7 @@ describe("Math tag tests", async () => { }); await updateMatrixInputValue({ latex: "-12", - componentName: "/mi12", + name: "/mi12", rowInd: 0, colInd: 1, core, @@ -7420,7 +7322,7 @@ describe("Math tag tests", async () => { // change from matrix inputs await updateMatrixInputValue({ latex: "a", - componentName: "/mi1", + name: "/mi1", rowInd: 0, colInd: 0, core, @@ -7428,7 +7330,7 @@ describe("Math tag tests", async () => { }); await updateMatrixInputValue({ latex: "b", - componentName: "/mi1", + name: "/mi1", rowInd: 0, colInd: 1, core, @@ -7436,7 +7338,7 @@ describe("Math tag tests", async () => { }); await updateMatrixInputValue({ latex: "c", - componentName: "/mi1", + name: "/mi1", rowInd: 1, colInd: 0, core, @@ -7444,7 +7346,7 @@ describe("Math tag tests", async () => { }); await updateMatrixInputValue({ latex: "d", - componentName: "/mi1", + name: "/mi1", rowInd: 1, colInd: 1, core, @@ -7452,7 +7354,7 @@ describe("Math tag tests", async () => { }); await updateMatrixInputValue({ latex: "e", - componentName: "/mi1", + name: "/mi1", rowInd: 2, colInd: 0, core, @@ -7460,7 +7362,7 @@ describe("Math tag tests", async () => { }); await updateMatrixInputValue({ latex: "f", - componentName: "/mi1", + name: "/mi1", rowInd: 2, colInd: 1, core, @@ -7469,7 +7371,7 @@ describe("Math tag tests", async () => { await updateMatrixInputValue({ latex: "g", - componentName: "/mi2", + name: "/mi2", rowInd: 0, colInd: 0, core, @@ -7477,7 +7379,7 @@ describe("Math tag tests", async () => { }); await updateMatrixInputValue({ latex: "h", - componentName: "/mi2", + name: "/mi2", rowInd: 0, colInd: 1, core, @@ -7485,7 +7387,7 @@ describe("Math tag tests", async () => { }); await updateMatrixInputValue({ latex: "i", - componentName: "/mi2", + name: "/mi2", rowInd: 1, colInd: 0, core, @@ -7493,7 +7395,7 @@ describe("Math tag tests", async () => { }); await updateMatrixInputValue({ latex: "j", - componentName: "/mi2", + name: "/mi2", rowInd: 1, colInd: 1, core, @@ -7501,7 +7403,7 @@ describe("Math tag tests", async () => { }); await updateMatrixInputValue({ latex: "k", - componentName: "/mi2", + name: "/mi2", rowInd: 2, colInd: 0, core, @@ -7509,7 +7411,7 @@ describe("Math tag tests", async () => { }); await updateMatrixInputValue({ latex: "l", - componentName: "/mi2", + name: "/mi2", rowInd: 2, colInd: 1, core, @@ -7518,7 +7420,7 @@ describe("Math tag tests", async () => { await updateMatrixInputValue({ latex: "m", - componentName: "/mi3", + name: "/mi3", rowInd: 0, colInd: 0, core, @@ -7526,7 +7428,7 @@ describe("Math tag tests", async () => { }); await updateMatrixInputValue({ latex: "n", - componentName: "/mi3", + name: "/mi3", rowInd: 0, colInd: 1, core, @@ -7534,7 +7436,7 @@ describe("Math tag tests", async () => { }); await updateMatrixInputValue({ latex: "o", - componentName: "/mi3", + name: "/mi3", rowInd: 1, colInd: 0, core, @@ -7542,7 +7444,7 @@ describe("Math tag tests", async () => { }); await updateMatrixInputValue({ latex: "p", - componentName: "/mi3", + name: "/mi3", rowInd: 1, colInd: 1, core, @@ -7550,7 +7452,7 @@ describe("Math tag tests", async () => { }); await updateMatrixInputValue({ latex: "q", - componentName: "/mi3", + name: "/mi3", rowInd: 2, colInd: 0, core, @@ -7558,7 +7460,7 @@ describe("Math tag tests", async () => { }); await updateMatrixInputValue({ latex: "r", - componentName: "/mi3", + name: "/mi3", rowInd: 2, colInd: 1, core, @@ -7695,7 +7597,7 @@ describe("Math tag tests", async () => { await updateBooleanInputValue({ boolean: true, - componentName: "/p1", + name: "/p1", core, }); stateVariables = await returnAllStateVariables(core); @@ -7715,7 +7617,7 @@ describe("Math tag tests", async () => { await updateBooleanInputValue({ boolean: true, - componentName: "/p2", + name: "/p2", core, }); stateVariables = await returnAllStateVariables(core); @@ -7729,7 +7631,7 @@ describe("Math tag tests", async () => { await updateBooleanInputValue({ boolean: false, - componentName: "/p1", + name: "/p1", core, }); stateVariables = await returnAllStateVariables(core); @@ -7749,7 +7651,7 @@ describe("Math tag tests", async () => { await updateBooleanInputValue({ boolean: false, - componentName: "/p2", + name: "/p2", core, }); stateVariables = await returnAllStateVariables(core); @@ -7790,448 +7692,21 @@ describe("Math tag tests", async () => { ); expect(stateVariables["/m3t"].stateValues.text).eq("a² - b₂"); - await core.requestAction({ - componentName: "/uv", - actionName: "updateValue", - args: {}, - event: null, - }); + await updateValue({ name: "/uv", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/m3t"].stateValues.text).eq("a₂ - b²"); }); it("math in graph", async () => { - let core = await createTestCore({ - doenetML: ` - - $content1 - e^(-x^2) - - -

Anchor 1 coordinates:

-

Anchor 2 coordinates:

-

Change anchor 1 coordinates:

-

Change anchor 2 coordinates:

-

Position from anchor 1: $math1.positionFromAnchor

-

Position from anchor 2: $math2.positionFromAnchor

-

Change position from anchor 1 - - upperRight - upperLeft - lowerRight - lowerLeft - left - right - top - bottom - center - -

-

Change position from anchor 2 - - upperRight - upperLeft - lowerRight - lowerLeft - left - right - top - bottom - center - -

-

Draggable 1: $draggable1

-

Draggable 2: $draggable2

-

Change draggable 1

-

Change draggable 2

-

Content 1

-

Content 2

-

Fixed 1: $fixed1

-

Fixed 2: $fixed2

-

Change fixed 1

-

Change fixed 2

-

FixLocation 1: $fixLocation1

-

FixLocation 2: $fixLocation2

-

Change fixLocation 1

-

Change fixLocation 2

- - `, - }); - - let stateVariables = await returnAllStateVariables(core); - - expect(cleanLatex(stateVariables["/math1anchor"].stateValues.latex)).eq( - "(1,3)", - ); - expect(cleanLatex(stateVariables["/math2anchor"].stateValues.latex)).eq( - "(0,0)", - ); - - expect(stateVariables["/pPositionFromAnchor1"].stateValues.text).eq( - "Position from anchor 1: upperright", - ); - expect(stateVariables["/pPositionFromAnchor2"].stateValues.text).eq( - "Position from anchor 2: center", - ); - expect( - stateVariables["/positionFromAnchor1"].stateValues.selectedIndices, - ).eqls([1]); - expect( - stateVariables["/positionFromAnchor2"].stateValues.selectedIndices, - ).eqls([9]); - - expect(stateVariables["/pDraggable1"].stateValues.text).eq( - "Draggable 1: true", - ); - expect(stateVariables["/pDraggable2"].stateValues.text).eq( - "Draggable 2: true", - ); - - expect(cleanLatex(stateVariables["/math1"].stateValues.latex)).eq( - "\\frac{x^{2}}{3}", - ); - expect(cleanLatex(stateVariables["/math2"].stateValues.latex)).eq( - "e^{-x^{2}}", - ); - - // move maths by dragging - await core.requestAction({ - actionName: "moveMath", - componentName: "/math1", - args: { x: -2, y: 3 }, - event: null, - }); - await core.requestAction({ - actionName: "moveMath", - componentName: "/math2", - args: { x: 4, y: -5 }, - event: null, - }); - - stateVariables = await returnAllStateVariables(core); - - expect(cleanLatex(stateVariables["/math1anchor"].stateValues.latex)).eq( - "(-2,3)", - ); - expect(cleanLatex(stateVariables["/math2anchor"].stateValues.latex)).eq( - "(4,-5)", - ); - - // move maths by entering coordinates - - await updateMathInputValue({ - latex: "(6,7)", - componentName: "/anchorCoords1", - core, - }); - await updateMathInputValue({ - latex: "(8,9)", - componentName: "/anchorCoords2", - core, - }); - - stateVariables = await returnAllStateVariables(core); - - expect(cleanLatex(stateVariables["/math1anchor"].stateValues.latex)).eq( - "(6,7)", - ); - expect(cleanLatex(stateVariables["/math2anchor"].stateValues.latex)).eq( - "(8,9)", - ); - - // change position from anchor - await core.requestAction({ - actionName: "updateSelectedIndices", - componentName: "/positionFromAnchor1", - args: { selectedIndices: [4] }, - event: null, - }); - await core.requestAction({ - actionName: "updateSelectedIndices", - componentName: "/positionFromAnchor2", - args: { selectedIndices: [3] }, - event: null, - }); - - stateVariables = await returnAllStateVariables(core); - - expect(stateVariables["/pPositionFromAnchor1"].stateValues.text).eq( - "Position from anchor 1: lowerleft", - ); - expect(stateVariables["/pPositionFromAnchor2"].stateValues.text).eq( - "Position from anchor 2: lowerright", - ); - - // make not draggable - await updateBooleanInputValue({ - boolean: false, - componentName: "/draggable1", - core, - }); - await updateBooleanInputValue({ - boolean: false, - componentName: "/draggable2", - core, - }); - stateVariables = await returnAllStateVariables(core); - - expect(stateVariables["/pDraggable1"].stateValues.text).eq( - "Draggable 1: false", - ); - expect(stateVariables["/pDraggable2"].stateValues.text).eq( - "Draggable 2: false", - ); - - // cannot move maths by dragging - await core.requestAction({ - actionName: "moveMath", - componentName: "/math1", - args: { x: -10, y: -9 }, - event: null, - }); - await core.requestAction({ - actionName: "moveMath", - componentName: "/math2", - args: { x: -8, y: -7 }, - event: null, - }); - - stateVariables = await returnAllStateVariables(core); - - expect(cleanLatex(stateVariables["/math1anchor"].stateValues.latex)).eq( - "(6,7)", - ); - expect(cleanLatex(stateVariables["/math2anchor"].stateValues.latex)).eq( - "(8,9)", - ); - - // change content of math - await updateMathInputValue({ - latex: "\\frac{x^{2}}{3}+5", - componentName: "/content1", - core, - }); - await updateMathInputValue({ - latex: "e^{-x^{2}}-a", - componentName: "/content2", - core, - }); - stateVariables = await returnAllStateVariables(core); - - expect(cleanLatex(stateVariables["/math1"].stateValues.latex)).eq( - "\\frac{x^{2}}{3}+5", - ); - expect(cleanLatex(stateVariables["/math2"].stateValues.latex)).eq( - "e^{-x^{2}}-a", - ); - - // make draggable again - await updateBooleanInputValue({ - boolean: true, - componentName: "/draggable1", - core, - }); - await updateBooleanInputValue({ - boolean: true, - componentName: "/draggable2", - core, - }); - stateVariables = await returnAllStateVariables(core); - - expect(stateVariables["/pDraggable1"].stateValues.text).eq( - "Draggable 1: true", - ); - expect(stateVariables["/pDraggable2"].stateValues.text).eq( - "Draggable 2: true", - ); - - await core.requestAction({ - actionName: "moveMath", - componentName: "/math1", - args: { x: -10, y: -9 }, - event: null, - }); - await core.requestAction({ - actionName: "moveMath", - componentName: "/math2", - args: { x: -8, y: -7 }, - event: null, - }); - stateVariables = await returnAllStateVariables(core); - - expect(cleanLatex(stateVariables["/math1anchor"].stateValues.latex)).eq( - "(-10,-9)", - ); - expect(cleanLatex(stateVariables["/math2anchor"].stateValues.latex)).eq( - "(-8,-7)", - ); - - // fix location - await updateBooleanInputValue({ - boolean: true, - componentName: "/fixLocation1", - core, - }); - await updateBooleanInputValue({ - boolean: true, - componentName: "/fixLocation2", - core, - }); - stateVariables = await returnAllStateVariables(core); - - expect(stateVariables["/pFixLocation1"].stateValues.text).eq( - "FixLocation 1: true", - ); - expect(stateVariables["/pFixLocation2"].stateValues.text).eq( - "FixLocation 2: true", - ); - - // can change coordinates entering coordinates only for math 1 - await updateMathInputValue({ - latex: "(3,4)", - componentName: "/anchorCoords2", - core, - }); - await updateMathInputValue({ - latex: "(1,2)", - componentName: "/anchorCoords1", - core, - }); - - stateVariables = await returnAllStateVariables(core); - - expect(cleanLatex(stateVariables["/math1anchor"].stateValues.latex)).eq( - "(1,2)", - ); - expect(cleanLatex(stateVariables["/math2anchor"].stateValues.latex)).eq( - "(-8,-7)", - ); - - // cannot move maths by dragging - await core.requestAction({ - actionName: "moveMath", - componentName: "/math1", - args: { x: 4, y: 6 }, - event: null, - }); - await core.requestAction({ - actionName: "moveMath", - componentName: "/math2", - args: { x: 7, y: 8 }, - event: null, - }); - stateVariables = await returnAllStateVariables(core); - - expect(cleanLatex(stateVariables["/math1anchor"].stateValues.latex)).eq( - "(1,2)", - ); - expect(cleanLatex(stateVariables["/math2anchor"].stateValues.latex)).eq( - "(-8,-7)", - ); - - // can change position from anchor only for math 1 - await core.requestAction({ - actionName: "updateSelectedIndices", - componentName: "/positionFromAnchor2", - args: { selectedIndices: [8] }, - event: null, - }); - await core.requestAction({ - actionName: "updateSelectedIndices", - componentName: "/positionFromAnchor1", - args: { selectedIndices: [7] }, - event: null, - }); - - stateVariables = await returnAllStateVariables(core); - - expect(stateVariables["/pPositionFromAnchor1"].stateValues.text).eq( - "Position from anchor 1: top", - ); - expect(stateVariables["/pPositionFromAnchor2"].stateValues.text).eq( - "Position from anchor 2: lowerright", - ); - - // make completely fixed - await updateBooleanInputValue({ - boolean: true, - componentName: "/fixed1", - core, - }); - await updateBooleanInputValue({ - boolean: true, - componentName: "/fixed2", - core, - }); - stateVariables = await returnAllStateVariables(core); - - expect(stateVariables["/pFixed1"].stateValues.text).eq("Fixed 1: true"); - expect(stateVariables["/pFixed2"].stateValues.text).eq("Fixed 2: true"); - - // can change coordinates entering coordinates only for math 1 - await updateMathInputValue({ - latex: "(7,8)", - componentName: "/anchorCoords2", - core, - }); - await updateMathInputValue({ - latex: "(5,6)", - componentName: "/anchorCoords1", - core, - }); - - stateVariables = await returnAllStateVariables(core); - - expect(cleanLatex(stateVariables["/math1anchor"].stateValues.latex)).eq( - "(5,6)", - ); - expect(cleanLatex(stateVariables["/math2anchor"].stateValues.latex)).eq( - "(-8,-7)", - ); - - // can change position from anchor only for math 1 - await core.requestAction({ - actionName: "updateSelectedIndices", - componentName: "/positionFromAnchor2", - args: { selectedIndices: [5] }, - event: null, - }); - await core.requestAction({ - actionName: "updateSelectedIndices", - componentName: "/positionFromAnchor1", - args: { selectedIndices: [6] }, - event: null, - }); - - stateVariables = await returnAllStateVariables(core); - - expect(stateVariables["/pPositionFromAnchor1"].stateValues.text).eq( - "Position from anchor 1: right", - ); - expect(stateVariables["/pPositionFromAnchor2"].stateValues.text).eq( - "Position from anchor 2: lowerright", - ); - - // can change content only for math 1 - await updateMathInputValue({ - latex: "e^{-x^{2}}-a+y", - componentName: "/content2", - core, - }); - await updateMathInputValue({ - latex: "\\frac{x^{2}}{3}+5+z", - componentName: "/content1", - core, - }); - stateVariables = await returnAllStateVariables(core); - - expect(cleanLatex(stateVariables["/math1"].stateValues.latex)).eq( - "\\frac{x^{2}}{3}+5+z", - ); - expect(cleanLatex(stateVariables["/math2"].stateValues.latex)).eq( - "e^{-x^{2}}-a", - ); + const doenetMLsnippet = ` + + "x^2/3 + e^(-x^2) + + `; + + await test_in_graph(doenetMLsnippet, moveMath); }); it("math in graph, handle bad anchor coordinates", async () => { @@ -8243,7 +7718,7 @@ describe("Math tag tests", async () => {

Anchor 1 coordinates:

-

Change anchor 1 coordinates:

+

Change anchor 1 coordinates:

`, @@ -8258,7 +7733,7 @@ describe("Math tag tests", async () => { // give good anchor coords await updateMathInputValue({ latex: "(6,7)", - componentName: "/anchorCoords1", + name: "/anchorCoords1", core, }); @@ -8271,7 +7746,7 @@ describe("Math tag tests", async () => { // give bad anchor coords again await updateMathInputValue({ latex: "q", - componentName: "/anchorCoords1", + name: "/anchorCoords1", core, }); @@ -8327,7 +7802,7 @@ describe("Math tag tests", async () => { "none", ); - await updateMathInputValue({ latex: "2", componentName: "/sn", core }); + await updateMathInputValue({ latex: "2", name: "/sn", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/tsd_variable_style"].stateValues.text).eq( @@ -8348,7 +7823,7 @@ describe("Math tag tests", async () => { expect(stateVariables["/tc_fixed_style"].stateValues.text).eq("green"); expect(stateVariables["/bc_fixed_style"].stateValues.text).eq("none"); - await updateMathInputValue({ latex: "3", componentName: "/sn", core }); + await updateMathInputValue({ latex: "3", name: "/sn", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/tsd_variable_style"].stateValues.text).eq( @@ -8491,18 +7966,8 @@ describe("Math tag tests", async () => { ); // move first maths - await core.requestAction({ - actionName: "moveMath", - componentName: "/m1", - args: { x: -2, y: 3 }, - event: null, - }); - await core.requestAction({ - actionName: "moveMath", - componentName: "/m2", - args: { x: 4, y: -5 }, - event: null, - }); + await moveMath({ name: "/m1", x: -2, y: 3, core }); + await moveMath({ name: "/m2", x: 4, y: -5, core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/m1coords"].stateValues.latex)).eq( @@ -8525,18 +7990,8 @@ describe("Math tag tests", async () => { ); // move second maths - await core.requestAction({ - actionName: "moveMath", - componentName: "/m1a", - args: { x: 7, y: 1 }, - event: null, - }); - await core.requestAction({ - actionName: "moveMath", - componentName: "/m2a", - args: { x: -8, y: 2 }, - event: null, - }); + await moveMath({ name: "/m1a", x: 7, y: 1, core }); + await moveMath({ name: "/m2a", x: -8, y: 2, core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/m1coords"].stateValues.latex)).eq( @@ -8559,18 +8014,8 @@ describe("Math tag tests", async () => { ); // move third maths - await core.requestAction({ - actionName: "moveMath", - componentName: "/m1b", - args: { x: -6, y: 3 }, - event: null, - }); - await core.requestAction({ - actionName: "moveMath", - componentName: "/m2b", - args: { x: -5, y: -4 }, - event: null, - }); + await moveMath({ name: "/m1b", x: -6, y: 3, core }); + await moveMath({ name: "/m2b", x: -5, y: -4, core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/m1coords"].stateValues.latex)).eq( @@ -8953,10 +8398,10 @@ describe("Math tag tests", async () => { await updateMathInputValue({ latex: "(6,8)", - componentName: "/mi", + name: "/mi", core, }); - await updateMathInputValue({ latex: "8", componentName: "/mi2", core }); + await updateMathInputValue({ latex: "8", name: "/mi2", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/n"].stateValues.value).eq(3); diff --git a/packages/doenetml-worker/src/test/tagSpecific/mathinput.test.ts b/packages/doenetml-worker/src/test/tagSpecific/mathinput.test.ts index b09066cab..b006ca850 100644 --- a/packages/doenetml-worker/src/test/tagSpecific/mathinput.test.ts +++ b/packages/doenetml-worker/src/test/tagSpecific/mathinput.test.ts @@ -2,6 +2,7 @@ import { describe, expect, it, vi } from "vitest"; import { createTestCore, returnAllStateVariables } from "../utils/test-core"; import { cleanLatex } from "../utils/math"; import { + movePoint, updateBooleanInputValue, updateMathInputImmediateValue, updateMathInputValue, @@ -11,6 +12,7 @@ import me from "math-expressions"; const Mock = vi.fn(); vi.stubGlobal("postMessage", Mock); +vi.mock("hyperformula"); describe("MathInput tag tests", async () => { it("mathInput references", async () => { @@ -70,7 +72,7 @@ describe("MathInput tag tests", async () => { // Type 2 in first mathInput await updateMathInputImmediateValue({ latex: "x+12", - componentName: "/mi1", + name: "/mi1", core, }); stateVariables = await returnAllStateVariables(core); @@ -113,12 +115,12 @@ describe("MathInput tag tests", async () => { // Changing to 3 in first mathInput await updateMathInputImmediateValue({ latex: "x+1", - componentName: "/mi1", + name: "/mi1", core, }); await updateMathInputImmediateValue({ latex: "x+13", - componentName: "/mi1", + name: "/mi1", core, }); stateVariables = await returnAllStateVariables(core); @@ -159,7 +161,7 @@ describe("MathInput tag tests", async () => { // Update value (e.g., by pressing Enter) in first mathInput await updateMathInputValueToImmediateValue({ - componentName: "/mi1", + name: "/mi1", core, }); stateVariables = await returnAllStateVariables(core); @@ -202,17 +204,17 @@ describe("MathInput tag tests", async () => { // Erasing 13 and typing y second mathInput await updateMathInputImmediateValue({ latex: "x+1", - componentName: "/mi1a", + name: "/mi1a", core, }); await updateMathInputImmediateValue({ latex: "x+", - componentName: "/mi1a", + name: "/mi1a", core, }); await updateMathInputImmediateValue({ latex: "x+y", - componentName: "/mi1a", + name: "/mi1a", core, }); stateVariables = await returnAllStateVariables(core); @@ -254,7 +256,7 @@ describe("MathInput tag tests", async () => { // Update value (e.g., by changing focus) of second mathInput await updateMathInputValueToImmediateValue({ - componentName: "/mi1a", + name: "/mi1a", core, }); stateVariables = await returnAllStateVariables(core); @@ -297,12 +299,12 @@ describe("MathInput tag tests", async () => { // Typing pq in third mathInput await updateMathInputImmediateValue({ latex: "p", - componentName: "/mi2", + name: "/mi2", core, }); await updateMathInputImmediateValue({ latex: "pq", - componentName: "/mi2", + name: "/mi2", core, }); stateVariables = await returnAllStateVariables(core); @@ -346,7 +348,7 @@ describe("MathInput tag tests", async () => { // update value (e.g., update value) in mathInput 3 await updateMathInputValueToImmediateValue({ - componentName: "/mi2", + name: "/mi2", core, }); stateVariables = await returnAllStateVariables(core); @@ -395,22 +397,22 @@ describe("MathInput tag tests", async () => { // type abc in mathInput 2 await updateMathInputImmediateValue({ latex: "", - componentName: "/mi1a", + name: "/mi1a", core, }); await updateMathInputImmediateValue({ latex: "a", - componentName: "/mi1a", + name: "/mi1a", core, }); await updateMathInputImmediateValue({ latex: "ab", - componentName: "/mi1a", + name: "/mi1a", core, }); await updateMathInputImmediateValue({ latex: "abc", - componentName: "/mi1a", + name: "/mi1a", core, }); stateVariables = await returnAllStateVariables(core); @@ -460,7 +462,7 @@ describe("MathInput tag tests", async () => { // update value (e.g., blur) mathInput 2 await updateMathInputValueToImmediateValue({ - componentName: "/mi1a", + name: "/mi1a", core, }); stateVariables = await returnAllStateVariables(core); @@ -514,7 +516,7 @@ describe("MathInput tag tests", async () => { await updateMathInputImmediateValue({ latex: "", - componentName: "/mi1", + name: "/mi1", core, }); stateVariables = await returnAllStateVariables(core); @@ -525,21 +527,21 @@ describe("MathInput tag tests", async () => { await updateMathInputImmediateValue({ latex: "a", - componentName: "/mi1", + name: "/mi1", core, }); await updateMathInputImmediateValue({ latex: "ab", - componentName: "/mi1", + name: "/mi1", core, }); await updateMathInputImmediateValue({ latex: "abc", - componentName: "/mi1", + name: "/mi1", core, }); await updateMathInputValueToImmediateValue({ - componentName: "/mi1", + name: "/mi1", core, }); stateVariables = await returnAllStateVariables(core); @@ -593,22 +595,22 @@ describe("MathInput tag tests", async () => { await updateMathInputImmediateValue({ latex: "", - componentName: "/mi2", + name: "/mi2", core, }); await updateMathInputImmediateValue({ latex: "u", - componentName: "/mi2", + name: "/mi2", core, }); await updateMathInputImmediateValue({ latex: "u/", - componentName: "/mi2", + name: "/mi2", core, }); await updateMathInputImmediateValue({ latex: "u/v", - componentName: "/mi2", + name: "/mi2", core, }); @@ -661,13 +663,13 @@ describe("MathInput tag tests", async () => { // blue mathInput 2 and type d in mathInput 1 await updateMathInputValueToImmediateValue({ - componentName: "/mi2", + name: "/mi2", core, }); await updateMathInputImmediateValue({ latex: "abcd", - componentName: "/mi1", + name: "/mi1", core, }); @@ -722,7 +724,7 @@ describe("MathInput tag tests", async () => { // Update value (e.g., blur) first mathInput await updateMathInputValueToImmediateValue({ - componentName: "/mi1", + name: "/mi1", core, }); @@ -781,7 +783,7 @@ describe("MathInput tag tests", async () => { await updateMathInputImmediateValue({ latex: "", - componentName: "/mi1a", + name: "/mi1a", core, }); @@ -831,7 +833,7 @@ describe("MathInput tag tests", async () => { // update value (e.g., by blurring) of second mathInput await updateMathInputValueToImmediateValue({ - componentName: "/mi1a", + name: "/mi1a", core, }); @@ -896,12 +898,12 @@ describe("MathInput tag tests", async () => { // Type x~ in first mathinput await updateMathInputImmediateValue({ latex: "x", - componentName: "/mi1", + name: "/mi1", core, }); await updateMathInputValue({ latex: "x~", - componentName: "/mi1", + name: "/mi1", core, }); stateVariables = await returnAllStateVariables(core); @@ -924,17 +926,17 @@ describe("MathInput tag tests", async () => { // Delete ~ and add -y in copied mathinput await updateMathInputImmediateValue({ latex: "x", - componentName: "/mi1a", + name: "/mi1a", core, }); await updateMathInputImmediateValue({ latex: "x-", - componentName: "/mi1a", + name: "/mi1a", core, }); await updateMathInputImmediateValue({ latex: "x-y", - componentName: "/mi1a", + name: "/mi1a", core, }); stateVariables = await returnAllStateVariables(core); @@ -960,7 +962,7 @@ describe("MathInput tag tests", async () => { // update value (e.g., blur) await updateMathInputValueToImmediateValue({ - componentName: "/mi1a", + name: "/mi1a", core, }); stateVariables = await returnAllStateVariables(core); @@ -995,7 +997,7 @@ describe("MathInput tag tests", async () => { // Add & in copied mathinput await updateMathInputImmediateValue({ latex: "x-y@", - componentName: "/mi1a", + name: "/mi1a", core, }); stateVariables = await returnAllStateVariables(core); @@ -1025,23 +1027,23 @@ describe("MathInput tag tests", async () => { // Delete @ and add *z in first mathinput await updateMathInputValueToImmediateValue({ - componentName: "/mi1a", + name: "/mi1a", core, }); await updateMathInputImmediateValue({ latex: "x-y", - componentName: "/mi1", + name: "/mi1", core, }); await updateMathInputImmediateValue({ latex: "x-y*", - componentName: "/mi1", + name: "/mi1", core, }); await updateMathInputImmediateValue({ latex: "x-y*z", - componentName: "/mi1", + name: "/mi1", core, }); stateVariables = await returnAllStateVariables(core); @@ -1067,7 +1069,7 @@ describe("MathInput tag tests", async () => { // Update value (e.g., update value) await updateMathInputValueToImmediateValue({ - componentName: "/mi1", + name: "/mi1", core, }); stateVariables = await returnAllStateVariables(core); @@ -1136,12 +1138,12 @@ describe("MathInput tag tests", async () => { // Type x- in first mathinput await updateMathInputImmediateValue({ latex: "x", - componentName: "/mi1", + name: "/mi1", core, }); await updateMathInputValue({ latex: "x-", - componentName: "/mi1", + name: "/mi1", core, }); stateVariables = await returnAllStateVariables(core); @@ -1165,7 +1167,7 @@ describe("MathInput tag tests", async () => { // Add y in copied mathinput await updateMathInputImmediateValue({ latex: "x-y", - componentName: "/mi1a", + name: "/mi1a", core, }); stateVariables = await returnAllStateVariables(core); @@ -1191,7 +1193,7 @@ describe("MathInput tag tests", async () => { // update value (e.g., blur) await updateMathInputValueToImmediateValue({ - componentName: "/mi1a", + name: "/mi1a", core, }); stateVariables = await returnAllStateVariables(core); @@ -1227,7 +1229,7 @@ describe("MathInput tag tests", async () => { // Add * in copied mathinput await updateMathInputImmediateValue({ latex: "x-y*", - componentName: "/mi1a", + name: "/mi1a", core, }); stateVariables = await returnAllStateVariables(core); @@ -1261,12 +1263,12 @@ describe("MathInput tag tests", async () => { // Add z in first mathinput await updateMathInputValueToImmediateValue({ - componentName: "/mi1a", + name: "/mi1a", core, }); await updateMathInputImmediateValue({ latex: "x-y*z", - componentName: "/mi1", + name: "/mi1", core, }); stateVariables = await returnAllStateVariables(core); @@ -1300,7 +1302,7 @@ describe("MathInput tag tests", async () => { // Update value (e.g., update value) await updateMathInputValueToImmediateValue({ - componentName: "/mi1", + name: "/mi1", core, }); stateVariables = await returnAllStateVariables(core); @@ -1379,17 +1381,17 @@ describe("MathInput tag tests", async () => { // type new values await updateMathInputImmediateValue({ latex: "", - componentName: "/mi1", + name: "/mi1", core, }); await updateMathInputImmediateValue({ latex: "x", - componentName: "/mi1", + name: "/mi1", core, }); await updateMathInputImmediateValue({ latex: "xy", - componentName: "/mi1", + name: "/mi1", core, }); stateVariables = await returnAllStateVariables(core); @@ -1428,7 +1430,7 @@ describe("MathInput tag tests", async () => { // update value await updateMathInputValueToImmediateValue({ - componentName: "/mi1", + name: "/mi1", core, }); stateVariables = await returnAllStateVariables(core); @@ -1468,7 +1470,7 @@ describe("MathInput tag tests", async () => { // enter new values in referenced await updateMathInputValue({ latex: "qr", - componentName: "/mi1", + name: "/mi1", core, }); stateVariables = await returnAllStateVariables(core); @@ -1602,7 +1604,7 @@ describe("MathInput tag tests", async () => { // type new values await updateMathInputValue({ latex: "xy", - componentName: "/mi1", + name: "/mi1", core, }); stateVariables = await returnAllStateVariables(core); @@ -1648,7 +1650,7 @@ describe("MathInput tag tests", async () => { // enter new values in reffed await updateMathInputValue({ latex: "qr", - componentName: "/mi2", + name: "/mi2", core, }); stateVariables = await returnAllStateVariables(core); @@ -1737,17 +1739,17 @@ describe("MathInput tag tests", async () => { // type new values await updateMathInputImmediateValue({ latex: "", - componentName: "/mi1", + name: "/mi1", core, }); await updateMathInputImmediateValue({ latex: "x", - componentName: "/mi1", + name: "/mi1", core, }); await updateMathInputImmediateValue({ latex: "xy", - componentName: "/mi1", + name: "/mi1", core, }); stateVariables = await returnAllStateVariables(core); @@ -1786,7 +1788,7 @@ describe("MathInput tag tests", async () => { // update value await updateMathInputValueToImmediateValue({ - componentName: "/mi1", + name: "/mi1", core, }); stateVariables = await returnAllStateVariables(core); @@ -1826,7 +1828,7 @@ describe("MathInput tag tests", async () => { // enter new values in referenced await updateMathInputValue({ latex: "qr", - componentName: "/mi1", + name: "/mi1", core, }); stateVariables = await returnAllStateVariables(core); @@ -1938,7 +1940,7 @@ describe("MathInput tag tests", async () => { // type in new values await updateMathInputValue({ latex: "2y+1", - componentName: "/mi1", + name: "/mi1", core, }); stateVariables = await returnAllStateVariables(core); @@ -1982,7 +1984,7 @@ describe("MathInput tag tests", async () => { // type in new values await updateMathInputValue({ latex: "2z", - componentName: "/mi1", + name: "/mi1", core, }); stateVariables = await returnAllStateVariables(core); @@ -2054,7 +2056,7 @@ describe("MathInput tag tests", async () => { // type new values await updateMathInputValue({ latex: "xy", - componentName: "/mi1", + name: "/mi1", core, }); stateVariables = await returnAllStateVariables(core); @@ -2100,7 +2102,7 @@ describe("MathInput tag tests", async () => { // enter new values in reffed await updateMathInputValue({ latex: "qr", - componentName: "/mi2", + name: "/mi2", core, }); stateVariables = await returnAllStateVariables(core); @@ -2191,7 +2193,7 @@ describe("MathInput tag tests", async () => { // type new values await updateMathInputImmediateValue({ latex: "xy", - componentName: "/mi1", + name: "/mi1", core, }); stateVariables = await returnAllStateVariables(core); @@ -2231,7 +2233,7 @@ describe("MathInput tag tests", async () => { // value revert when updateValue (e.g., update value) await updateMathInputValueToImmediateValue({ - componentName: "/mi1", + name: "/mi1", core, }); stateVariables = await returnAllStateVariables(core); @@ -2273,7 +2275,7 @@ describe("MathInput tag tests", async () => { await updateMathInputImmediateValue({ latex: "qr", - componentName: "/mi2", + name: "/mi2", core, }); stateVariables = await returnAllStateVariables(core); @@ -2314,7 +2316,7 @@ describe("MathInput tag tests", async () => { // values revert when update value (e.g., blur) await updateMathInputValueToImmediateValue({ - componentName: "/mi2", + name: "/mi2", core, }); stateVariables = await returnAllStateVariables(core); @@ -2380,7 +2382,7 @@ describe("MathInput tag tests", async () => { // type new values await updateMathInputImmediateValue({ latex: "y", - componentName: "/mi1", + name: "/mi1", core, }); stateVariables = await returnAllStateVariables(core); @@ -2399,7 +2401,7 @@ describe("MathInput tag tests", async () => { // value revert when update value (e.g., press enter) await updateMathInputValueToImmediateValue({ - componentName: "/mi1", + name: "/mi1", core, }); stateVariables = await returnAllStateVariables(core); @@ -2420,7 +2422,7 @@ describe("MathInput tag tests", async () => { // type new values in copy await updateMathInputImmediateValue({ latex: "z", - componentName: "/mi2", + name: "/mi2", core, }); stateVariables = await returnAllStateVariables(core); @@ -2439,7 +2441,7 @@ describe("MathInput tag tests", async () => { // values revert when update value (e.g., blur) await updateMathInputValueToImmediateValue({ - componentName: "/mi2", + name: "/mi2", core, }); stateVariables = await returnAllStateVariables(core); @@ -2497,7 +2499,7 @@ describe("MathInput tag tests", async () => { // type 2 in first mathinput await updateMathInputImmediateValue({ latex: "x+12", - componentName: "/mi1", + name: "/mi1", core, }); stateVariables = await returnAllStateVariables(core); @@ -2531,7 +2533,7 @@ describe("MathInput tag tests", async () => { // update value (e.g., press enter) await updateMathInputValueToImmediateValue({ - componentName: "/mi1", + name: "/mi1", core, }); stateVariables = await returnAllStateVariables(core); @@ -2566,7 +2568,7 @@ describe("MathInput tag tests", async () => { // type 3 in second mathinput await updateMathInputImmediateValue({ latex: "x+123", - componentName: "/mi2", + name: "/mi2", core, }); stateVariables = await returnAllStateVariables(core); @@ -2601,7 +2603,7 @@ describe("MathInput tag tests", async () => { // update value (e.g., blur) await updateMathInputValueToImmediateValue({ - componentName: "/mi2", + name: "/mi2", core, }); stateVariables = await returnAllStateVariables(core); @@ -2675,7 +2677,7 @@ describe("MathInput tag tests", async () => { // type 2 in first mathinput await updateMathInputImmediateValue({ latex: "x+12", - componentName: "/mi1", + name: "/mi1", core, }); stateVariables = await returnAllStateVariables(core); @@ -2709,7 +2711,7 @@ describe("MathInput tag tests", async () => { // update value (e.g., press enter) await updateMathInputValueToImmediateValue({ - componentName: "/mi1", + name: "/mi1", core, }); stateVariables = await returnAllStateVariables(core); @@ -2744,7 +2746,7 @@ describe("MathInput tag tests", async () => { // type 3 in second mathinput await updateMathInputImmediateValue({ latex: "x+123", - componentName: "/mi2", + name: "/mi2", core, }); stateVariables = await returnAllStateVariables(core); @@ -2779,7 +2781,7 @@ describe("MathInput tag tests", async () => { // update value (e.g., blur) await updateMathInputValueToImmediateValue({ - componentName: "/mi2", + name: "/mi2", core, }); stateVariables = await returnAllStateVariables(core); @@ -2831,7 +2833,7 @@ describe("MathInput tag tests", async () => { await updateMathInputValue({ latex: "(1,2,3)", - componentName: "/a", + name: "/a", core, }); stateVariables = await returnAllStateVariables(core); @@ -2842,7 +2844,7 @@ describe("MathInput tag tests", async () => { await updateMathInputValue({ latex: "(2,3)", - componentName: "/a", + name: "/a", core, }); stateVariables = await returnAllStateVariables(core); @@ -2868,12 +2870,12 @@ describe("MathInput tag tests", async () => { await updateMathInputValue({ latex: "f(x)", - componentName: "/a", + name: "/a", core, }); await updateMathInputValue({ latex: "f(x)", - componentName: "/b", + name: "/b", core, }); @@ -2902,12 +2904,12 @@ describe("MathInput tag tests", async () => { await updateMathInputValue({ latex: "g(f)", - componentName: "/a", + name: "/a", core, }); await updateMathInputValue({ latex: "g(f)", - componentName: "/b", + name: "/b", core, }); @@ -2936,12 +2938,12 @@ describe("MathInput tag tests", async () => { await updateMathInputValue({ latex: "h(q)", - componentName: "/a", + name: "/a", core, }); await updateMathInputValue({ latex: "h(q)", - componentName: "/b", + name: "/b", core, }); @@ -2970,12 +2972,12 @@ describe("MathInput tag tests", async () => { await updateMathInputValue({ latex: "q(z)", - componentName: "/a", + name: "/a", core, }); await updateMathInputValue({ latex: "q(z)", - componentName: "/b", + name: "/b", core, }); @@ -3165,7 +3167,7 @@ describe("MathInput tag tests", async () => { await updateMathInputImmediateValue({ latex: "\\sin(345.15389319x)", - componentName: "/a", + name: "/a", core, }); @@ -3189,12 +3191,12 @@ describe("MathInput tag tests", async () => { ); await updateMathInputValueToImmediateValue({ - componentName: "/a", + name: "/a", core, }); await updateMathInputImmediateValue({ latex: "2.047529344518e^{0.0000073013048309y}", - componentName: "/b2", + name: "/b2", core, }); stateVariables = await returnAllStateVariables(core); @@ -3235,7 +3237,7 @@ describe("MathInput tag tests", async () => { ); await updateMathInputValueToImmediateValue({ - componentName: "/b2", + name: "/b2", core, }); stateVariables = await returnAllStateVariables(core); @@ -3367,7 +3369,7 @@ describe("MathInput tag tests", async () => { await updateMathInputImmediateValue({ latex: "\\sin(345.14x)", - componentName: "/a", + name: "/a", core, }); @@ -3441,7 +3443,7 @@ describe("MathInput tag tests", async () => { ]); await updateMathInputValueToImmediateValue({ - componentName: "/a", + name: "/a", core, }); @@ -3516,7 +3518,7 @@ describe("MathInput tag tests", async () => { await updateMathInputImmediateValue({ latex: "6.05e^{0.0000073y}", - componentName: "/b2", + name: "/b2", core, }); stateVariables = await returnAllStateVariables(core); @@ -3597,7 +3599,7 @@ describe("MathInput tag tests", async () => { ]); await updateMathInputValueToImmediateValue({ - componentName: "/b2", + name: "/b2", core, }); stateVariables = await returnAllStateVariables(core); @@ -3784,7 +3786,7 @@ describe("MathInput tag tests", async () => { await updateMathInputImmediateValue({ latex: "\\sin(345.15389319x)", - componentName: "/a", + name: "/a", core, }); @@ -3801,12 +3803,12 @@ describe("MathInput tag tests", async () => { ); await updateMathInputValueToImmediateValue({ - componentName: "/a", + name: "/a", core, }); await updateMathInputImmediateValue({ latex: "2.047529344518e^{0.0000073013048309y}", - componentName: "/b2", + name: "/b2", core, }); stateVariables = await returnAllStateVariables(core); @@ -3835,7 +3837,7 @@ describe("MathInput tag tests", async () => { ); await updateMathInputValueToImmediateValue({ - componentName: "/b2", + name: "/b2", core, }); stateVariables = await returnAllStateVariables(core); @@ -3921,7 +3923,7 @@ describe("MathInput tag tests", async () => { await updateMathInputImmediateValue({ latex: "\\sin(345.14x)", - componentName: "/a", + name: "/a", core, }); @@ -3969,7 +3971,7 @@ describe("MathInput tag tests", async () => { ]); await updateMathInputValueToImmediateValue({ - componentName: "/a", + name: "/a", core, }); @@ -4018,7 +4020,7 @@ describe("MathInput tag tests", async () => { await updateMathInputImmediateValue({ latex: "6.04752934e^{0.0000073y}", - componentName: "/b2", + name: "/b2", core, }); stateVariables = await returnAllStateVariables(core); @@ -4073,7 +4075,7 @@ describe("MathInput tag tests", async () => { ]); await updateMathInputValueToImmediateValue({ - componentName: "/b2", + name: "/b2", core, }); stateVariables = await returnAllStateVariables(core); @@ -4329,22 +4331,22 @@ describe("MathInput tag tests", async () => { await updateMathInputValue({ latex: "\\sin(0.000000000000000472946384739473x)", - componentName: "/a", + name: "/a", core, }); await updateMathInputValue({ latex: "0.0000000000000934720357236e^{0.0000000000000073013048309y}", - componentName: "/b2", + name: "/b2", core, }); await updateMathInputValue({ latex: "\\sin(0.000000000000000472946384739473x)", - componentName: "/c", + name: "/c", core, }); await updateMathInputValue({ latex: "0.0000000000000934720357236e^{0.0000000000000073013048309y}", - componentName: "/d2", + name: "/d2", core, }); @@ -4525,22 +4527,22 @@ describe("MathInput tag tests", async () => { await updateMathInputValue({ latex: "\\sin(5.7295\\cdot10^{-16}x)", - componentName: "/a", + name: "/a", core, }); await updateMathInputValue({ latex: "8.35\\cdot10^{-14}e^{7.3\\cdot10^{-15}y}", - componentName: "/b2", + name: "/b2", core, }); await updateMathInputValue({ latex: "\\sin(30x)", - componentName: "/c", + name: "/c", core, }); await updateMathInputValue({ latex: "6.35\\cdot10^{-14}e^{0y}", - componentName: "/d2", + name: "/d2", core, }); @@ -4766,7 +4768,7 @@ describe("MathInput tag tests", async () => { await updateMathInputValue({ latex: "98765.4321876", - componentName: "/a", + name: "/a", core, }); stateVariables = await returnAllStateVariables(core); @@ -4818,7 +4820,7 @@ describe("MathInput tag tests", async () => { await updateMathInputValue({ latex: "0.00000000000000004736286523434185", - componentName: "/a", + name: "/a", core, }); stateVariables = await returnAllStateVariables(core); @@ -4862,12 +4864,12 @@ describe("MathInput tag tests", async () => { await updateMathInputValue({ latex: "2.4295639461593", - componentName: "/a", + name: "/a", core, }); await updateMathInputValue({ latex: "9.3935596792746", - componentName: "/b2", + name: "/b2", core, }); stateVariables = await returnAllStateVariables(core); @@ -4887,11 +4889,11 @@ describe("MathInput tag tests", async () => { expect(stateVariables["/b2"].stateValues.valueForDisplay.tree).eq(9.39); expect(stateVariables["/p"].stateValues.xs[1].tree).eq(9.3935596792746); - await core.requestAction({ - componentName: "/p", - actionName: "movePoint", - args: { x: 7.936497798143, y: 2.142218345836 }, - event: null, + await movePoint({ + name: "/p", + x: 7.936497798143, + y: 2.142218345836, + core, }); stateVariables = await returnAllStateVariables(core); @@ -4935,12 +4937,12 @@ describe("MathInput tag tests", async () => { await updateMathInputValue({ latex: "2.4295639461593", - componentName: "/a", + name: "/a", core, }); await updateMathInputValue({ latex: "9.3935596792746", - componentName: "/b2", + name: "/b2", core, }); stateVariables = await returnAllStateVariables(core); @@ -4960,11 +4962,11 @@ describe("MathInput tag tests", async () => { expect(stateVariables["/b2"].stateValues.valueForDisplay.tree).eq(9.39); expect(stateVariables["/p"].stateValues.xs[1].tree).eq(9.3935596792746); - await core.requestAction({ - componentName: "/p", - actionName: "movePoint", - args: { x: 7.936497798143, y: 2.142218345836 }, - event: null, + await movePoint({ + name: "/p", + x: 7.936497798143, + y: 2.142218345836, + core, }); stateVariables = await returnAllStateVariables(core); @@ -4999,7 +5001,7 @@ describe("MathInput tag tests", async () => { // unicode α U+03B1 await updateMathInputValue({ latex: "α", - componentName: "/a", + name: "/a", core, }); stateVariables = await returnAllStateVariables(core); @@ -5021,7 +5023,7 @@ describe("MathInput tag tests", async () => { // latex \\alpha\\beta await updateMathInputValue({ latex: "\\alpha\\beta", - componentName: "/a", + name: "/a", core, }); stateVariables = await returnAllStateVariables(core); @@ -5056,7 +5058,7 @@ describe("MathInput tag tests", async () => { await updateMathInputValue({ latex: "y\u2212z", - componentName: "/a", + name: "/a", core, }); stateVariables = await returnAllStateVariables(core); @@ -5086,7 +5088,7 @@ describe("MathInput tag tests", async () => { await updateMathInputValue({ latex: "a-b", - componentName: "/a", + name: "/a", core, }); stateVariables = await returnAllStateVariables(core); @@ -5116,7 +5118,7 @@ describe("MathInput tag tests", async () => { await updateMathInputValue({ latex: "y\u22C5z", - componentName: "/a", + name: "/a", core, }); stateVariables = await returnAllStateVariables(core); @@ -5146,7 +5148,7 @@ describe("MathInput tag tests", async () => { await updateMathInputValue({ latex: "a*b", - componentName: "/a", + name: "/a", core, }); stateVariables = await returnAllStateVariables(core); @@ -5176,7 +5178,7 @@ describe("MathInput tag tests", async () => { await updateMathInputValue({ latex: "y\u00B7z", - componentName: "/a", + name: "/a", core, }); stateVariables = await returnAllStateVariables(core); @@ -5206,7 +5208,7 @@ describe("MathInput tag tests", async () => { await updateMathInputValue({ latex: "u\u00D7v", - componentName: "/a", + name: "/a", core, }); stateVariables = await returnAllStateVariables(core); @@ -5236,7 +5238,7 @@ describe("MathInput tag tests", async () => { await updateMathInputValue({ latex: "A\u222AB", - componentName: "/a", + name: "/a", core, }); stateVariables = await returnAllStateVariables(core); @@ -5270,7 +5272,7 @@ describe("MathInput tag tests", async () => { await updateMathInputValue({ latex: "A\u2229B", - componentName: "/a", + name: "/a", core, }); stateVariables = await returnAllStateVariables(core); @@ -5304,7 +5306,7 @@ describe("MathInput tag tests", async () => { await updateMathInputValue({ latex: "\u221E", - componentName: "/a", + name: "/a", core, }); stateVariables = await returnAllStateVariables(core); @@ -5326,7 +5328,7 @@ describe("MathInput tag tests", async () => { await updateMathInputValue({ latex: "\u00B5", - componentName: "/a", + name: "/a", core, }); stateVariables = await returnAllStateVariables(core); @@ -5344,7 +5346,7 @@ describe("MathInput tag tests", async () => { await updateMathInputValue({ latex: "\u03BC", - componentName: "/a", + name: "/a", core, }); stateVariables = await returnAllStateVariables(core); @@ -5362,7 +5364,7 @@ describe("MathInput tag tests", async () => { await updateMathInputValue({ latex: "f\u2032", - componentName: "/a", + name: "/a", core, }); stateVariables = await returnAllStateVariables(core); @@ -5403,7 +5405,7 @@ describe("MathInput tag tests", async () => { await updateMathInputValue({ latex: "3^25", - componentName: "/a", + name: "/a", core, }); stateVariables = await returnAllStateVariables(core); @@ -5422,7 +5424,7 @@ describe("MathInput tag tests", async () => { await updateMathInputValue({ latex: "3^{25}", - componentName: "/a", + name: "/a", core, }); stateVariables = await returnAllStateVariables(core); @@ -5433,7 +5435,7 @@ describe("MathInput tag tests", async () => { await updateMathInputValue({ latex: "3^{2x}", - componentName: "/a", + name: "/a", core, }); stateVariables = await returnAllStateVariables(core); @@ -5456,7 +5458,7 @@ describe("MathInput tag tests", async () => { await updateMathInputValue({ latex: "3^2x", - componentName: "/a", + name: "/a", core, }); stateVariables = await returnAllStateVariables(core); @@ -5479,7 +5481,7 @@ describe("MathInput tag tests", async () => { await updateMathInputValue({ latex: "3^{x2}", - componentName: "/a", + name: "/a", core, }); stateVariables = await returnAllStateVariables(core); @@ -5502,7 +5504,7 @@ describe("MathInput tag tests", async () => { await updateMathInputValue({ latex: "3^x2", - componentName: "/a", + name: "/a", core, }); stateVariables = await returnAllStateVariables(core); @@ -5525,7 +5527,7 @@ describe("MathInput tag tests", async () => { await updateMathInputValue({ latex: "f^32", - componentName: "/a", + name: "/a", core, }); stateVariables = await returnAllStateVariables(core); @@ -5548,7 +5550,7 @@ describe("MathInput tag tests", async () => { await updateMathInputValue({ latex: "x^32", - componentName: "/a", + name: "/a", core, }); stateVariables = await returnAllStateVariables(core); @@ -5584,7 +5586,7 @@ describe("MathInput tag tests", async () => { await updateMathInputValue({ latex: "3_25", - componentName: "/a", + name: "/a", core, }); stateVariables = await returnAllStateVariables(core); @@ -5607,7 +5609,7 @@ describe("MathInput tag tests", async () => { await updateMathInputValue({ latex: "3_{25}", - componentName: "/a", + name: "/a", core, }); stateVariables = await returnAllStateVariables(core); @@ -5618,7 +5620,7 @@ describe("MathInput tag tests", async () => { await updateMathInputValue({ latex: "3_{2x}", - componentName: "/a", + name: "/a", core, }); stateVariables = await returnAllStateVariables(core); @@ -5641,7 +5643,7 @@ describe("MathInput tag tests", async () => { await updateMathInputValue({ latex: "3_2x", - componentName: "/a", + name: "/a", core, }); stateVariables = await returnAllStateVariables(core); @@ -5664,7 +5666,7 @@ describe("MathInput tag tests", async () => { await updateMathInputValue({ latex: "3_{x2}", - componentName: "/a", + name: "/a", core, }); stateVariables = await returnAllStateVariables(core); @@ -5687,7 +5689,7 @@ describe("MathInput tag tests", async () => { await updateMathInputValue({ latex: "3_x2", - componentName: "/a", + name: "/a", core, }); stateVariables = await returnAllStateVariables(core); @@ -5710,7 +5712,7 @@ describe("MathInput tag tests", async () => { await updateMathInputValue({ latex: "f_32", - componentName: "/a", + name: "/a", core, }); stateVariables = await returnAllStateVariables(core); @@ -5733,7 +5735,7 @@ describe("MathInput tag tests", async () => { await updateMathInputValue({ latex: "x_32", - componentName: "/a", + name: "/a", core, }); stateVariables = await returnAllStateVariables(core); @@ -5790,7 +5792,7 @@ describe("MathInput tag tests", async () => { await updateMathInputValue({ latex: "-7.4", - componentName: "/mi", + name: "/mi", core, }); stateVariables = await returnAllStateVariables(core); @@ -5807,12 +5809,7 @@ describe("MathInput tag tests", async () => { // move point A - await core.requestAction({ - componentName: "/A", - actionName: "movePoint", - args: { x: 3.9, y: -8.4 }, - event: null, - }); + await movePoint({ name: "/A", x: 3.9, y: -8.4, core }); stateVariables = await returnAllStateVariables(core); @@ -5828,12 +5825,7 @@ describe("MathInput tag tests", async () => { // move point B - await core.requestAction({ - componentName: "/B", - actionName: "movePoint", - args: { x: 5.1, y: 1.3 }, - event: null, - }); + await movePoint({ name: "/B", x: 5.1, y: 1.3, core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/mi"].stateValues.rawRendererValue).eq("5"); @@ -5863,7 +5855,7 @@ describe("MathInput tag tests", async () => { await updateMathInputImmediateValue({ latex: "y", - componentName: "/mi", + name: "/mi", core, }); stateVariables = await returnAllStateVariables(core); @@ -5873,12 +5865,12 @@ describe("MathInput tag tests", async () => { await updateMathInputImmediateValue({ latex: "", - componentName: "/mi", + name: "/mi", core, }); await updateMathInputImmediateValue({ latex: "x", - componentName: "/mi", + name: "/mi", core, }); stateVariables = await returnAllStateVariables(core); @@ -5887,7 +5879,7 @@ describe("MathInput tag tests", async () => { expect(stateVariables["/x"].stateValues.value.tree).eq("x"); await updateMathInputValueToImmediateValue({ - componentName: "/mi", + name: "/mi", core, }); stateVariables = await returnAllStateVariables(core); @@ -5897,12 +5889,12 @@ describe("MathInput tag tests", async () => { await updateMathInputImmediateValue({ latex: "", - componentName: "/mi", + name: "/mi", core, }); await updateMathInputImmediateValue({ latex: "y", - componentName: "/mi", + name: "/mi", core, }); stateVariables = await returnAllStateVariables(core); @@ -5912,7 +5904,7 @@ describe("MathInput tag tests", async () => { await updateMathInputImmediateValue({ latex: "y+x", - componentName: "/mi", + name: "/mi", core, }); stateVariables = await returnAllStateVariables(core); @@ -5925,7 +5917,7 @@ describe("MathInput tag tests", async () => { expect(stateVariables["/x"].stateValues.value.tree).eqls(["*", 2, "x"]); await updateMathInputValueToImmediateValue({ - componentName: "/mi", + name: "/mi", core, }); stateVariables = await returnAllStateVariables(core); @@ -5959,12 +5951,12 @@ describe("MathInput tag tests", async () => { await updateMathInputValue({ latex: "xy", - componentName: "/mins", + name: "/mins", core, }); await updateMathInputValue({ latex: "xy", - componentName: "/mis", + name: "/mis", core, }); stateVariables = await returnAllStateVariables(core); @@ -5984,12 +5976,12 @@ describe("MathInput tag tests", async () => { await updateMathInputValue({ latex: "xy0", - componentName: "/mins", + name: "/mins", core, }); await updateMathInputValue({ latex: "xy0", - componentName: "/mis", + name: "/mis", core, }); stateVariables = await returnAllStateVariables(core); @@ -6001,12 +5993,12 @@ describe("MathInput tag tests", async () => { await updateMathInputValue({ latex: "xy_{uv}", - componentName: "/mins", + name: "/mins", core, }); await updateMathInputValue({ latex: "xy_{uv}", - componentName: "/mis", + name: "/mis", core, }); stateVariables = await returnAllStateVariables(core); @@ -6049,7 +6041,7 @@ describe("MathInput tag tests", async () => { await updateMathInputValue({ latex: "...x,y,z...", - componentName: "/mi", + name: "/mi", core, }); stateVariables = await returnAllStateVariables(core); @@ -6074,7 +6066,7 @@ describe("MathInput tag tests", async () => { await updateMathInputValue({ latex: "\\ .\\ .\\ .x,y,a..\\ .\\ ", - componentName: "/mi", + name: "/mi", core, }); stateVariables = await returnAllStateVariables(core); @@ -6100,7 +6092,7 @@ describe("MathInput tag tests", async () => { await updateMathInputValue({ latex: "\\ .\\ .\\ .,b,y,a..\\ .\\ ", - componentName: "/mi", + name: "/mi", core, }); stateVariables = await returnAllStateVariables(core); @@ -6126,7 +6118,7 @@ describe("MathInput tag tests", async () => { await updateMathInputValue({ latex: "\\ .\\ .\\ .,b,y,c,..\\ .\\ ", - componentName: "/mi", + name: "/mi", core, }); stateVariables = await returnAllStateVariables(core); @@ -6152,7 +6144,7 @@ describe("MathInput tag tests", async () => { await updateMathInputValue({ latex: "\\ .\\ .\\ .,b,y,d,\\ldots\\ ", - componentName: "/mi", + name: "/mi", core, }); stateVariables = await returnAllStateVariables(core); @@ -6178,7 +6170,7 @@ describe("MathInput tag tests", async () => { await updateMathInputValue({ latex: "\\ \\ldots\\ ,e,y,d,\\ldots\\ ", - componentName: "/mi", + name: "/mi", core, }); stateVariables = await returnAllStateVariables(core); @@ -6204,7 +6196,7 @@ describe("MathInput tag tests", async () => { await updateMathInputValue({ latex: "\\ \\ldots\\ f,y,d,\\ldots\\ ", - componentName: "/mi", + name: "/mi", core, }); stateVariables = await returnAllStateVariables(core); @@ -6230,7 +6222,7 @@ describe("MathInput tag tests", async () => { await updateMathInputValue({ latex: "\\ \\ldots\\ f,y,g\\ldots\\ ", - componentName: "/mi", + name: "/mi", core, }); stateVariables = await returnAllStateVariables(core); @@ -6275,12 +6267,12 @@ describe("MathInput tag tests", async () => { await updateMathInputValue({ latex: "xu9j", - componentName: "/varWithNum2", + name: "/varWithNum2", core, }); await updateMathInputValue({ latex: "xyuv", - componentName: "/noSplit2", + name: "/noSplit2", core, }); stateVariables = await returnAllStateVariables(core); @@ -6481,42 +6473,42 @@ describe("MathInput tag tests", async () => { await updateMathInputValue({ latex: "xyz", - componentName: "/splits1", + name: "/splits1", core, }); await updateMathInputValue({ latex: "xyz", - componentName: "/splits2", + name: "/splits2", core, }); await updateMathInputValue({ latex: "xyz", - componentName: "/splits3", + name: "/splits3", core, }); await updateMathInputValue({ latex: "xyz", - componentName: "/splits4", + name: "/splits4", core, }); await updateMathInputValue({ latex: "xyz", - componentName: "/splits5", + name: "/splits5", core, }); await updateMathInputValue({ latex: "xyz", - componentName: "/splits6", + name: "/splits6", core, }); await updateMathInputValue({ latex: "xyz", - componentName: "/splits7", + name: "/splits7", core, }); await updateMathInputValue({ latex: "xyz", - componentName: "/splits8", + name: "/splits8", core, }); stateVariables = await returnAllStateVariables(core); @@ -6760,42 +6752,42 @@ describe("MathInput tag tests", async () => { await updateMathInputValue({ latex: "h(y)", - componentName: "/hFunction1", + name: "/hFunction1", core, }); await updateMathInputValue({ latex: "h(y)", - componentName: "/hFunction2", + name: "/hFunction2", core, }); await updateMathInputValue({ latex: "h(y)", - componentName: "/hFunction3", + name: "/hFunction3", core, }); await updateMathInputValue({ latex: "h(y)", - componentName: "/hFunction4", + name: "/hFunction4", core, }); await updateMathInputValue({ latex: "h(y)", - componentName: "/hFunction5", + name: "/hFunction5", core, }); await updateMathInputValue({ latex: "h(y)", - componentName: "/hFunction6", + name: "/hFunction6", core, }); await updateMathInputValue({ latex: "h(y)", - componentName: "/hFunction7", + name: "/hFunction7", core, }); await updateMathInputValue({ latex: "h(y)", - componentName: "/hFunction8", + name: "/hFunction8", core, }); stateVariables = await returnAllStateVariables(core); @@ -7020,7 +7012,7 @@ describe("MathInput tag tests", async () => { await updateMathInputValue({ latex: "\\frac{a}{b} \\int_a^b f(x) dx", - componentName: "/input1", + name: "/input1", core, }); stateVariables = await returnAllStateVariables(core); @@ -7040,7 +7032,7 @@ describe("MathInput tag tests", async () => { await updateMathInputValue({ latex: "hello(a)(b)", - componentName: "/input2", + name: "/input2", core, }); stateVariables = await returnAllStateVariables(core); @@ -7078,7 +7070,7 @@ describe("MathInput tag tests", async () => { await updateMathInputValue({ latex: "\\text{h}(a)(b)", - componentName: "/input3", + name: "/input3", core, }); stateVariables = await returnAllStateVariables(core); @@ -7100,7 +7092,7 @@ describe("MathInput tag tests", async () => { await updateMathInputValue({ latex: "(a)(b)", - componentName: "/input3", + name: "/input3", core, }); stateVariables = await returnAllStateVariables(core); @@ -7139,7 +7131,7 @@ describe("MathInput tag tests", async () => { await updateMathInputValue({ latex: "x=1 or u=x", - componentName: "/mi", + name: "/mi", core, }); stateVariables = await returnAllStateVariables(core); @@ -7158,7 +7150,7 @@ describe("MathInput tag tests", async () => { // inequalities with and await updateMathInputValue({ latex: "x>3 and x \\le 5", - componentName: "/mi", + name: "/mi", core, }); stateVariables = await returnAllStateVariables(core); @@ -7177,7 +7169,7 @@ describe("MathInput tag tests", async () => { // don't convert if not word await updateMathInputValue({ latex: "AandBorC", - componentName: "/mi", + name: "/mi", core, }); stateVariables = await returnAllStateVariables(core); @@ -7208,7 +7200,7 @@ describe("MathInput tag tests", async () => { // add parens or spaces await updateMathInputValue({ latex: "(A)and B or(C)", - componentName: "/mi", + name: "/mi", core, }); stateVariables = await returnAllStateVariables(core); @@ -7242,7 +7234,7 @@ describe("MathInput tag tests", async () => { await updateMathInputValue({ latex: "A U C", - componentName: "/mi", + name: "/mi", core, }); stateVariables = await returnAllStateVariables(core); @@ -7264,12 +7256,12 @@ describe("MathInput tag tests", async () => { await updateBooleanInputValue({ boolean: true, - componentName: "/ufu", + name: "/ufu", core, }); await updateMathInputValue({ latex: "A U B", - componentName: "/mi", + name: "/mi", core, }); stateVariables = await returnAllStateVariables(core); @@ -7289,7 +7281,7 @@ describe("MathInput tag tests", async () => { await updateMathInputValue({ latex: "A UB", - componentName: "/mi", + name: "/mi", core, }); stateVariables = await returnAllStateVariables(core); @@ -7311,7 +7303,7 @@ describe("MathInput tag tests", async () => { await updateMathInputValue({ latex: "A U(B)", - componentName: "/mi", + name: "/mi", core, }); stateVariables = await returnAllStateVariables(core); @@ -7347,7 +7339,7 @@ describe("MathInput tag tests", async () => { await updateMathInputValue({ latex: "3", - componentName: "/x1", + name: "/x1", core, }); stateVariables = await returnAllStateVariables(core); @@ -7358,7 +7350,7 @@ describe("MathInput tag tests", async () => { await updateMathInputValue({ latex: "4", - componentName: "/x2", + name: "/x2", core, }); stateVariables = await returnAllStateVariables(core); @@ -7387,7 +7379,7 @@ describe("MathInput tag tests", async () => { await updateMathInputValue({ latex: "3", - componentName: "/x1", + name: "/x1", core, }); stateVariables = await returnAllStateVariables(core); @@ -7398,7 +7390,7 @@ describe("MathInput tag tests", async () => { await updateMathInputValue({ latex: "4", - componentName: "/x2", + name: "/x2", core, }); stateVariables = await returnAllStateVariables(core); @@ -7449,7 +7441,7 @@ describe("MathInput tag tests", async () => { await updateMathInputValue({ latex: "(1,5)", - componentName: "/mipf", + name: "/mipf", core, }); stateVariables = await returnAllStateVariables(core); @@ -7484,7 +7476,7 @@ describe("MathInput tag tests", async () => { await updateMathInputValue({ latex: "(1,9)", - componentName: "/mi", + name: "/mi", core, }); stateVariables = await returnAllStateVariables(core); @@ -7519,7 +7511,7 @@ describe("MathInput tag tests", async () => { await updateMathInputValue({ latex: "(1,7)", - componentName: "/mipf", + name: "/mipf", core, }); stateVariables = await returnAllStateVariables(core); @@ -7571,7 +7563,7 @@ describe("MathInput tag tests", async () => { await updateMathInputImmediateValue({ latex: "5", - componentName: "/mi", + name: "/mi", core, }); stateVariables = await returnAllStateVariables(core); @@ -7585,7 +7577,7 @@ describe("MathInput tag tests", async () => { // update value (e.g., hit enter) await updateMathInputValueToImmediateValue({ - componentName: "/mi", + name: "/mi", core, }); stateVariables = await returnAllStateVariables(core); @@ -7600,7 +7592,7 @@ describe("MathInput tag tests", async () => { await updateMathInputImmediateValue({ latex: "\\pi", - componentName: "/mi", + name: "/mi", core, }); stateVariables = await returnAllStateVariables(core); @@ -7616,7 +7608,7 @@ describe("MathInput tag tests", async () => { // update value (e.g., hit enter) await updateMathInputValueToImmediateValue({ - componentName: "/mi", + name: "/mi", core, }); stateVariables = await returnAllStateVariables(core); @@ -7640,7 +7632,7 @@ describe("MathInput tag tests", async () => { // type x await updateMathInputImmediateValue({ latex: "x", - componentName: "/mi", + name: "/mi", core, }); stateVariables = await returnAllStateVariables(core); @@ -7657,7 +7649,7 @@ describe("MathInput tag tests", async () => { // update value (e.g., hit enter) await updateMathInputValueToImmediateValue({ - componentName: "/mi", + name: "/mi", core, }); stateVariables = await returnAllStateVariables(core); @@ -7672,7 +7664,7 @@ describe("MathInput tag tests", async () => { await updateMathInputImmediateValue({ latex: "2/3", - componentName: "/mi", + name: "/mi", core, }); stateVariables = await returnAllStateVariables(core); @@ -7690,7 +7682,7 @@ describe("MathInput tag tests", async () => { // update value (e.g., hit enter) await updateMathInputValueToImmediateValue({ - componentName: "/mi", + name: "/mi", core, }); stateVariables = await returnAllStateVariables(core); @@ -7732,7 +7724,7 @@ describe("MathInput tag tests", async () => { await updateMathInputImmediateValue({ latex: "5", - componentName: "/mi", + name: "/mi", core, }); stateVariables = await returnAllStateVariables(core); @@ -7746,7 +7738,7 @@ describe("MathInput tag tests", async () => { // update value (e.g., hit enter) await updateMathInputValueToImmediateValue({ - componentName: "/mi", + name: "/mi", core, }); stateVariables = await returnAllStateVariables(core); @@ -7761,7 +7753,7 @@ describe("MathInput tag tests", async () => { await updateMathInputImmediateValue({ latex: "\\pi", - componentName: "/mi", + name: "/mi", core, }); stateVariables = await returnAllStateVariables(core); @@ -7777,7 +7769,7 @@ describe("MathInput tag tests", async () => { // update value (e.g., hit enter) await updateMathInputValueToImmediateValue({ - componentName: "/mi", + name: "/mi", core, }); stateVariables = await returnAllStateVariables(core); @@ -7801,7 +7793,7 @@ describe("MathInput tag tests", async () => { // type x await updateMathInputImmediateValue({ latex: "x", - componentName: "/mi", + name: "/mi", core, }); stateVariables = await returnAllStateVariables(core); @@ -7818,7 +7810,7 @@ describe("MathInput tag tests", async () => { // update value (e.g., hit enter) await updateMathInputValueToImmediateValue({ - componentName: "/mi", + name: "/mi", core, }); stateVariables = await returnAllStateVariables(core); @@ -7833,7 +7825,7 @@ describe("MathInput tag tests", async () => { await updateMathInputImmediateValue({ latex: "2/3", - componentName: "/mi", + name: "/mi", core, }); stateVariables = await returnAllStateVariables(core); @@ -7851,7 +7843,7 @@ describe("MathInput tag tests", async () => { // update value (e.g., hit enter) await updateMathInputValueToImmediateValue({ - componentName: "/mi", + name: "/mi", core, }); stateVariables = await returnAllStateVariables(core); @@ -7893,7 +7885,7 @@ describe("MathInput tag tests", async () => { await updateMathInputImmediateValue({ latex: "5", - componentName: "/mi", + name: "/mi", core, }); stateVariables = await returnAllStateVariables(core); @@ -7907,7 +7899,7 @@ describe("MathInput tag tests", async () => { // update value (e.g., hit enter) await updateMathInputValueToImmediateValue({ - componentName: "/mi", + name: "/mi", core, }); stateVariables = await returnAllStateVariables(core); @@ -7922,7 +7914,7 @@ describe("MathInput tag tests", async () => { await updateMathInputImmediateValue({ latex: "\\pi", - componentName: "/mi", + name: "/mi", core, }); stateVariables = await returnAllStateVariables(core); @@ -7938,7 +7930,7 @@ describe("MathInput tag tests", async () => { // update value (e.g., hit enter) await updateMathInputValueToImmediateValue({ - componentName: "/mi", + name: "/mi", core, }); stateVariables = await returnAllStateVariables(core); @@ -7962,7 +7954,7 @@ describe("MathInput tag tests", async () => { // type x await updateMathInputImmediateValue({ latex: "x", - componentName: "/mi", + name: "/mi", core, }); stateVariables = await returnAllStateVariables(core); @@ -7979,7 +7971,7 @@ describe("MathInput tag tests", async () => { // update value (e.g., hit enter) await updateMathInputValueToImmediateValue({ - componentName: "/mi", + name: "/mi", core, }); stateVariables = await returnAllStateVariables(core); @@ -7994,7 +7986,7 @@ describe("MathInput tag tests", async () => { await updateMathInputImmediateValue({ latex: "2/3", - componentName: "/mi", + name: "/mi", core, }); stateVariables = await returnAllStateVariables(core); @@ -8012,7 +8004,7 @@ describe("MathInput tag tests", async () => { // update value (e.g., hit enter) await updateMathInputValueToImmediateValue({ - componentName: "/mi", + name: "/mi", core, }); stateVariables = await returnAllStateVariables(core); @@ -8056,7 +8048,7 @@ describe("MathInput tag tests", async () => { await updateMathInputImmediateValue({ latex: "5", - componentName: "/mi", + name: "/mi", core, }); stateVariables = await returnAllStateVariables(core); @@ -8070,7 +8062,7 @@ describe("MathInput tag tests", async () => { // update value (e.g., hit enter) await updateMathInputValueToImmediateValue({ - componentName: "/mi", + name: "/mi", core, }); stateVariables = await returnAllStateVariables(core); @@ -8085,7 +8077,7 @@ describe("MathInput tag tests", async () => { await updateMathInputImmediateValue({ latex: "\\pi", - componentName: "/mi", + name: "/mi", core, }); stateVariables = await returnAllStateVariables(core); @@ -8101,7 +8093,7 @@ describe("MathInput tag tests", async () => { // update value (e.g., hit enter) await updateMathInputValueToImmediateValue({ - componentName: "/mi", + name: "/mi", core, }); stateVariables = await returnAllStateVariables(core); @@ -8115,7 +8107,7 @@ describe("MathInput tag tests", async () => { // type x await updateMathInputImmediateValue({ latex: "x", - componentName: "/mi", + name: "/mi", core, }); stateVariables = await returnAllStateVariables(core); @@ -8129,7 +8121,7 @@ describe("MathInput tag tests", async () => { // update value (e.g., hit enter) await updateMathInputValueToImmediateValue({ - componentName: "/mi", + name: "/mi", core, }); stateVariables = await returnAllStateVariables(core); @@ -8144,7 +8136,7 @@ describe("MathInput tag tests", async () => { await updateMathInputImmediateValue({ latex: "2/3", - componentName: "/mi", + name: "/mi", core, }); stateVariables = await returnAllStateVariables(core); @@ -8162,7 +8154,7 @@ describe("MathInput tag tests", async () => { // update value (e.g., hit enter) await updateMathInputValueToImmediateValue({ - componentName: "/mi", + name: "/mi", core, }); stateVariables = await returnAllStateVariables(core); @@ -8193,7 +8185,7 @@ describe("MathInput tag tests", async () => { // enter value that parses to math await updateMathInputValue({ latex: "a", - componentName: "/mi", + name: "/mi", core, }); stateVariables = await returnAllStateVariables(core); @@ -8207,7 +8199,7 @@ describe("MathInput tag tests", async () => { await updateMathInputValue({ latex: "a^{ }", - componentName: "/mi", + name: "/mi", core, }); stateVariables = await returnAllStateVariables(core); @@ -8231,7 +8223,7 @@ describe("MathInput tag tests", async () => { await updateMathInputValue({ latex: "a^{bc+}", - componentName: "/mi", + name: "/mi", core, }); stateVariables = await returnAllStateVariables(core); @@ -8255,7 +8247,7 @@ describe("MathInput tag tests", async () => { await updateMathInputValue({ latex: "a^{bc+d}", - componentName: "/mi", + name: "/mi", core, }); stateVariables = await returnAllStateVariables(core); @@ -8279,7 +8271,7 @@ describe("MathInput tag tests", async () => { await updateMathInputValue({ latex: "a^{bc+d}-", - componentName: "/mi", + name: "/mi", core, }); stateVariables = await returnAllStateVariables(core); @@ -8303,7 +8295,7 @@ describe("MathInput tag tests", async () => { await updateMathInputValue({ latex: "a^{bc+d}-e", - componentName: "/mi", + name: "/mi", core, }); stateVariables = await returnAllStateVariables(core); @@ -8343,7 +8335,7 @@ describe("MathInput tag tests", async () => { // enter value that parses to math await updateMathInputValue({ latex: "a", - componentName: "/mi", + name: "/mi", core, }); stateVariables = await returnAllStateVariables(core); @@ -8357,7 +8349,7 @@ describe("MathInput tag tests", async () => { await updateMathInputValue({ latex: "a@", - componentName: "/mi", + name: "/mi", core, }); stateVariables = await returnAllStateVariables(core); @@ -8373,7 +8365,7 @@ describe("MathInput tag tests", async () => { await updateMathInputValue({ latex: "ab+@", - componentName: "/mi", + name: "/mi", core, }); stateVariables = await returnAllStateVariables(core); @@ -8389,7 +8381,7 @@ describe("MathInput tag tests", async () => { await updateMathInputValue({ latex: "ab+c", - componentName: "/mi", + name: "/mi", core, }); stateVariables = await returnAllStateVariables(core); @@ -8439,7 +8431,7 @@ describe("MathInput tag tests", async () => { await updateMathInputValue({ latex: "2x-3E+2", - componentName: "/mi1", + name: "/mi1", core, }); stateVariables = await returnAllStateVariables(core); @@ -8462,7 +8454,7 @@ describe("MathInput tag tests", async () => { await updateMathInputValue({ latex: "2x-3E+2", - componentName: "/mi2", + name: "/mi2", core, }); stateVariables = await returnAllStateVariables(core); @@ -8499,17 +8491,17 @@ describe("MathInput tag tests", async () => { await updateMathInputValue({ latex: "12,345", - componentName: "/mi1", + name: "/mi1", core, }); await updateMathInputValue({ latex: "12,345", - componentName: "/mi2", + name: "/mi2", core, }); await updateMathInputValue({ latex: "12,345", - componentName: "/mi3", + name: "/mi3", core, }); stateVariables = await returnAllStateVariables(core); @@ -8524,17 +8516,17 @@ describe("MathInput tag tests", async () => { await updateMathInputValue({ latex: "\\$45.23", - componentName: "/mi1", + name: "/mi1", core, }); await updateMathInputValue({ latex: "\\$45.23", - componentName: "/mi2", + name: "/mi2", core, }); await updateMathInputValue({ latex: "\\$45.23", - componentName: "/mi3", + name: "/mi3", core, }); stateVariables = await returnAllStateVariables(core); @@ -8549,17 +8541,17 @@ describe("MathInput tag tests", async () => { await updateMathInputValue({ latex: "78\\%", - componentName: "/mi1", + name: "/mi1", core, }); await updateMathInputValue({ latex: "78\\%", - componentName: "/mi2", + name: "/mi2", core, }); await updateMathInputValue({ latex: "78\\%", - componentName: "/mi3", + name: "/mi3", core, }); stateVariables = await returnAllStateVariables(core); @@ -8574,17 +8566,17 @@ describe("MathInput tag tests", async () => { await updateMathInputValue({ latex: "\\$34,000\\%dx", - componentName: "/mi1", + name: "/mi1", core, }); await updateMathInputValue({ latex: "\\$34,000\\%dx", - componentName: "/mi2", + name: "/mi2", core, }); await updateMathInputValue({ latex: "\\$34,000\\%dx", - componentName: "/mi3", + name: "/mi3", core, }); stateVariables = await returnAllStateVariables(core); @@ -8617,7 +8609,7 @@ describe("MathInput tag tests", async () => { await updateMathInputValue({ latex: "\\sqrt{4}", - componentName: "/mi", + name: "/mi", core, }); stateVariables = await returnAllStateVariables(core); @@ -8740,14 +8732,12 @@ describe("MathInput tag tests", async () => { [mi1ivchanged, mi2ivchanged, mi3ivchanged, mi4ivchanged], ); - let stateVariables = await returnAllStateVariables(core); - // type in first marks only first immediate value as changed mi1iv = "y"; mi1ivchanged = true; await updateMathInputImmediateValue({ latex: mi1iv, - componentName: "/mi1", + name: "/mi1", core, }); @@ -8762,7 +8752,7 @@ describe("MathInput tag tests", async () => { mi1 = mi3 = mi3iv = mi1iv; mi1changed = true; await updateMathInputValueToImmediateValue({ - componentName: "/mi1", + name: "/mi1", core, }); @@ -8778,7 +8768,7 @@ describe("MathInput tag tests", async () => { mi2ivchanged = true; await updateMathInputImmediateValue({ latex: mi2iv, - componentName: "/mi2", + name: "/mi2", core, }); @@ -8793,7 +8783,7 @@ describe("MathInput tag tests", async () => { mi2 = mi2iv; mi2changed = true; await updateMathInputValueToImmediateValue({ - componentName: "/mi2", + name: "/mi2", core, }); @@ -8809,7 +8799,7 @@ describe("MathInput tag tests", async () => { mi3ivchanged = true; await updateMathInputImmediateValue({ latex: mi3iv, - componentName: "/mi3", + name: "/mi3", core, }); @@ -8825,7 +8815,7 @@ describe("MathInput tag tests", async () => { mi3changed = true; await updateMathInputValueToImmediateValue({ - componentName: "/mi3", + name: "/mi3", core, }); @@ -8841,7 +8831,7 @@ describe("MathInput tag tests", async () => { mi4ivchanged = true; await updateMathInputImmediateValue({ latex: mi4iv, - componentName: "/mi4", + name: "/mi4", core, }); @@ -8856,7 +8846,7 @@ describe("MathInput tag tests", async () => { mi2 = mi2iv = mi4 = mi4iv; mi4changed = true; await updateMathInputValueToImmediateValue({ - componentName: "/mi4", + name: "/mi4", core, }); @@ -8902,7 +8892,7 @@ describe("MathInput tag tests", async () => { mi3ivchanged = true; await updateMathInputImmediateValue({ latex: mi3iv, - componentName: "/mi3", + name: "/mi3", core, }); await check_items( @@ -8919,7 +8909,7 @@ describe("MathInput tag tests", async () => { mi3changed = true; await updateMathInputValueToImmediateValue({ - componentName: "/mi3", + name: "/mi3", core, }); await check_items( @@ -8935,7 +8925,7 @@ describe("MathInput tag tests", async () => { await updateMathInputImmediateValue({ latex: mi4iv, - componentName: "/mi4", + name: "/mi4", core, }); await check_items( @@ -8951,7 +8941,7 @@ describe("MathInput tag tests", async () => { mi2ivchanged = true; mi4changed = true; await updateMathInputValueToImmediateValue({ - componentName: "/mi4", + name: "/mi4", core, }); await check_items( @@ -9011,7 +9001,7 @@ describe("MathInput tag tests", async () => { // Delete contents from mathinput 1 await updateMathInputImmediateValue({ latex: "", - componentName: "/mi1", + name: "/mi1", core, }); stateVariables = await returnAllStateVariables(core); @@ -9033,7 +9023,7 @@ describe("MathInput tag tests", async () => { // Contents of mathinput 1 restored on enter await updateMathInputValueToImmediateValue({ - componentName: "/mi1", + name: "/mi1", core, }); stateVariables = await returnAllStateVariables(core); @@ -9054,7 +9044,7 @@ describe("MathInput tag tests", async () => { await updateMathInputImmediateValue({ latex: "12", - componentName: "/mi1", + name: "/mi1", core, }); stateVariables = await returnAllStateVariables(core); @@ -9074,7 +9064,7 @@ describe("MathInput tag tests", async () => { // Contents of mathinput 1 restored on enter await updateMathInputValueToImmediateValue({ - componentName: "/mi1", + name: "/mi1", core, }); stateVariables = await returnAllStateVariables(core); @@ -9095,7 +9085,7 @@ describe("MathInput tag tests", async () => { await updateMathInputImmediateValue({ latex: "", - componentName: "/mi2", + name: "/mi2", core, }); stateVariables = await returnAllStateVariables(core); @@ -9117,7 +9107,7 @@ describe("MathInput tag tests", async () => { // Contents of mathinput 2 restored on enter await updateMathInputValueToImmediateValue({ - componentName: "/mi2", + name: "/mi2", core, }); stateVariables = await returnAllStateVariables(core); @@ -9138,7 +9128,7 @@ describe("MathInput tag tests", async () => { await updateMathInputImmediateValue({ latex: "12", - componentName: "/mi2", + name: "/mi2", core, }); stateVariables = await returnAllStateVariables(core); @@ -9158,7 +9148,7 @@ describe("MathInput tag tests", async () => { // Contents of mathinput 2 restored on enter await updateMathInputValueToImmediateValue({ - componentName: "/mi2", + name: "/mi2", core, }); stateVariables = await returnAllStateVariables(core); @@ -9197,7 +9187,7 @@ describe("MathInput tag tests", async () => { expect(stateVariables["/P"].stateValues.xs[1].tree).eq(2); // Enter -3 for x - await updateMathInputValue({ latex: "-3", componentName: "/x", core }); + await updateMathInputValue({ latex: "-3", name: "/x", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/x"].stateValues.value.tree).eq(-3); @@ -9206,7 +9196,7 @@ describe("MathInput tag tests", async () => { expect(stateVariables["/P"].stateValues.xs[1].tree).eq(2); // Enter -4 for y - await updateMathInputValue({ latex: "-4", componentName: "/y", core }); + await updateMathInputValue({ latex: "-4", name: "/y", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/x"].stateValues.value.tree).eq(-3); @@ -9215,12 +9205,7 @@ describe("MathInput tag tests", async () => { expect(stateVariables["/P"].stateValues.xs[1].tree).eq(-4); // move point to (5,-6) - await core.requestAction({ - actionName: "movePoint", - componentName: "/P", - args: { x: 5, y: -6 }, - event: null, - }); + await movePoint({ name: "/P", x: 5, y: -6, core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/x"].stateValues.value.tree).eq(5); @@ -9253,7 +9238,7 @@ describe("MathInput tag tests", async () => { // Enter -1.2 for x await updateMathInputValue({ latex: "-1.2", - componentName: "/x", + name: "/x", core, }); @@ -9264,12 +9249,7 @@ describe("MathInput tag tests", async () => { expect(stateVariables["/P"].stateValues.xs[1].tree).eq(2); // try to move point to (5,6), only y changes - await core.requestAction({ - actionName: "movePoint", - componentName: "/P", - args: { x: 5, y: 6 }, - event: null, - }); + await movePoint({ name: "/P", x: 5, y: 6, core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/x"].stateValues.value.tree).eq(-1.2); @@ -9303,7 +9283,7 @@ describe("MathInput tag tests", async () => { // Enter -1.5 for a await updateMathInputValue({ latex: "-1.5", - componentName: "/a", + name: "/a", core, }); stateVariables = await returnAllStateVariables(core); @@ -9313,12 +9293,7 @@ describe("MathInput tag tests", async () => { expect(stateVariables["/P"].stateValues.xs[1].tree).eq(-7); // try to move point to (5,6), only y changes - await core.requestAction({ - actionName: "movePoint", - componentName: "/P", - args: { x: 5, y: 6 }, - event: null, - }); + await movePoint({ name: "/P", x: 5, y: 6, core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/a"].stateValues.value.tree).eq(-1.5); expect(stateVariables["/b"].stateValues.value.tree).eq(2); @@ -9351,7 +9326,7 @@ describe("MathInput tag tests", async () => { // Enter -1.5 for a await updateMathInputValue({ latex: "-1.5", - componentName: "/a", + name: "/a", core, }); stateVariables = await returnAllStateVariables(core); @@ -9361,12 +9336,7 @@ describe("MathInput tag tests", async () => { expect(stateVariables["/P"].stateValues.xs[1].tree).eq(-7); // try to move point to (5,6) - await core.requestAction({ - actionName: "movePoint", - componentName: "/P", - args: { x: 5, y: 6 }, - event: null, - }); + await movePoint({ name: "/P", x: 5, y: 6, core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/a"].stateValues.value.tree).eqls(["/", 5, 2]); expect(stateVariables["/b"].stateValues.value.tree).eq(2); @@ -9624,7 +9594,7 @@ describe("MathInput tag tests", async () => { await updateMathInputValue({ latex: "(1,2)", - componentName: "/mi", + name: "/mi", core, }); math = me.fromAst(["vector", 1, 2]); @@ -9633,7 +9603,7 @@ describe("MathInput tag tests", async () => { i = 2; await updateMathInputValue({ latex: i.toString(), - componentName: "/i", + name: "/i", core, }); await check_items(math, i); @@ -9641,14 +9611,14 @@ describe("MathInput tag tests", async () => { i = 3; await updateMathInputValue({ latex: i.toString(), - componentName: "/i", + name: "/i", core, }); await check_items(math, i); await updateMathInputValue({ latex: "(a,b,c)", - componentName: "/mi", + name: "/mi", core, }); math = me.fromAst(["vector", "a", "b", "c"]); @@ -9657,7 +9627,7 @@ describe("MathInput tag tests", async () => { i = 4; await updateMathInputValue({ latex: i.toString(), - componentName: "/i", + name: "/i", core, }); await check_items(math, i); @@ -9665,14 +9635,14 @@ describe("MathInput tag tests", async () => { i = 2; await updateMathInputValue({ latex: i.toString(), - componentName: "/i", + name: "/i", core, }); await check_items(math, i); await updateMathInputValue({ latex: "xyz", - componentName: "/mi", + name: "/mi", core, }); math = me.fromAst(["*", "x", "y", "z"]); @@ -9681,14 +9651,14 @@ describe("MathInput tag tests", async () => { i = 1; await updateMathInputValue({ latex: i.toString(), - componentName: "/i", + name: "/i", core, }); await check_items(math, i); await updateMathInputValue({ latex: "p,q", - componentName: "/mi", + name: "/mi", core, }); math = me.fromAst(["list", "p", "q"]); @@ -9696,7 +9666,7 @@ describe("MathInput tag tests", async () => { await updateMathInputValue({ latex: "5,4,3", - componentName: "/mi", + name: "/mi", core, }); math = me.fromAst(["list", 5, 4, 3]); diff --git a/packages/doenetml-worker/src/test/tagSpecific/mathlist.test.ts b/packages/doenetml-worker/src/test/tagSpecific/mathlist.test.ts index 7c9f72eb9..bf34d259a 100644 --- a/packages/doenetml-worker/src/test/tagSpecific/mathlist.test.ts +++ b/packages/doenetml-worker/src/test/tagSpecific/mathlist.test.ts @@ -4,9 +4,11 @@ import { updateBooleanInputValue, updateMathInputValue, } from "../utils/actions"; +import Core from "../../Core"; const Mock = vi.fn(); vi.stubGlobal("postMessage", Mock); +vi.mock("hyperformula"); describe("MathList tag tests", async () => { async function test_mathList({ @@ -16,7 +18,7 @@ describe("MathList tag tests", async () => { text, maths, }: { - core: any; + core: Core; name?: string; pName?: string; text?: string; @@ -184,7 +186,7 @@ describe("MathList tag tests", async () => { }); }); - async function test_nested_and_inverse(core: any) { + async function test_nested_and_inverse(core: Core) { await test_mathList({ core, name: "/ml1", @@ -220,15 +222,15 @@ describe("MathList tag tests", async () => { // change values - await updateMathInputValue({ componentName: "/mi1", latex: "1", core }); - await updateMathInputValue({ componentName: "/mi2", latex: "2", core }); - await updateMathInputValue({ componentName: "/mi3", latex: "3", core }); - await updateMathInputValue({ componentName: "/mi4", latex: "4", core }); - await updateMathInputValue({ componentName: "/mi5", latex: "5", core }); - await updateMathInputValue({ componentName: "/mi6", latex: "6", core }); - await updateMathInputValue({ componentName: "/mi7", latex: "7", core }); - await updateMathInputValue({ componentName: "/mi8", latex: "8", core }); - await updateMathInputValue({ componentName: "/mi9", latex: "9", core }); + await updateMathInputValue({ name: "/mi1", latex: "1", core }); + await updateMathInputValue({ name: "/mi2", latex: "2", core }); + await updateMathInputValue({ name: "/mi3", latex: "3", core }); + await updateMathInputValue({ name: "/mi4", latex: "4", core }); + await updateMathInputValue({ name: "/mi5", latex: "5", core }); + await updateMathInputValue({ name: "/mi6", latex: "6", core }); + await updateMathInputValue({ name: "/mi7", latex: "7", core }); + await updateMathInputValue({ name: "/mi8", latex: "8", core }); + await updateMathInputValue({ name: "/mi9", latex: "9", core }); await test_mathList({ core, @@ -407,7 +409,7 @@ describe("MathList tag tests", async () => { await updateMathInputValue({ latex: val, - componentName: `/mi${mathInd + 1}`, + name: `/mi${mathInd + 1}`, core, }); unique_values[uniqueInd] = val; @@ -578,13 +580,13 @@ describe("MathList tag tests", async () => { await check_items(max1, max2); max1 = Infinity; - await updateMathInputValue({ latex: "", componentName: "/mn1", core }); + await updateMathInputValue({ latex: "", name: "/mn1", core }); await check_items(max1, max2); max2 = 3; await updateMathInputValue({ latex: max2.toString(), - componentName: "/mn2", + name: "/mn2", core, }); await check_items(max1, max2); @@ -592,7 +594,7 @@ describe("MathList tag tests", async () => { max1 = 4; await updateMathInputValue({ latex: max1.toString(), - componentName: "/mn1", + name: "/mn1", core, }); await check_items(max1, max2); @@ -600,7 +602,7 @@ describe("MathList tag tests", async () => { max1 = 1; await updateMathInputValue({ latex: max1.toString(), - componentName: "/mn1", + name: "/mn1", core, }); await check_items(max1, max2); @@ -608,7 +610,7 @@ describe("MathList tag tests", async () => { max2 = 10; await updateMathInputValue({ latex: max2.toString(), - componentName: "/mn2", + name: "/mn2", core, }); await check_items(max1, max2); @@ -673,7 +675,7 @@ describe("MathList tag tests", async () => { for (let [i, v] of vals.entries()) { await updateMathInputValue({ latex: v.toString(), - componentName: `/mi${i + 1}`, + name: `/mi${i + 1}`, core, }); } @@ -681,7 +683,7 @@ describe("MathList tag tests", async () => { await updateBooleanInputValue({ boolean: true, - componentName: "/merge", + name: "/merge", core, }); vals = ["h", "b", "c", "i", "e", "j", "k"]; @@ -691,7 +693,7 @@ describe("MathList tag tests", async () => { for (let [i, v] of vals.entries()) { await updateMathInputValue({ latex: v.toString(), - componentName: `/mi${i + 1}`, + name: `/mi${i + 1}`, core, }); } @@ -699,7 +701,7 @@ describe("MathList tag tests", async () => { await updateBooleanInputValue({ boolean: false, - componentName: "/merge", + name: "/merge", core, }); vals = ["l", ["m", "n", "o"], ["p", "q"], "r"]; @@ -753,7 +755,7 @@ describe("MathList tag tests", async () => { for (let [i, v] of vals.entries()) { await updateMathInputValue({ latex: v.toString(), - componentName: `/mi${i + 1}`, + name: `/mi${i + 1}`, core, }); } @@ -884,7 +886,7 @@ describe("MathList tag tests", async () => { maxNum = 6; await updateMathInputValue({ latex: maxNum.toString(), - componentName: "/maxNum", + name: "/maxNum", core, }); await check_items(mml, maxNum); @@ -892,7 +894,7 @@ describe("MathList tag tests", async () => { maxNum = 7; await updateMathInputValue({ latex: maxNum.toString(), - componentName: "/maxNum", + name: "/maxNum", core, }); await check_items(mml, maxNum); @@ -900,7 +902,7 @@ describe("MathList tag tests", async () => { mml = true; await updateBooleanInputValue({ boolean: mml, - componentName: "/mml", + name: "/mml", core, }); await check_items(mml, maxNum); @@ -908,7 +910,7 @@ describe("MathList tag tests", async () => { maxNum = 13; await updateMathInputValue({ latex: maxNum.toString(), - componentName: "/maxNum", + name: "/maxNum", core, }); await check_items(mml, maxNum); @@ -916,7 +918,7 @@ describe("MathList tag tests", async () => { mml = false; await updateBooleanInputValue({ boolean: mml, - componentName: "/mml", + name: "/mml", core, }); await check_items(mml, maxNum); @@ -967,7 +969,7 @@ describe("MathList tag tests", async () => { maxN = 4; await updateMathInputValue({ latex: maxN.toString(), - componentName: "/maxN", + name: "/maxN", core, }); await check_items(maxN); @@ -975,7 +977,7 @@ describe("MathList tag tests", async () => { maxN = 1; await updateMathInputValue({ latex: maxN.toString(), - componentName: "/maxN", + name: "/maxN", core, }); await check_items(maxN); @@ -1557,7 +1559,7 @@ describe("MathList tag tests", async () => { n2 = 83; await updateMathInputValue({ latex: n2.toString(), - componentName: "/mi1", + name: "/mi1", core, }); await test_items(n1, n2, n3); @@ -1566,7 +1568,7 @@ describe("MathList tag tests", async () => { n3 = 2; await updateMathInputValue({ latex: `(${n1}, ${n3})`, - componentName: "/mi2", + name: "/mi2", core, }); await test_items(n1, n2, n3); @@ -1619,7 +1621,7 @@ describe("MathList tag tests", async () => { x2 = "d"; await updateMathInputValue({ latex: `${x1}, ${x2}`, - componentName: "/mi", + name: "/mi", core, }); await test_mathList({ diff --git a/packages/doenetml-worker/src/test/tagSpecific/mathoperators.test.ts b/packages/doenetml-worker/src/test/tagSpecific/mathoperators.test.ts index 3eb62f3b5..2ddf41869 100644 --- a/packages/doenetml-worker/src/test/tagSpecific/mathoperators.test.ts +++ b/packages/doenetml-worker/src/test/tagSpecific/mathoperators.test.ts @@ -1,10 +1,11 @@ import { describe, expect, it, vi } from "vitest"; import { createTestCore, returnAllStateVariables } from "../utils/test-core"; import me from "math-expressions"; -import { updateMathInputValue } from "../utils/actions"; +import { movePoint, updateMathInputValue } from "../utils/actions"; const Mock = vi.fn(); vi.stubGlobal("postMessage", Mock); +vi.mock("hyperformula"); describe("Math operator tests", async () => { it("sum", async () => { @@ -2019,12 +2020,7 @@ describe("Math operator tests", async () => { // move point 1 x = -5; y = 0; - await core.requestAction({ - actionName: "movePoint", - componentName: "/P1", - args: { x, y }, - event: null, - }); + await movePoint({ name: "/P1", x, y, core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/P1"].stateValues.xs[0].tree).eq(x); expect(stateVariables["/P1"].stateValues.xs[1].tree).eq(y); @@ -2046,12 +2042,7 @@ describe("Math operator tests", async () => { // move point 2 x = 9; y = -3; - await core.requestAction({ - actionName: "movePoint", - componentName: "/P2", - args: { x, y }, - event: null, - }); + await movePoint({ name: "/P2", x, y, core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/P1"].stateValues.xs[0].tree).eq(clamp(x)); @@ -2074,12 +2065,7 @@ describe("Math operator tests", async () => { // move point 3 x = -4; y = 8; - await core.requestAction({ - actionName: "movePoint", - componentName: "/P3", - args: { x: y, y: x }, - event: null, - }); + await movePoint({ name: "/P3", x: y, y: x, core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/P1"].stateValues.xs[0].tree).eq(clamp(x)); @@ -2103,12 +2089,11 @@ describe("Math operator tests", async () => { x = 10; y = -10; - await core.requestAction({ - actionName: "movePoint", - componentName: - stateVariables["/g2"].activeChildren[0].componentName, - args: { x, y }, - event: null, + await movePoint({ + name: stateVariables["/g2"].activeChildren[0].componentName, + x, + y, + core, }); stateVariables = await returnAllStateVariables(core); @@ -2133,12 +2118,11 @@ describe("Math operator tests", async () => { x = 11; y = -13; - await core.requestAction({ - actionName: "movePoint", - componentName: - stateVariables["/g2"].activeChildren[1].componentName, - args: { x, y }, - event: null, + await movePoint({ + name: stateVariables["/g2"].activeChildren[1].componentName, + x, + y, + core, }); stateVariables = await returnAllStateVariables(core); @@ -2164,12 +2148,11 @@ describe("Math operator tests", async () => { x = -3; y = 12; - await core.requestAction({ - actionName: "movePoint", - componentName: - stateVariables["/g2"].activeChildren[2].componentName, - args: { x: y, y: x }, - event: null, + await movePoint({ + name: stateVariables["/g2"].activeChildren[2].componentName, + x: y, + y: x, + core, }); stateVariables = await returnAllStateVariables(core); @@ -2586,21 +2569,21 @@ describe("Math operator tests", async () => { expect(stateVariables["/a2"].stateValues.value.tree).eq(9); expect(stateVariables["/a3"].stateValues.value.tree).eq(9); - await updateMathInputValue({ componentName: "/a2", latex: "-3", core }); + await updateMathInputValue({ name: "/a2", latex: "-3", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/a1"].stateValues.value.tree).eq(0); expect(stateVariables["/a2"].stateValues.value.tree).eq(0); expect(stateVariables["/a3"].stateValues.value.tree).eq(0); - await updateMathInputValue({ componentName: "/a2", latex: "7", core }); + await updateMathInputValue({ name: "/a2", latex: "7", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/a1"].stateValues.value.tree).eq(7); expect(stateVariables["/a2"].stateValues.value.tree).eq(7); expect(stateVariables["/a3"].stateValues.value.tree).eq(7); - await updateMathInputValue({ componentName: "/a2", latex: "x", core }); + await updateMathInputValue({ name: "/a2", latex: "x", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/a1"].stateValues.value.tree).eqls([ @@ -2619,7 +2602,7 @@ describe("Math operator tests", async () => { "x", ]); - await updateMathInputValue({ componentName: "/a2", latex: "y", core }); + await updateMathInputValue({ name: "/a2", latex: "y", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/a1"].stateValues.value.tree).eqls([ @@ -2708,36 +2691,21 @@ describe("Math operator tests", async () => { let x = -5.1; let y = 0.3; - await core.requestAction({ - actionName: "movePoint", - componentName: "/_point1", - args: { x, y }, - event: null, - }); + await movePoint({ name: "/_point1", x, y, core }); await checkPoints(x, y); // move point 1, negative y x = -7.9; y = -5.8; - await core.requestAction({ - actionName: "movePoint", - componentName: "/_point1", - args: { x, y }, - event: null, - }); + await movePoint({ name: "/_point1", x, y, core }); await checkPoints(x, y); // move point 2, positive y x = 3.4; y = 8.6; - await core.requestAction({ - actionName: "movePoint", - componentName: "/_point2", - args: { x, y }, - event: null, - }); + await movePoint({ name: "/_point2", x, y, core }); await checkPoints(x, y); // move point 2, negative y @@ -2745,36 +2713,21 @@ describe("Math operator tests", async () => { x = 7.7; y = -4.4; - await core.requestAction({ - actionName: "movePoint", - componentName: "/_point2", - args: { x, y }, - event: null, - }); + await movePoint({ name: "/_point2", x, y, core }); await checkPoints(x, y); // move point 3, positive x x = 9.4; y = -1.3; - await core.requestAction({ - actionName: "movePoint", - componentName: "/_point3", - args: { x, y }, - event: null, - }); + await movePoint({ name: "/_point3", x, y, core }); await checkPoints(y, x); // move point 3, negative x x = -8.9; y = -4.6; - await core.requestAction({ - actionName: "movePoint", - componentName: "/_point3", - args: { x, y }, - event: null, - }); + await movePoint({ name: "/_point3", x, y, core }); await checkPoints(y, 0); // move point 4, positive y @@ -2782,72 +2735,42 @@ describe("Math operator tests", async () => { x = 6.8; y = 3.7; - await core.requestAction({ - actionName: "movePoint", - componentName: g2ChildrenNames[0], - args: { x, y }, - event: null, - }); + await movePoint({ name: g2ChildrenNames[0], x, y, core }); await checkPoints(x, y); // move point 4, negative y x = 1.2; y = -1.4; - await core.requestAction({ - actionName: "movePoint", - componentName: g2ChildrenNames[0], - args: { x, y }, - event: null, - }); + await movePoint({ name: g2ChildrenNames[0], x, y, core }); await checkPoints(x, y); // move point 5, positive y x = -6.6; y = 3.2; - await core.requestAction({ - actionName: "movePoint", - componentName: g2ChildrenNames[1], - args: { x, y }, - event: null, - }); + await movePoint({ name: g2ChildrenNames[1], x, y, core }); await checkPoints(x, y); // move point 5, negative y x = -4.3; y = -8.9; - await core.requestAction({ - actionName: "movePoint", - componentName: g2ChildrenNames[1], - args: { x, y }, - event: null, - }); + await movePoint({ name: g2ChildrenNames[1], x, y, core }); await checkPoints(x, y); // move point 6, positive x x = 6.4; y = 2.3; - await core.requestAction({ - actionName: "movePoint", - componentName: g2ChildrenNames[2], - args: { x, y }, - event: null, - }); + await movePoint({ name: g2ChildrenNames[2], x, y, core }); await checkPoints(y, x); // move point 6, negative x x = -5.6; y = 7.8; - await core.requestAction({ - actionName: "movePoint", - componentName: g2ChildrenNames[2], - args: { x, y }, - event: null, - }); + await movePoint({ name: g2ChildrenNames[2], x, y, core }); await checkPoints(y, 0); }); @@ -6816,43 +6739,43 @@ describe("Math operator tests", async () => { expect(stateVariables["/maths11"].stateValues.value.tree).eq(6); await updateMathInputValue({ - componentName: "/minumbers00", + name: "/minumbers00", latex: "9", core, }); await updateMathInputValue({ - componentName: "/minumbers01", + name: "/minumbers01", latex: "9", core, }); await updateMathInputValue({ - componentName: "/minumbers10", + name: "/minumbers10", latex: "9", core, }); await updateMathInputValue({ - componentName: "/minumbers11", + name: "/minumbers11", latex: "9", core, }); await updateMathInputValue({ - componentName: "/mimaths00", + name: "/mimaths00", latex: "9", core, }); await updateMathInputValue({ - componentName: "/mimaths01", + name: "/mimaths01", latex: "9", core, }); await updateMathInputValue({ - componentName: "/mimaths10", + name: "/mimaths10", latex: "9", core, }); await updateMathInputValue({ - componentName: "/mimaths11", + name: "/mimaths11", latex: "9", core, }); @@ -6869,43 +6792,43 @@ describe("Math operator tests", async () => { expect(stateVariables["/maths11"].stateValues.value.tree).eq(6); await updateMathInputValue({ - componentName: "/minumbers00", + name: "/minumbers00", latex: "5", core, }); await updateMathInputValue({ - componentName: "/minumbers01", + name: "/minumbers01", latex: "5", core, }); await updateMathInputValue({ - componentName: "/minumbers10", + name: "/minumbers10", latex: "5", core, }); await updateMathInputValue({ - componentName: "/minumbers11", + name: "/minumbers11", latex: "5", core, }); await updateMathInputValue({ - componentName: "/mimaths00", + name: "/mimaths00", latex: "5", core, }); await updateMathInputValue({ - componentName: "/mimaths01", + name: "/mimaths01", latex: "5", core, }); await updateMathInputValue({ - componentName: "/mimaths10", + name: "/mimaths10", latex: "5", core, }); await updateMathInputValue({ - componentName: "/mimaths11", + name: "/mimaths11", latex: "5", core, }); @@ -6922,43 +6845,43 @@ describe("Math operator tests", async () => { expect(stateVariables["/maths11"].stateValues.value.tree).eq(6); await updateMathInputValue({ - componentName: "/minumbers00", + name: "/minumbers00", latex: "2", core, }); await updateMathInputValue({ - componentName: "/minumbers01", + name: "/minumbers01", latex: "2", core, }); await updateMathInputValue({ - componentName: "/minumbers10", + name: "/minumbers10", latex: "2", core, }); await updateMathInputValue({ - componentName: "/minumbers11", + name: "/minumbers11", latex: "2", core, }); await updateMathInputValue({ - componentName: "/mimaths00", + name: "/mimaths00", latex: "2", core, }); await updateMathInputValue({ - componentName: "/mimaths01", + name: "/mimaths01", latex: "2", core, }); await updateMathInputValue({ - componentName: "/mimaths10", + name: "/mimaths10", latex: "2", core, }); await updateMathInputValue({ - componentName: "/mimaths11", + name: "/mimaths11", latex: "2", core, }); @@ -6975,43 +6898,43 @@ describe("Math operator tests", async () => { expect(stateVariables["/maths11"].stateValues.value.tree).eq(6); await updateMathInputValue({ - componentName: "/minumbers00", + name: "/minumbers00", latex: "x", core, }); await updateMathInputValue({ - componentName: "/minumbers01", + name: "/minumbers01", latex: "x", core, }); await updateMathInputValue({ - componentName: "/minumbers10", + name: "/minumbers10", latex: "x", core, }); await updateMathInputValue({ - componentName: "/minumbers11", + name: "/minumbers11", latex: "x", core, }); await updateMathInputValue({ - componentName: "/mimaths00", + name: "/mimaths00", latex: "x", core, }); await updateMathInputValue({ - componentName: "/mimaths01", + name: "/mimaths01", latex: "x", core, }); await updateMathInputValue({ - componentName: "/mimaths10", + name: "/mimaths10", latex: "x", core, }); await updateMathInputValue({ - componentName: "/mimaths11", + name: "/mimaths11", latex: "x", core, }); @@ -7036,43 +6959,43 @@ describe("Math operator tests", async () => { expect(stateVariables["/maths11"].stateValues.value.tree).eq(6); await updateMathInputValue({ - componentName: "/minumbers00", + name: "/minumbers00", latex: "y", core, }); await updateMathInputValue({ - componentName: "/minumbers01", + name: "/minumbers01", latex: "y", core, }); await updateMathInputValue({ - componentName: "/minumbers10", + name: "/minumbers10", latex: "y", core, }); await updateMathInputValue({ - componentName: "/minumbers11", + name: "/minumbers11", latex: "y", core, }); await updateMathInputValue({ - componentName: "/mimaths00", + name: "/mimaths00", latex: "y", core, }); await updateMathInputValue({ - componentName: "/mimaths01", + name: "/mimaths01", latex: "y", core, }); await updateMathInputValue({ - componentName: "/mimaths10", + name: "/mimaths10", latex: "y", core, }); await updateMathInputValue({ - componentName: "/mimaths11", + name: "/mimaths11", latex: "y", core, }); @@ -7097,43 +7020,43 @@ describe("Math operator tests", async () => { expect(stateVariables["/maths11"].stateValues.value.tree).eq(6); await updateMathInputValue({ - componentName: "/minumbers00", + name: "/minumbers00", latex: "7", core, }); await updateMathInputValue({ - componentName: "/minumbers01", + name: "/minumbers01", latex: "7", core, }); await updateMathInputValue({ - componentName: "/minumbers10", + name: "/minumbers10", latex: "7", core, }); await updateMathInputValue({ - componentName: "/minumbers11", + name: "/minumbers11", latex: "7", core, }); await updateMathInputValue({ - componentName: "/mimaths00", + name: "/mimaths00", latex: "7", core, }); await updateMathInputValue({ - componentName: "/mimaths01", + name: "/mimaths01", latex: "7", core, }); await updateMathInputValue({ - componentName: "/mimaths10", + name: "/mimaths10", latex: "7", core, }); await updateMathInputValue({ - componentName: "/mimaths11", + name: "/mimaths11", latex: "7", core, }); @@ -7692,7 +7615,7 @@ describe("Math operator tests", async () => { await updateMathInputValue({ latex: "2", - componentName: "/nArgument", + name: "/nArgument", core, }); @@ -7701,7 +7624,7 @@ describe("Math operator tests", async () => { await updateMathInputValue({ latex: "2", - componentName: "/nOperand", + name: "/nOperand", core, }); @@ -7716,7 +7639,7 @@ describe("Math operator tests", async () => { await updateMathInputValue({ latex: "3", - componentName: "/nArgument", + name: "/nArgument", core, }); @@ -7725,7 +7648,7 @@ describe("Math operator tests", async () => { await updateMathInputValue({ latex: "1", - componentName: "/nArgument", + name: "/nArgument", core, }); @@ -7734,7 +7657,7 @@ describe("Math operator tests", async () => { await updateMathInputValue({ latex: "3", - componentName: "/nOperand", + name: "/nOperand", core, }); @@ -7749,7 +7672,7 @@ describe("Math operator tests", async () => { await updateMathInputValue({ latex: "4", - componentName: "/nOperand", + name: "/nOperand", core, }); diff --git a/packages/doenetml-worker/src/test/tagSpecific/matrix.test.ts b/packages/doenetml-worker/src/test/tagSpecific/matrix.test.ts new file mode 100644 index 000000000..c5d839724 --- /dev/null +++ b/packages/doenetml-worker/src/test/tagSpecific/matrix.test.ts @@ -0,0 +1,787 @@ +import { describe, expect, it, vi } from "vitest"; +import { createTestCore, returnAllStateVariables } from "../utils/test-core"; +import { cleanLatex } from "../utils/math"; +import { + moveMath, + movePoint, + moveVector, + updateBooleanInputValue, + updateMathInputValue, + updateMatrixInputNumColumns, + updateMatrixInputNumRows, + updateMatrixInputValue, + updateSelectedIndices, + updateTextInputValue, + updateValue, +} from "../utils/actions"; +import { numberToLetters } from "@doenet/utils"; + +const Mock = vi.fn(); +vi.stubGlobal("postMessage", Mock); +vi.mock("hyperformula"); + +describe("Matrix tag tests", async () => { + async function test_matrix({ + matrixML, + initialValues, + initialNumRows, + initialNumColumns, + numRowsName, + numColumnsName, + initialDefaultEntry = 0, + defaultEntryName, + }: { + matrixML: string; + initialValues: (number | string | (number | string)[])[][]; + initialNumRows?: number; + initialNumColumns?: number; + numRowsName?: string; + numColumnsName?: string; + initialDefaultEntry?: number | string; + defaultEntryName?: string; + }) { + let doenetML = ` + ${matrixML} +

Copy 1:

+

Copy 2:

+

Copy 3: $A{name="A3"}

+

Copy 4:

+

Copy 5: $A

+

Copy 6: $A.value

+

Copy 7:

+

Copy 8:

+

Modify:

+

Modify copy 1:

+

Modify copy 2:

+

Modify copy 3:

+

Modify copy 4:

+

Modify copy 5:

+

Modify copy 6:

+

Modify copy 7:

+

Modify copy 8:

+

Size: , + numRows: , + numColumns: +

+

Size 1: , + numRows 1: , + numColumns 1: +

+

Size 2: , + numRows 2: , + numColumns 2: +

+

Size 3: , + numRows 3: , + numColumns 3: +

+

Size 4: , + numRows 4: , + numColumns 4: +

+

Size 5: , + numRows 5: , + numColumns 5: +

+

Size 6: , + numRows 6: , + numColumns 6: +

+

Size 7: , + numRows 7: , + numColumns 7: +

+

Size 8: , + numRows 8: , + numColumns 8: +

+ `; + + let core = await createTestCore({ doenetML }); + + async function check_items( + matrixValue: (number | string | (number | string)[])[][], + ) { + let numRows = matrixValue.length; + let numColumns = matrixValue.length > 0 ? matrixValue[0].length : 0; + + let matrixAst = [ + "matrix", + ["tuple", numRows, numColumns], + ["tuple", ...matrixValue.map((row) => ["tuple", ...row])], + ]; + + const stateVariables = await returnAllStateVariables(core); + + expect(stateVariables["/A"].stateValues.matrixSize).eqls([ + numRows, + numColumns, + ]); + + for (let i of ["", 1, 2, 3, 4, 5, 6, 7, 8]) { + expect(stateVariables[`/A${i}`].stateValues.value.tree).eqls( + matrixAst, + ); + expect( + stateVariables[`/A${i}`].stateValues.matrix.map((row) => + row.map((v) => v.tree), + ), + ).eqls(matrixValue); + expect(stateVariables[`/mi${i}`].stateValues.value.tree).eqls( + matrixAst, + ); + expect( + stateVariables[`/matrixSize${i}`].stateValues.numbers, + ).eqls([numRows, numColumns]); + expect(stateVariables[`/numRows${i}`].stateValues.value).eq( + numRows, + ); + expect(stateVariables[`/numColumns${i}`].stateValues.value).eq( + numColumns, + ); + } + } + + function paddedValues( + values: (number | string | (number | string)[])[][], + numRows: number, + numColumns: number, + defaultEntry: number | string, + trim: boolean = true, + ) { + let newValues = trim ? values.slice(0, numRows) : [...values]; + newValues = newValues.map((row) => { + if (trim) { + row = row.slice(0, numColumns); + } else { + row = [...row]; + } + if (row.length < numColumns) { + row.push( + ...Array(numColumns - row.length).fill(defaultEntry), + ); + } + return row; + }); + + while (newValues.length < numRows) { + newValues.push(Array(numColumns).fill(defaultEntry)); + } + return newValues; + } + + let numRows = initialNumRows ?? initialValues.length; + let numColumns = + initialNumColumns ?? (numRows ? initialValues[0].length : 0); + let defaultEntry = initialDefaultEntry; + + let values = initialValues.map((row) => [...row]); + + await check_items( + paddedValues(values, numRows, numColumns, defaultEntry), + ); + + if (defaultEntryName !== undefined) { + // change default entry + defaultEntry = "k"; + await updateMathInputValue({ + latex: defaultEntry, + name: defaultEntryName, + core, + }); + await check_items( + paddedValues(values, numRows, numColumns, defaultEntry), + ); + + // change first entry, which locks in all displayed default entries + values[0][0] = "y"; + await updateMatrixInputValue({ + latex: "y", + name: "/mi", + rowInd: 0, + colInd: 0, + core, + }); + + // lock in displayed default values + values = paddedValues( + values, + numRows, + numColumns, + defaultEntry, + false, + ); + await check_items( + paddedValues(values, numRows, numColumns, defaultEntry), + ); + + // changing default value doesn't change the displayed values + // (it will affect values added when increasing number of rows/column later) + defaultEntry = "j"; + await updateMathInputValue({ + latex: defaultEntry, + name: defaultEntryName, + core, + }); + await check_items( + paddedValues(values, numRows, numColumns, defaultEntry), + ); + } + + // change the values until 9 inputs have been tested + if (numRows > 0 && numColumns > 0) { + let ind = 0; + let stateVariables = await returnAllStateVariables(core); + + // Note: changing values locks in displayed default values + values = paddedValues( + values, + numRows, + numColumns, + defaultEntry, + false, + ); + while (ind < 9) { + for (let i = 0; i < numRows; i++) { + for (let j = 0; j < numColumns; j++) { + ind++; + let miInd = ind % 9 || ""; + values[i][j] = ind; + await updateMatrixInputValue({ + latex: ind.toString(), + name: `/mi${miInd}`, + rowInd: i, + colInd: j, + core, + stateVariables, + }); + } + } + + await check_items( + paddedValues(values, numRows, numColumns, defaultEntry), + ); + } + } + + // cannot change number of rows or columns directly + for (let i = 0; i < 9; i++) { + await updateMatrixInputNumRows({ + numRows: numRows + i + 1, + name: `/mi${i || ""}`, + core, + }); + await updateMatrixInputNumColumns({ + numColumns: numColumns + i + 1, + name: `/mi${i || ""}`, + core, + }); + } + + await check_items( + paddedValues(values, numRows, numColumns, defaultEntry), + ); + + if (numRowsName && numColumnsName) { + // add rows and columns via math inputs + numRows++; + await updateMathInputValue({ + latex: numRows.toString(), + name: numRowsName, + core, + }); + numColumns++; + await updateMathInputValue({ + latex: numColumns.toString(), + name: numColumnsName, + core, + }); + await check_items( + paddedValues(values, numRows, numColumns, defaultEntry), + ); + + // Note: changing values locks in displayed default values + values = paddedValues( + values, + numRows, + numColumns, + defaultEntry, + false, + ); + + // change lower-right corner + values[numRows - 1][numColumns - 1] = "z"; + await updateMatrixInputValue({ + latex: "z", + name: "/mi", + rowInd: numRows - 1, + colInd: numColumns - 1, + core, + }); + await check_items( + paddedValues(values, numRows, numColumns, defaultEntry), + ); + + // delete rows and columns via math inputs + numRows -= 2; + await updateMathInputValue({ + latex: numRows.toString(), + name: numRowsName, + core, + }); + numColumns -= 2; + await updateMathInputValue({ + latex: numColumns.toString(), + name: numColumnsName, + core, + }); + await check_items( + paddedValues(values, numRows, numColumns, defaultEntry), + ); + } + } + + it("no arguments, 0x0 matrix", async () => { + let matrixML = ` +

A:

`; + + let initialValues = []; + await test_matrix({ matrixML, initialValues }); + }); + + it("specify numRows, get 1 column, 2x1 matrix of 0s", async () => { + let matrixML = ` +

A:

`; + + let initialValues = [[0], [0]]; + await test_matrix({ matrixML, initialValues }); + }); + + it("specify numColumns, get 1 row, 1x2 matrix of zeros", async () => { + let matrixML = ` +

A:

`; + + let initialValues = [[0, 0]]; + await test_matrix({ matrixML, initialValues }); + }); + + it("with string/math children get 1x1 matrix", async () => { + let matrixML = ` +

A: 2x

`; + + let initialValues = [[["*", 2, "x"]]]; + await test_matrix({ matrixML, initialValues }); + }); + + it("2x3 matrix by rows", async () => { + let matrixML = ` +

A: + a b c + d e f + +

`; + + let initialValues = [ + ["a", "b", "c"], + ["d", "e", "f"], + ]; + await test_matrix({ matrixML, initialValues }); + }); + + it("2x3 matrix by columns", async () => { + let matrixML = ` +

A: + a d + b e + c f + +

`; + + let initialValues = [ + ["a", "b", "c"], + ["d", "e", "f"], + ]; + await test_matrix({ matrixML, initialValues }); + }); + + it("3x2 matrix, rows, change size", async () => { + let matrixML = ` +

numRows: , + numColumns:

+

A: + a b c + d e f + +

`; + + let initialValues = [ + ["a", "b", "c"], + ["d", "e", "f"], + ]; + await test_matrix({ + matrixML, + initialValues, + initialNumRows: 3, + initialNumColumns: 2, + numRowsName: "/mi_nRows", + numColumnsName: "/mi_nColumns", + }); + }); + + it("3x2 matrix, columns, change size", async () => { + let matrixML = ` +

numRows: , + numColumns:

+

A: + a d + b e + c f + +

`; + + let initialValues = [ + ["a", "b", "c"], + ["d", "e", "f"], + ]; + await test_matrix({ + matrixML, + initialValues, + initialNumRows: 3, + initialNumColumns: 2, + numRowsName: "/mi_nRows", + numColumnsName: "/mi_nColumns", + }); + }); + + it("3x2 matrix, rows, change size, change default entry", async () => { + let matrixML = ` +

Default entry:

+

numRows: , + numColumns:

+

A: + a b c + d e f + +

`; + + let initialValues = [ + ["a", "b", "c"], + ["d", "e", "f"], + ]; + await test_matrix({ + matrixML, + initialValues, + initialNumRows: 3, + initialNumColumns: 2, + numRowsName: "/mi_nRows", + numColumnsName: "/mi_nColumns", + initialDefaultEntry: "q", + defaultEntryName: "/de", + }); + }); + + it("3x2 matrix, columns, change size, change default entry", async () => { + let matrixML = ` +

Default entry:

+

numRows: , + numColumns:

+

A: + a d + b e + c f + +

`; + + let initialValues = [ + ["a", "b", "c"], + ["d", "e", "f"], + ]; + await test_matrix({ + matrixML, + initialValues, + initialNumRows: 3, + initialNumColumns: 2, + numRowsName: "/mi_nRows", + numColumnsName: "/mi_nColumns", + initialDefaultEntry: "q", + defaultEntryName: "/de", + }); + }); + + it("functionSymbols", async () => { + let core = await createTestCore({ + doenetML: ` +

+ f(x) g(x) + h(x) a(x) + +

+

+ f(x) g(x) + h(x) a(x) + +

+

+ h(x) g(x) a(x) + h(x) a(x) b(x) + +

+

+ h(x) g(x) a(x) + h(x) a(x) b(x) + +

+ `, + }); + + let stateVariables = await returnAllStateVariables(core); + let matrixdefAst = [ + "matrix", + ["tuple", 2, 2], + [ + "tuple", + ["tuple", ["apply", "f", "x"], ["apply", "g", "x"]], + ["tuple", ["*", "h", "x"], ["*", "a", "x"]], + ], + ]; + let matrixhAst = [ + "matrix", + ["tuple", 2, 2], + [ + "tuple", + ["tuple", ["*", "f", "x"], ["*", "g", "x"]], + ["tuple", ["apply", "h", "x"], ["*", "a", "x"]], + ], + ]; + let matrixmixedbyrowAst = [ + "matrix", + ["tuple", 2, 3], + [ + "tuple", + [ + "tuple", + ["*", "h", "x"], + ["apply", "g", "x"], + ["apply", "a", "x"], + ], + [ + "tuple", + ["apply", "h", "x"], + ["*", "a", "x"], + ["apply", "b", "x"], + ], + ], + ]; + let matrixmixedbycolumnAst = [ + "matrix", + ["tuple", 3, 2], + [ + "tuple", + ["tuple", ["*", "h", "x"], ["apply", "h", "x"]], + ["tuple", ["apply", "g", "x"], ["*", "a", "x"]], + ["tuple", ["apply", "a", "x"], ["apply", "b", "x"]], + ], + ]; + expect(stateVariables["/Adef"].stateValues.value.tree).eqls( + matrixdefAst, + ); + expect(stateVariables["/Ah"].stateValues.value.tree).eqls(matrixhAst); + expect(stateVariables["/Amixedbyrow"].stateValues.value.tree).eqls( + matrixmixedbyrowAst, + ); + expect(stateVariables["/Amixedbycolumn"].stateValues.value.tree).eqls( + matrixmixedbycolumnAst, + ); + }); + + it("sourcesAreFunctionSymbols", async () => { + let core = await createTestCore({ + doenetML: ` + + f + g + h + a + b + +

+ $fun1(x) $fun2(x) + $fun3(x) $fun4(x) + +

+

+ $fun1(x) $fun2(x) + $fun3(x) $fun4(x) + +

+

+ $fun3(x) $fun2(x) $fun4(x) + $fun3(x) $fun4(x) $fun5(x) + +

+

+ h(x) $fun2(x) $fun4(x) + $fun3(x) $fun4(x) $fun5(x) + +

+ `, + }); + + let stateVariables = await returnAllStateVariables(core); + let matrixdefAst = [ + "matrix", + ["tuple", 2, 2], + [ + "tuple", + ["tuple", ["*", "f", "x"], ["*", "g", "x"]], + ["tuple", ["*", "h", "x"], ["*", "a", "x"]], + ], + ]; + let matrixhAst = [ + "matrix", + ["tuple", 2, 2], + [ + "tuple", + ["tuple", ["apply", "f", "x"], ["*", "g", "x"]], + ["tuple", ["*", "h", "x"], ["apply", "a", "x"]], + ], + ]; + let matrixmixedbyrowAst = [ + "matrix", + ["tuple", 2, 3], + [ + "tuple", + [ + "tuple", + ["*", "h", "x"], + ["apply", "g", "x"], + ["apply", "a", "x"], + ], + [ + "tuple", + ["apply", "h", "x"], + ["*", "a", "x"], + ["apply", "b", "x"], + ], + ], + ]; + let matrixmixedbycolumnAst = [ + "matrix", + ["tuple", 3, 2], + [ + "tuple", + ["tuple", ["*", "h", "x"], ["apply", "h", "x"]], + ["tuple", ["apply", "g", "x"], ["*", "a", "x"]], + ["tuple", ["apply", "a", "x"], ["apply", "b", "x"]], + ], + ]; + expect(stateVariables["/Adef"].stateValues.value.tree).eqls( + matrixdefAst, + ); + expect(stateVariables["/Ah"].stateValues.value.tree).eqls(matrixhAst); + expect(stateVariables["/Amixedbyrow"].stateValues.value.tree).eqls( + matrixmixedbyrowAst, + ); + expect(stateVariables["/Amixedbycolumn"].stateValues.value.tree).eqls( + matrixmixedbycolumnAst, + ); + }); + + it("splitsymbols", async () => { + let core = await createTestCore({ + doenetML: ` +

+ xy yz + ab bc + +

+

+ xy yz + ab bc + +

+

+ xy yz zx + ab bc ca + +

+

+ xy yz zx + ab bc ca + +

+ `, + }); + + let stateVariables = await returnAllStateVariables(core); + let matrixdefAst = [ + "matrix", + ["tuple", 2, 2], + [ + "tuple", + ["tuple", ["*", "x", "y"], ["*", "y", "z"]], + ["tuple", ["*", "a", "b"], ["*", "b", "c"]], + ], + ]; + let matrixnAst = [ + "matrix", + ["tuple", 2, 2], + ["tuple", ["tuple", "xy", "yz"], ["tuple", "ab", "bc"]], + ]; + let matrixmixedbyrowAst = [ + "matrix", + ["tuple", 2, 3], + [ + "tuple", + ["tuple", "xy", ["*", "y", "z"], "zx"], + ["tuple", ["*", "a", "b"], "bc", ["*", "c", "a"]], + ], + ]; + let matrixmixedbycolumnAst = [ + "matrix", + ["tuple", 3, 2], + [ + "tuple", + ["tuple", "xy", ["*", "a", "b"]], + ["tuple", ["*", "y", "z"], "bc"], + ["tuple", "zx", ["*", "c", "a"]], + ], + ]; + expect(stateVariables["/Adef"].stateValues.value.tree).eqls( + matrixdefAst, + ); + expect(stateVariables["/Ah"].stateValues.value.tree).eqls(matrixnAst); + expect(stateVariables["/Amixedbyrow"].stateValues.value.tree).eqls( + matrixmixedbyrowAst, + ); + expect(stateVariables["/Amixedbycolumn"].stateValues.value.tree).eqls( + matrixmixedbycolumnAst, + ); + }); + + it("displayBlanks", async () => { + let core = await createTestCore({ + doenetML: ` +

+ x_ y_ + a_ b_ + +

+

+ x_ y_ + a_ b_ + +

+ `, + }); + + let stateVariables = await returnAllStateVariables(core); + + expect(stateVariables["/Adef"].stateValues.text).eqls( + "[ [ x__, y__ ], [ a__, b__ ] ]", + ); + expect(stateVariables["/Ah"].stateValues.text).eqls( + "[ [ x_, y_ ], [ a_, b_ ] ]", + ); + }); +}); diff --git a/packages/doenetml-worker/src/test/tagSpecific/matrixinput.test.ts b/packages/doenetml-worker/src/test/tagSpecific/matrixinput.test.ts new file mode 100644 index 000000000..fc0b6186a --- /dev/null +++ b/packages/doenetml-worker/src/test/tagSpecific/matrixinput.test.ts @@ -0,0 +1,2136 @@ +import { describe, expect, it, vi } from "vitest"; +import { createTestCore, returnAllStateVariables } from "../utils/test-core"; +import { + updateMathInputValue, + updateMatrixInputImmediateValue, + updateMatrixInputNumColumns, + updateMatrixInputNumRows, + updateMatrixInputValue, + updateMatrixInputValueToImmediateValue, +} from "../utils/actions"; +import Core from "../../Core"; + +const Mock = vi.fn(); +vi.stubGlobal("postMessage", Mock); +vi.mock("hyperformula"); + +describe("MathInput tag tests", async () => { + it("no arguments, copy matrixInput", async () => { + let core = await createTestCore({ + doenetML: ` +

Matrix 1:

+

Matrix 2:

+

Matrix 3: $mi1.value{assignNames="m1"}

+

Matrix 4: $mi1.immediateValue{assignNames="m2"}

+ `, + }); + + async function check_items(value: any, immediateValue?: any) { + if (immediateValue === undefined) { + immediateValue = value; + } + + const stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/mi1"].stateValues.immediateValue.tree).eqls( + immediateValue, + ); + expect(stateVariables["/mi1"].stateValues.value.tree).eqls(value); + expect(stateVariables["/mi2"].stateValues.immediateValue.tree).eqls( + immediateValue, + ); + expect(stateVariables["/mi2"].stateValues.value.tree).eqls(value); + expect(stateVariables["/m1"].stateValues.value.tree).eqls(value); + expect(stateVariables["/m2"].stateValues.value.tree).eqls( + immediateValue, + ); + } + + let matrixValue = [ + "matrix", + ["tuple", 1, 1], + ["tuple", ["tuple", "_"]], + ]; + + await check_items(matrixValue); + + // type a in mi1 + await updateMatrixInputImmediateValue({ + latex: "a", + rowInd: 0, + colInd: 0, + name: "/mi1", + core, + }); + + let matrixImmediateValue = [ + "matrix", + ["tuple", 1, 1], + ["tuple", ["tuple", "a"]], + ]; + + await check_items(matrixValue, matrixImmediateValue); + + // update value (e.g., blur) + await updateMatrixInputValueToImmediateValue({ + rowInd: 0, + colInd: 0, + name: "/mi1", + core, + }); + matrixValue = matrixImmediateValue; + await check_items(matrixValue); + + // add row to mi1 + await updateMatrixInputNumRows({ numRows: 2, name: "/mi1", core }); + + matrixValue = [ + "matrix", + ["tuple", 2, 1], + ["tuple", ["tuple", "a"], ["tuple", "_"]], + ]; + await check_items(matrixValue); + + // type b in second row of mi2 + await updateMatrixInputImmediateValue({ + latex: "b", + rowInd: 1, + colInd: 0, + name: "/mi2", + core, + }); + + matrixImmediateValue = [ + "matrix", + ["tuple", 2, 1], + ["tuple", ["tuple", "a"], ["tuple", "b"]], + ]; + await check_items(matrixValue, matrixImmediateValue); + + // update value (e.g., type enter) + await updateMatrixInputValueToImmediateValue({ + rowInd: 1, + colInd: 0, + name: "/mi2", + core, + }); + matrixValue = matrixImmediateValue; + await check_items(matrixValue); + + // add column to mi2 + await updateMatrixInputNumColumns({ + numColumns: 2, + name: "/mi2", + core, + }); + + matrixValue = [ + "matrix", + ["tuple", 2, 2], + ["tuple", ["tuple", "a", "_"], ["tuple", "b", "_"]], + ]; + await check_items(matrixValue); + + // c and d in second column + await updateMatrixInputValue({ + latex: "c", + rowInd: 0, + colInd: 1, + name: "/mi2", + core, + }); + await updateMatrixInputImmediateValue({ + latex: "d", + rowInd: 1, + colInd: 1, + name: "/mi2", + core, + }); + + matrixValue = [ + "matrix", + ["tuple", 2, 2], + ["tuple", ["tuple", "a", "c"], ["tuple", "b", "_"]], + ]; + matrixImmediateValue = [ + "matrix", + ["tuple", 2, 2], + ["tuple", ["tuple", "a", "c"], ["tuple", "b", "d"]], + ]; + + await check_items(matrixValue, matrixImmediateValue); + + // update value (e.g., blur) + await updateMatrixInputValueToImmediateValue({ + rowInd: 1, + colInd: 1, + name: "/mi2", + core, + }); + matrixValue = matrixImmediateValue; + await check_items(matrixValue); + + // remove row in mi2 + await updateMatrixInputNumRows({ numRows: 1, name: "/mi2", core }); + + matrixValue = [ + "matrix", + ["tuple", 1, 2], + ["tuple", ["tuple", "a", "c"]], + ]; + await check_items(matrixValue); + + // change second value + await updateMatrixInputValue({ + latex: "e", + rowInd: 0, + colInd: 1, + name: "/mi1", + core, + }); + + matrixValue = [ + "matrix", + ["tuple", 1, 2], + ["tuple", ["tuple", "a", "e"]], + ]; + await check_items(matrixValue); + + // remove column in mi1 + await updateMatrixInputNumColumns({ + numColumns: 1, + name: "/mi1", + core, + }); + + matrixValue = ["matrix", ["tuple", 1, 1], ["tuple", ["tuple", "a"]]]; + + await check_items(matrixValue); + + // change value + await updateMatrixInputValue({ + latex: "f", + rowInd: 0, + colInd: 0, + name: "/mi2", + core, + }); + + matrixValue = ["matrix", ["tuple", 1, 1], ["tuple", ["tuple", "f"]]]; + await check_items(matrixValue); + + // values remembered when add back row and column + await updateMatrixInputNumColumns({ + numColumns: 2, + name: "/mi1", + core, + }); + await updateMatrixInputNumRows({ numRows: 2, name: "/mi2", core }); + + matrixValue = [ + "matrix", + ["tuple", 2, 2], + ["tuple", ["tuple", "f", "e"], ["tuple", "b", "d"]], + ]; + await check_items(matrixValue); + + // change values + let stateVariables = await returnAllStateVariables(core); + await updateMatrixInputValue({ + latex: "g", + rowInd: 0, + colInd: 0, + name: "/mi1", + stateVariables, + core, + }); + await updateMatrixInputValue({ + latex: "h", + rowInd: 0, + colInd: 1, + name: "/mi1", + stateVariables, + core, + }); + await updateMatrixInputValue({ + latex: "i", + rowInd: 1, + colInd: 0, + name: "/mi2", + stateVariables, + core, + }); + await updateMatrixInputValue({ + latex: "j", + rowInd: 1, + colInd: 1, + name: "/mi2", + stateVariables, + core, + }); + + matrixValue = [ + "matrix", + ["tuple", 2, 2], + ["tuple", ["tuple", "g", "h"], ["tuple", "i", "j"]], + ]; + await check_items(matrixValue); + }); + + async function test_matrix_input({ + core, + initialValues, + initialNumRows, + initialNumColumns, + numRowsName, + numColumnsName, + ignoreInputSizes = false, + boundValueName, + boundValueStartsAs = "matrix", + defaultEntry = "\uff3f", + }: { + core: Core; + initialValues: (string | number)[][]; + initialNumRows?: number; + initialNumColumns?: number; + numRowsName?: string; + numColumnsName?: string; + ignoreInputSizes?: boolean; + boundValueName?: string; + boundValueStartsAs?: string; + defaultEntry?: string | number; + }) { + async function check_items(ast: any, boundValueFormat?: string) { + const stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/mi1"].stateValues.value.tree).eqls(ast); + expect(stateVariables["/m1"].stateValues.value.tree).eqls(ast); + if (boundValueName) { + if ( + boundValueFormat === "tuple" || + boundValueFormat === "vector" || + boundValueFormat === "altvector" + ) { + let vectorAst = + stateVariables[boundValueName].stateValues.value.tree; + expect(vectorAst).eqls([ + boundValueFormat, + ...ast[2].slice(1).map((v) => v[1]), + ]); + } else if ( + boundValueFormat === "tupleTrans" || + boundValueFormat === "vectorTrans" || + boundValueFormat === "altvectorTrans" + ) { + let operator = boundValueFormat.slice( + 0, + boundValueFormat.length - 5, + ); + let vectorAst = + stateVariables[boundValueName].stateValues.value.tree; + expect(vectorAst).eqls([ + "^", + [operator, ...ast[2][1].slice(1)], + "T", + ]); + } else if ( + boundValueFormat === "tuplePrime" || + boundValueFormat === "vectorPrime" || + boundValueFormat === "altvectorPrime" + ) { + let operator = boundValueFormat.slice( + 0, + boundValueFormat.length - 5, + ); + let vectorAst = + stateVariables[boundValueName].stateValues.value.tree; + expect(vectorAst).eqls([ + "prime", + [operator, ...ast[2][1].slice(1)], + ]); + } else { + expect( + stateVariables[boundValueName].stateValues.value.tree, + ).eqls(ast); + } + } + } + + function expandValuesToFit( + values: (string | number)[][], + numRows: number, + numColumns: number, + ) { + let newValues = values.map((row) => { + row = [...row]; + if (row.length < numColumns) { + row.push( + ...Array(numColumns - row.length).fill(defaultEntry), + ); + } + return row; + }); + + while (newValues.length < numRows) { + newValues.push(Array(numColumns).fill(defaultEntry)); + } + return newValues; + } + + function astFromValues( + values: (string | number)[][], + numRows: number, + numColumns: number, + ) { + return [ + "matrix", + ["tuple", numRows, numColumns], + [ + "tuple", + ...values + .slice(0, numRows) + .map((row) => ["tuple", ...row.slice(0, numColumns)]), + ], + ]; + } + + let numRows = initialNumRows ?? initialValues.length; + let numColumns = + initialNumColumns ?? (numRows ? initialValues[0].length : 0); + + let values = expandValuesToFit(initialValues, numRows, numColumns); + await check_items( + astFromValues(values, numRows, numColumns), + boundValueStartsAs, + ); + + if (boundValueName) { + if ( + boundValueStartsAs === "tuple" || + boundValueStartsAs === "vector" || + boundValueStartsAs === "altvector" + ) { + // bound value stays a vector if add or delete a row + numRows++; + await updateMatrixInputNumRows({ numRows, name: "/mi1", core }); + values = expandValuesToFit(values, numRows, numColumns); + await check_items( + astFromValues(values, numRows, numColumns), + boundValueStartsAs, + ); + + // change value + values[numRows - 1][0] = "q"; + await updateMatrixInputValue({ + latex: values[numRows - 1][0].toString(), + rowInd: numRows - 1, + colInd: 0, + name: "/mi1", + core, + }); + await check_items( + astFromValues(values, numRows, numColumns), + boundValueStartsAs, + ); + + numRows--; + await updateMatrixInputNumRows({ numRows, name: "/mi1", core }); + await check_items( + astFromValues(values, numRows, numColumns), + boundValueStartsAs, + ); + + // bound value changes to matrix if add column + numColumns++; + await updateMatrixInputNumColumns({ + numColumns, + name: "/mi1", + core, + }); + values = expandValuesToFit(values, numRows, numColumns); + // omit boundValueStartsAs from now on, indicating it is a matrix + await check_items(astFromValues(values, numRows, numColumns)); + } else if ( + boundValueStartsAs === "tupleTrans" || + boundValueStartsAs === "vectorTrans" || + boundValueStartsAs === "altvectorTrans" || + boundValueStartsAs === "tuplePrime" || + boundValueStartsAs === "vectorPrime" || + boundValueStartsAs === "altvectorPrime" + ) { + // bound value stays a transposed vector if add or delete a column + numColumns++; + await updateMatrixInputNumColumns({ + numColumns, + name: "/mi1", + core, + }); + values = expandValuesToFit(values, numRows, numColumns); + await check_items( + astFromValues(values, numRows, numColumns), + boundValueStartsAs, + ); + + // change value + values[0][numColumns - 1] = "q"; + await updateMatrixInputValue({ + latex: values[0][numColumns - 1].toString(), + rowInd: 0, + colInd: numColumns - 1, + name: "/mi1", + core, + }); + await check_items( + astFromValues(values, numRows, numColumns), + boundValueStartsAs, + ); + + numColumns--; + await updateMatrixInputNumColumns({ + numColumns, + name: "/mi1", + core, + }); + await check_items( + astFromValues(values, numRows, numColumns), + boundValueStartsAs, + ); + + // bound value changes to matrix if add row + numRows++; + await updateMatrixInputNumRows({ + numRows, + name: "/mi1", + core, + }); + values = expandValuesToFit(values, numRows, numColumns); + // omit boundValueStartsAs from now on, indicating it is a matrix + await check_items(astFromValues(values, numRows, numColumns)); + } + } + + // progressively add up to three rows and columns to reveal any hidden items + // (since no hidden items with bound values, just check 1 in this case) + let numToAdd = boundValueName ? 1 : 3; + let newValues = ["x", "y", "z"]; + for (let i = 0; i < numToAdd; i++) { + numRows++; + await updateMatrixInputNumRows({ numRows, name: "/mi1", core }); + values = expandValuesToFit(values, numRows, numColumns); + await check_items(astFromValues(values, numRows, numColumns)); + + numColumns++; + await updateMatrixInputNumColumns({ + numColumns, + name: "/mi1", + core, + }); + values = expandValuesToFit(values, numRows, numColumns); + await check_items(astFromValues(values, numRows, numColumns)); + + values[numRows - 1][numColumns - 1] = newValues[i]; + await updateMatrixInputValue({ + latex: values[numRows - 1][numColumns - 1].toString(), + rowInd: numRows - 1, + colInd: numColumns - 1, + name: "/mi1", + core, + }); + + await check_items(astFromValues(values, numRows, numColumns)); + } + + // progressively remove those rows and columns + for (let i = 0; i < numToAdd; i++) { + numRows--; + await updateMatrixInputNumRows({ numRows, name: "/mi1", core }); + await check_items(astFromValues(values, numRows, numColumns)); + + numColumns--; + await updateMatrixInputNumColumns({ + numColumns, + name: "/mi1", + core, + }); + await check_items(astFromValues(values, numRows, numColumns)); + } + + // set size to zero + numRows = 0; + await updateMatrixInputNumRows({ numRows, name: "/mi1", core }); + await check_items(astFromValues(values, numRows, numColumns)); + + numColumns = 0; + await updateMatrixInputNumColumns({ + numColumns, + name: "/mi1", + core, + }); + await check_items(astFromValues(values, numRows, numColumns)); + + // add two rows and columns with inputs, if exist and aren't ignored, else add via actions + numColumns = 2; + numRows = 2; + + if (numColumnsName && !ignoreInputSizes) { + await updateMathInputValue({ + latex: numColumns.toString(), + name: numColumnsName, + core, + }); + } else { + await updateMatrixInputNumColumns({ + numColumns, + name: "/mi1", + core, + }); + } + if (numRowsName && !ignoreInputSizes) { + await updateMathInputValue({ + latex: numRows.toString(), + name: numRowsName, + core, + }); + } else { + await updateMatrixInputNumRows({ numRows, name: "/mi1", core }); + } + await check_items(astFromValues(values, numRows, numColumns)); + + // update values + values[0][0] = "e"; + values[0][1] = "f"; + values[1][0] = "g"; + values[1][1] = "h"; + + let stateVariables = await returnAllStateVariables(core); + + for (let i = 0; i < 2; i++) { + for (let j = 0; j < 2; j++) { + await updateMatrixInputValue({ + latex: values[i][j].toString(), + name: "/mi1", + rowInd: i, + colInd: j, + core, + stateVariables, + }); + } + } + + await check_items(astFromValues(values, numRows, numColumns)); + + // if are to ignore inputs determining sizes, verify that they are ignored + if (ignoreInputSizes && numRowsName && numColumnsName) { + expect(stateVariables[numRowsName].stateValues.value.tree).eq( + numRows, + ); + expect(stateVariables[numColumnsName].stateValues.value.tree).eq( + numColumns, + ); + + let ignoredNumColumns = 9; + let ignoredNumRows = 8; + + await updateMathInputValue({ + latex: ignoredNumColumns.toString(), + name: numColumnsName, + core, + }); + + await updateMathInputValue({ + latex: ignoredNumRows.toString(), + name: numRowsName, + core, + }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables[numRowsName].stateValues.value.tree).eq( + ignoredNumRows, + ); + expect(stateVariables[numColumnsName].stateValues.value.tree).eq( + ignoredNumColumns, + ); + + await check_items(astFromValues(values, numRows, numColumns)); + } + } + + it("prefill with matrix", async () => { + let core = await createTestCore({ + doenetML: ` +

Matrix 1:

+

Matrix 2: $mi1.value{assignNames="m1"}

+ `, + }); + + await test_matrix_input({ + core, + initialValues: [ + ["a", "b"], + ["c", "d"], + ], + }); + }); + + it("prefill with matrix, start smaller", async () => { + let core = await createTestCore({ + doenetML: ` +

Number of rows:

+

Number of columns:

+ +

Matrix 1:

+

Matrix 2: $mi1.value{assignNames="m1"}

+ `, + }); + + await test_matrix_input({ + core, + initialValues: [ + ["a", "b"], + ["c", "d"], + ], + initialNumRows: 0, + initialNumColumns: 0, + numRowsName: "/numRows", + numColumnsName: "/numColumns", + }); + }); + + it("prefill with vector", async () => { + let core = await createTestCore({ + doenetML: ` +

Matrix 1:

+

Matrix 2: $mi1.value{assignNames="m1"}

+ `, + }); + + await test_matrix_input({ + core, + initialValues: [["a"], ["b"]], + }); + }); + + it("prefill with altvector", async () => { + let core = await createTestCore({ + doenetML: ` +

Matrix 1:

+

Matrix 2: $mi1.value{assignNames="m1"}

+ `, + }); + + await test_matrix_input({ + core, + initialValues: [["a"], ["b"]], + }); + }); + + it("prefill with vector, start smaller", async () => { + let core = await createTestCore({ + doenetML: ` +

Number of rows:

+

Number of columns:

+ +

Matrix 1:

+

Matrix 2: $mi1.value{assignNames="m1"}

+ `, + }); + + await test_matrix_input({ + core, + initialValues: [["a"], ["b"]], + initialNumRows: 0, + initialNumColumns: 0, + numRowsName: "/numRows", + numColumnsName: "/numColumns", + }); + }); + + it("prefill with vector, start smaller", async () => { + let core = await createTestCore({ + doenetML: ` +

Number of rows:

+

Number of columns:

+ +

Matrix 1:

+

Matrix 2: $mi1.value{assignNames="m1"}

+ `, + }); + + await test_matrix_input({ + core, + initialValues: [["a"], ["b"]], + initialNumRows: 0, + initialNumColumns: 0, + numRowsName: "/numRows", + numColumnsName: "/numColumns", + }); + }); + + it("prefill with transpose of vector", async () => { + let core = await createTestCore({ + doenetML: ` +

Matrix 1:

+

Matrix 2: $mi1.value{assignNames="m1"}

+ `, + }); + + await test_matrix_input({ + core, + initialValues: [["a", "b"]], + }); + }); + + it("prefill with transpose of altvector", async () => { + let core = await createTestCore({ + doenetML: ` +

Matrix 1:

+

Matrix 2: $mi1.value{assignNames="m1"}

+ `, + }); + + await test_matrix_input({ + core, + initialValues: [["a", "b"]], + }); + }); + + it("prefill with transpose of vector, start smaller", async () => { + let core = await createTestCore({ + doenetML: ` +

Number of rows:

+

Number of columns:

+ +

Matrix 1:

+

Matrix 2: $mi1.value{assignNames="m1"}

+ `, + }); + + await test_matrix_input({ + core, + initialValues: [["a", "b"]], + initialNumRows: 0, + initialNumColumns: 0, + numRowsName: "/numRows", + numColumnsName: "/numColumns", + }); + }); + + it("prefill with transpose of altvector, start smaller", async () => { + let core = await createTestCore({ + doenetML: ` +

Number of rows:

+

Number of columns:

+ +

Matrix 1:

+

Matrix 2: $mi1.value{assignNames="m1"}

+ `, + }); + + await test_matrix_input({ + core, + initialValues: [["a", "b"]], + initialNumRows: 0, + initialNumColumns: 0, + numRowsName: "/numRows", + numColumnsName: "/numColumns", + }); + }); + + it("prefill with transpose of vector, start smaller, alternative format", async () => { + let core = await createTestCore({ + doenetML: ` +

Number of rows:

+

Number of columns:

+ +

Matrix 1:

+

Matrix 2: $mi1.value{assignNames="m1"}

+ `, + }); + + await test_matrix_input({ + core, + initialValues: [["a", "b"]], + initialNumRows: 0, + initialNumColumns: 0, + numRowsName: "/numRows", + numColumnsName: "/numColumns", + }); + }); + + it("prefill with transpose of altvector, start smaller, alternative format", async () => { + let core = await createTestCore({ + doenetML: ` +

Number of rows:

+

Number of columns:

+ +

Matrix 1:

+

Matrix 2: $mi1.value{assignNames="m1"}

+ `, + }); + + await test_matrix_input({ + core, + initialValues: [["a", "b"]], + initialNumRows: 0, + initialNumColumns: 0, + numRowsName: "/numRows", + numColumnsName: "/numColumns", + }); + }); + + it("bind to matrix", async () => { + let core = await createTestCore({ + doenetML: ` +

Number of rows:

+

Number of columns:

+ +

Matrix 1: \\begin{matrix}a & b\\\\c & d\\end{matrix}

+

Matrix 2:

+

Matrix 3: $mi1.value{assignNames="m1"}

+ `, + }); + + await test_matrix_input({ + core, + initialValues: [ + ["a", "b"], + ["c", "d"], + ], + initialNumRows: 2, + initialNumColumns: 2, + numRowsName: "/numRows", + numColumnsName: "/numColumns", + boundValueName: "/m0", + }); + }); + + it("bind to matrix, ignore size via definition", async () => { + let core = await createTestCore({ + doenetML: ` +

Number of rows:

+

Number of columns:

+ +

Matrix 1: \\begin{matrix}a & b\\\\c & d\\end{matrix}

+

Matrix 2:

+

Matrix 3: $mi1.value{assignNames="m1"}

+ + `, + }); + + await test_matrix_input({ + core, + initialValues: [ + ["a", "b"], + ["c", "d"], + ], + initialNumRows: 2, + initialNumColumns: 2, + numRowsName: "/numRows", + numColumnsName: "/numColumns", + ignoreInputSizes: true, + boundValueName: "/m0", + }); + }); + + it("bind to tuple", async () => { + let core = await createTestCore({ + doenetML: ` +

Number of rows:

+

Number of columns:

+ +

Tuple 1: (a,b)

+

Matrix 2:

+

Matrix 3: $mi1.value{assignNames="m1"}

+ `, + }); + + await test_matrix_input({ + core, + initialValues: [["a"], ["b"]], + initialNumRows: 2, + initialNumColumns: 1, + numRowsName: "/numRows", + numColumnsName: "/numColumns", + boundValueName: "/m0", + boundValueStartsAs: "tuple", + }); + }); + + it("bind to vector", async () => { + let core = await createTestCore({ + doenetML: ` +

Number of rows:

+

Number of columns:

+ +

Vector 1: (a,b)

+

Matrix 2:

+

Matrix 3: $mi1.value{assignNames="m1"}

+ `, + }); + + await test_matrix_input({ + core, + initialValues: [["a"], ["b"]], + initialNumRows: 2, + initialNumColumns: 1, + numRowsName: "/numRows", + numColumnsName: "/numColumns", + boundValueName: "/m0", + boundValueStartsAs: "vector", + }); + }); + + it("bind to altvector", async () => { + let core = await createTestCore({ + doenetML: ` +

Number of rows:

+

Number of columns:

+ +

Vector 1: ⟨a,b⟩

+

Matrix 2:

+

Matrix 3: $mi1.value{assignNames="m1"}

+ `, + }); + + await test_matrix_input({ + core, + initialValues: [["a"], ["b"]], + initialNumRows: 2, + initialNumColumns: 1, + numRowsName: "/numRows", + numColumnsName: "/numColumns", + boundValueName: "/m0", + boundValueStartsAs: "altvector", + }); + }); + + it("bind to tuple, ignore size via definition", async () => { + let core = await createTestCore({ + doenetML: ` +

Number of rows:

+

Number of columns:

+ +

Tuple 1: (a,b,c)

+

Matrix 2:

+

Matrix 3: $mi1.value{assignNames="m1"}

+ + `, + }); + + await test_matrix_input({ + core, + initialValues: [["a"], ["b"], ["c"]], + initialNumRows: 3, + initialNumColumns: 1, + numRowsName: "/numRows", + numColumnsName: "/numColumns", + ignoreInputSizes: true, + boundValueName: "/m0", + boundValueStartsAs: "tuple", + }); + }); + + it("bind to vector, ignore size via definition", async () => { + let core = await createTestCore({ + doenetML: ` +

Number of rows:

+

Number of columns:

+ +

Vector 1: (a,b,c)

+

Matrix 2:

+

Matrix 3: $mi1.value{assignNames="m1"}

+ + `, + }); + + await test_matrix_input({ + core, + initialValues: [["a"], ["b"], ["c"]], + initialNumRows: 3, + initialNumColumns: 1, + numRowsName: "/numRows", + numColumnsName: "/numColumns", + ignoreInputSizes: true, + boundValueName: "/m0", + boundValueStartsAs: "vector", + }); + }); + + it("bind to altvector, ignore size via definition", async () => { + let core = await createTestCore({ + doenetML: ` +

Number of rows:

+

Number of columns:

+ +

Vector 1: ⟨a,b,c⟩

+

Matrix 2:

+

Matrix 3: $mi1.value{assignNames="m1"}

+ + `, + }); + await test_matrix_input({ + core, + initialValues: [["a"], ["b"], ["c"]], + initialNumRows: 3, + initialNumColumns: 1, + numRowsName: "/numRows", + numColumnsName: "/numColumns", + ignoreInputSizes: true, + boundValueName: "/m0", + boundValueStartsAs: "altvector", + }); + }); + + it("bind to transpose of tuple", async () => { + let core = await createTestCore({ + doenetML: ` +

Number of rows:

+

Number of columns:

+ +

Tuple 1: (a,b)^T

+

Matrix 2:

+

Matrix 3: $mi1.value{assignNames="m1"}

+ `, + }); + + await test_matrix_input({ + core, + initialValues: [["a", "b"]], + initialNumRows: 1, + initialNumColumns: 2, + numRowsName: "/numRows", + numColumnsName: "/numColumns", + boundValueName: "/m0", + boundValueStartsAs: "tupleTrans", + }); + }); + + it("bind to transpose of vector", async () => { + let core = await createTestCore({ + doenetML: ` +

Number of rows:

+

Number of columns:

+ +

Vector 1: (a,b)^T

+

Matrix 2:

+

Matrix 3: $mi1.value{assignNames="m1"}

+ `, + }); + + await test_matrix_input({ + core, + initialValues: [["a", "b"]], + initialNumRows: 1, + initialNumColumns: 2, + numRowsName: "/numRows", + numColumnsName: "/numColumns", + boundValueName: "/m0", + boundValueStartsAs: "vectorTrans", + }); + }); + + it("bind to transpose of altvector", async () => { + let core = await createTestCore({ + doenetML: ` +

Number of rows:

+

Number of columns:

+ +

Vector 1: ⟨a,b⟩^T

+

Matrix 2:

+

Matrix 3: $mi1.value{assignNames="m1"}

+ `, + }); + + await test_matrix_input({ + core, + initialValues: [["a", "b"]], + initialNumRows: 1, + initialNumColumns: 2, + numRowsName: "/numRows", + numColumnsName: "/numColumns", + boundValueName: "/m0", + boundValueStartsAs: "altvectorTrans", + }); + }); + + it("bind to transpose of tuple, alternative format", async () => { + let core = await createTestCore({ + doenetML: ` +

Number of rows:

+

Number of columns:

+ +

Tuple 1: (a,b)'

+

Matrix 2:

+

Matrix 3: $mi1.value{assignNames="m1"}

+ `, + }); + + await test_matrix_input({ + core, + initialValues: [["a", "b"]], + initialNumRows: 1, + initialNumColumns: 2, + numRowsName: "/numRows", + numColumnsName: "/numColumns", + boundValueName: "/m0", + boundValueStartsAs: "tuplePrime", + }); + }); + + it("bind to transpose of vector, alternative format", async () => { + let core = await createTestCore({ + doenetML: ` +

Number of rows:

+

Number of columns:

+ +

Vector 1: (a,b)'

+

Matrix 2:

+

Matrix 3: $mi1.value{assignNames="m1"}

+ `, + }); + + await test_matrix_input({ + core, + initialValues: [["a", "b"]], + initialNumRows: 1, + initialNumColumns: 2, + numRowsName: "/numRows", + numColumnsName: "/numColumns", + boundValueName: "/m0", + boundValueStartsAs: "vectorPrime", + }); + }); + + it("bind to transpose of altvector, alternative format", async () => { + let core = await createTestCore({ + doenetML: ` +

Number of rows:

+

Number of columns:

+ +

Vector 1: ⟨a,b⟩'

+

Matrix 2:

+

Matrix 3: $mi1.value{assignNames="m1"}

+ `, + }); + + await test_matrix_input({ + core, + initialValues: [["a", "b"]], + initialNumRows: 1, + initialNumColumns: 2, + numRowsName: "/numRows", + numColumnsName: "/numColumns", + boundValueName: "/m0", + boundValueStartsAs: "altvectorPrime", + }); + }); + + it("bind to transpose of tuple, ignore size via definition", async () => { + let core = await createTestCore({ + doenetML: ` +

Number of rows:

+

Number of columns:

+ +

Tuple 1: (a,b,c)^T

+

Matrix 2:

+

Matrix 3: $mi1.value{assignNames="m1"}

+ `, + }); + + await test_matrix_input({ + core, + initialValues: [["a", "b", "c"]], + initialNumRows: 1, + initialNumColumns: 3, + numRowsName: "/numRows", + numColumnsName: "/numColumns", + ignoreInputSizes: true, + boundValueName: "/m0", + boundValueStartsAs: "tupleTrans", + }); + }); + + it("matrixInput and splitting symbols", async () => { + let core = await createTestCore({ + doenetML: ` + x2 + xyz + + + $varWithNum2.value{assignNames="varWithNum3"} + $noSplit2.value{assignNames="noSplit3"} + `, + }); + + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/varWithNum"].stateValues.value.tree).eq("x2"); + expect(stateVariables["/varWithNum2"].stateValues.value.tree).eqls([ + "matrix", + ["tuple", 1, 1], + ["tuple", ["tuple", "x2"]], + ]); + expect(stateVariables["/varWithNum3"].stateValues.value.tree).eqls([ + "matrix", + ["tuple", 1, 1], + ["tuple", ["tuple", "x2"]], + ]); + expect(stateVariables["/noSplit"].stateValues.value.tree).eq("xyz"); + expect(stateVariables["/noSplit2"].stateValues.value.tree).eqls([ + "matrix", + ["tuple", 1, 1], + ["tuple", ["tuple", "xyz"]], + ]); + expect(stateVariables["/noSplit3"].stateValues.value.tree).eqls([ + "matrix", + ["tuple", 1, 1], + ["tuple", ["tuple", "xyz"]], + ]); + + await updateMatrixInputValue({ + latex: "xu9j", + rowInd: 0, + colInd: 0, + name: "/varWithNum2", + core, + }); + await updateMatrixInputValue({ + latex: "xyuv", + rowInd: 0, + colInd: 0, + name: "/noSplit2", + core, + }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/varWithNum"].stateValues.value.tree).eqls([ + "matrix", + ["tuple", 1, 1], + ["tuple", ["tuple", "xu9j"]], + ]); + expect(stateVariables["/varWithNum2"].stateValues.value.tree).eqls([ + "matrix", + ["tuple", 1, 1], + ["tuple", ["tuple", "xu9j"]], + ]); + expect(stateVariables["/varWithNum3"].stateValues.value.tree).eqls([ + "matrix", + ["tuple", 1, 1], + ["tuple", ["tuple", "xu9j"]], + ]); + expect(stateVariables["/noSplit"].stateValues.value.tree).eqls([ + "matrix", + ["tuple", 1, 1], + ["tuple", ["tuple", "xyuv"]], + ]); + expect(stateVariables["/noSplit2"].stateValues.value.tree).eqls([ + "matrix", + ["tuple", 1, 1], + ["tuple", ["tuple", "xyuv"]], + ]); + expect(stateVariables["/noSplit3"].stateValues.value.tree).eqls([ + "matrix", + ["tuple", 1, 1], + ["tuple", ["tuple", "xyuv"]], + ]); + }); + + it("default entry", async () => { + let core = await createTestCore({ + doenetML: ` +

Matrix 1:

+

Matrix 2: $mi1.value{assignNames="m1"}

+ `, + }); + + await test_matrix_input({ + core, + initialValues: [ + [0, 0], + [0, 0], + ], + defaultEntry: 0, + }); + }); + + it("no default entry, prefill sparse matrix", async () => { + let core = await createTestCore({ + doenetML: ` + +

Sparse matrix: + \\begin{matrix}\\\\ & 3\\end{matrix} +

+

Matrix 1:

+

Matrix 2: $mi1.value{assignNames="m1"}

+ `, + }); + + await test_matrix_input({ + core, + initialValues: [ + [0, 0, "\uff3f"], + [0, 3, "\uff3f"], + ["\uff3f", "\uff3f", "\uff3f"], + ], + }); + }); + + it("default entry, prefill sparse matrix", async () => { + let core = await createTestCore({ + doenetML: ` + +

Sparse matrix: + \\begin{matrix}\\\\ & 3\\end{matrix} +

+

Matrix 1:

+

Matrix 2: $mi1.value{assignNames="m1"}

+ `, + }); + + await test_matrix_input({ + core, + initialValues: [ + [0, 0, 0], + [0, 3, 0], + [0, 0, 0], + ], + defaultEntry: 0, + }); + }); + + it("no default entry, bind value to sparse matrix", async () => { + let core = await createTestCore({ + doenetML: ` +

Sparse matrix: + \\begin{matrix}\\\\ & 3\\end{matrix} +

+

Matrix 1:

+

Matrix 2: $mi1.value{assignNames="m1"}

+ `, + }); + + await test_matrix_input({ + core, + initialValues: [ + [0, 0], + [0, 3], + ], + }); + }); + + it("default entry, bind value to sparse matrix", async () => { + let core = await createTestCore({ + doenetML: ` +

Sparse matrix: + \\begin{matrix}\\\\ & 3\\end{matrix} +

+

Matrix 1:

+

Matrix 21: $mi1.value{assignNames="m1"}

+ `, + }); + + await test_matrix_input({ + core, + initialValues: [ + [0, 0], + [0, 3], + ], + defaultEntry: 0, + }); + }); + + it("values from matrix prop using array notation", async () => { + let core = await createTestCore({ + doenetML: ` + + + a b + c d + + +

Matrix Input:

+

Matrix: $mi.matrix

+

Row number:

+

Column number:

+

Row: $mi.matrix[$rNum]

+

Entry: $mi.matrix[$rNum][$cNum]{assignNames="entry"}

+

Change entry: $entry

+ `, + }); + + let stateVariables = await returnAllStateVariables(core); + + expect(stateVariables["/pMatrix"].stateValues.text).eq( + "Matrix: [ [ a, b ], [ c, d ] ]", + ); + expect(stateVariables["/pRow"].stateValues.text).eq("Row: "); + expect(stateVariables["/pEntry"].stateValues.text).eq("Entry: "); + + // pick second row + await updateMathInputValue({ latex: "2", name: "/rNum", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/pRow"].stateValues.text).eq( + "Row: [ [ c, d ] ]", + ); + expect(stateVariables["/pEntry"].stateValues.text).eq("Entry: "); + + // pick first column + await updateMathInputValue({ latex: "1", name: "/cNum", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/pEntry"].stateValues.text).eq("Entry: c"); + + // change entry from bound value + await updateMathInputValue({ latex: "x", name: "/mi_entry", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/pMatrix"].stateValues.text).eq( + "Matrix: [ [ a, b ], [ x, d ] ]", + ); + expect(stateVariables["/pRow"].stateValues.text).eq( + "Row: [ [ x, d ] ]", + ); + expect(stateVariables["/pEntry"].stateValues.text).eq("Entry: x"); + + // change row + await updateMathInputValue({ latex: "1", name: "/rNum", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/pRow"].stateValues.text).eq( + "Row: [ [ a, b ] ]", + ); + expect(stateVariables["/pEntry"].stateValues.text).eq("Entry: a"); + + // change value from matrix input + await updateMatrixInputValue({ + latex: "y", + name: "/mi", + rowInd: 0, + colInd: 0, + core, + stateVariables, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/pMatrix"].stateValues.text).eq( + "Matrix: [ [ y, b ], [ x, d ] ]", + ); + expect(stateVariables["/pRow"].stateValues.text).eq( + "Row: [ [ y, b ] ]", + ); + expect(stateVariables["/pEntry"].stateValues.text).eq("Entry: y"); + + // change column + await updateMathInputValue({ latex: "2", name: "/cNum", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/pEntry"].stateValues.text).eq("Entry: b"); + + // change entry from bound value + await updateMathInputValue({ latex: "z", name: "/mi_entry", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/pMatrix"].stateValues.text).eq( + "Matrix: [ [ y, z ], [ x, d ] ]", + ); + expect(stateVariables["/pRow"].stateValues.text).eq( + "Row: [ [ y, z ] ]", + ); + expect(stateVariables["/pEntry"].stateValues.text).eq("Entry: z"); + }); + + it("parse scientific notation", async () => { + let core = await createTestCore({ + doenetML: ` +

+

+ + `, + }); + + let stateVariables = await returnAllStateVariables(core); + let entryName1 = stateVariables["/mi1"].activeChildren[0].componentName; + let entryName2 = stateVariables["/mi2"].activeChildren[0].componentName; + + expect(stateVariables["/mi1"].stateValues.text).eq("[ [ 5 E + 1 ] ]"); + expect(stateVariables["/m1"].stateValues.text).eq("[ [ 5 E + 1 ] ]"); + expect(stateVariables[entryName1].stateValues.rawRendererValue).eq( + "5 E + 1", + ); + expect(stateVariables[entryName1].stateValues.text).eq("5 E + 1"); + expect(stateVariables["/mi2"].stateValues.text).eq("[ [ 50 ] ]"); + expect(stateVariables["/m2"].stateValues.text).eq("[ [ 50 ] ]"); + expect(stateVariables[entryName2].stateValues.rawRendererValue).eq( + "50", + ); + expect(stateVariables[entryName2].stateValues.text).eq("50"); + + await updateMatrixInputValue({ + latex: "2x-3E+2", + name: "/mi1", + rowInd: 0, + colInd: 0, + core, + stateVariables, + }); + stateVariables = await returnAllStateVariables(core); + + expect(stateVariables["/mi1"].stateValues.text).eq( + "[ [ 2 x - 3 E + 2 ] ]", + ); + expect(stateVariables["/m1"].stateValues.text).eq( + "[ [ 2 x - 3 E + 2 ] ]", + ); + expect(stateVariables[entryName1].stateValues.rawRendererValue).eq( + "2x-3E+2", + ); + expect(stateVariables[entryName1].stateValues.text).eq("2 x - 3 E + 2"); + + await updateMatrixInputValue({ + latex: "2x-3E+2", + name: "/mi2", + rowInd: 0, + colInd: 0, + core, + stateVariables, + }); + stateVariables = await returnAllStateVariables(core); + + expect(stateVariables["/mi2"].stateValues.text).eq("[ [ 2 x - 300 ] ]"); + expect(stateVariables["/m2"].stateValues.text).eq("[ [ 2 x - 300 ] ]"); + expect(stateVariables[entryName2].stateValues.rawRendererValue).eq( + "2x-3E+2", + ); + expect(stateVariables[entryName2].stateValues.text).eq("2 x - 300"); + }); + + it("minComponentWidth attribute", async () => { + let core = await createTestCore({ + doenetML: ` +

Specify min component width:

+ +

Original:

+

Result:

+ `, + }); + + let stateVariables = await returnAllStateVariables(core); + let entry11OriginalName = + stateVariables["/original"].activeChildren[0].componentName; + let entry11ResultName = + stateVariables["/result"].activeChildren[0].componentName; + + expect(stateVariables["/mcw"].stateValues.minWidth).eq(50); + expect(stateVariables[entry11OriginalName].stateValues.minWidth).eq(0); + expect(stateVariables[entry11ResultName].stateValues.minWidth).eq(0); + + await updateMathInputValue({ latex: "100", name: "/mcw", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables[entry11ResultName].stateValues.minWidth).eq(100); + + await updateMatrixInputNumRows({ numRows: 2, name: "/original", core }); + await updateMatrixInputNumColumns({ + numColumns: 2, + name: "/original", + core, + }); + await updateMatrixInputNumRows({ numRows: 2, name: "/result", core }); + await updateMatrixInputNumColumns({ + numColumns: 2, + name: "/result", + core, + }); + stateVariables = await returnAllStateVariables(core); + + let entry12OriginalName = + stateVariables["/original"].activeChildren[1].componentName; + let entry21OriginalName = + stateVariables["/original"].activeChildren[2].componentName; + let entry22OriginalName = + stateVariables["/original"].activeChildren[3].componentName; + let entry12ResultName = + stateVariables["/result"].activeChildren[1].componentName; + let entry21ResultName = + stateVariables["/result"].activeChildren[2].componentName; + let entry22ResultName = + stateVariables["/result"].activeChildren[3].componentName; + + expect(stateVariables[entry11OriginalName].stateValues.minWidth).eq(0); + expect(stateVariables[entry12OriginalName].stateValues.minWidth).eq(0); + expect(stateVariables[entry21OriginalName].stateValues.minWidth).eq(0); + expect(stateVariables[entry22OriginalName].stateValues.minWidth).eq(0); + expect(stateVariables[entry11ResultName].stateValues.minWidth).eq(100); + expect(stateVariables[entry12ResultName].stateValues.minWidth).eq(100); + expect(stateVariables[entry21ResultName].stateValues.minWidth).eq(100); + expect(stateVariables[entry22ResultName].stateValues.minWidth).eq(100); + + await updateMathInputValue({ latex: "100x", name: "/mcw", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables[entry11ResultName].stateValues.minWidth).eq(0); + expect(stateVariables[entry12ResultName].stateValues.minWidth).eq(0); + expect(stateVariables[entry21ResultName].stateValues.minWidth).eq(0); + expect(stateVariables[entry22ResultName].stateValues.minWidth).eq(0); + + await updateMathInputValue({ latex: "17", name: "/mcw", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables[entry11ResultName].stateValues.minWidth).eq(17); + expect(stateVariables[entry12ResultName].stateValues.minWidth).eq(17); + expect(stateVariables[entry21ResultName].stateValues.minWidth).eq(17); + expect(stateVariables[entry22ResultName].stateValues.minWidth).eq(17); + + await updateMathInputValue({ latex: "-20", name: "/mcw", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables[entry11ResultName].stateValues.minWidth).eq(0); + expect(stateVariables[entry12ResultName].stateValues.minWidth).eq(0); + expect(stateVariables[entry21ResultName].stateValues.minWidth).eq(0); + expect(stateVariables[entry22ResultName].stateValues.minWidth).eq(0); + }); + + it("valueChanged", async () => { + let doenetML = ` +

+

+

+

$mi2.immediateValue

+ + `; + + async function check_items( + [mi1, mi2, mi3, mi4]: [ + mi1: string, + mi2: string, + mi3: string, + mi4: string, + ], + [mi1iv, mi2iv, mi3iv, mi4iv]: [ + mi1iv: string, + mi2iv: string, + mi3iv: string, + mi4iv: string, + ], + [mi1changed, mi2changed, mi3changed, mi4changed]: [ + mi1changed: boolean, + mi2changed: boolean, + mi3changed: boolean, + mi4changed: boolean, + ], + [mi1ivchanged, mi2ivchanged, mi3ivchanged, mi4ivchanged]: [ + mi1ivchanged: boolean, + mi2ivchanged: boolean, + mi3ivchanged: boolean, + mi4ivchanged: boolean, + ], + ) { + const stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/mi1"].stateValues.text).eq(mi1); + expect(stateVariables["/mi2"].stateValues.text).eq(mi2); + expect(stateVariables["/mi3"].stateValues.text).eq(mi3); + expect(stateVariables["/mi4"].stateValues.text).eq(mi4); + + expect(stateVariables["/mi1a"].stateValues.text).eq(mi1); + expect(stateVariables["/mi2a"].stateValues.text).eq(mi2); + expect(stateVariables["/mi3a"].stateValues.text).eq(mi3); + expect(stateVariables["/mi4a"].stateValues.text).eq(mi4); + + expect(stateVariables["/mi1iva"].stateValues.text).eq(mi1iv); + expect(stateVariables["/mi2iva"].stateValues.text).eq(mi2iv); + expect(stateVariables["/mi3iva"].stateValues.text).eq(mi3iv); + expect(stateVariables["/mi4iva"].stateValues.text).eq(mi4iv); + + expect(stateVariables["/mi1changed"].stateValues.value).eq( + mi1changed, + ); + expect(stateVariables["/mi2changed"].stateValues.value).eq( + mi2changed, + ); + expect(stateVariables["/mi3changed"].stateValues.value).eq( + mi3changed, + ); + expect(stateVariables["/mi4changed"].stateValues.value).eq( + mi4changed, + ); + + expect(stateVariables["/mi1ivchanged"].stateValues.value).eq( + mi1ivchanged, + ); + expect(stateVariables["/mi2ivchanged"].stateValues.value).eq( + mi2ivchanged, + ); + expect(stateVariables["/mi3ivchanged"].stateValues.value).eq( + mi3ivchanged, + ); + expect(stateVariables["/mi4ivchanged"].stateValues.value).eq( + mi4ivchanged, + ); + } + + let core = await createTestCore({ doenetML }); + + let mi1 = "[ [ \uff3f ] ]", + mi2 = "[ [ x ], [ y ] ]", + mi3 = "[ [ \uff3f ] ]", + mi4 = "[ [ x ], [ y ] ]"; + let mi1iv = "[ [ \uff3f ] ]", + mi2iv = "[ [ x ], [ y ] ]", + mi3iv = "[ [ \uff3f ] ]", + mi4iv = "[ [ x ], [ y ] ]"; + let mi1changed = false, + mi2changed = false, + mi3changed = false, + mi4changed = false; + let mi1ivchanged = false, + mi2ivchanged = false, + mi3ivchanged = false, + mi4ivchanged = false; + + await check_items( + [mi1, mi2, mi3, mi4], + [mi1iv, mi2iv, mi3iv, mi4iv], + [mi1changed, mi2changed, mi3changed, mi4changed], + [mi1ivchanged, mi2ivchanged, mi3ivchanged, mi4ivchanged], + ); + + // type in first marks only first immediate value as changed + + mi1iv = "[ [ z ] ]"; + mi1ivchanged = true; + await updateMatrixInputImmediateValue({ + latex: "z", + rowInd: 0, + colInd: 0, + name: "/mi1", + core, + }); + + await check_items( + [mi1, mi2, mi3, mi4], + [mi1iv, mi2iv, mi3iv, mi4iv], + [mi1changed, mi2changed, mi3changed, mi4changed], + [mi1ivchanged, mi2ivchanged, mi3ivchanged, mi4ivchanged], + ); + + // update value in first marks only first value as changed + mi1 = mi3 = mi3iv = mi1iv; + mi1changed = true; + await updateMatrixInputValueToImmediateValue({ + rowInd: 0, + colInd: 0, + name: "/mi1", + core, + }); + await check_items( + [mi1, mi2, mi3, mi4], + [mi1iv, mi2iv, mi3iv, mi4iv], + [mi1changed, mi2changed, mi3changed, mi4changed], + [mi1ivchanged, mi2ivchanged, mi3ivchanged, mi4ivchanged], + ); + + // type in second marks only second immediate value as changed + + mi4 = mi4iv = mi2iv = "[ [ a ], [ y ] ]"; + mi2ivchanged = true; + await updateMatrixInputImmediateValue({ + latex: "a", + rowInd: 0, + colInd: 0, + name: "/mi2", + core, + }); + + await check_items( + [mi1, mi2, mi3, mi4], + [mi1iv, mi2iv, mi3iv, mi4iv], + [mi1changed, mi2changed, mi3changed, mi4changed], + [mi1ivchanged, mi2ivchanged, mi3ivchanged, mi4ivchanged], + ); + + // update value in second marks only second value as changed + mi2 = mi2iv; + mi2changed = true; + await updateMatrixInputValueToImmediateValue({ + rowInd: 0, + colInd: 0, + name: "/mi2", + core, + }); + + await check_items( + [mi1, mi2, mi3, mi4], + [mi1iv, mi2iv, mi3iv, mi4iv], + [mi1changed, mi2changed, mi3changed, mi4changed], + [mi1ivchanged, mi2ivchanged, mi3ivchanged, mi4ivchanged], + ); + + // type in third marks third immediate value as changed + mi3iv = "[ [ b ] ]"; + mi3ivchanged = true; + await updateMatrixInputImmediateValue({ + latex: "b", + rowInd: 0, + colInd: 0, + name: "/mi3", + core, + }); + + await check_items( + [mi1, mi2, mi3, mi4], + [mi1iv, mi2iv, mi3iv, mi4iv], + [mi1changed, mi2changed, mi3changed, mi4changed], + [mi1ivchanged, mi2ivchanged, mi3ivchanged, mi4ivchanged], + ); + + // update value in third marks third value as changed + mi1 = mi1iv = mi3 = mi3iv; + mi3changed = true; + + await updateMatrixInputValueToImmediateValue({ + rowInd: 0, + colInd: 0, + name: "/mi3", + core, + }); + await check_items( + [mi1, mi2, mi3, mi4], + [mi1iv, mi2iv, mi3iv, mi4iv], + [mi1changed, mi2changed, mi3changed, mi4changed], + [mi1ivchanged, mi2ivchanged, mi3ivchanged, mi4ivchanged], + ); + + // type in fourth marks fourth immediate value as changed + + mi4iv = "[ [ a ], [ c ] ]"; + mi4ivchanged = true; + await updateMatrixInputImmediateValue({ + latex: "c", + rowInd: 1, + colInd: 0, + name: "/mi4", + core, + }); + + await check_items( + [mi1, mi2, mi3, mi4], + [mi1iv, mi2iv, mi3iv, mi4iv], + [mi1changed, mi2changed, mi3changed, mi4changed], + [mi1ivchanged, mi2ivchanged, mi3ivchanged, mi4ivchanged], + ); + + // update value in fourth marks fourth value as changed + mi2 = mi2iv = mi4 = mi4iv; + mi4changed = true; + await updateMatrixInputValueToImmediateValue({ + rowInd: 1, + colInd: 0, + name: "/mi4", + core, + }); + + await check_items( + [mi1, mi2, mi3, mi4], + [mi1iv, mi2iv, mi3iv, mi4iv], + [mi1changed, mi2changed, mi3changed, mi4changed], + [mi1ivchanged, mi2ivchanged, mi3ivchanged, mi4ivchanged], + ); + + // reload + + core = await createTestCore({ doenetML }); + + mi1 = "[ [ \uff3f ] ]"; + mi2 = "[ [ x ], [ y ] ]"; + mi3 = "[ [ \uff3f ] ]"; + mi4 = "[ [ x ], [ y ] ]"; + mi1iv = "[ [ \uff3f ] ]"; + mi2iv = "[ [ x ], [ y ] ]"; + mi3iv = "[ [ \uff3f ] ]"; + mi4iv = "[ [ x ], [ y ] ]"; + mi1changed = false; + mi2changed = false; + mi3changed = false; + mi4changed = false; + mi1ivchanged = false; + mi2ivchanged = false; + mi3ivchanged = false; + mi4ivchanged = false; + + await check_items( + [mi1, mi2, mi3, mi4], + [mi1iv, mi2iv, mi3iv, mi4iv], + [mi1changed, mi2changed, mi3changed, mi4changed], + [mi1ivchanged, mi2ivchanged, mi3ivchanged, mi4ivchanged], + ); + + // type in third marks only third immediate value as changed + mi3iv = "[ [ z ] ]"; + mi3ivchanged = true; + + await updateMatrixInputImmediateValue({ + latex: "z", + rowInd: 0, + colInd: 0, + name: "/mi3", + core, + }); + await check_items( + [mi1, mi2, mi3, mi4], + [mi1iv, mi2iv, mi3iv, mi4iv], + [mi1changed, mi2changed, mi3changed, mi4changed], + [mi1ivchanged, mi2ivchanged, mi3ivchanged, mi4ivchanged], + ); + + // update value in third marks first and third value/immediateValue as changed + mi1 = mi1iv = mi3 = mi3iv; + mi1changed = true; + mi1ivchanged = true; + mi3changed = true; + + await updateMatrixInputValueToImmediateValue({ + rowInd: 0, + colInd: 0, + name: "/mi3", + core, + }); + await check_items( + [mi1, mi2, mi3, mi4], + [mi1iv, mi2iv, mi3iv, mi4iv], + [mi1changed, mi2changed, mi3changed, mi4changed], + [mi1ivchanged, mi2ivchanged, mi3ivchanged, mi4ivchanged], + ); + + // type in fourth marks only fourth immediate value as changed + + mi4iv = "[ [ a ], [ y ] ]"; + mi4ivchanged = true; + + await updateMatrixInputImmediateValue({ + latex: "a", + rowInd: 0, + colInd: 0, + name: "/mi4", + core, + }); + await check_items( + [mi1, mi2, mi3, mi4], + [mi1iv, mi2iv, mi3iv, mi4iv], + [mi1changed, mi2changed, mi3changed, mi4changed], + [mi1ivchanged, mi2ivchanged, mi3ivchanged, mi4ivchanged], + ); + + // update value in fourth marks third and fourth value/immediateValue as changed + mi2 = mi2iv = mi4 = mi4iv; + mi2changed = true; + mi2ivchanged = true; + mi4changed = true; + await updateMatrixInputValueToImmediateValue({ + rowInd: 0, + colInd: 0, + name: "/mi4", + core, + }); + await check_items( + [mi1, mi2, mi3, mi4], + [mi1iv, mi2iv, mi3iv, mi4iv], + [mi1changed, mi2changed, mi3changed, mi4changed], + [mi1ivchanged, mi2ivchanged, mi3ivchanged, mi4ivchanged], + ); + + // reload + + core = await createTestCore({ doenetML }); + mi1 = "[ [ \uff3f ] ]"; + mi2 = "[ [ x ], [ y ] ]"; + mi3 = "[ [ \uff3f ] ]"; + mi4 = "[ [ x ], [ y ] ]"; + mi1iv = "[ [ \uff3f ] ]"; + mi2iv = "[ [ x ], [ y ] ]"; + mi3iv = "[ [ \uff3f ] ]"; + mi4iv = "[ [ x ], [ y ] ]"; + mi1changed = false; + mi2changed = false; + mi3changed = false; + mi4changed = false; + mi1ivchanged = false; + mi2ivchanged = false; + mi3ivchanged = false; + mi4ivchanged = false; + + await check_items( + [mi1, mi2, mi3, mi4], + [mi1iv, mi2iv, mi3iv, mi4iv], + [mi1changed, mi2changed, mi3changed, mi4changed], + [mi1ivchanged, mi2ivchanged, mi3ivchanged, mi4ivchanged], + ); + + // adding row to first marks first value/immediateValue as changed + + mi1 = mi3 = mi3iv = mi1iv = "[ [ \uff3f ], [ \uff3f ] ]"; + mi1changed = mi1ivchanged = true; + await updateMatrixInputNumRows({ numRows: 2, name: "/mi1", core }); + await check_items( + [mi1, mi2, mi3, mi4], + [mi1iv, mi2iv, mi3iv, mi4iv], + [mi1changed, mi2changed, mi3changed, mi4changed], + [mi1ivchanged, mi2ivchanged, mi3ivchanged, mi4ivchanged], + ); + + // adding column to second marks second value/immediateValue as changed + + mi2 = mi4 = mi4iv = mi2iv = "[ [ x, \uff3f ], [ y, \uff3f ] ]"; + mi2changed = mi2ivchanged = true; + await updateMatrixInputNumColumns({ + numColumns: 2, + name: "/mi2", + core, + }); + await check_items( + [mi1, mi2, mi3, mi4], + [mi1iv, mi2iv, mi3iv, mi4iv], + [mi1changed, mi2changed, mi3changed, mi4changed], + [mi1ivchanged, mi2ivchanged, mi3ivchanged, mi4ivchanged], + ); + + // reload + + core = await createTestCore({ doenetML }); + mi1 = "[ [ \uff3f ] ]"; + mi2 = "[ [ x ], [ y ] ]"; + mi3 = "[ [ \uff3f ] ]"; + mi4 = "[ [ x ], [ y ] ]"; + mi1iv = "[ [ \uff3f ] ]"; + mi2iv = "[ [ x ], [ y ] ]"; + mi3iv = "[ [ \uff3f ] ]"; + mi4iv = "[ [ x ], [ y ] ]"; + mi1changed = false; + mi2changed = false; + mi3changed = false; + mi4changed = false; + mi1ivchanged = false; + mi2ivchanged = false; + mi3ivchanged = false; + mi4ivchanged = false; + + await check_items( + [mi1, mi2, mi3, mi4], + [mi1iv, mi2iv, mi3iv, mi4iv], + [mi1changed, mi2changed, mi3changed, mi4changed], + [mi1ivchanged, mi2ivchanged, mi3ivchanged, mi4ivchanged], + ); + + // adding column to third marks first and third value/immediateValue as changed + mi1 = mi3 = mi3iv = mi1iv = "[ [ \uff3f ], [ \uff3f ] ]"; + mi3changed = mi3ivchanged = mi1changed = mi1ivchanged = true; + await updateMatrixInputNumRows({ numRows: 2, name: "/mi3", core }); + await check_items( + [mi1, mi2, mi3, mi4], + [mi1iv, mi2iv, mi3iv, mi4iv], + [mi1changed, mi2changed, mi3changed, mi4changed], + [mi1ivchanged, mi2ivchanged, mi3ivchanged, mi4ivchanged], + ); + + // subtracting row from fourth marks second and fourth value/immediateValue as changed + mi2 = mi4 = mi4iv = mi2iv = "[ [ x ] ]"; + mi4changed = mi4ivchanged = mi2changed = mi2ivchanged = true; + await updateMatrixInputNumRows({ + numRows: 1, + name: "/mi4", + core, + }); + await check_items( + [mi1, mi2, mi3, mi4], + [mi1iv, mi2iv, mi3iv, mi4iv], + [mi1changed, mi2changed, mi3changed, mi4changed], + [mi1ivchanged, mi2ivchanged, mi3ivchanged, mi4ivchanged], + ); + }); +}); diff --git a/packages/doenetml-worker/src/test/tagSpecific/module.test.ts b/packages/doenetml-worker/src/test/tagSpecific/module.test.ts new file mode 100644 index 000000000..950e0e024 --- /dev/null +++ b/packages/doenetml-worker/src/test/tagSpecific/module.test.ts @@ -0,0 +1,809 @@ +import { describe, expect, it, vi } from "vitest"; +import { createTestCore, returnAllStateVariables } from "../utils/test-core"; +import { cleanLatex } from "../utils/math"; +import { + movePoint, + submitAnswer, + updateMathInputValue, + updateTextInputValue, +} from "../utils/actions"; +import { widthsBySize } from "@doenet/utils"; + +const Mock = vi.fn(); +vi.stubGlobal("postMessage", Mock); +vi.mock("hyperformula"); + +describe("Module tag tests", async () => { + it("module with sentence", async () => { + let core = await createTestCore({ + doenetML: ` +

+ + + + Hello $item! + +

+ +

Hello $item!

+ +

$m{item="plant"}

+ +

+

$m{item="$item2"}

+

$m

+ + `, + }); + + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p1"].stateValues.text).contain("Hello who?!"); + expect(stateVariables["/p2"].stateValues.text).contain("Hello who?!"); + expect(stateVariables["/p3"].stateValues.text).contain("Hello plant!"); + expect(stateVariables["/p4"].stateValues.text).contain("Hello animal!"); + expect(stateVariables["/p5"].stateValues.text).contain("Hello who?!"); + + await updateTextInputValue({ text: "rock", name: "/item2", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p4"].stateValues.text).contain("Hello rock!"); + expect(stateVariables["/p1"].stateValues.text).contain("Hello who?!"); + expect(stateVariables["/p2"].stateValues.text).contain("Hello who?!"); + expect(stateVariables["/p3"].stateValues.text).contain("Hello plant!"); + expect(stateVariables["/p5"].stateValues.text).contain("Hello who?!"); + }); + + it("module with sentence, newnamespace", async () => { + let core = await createTestCore({ + doenetML: ` + + + + +

Hello $item!

+
+ +

Hello $(m/item)!

+ + $m{item="plant" name="m2"} +

+ $m{item="$item" name="m3"} + $m{name="m4"} + `, + }); + + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/m/p"].stateValues.text).eq("Hello who?!"); + expect(stateVariables["/p2"].stateValues.text).eq("Hello who?!"); + expect(stateVariables["/m2/p"].stateValues.text).eq("Hello plant!"); + expect(stateVariables["/m3/p"].stateValues.text).eq("Hello animal!"); + expect(stateVariables["/m4/p"].stateValues.text).eq("Hello who?!"); + + await updateTextInputValue({ text: "rock", name: "/item", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/m3/p"].stateValues.text).eq("Hello rock!"); + expect(stateVariables["/m/p"].stateValues.text).eq("Hello who?!"); + expect(stateVariables["/p2"].stateValues.text).eq("Hello who?!"); + expect(stateVariables["/m2/p"].stateValues.text).eq("Hello plant!"); + expect(stateVariables["/m4/p"].stateValues.text).eq("Hello who?!"); + }); + + it("module with sentence, nested newnamespaces", async () => { + let core = await createTestCore({ + doenetML: ` + + + + +

Hello $(mads/item)!

+
+ +

Hello $(m/mads/item)!

+ + $m{item="plant" name="m2"} +

+ $m{item="$item" name="m3"} + $m{name="m4"} + `, + }); + + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/m/p"].stateValues.text).eq("Hello who?!"); + expect(stateVariables["/p2"].stateValues.text).eq("Hello who?!"); + expect(stateVariables["/m2/p"].stateValues.text).eq("Hello plant!"); + expect(stateVariables["/m3/p"].stateValues.text).eq("Hello animal!"); + expect(stateVariables["/m4/p"].stateValues.text).eq("Hello who?!"); + + await updateTextInputValue({ text: "rock", name: "/item", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/m3/p"].stateValues.text).eq("Hello rock!"); + expect(stateVariables["/m/p"].stateValues.text).eq("Hello who?!"); + expect(stateVariables["/p2"].stateValues.text).eq("Hello who?!"); + expect(stateVariables["/m2/p"].stateValues.text).eq("Hello plant!"); + expect(stateVariables["/m4/p"].stateValues.text).eq("Hello who?!"); + }); + + it("module with sentence, triple nested newnamespaces", async () => { + let core = await createTestCore({ + doenetML: ` + + + + +

Hello $(mads/ma/item)!

+
+ +

Hello $(m/mads/ma/item)!

+ + $m{item="plant" name="m2"} +

+ $m{item="$item" name="m3"} + $m{name="m4"} + `, + }); + + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/m/p"].stateValues.text).eq("Hello who?!"); + expect(stateVariables["/p2"].stateValues.text).eq("Hello who?!"); + expect(stateVariables["/m2/p"].stateValues.text).eq("Hello plant!"); + expect(stateVariables["/m3/p"].stateValues.text).eq("Hello animal!"); + expect(stateVariables["/m4/p"].stateValues.text).eq("Hello who?!"); + + await updateTextInputValue({ text: "rock", name: "/item", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/m3/p"].stateValues.text).eq("Hello rock!"); + expect(stateVariables["/m/p"].stateValues.text).eq("Hello who?!"); + expect(stateVariables["/p2"].stateValues.text).eq("Hello who?!"); + expect(stateVariables["/m2/p"].stateValues.text).eq("Hello plant!"); + expect(stateVariables["/m4/p"].stateValues.text).eq("Hello who?!"); + }); + it("module with graph, newnamespace", async () => { + let core = await createTestCore({ + doenetML: ` + + + + + + + + + + +

Point coords: + + +

+
+ +

Point coords:

+

Graph size:

+ + $m{x="$x" y="$y" size="$s" aspectRatio="$ar" name="m2"} + `, + }); + + async function check_items({ + p1, + p2, + size, + ar, + }: { + p1: number[]; + p2: number[]; + size: string; + ar: number; + }) { + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/m/g"].stateValues.size).eq("medium"); + expect(stateVariables["/m/g"].stateValues.width.size).eq( + widthsBySize["medium"], + ); + expect(stateVariables["/m/g"].stateValues.aspectRatio).eq(1); + expect( + stateVariables["/m/p"].stateValues.xs.map((v) => v.tree), + ).eqls(p1); + expect(stateVariables["/m2/g"].stateValues.size).eq(size); + expect(stateVariables["/m2/g"].stateValues.width.size).eq( + widthsBySize[size], + ); + expect(stateVariables["/m2/g"].stateValues.aspectRatio).eq(ar); + expect( + stateVariables["/m2/p"].stateValues.xs.map((v) => v.tree), + ).eqls(p2); + } + + let p1 = [3, 5]; + let p2 = [7, -7]; + let size = "small"; + let ar = 0.5; + + await check_items({ p1, p2, size, ar }); + + p1 = [-6, 9]; + await updateMathInputValue({ + latex: p1[0].toString(), + name: "/m/x2", + core, + }); + await updateMathInputValue({ + latex: p1[1].toString(), + name: "/m/y2", + core, + }); + await check_items({ p1, p2, size, ar }); + + p2 = [1, 2]; + size = "large"; + ar = 3 / 2; + await updateMathInputValue({ + latex: p2[0].toString(), + name: "/x", + core, + }); + await updateMathInputValue({ + latex: p2[1].toString(), + name: "/y", + core, + }); + await updateTextInputValue({ text: size, name: "/s", core }); + await updateMathInputValue({ latex: "3/2", name: "/ar", core }); + await check_items({ p1, p2, size, ar }); + + p2 = [-3, 4]; + + await updateMathInputValue({ + latex: p2[0].toString(), + name: "/m2/x2", + core, + }); + await updateMathInputValue({ + latex: p2[1].toString(), + name: "/m2/y2", + core, + }); + await check_items({ p1, p2, size, ar }); + + p1 = [-8, 9]; + await movePoint({ name: "/m/p", x: p1[0], y: p1[1], core }); + await check_items({ p1, p2, size, ar }); + + p2 = [6, -10]; + await movePoint({ name: "/m2/p", x: p2[0], y: p2[1], core }); + await check_items({ p1, p2, size, ar }); + }); + + it("module inside a module", async () => { + let core = await createTestCore({ + doenetML: ` + + + + + + + + + + + + + + + + + + + $v + $(../m{x="$u+$vfixed" y="9" name="m"}) + + + +

Point coords:

+ $n{u="$x" v="$y" name="n2"} + + `, + }); + + async function check_items({ + mx, + my, + nx, + ny, + nmy, + n2x, + n2y, + n2my, + }: { + mx: number; + my: number; + nx: number; + ny: number; + nmy: number; + n2x: number; + n2y: number; + n2my: number; + }) { + const stateVariables = await returnAllStateVariables(core); + expect( + stateVariables["/m/p"].stateValues.xs.map((v) => v.tree), + ).eqls([mx, my]); + expect( + stateVariables["/n/p"].stateValues.xs.map((v) => v.tree), + ).eqls([nx, ny]); + expect( + stateVariables["/n/m/p"].stateValues.xs.map((v) => v.tree), + ).eqls([nx + ny, nmy]); + expect( + stateVariables["/n2/p"].stateValues.xs.map((v) => v.tree), + ).eqls([n2x, n2y]); + expect( + stateVariables["/n2/m/p"].stateValues.xs.map((v) => v.tree), + ).eqls([n2x + n2y, n2my]); + } + + let mx = 3; + let my = 5; + let nx = 1; + let ny = -2; + let nmy = 9; + let n2x = 7; + let n2y = -7; + let n2my = 9; + + await check_items({ mx, my, nx, ny, nmy, n2x, n2y, n2my }); + + n2x = -6; + n2y = 8; + await updateMathInputValue({ latex: n2x.toString(), name: "/x", core }); + await updateMathInputValue({ latex: n2y.toString(), name: "/y", core }); + await check_items({ mx, my, nx, ny, nmy, n2x, n2y, n2my }); + + mx = -2; + my = -4; + await movePoint({ name: "/m/p", x: mx, y: my, core }); + await check_items({ mx, my, nx, ny, nmy, n2x, n2y, n2my }); + + nx = 7; + ny = -3; + await movePoint({ name: "/n/p", x: nx, y: ny, core }); + await check_items({ mx, my, nx, ny, nmy, n2x, n2y, n2my }); + + nx = -2; + nmy = -7; + await movePoint({ name: "/n/m/p", x: nx + ny, y: nmy, core }); + await check_items({ mx, my, nx, ny, nmy, n2x, n2y, n2my }); + + n2x = 4; + n2y = 5; + await movePoint({ name: "/n2/p", x: n2x, y: n2y, core }); + await check_items({ mx, my, nx, ny, nmy, n2x, n2y, n2my }); + + n2x = -10; + n2my = -6; + await movePoint({ name: "/n2/m/p", x: n2x + n2y, y: n2my, core }); + await check_items({ mx, my, nx, ny, nmy, n2x, n2y, n2my }); + }); + + it("apply sugar in module attributes", async () => { + let core = await createTestCore({ + doenetML: ` + + + + +

Point: $P{name="p"}

+
+ + $m{P="(3,4)" name="m2"} + + + (5,6) + + $m{P="$Q" name="m3"} + + + `, + }); + + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/m/p"].stateValues.xs.map((v) => v.tree)).eqls([ + 1, 2, + ]); + expect(stateVariables["/m2/p"].stateValues.xs.map((v) => v.tree)).eqls([ + 3, 4, + ]); + expect(stateVariables["/m3/p"].stateValues.xs.map((v) => v.tree)).eqls([ + 5, 6, + ]); + + await movePoint({ name: "/Q", x: 7, y: 8, core }); + expect(stateVariables["/m3/p"].stateValues.xs.map((v) => v.tree)).eqls([ + 7, 8, + ]); + }); + + it("invalid attributes ignored in module", async () => { + // disabled is already an attribute on all components, so we can't add a custom attribute with that name + let core = await createTestCore({ + doenetML: ` + + + + +

Disabled? $disabled

+
+ + $m{name="m1"} + $m{disabled="true" name="m2"} + $m{disabled="false" name="m3"} + `, + }); + + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/m/p"].stateValues.text).eq("Disabled? "); + expect(stateVariables["/m1/p"].stateValues.text).eq("Disabled? "); + expect(stateVariables["/m2/p"].stateValues.text).eq("Disabled? "); + expect(stateVariables["/m3/p"].stateValues.text).eq("Disabled? "); + + let errorWarnings = core.errorWarnings; + expect(errorWarnings.errors.length).eq(0); + expect(errorWarnings.warnings.length).eq(4); + for (let i = 0; i < 4; i++) { + expect(errorWarnings.warnings[i].message).contain( + `Cannot add attribute "disabled" to a because the component type already has a "disabled" attribute defined`, + ); + } + }); + + it("handle error in custom attributes", async () => { + let core = await createTestCore({ + doenetML: ` + + + + + + + + `, + }); + + let stateVariables = await returnAllStateVariables(core); + let errorChild = + stateVariables[ + stateVariables["/_document1"].activeChildren[0].componentName + ]; + expect(errorChild.componentType).eq("_error"); + expect(errorChild.stateValues.message).eq( + "Duplicate component name: duplicate.", + ); + + let errorWarnings = core.errorWarnings; + + expect(errorWarnings.errors.length).eq(1); + expect(errorWarnings.warnings.length).eq(0); + + expect(errorWarnings.errors[0].message).contain( + "Duplicate component name: duplicate", + ); + expect(errorWarnings.errors[0].doenetMLrange.lineBegin).eq(6); + expect(errorWarnings.errors[0].doenetMLrange.charBegin).eq(9); + expect(errorWarnings.errors[0].doenetMLrange.lineEnd).eq(6); + expect(errorWarnings.errors[0].doenetMLrange.charEnd).eq(106); + }); + + it("warnings in custom attributes", async () => { + let core = await createTestCore({ + doenetML: ` + + + + + + + + + + + + + + +

b: $b

+
+ + + `, + }); + + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/m/p"].stateValues.text).eq("b: "); + expect(stateVariables["/m1/p"].stateValues.text).eq("b: hello"); + + let errorWarnings = core.errorWarnings; + + expect(errorWarnings.errors.length).eq(0); + expect(errorWarnings.warnings.length).eq(9); + + expect(errorWarnings.warnings[0].message).contain( + "Could not create . It must be inside a component that is inside a or similar component", + ); + expect(errorWarnings.warnings[0].level).eq(1); + expect(errorWarnings.warnings[0].doenetMLrange.lineBegin).eq(14); + expect(errorWarnings.warnings[0].doenetMLrange.charBegin).eq(7); + expect(errorWarnings.warnings[0].doenetMLrange.lineEnd).eq(14); + expect(errorWarnings.warnings[0].doenetMLrange.charEnd).eq(111); + + expect(errorWarnings.warnings[1].message).contain( + "Could not create . It must be inside a component that is inside a or similar component", + ); + expect(errorWarnings.warnings[1].level).eq(1); + expect(errorWarnings.warnings[1].doenetMLrange.lineBegin).eq(14); + expect(errorWarnings.warnings[1].doenetMLrange.charBegin).eq(7); + expect(errorWarnings.warnings[1].doenetMLrange.lineEnd).eq(14); + expect(errorWarnings.warnings[1].doenetMLrange.charEnd).eq(111); + + expect(errorWarnings.warnings[2].message).contain( + `Cannot add attribute "disabled" to a because the component type already has a "disabled" attribute defined`, + ); + expect(errorWarnings.warnings[2].level).eq(1); + expect(errorWarnings.warnings[2].doenetMLrange.lineBegin).eq(4); + expect(errorWarnings.warnings[2].doenetMLrange.charBegin).eq(9); + expect(errorWarnings.warnings[2].doenetMLrange.lineEnd).eq(4); + expect(errorWarnings.warnings[2].doenetMLrange.charEnd).eq(115); + + expect(errorWarnings.warnings[3].message).contain( + " contains an invalid component type: ", + ); + expect(errorWarnings.warnings[3].level).eq(1); + expect(errorWarnings.warnings[3].doenetMLrange.lineBegin).eq(6); + expect(errorWarnings.warnings[3].doenetMLrange.charBegin).eq(9); + expect(errorWarnings.warnings[3].doenetMLrange.lineEnd).eq(6); + expect(errorWarnings.warnings[3].doenetMLrange.charEnd).eq(96); + + expect(errorWarnings.warnings[4].message).contain( + `Since a default value was not supplied for with attribute="b", it will not be created unless a value is specified`, + ); + expect(errorWarnings.warnings[4].level).eq(1); + expect(errorWarnings.warnings[4].doenetMLrange.lineBegin).eq(8); + expect(errorWarnings.warnings[4].doenetMLrange.charBegin).eq(9); + expect(errorWarnings.warnings[4].doenetMLrange.lineEnd).eq(8); + expect(errorWarnings.warnings[4].doenetMLrange.charEnd).eq(78); + + expect(errorWarnings.warnings[5].message).contain( + ` must contain a componentType attribute`, + ); + expect(errorWarnings.warnings[5].level).eq(1); + expect(errorWarnings.warnings[5].doenetMLrange.lineBegin).eq(10); + expect(errorWarnings.warnings[5].doenetMLrange.charBegin).eq(9); + expect(errorWarnings.warnings[5].doenetMLrange.lineEnd).eq(10); + expect(errorWarnings.warnings[5].doenetMLrange.charEnd).eq(27); + + expect(errorWarnings.warnings[6].message).contain( + `Cannot add attribute "disabled" to a because the component type already has a "disabled" attribute defined`, + ); + expect(errorWarnings.warnings[6].level).eq(1); + expect(errorWarnings.warnings[6].doenetMLrange.lineBegin).eq(4); + expect(errorWarnings.warnings[6].doenetMLrange.charBegin).eq(9); + expect(errorWarnings.warnings[6].doenetMLrange.lineEnd).eq(4); + expect(errorWarnings.warnings[6].doenetMLrange.charEnd).eq(115); + + expect(errorWarnings.warnings[7].message).contain( + " contains an invalid component type: ", + ); + expect(errorWarnings.warnings[7].level).eq(1); + expect(errorWarnings.warnings[7].doenetMLrange.lineBegin).eq(6); + expect(errorWarnings.warnings[7].doenetMLrange.charBegin).eq(9); + expect(errorWarnings.warnings[7].doenetMLrange.lineEnd).eq(6); + expect(errorWarnings.warnings[7].doenetMLrange.charEnd).eq(96); + + expect(errorWarnings.warnings[8].message).contain( + ` must contain a componentType attribute`, + ); + expect(errorWarnings.warnings[8].level).eq(1); + expect(errorWarnings.warnings[8].doenetMLrange.lineBegin).eq(10); + expect(errorWarnings.warnings[8].doenetMLrange.charBegin).eq(9); + expect(errorWarnings.warnings[8].doenetMLrange.lineEnd).eq(10); + expect(errorWarnings.warnings[8].doenetMLrange.charEnd).eq(27); + }); + + it("copy module and overwrite attribute values", async () => { + let core = await createTestCore({ + doenetML: ` + + + + + +

The first number is $m; the second number is $n.

+

Next value? OK $q{name="q2"} it is.

+
+ + $md{name="md1"} + $md1{n="10" name="md2"} + $md2{m="100" name="md3"} + $md3{n="0" name="md4"} + + $md{m="13" n="17" name="md5"} + $md5{m="" n="a" name="md6"} + $md6{m="3" n="4" name="md7"} + + `, + }); + + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/md/p1"].stateValues.text).eq( + "The first number is 1; the second number is 2.", + ); + expect(stateVariables["/md1/p1"].stateValues.text).eq( + "The first number is 1; the second number is 2.", + ); + expect(stateVariables["/md2/p1"].stateValues.text).eq( + "The first number is 1; the second number is 10.", + ); + expect(stateVariables["/md3/p1"].stateValues.text).eq( + "The first number is 100; the second number is 10.", + ); + expect(stateVariables["/md4/p1"].stateValues.text).eq( + "The first number is 100; the second number is 0.", + ); + expect(stateVariables["/md5/p1"].stateValues.text).eq( + "The first number is 13; the second number is 17.", + ); + expect(stateVariables["/md6/p1"].stateValues.text).eq( + "The first number is NaN; the second number is NaN.", + ); + expect(stateVariables["/md7/p1"].stateValues.text).eq( + "The first number is 3; the second number is 4.", + ); + + for (let i = 0; i <= 7; i++) { + expect( + stateVariables[`/md${i || ""}/q2`].stateValues.value.tree, + ).eq("\uff3f"); + } + + let qs = ["x", "y", "z", "u", "v", "w", "s", "t"]; + + for (let [i, v] of qs.entries()) { + await updateMathInputValue({ + latex: v, + name: `/md${i || ""}/q`, + core, + }); + } + + stateVariables = await returnAllStateVariables(core); + for (let [i, v] of qs.entries()) { + expect( + stateVariables[`/md${i || ""}/q2`].stateValues.value.tree, + ).eq(v); + } + }); + + it("copy sourcesAreResponses with parent namespace target", async () => { + let core = await createTestCore({ + doenetML: ` + + + + + + + + + + + + + $title + +

Move the point to ($goalx, $goaly).

+ + + + + + + + + + + + $(../P) = ($(../goalx), $(../goaly)) + + + +
+
+
+ + +
First one + $mod{name="m1"} + +

Submitted response for problem 1: $(m1/ans.submittedResponse)

+

Credit for problem 1: $(m1/prob.creditAchieved{assignNames="ca1"})

+
+ +
Second one + +

Now, let's use initial point (-3, 3) and the goal point (7, -5)

+ + + $mod{title="Find point again" goalX="$xb" GoaLy="$yb" initialX="$xa" initialy="$ya" width="200px" aspectRatio="1" name="m2"} +

Submitted response for problem 2: $(m2/ans.submittedResponse)

+

Credit for problem 2: $(m2/prob.creditAchieved{assignNames="ca2"})

+
+ + `, + }); + + let stateVariables = await returnAllStateVariables(core); + expect(cleanLatex(stateVariables["/m1/m1"].stateValues.latex)).eq( + "(3,4)", + ); + expect(cleanLatex(stateVariables["/coordsa"].stateValues.latex)).eq( + "(-3,3)", + ); + expect(cleanLatex(stateVariables["/coordsb"].stateValues.latex)).eq( + "(7,-5)", + ); + expect(cleanLatex(stateVariables["/m2/m1"].stateValues.latex)).eq( + "(7,-5)", + ); + expect(cleanLatex(stateVariables["/sr1"].stateValues.latex)).eq("_"); + expect(stateVariables["/ca1"].stateValues.value).eq(0); + expect(cleanLatex(stateVariables["/sr2"].stateValues.latex)).eq("_"); + expect(stateVariables["/ca2"].stateValues.value).eq(0); + + expect(stateVariables["/m1/P"].stateValues.xs.map((v) => v.tree)).eqls([ + 0, 0, + ]); + expect(stateVariables["/m2/P"].stateValues.xs.map((v) => v.tree)).eqls([ + -3, 3, + ]); + + // submit answers + await submitAnswer({ name: "/m1/ans", core }); + await submitAnswer({ name: "/m2/ans", core }); + stateVariables = await returnAllStateVariables(core); + expect(cleanLatex(stateVariables["/sr1"].stateValues.latex)).eq( + "(0,0)", + ); + expect(stateVariables["/ca1"].stateValues.value).eq(0); + expect(cleanLatex(stateVariables["/sr2"].stateValues.latex)).eq( + "(-3,3)", + ); + expect(stateVariables["/ca2"].stateValues.value).eq(0); + + // move near correct answers + await movePoint({ name: "/m1/P", x: 3.2, y: 3.9, core }); + await movePoint({ name: "/m2/P", x: 7.2, y: -4.9, core }); + expect(cleanLatex(stateVariables["/m1/m1"].stateValues.latex)).eq( + "(3,4)", + ); + expect(cleanLatex(stateVariables["/coordsa"].stateValues.latex)).eq( + "(-3,3)", + ); + expect(cleanLatex(stateVariables["/coordsb"].stateValues.latex)).eq( + "(7,-5)", + ); + expect(cleanLatex(stateVariables["/m2/m1"].stateValues.latex)).eq( + "(7,-5)", + ); + + // submit answers + await submitAnswer({ name: "/m1/ans", core }); + await submitAnswer({ name: "/m2/ans", core }); + stateVariables = await returnAllStateVariables(core); + expect(cleanLatex(stateVariables["/sr1"].stateValues.latex)).eq( + "(3,4)", + ); + expect(stateVariables["/ca1"].stateValues.value).eq(1); + expect(cleanLatex(stateVariables["/sr2"].stateValues.latex)).eq( + "(7,-5)", + ); + expect(stateVariables["/ca2"].stateValues.value).eq(1); + }); +}); diff --git a/packages/doenetml-worker/src/test/tagSpecific/number.test.ts b/packages/doenetml-worker/src/test/tagSpecific/number.test.ts index 1516d0059..aa3830ca8 100644 --- a/packages/doenetml-worker/src/test/tagSpecific/number.test.ts +++ b/packages/doenetml-worker/src/test/tagSpecific/number.test.ts @@ -2,12 +2,16 @@ import { describe, expect, it, vi } from "vitest"; import { createTestCore, returnAllStateVariables } from "../utils/test-core"; import { cleanLatex } from "../utils/math"; import { + moveNumber, updateBooleanInputValue, updateMathInputValue, + updateSelectedIndices, } from "../utils/actions"; +import { test_in_graph } from "../utils/in-graph"; const Mock = vi.fn(); vi.stubGlobal("postMessage", Mock); +vi.mock("hyperformula"); describe("Number tag tests", async () => { it("1+1", async () => { @@ -47,12 +51,12 @@ describe("Number tag tests", async () => { let stateVariables = await returnAllStateVariables(core); expect(stateVariables["/n"].stateValues.value).eq(5); - await updateMathInputValue({ latex: "x", componentName: "/mi", core }); + await updateMathInputValue({ latex: "x", name: "/mi", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/n"].stateValues.value).eqls(NaN); - await updateMathInputValue({ latex: "9", componentName: "/mi", core }); + await updateMathInputValue({ latex: "9", name: "/mi", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/n"].stateValues.value).eq(9); @@ -722,7 +726,7 @@ describe("Number tag tests", async () => { // only digits await updateMathInputValue({ latex: "-\\infty", - componentName: "/ndecimals", + name: "/ndecimals", core, }); stateVariables = await returnAllStateVariables(core); @@ -731,7 +735,7 @@ describe("Number tag tests", async () => { // more digits await updateMathInputValue({ latex: "12", - componentName: "/ndigits", + name: "/ndigits", core, }); stateVariables = await returnAllStateVariables(core); @@ -740,7 +744,7 @@ describe("Number tag tests", async () => { // remove digits await updateMathInputValue({ latex: "0", - componentName: "/ndigits", + name: "/ndigits", core, }); stateVariables = await returnAllStateVariables(core); @@ -749,7 +753,7 @@ describe("Number tag tests", async () => { // Fewer digits than have await updateMathInputValue({ latex: "-10", - componentName: "/ndecimals", + name: "/ndecimals", core, }); stateVariables = await returnAllStateVariables(core); @@ -758,7 +762,7 @@ describe("Number tag tests", async () => { // add one digit await updateMathInputValue({ latex: "1", - componentName: "/ndigits", + name: "/ndigits", core, }); stateVariables = await returnAllStateVariables(core); @@ -767,12 +771,12 @@ describe("Number tag tests", async () => { // invalid precision means no rounding await updateMathInputValue({ latex: "x", - componentName: "/ndigits", + name: "/ndigits", core, }); await updateMathInputValue({ latex: "y", - componentName: "/ndecimals", + name: "/ndecimals", core, }); stateVariables = await returnAllStateVariables(core); @@ -781,7 +785,7 @@ describe("Number tag tests", async () => { // add a decimal await updateMathInputValue({ latex: "1", - componentName: "/ndecimals", + name: "/ndecimals", core, }); stateVariables = await returnAllStateVariables(core); @@ -790,12 +794,12 @@ describe("Number tag tests", async () => { // negative precision, ignores display digits await updateMathInputValue({ latex: "-3", - componentName: "/ndigits", + name: "/ndigits", core, }); await updateMathInputValue({ latex: "-3", - componentName: "/ndecimals", + name: "/ndecimals", core, }); stateVariables = await returnAllStateVariables(core); @@ -1195,52 +1199,52 @@ describe("Number tag tests", async () => { await updateMathInputValue({ latex: "3/4", - componentName: "/mi1", + name: "/mi1", core, }); await updateMathInputValue({ latex: "3/4", - componentName: "/mi2", + name: "/mi2", core, }); await updateMathInputValue({ latex: "3/4", - componentName: "/mi3", + name: "/mi3", core, }); await updateMathInputValue({ latex: "3/4", - componentName: "/mi4", + name: "/mi4", core, }); await updateMathInputValue({ latex: "3/4", - componentName: "/mi1a", + name: "/mi1a", core, }); await updateMathInputValue({ latex: "3/4", - componentName: "/mi2a", + name: "/mi2a", core, }); await updateMathInputValue({ latex: "3/4", - componentName: "/mi3a", + name: "/mi3a", core, }); await updateMathInputValue({ latex: "3/4", - componentName: "/mi4a", + name: "/mi4a", core, }); await updateMathInputValue({ latex: "3/4", - componentName: "/mi2b", + name: "/mi2b", core, }); await updateMathInputValue({ latex: "3/4", - componentName: "/mi4b", + name: "/mi4b", core, }); stateVariables = await returnAllStateVariables(core); @@ -1277,52 +1281,52 @@ describe("Number tag tests", async () => { await updateMathInputValue({ latex: "x", - componentName: "/mi1", + name: "/mi1", core, }); await updateMathInputValue({ latex: "x", - componentName: "/mi2", + name: "/mi2", core, }); await updateMathInputValue({ latex: "x", - componentName: "/mi3", + name: "/mi3", core, }); await updateMathInputValue({ latex: "x", - componentName: "/mi4", + name: "/mi4", core, }); await updateMathInputValue({ latex: "x", - componentName: "/mi1a", + name: "/mi1a", core, }); await updateMathInputValue({ latex: "x", - componentName: "/mi2a", + name: "/mi2a", core, }); await updateMathInputValue({ latex: "x", - componentName: "/mi3a", + name: "/mi3a", core, }); await updateMathInputValue({ latex: "x", - componentName: "/mi4a", + name: "/mi4a", core, }); await updateMathInputValue({ latex: "x", - componentName: "/mi2b", + name: "/mi2b", core, }); await updateMathInputValue({ latex: "x", - componentName: "/mi4b", + name: "/mi4b", core, }); stateVariables = await returnAllStateVariables(core); @@ -1559,9 +1563,9 @@ describe("Number tag tests", async () => { expect(stateVariables["/mi2"].stateValues.value.tree).eqls(1); expect(stateVariables["/mi3"].stateValues.value.tree).eqls(NaN); - await updateMathInputValue({ latex: "i", componentName: "/mi1", core }); - await updateMathInputValue({ latex: "i", componentName: "/mi2", core }); - await updateMathInputValue({ latex: "i", componentName: "/mi3", core }); + await updateMathInputValue({ latex: "i", name: "/mi1", core }); + await updateMathInputValue({ latex: "i", name: "/mi2", core }); + await updateMathInputValue({ latex: "i", name: "/mi3", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/n1"].stateValues.value).eqls({ @@ -1582,17 +1586,17 @@ describe("Number tag tests", async () => { await updateMathInputValue({ latex: "i+2", - componentName: "/mi1", + name: "/mi1", core, }); await updateMathInputValue({ latex: "i+2", - componentName: "/mi2", + name: "/mi2", core, }); await updateMathInputValue({ latex: "i+2", - componentName: "/mi3", + name: "/mi3", core, }); @@ -1621,17 +1625,17 @@ describe("Number tag tests", async () => { await updateMathInputValue({ latex: "3+0i", - componentName: "/mi1", + name: "/mi1", core, }); await updateMathInputValue({ latex: "3+0i", - componentName: "/mi2", + name: "/mi2", core, }); await updateMathInputValue({ latex: "3+0i", - componentName: "/mi3", + name: "/mi3", core, }); @@ -1645,17 +1649,17 @@ describe("Number tag tests", async () => { await updateMathInputValue({ latex: "1i", - componentName: "/mi1", + name: "/mi1", core, }); await updateMathInputValue({ latex: "1i", - componentName: "/mi2", + name: "/mi2", core, }); await updateMathInputValue({ latex: "1i", - componentName: "/mi3", + name: "/mi3", core, }); @@ -1678,17 +1682,17 @@ describe("Number tag tests", async () => { await updateMathInputValue({ latex: "-1i+0", - componentName: "/mi1", + name: "/mi1", core, }); await updateMathInputValue({ latex: "-1i+0", - componentName: "/mi2", + name: "/mi2", core, }); await updateMathInputValue({ latex: "-1i+0", - componentName: "/mi3", + name: "/mi3", core, }); @@ -1739,221 +1743,13 @@ describe("Number tag tests", async () => { }); it("number in graph", async () => { - let core = await createTestCore({ - doenetML: ` + const doenetMLsnippet = ` - $content1 - -17 + $content1 + -17 + `; -

Anchor 1 coordinates:

-

Anchor 2 coordinates:

-

Change anchor 1 coordinates:

-

Change anchor 2 coordinates:

-

Position from anchor 1: $number1.positionFromAnchor

-

Position from anchor 2: $number2.positionFromAnchor

-

Change position from anchor 1 - - upperRight - upperLeft - lowerRight - lowerLeft - left - right - top - bottom - center - -

-

Change position from anchor 2 - - upperRight - upperLeft - lowerRight - lowerLeft - left - right - top - bottom - center - -

-

Draggable 1: $draggable1

-

Draggable 2: $draggable2

-

Change draggable 1

-

Change draggable 2

-

Content 1: $number1

-

Content 2: $number2

-

Content 1

-

Content 2

- - `, - }); - - let stateVariables = await returnAllStateVariables(core); - - expect( - cleanLatex(stateVariables["/number1anchor"].stateValues.latex), - ).eq("(1,3)"); - expect( - cleanLatex(stateVariables["/number2anchor"].stateValues.latex), - ).eq("(0,0)"); - - expect(stateVariables["/pPositionFromAnchor1"].stateValues.text).eq( - "Position from anchor 1: upperright", - ); - expect(stateVariables["/pPositionFromAnchor2"].stateValues.text).eq( - "Position from anchor 2: center", - ); - expect( - stateVariables["/positionFromAnchor1"].stateValues.selectedIndices, - ).eqls([1]); - expect( - stateVariables["/positionFromAnchor2"].stateValues.selectedIndices, - ).eqls([9]); - - expect(stateVariables["/pDraggable1"].stateValues.text).eq( - "Draggable 1: true", - ); - expect(stateVariables["/pDraggable2"].stateValues.text).eq( - "Draggable 2: true", - ); - expect(stateVariables["/pContent1"].stateValues.text).eq( - "Content 1: 11", - ); - expect(stateVariables["/pContent2"].stateValues.text).eq( - "Content 2: -17", - ); - - // move numbers by dragging - await core.requestAction({ - actionName: "moveNumber", - componentName: "/number1", - args: { x: -2, y: 3 }, - event: null, - }); - await core.requestAction({ - actionName: "moveNumber", - componentName: "/number2", - args: { x: 4, y: -5 }, - event: null, - }); - - stateVariables = await returnAllStateVariables(core); - - expect( - cleanLatex(stateVariables["/number1anchor"].stateValues.latex), - ).eq("(-2,3)"); - expect( - cleanLatex(stateVariables["/number2anchor"].stateValues.latex), - ).eq("(4,-5)"); - - // move numbers by entering coordinates - - await updateMathInputValue({ - latex: "(6,7)", - componentName: "/anchorCoords1", - core, - }); - await updateMathInputValue({ - latex: "(8,9)", - componentName: "/anchorCoords2", - core, - }); - - stateVariables = await returnAllStateVariables(core); - - expect( - cleanLatex(stateVariables["/number1anchor"].stateValues.latex), - ).eq("(6,7)"); - expect( - cleanLatex(stateVariables["/number2anchor"].stateValues.latex), - ).eq("(8,9)"); - - // change position from anchor - await core.requestAction({ - actionName: "updateSelectedIndices", - componentName: "/positionFromAnchor1", - args: { selectedIndices: [4] }, - event: null, - }); - await core.requestAction({ - actionName: "updateSelectedIndices", - componentName: "/positionFromAnchor2", - args: { selectedIndices: [3] }, - event: null, - }); - - stateVariables = await returnAllStateVariables(core); - - expect(stateVariables["/pPositionFromAnchor1"].stateValues.text).eq( - "Position from anchor 1: lowerleft", - ); - expect(stateVariables["/pPositionFromAnchor2"].stateValues.text).eq( - "Position from anchor 2: lowerright", - ); - - // make not draggable - await updateBooleanInputValue({ - boolean: false, - componentName: "/draggable1", - core, - }); - await updateBooleanInputValue({ - boolean: false, - componentName: "/draggable2", - core, - }); - stateVariables = await returnAllStateVariables(core); - - expect(stateVariables["/pDraggable1"].stateValues.text).eq( - "Draggable 1: false", - ); - expect(stateVariables["/pDraggable2"].stateValues.text).eq( - "Draggable 2: false", - ); - - // cannot move numbers by dragging - await core.requestAction({ - actionName: "moveNumber", - componentName: "/number1", - args: { x: -10, y: -9 }, - event: null, - }); - await core.requestAction({ - actionName: "moveNumber", - componentName: "/number2", - args: { x: -8, y: -7 }, - event: null, - }); - - stateVariables = await returnAllStateVariables(core); - - expect( - cleanLatex(stateVariables["/number1anchor"].stateValues.latex), - ).eq("(6,7)"); - expect( - cleanLatex(stateVariables["/number2anchor"].stateValues.latex), - ).eq("(8,9)"); - - // change content of number - await updateMathInputValue({ - latex: "11+5", - componentName: "/content1", - core, - }); - await updateMathInputValue({ - latex: "-17-1", - componentName: "/content2", - core, - }); - stateVariables = await returnAllStateVariables(core); - - expect(stateVariables["/pContent1"].stateValues.text).eq( - "Content 1: 16", - ); - expect(stateVariables["/pContent2"].stateValues.text).eq( - "Content 2: -18", - ); + await test_in_graph(doenetMLsnippet, moveNumber); }); }); diff --git a/packages/doenetml-worker/src/test/tagSpecific/numberlist.test.ts b/packages/doenetml-worker/src/test/tagSpecific/numberlist.test.ts index 1303dcde9..8b18d1b75 100644 --- a/packages/doenetml-worker/src/test/tagSpecific/numberlist.test.ts +++ b/packages/doenetml-worker/src/test/tagSpecific/numberlist.test.ts @@ -4,9 +4,11 @@ import { updateBooleanInputValue, updateMathInputValue, } from "../utils/actions"; +import Core from "../../Core"; const Mock = vi.fn(); vi.stubGlobal("postMessage", Mock); +vi.mock("hyperformula"); describe("NumberList tag tests", async () => { async function test_numberList({ @@ -16,7 +18,7 @@ describe("NumberList tag tests", async () => { text, numbers, }: { - core: any; + core: Core; name?: string; pName?: string; text?: string; @@ -141,7 +143,7 @@ describe("NumberList tag tests", async () => { }); }); - async function test_nested_and_inverse(core: any) { + async function test_nested_and_inverse(core: Core) { await test_numberList({ core, name: "/nl1", @@ -179,47 +181,47 @@ describe("NumberList tag tests", async () => { // change values await updateMathInputValue({ - componentName: "/mi1", + name: "/mi1", latex: "-11", core, }); await updateMathInputValue({ - componentName: "/mi2", + name: "/mi2", latex: "-12", core, }); await updateMathInputValue({ - componentName: "/mi3", + name: "/mi3", latex: "-13", core, }); await updateMathInputValue({ - componentName: "/mi4", + name: "/mi4", latex: "-14", core, }); await updateMathInputValue({ - componentName: "/mi5", + name: "/mi5", latex: "-15", core, }); await updateMathInputValue({ - componentName: "/mi6", + name: "/mi6", latex: "-16", core, }); await updateMathInputValue({ - componentName: "/mi7", + name: "/mi7", latex: "-17", core, }); await updateMathInputValue({ - componentName: "/mi8", + name: "/mi8", latex: "-18", core, }); await updateMathInputValue({ - componentName: "/mi9", + name: "/mi9", latex: "-19", core, }); @@ -484,13 +486,13 @@ describe("NumberList tag tests", async () => { await check_items(max1, max2); max1 = Infinity; - await updateMathInputValue({ latex: "", componentName: "/mn1", core }); + await updateMathInputValue({ latex: "", name: "/mn1", core }); await check_items(max1, max2); max2 = 3; await updateMathInputValue({ latex: max2.toString(), - componentName: "/mn2", + name: "/mn2", core, }); await check_items(max1, max2); @@ -498,7 +500,7 @@ describe("NumberList tag tests", async () => { max1 = 4; await updateMathInputValue({ latex: max1.toString(), - componentName: "/mn1", + name: "/mn1", core, }); await check_items(max1, max2); @@ -506,7 +508,7 @@ describe("NumberList tag tests", async () => { max1 = 1; await updateMathInputValue({ latex: max1.toString(), - componentName: "/mn1", + name: "/mn1", core, }); await check_items(max1, max2); @@ -514,7 +516,7 @@ describe("NumberList tag tests", async () => { max2 = 10; await updateMathInputValue({ latex: max2.toString(), - componentName: "/mn2", + name: "/mn2", core, }); await check_items(max1, max2); @@ -567,7 +569,7 @@ describe("NumberList tag tests", async () => { for (let [i, v] of vals.entries()) { await updateMathInputValue({ latex: v.toString(), - componentName: `/mi${i + 1}`, + name: `/mi${i + 1}`, core, }); } @@ -653,7 +655,7 @@ describe("NumberList tag tests", async () => { maxN = 4; await updateMathInputValue({ latex: maxN.toString(), - componentName: "/maxN", + name: "/maxN", core, }); await check_items(maxN); @@ -661,7 +663,7 @@ describe("NumberList tag tests", async () => { maxN = 1; await updateMathInputValue({ latex: maxN.toString(), - componentName: "/maxN", + name: "/maxN", core, }); await check_items(maxN); @@ -1103,7 +1105,7 @@ describe("NumberList tag tests", async () => { n2 = 83; await updateMathInputValue({ latex: n2.toString(), - componentName: "/mi1", + name: "/mi1", core, }); await test_items(n1, n2, n3); @@ -1112,7 +1114,7 @@ describe("NumberList tag tests", async () => { n3 = 2; await updateMathInputValue({ latex: `(${n1}, ${n3})`, - componentName: "/mi2", + name: "/mi2", core, }); await test_items(n1, n2, n3); @@ -1162,7 +1164,7 @@ describe("NumberList tag tests", async () => { n2 = 6; await updateMathInputValue({ latex: `${n1}, ${n2}`, - componentName: "/mi", + name: "/mi", core, }); await test_numberList({ diff --git a/packages/doenetml-worker/src/test/tagSpecific/p.test.ts b/packages/doenetml-worker/src/test/tagSpecific/p.test.ts index 39022861c..7a631a56a 100644 --- a/packages/doenetml-worker/src/test/tagSpecific/p.test.ts +++ b/packages/doenetml-worker/src/test/tagSpecific/p.test.ts @@ -3,6 +3,7 @@ import { createTestCore, returnAllStateVariables } from "../utils/test-core"; const Mock = vi.fn(); vi.stubGlobal("postMessage", Mock); +vi.mock("hyperformula"); describe("P tag tests", async () => { it("two paragraphs", async () => { diff --git a/packages/doenetml-worker/src/test/tagSpecific/paginator.test.ts b/packages/doenetml-worker/src/test/tagSpecific/paginator.test.ts new file mode 100644 index 000000000..c60670040 --- /dev/null +++ b/packages/doenetml-worker/src/test/tagSpecific/paginator.test.ts @@ -0,0 +1,244 @@ +import { describe, expect, it, vi } from "vitest"; +import { createTestCore, returnAllStateVariables } from "../utils/test-core"; +import { + callAction, + submitAnswer, + updateMathInputValue, + updateTextInputValue, +} from "../utils/actions"; + +const Mock = vi.fn(); +vi.stubGlobal("postMessage", Mock); +vi.mock("hyperformula"); + +describe("Paginator tag tests", async () => { + it("Multiple sections in paginator", async () => { + let core = await createTestCore({ + doenetML: ` + + + +
+ Page 1 +

What is 1+1? $two

+ 2 +
+
+

What is your name?

+

Hello, $name!

+
+
+ Page 3 + 2x +

What is x+x? $twox

+

What is y+y? 2y

+
+
+

+ + + + Page $pgn.currentPage{assignNames="pageNum"} + of $pgn.numPages{assignNames="numPages"} + + + + +

+

What is 2+2? 4

+ +

Credit achieved: $_document1.creditAchieved{assignNames="ca"}

+ + `, + }); + + let stateVariables = await returnAllStateVariables(core); + + let mathinput1Name = + stateVariables["/answer1"].stateValues.inputChildren[0] + .componentName; + let mathinput2Name = + stateVariables["/answer2"].stateValues.inputChildren[0] + .componentName; + let mathinput3Name = + stateVariables["/answer3"].stateValues.inputChildren[0] + .componentName; + let mathinput4Name = + stateVariables["/answer4"].stateValues.inputChildren[0] + .componentName; + + expect(stateVariables["/pgn"].stateValues.numPages).eq(3); + expect(stateVariables["/pgn"].stateValues.currentPage).eq(1); + expect(stateVariables["/pcontrols"].stateValues.numPages).eq(3); + expect(stateVariables["/pcontrols"].stateValues.currentPage).eq(1); + + expect(stateVariables["/pgn"].stateValues.childIndicesToRender).eqls([ + 0, + ]); + expect(stateVariables["/ca"].stateValues.value).eq(0); + expect(stateVariables["/title1"].stateValues.value).eq("Page 1"); + expect(stateVariables["/title3"].stateValues.value).eq("Page 3"); + + await updateMathInputValue({ latex: "4", name: mathinput4Name, core }); + await submitAnswer({ name: "/answer4", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/answer4"].stateValues.creditAchieved).eq(1); + expect(stateVariables["/ca"].stateValues.value).eq(0.25); + + await updateMathInputValue({ latex: "2", name: mathinput1Name, core }); + await submitAnswer({ name: "/answer1", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/answer1"].stateValues.creditAchieved).eq(1); + expect(stateVariables["/ca"].stateValues.value).eq(0.5); + + // move to page 2 + await core.requestAction({ + componentName: "/pgn", + actionName: "setPage", + args: { number: 2 }, + event: null, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/pgn"].stateValues.currentPage).eq(2); + expect(stateVariables["/pgn"].stateValues.childIndicesToRender).eqls([ + 1, + ]); + expect(stateVariables["/pcontrols"].stateValues.currentPage).eq(2); + expect(stateVariables["/ca"].stateValues.value).eq(0.5); + + await updateTextInputValue({ text: "Me", name: "/name", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p3"].stateValues.text).eq("Hello, Me!"); + expect(stateVariables["/ca"].stateValues.value).eq(0.5); + + await updateMathInputValue({ latex: "3", name: mathinput4Name, core }); + await submitAnswer({ name: "/answer4", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/answer4"].stateValues.creditAchieved).eq(0); + expect(stateVariables["/ca"].stateValues.value).eq(0.25); + + // back to page 1 + await core.requestAction({ + componentName: "/pgn", + actionName: "setPage", + args: { number: 1 }, + event: null, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/pgn"].stateValues.currentPage).eq(1); + expect(stateVariables["/pgn"].stateValues.childIndicesToRender).eqls([ + 0, + ]); + expect(stateVariables["/pcontrols"].stateValues.currentPage).eq(1); + expect(stateVariables["/ca"].stateValues.value).eq(0.25); + + // back to second page + await callAction({ name: "/nextPage", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/pgn"].stateValues.currentPage).eq(2); + expect(stateVariables["/pgn"].stateValues.childIndicesToRender).eqls([ + 1, + ]); + expect(stateVariables["/pcontrols"].stateValues.currentPage).eq(2); + expect(stateVariables["/ca"].stateValues.value).eq(0.25); + + await updateMathInputValue({ latex: "4", name: mathinput4Name, core }); + await submitAnswer({ name: "/answer4", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/answer4"].stateValues.creditAchieved).eq(1); + expect(stateVariables["/ca"].stateValues.value).eq(0.5); + + // on to third page + await core.requestAction({ + componentName: "/pgn", + actionName: "setPage", + args: { number: 3 }, + event: null, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/pgn"].stateValues.currentPage).eq(3); + expect(stateVariables["/pgn"].stateValues.childIndicesToRender).eqls([ + 2, + ]); + expect(stateVariables["/pcontrols"].stateValues.currentPage).eq(3); + expect(stateVariables["/ca"].stateValues.value).eq(0.5); + + await updateMathInputValue({ latex: "2x", name: mathinput2Name, core }); + await submitAnswer({ name: "/answer2", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/answer2"].stateValues.creditAchieved).eq(1); + expect(stateVariables["/ca"].stateValues.value).eq(0.75); + + await updateMathInputValue({ latex: "2y", name: mathinput3Name, core }); + await submitAnswer({ name: "/answer3", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/answer3"].stateValues.creditAchieved).eq(1); + expect(stateVariables["/ca"].stateValues.value).eq(1); + + await updateMathInputValue({ latex: "2z", name: mathinput2Name, core }); + await submitAnswer({ name: "/answer2", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/answer2"].stateValues.creditAchieved).eq(0); + expect(stateVariables["/ca"].stateValues.value).eq(0.75); + + // back to second page + await callAction({ name: "/prevPage", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/pgn"].stateValues.currentPage).eq(2); + expect(stateVariables["/pgn"].stateValues.childIndicesToRender).eqls([ + 1, + ]); + expect(stateVariables["/pcontrols"].stateValues.currentPage).eq(2); + expect(stateVariables["/ca"].stateValues.value).eq(0.75); + }); + + it("Set page action ignores read only flag", async () => { + let core = await createTestCore({ + doenetML: ` + + + + + Problem 1 +

1: 1

+
+ + Problem 2 +

2: 2

+
+
+

Credit achieved: $_document1.creditAchieved{assignNames="ca"}

+ `, + flags: { readOnly: true }, + }); + + let stateVariables = await returnAllStateVariables(core); + + expect(stateVariables["/pgn"].stateValues.numPages).eq(2); + expect(stateVariables["/pgn"].stateValues.currentPage).eq(1); + expect(stateVariables["/pcontrols"].stateValues.numPages).eq(2); + expect(stateVariables["/pcontrols"].stateValues.currentPage).eq(1); + + expect(stateVariables["/pgn"].stateValues.childIndicesToRender).eqls([ + 0, + ]); + + expect(stateVariables["/ti1"].stateValues.disabled).eq(true); + expect(stateVariables["/ti2"].stateValues.disabled).eq(true); + + await core.requestAction({ + componentName: "/pgn", + actionName: "setPage", + args: { number: 2 }, + event: null, + }); + stateVariables = await returnAllStateVariables(core); + + expect(stateVariables["/pgn"].stateValues.currentPage).eq(2); + expect(stateVariables["/pcontrols"].stateValues.currentPage).eq(2); + + expect(stateVariables["/pgn"].stateValues.childIndicesToRender).eqls([ + 1, + ]); + }); +}); diff --git a/packages/doenetml-worker/src/test/tagSpecific/periodicset.test.ts b/packages/doenetml-worker/src/test/tagSpecific/periodicset.test.ts new file mode 100644 index 000000000..42f64a5c5 --- /dev/null +++ b/packages/doenetml-worker/src/test/tagSpecific/periodicset.test.ts @@ -0,0 +1,911 @@ +import { describe, expect, it, vi } from "vitest"; +import { createTestCore, returnAllStateVariables } from "../utils/test-core"; +import { submitAnswer, updateMathInputValue } from "../utils/actions"; +import Core from "../../Core"; + +const Mock = vi.fn(); +vi.stubGlobal("postMessage", Mock); +vi.mock("hyperformula"); + +type Offset = + | string + | number + | (string | number | (string | number | (string | number)[])[])[]; +type Period = string | number | (string | number)[]; +type PeriodicSetItem = ["tuple", Offset, Period, number, number]; + +type PeriodicSet = ["periodic_set", ...PeriodicSetItem[]]; + +async function check_periodic_set({ + core, + name, + offsets, + period, + lim1 = -Infinity, + lim2 = Infinity, + redundantOffsets = false, +}: { + core: Core; + name: string; + offsets?: Offset[]; + period?: Period; + lim1?: number; + lim2?: number; + redundantOffsets?: boolean; +}) { + const stateVariables = await returnAllStateVariables(core); + if (offsets === undefined || period === undefined) { + expect(stateVariables[name].stateValues.value.tree).eq("\uff3f"); + } else { + let items: PeriodicSetItem[] = offsets.map((os) => [ + "tuple", + os, + period, + lim1, + lim2, + ]); + + let s: PeriodicSet = ["periodic_set", ...items]; + + expect(stateVariables[name].stateValues.value.tree).eqls(s); + expect(stateVariables[name].stateValues.numOffsets).eq(offsets.length); + + expect( + stateVariables[name].stateValues.offsets.map((v) => v.tree), + ).eqls(offsets); + expect(stateVariables[name].stateValues.period.tree).eqls(period); + expect(stateVariables[name].stateValues.redundantOffsets).eq( + redundantOffsets, + ); + } +} + +describe("PeriodicSet tag tests", async () => { + it("match given periodic set", async () => { + let core = await createTestCore({ + doenetML: ` +

Offsets:

+

Period:

+ + + + + = + + + + + `, + }); + + await check_periodic_set({ core, name: "/s1" }); + + let period: number | string | (number | string)[] = "pi"; + let offsets: Offset[] = [ + ["/", "pi", 4], + ["/", ["*", 3, "pi"], 4], + ]; + + await check_periodic_set({ core, name: "/s2", offsets, period }); + + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/ans"].stateValues.creditAchieved).eq(0); + + // Type in an offset and submit + await updateMathInputValue({ latex: "-\\pi/4", name: "/o", core }); + await submitAnswer({ name: "/ans", core }); + await check_periodic_set({ core, name: "/s1" }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/ans"].stateValues.creditAchieved).eq(0); + + // Type in a period and submit + await updateMathInputValue({ latex: "\\pi/2", name: "/p", core }); + await submitAnswer({ name: "/ans", core }); + period = ["/", "pi", 2]; + offsets = [["-", ["/", "pi", 4]]]; + await check_periodic_set({ core, name: "/s1", offsets, period }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/ans"].stateValues.creditAchieved).eq(1); + + // Change period to be irrational factor of other period + await updateMathInputValue({ latex: "1", name: "/p", core }); + await submitAnswer({ name: "/ans", core }); + period = 1; + await check_periodic_set({ core, name: "/s1", offsets, period }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/ans"].stateValues.creditAchieved).eq(0); + + // Change period + await updateMathInputValue({ latex: "\\pi", name: "/p", core }); + await submitAnswer({ name: "/ans", core }); + period = "pi"; + await check_periodic_set({ core, name: "/s1", offsets, period }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/ans"].stateValues.creditAchieved).eq(0); + + // add offset + await updateMathInputValue({ + latex: "-\\pi/4, 5\\pi/4", + name: "/o", + core, + }); + await submitAnswer({ name: "/ans", core }); + offsets.push(["/", ["*", 5, "pi"], 4]); + await check_periodic_set({ core, name: "/s1", offsets, period }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/ans"].stateValues.creditAchieved).eq(1); + + // add redundant offset + await updateMathInputValue({ + latex: "-\\pi/4, 5\\pi/4, \\pi/4", + name: "/o", + core, + }); + await submitAnswer({ name: "/ans", core }); + offsets.push(["/", "pi", 4]); + await check_periodic_set({ + core, + name: "/s1", + offsets, + period, + redundantOffsets: true, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/ans"].stateValues.creditAchieved).eq(1); + + // add incorrect offset + await updateMathInputValue({ + latex: "-\\pi/4, 5\\pi/4, \\pi/4, \\pi/2", + name: "/o", + core, + }); + await submitAnswer({ name: "/ans", core }); + offsets.push(["/", "pi", 2]); + await check_periodic_set({ + core, + name: "/s1", + offsets, + period, + redundantOffsets: true, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/ans"].stateValues.creditAchieved).eq(0); + + // add invalid math + await updateMathInputValue({ + latex: "-\\pi/4, 5\\pi/4, \\pi/4, \\pi/2, (", + name: "/o", + core, + }); + await submitAnswer({ name: "/ans", core }); + await check_periodic_set({ core, name: "/s1" }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/ans"].stateValues.creditAchieved).eq(0); + }); + + it("match copied periodic sets", async () => { + let core = await createTestCore({ + doenetML: ` +

Offsets:

+

Period:

+ +

Offsets 2:

+

Period 2:

+ + + + + + + $a{name="a2"} = $b{name="b2"} + + + +

Redundancies: $a.redundantOffsets, $b.redundantOffsets, $a2.redundantOffsets, $b2.redundantOffsets

+ `, + }); + + await submitAnswer({ name: "/ans", core }); + + await check_periodic_set({ core, name: "/a" }); + await check_periodic_set({ core, name: "/b" }); + await check_periodic_set({ core, name: "/a2" }); + await check_periodic_set({ core, name: "/b2" }); + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/ans"].stateValues.creditAchieved).eq(0); + expect(stateVariables["/pR"].stateValues.text).eq( + "Redundancies: false, false, false, false", + ); + + // Submit offset for both + + await updateMathInputValue({ + latex: "-\\pi/4", + name: "/offsets", + core, + }); + await updateMathInputValue({ + latex: "-\\pi/4", + name: "/offsets2", + core, + }); + let offsets1: Offset[] = [["-", ["/", "pi", 4]]]; + let offsets2: Offset[] = [["-", ["/", "pi", 4]]]; + await submitAnswer({ name: "/ans", core }); + await check_periodic_set({ core, name: "/a" }); + await check_periodic_set({ core, name: "/b" }); + await check_periodic_set({ core, name: "/a2" }); + await check_periodic_set({ core, name: "/b2" }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/ans"].stateValues.creditAchieved).eq(0); + expect(stateVariables["/pR"].stateValues.text).eq( + "Redundancies: false, false, false, false", + ); + + // Submit periods for both + await updateMathInputValue({ + latex: "\\pi/2", + name: "/period", + core, + }); + await updateMathInputValue({ + latex: "2\\pi", + name: "/period2", + core, + }); + let period1: Period = ["/", "pi", 2]; + let period2: Period = ["*", 2, "pi"]; + await submitAnswer({ name: "/ans", core }); + await check_periodic_set({ + core, + name: "/a", + offsets: offsets1, + period: period1, + }); + await check_periodic_set({ + core, + name: "/b", + offsets: offsets2, + period: period2, + }); + await check_periodic_set({ + core, + name: "/a2", + offsets: offsets1, + period: period1, + }); + await check_periodic_set({ + core, + name: "/b2", + offsets: offsets2, + period: period2, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/ans"].stateValues.creditAchieved).eq(0); + expect(stateVariables["/pR"].stateValues.text).eq( + "Redundancies: false, false, false, false", + ); + + // Add offsets to match + await updateMathInputValue({ + latex: "-\\pi/4, \\pi/4, 11\\pi/4, -11\\pi/4", + name: "/offsets2", + core, + }); + offsets2.push( + ...[ + ["/", "pi", 4], + ["/", ["*", 11, "pi"], 4], + ["-", ["/", ["*", 11, "pi"], 4]], + ], + ); + await submitAnswer({ name: "/ans", core }); + await check_periodic_set({ + core, + name: "/a", + offsets: offsets1, + period: period1, + }); + await check_periodic_set({ + core, + name: "/b", + offsets: offsets2, + period: period2, + }); + await check_periodic_set({ + core, + name: "/a2", + offsets: offsets1, + period: period1, + }); + await check_periodic_set({ + core, + name: "/b2", + offsets: offsets2, + period: period2, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/ans"].stateValues.creditAchieved).eq(1); + expect(stateVariables["/pR"].stateValues.text).eq( + "Redundancies: false, false, false, false", + ); + + // Add extra offsets + await updateMathInputValue({ + latex: "-\\pi/4, -17\\pi/4", + name: "/offsets", + core, + }); + offsets1.push(["-", ["/", ["*", 17, "pi"], 4]]); + await submitAnswer({ name: "/ans", core }); + await check_periodic_set({ + core, + name: "/a", + offsets: offsets1, + period: period1, + redundantOffsets: true, + }); + await check_periodic_set({ + core, + name: "/b", + offsets: offsets2, + period: period2, + }); + await check_periodic_set({ + core, + name: "/a2", + offsets: offsets1, + period: period1, + redundantOffsets: true, + }); + await check_periodic_set({ + core, + name: "/b2", + offsets: offsets2, + period: period2, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/ans"].stateValues.creditAchieved).eq(1); + expect(stateVariables["/pR"].stateValues.text).eq( + "Redundancies: true, false, true, false", + ); + + // reduce period + await updateMathInputValue({ + latex: "\\pi", + name: "/period2", + core, + }); + period2 = "pi"; + await submitAnswer({ name: "/ans", core }); + await check_periodic_set({ + core, + name: "/a", + offsets: offsets1, + period: period1, + redundantOffsets: true, + }); + await check_periodic_set({ + core, + name: "/b", + offsets: offsets2, + period: period2, + redundantOffsets: true, + }); + await check_periodic_set({ + core, + name: "/a2", + offsets: offsets1, + period: period1, + redundantOffsets: true, + }); + await check_periodic_set({ + core, + name: "/b2", + offsets: offsets2, + period: period2, + redundantOffsets: true, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/ans"].stateValues.creditAchieved).eq(1); + expect(stateVariables["/pR"].stateValues.text).eq( + "Redundancies: true, true, true, true", + ); + + // add incorrect offset + await updateMathInputValue({ + latex: "-\\pi/4, \\pi/4, 11\\pi/4, -11\\pi/4, 0", + name: "/offsets2", + core, + }); + offsets2.push(0); + await submitAnswer({ name: "/ans", core }); + await check_periodic_set({ + core, + name: "/a", + offsets: offsets1, + period: period1, + redundantOffsets: true, + }); + await check_periodic_set({ + core, + name: "/b", + offsets: offsets2, + period: period2, + redundantOffsets: true, + }); + await check_periodic_set({ + core, + name: "/a2", + offsets: offsets1, + period: period1, + redundantOffsets: true, + }); + await check_periodic_set({ + core, + name: "/b2", + offsets: offsets2, + period: period2, + redundantOffsets: true, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/ans"].stateValues.creditAchieved).eq(0); + expect(stateVariables["/pR"].stateValues.text).eq( + "Redundancies: true, true, true, true", + ); + }); + + it("partial credit with periodic set", async () => { + let core = await createTestCore({ + doenetML: ` + + 30,150 + 2 + 180 + + +

What is the period? + + + + $period_input/$correct_period + + +

+ +

How many offsets? + + + + $number_offsets_input + and + $number_offsets_input >= $period/$correct_period*$n_correct_offsets + + +

+ +

Enter the offsets: + + + + + + +

+ + + + + + + + + + 0.8 + + + 1 + + + + + + + + $userPeriodicSet = $correct + + + + + +

Answer when penalizing redundant offsets: + + + + $userPeriodicSet = $correct + + + + $userPeriodicSet.redundantOffsets + + + $p$o + + +

+ + `, + }); + + // partially correct answer + await updateMathInputValue({ + latex: "360", + name: "/period_input", + core, + }); + await submitAnswer({ name: "/period", core }); + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/period"].stateValues.creditAchieved).eq(1); + + await updateMathInputValue({ + latex: "4", + name: "/number_offsets_input", + core, + }); + await submitAnswer({ name: "/number_offsets", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/number_offsets"].stateValues.creditAchieved).eq( + 1, + ); + + await updateMathInputValue({ latex: "30", name: "/mi1", core }); + await updateMathInputValue({ latex: "150", name: "/mi2", core }); + await updateMathInputValue({ latex: "210", name: "/mi3", core }); + await updateMathInputValue({ latex: "211", name: "/mi4", core }); + await submitAnswer({ name: "/answerNoPenalty", core }); + stateVariables = await returnAllStateVariables(core); + expect( + stateVariables["/answerNoPenalty"].stateValues.creditAchieved, + ).eq(0.75); + await submitAnswer({ name: "/answerPenalty", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/answerPenalty"].stateValues.creditAchieved).eq( + 0.75, + ); + + // correct answer + await updateMathInputValue({ latex: "-30", name: "/mi4", core }); + await submitAnswer({ name: "/answerNoPenalty", core }); + stateVariables = await returnAllStateVariables(core); + expect( + stateVariables["/answerNoPenalty"].stateValues.creditAchieved, + ).eq(1); + await submitAnswer({ name: "/answerPenalty", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/answerPenalty"].stateValues.creditAchieved).eq( + 1, + ); + + // add extraneous answer blanks + await updateMathInputValue({ + latex: "10", + name: "/number_offsets_input", + core, + }); + await submitAnswer({ name: "/number_offsets", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/number_offsets"].stateValues.creditAchieved).eq( + 1, + ); + + await submitAnswer({ name: "/answerNoPenalty", core }); + stateVariables = await returnAllStateVariables(core); + expect( + stateVariables["/answerNoPenalty"].stateValues.creditAchieved, + ).eq(0.4); + await submitAnswer({ name: "/answerPenalty", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/answerPenalty"].stateValues.creditAchieved).eq( + 0.4, + ); + + // add in a duplicate + await updateMathInputValue({ latex: "330", name: "/mi5", core }); + await submitAnswer({ name: "/answerNoPenalty", core }); + stateVariables = await returnAllStateVariables(core); + expect( + stateVariables["/answerNoPenalty"].stateValues.creditAchieved, + ).eq(0.5); + await submitAnswer({ name: "/answerPenalty", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/answerPenalty"].stateValues.creditAchieved).eq( + 0.4, + ); + + // fill in with duplicates + await updateMathInputValue({ latex: "330", name: "/mi6", core }); + await updateMathInputValue({ latex: "330", name: "/mi7", core }); + await updateMathInputValue({ latex: "330", name: "/mi8", core }); + await updateMathInputValue({ latex: "330", name: "/mi9", core }); + await updateMathInputValue({ latex: "330", name: "/mi10", core }); + await submitAnswer({ name: "/answerNoPenalty", core }); + stateVariables = await returnAllStateVariables(core); + expect( + stateVariables["/answerNoPenalty"].stateValues.creditAchieved, + ).eq(1); + await submitAnswer({ name: "/answerPenalty", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/answerPenalty"].stateValues.creditAchieved).eq( + 0.8, + ); + + // too few answer blanks + await updateMathInputValue({ + latex: "3", + name: "/number_offsets_input", + core, + }); + await submitAnswer({ name: "/number_offsets", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/number_offsets"].stateValues.creditAchieved).eq( + 0, + ); + + await submitAnswer({ name: "/answerNoPenalty", core }); + stateVariables = await returnAllStateVariables(core); + expect( + stateVariables["/answerNoPenalty"].stateValues.creditAchieved, + ).eq(0.75); + await submitAnswer({ name: "/answerPenalty", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/answerPenalty"].stateValues.creditAchieved).eq( + 0.75, + ); + + await updateMathInputValue({ latex: "100", name: "/mi3", core }); + await submitAnswer({ name: "/answerNoPenalty", core }); + stateVariables = await returnAllStateVariables(core); + expect( + stateVariables["/answerNoPenalty"].stateValues.creditAchieved, + ).eq(0.5); + await submitAnswer({ name: "/answerPenalty", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/answerPenalty"].stateValues.creditAchieved).eq( + 0.5, + ); + + // even fewer answer blanks + await updateMathInputValue({ + latex: "2", + name: "/number_offsets_input", + core, + }); + await submitAnswer({ name: "/number_offsets", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/number_offsets"].stateValues.creditAchieved).eq( + 0, + ); + + await updateMathInputValue({ latex: "100", name: "/mi3", core }); + await submitAnswer({ name: "/answerNoPenalty", core }); + stateVariables = await returnAllStateVariables(core); + expect( + stateVariables["/answerNoPenalty"].stateValues.creditAchieved, + ).eq(0.5); + await submitAnswer({ name: "/answerPenalty", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/answerPenalty"].stateValues.creditAchieved).eq( + 0.5, + ); + + // change period + await updateMathInputValue({ + latex: "180", + name: "/period_input", + core, + }); + await submitAnswer({ name: "/period", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/period"].stateValues.creditAchieved).eq(1); + + await submitAnswer({ name: "/number_offsets", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/number_offsets"].stateValues.creditAchieved).eq( + 1, + ); + + await submitAnswer({ name: "/answerNoPenalty", core }); + stateVariables = await returnAllStateVariables(core); + expect( + stateVariables["/answerNoPenalty"].stateValues.creditAchieved, + ).eq(1); + await submitAnswer({ name: "/answerPenalty", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/answerPenalty"].stateValues.creditAchieved).eq( + 1, + ); + + // additional answer blanks + await updateMathInputValue({ + latex: "3", + name: "/number_offsets_input", + core, + }); + await submitAnswer({ name: "/number_offsets", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/number_offsets"].stateValues.creditAchieved).eq( + 1, + ); + + await submitAnswer({ name: "/answerNoPenalty", core }); + stateVariables = await returnAllStateVariables(core); + expect( + stateVariables["/answerNoPenalty"].stateValues.creditAchieved, + ).eq(2 / 3); + await submitAnswer({ name: "/answerPenalty", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/answerPenalty"].stateValues.creditAchieved).eq( + 2 / 3, + ); + + await updateMathInputValue({ latex: "330", name: "/mi3", core }); + await submitAnswer({ name: "/answerNoPenalty", core }); + stateVariables = await returnAllStateVariables(core); + expect( + stateVariables["/answerNoPenalty"].stateValues.creditAchieved, + ).eq(1); + await submitAnswer({ name: "/answerPenalty", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/answerPenalty"].stateValues.creditAchieved).eq( + 0.8, + ); + + // change period + await updateMathInputValue({ + latex: "90", + name: "/period_input", + core, + }); + await submitAnswer({ name: "/period", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/period"].stateValues.creditAchieved).eq(0); + + await submitAnswer({ name: "/number_offsets", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/number_offsets"].stateValues.creditAchieved).eq( + 1, + ); + + await submitAnswer({ name: "/answerNoPenalty", core }); + stateVariables = await returnAllStateVariables(core); + expect( + stateVariables["/answerNoPenalty"].stateValues.creditAchieved, + ).eq(0.5); + await submitAnswer({ name: "/answerPenalty", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/answerPenalty"].stateValues.creditAchieved).eq( + 0.4, + ); + + await updateMathInputValue({ latex: "100", name: "/mi3", core }); + await submitAnswer({ name: "/answerNoPenalty", core }); + stateVariables = await returnAllStateVariables(core); + expect( + stateVariables["/answerNoPenalty"].stateValues.creditAchieved, + ).eq(1 / 3); + await submitAnswer({ name: "/answerPenalty", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/answerPenalty"].stateValues.creditAchieved).eq( + 1 / 3, + ); + + await updateMathInputValue({ latex: "150", name: "/mi3", core }); + await submitAnswer({ name: "/answerNoPenalty", core }); + stateVariables = await returnAllStateVariables(core); + expect( + stateVariables["/answerNoPenalty"].stateValues.creditAchieved, + ).eq(0.5); + await submitAnswer({ name: "/answerPenalty", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/answerPenalty"].stateValues.creditAchieved).eq( + 0.4, + ); + }); + + it("display periodic set as list", async () => { + let core = await createTestCore({ + doenetML: ` +

Period:

+

Offsets:

+ + + +

As list: $pset.asList{assignNames="l1"}

+ +

Min index: ,

+ + + +

As list with specified min/max: $pset2.asList{assignNames="l2"}

+ + `, + }); + + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/pL1"].stateValues.text).eq("As list: "); + expect(stateVariables["/pL2"].stateValues.text).eq( + "As list with specified min/max: ", + ); + + await updateMathInputValue({ latex: "7", name: "/period", core }); + await updateMathInputValue({ latex: "1", name: "/offsets", core }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/pL1"].stateValues.text).eq( + "As list: ..., -6, 1, 8, ...", + ); + expect(stateVariables["/pL2"].stateValues.text).eq( + "As list with specified min/max: ..., -6, 1, 8, ...", + ); + + await updateMathInputValue({ latex: "3", name: "/minIndex", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/pL1"].stateValues.text).eq( + "As list: ..., -6, 1, 8, ...", + ); + expect(stateVariables["/pL2"].stateValues.text).eq( + "As list with specified min/max: ..., ...", + ); + + await updateMathInputValue({ latex: "6", name: "/maxIndex", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/pL1"].stateValues.text).eq( + "As list: ..., -6, 1, 8, ...", + ); + expect(stateVariables["/pL2"].stateValues.text).eq( + "As list with specified min/max: ..., 22, 29, 36, 43, ...", + ); + + await updateMathInputValue({ latex: "1,3", name: "/offsets", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/pL1"].stateValues.text).eq( + "As list: ..., -6, -4, 1, 3, 8, 10, ...", + ); + expect(stateVariables["/pL2"].stateValues.text).eq( + "As list with specified min/max: ..., 22, 24, 29, 31, 36, 38, 43, 45, ...", + ); + + await updateMathInputValue({ latex: "3,1", name: "/offsets", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/pL1"].stateValues.text).eq( + "As list: ..., -6, -4, 1, 3, 8, 10, ...", + ); + expect(stateVariables["/pL2"].stateValues.text).eq( + "As list with specified min/max: ..., 22, 24, 29, 31, 36, 38, 43, 45, ...", + ); + + await updateMathInputValue({ latex: "3,1,8", name: "/offsets", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/pL1"].stateValues.text).eq( + "As list: ..., -6, -4, 1, 3, 8, 10, ...", + ); + expect(stateVariables["/pL2"].stateValues.text).eq( + "As list with specified min/max: ..., 22, 24, 29, 31, 36, 38, 43, 45, ...", + ); + + await updateMathInputValue({ + latex: "3,1,8,79", + name: "/offsets", + core, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/pL1"].stateValues.text).eq( + "As list: ..., -6, -5, -4, 1, 2, 3, 8, 9, 10, ...", + ); + expect(stateVariables["/pL2"].stateValues.text).eq( + "As list with specified min/max: ..., 22, 23, 24, 29, 30, 31, 36, 37, 38, 43, 44, 45, ...", + ); + }); +}); diff --git a/packages/doenetml-worker/src/test/tagSpecific/piecewisefunction.test.ts b/packages/doenetml-worker/src/test/tagSpecific/piecewisefunction.test.ts new file mode 100644 index 000000000..6f9dec69d --- /dev/null +++ b/packages/doenetml-worker/src/test/tagSpecific/piecewisefunction.test.ts @@ -0,0 +1,1290 @@ +import { describe, expect, it, vi } from "vitest"; +import { createTestCore, returnAllStateVariables } from "../utils/test-core"; +import Core from "../../Core"; + +const Mock = vi.fn(); +vi.stubGlobal("postMessage", Mock); +vi.mock("hyperformula"); + +describe("Piecewise Function Tag Tests", async () => { + async function check_heaviside(core: Core) { + const stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/mef"].stateValues.latex) + .eq(`f(x)= \\begin{cases} + 1 & \\text{if } x > 0\\\\ + 0 & \\text{otherwise} +\\end{cases}`); + expect(stateVariables["/mef2"].stateValues.latex) + .eq(`f_2(x)= \\begin{cases} + 0 & \\text{if } x \\le 0\\\\ + 1 & \\text{otherwise} +\\end{cases}`); + expect(stateVariables["/meg"].stateValues.latex) + .eq(`g(x)= \\begin{cases} + 1 & \\text{if } x \\ge 0\\\\ + 0 & \\text{otherwise} +\\end{cases}`); + expect(stateVariables["/meg2"].stateValues.latex) + .eq(`g_2(x)= \\begin{cases} + 0 & \\text{if } x < 0\\\\ + 1 & \\text{otherwise} +\\end{cases}`); + expect(stateVariables["/meh"].stateValues.latex) + .eq(`h(x)= \\begin{cases} + \\frac{1}{2} & \\text{if } x = 0\\\\ + 1 & \\text{if } x > 0\\\\ + 0 & \\text{otherwise} +\\end{cases}`); + expect(stateVariables["/meh2"].stateValues.latex) + .eq(`h_2(x)= \\begin{cases} + 1 & \\text{if } x > 0\\\\ + 0 & \\text{if } x < 0\\\\ + \\frac{1}{2} & \\text{otherwise} +\\end{cases}`); + + let fs = ["f", "f2"]; + let gs = ["g", "g2"]; + let hs = ["h", "h2"]; + + for (let fsym of fs) { + let f = stateVariables["/" + fsym].stateValues.numericalfs[0]; + expect(f(-2)).closeTo(0, 1e-12); + expect(f(-1)).closeTo(0, 1e-12); + expect(f(-1e-6)).closeTo(0, 1e-12); + expect(f(0)).closeTo(0, 1e-12); + expect(f(1e-6)).closeTo(1, 1e-12); + expect(f(1)).closeTo(1, 1e-12); + expect(f(2)).closeTo(1, 1e-12); + + expect(stateVariables["/" + fsym].stateValues.globalMinimum[1]).eq( + 0, + ); + expect(stateVariables["/" + fsym].stateValues.globalInfimum[1]).eq( + 0, + ); + expect(stateVariables["/" + fsym].stateValues.globalMaximum[1]).eq( + 1, + ); + expect(stateVariables["/" + fsym].stateValues.globalSupremum[1]).eq( + 1, + ); + } + + for (let gsym of gs) { + let g = stateVariables["/" + gsym].stateValues.numericalfs[0]; + expect(g(-2)).closeTo(0, 1e-12); + expect(g(-1)).closeTo(0, 1e-12); + expect(g(-1e-6)).closeTo(0, 1e-12); + expect(g(0)).closeTo(1, 1e-12); + expect(g(1e-6)).closeTo(1, 1e-12); + expect(g(1)).closeTo(1, 1e-12); + expect(g(2)).closeTo(1, 1e-12); + + expect(stateVariables["/" + gsym].stateValues.globalMinimum[1]).eq( + 0, + ); + expect(stateVariables["/" + gsym].stateValues.globalInfimum[1]).eq( + 0, + ); + expect(stateVariables["/" + gsym].stateValues.globalMaximum[1]).eq( + 1, + ); + expect(stateVariables["/" + gsym].stateValues.globalSupremum[1]).eq( + 1, + ); + } + + for (let hsym of hs) { + let h = stateVariables["/" + hsym].stateValues.numericalfs[0]; + expect(h(-2)).closeTo(0, 1e-12); + expect(h(-1)).closeTo(0, 1e-12); + expect(h(-1e-6)).closeTo(0, 1e-12); + expect(h(0)).closeTo(0.5, 1e-12); + expect(h(1e-6)).closeTo(1, 1e-12); + expect(h(1)).closeTo(1, 1e-12); + expect(h(2)).closeTo(1, 1e-12); + + expect(stateVariables["/" + hsym].stateValues.globalMinimum[1]).eq( + 0, + ); + expect(stateVariables["/" + hsym].stateValues.globalInfimum[1]).eq( + 0, + ); + expect(stateVariables["/" + hsym].stateValues.globalMaximum[1]).eq( + 1, + ); + expect(stateVariables["/" + hsym].stateValues.globalSupremum[1]).eq( + 1, + ); + } + } + + it("heaviside function", async () => { + let core = await createTestCore({ + doenetML: ` + + + 1 + 0 + + + 0 + 1 + + + 1 + 0 + + + 0 + 1 + + + 1/2 + 1 + 0 + + + 1 + 0 + 1/2 + + + f(x)=$f + f_2(x)=$f2 + g(x)=$g + g_2(x)=$g2 + h(x)=$h + h_2(x)=$h2 + `, + }); + + await check_heaviside(core); + }); + + it("heaviside function, ignore extra pieces", async () => { + let core = await createTestCore({ + doenetML: ` + + + 1 + 0 + x^2 + + + 0 + 1 + x^2 + + + 1 + 0 + x^2 + + + 0 + 1 + x^2 + + + 1/2 + 1 + 0 + x^2 + x^3 + + + 1 + 0 + 1/2 + x^2 + x^3 + + + f(x)=$f + f_2(x)=$f2 + g(x)=$g + g_2(x)=$g2 + h(x)=$h + h_2(x)=$h2 + `, + }); + + await check_heaviside(core); + }); + + it("different variables", async () => { + let core = await createTestCore({ + doenetML: ` + + + x + 2-y + + + q + 2-y + + + q + 2-y + + + q + 2-y + + + f(t)=$f + g(q)=$g + h(s)=$h + k(u)=$k + `, + }); + + const stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/mef"].stateValues.latex) + .eq(`f(t)= \\begin{cases} + t & \\text{if } t > 0\\\\ + 2 - t & \\text{if } t \\le 0 +\\end{cases}`); + expect(stateVariables["/meg"].stateValues.latex) + .eq(`g(q)= \\begin{cases} + q & \\text{if } q > 0\\\\ + 2 - q & \\text{if } q < 0 +\\end{cases}`); + expect(stateVariables["/meh"].stateValues.latex) + .eq(`h(s)= \\begin{cases} + s & \\text{if } 0 \\le s < 10\\\\ + 2 - s & \\text{if } -10 \\le s < 0 +\\end{cases}`); + expect(stateVariables["/mek"].stateValues.latex) + .eq(`k(u)= \\begin{cases} + u & \\text{if } u > 0\\\\ + 2 - u & \\text{if } u < 0 +\\end{cases}`); + + let f = stateVariables["/f"].stateValues.numericalfs[0]; + expect(f(-2)).closeTo(4, 1e-12); + expect(f(-1)).closeTo(3, 1e-12); + expect(f(-1e-6)).closeTo(2.000001, 1e-12); + expect(f(0)).closeTo(2, 1e-12); + expect(f(1e-6)).closeTo(1e-6, 1e-12); + expect(f(1)).closeTo(1, 1e-12); + expect(f(2)).closeTo(2, 1e-12); + + expect(stateVariables["/f"].stateValues.globalMinimum).eqls([]); + expect(stateVariables["/f"].stateValues.globalInfimum).eqls([0, 0]); + expect(stateVariables["/f"].stateValues.globalMaximum).eqls([]); + expect(stateVariables["/f"].stateValues.globalSupremum).eqls([ + -200, 202, + ]); + + let g = stateVariables["/g"].stateValues.numericalfs[0]; + expect(g(-2)).closeTo(4, 1e-12); + expect(g(-1)).closeTo(3, 1e-12); + expect(g(-1e-6)).closeTo(2.000001, 1e-12); + expect(g(0)).eqls(NaN); + expect(g(1e-6)).closeTo(1e-6, 1e-12); + expect(g(1)).closeTo(1, 1e-12); + expect(g(2)).closeTo(2, 1e-12); + + expect(stateVariables["/g"].stateValues.globalMinimum).eqls([]); + expect(stateVariables["/g"].stateValues.globalInfimum).eqls([0, 0]); + expect(stateVariables["/g"].stateValues.globalMaximum).eqls([]); + expect(stateVariables["/g"].stateValues.globalSupremum).eqls([ + -200, 202, + ]); + + let h = stateVariables["/h"].stateValues.numericalfs[0]; + expect(h(-10.000001)).eqls(NaN); + expect(h(-10)).closeTo(12, 1e-12); + expect(h(-2)).closeTo(4, 1e-12); + expect(h(-1)).closeTo(3, 1e-12); + expect(h(-1e-6)).closeTo(2.000001, 1e-12); + expect(h(0)).closeTo(0, 1e-12); + expect(h(1e-6)).closeTo(1e-6, 1e-12); + expect(h(1)).closeTo(1, 1e-12); + expect(h(2)).closeTo(2, 1e-12); + expect(h(9.99999)).closeTo(9.99999, 1e-12); + expect(h(10)).eqls(NaN); + + expect(stateVariables["/h"].stateValues.globalMinimum).eqls([0, 0]); + expect(stateVariables["/h"].stateValues.globalInfimum).eqls([0, 0]); + expect(stateVariables["/h"].stateValues.globalMaximum).eqls([-10, 12]); + expect(stateVariables["/h"].stateValues.globalSupremum).eqls([-10, 12]); + + let k = stateVariables["/k"].stateValues.numericalfs[0]; + expect(k(-10)).eqls(NaN); + expect(k(-9.99999)).closeTo(11.99999, 1e-12); + expect(k(-2)).closeTo(4, 1e-12); + expect(k(-1)).closeTo(3, 1e-12); + expect(k(-1e-6)).closeTo(2.000001, 1e-12); + expect(k(0)).eqls(NaN); + expect(k(1e-6)).closeTo(1e-6, 1e-12); + expect(k(1)).closeTo(1, 1e-12); + expect(k(2)).closeTo(2, 1e-12); + expect(k(10)).closeTo(10, 1e-12); + expect(k(10.0000001)).eqls(NaN); + + expect(stateVariables["/k"].stateValues.globalMinimum).eqls([]); + expect(stateVariables["/k"].stateValues.globalInfimum).eqls([0, 0]); + expect(stateVariables["/k"].stateValues.globalMaximum).eqls([]); + expect(stateVariables["/k"].stateValues.globalSupremum).eqls([-10, 12]); + }); + + it("extrema", async () => { + let core = await createTestCore({ + doenetML: ` + + + x^2 + -x^2 + + + x^2 + -x^2 + + + f(x)=$f + f_2(x)=$f2 +

Minima of f: $f.minima

+

Maxima of f: $f.maxima

+

Minima of f_2: $f2.minima

+

Maxima of f_2: $f2.maxima

+ + + + x^2 + -x^2 + + + x^2 + -x^2 + + + g(x)=$g + g_2(x)=$g2 +

Minima of g: $g.minima

+

Maxima of g: $g.maxima

+

Minima of g_2: $g2.minima

+

Maxima of g_2: $g2.maxima

+ + + + x^2 + -x^2 + + + x^2 + -x^2 + + + h(x)=$h + h_2(x)=$h2 +

Minima of h: $h.minima

+

Maxima of h: $h.maxima

+

Minima of h_2: $h2.minima

+

Maxima of h_2: $h2.maxima

+ + + + (x-2)^2 + (x+2)^2 + cos(pi*x/2) + + + (x-2)^2 + (x+2)^2 + cos(pi*x/2) + + + k(x)=$k + k_2(x)=$k2 +

Minima of k: $k.minima

+

Maxima of k: $k.maxima

+

Minima of k_2: $k2.minima

+

Maxima of k_2: $k2.maxima

+ `, + }); + + const stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/mef"].stateValues.latex) + .eq(`f(x)= \\begin{cases} + x^{2} & \\text{if } x > 0\\\\ + -x^{2} & \\text{otherwise} +\\end{cases}`); + expect(stateVariables["/mef2"].stateValues.latex) + .eq(`f_2(x)= \\begin{cases} + x^{2} & \\text{if } x \\ge 0\\\\ + -x^{2} & \\text{otherwise} +\\end{cases}`); + expect(stateVariables["/pfmin"].stateValues.text).eq("Minima of f: "); + expect(stateVariables["/pfmax"].stateValues.text).eq("Maxima of f: "); + expect(stateVariables["/pf2min"].stateValues.text).eq( + "Minima of f_2: ", + ); + expect(stateVariables["/pf2max"].stateValues.text).eq( + "Maxima of f_2: ", + ); + + expect(stateVariables["/f"].stateValues.minima).eqls([]); + expect(stateVariables["/f"].stateValues.maxima).eqls([]); + expect(stateVariables["/f2"].stateValues.minima).eqls([]); + expect(stateVariables["/f2"].stateValues.maxima).eqls([]); + expect(stateVariables["/f"].stateValues.globalMinimum).eqls([]); + expect(stateVariables["/f"].stateValues.globalInfimum).eqls([ + -200, + -(200 ** 2), + ]); + expect(stateVariables["/f"].stateValues.globalMaximum).eqls([]); + expect(stateVariables["/f"].stateValues.globalSupremum).eqls([ + 200, + 200 ** 2, + ]); + expect(stateVariables["/f2"].stateValues.globalMinimum).eqls([]); + expect(stateVariables["/f2"].stateValues.globalInfimum).eqls([ + -200, + -(200 ** 2), + ]); + expect(stateVariables["/f2"].stateValues.globalMaximum).eqls([]); + expect(stateVariables["/f2"].stateValues.globalSupremum).eqls([ + 200, + 200 ** 2, + ]); + + expect(stateVariables["/meg"].stateValues.latex) + .eq(`g(x)= \\begin{cases} + x^{2} & \\text{if } x > 0.1\\\\ + -x^{2} & \\text{otherwise} +\\end{cases}`); + expect(stateVariables["/meg2"].stateValues.latex) + .eq(`g_2(x)= \\begin{cases} + x^{2} & \\text{if } x \\ge 0.1\\\\ + -x^{2} & \\text{otherwise} +\\end{cases}`); + expect(stateVariables["/pgmin"].stateValues.text).eq( + "Minima of g: ( 0.1, -0.01 )", + ); + expect(stateVariables["/pgmax"].stateValues.text).eq( + "Maxima of g: ( 0, 0 )", + ); + expect(stateVariables["/pg2min"].stateValues.text).eq( + "Minima of g_2: ", + ); + expect(stateVariables["/pg2max"].stateValues.text).eq( + "Maxima of g_2: ( 0, 0 )", + ); + + expect(stateVariables["/g"].stateValues.minima.length).eq(1); + expect(stateVariables["/g"].stateValues.minima[0][0]).closeTo( + 0.1, + 1e-14, + ); + expect(stateVariables["/g"].stateValues.minima[0][1]).closeTo( + -0.01, + 1e-14, + ); + expect(stateVariables["/g"].stateValues.maxima.length).eq(1); + expect(stateVariables["/g"].stateValues.maxima[0][0]).closeTo(0, 1e-14); + expect(stateVariables["/g"].stateValues.maxima[0][1]).closeTo(0, 1e-14); + expect(stateVariables["/g2"].stateValues.minima).eqls([]); + expect(stateVariables["/g2"].stateValues.maxima.length).eq(1); + expect(stateVariables["/g2"].stateValues.maxima[0][0]).closeTo( + 0, + 1e-14, + ); + expect(stateVariables["/g2"].stateValues.maxima[0][1]).closeTo( + 0, + 1e-14, + ); + expect(stateVariables["/g"].stateValues.globalMinimum).eqls([]); + expect(stateVariables["/g"].stateValues.globalInfimum).eqls([ + -199.9, + -(199.9 ** 2), + ]); + expect(stateVariables["/g"].stateValues.globalMaximum).eqls([]); + expect(stateVariables["/g"].stateValues.globalSupremum).eqls([ + 200.1, + 200.1 ** 2, + ]); + expect(stateVariables["/g2"].stateValues.globalMinimum).eqls([]); + expect(stateVariables["/g2"].stateValues.globalInfimum).eqls([ + -199.9, + -(199.9 ** 2), + ]); + expect(stateVariables["/g2"].stateValues.globalMaximum).eqls([]); + expect(stateVariables["/g2"].stateValues.globalSupremum).eqls([ + 200.1, + 200.1 ** 2, + ]); + + expect(stateVariables["/meh"].stateValues.latex) + .eq(`h(x)= \\begin{cases} + x^{2} & \\text{if } x > -0.1\\\\ + -x^{2} & \\text{otherwise} +\\end{cases}`); + expect(stateVariables["/meh2"].stateValues.latex) + .eq(`h_2(x)= \\begin{cases} + x^{2} & \\text{if } x \\ge -0.1\\\\ + -x^{2} & \\text{otherwise} +\\end{cases}`); + expect(stateVariables["/phmin"].stateValues.text).eq( + "Minima of h: ( 0, 0 )", + ); + expect(stateVariables["/phmax"].stateValues.text).eq("Maxima of h: "); + expect(stateVariables["/ph2min"].stateValues.text).eq( + "Minima of h_2: ( 0, 0 )", + ); + expect(stateVariables["/ph2max"].stateValues.text).eq( + "Maxima of h_2: ( -0.1, 0.01 )", + ); + + expect(stateVariables["/h"].stateValues.minima.length).eq(1); + expect(stateVariables["/h"].stateValues.minima[0][0]).closeTo(0, 1e-14); + expect(stateVariables["/h"].stateValues.minima[0][1]).closeTo(0, 1e-14); + expect(stateVariables["/h"].stateValues.maxima).eqls([]); + expect(stateVariables["/h2"].stateValues.minima.length).eq(1); + expect(stateVariables["/h2"].stateValues.minima[0][0]).closeTo( + 0, + 1e-14, + ); + expect(stateVariables["/h2"].stateValues.minima[0][1]).closeTo( + 0, + 1e-14, + ); + expect(stateVariables["/h2"].stateValues.maxima.length).eq(1); + expect(stateVariables["/h2"].stateValues.maxima[0][0]).closeTo( + -0.1, + 1e-14, + ); + expect(stateVariables["/h2"].stateValues.maxima[0][1]).closeTo( + 0.01, + 1e-14, + ); + expect(stateVariables["/h"].stateValues.globalMinimum).eqls([]); + expect(stateVariables["/h"].stateValues.globalInfimum).eqls([ + -200.1, + -(200.1 ** 2), + ]); + expect(stateVariables["/h"].stateValues.globalMaximum).eqls([]); + expect(stateVariables["/h"].stateValues.globalSupremum).eqls([ + 199.9, + 199.9 ** 2, + ]); + expect(stateVariables["/h2"].stateValues.globalMinimum).eqls([]); + expect(stateVariables["/h2"].stateValues.globalInfimum).eqls([ + -200.1, + -(200.1 ** 2), + ]); + expect(stateVariables["/h2"].stateValues.globalMaximum).eqls([]); + expect(stateVariables["/h2"].stateValues.globalSupremum).eqls([ + 199.9, + 199.9 ** 2, + ]); + + expect(stateVariables["/mek"].stateValues.latex) + .eq(`k(x)= \\begin{cases} + \\left(x - 2\\right)^{2} & \\text{if } x > 1\\\\ + \\left(x + 2\\right)^{2} & \\text{if } x < -1\\\\ + \\cos\\left(\\frac{\\pi x}{2}\\right) & \\text{otherwise} +\\end{cases}`); + expect(stateVariables["/mek2"].stateValues.latex) + .eq(`k_2(x)= \\begin{cases} + \\left(x - 2\\right)^{2} & \\text{if } x \\ge 1\\\\ + \\left(x + 2\\right)^{2} & \\text{if } x \\le -1\\\\ + \\cos\\left(\\frac{\\pi x}{2}\\right) & \\text{otherwise} +\\end{cases}`); + expect(stateVariables["/pkmin"].stateValues.text).eq( + "Minima of k: ( -2, 0 ), ( -1, 0 ), ( 1, 0 ), ( 2, 0 )", + ); + expect(stateVariables["/pkmax"].stateValues.text).eq( + "Maxima of k: ( 0, 1 )", + ); + expect(stateVariables["/pk2min"].stateValues.text).eq( + "Minima of k_2: ( -2, 0 ), ( 2, 0 )", + ); + expect(stateVariables["/pk2max"].stateValues.text).eq( + "Maxima of k_2: ( -1, 1 ), ( 0, 1 ), ( 1, 1 )", + ); + + expect(stateVariables["/k"].stateValues.minima.length).eq(4); + expect(stateVariables["/k"].stateValues.minima[0][0]).closeTo( + -2, + 1e-14, + ); + expect(stateVariables["/k"].stateValues.minima[0][1]).closeTo(0, 1e-14); + expect(stateVariables["/k"].stateValues.minima[1][0]).closeTo( + -1, + 1e-14, + ); + expect(stateVariables["/k"].stateValues.minima[1][1]).closeTo(0, 1e-14); + expect(stateVariables["/k"].stateValues.minima[2][0]).closeTo(1, 1e-14); + expect(stateVariables["/k"].stateValues.minima[2][1]).closeTo(0, 1e-14); + expect(stateVariables["/k"].stateValues.minima[3][0]).closeTo(2, 1e-14); + expect(stateVariables["/k"].stateValues.minima[3][1]).closeTo(0, 1e-14); + + expect(stateVariables["/k"].stateValues.maxima.length).eq(1); + expect(stateVariables["/k"].stateValues.maxima[0][0]).closeTo(0, 1e-14); + expect(stateVariables["/k"].stateValues.maxima[0][1]).closeTo(1, 1e-14); + + expect(stateVariables["/k2"].stateValues.minima.length).eq(2); + expect(stateVariables["/k2"].stateValues.minima[0][0]).closeTo( + -2, + 1e-14, + ); + expect(stateVariables["/k2"].stateValues.minima[0][1]).closeTo( + 0, + 1e-14, + ); + expect(stateVariables["/k2"].stateValues.minima[1][0]).closeTo( + 2, + 1e-14, + ); + expect(stateVariables["/k2"].stateValues.minima[1][1]).closeTo( + 0, + 1e-14, + ); + + expect(stateVariables["/k2"].stateValues.maxima.length).eq(3); + expect(stateVariables["/k2"].stateValues.maxima[0][0]).closeTo( + -1, + 1e-14, + ); + expect(stateVariables["/k2"].stateValues.maxima[0][1]).closeTo( + 1, + 1e-14, + ); + expect(stateVariables["/k2"].stateValues.maxima[1][0]).closeTo( + 0, + 1e-14, + ); + expect(stateVariables["/k2"].stateValues.maxima[1][1]).closeTo( + 1, + 1e-14, + ); + expect(stateVariables["/k2"].stateValues.maxima[2][0]).closeTo( + 1, + 1e-14, + ); + expect(stateVariables["/k2"].stateValues.maxima[2][1]).closeTo( + 1, + 1e-14, + ); + + expect(stateVariables["/k"].stateValues.globalMinimum).eqls([2, 0]); + expect(stateVariables["/k"].stateValues.globalInfimum).eqls([2, 0]); + expect(stateVariables["/k"].stateValues.globalMaximum).eqls([]); + expect(stateVariables["/k"].stateValues.globalSupremum).eqls([ + 201, + 199 ** 2, + ]); + expect(stateVariables["/k2"].stateValues.globalMinimum).eqls([2, 0]); + expect(stateVariables["/k2"].stateValues.globalInfimum).eqls([2, 0]); + expect(stateVariables["/k2"].stateValues.globalMaximum).eqls([]); + expect(stateVariables["/k2"].stateValues.globalSupremum).eqls([ + 201, + 199 ** 2, + ]); + }); + + it("extrema 2, overlap in domains", async () => { + let core = await createTestCore({ + doenetML: ` + + + x^2 + (x-1)^2+1 + (x+1)^2+1 + + + x^2 + (x-1)^2+1 + (x+1)^2+1 + + + f(x)=$f + f_2(x)=$f2 +

Minima of f: $f.minima

+

Maxima of f: $f.maxima

+

Minima of f_2: $f2.minima

+

Maxima of f_2: $f2.maxima

+ + + + x^2 + (x-2)^2+1 + (x+2)^2+1 + + + x^2 + (x-2)^2+1 + (x+2)^2+1 + + + g(x)=$g + g_2(x)=$g2 +

Minima of g: $g.minima

+

Maxima of g: $g.maxima

+

Minima of g_2: $g2.minima

+

Maxima of g_2: $g2.maxima

+ + + + + + x^2 + x^4/4-2x^2 + abs(x) + + + x^2 + x^4/4-2x^2 + abs(x) + + + h(x)=$h + h_2(x)=$h2 +

Minima of h: $h.minima

+

Maxima of h: $h.maxima

+

Minima of h_2: $h2.minima

+

Maxima of h_2: $h2.maxima

+ + + + x^2 + x^4/4-2x^2+4 + abs(x) + + + x^2 + x^4/4-2x^2+4 + abs(x) + + + k(x)=$k + k_2(x)=$k2 +

Minima of k: $k.minima

+

Maxima of k: $k.maxima

+

Minima of k_2: $k2.minima

+

Maxima of k_2: $k2.maxima

+ `, + }); + + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/f"].stateValues.minima).eqls([[0, 0]]); + expect(stateVariables["/f"].stateValues.maxima).eqls([]); + expect(stateVariables["/f2"].stateValues.minima).eqls([[0, 0]]); + expect(stateVariables["/f2"].stateValues.maxima).eqls([]); + + expect(stateVariables["/f"].stateValues.globalMinimum).eqls([0, 0]); + expect(stateVariables["/f"].stateValues.globalInfimum).eqls([0, 0]); + expect(stateVariables["/f"].stateValues.globalMaximum).eqls([]); + expect(stateVariables["/f"].stateValues.globalSupremum).eqls([ + 201, + 200 ** 2 + 1, + ]); + expect(stateVariables["/f2"].stateValues.globalMinimum).eqls([0, 0]); + expect(stateVariables["/f2"].stateValues.globalInfimum).eqls([0, 0]); + expect(stateVariables["/f2"].stateValues.globalMaximum).eqls([]); + expect(stateVariables["/f2"].stateValues.globalSupremum).eqls([ + 201, + 200 ** 2 + 1, + ]); + + expect(stateVariables["/g"].stateValues.minima.length).eq(3); + expect(stateVariables["/g"].stateValues.minima[0][0]).closeTo( + -2, + 1e-14, + ); + expect(stateVariables["/g"].stateValues.minima[0][1]).closeTo(1, 1e-14); + expect(stateVariables["/g"].stateValues.minima[1][0]).closeTo(0, 1e-14); + expect(stateVariables["/g"].stateValues.minima[1][1]).closeTo(0, 1e-14); + expect(stateVariables["/g"].stateValues.minima[2][0]).closeTo(2, 1e-14); + expect(stateVariables["/g"].stateValues.minima[2][1]).closeTo(1, 1e-14); + expect(stateVariables["/g"].stateValues.maxima.length).eq(2); + expect(stateVariables["/g"].stateValues.maxima[0][0]).closeTo( + -1, + 1e-14, + ); + expect(stateVariables["/g"].stateValues.maxima[0][1]).closeTo(2, 1e-14); + expect(stateVariables["/g"].stateValues.maxima[1][0]).closeTo(1, 1e-14); + expect(stateVariables["/g"].stateValues.maxima[1][1]).closeTo(2, 1e-14); + expect(stateVariables["/g2"].stateValues.minima.length).eq(3); + expect(stateVariables["/g2"].stateValues.minima[0][0]).closeTo( + -2, + 1e-14, + ); + expect(stateVariables["/g2"].stateValues.minima[0][1]).closeTo( + 1, + 1e-14, + ); + expect(stateVariables["/g2"].stateValues.minima[1][0]).closeTo( + 0, + 1e-14, + ); + expect(stateVariables["/g2"].stateValues.minima[1][1]).closeTo( + 0, + 1e-14, + ); + expect(stateVariables["/g2"].stateValues.minima[2][0]).closeTo( + 2, + 1e-14, + ); + expect(stateVariables["/g2"].stateValues.minima[2][1]).closeTo( + 1, + 1e-14, + ); + expect(stateVariables["/g2"].stateValues.maxima).eqls([]); + + expect(stateVariables["/g"].stateValues.globalMinimum).eqls([0, 0]); + expect(stateVariables["/g"].stateValues.globalInfimum).eqls([0, 0]); + expect(stateVariables["/g"].stateValues.globalMaximum).eqls([]); + expect(stateVariables["/g"].stateValues.globalSupremum).eqls([ + 201, + 199 ** 2 + 1, + ]); + expect(stateVariables["/g2"].stateValues.globalMinimum).eqls([0, 0]); + expect(stateVariables["/g2"].stateValues.globalInfimum).eqls([0, 0]); + expect(stateVariables["/g2"].stateValues.globalMaximum).eqls([]); + expect(stateVariables["/g2"].stateValues.globalSupremum).eqls([ + 201, + 199 ** 2 + 1, + ]); + + expect(stateVariables["/h"].stateValues.minima.length).eq(3); + expect(stateVariables["/h"].stateValues.minima[0][0]).closeTo(-2, 1e-8); + expect(stateVariables["/h"].stateValues.minima[0][1]).closeTo(-4, 1e-8); + expect(stateVariables["/h"].stateValues.minima[1][0]).closeTo(0, 1e-8); + expect(stateVariables["/h"].stateValues.minima[1][1]).closeTo(0, 1e-8); + expect(stateVariables["/h"].stateValues.minima[2][0]).closeTo(2, 1e-8); + expect(stateVariables["/h"].stateValues.minima[2][1]).closeTo(-4, 1e-8); + expect(stateVariables["/h"].stateValues.maxima).eqls([]); + expect(stateVariables["/h2"].stateValues.minima.length).eq(3); + expect(stateVariables["/h2"].stateValues.minima[0][0]).closeTo( + -2, + 1e-8, + ); + expect(stateVariables["/h2"].stateValues.minima[0][1]).closeTo( + -4, + 1e-8, + ); + expect(stateVariables["/h2"].stateValues.minima[1][0]).closeTo(0, 1e-8); + expect(stateVariables["/h2"].stateValues.minima[1][1]).closeTo(0, 1e-8); + expect(stateVariables["/h2"].stateValues.minima[2][0]).closeTo(2, 1e-8); + expect(stateVariables["/h2"].stateValues.minima[2][1]).closeTo( + -4, + 1e-8, + ); + expect(stateVariables["/h2"].stateValues.maxima.length).eq(2); + expect(stateVariables["/h2"].stateValues.maxima[0][0]).closeTo( + -1, + 1e-8, + ); + expect(stateVariables["/h2"].stateValues.maxima[0][1]).closeTo(1, 1e-8); + expect(stateVariables["/h2"].stateValues.maxima[1][0]).closeTo(1, 1e-8); + expect(stateVariables["/h2"].stateValues.maxima[1][1]).closeTo(1, 1e-8); + + expect(stateVariables["/h"].stateValues.globalMinimum).eqls([-2, -4]); + expect(stateVariables["/h"].stateValues.globalInfimum).eqls([-2, -4]); + expect(stateVariables["/h"].stateValues.globalMaximum).eqls([]); + expect(stateVariables["/h"].stateValues.globalSupremum).eqls([ + -203, 203, + ]); + expect(stateVariables["/h2"].stateValues.globalMinimum).eqls([-2, -4]); + expect(stateVariables["/h2"].stateValues.globalInfimum).eqls([-2, -4]); + expect(stateVariables["/h2"].stateValues.globalMaximum).eqls([]); + expect(stateVariables["/h2"].stateValues.globalSupremum).eqls([ + -203, 203, + ]); + + expect(stateVariables["/k"].stateValues.minima.length).eq(5); + expect(stateVariables["/k"].stateValues.minima[0][0]).closeTo(-3, 1e-8); + expect(stateVariables["/k"].stateValues.minima[0][1]).closeTo(3, 1e-8); + expect(stateVariables["/k"].stateValues.minima[1][0]).closeTo(-2, 1e-8); + expect(stateVariables["/k"].stateValues.minima[1][1]).closeTo(0, 1e-8); + expect(stateVariables["/k"].stateValues.minima[2][0]).closeTo(0, 1e-8); + expect(stateVariables["/k"].stateValues.minima[2][1]).closeTo(0, 1e-8); + expect(stateVariables["/k"].stateValues.minima[3][0]).closeTo(2, 1e-8); + expect(stateVariables["/k"].stateValues.minima[3][1]).closeTo(0, 1e-8); + expect(stateVariables["/k"].stateValues.minima[4][0]).closeTo(3, 1e-8); + expect(stateVariables["/k"].stateValues.minima[4][1]).closeTo(3, 1e-8); + expect(stateVariables["/k"].stateValues.maxima.length).eq(2); + expect(stateVariables["/k"].stateValues.maxima[0][0]).closeTo(-1, 1e-8); + expect(stateVariables["/k"].stateValues.maxima[0][1]).closeTo( + 2.25, + 1e-8, + ); + expect(stateVariables["/k"].stateValues.maxima[1][0]).closeTo(1, 1e-8); + expect(stateVariables["/k"].stateValues.maxima[1][1]).closeTo( + 2.25, + 1e-8, + ); + expect(stateVariables["/k2"].stateValues.minima.length).eq(3); + expect(stateVariables["/k2"].stateValues.minima[0][0]).closeTo( + -2, + 1e-8, + ); + expect(stateVariables["/k2"].stateValues.minima[0][1]).closeTo(0, 1e-8); + expect(stateVariables["/k2"].stateValues.minima[1][0]).closeTo(0, 1e-8); + expect(stateVariables["/k2"].stateValues.minima[1][1]).closeTo(0, 1e-8); + expect(stateVariables["/k2"].stateValues.minima[2][0]).closeTo(2, 1e-8); + expect(stateVariables["/k2"].stateValues.minima[2][1]).closeTo(0, 1e-8); + expect(stateVariables["/k2"].stateValues.maxima.length).eq(2); + expect(stateVariables["/k2"].stateValues.maxima[0][0]).closeTo( + -3, + 1e-8, + ); + expect(stateVariables["/k2"].stateValues.maxima[0][1]).closeTo( + 6.25, + 1e-8, + ); + expect(stateVariables["/k2"].stateValues.maxima[1][0]).closeTo(3, 1e-8); + expect(stateVariables["/k2"].stateValues.maxima[1][1]).closeTo( + 6.25, + 1e-8, + ); + + expect(stateVariables["/k"].stateValues.globalMinimum).eqls([0, 0]); + expect(stateVariables["/k"].stateValues.globalInfimum).eqls([0, 0]); + expect(stateVariables["/k"].stateValues.globalMaximum).eqls([]); + expect(stateVariables["/k"].stateValues.globalSupremum).eqls([ + -203, 203, + ]); + expect(stateVariables["/k2"].stateValues.globalMinimum).eqls([0, 0]); + expect(stateVariables["/k2"].stateValues.globalInfimum).eqls([0, 0]); + expect(stateVariables["/k2"].stateValues.globalMaximum).eqls([]); + expect(stateVariables["/k2"].stateValues.globalSupremum).eqls([ + -203, 203, + ]); + }); + + it("ignore function pieces with non-numerical domain when evaluating numerically", async () => { + let core = await createTestCore({ + doenetML: ` + + + 1 + x + x^2/10 + x^3/100 + x^4/1000 + x^5/10000 + + + f(x)=$f +

$$f(7)

+

$$f(8)

+

$$f(9)

+

$$f(10)

+

$$f(11)

+ `, + }); + + const stateVariables = await returnAllStateVariables(core); + + expect(stateVariables["/mef"].stateValues.latex) + .eq(`f(x)= \\begin{cases} + 1 & \\text{if } x = a\\\\ + x & \\text{if } {s} < x < t\\\\ + \\frac{x^{2}}{10} & \\text{if } {1} \\le x < q\\\\ + \\frac{x^{3}}{100} & \\text{if } {z} < x < 5\\\\ + \\frac{x^{4}}{1000} & \\text{if } 8 \\le x \\le 10 +\\end{cases}`); + + expect(stateVariables["/p7"].stateValues.text).eq("NaN"); + expect(stateVariables["/p8"].stateValues.text).eq("4.1"); + expect(stateVariables["/p9"].stateValues.text).eq("6.56"); + expect(stateVariables["/p10"].stateValues.text).eq("10"); + expect(stateVariables["/p11"].stateValues.text).eq("NaN"); + + let f = stateVariables["/f"].stateValues.numericalfs[0]; + for (let i = -10; i <= 15; i++) { + if (i >= 8 && i <= 10) { + expect(f(i)).closeTo(i ** 4 / 1000, 1e-14); + } else { + expect(f(i)).eqls(NaN); + } + } + + expect(stateVariables["/f"].stateValues.globalMinimum).eqls([8, f(8)]); + expect(stateVariables["/f"].stateValues.globalInfimum).eqls([8, f(8)]); + expect(stateVariables["/f"].stateValues.globalMaximum).eqls([ + 10, + f(10), + ]); + expect(stateVariables["/f"].stateValues.globalSupremum).eqls([ + 10, + f(10), + ]); + }); + + it("use single point notation", async () => { + let core = await createTestCore({ + doenetML: ` + + + x + x^2/10 + x^3/100 + + $f.extrema + $f.globalMaximum{styleNumber="2"} + $f.globalInfimum{styleNumber="3"} + + f(x)=$f + `, + }); + + const stateVariables = await returnAllStateVariables(core); + + expect(stateVariables["/mef"].stateValues.latex) + .eq(`f(x)= \\begin{cases} + x & \\text{if } x = 1\\\\ + \\frac{x^{2}}{10} & \\text{if } x = 2\\\\ + \\frac{x^{3}}{100} & \\text{if } 1 < x < 2 \\text{ or }2 < x < 3 +\\end{cases}`); + + let f = stateVariables["/f"].stateValues.numericalfs[0]; + expect(f(0)).eqls(NaN); + expect(f(0.999)).eqls(NaN); + expect(f(1)).eq(1); + expect(f(1.0001)).closeTo(1.0001 ** 3 / 100, 1e-14); + expect(f(1.9999)).closeTo(1.9999 ** 3 / 100, 1e-14); + expect(f(2)).eq(4 / 10); + expect(f(2.0001)).closeTo(2.0001 ** 3 / 100, 1e-14); + expect(f(2.9999)).closeTo(2.9999 ** 3 / 100, 1e-14); + expect(f(3)).eqls(NaN); + expect(f(4)).eqls(NaN); + + expect(stateVariables["/f"].stateValues.maxima).eqls([[2, 4 / 10]]); + expect(stateVariables["/f"].stateValues.minima).eqls([]); + expect(stateVariables["/f"].stateValues.globalMaximum).eqls([1, 1]); + expect(stateVariables["/f"].stateValues.globalSupremum).eqls([1, 1]); + expect(stateVariables["/f"].stateValues.globalMinimum).eqls([]); + expect(stateVariables["/f"].stateValues.globalInfimum).eqls([ + 1, + 1 / 100, + ]); + }); + + it("global extrema, find single points from gaps", async () => { + let core = await createTestCore({ + doenetML: ` + + + 3 + 1/x-1 + 1/x-1 + -2-(x+4)^2 + + $f.extrema + $f.globalMaximum{styleNumber="2"} + $f.globalInfimum{styleNumber="3"} + + f(x)=$f + `, + }); + + const stateVariables = await returnAllStateVariables(core); + + expect(stateVariables["/mef"].stateValues.latex) + .eq(`f(x)= \\begin{cases} + 3 & \\text{if } -1 < x < 0.1\\\\ + \\frac{1}{x} - 1 & \\text{if } 3 < x < 4 \\text{ or }4 < x < 5\\\\ + -2 - \\left(x + 4\\right)^{2} & \\text{if } -6 < x \\le -1 \\text{ or }0.1 \\le x \\le 3 \\text{ or }x = 4 \\text{ or }x \\ge 5 +\\end{cases}`); + + let f = stateVariables["/f"].stateValues.numericalfs[0]; + expect(f(-5)).eqls(NaN); + expect(f(-4.999)).closeTo(-2 - 0.999 ** 2, 1e-14); + expect(f(-1)).eq(-2 - 3 ** 2); + expect(f(-0.999)).eq(3); + expect(f(0.0999)).eq(3); + expect(f(0.1)).eq(-2 - 4.1 ** 2); + expect(f(3)).eq(-2 - 7 ** 2); + expect(f(3.001)).eq(1 / 3.001 - 1); + expect(f(3.999)).eq(1 / 3.999 - 1); + expect(f(4)).eq(-2 - 8 ** 2); + expect(f(4.001)).eq(1 / 4.001 - 1); + expect(f(4.999)).eq(1 / 4.999 - 1); + expect(f(5)).eqls(NaN); + + expect(stateVariables["/f"].stateValues.maxima).eqls([[-4, -2]]); + expect(stateVariables["/f"].stateValues.minima).eqls([ + [-1, -2 - 3 ** 2], + [3, -2 - 7 ** 2], + [4, -2 - 8 ** 2], + ]); + expect(stateVariables["/f"].stateValues.globalMaximum[0]).within( + -1, + -0.9, + ); + expect(stateVariables["/f"].stateValues.globalMaximum[1]).eq(3); + expect(stateVariables["/f"].stateValues.globalSupremum).eqls([-1, 3]); + expect(stateVariables["/f"].stateValues.globalMinimum).eqls([ + 4, + -2 - 8 ** 2, + ]); + expect(stateVariables["/f"].stateValues.globalInfimum).eqls([ + 4, + -2 - 8 ** 2, + ]); + }); + + it("latex combines pieces", async () => { + let core = await createTestCore({ + doenetML: ` + + + x + x + x + x^2 + x^2 + x^2 + x^2 + x + + + f(x)=$f + + `, + }); + + const stateVariables = await returnAllStateVariables(core); + + expect(stateVariables["/mef"].stateValues.latex) + .eq(`f(x)= \\begin{cases} + x & \\text{if } {s} < x < t \\text{ or } 1 < x < 4\\\\ + x^{2} & \\text{if } {1} \\le x < q \\text{ or } {b} \\le x < 1 \\text{ or } 4 \\le x < 6\\\\ + x & \\text{if } 8 < x < 9 +\\end{cases}`); + }); + + it("extrema of a function with piecewise function child", async () => { + let core = await createTestCore({ + doenetML: ` + + + + x^2 + 1-x^2/4 + cos(pi x) + + + $f.extrema + $f.globalSupremum{styleNumber="2"} + $f.globalMaximum{styleNumber="3"} + $f.globalInfimum{styleNumber="4"} + $f.globalMinimum{styleNumber="5"} + + f(x)=$f + `, + }); + + const stateVariables = await returnAllStateVariables(core); + + expect(stateVariables["/mef"].stateValues.latex) + .eq(`f(x)= \\begin{cases} + x^{2} & \\text{if } -1 < x \\le 1\\\\ + 1 - \\frac{x^{2}}{4} & \\text{if } -4 < x \\le -1 \\text{ or }1 < x \\le 4\\\\ + \\cos\\left(\\pi x\\right) & \\text{otherwise} +\\end{cases}`); + + expect(stateVariables["/f"].stateValues.maxima).eqls([ + [-4, 1], + [1, 1], + [6, 1], + [8, 1], + ]); + expect(stateVariables["/f"].stateValues.minima).eqls([ + [0, 0], + [4, -3], + [5, -1], + [7, -1], + ]); + expect(stateVariables["/f"].stateValues.globalMaximum).eqls([1, 1]); + expect(stateVariables["/f"].stateValues.globalSupremum).eqls([-1, 1]); + expect(stateVariables["/f"].stateValues.globalMinimum).eqls([4, -3]); + expect(stateVariables["/f"].stateValues.globalInfimum).eqls([-4, -3]); + }); + + it("extrema of piecewise functions with piecewise function children", async () => { + let core = await createTestCore({ + doenetML: ` + + + 0 + x^2 + + $f1.extrema + + + + + + 1/x + + $f2.extrema + + + + + 9*e^(-(x-3)) + 8-x + $f1 + + $f3.extrema + + `, + }); + + let stateVariables = await returnAllStateVariables(core); + + let f2 = stateVariables["/f2"].stateValues.numericalfs[0]; + + expect(f2(-4)).eq(-1 / 4); + expect(f2(-3)).eq(-1 / 3); + expect(f2(-2)).eq(0); + expect(f2(-1)).eq(0); + expect(f2(0)).eq(0); + expect(f2(1)).eq(1); + expect(f2(2)).eq(4); + expect(f2(3)).eq(1 / 3); + expect(f2(4)).eq(1 / 4); + + expect(stateVariables["/f2"].stateValues.maxima).eqls([[2, 4]]); + expect(stateVariables["/f2"].stateValues.minima).eqls([[-3, -1 / 3]]); + expect(stateVariables["/f2"].stateValues.globalMaximum).eqls([2, 4]); + expect(stateVariables["/f2"].stateValues.globalSupremum).eqls([2, 4]); + expect(stateVariables["/f2"].stateValues.globalMinimum).eqls([ + -3, + -1 / 3, + ]); + expect(stateVariables["/f2"].stateValues.globalInfimum).eqls([ + -3, + -1 / 3, + ]); + + let f3 = stateVariables["/f3"].stateValues.numericalfs[0]; + + expect(f3(-4)).eq(0); + expect(f3(0)).eq(0); + expect(f3(0.5)).eq(0.25); + expect(f3(1)).eq(7); + expect(f3(1.5)).eq(6.5); + expect(f3(2)).eq(4); + expect(f3(3)).eq(9); + expect(f3(4)).eq(9 * Math.exp(-1)); + + expect(stateVariables["/f3"].stateValues.maxima).eqls([ + [1, 7], + [3, 9], + ]); + expect(stateVariables["/f3"].stateValues.minima).eqls([[2, 4]]); + expect(stateVariables["/f3"].stateValues.globalMaximum).eqls([3, 9]); + expect(stateVariables["/f3"].stateValues.globalSupremum).eqls([3, 9]); + expect(stateVariables["/f3"].stateValues.globalMinimum[1]).eq(0); + expect(stateVariables["/f3"].stateValues.globalInfimum[1]).eq(0); + }); +}); diff --git a/packages/doenetml-worker/src/test/tagSpecific/pluralize.test.ts b/packages/doenetml-worker/src/test/tagSpecific/pluralize.test.ts new file mode 100644 index 000000000..e900caa8f --- /dev/null +++ b/packages/doenetml-worker/src/test/tagSpecific/pluralize.test.ts @@ -0,0 +1,246 @@ +import { describe, expect, it, vi } from "vitest"; +import { createTestCore, returnAllStateVariables } from "../utils/test-core"; +import { updateTextInputValue } from "../utils/actions"; + +const Mock = vi.fn(); +vi.stubGlobal("postMessage", Mock); +vi.mock("hyperformula"); + +describe("Pluralize tag tests", async () => { + it("number followed by noun", async () => { + let core = await createTestCore({ + doenetML: ` + +

one dog

+

two dog

+

zero dog

+

1 mouse

+

2 mouse

+

0 mouse

+

one thousand bus

+

0.5 bus

+

1 bus

+ `, + }); + + const stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p1"].stateValues.text).eq("one dog"); + expect(stateVariables["/p2"].stateValues.text).eq("two dogs"); + expect(stateVariables["/p3"].stateValues.text).eq("zero dogs"); + expect(stateVariables["/p4"].stateValues.text).eq("1 mouse"); + expect(stateVariables["/p5"].stateValues.text).eq("2 mice"); + expect(stateVariables["/p6"].stateValues.text).eq("0 mice"); + expect(stateVariables["/p7"].stateValues.text).eq("one thousand buses"); + expect(stateVariables["/p8"].stateValues.text).eq("0.5 buses"); + expect(stateVariables["/p9"].stateValues.text).eq("1 bus"); + }); + + it("single word", async () => { + let core = await createTestCore({ + doenetML: ` + +

dog

+

mouse

+

bus

+

goose

+

pony

+

only

+ `, + }); + + const stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p1"].stateValues.text).eq("dogs"); + expect(stateVariables["/p2"].stateValues.text).eq("mice"); + expect(stateVariables["/p3"].stateValues.text).eq("buses"); + expect(stateVariables["/p4"].stateValues.text).eq("geese"); + expect(stateVariables["/p5"].stateValues.text).eq("ponies"); + expect(stateVariables["/p6"].stateValues.text).eq("only"); + }); + + it("number followed by noun with plural form", async () => { + let core = await createTestCore({ + doenetML: ` + +

one dog

+

two dog

+

zero dog

+

1 mouse

+

2 mouse

+

0 mouse

+

one thousand bus

+

0.5 bus

+

1 bus

+ `, + }); + + const stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p1"].stateValues.text).eq("one dog"); + expect(stateVariables["/p2"].stateValues.text).eq("two cheetahs"); + expect(stateVariables["/p3"].stateValues.text).eq("zero cheetahs"); + expect(stateVariables["/p4"].stateValues.text).eq("1 mouse"); + expect(stateVariables["/p5"].stateValues.text).eq("2 cheetahs"); + expect(stateVariables["/p6"].stateValues.text).eq("0 cheetahs"); + expect(stateVariables["/p7"].stateValues.text).eq( + "one thousand cheetahs", + ); + expect(stateVariables["/p8"].stateValues.text).eq("0.5 cheetahs"); + expect(stateVariables["/p9"].stateValues.text).eq("1 bus"); + }); + + it("single word, with plural form", async () => { + let core = await createTestCore({ + doenetML: ` + +

dog

+

mouse

+

bus

+

goose

+

pony

+

only

+ `, + }); + + const stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p1"].stateValues.text).eq("cheetahs"); + expect(stateVariables["/p2"].stateValues.text).eq("cheetahs"); + expect(stateVariables["/p3"].stateValues.text).eq("cheetahs"); + expect(stateVariables["/p4"].stateValues.text).eq("cheetahs"); + expect(stateVariables["/p5"].stateValues.text).eq("cheetahs"); + expect(stateVariables["/p6"].stateValues.text).eq("cheetahs"); + }); + + it("number followed by noun, based on number", async () => { + let core = await createTestCore({ + doenetML: ` + +

one dog

+

two dog

+

zero dog

+

1 mouse

+

2 mouse

+

0 mouse

+

one thousand bus

+

0.5 bus

+

1 bus

+ `, + }); + + const stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p1"].stateValues.text).eq("one dogs"); + expect(stateVariables["/p2"].stateValues.text).eq("two dog"); + expect(stateVariables["/p3"].stateValues.text).eq("zero dogs"); + expect(stateVariables["/p4"].stateValues.text).eq("1 mice"); + expect(stateVariables["/p5"].stateValues.text).eq("2 mice"); + expect(stateVariables["/p6"].stateValues.text).eq("0 mouse"); + expect(stateVariables["/p7"].stateValues.text).eq("one thousand bus"); + expect(stateVariables["/p8"].stateValues.text).eq("0.5 buses"); + expect(stateVariables["/p9"].stateValues.text).eq("1 buses"); + }); + + it("single word, based on number", async () => { + let core = await createTestCore({ + doenetML: ` + +

dog

+

dog

+

mouse

+

mouse

+

bus

+

bus

+

goose

+

goose

+

pony

+

pony

+

only

+

only

+ `, + }); + + const stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p1"].stateValues.text).eq("dogs"); + expect(stateVariables["/p2"].stateValues.text).eq("dog"); + expect(stateVariables["/p3"].stateValues.text).eq("mice"); + expect(stateVariables["/p4"].stateValues.text).eq("mouse"); + expect(stateVariables["/p5"].stateValues.text).eq("buses"); + expect(stateVariables["/p6"].stateValues.text).eq("bus"); + expect(stateVariables["/p7"].stateValues.text).eq("geese"); + expect(stateVariables["/p8"].stateValues.text).eq("goose"); + expect(stateVariables["/p9"].stateValues.text).eq("ponies"); + expect(stateVariables["/p10"].stateValues.text).eq("pony"); + expect(stateVariables["/p11"].stateValues.text).eq("only"); + expect(stateVariables["/p12"].stateValues.text).eq("only"); + }); + + it("phrases", async () => { + let core = await createTestCore({ + doenetML: ` +

one dog three cat two squirrel or 1 cat plus 7 goose

+

one hundred green plane flew through one big sky, rather than six shiny sky

+ `, + }); + + const stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p1"].stateValues.text).eq( + "one dog three cats two squirrels or 1 cat plus 7 geese", + ); + expect(stateVariables["/p2"].stateValues.text).eq( + "one hundred green planes flew through one big sky, rather than six shiny skies", + ); + }); + + it("dynamic", async () => { + let core = await createTestCore({ + doenetML: ` +

How many geese?

+

How many teeth?

+ +

I have $nGeese.value goose even if one doesn't have $nTeeth.value tooth

+ `, + }); + + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p3"].stateValues.text).eq( + "I have 1 goose even if one doesn't have 1 tooth", + ); + + await updateTextInputValue({ name: "/nGeese", text: "three", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p3"].stateValues.text).eq( + "I have three geese even if one doesn't have 1 tooth", + ); + + await updateTextInputValue({ name: "/nTeeth", text: "0", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p3"].stateValues.text).eq( + "I have three geese even if one doesn't have 0 teeth", + ); + + await updateTextInputValue({ name: "/nTeeth", text: "one", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p3"].stateValues.text).eq( + "I have three geese even if one doesn't have one tooth", + ); + + await updateTextInputValue({ + name: "/nTeeth", + text: "one thousand", + core, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p3"].stateValues.text).eq( + "I have three geese even if one doesn't have one thousand teeth", + ); + + await updateTextInputValue({ name: "/nGeese", text: "-1", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p3"].stateValues.text).eq( + "I have -1 geese even if one doesn't have one thousand teeth", + ); + + await updateTextInputValue({ name: "/nGeese", text: "-2", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p3"].stateValues.text).eq( + "I have -2 geese even if one doesn't have one thousand teeth", + ); + }); +}); diff --git a/packages/doenetml-worker/src/test/tagSpecific/polygon.test.ts b/packages/doenetml-worker/src/test/tagSpecific/polygon.test.ts index f8781583a..b89b7e72e 100644 --- a/packages/doenetml-worker/src/test/tagSpecific/polygon.test.ts +++ b/packages/doenetml-worker/src/test/tagSpecific/polygon.test.ts @@ -1,13 +1,17 @@ import { describe, expect, it, vi } from "vitest"; import { createTestCore, returnAllStateVariables } from "../utils/test-core"; import { + movePoint, + movePolygon, updateBooleanInputValue, updateMathInputValue, } from "../utils/actions"; import me from "math-expressions"; +import Core from "../../Core"; const Mock = vi.fn(); vi.stubGlobal("postMessage", Mock); +vi.mock("hyperformula"); async function testPolygonCopiedTwice({ core, @@ -17,7 +21,7 @@ async function testPolygonCopiedTwice({ graph2Name = "/g2", graph3Name = "/g3", }: { - core: any; + core: Core; vertices: (number | string)[][]; polygonName?: string; graph1Name?: string; @@ -159,13 +163,10 @@ describe("Polygon tag tests", async () => { // move individual vertex vertices[1] = [4, 7]; - await core.requestAction({ - actionName: "movePolygon", - componentName: "/g1/pg", - args: { - pointCoords: { 1: vertices[1] }, - }, - event: null, + await movePolygon({ + name: "/g1/pg", + pointCoords: { 1: vertices[1] }, + core, }); await testPolygonCopiedTwice({ core, vertices }); @@ -179,27 +180,17 @@ describe("Polygon tag tests", async () => { vertices[i][1] = vertices[i][1] + moveY; } - await core.requestAction({ - actionName: "movePolygon", - componentName: "/g2/pg", - args: { - pointCoords: vertices, - }, - event: null, - }); + await movePolygon({ name: "/g2/pg", pointCoords: vertices, core }); await testPolygonCopiedTwice({ core, vertices }); // move double copied individual vertex vertices[2] = [-9, -8]; - await core.requestAction({ - actionName: "movePolygon", - componentName: "/g3/pg", - args: { - pointCoords: { 2: vertices[2] }, - }, - event: null, + await movePolygon({ + name: "/g3/pg", + pointCoords: { 2: vertices[2] }, + core, }); await testPolygonCopiedTwice({ core, vertices }); @@ -231,13 +222,10 @@ describe("Polygon tag tests", async () => { // move individual vertex vertices[1] = [4, 7]; - await core.requestAction({ - actionName: "movePolygon", - componentName: "/g1/pg", - args: { - pointCoords: { 1: vertices[1] }, - }, - event: null, + await movePolygon({ + name: "/g1/pg", + pointCoords: { 1: vertices[1] }, + core, }); await testPolygonCopiedTwice({ core, vertices }); @@ -251,27 +239,17 @@ describe("Polygon tag tests", async () => { vertices[i][1] = vertices[i][1] + moveY; } - await core.requestAction({ - actionName: "movePolygon", - componentName: "/g2/pg", - args: { - pointCoords: vertices, - }, - event: null, - }); + await movePolygon({ name: "/g2/pg", pointCoords: vertices, core }); await testPolygonCopiedTwice({ core, vertices }); // move double copied individual vertex vertices[2] = [-9, -8]; - await core.requestAction({ - actionName: "movePolygon", - componentName: "/g3/pg", - args: { - pointCoords: { 2: vertices[2] }, - }, - event: null, + await movePolygon({ + name: "/g3/pg", + pointCoords: { 2: vertices[2] }, + core, }); await testPolygonCopiedTwice({ core, vertices }); @@ -300,7 +278,7 @@ describe("Polygon tag tests", async () => { await updateMathInputValue({ latex: "1", - componentName: "/length", + name: "/length", core, }); vertices[0] = [0, 5 * Math.sin(0)]; @@ -308,7 +286,7 @@ describe("Polygon tag tests", async () => { await updateMathInputValue({ latex: "2", - componentName: "/length", + name: "/length", core, }); vertices[1] = [1, 5 * Math.sin(1)]; @@ -316,7 +294,7 @@ describe("Polygon tag tests", async () => { await updateMathInputValue({ latex: "3", - componentName: "/length", + name: "/length", core, }); vertices[2] = [2, 5 * Math.sin(2)]; @@ -324,7 +302,7 @@ describe("Polygon tag tests", async () => { await updateMathInputValue({ latex: "2", - componentName: "/length", + name: "/length", core, }); vertices.splice(2, 1); @@ -332,7 +310,7 @@ describe("Polygon tag tests", async () => { await updateMathInputValue({ latex: "0", - componentName: "/length", + name: "/length", core, }); vertices = []; @@ -340,7 +318,7 @@ describe("Polygon tag tests", async () => { await updateMathInputValue({ latex: "5", - componentName: "/length", + name: "/length", core, }); for (let i = 0; i < 5; i++) { @@ -372,7 +350,7 @@ describe("Polygon tag tests", async () => { await updateMathInputValue({ latex: "10", - componentName: "/length", + name: "/length", core, }); for (let i = 0; i < 10; i++) { @@ -382,7 +360,7 @@ describe("Polygon tag tests", async () => { await updateMathInputValue({ latex: "1", - componentName: "/length", + name: "/length", core, }); vertices = [[0, 5 * Math.sin(0)]]; @@ -415,7 +393,7 @@ describe("Polygon tag tests", async () => { await updateMathInputValue({ latex: "-2", - componentName: "/mi", + name: "/mi", core, }); @@ -447,18 +425,8 @@ describe("Polygon tag tests", async () => { await testPolygonCopiedTwice({ core, vertices }); // can't move points - await core.requestAction({ - actionName: "movePoint", - componentName: "/g1/mp1", - args: { x: 9, y: -8 }, - event: null, - }); - await core.requestAction({ - actionName: "movePoint", - componentName: "/g1/mp9", - args: { x: -8, y: 4 }, - event: null, - }); + await movePoint({ name: "/g1/mp1", x: 9, y: -8, core }); + await movePoint({ name: "/g1/mp9", x: -8, y: 4, core }); // can't move polygon1 let moveX = 3; @@ -466,14 +434,7 @@ describe("Polygon tag tests", async () => { let vertices2 = vertices.map((v) => [v[0] + moveX, v[1] + moveY]); - await core.requestAction({ - actionName: "movePolygon", - componentName: "/g1/pg", - args: { - pointCoords: vertices2, - }, - event: null, - }); + await movePolygon({ name: "/g1/pg", pointCoords: vertices2, core }); await testPolygonCopiedTwice({ core, vertices }); @@ -483,14 +444,7 @@ describe("Polygon tag tests", async () => { vertices2 = vertices.map((v) => [v[0] + moveX, v[1] + moveY]); - await core.requestAction({ - actionName: "movePolygon", - componentName: "/g2/pg", - args: { - pointCoords: vertices2, - }, - event: null, - }); + await movePolygon({ name: "/g2/pg", pointCoords: vertices2, core }); await testPolygonCopiedTwice({ core, vertices }); @@ -500,14 +454,7 @@ describe("Polygon tag tests", async () => { vertices2 = vertices.map((v) => [v[0] + moveX, v[1] + moveY]); - await core.requestAction({ - actionName: "movePolygon", - componentName: "/g3/pg", - args: { - pointCoords: vertices2, - }, - event: null, - }); + await movePolygon({ name: "/g3/pg", pointCoords: vertices2, core }); await testPolygonCopiedTwice({ core, vertices }); }); @@ -540,17 +487,17 @@ describe("Polygon tag tests", async () => { vertices[0] = [9, -8]; vertices[8] = [-8, 4]; - await core.requestAction({ - actionName: "movePoint", - componentName: "/g1/mp1", - args: { x: vertices[0][0], y: vertices[0][1] }, - event: null, + await movePoint({ + name: "/g1/mp1", + x: vertices[0][0], + y: vertices[0][1], + core, }); - await core.requestAction({ - actionName: "movePoint", - componentName: "/g1/mp9", - args: { x: vertices[8][0], y: vertices[8][1] }, - event: null, + await movePoint({ + name: "/g1/mp9", + x: vertices[8][0], + y: vertices[8][1], + core, }); await testPolygonCopiedTwice({ core, vertices }); @@ -564,14 +511,7 @@ describe("Polygon tag tests", async () => { vertices[i][1] += moveY; } - await core.requestAction({ - actionName: "movePolygon", - componentName: "/g1/pg", - args: { - pointCoords: vertices, - }, - event: null, - }); + await movePolygon({ name: "/g1/pg", pointCoords: vertices, core }); await testPolygonCopiedTwice({ core, vertices }); @@ -584,14 +524,7 @@ describe("Polygon tag tests", async () => { vertices[i][1] += moveY; } - await core.requestAction({ - actionName: "movePolygon", - componentName: "/g2/pg", - args: { - pointCoords: vertices, - }, - event: null, - }); + await movePolygon({ name: "/g2/pg", pointCoords: vertices, core }); await testPolygonCopiedTwice({ core, vertices }); @@ -603,14 +536,7 @@ describe("Polygon tag tests", async () => { vertices[i][1] += moveY; } - await core.requestAction({ - actionName: "movePolygon", - componentName: "/g2/pg", - args: { - pointCoords: vertices, - }, - event: null, - }); + await movePolygon({ name: "/g2/pg", pointCoords: vertices, core }); await testPolygonCopiedTwice({ core, vertices }); }); @@ -665,11 +591,11 @@ describe("Polygon tag tests", async () => { ]; for (let i = 0; i < 4; i++) { - await core.requestAction({ - actionName: "movePoint", - componentName: `/v${i + 1}`, - args: { x: ps[i][0], y: ps[i][1] }, - event: null, + await movePoint({ + name: `/v${i + 1}`, + x: ps[i][0], + y: ps[i][1], + core, }); } @@ -698,11 +624,11 @@ describe("Polygon tag tests", async () => { ]; for (let i = 0; i < 4; i++) { - await core.requestAction({ - actionName: "movePoint", - componentName: `/v${i + 1}a`, - args: { x: ps[i][0], y: ps[i][1] }, - event: null, + await movePoint({ + name: `/v${i + 1}a`, + x: ps[i][0], + y: ps[i][1], + core, }); } @@ -754,14 +680,7 @@ describe("Polygon tag tests", async () => { vertices[i][1] += moveY; } - await core.requestAction({ - actionName: "movePolygon", - componentName: "/g1/pg", - args: { - pointCoords: vertices, - }, - event: null, - }); + await movePolygon({ name: "/g1/pg", pointCoords: vertices, core }); await testPolygonCopiedTwice({ core, vertices }); @@ -774,14 +693,7 @@ describe("Polygon tag tests", async () => { vertices[i][1] += moveY; } - await core.requestAction({ - actionName: "movePolygon", - componentName: "/g2/pg", - args: { - pointCoords: vertices, - }, - event: null, - }); + await movePolygon({ name: "/g2/pg", pointCoords: vertices, core }); await testPolygonCopiedTwice({ core, vertices }); @@ -794,14 +706,7 @@ describe("Polygon tag tests", async () => { vertices[i][1] += moveY; } - await core.requestAction({ - actionName: "movePolygon", - componentName: "/g3/pg", - args: { - pointCoords: vertices, - }, - event: null, - }); + await movePolygon({ name: "/g3/pg", pointCoords: vertices, core }); await testPolygonCopiedTwice({ core, vertices }); }); @@ -920,14 +825,7 @@ describe("Polygon tag tests", async () => { [6, 3], ]; - await core.requestAction({ - actionName: "movePolygon", - componentName: "/_polygon1", - args: { - pointCoords: vertices, - }, - event: null, - }); + await movePolygon({ name: "/_polygon1", pointCoords: vertices, core }); await testPolygons({ vertices, transX, transY }); @@ -939,14 +837,7 @@ describe("Polygon tag tests", async () => { [2, -1], ]; - await core.requestAction({ - actionName: "movePolygon", - componentName: "/_polygon2", - args: { - pointCoords: vertices2, - }, - event: null, - }); + await movePolygon({ name: "/_polygon2", pointCoords: vertices2, core }); vertices = vertices2.map((v) => [v[0] - transX, v[1] - transY]); @@ -955,12 +846,12 @@ describe("Polygon tag tests", async () => { // change translation await updateMathInputValue({ latex: "2", - componentName: "/transx", + name: "/transx", core, }); await updateMathInputValue({ latex: "10", - componentName: "/transy", + name: "/transy", core, }); @@ -1010,13 +901,10 @@ describe("Polygon tag tests", async () => { A = [-4, -1]; D = [A[0] + C[0] - B[0], A[1] + C[1] - B[1]]; - await core.requestAction({ - actionName: "movePolygon", - componentName: "/parallelogram", - args: { - pointCoords: { 0: A }, - }, - event: null, + await movePolygon({ + name: "/parallelogram", + pointCoords: { 0: A }, + core, }); stateVariables = await returnAllStateVariables(core); @@ -1044,13 +932,10 @@ describe("Polygon tag tests", async () => { B = [8, 9]; D = [A[0] + C[0] - B[0], A[1] + C[1] - B[1]]; - await core.requestAction({ - actionName: "movePolygon", - componentName: "/parallelogram", - args: { - pointCoords: { 1: B }, - }, - event: null, + await movePolygon({ + name: "/parallelogram", + pointCoords: { 1: B }, + core, }); stateVariables = await returnAllStateVariables(core); @@ -1079,13 +964,10 @@ describe("Polygon tag tests", async () => { C = [-3, 7]; D = [A[0] + C[0] - B[0], A[1] + C[1] - B[1]]; - await core.requestAction({ - actionName: "movePolygon", - componentName: "/parallelogram", - args: { - pointCoords: { 2: C }, - }, - event: null, + await movePolygon({ + name: "/parallelogram", + pointCoords: { 2: C }, + core, }); stateVariables = await returnAllStateVariables(core); @@ -1114,13 +996,10 @@ describe("Polygon tag tests", async () => { D = [7, 0]; B = [A[0] + C[0] - D[0], A[1] + C[1] - D[1]]; - await core.requestAction({ - actionName: "movePolygon", - componentName: "/parallelogram", - args: { - pointCoords: { 3: D }, - }, - event: null, + await movePolygon({ + name: "/parallelogram", + pointCoords: { 3: D }, + core, }); stateVariables = await returnAllStateVariables(core); @@ -1246,14 +1125,7 @@ describe("Polygon tag tests", async () => { [-4, -3], ]; - await core.requestAction({ - actionName: "movePolygon", - componentName: "/_polygon1", - args: { - pointCoords: vertices, - }, - event: null, - }); + await movePolygon({ name: "/_polygon1", pointCoords: vertices, core }); await testPolygons({ vertices }); @@ -1265,14 +1137,7 @@ describe("Polygon tag tests", async () => { [-7, 6], ]; - await core.requestAction({ - actionName: "movePolygon", - componentName: "/_polygon2", - args: { - pointCoords: vertices2, - }, - event: null, - }); + await movePolygon({ name: "/_polygon2", pointCoords: vertices2, core }); vertices = [...vertices2]; vertices[1] = [vertices[1][1], vertices[1][0]]; @@ -1322,14 +1187,7 @@ describe("Polygon tag tests", async () => { A = [-4, -1]; D = [C[0] + B[0] - A[0], C[1] + B[1] - A[1]]; - await core.requestAction({ - actionName: "movePolygon", - componentName: "/_polygon1", - args: { - pointCoords: { 0: A }, - }, - event: null, - }); + await movePolygon({ name: "/_polygon1", pointCoords: { 0: A }, core }); stateVariables = await returnAllStateVariables(core); expect( @@ -1357,14 +1215,7 @@ describe("Polygon tag tests", async () => { B = [8, 9]; D = [C[0] + B[0] - A[0], C[1] + B[1] - A[1]]; - await core.requestAction({ - actionName: "movePolygon", - componentName: "/_polygon1", - args: { - pointCoords: { 1: B }, - }, - event: null, - }); + await movePolygon({ name: "/_polygon1", pointCoords: { 1: B }, core }); stateVariables = await returnAllStateVariables(core); expect( @@ -1392,14 +1243,7 @@ describe("Polygon tag tests", async () => { C = [-3, 7]; D = [C[0] + B[0] - A[0], C[1] + B[1] - A[1]]; - await core.requestAction({ - actionName: "movePolygon", - componentName: "/_polygon1", - args: { - pointCoords: { 2: C }, - }, - event: null, - }); + await movePolygon({ name: "/_polygon1", pointCoords: { 2: C }, core }); stateVariables = await returnAllStateVariables(core); expect( @@ -1427,14 +1271,7 @@ describe("Polygon tag tests", async () => { D = [7, 0]; A = [C[0] + B[0] - D[0], C[1] + B[1] - D[1]]; - await core.requestAction({ - actionName: "movePolygon", - componentName: "/_polygon1", - args: { - pointCoords: { 3: D }, - }, - event: null, - }); + await movePolygon({ name: "/_polygon1", pointCoords: { 3: D }, core }); stateVariables = await returnAllStateVariables(core); expect( @@ -1498,14 +1335,7 @@ describe("Polygon tag tests", async () => { // move first vertex A = [-4, -1]; - await core.requestAction({ - actionName: "movePolygon", - componentName: "/_polygon1", - args: { - pointCoords: { 0: A }, - }, - event: null, - }); + await movePolygon({ name: "/_polygon1", pointCoords: { 0: A }, core }); stateVariables = await returnAllStateVariables(core); expect( @@ -1532,14 +1362,7 @@ describe("Polygon tag tests", async () => { // move second vertex B = [8, 9]; - await core.requestAction({ - actionName: "movePolygon", - componentName: "/_polygon1", - args: { - pointCoords: { 1: B }, - }, - event: null, - }); + await movePolygon({ name: "/_polygon1", pointCoords: { 1: B }, core }); stateVariables = await returnAllStateVariables(core); expect( @@ -1566,14 +1389,7 @@ describe("Polygon tag tests", async () => { // move third vertex C = [-3, 7]; - await core.requestAction({ - actionName: "movePolygon", - componentName: "/_polygon1", - args: { - pointCoords: { 2: C }, - }, - event: null, - }); + await movePolygon({ name: "/_polygon1", pointCoords: { 2: C }, core }); stateVariables = await returnAllStateVariables(core); expect( @@ -1599,14 +1415,7 @@ describe("Polygon tag tests", async () => { // move fourth vertex A = [7, 0]; - await core.requestAction({ - actionName: "movePolygon", - componentName: "/_polygon1", - args: { - pointCoords: { 3: A }, - }, - event: null, - }); + await movePolygon({ name: "/_polygon1", pointCoords: { 3: A }, core }); stateVariables = await returnAllStateVariables(core); expect( @@ -1671,14 +1480,7 @@ describe("Polygon tag tests", async () => { // move first vertex A = [-4, -1]; - await core.requestAction({ - actionName: "movePolygon", - componentName: "/_polygon1", - args: { - pointCoords: { 0: A }, - }, - event: null, - }); + await movePolygon({ name: "/_polygon1", pointCoords: { 0: A }, core }); stateVariables = await returnAllStateVariables(core); expect( @@ -1705,14 +1507,7 @@ describe("Polygon tag tests", async () => { // move second vertex B = [8, 9]; - await core.requestAction({ - actionName: "movePolygon", - componentName: "/_polygon1", - args: { - pointCoords: { 1: B }, - }, - event: null, - }); + await movePolygon({ name: "/_polygon1", pointCoords: { 1: B }, core }); stateVariables = await returnAllStateVariables(core); expect( @@ -1739,14 +1534,7 @@ describe("Polygon tag tests", async () => { // move third vertex C = [-3, 7]; - await core.requestAction({ - actionName: "movePolygon", - componentName: "/_polygon1", - args: { - pointCoords: { 2: C }, - }, - event: null, - }); + await movePolygon({ name: "/_polygon1", pointCoords: { 2: C }, core }); stateVariables = await returnAllStateVariables(core); expect( @@ -1773,14 +1561,7 @@ describe("Polygon tag tests", async () => { // move fourth vertex A = [7, 0]; - await core.requestAction({ - actionName: "movePolygon", - componentName: "/_polygon1", - args: { - pointCoords: { 3: A }, - }, - event: null, - }); + await movePolygon({ name: "/_polygon1", pointCoords: { 3: A }, core }); stateVariables = await returnAllStateVariables(core); expect( @@ -1853,14 +1634,7 @@ describe("Polygon tag tests", async () => { A = [-4, -1]; D[0] = A[0] + 1; - await core.requestAction({ - actionName: "movePolygon", - componentName: "/_polygon1", - args: { - pointCoords: { 0: A }, - }, - event: null, - }); + await movePolygon({ name: "/_polygon1", pointCoords: { 0: A }, core }); stateVariables = await returnAllStateVariables(core); expect( @@ -1892,14 +1666,7 @@ describe("Polygon tag tests", async () => { // move second vertex B = [8, 9]; - await core.requestAction({ - actionName: "movePolygon", - componentName: "/_polygon1", - args: { - pointCoords: { 1: B }, - }, - event: null, - }); + await movePolygon({ name: "/_polygon1", pointCoords: { 1: B }, core }); stateVariables = await returnAllStateVariables(core); expect( @@ -1931,14 +1698,7 @@ describe("Polygon tag tests", async () => { // move third vertex C = [-3, 7]; - await core.requestAction({ - actionName: "movePolygon", - componentName: "/_polygon1", - args: { - pointCoords: { 2: C }, - }, - event: null, - }); + await movePolygon({ name: "/_polygon1", pointCoords: { 2: C }, core }); stateVariables = await returnAllStateVariables(core); expect( @@ -1971,14 +1731,7 @@ describe("Polygon tag tests", async () => { A = [7, 0]; D[0] = A[0] + 1; - await core.requestAction({ - actionName: "movePolygon", - componentName: "/_polygon1", - args: { - pointCoords: { 3: A }, - }, - event: null, - }); + await movePolygon({ name: "/_polygon1", pointCoords: { 3: A }, core }); stateVariables = await returnAllStateVariables(core); expect( @@ -2011,14 +1764,7 @@ describe("Polygon tag tests", async () => { D = [-5, 9]; A[0] = D[0] - 1; - await core.requestAction({ - actionName: "movePolygon", - componentName: "/_polygon1", - args: { - pointCoords: { 4: D }, - }, - event: null, - }); + await movePolygon({ name: "/_polygon1", pointCoords: { 4: D }, core }); stateVariables = await returnAllStateVariables(core); expect( @@ -2101,14 +1847,7 @@ describe("Polygon tag tests", async () => { // move first vertex A = [-4, -9]; - await core.requestAction({ - actionName: "movePolygon", - componentName: "/P", - args: { - pointCoords: { 0: A }, - }, - event: null, - }); + await movePolygon({ name: "/P", pointCoords: { 0: A }, core }); stateVariables = await returnAllStateVariables(core); expect( @@ -2145,14 +1884,7 @@ describe("Polygon tag tests", async () => { // move second vertex B = [8, 9]; - await core.requestAction({ - actionName: "movePolygon", - componentName: "/P", - args: { - pointCoords: { 1: B }, - }, - event: null, - }); + await movePolygon({ name: "/P", pointCoords: { 1: B }, core }); stateVariables = await returnAllStateVariables(core); expect( @@ -2189,14 +1921,7 @@ describe("Polygon tag tests", async () => { // move third vertex C = [-3, 7]; - await core.requestAction({ - actionName: "movePolygon", - componentName: "/P", - args: { - pointCoords: { 2: C }, - }, - event: null, - }); + await movePolygon({ name: "/P", pointCoords: { 2: C }, core }); stateVariables = await returnAllStateVariables(core); expect( @@ -2233,14 +1958,7 @@ describe("Polygon tag tests", async () => { // move fourth vertex A = [7, 0]; - await core.requestAction({ - actionName: "movePolygon", - componentName: "/P", - args: { - pointCoords: { 3: A }, - }, - event: null, - }); + await movePolygon({ name: "/P", pointCoords: { 3: A }, core }); stateVariables = await returnAllStateVariables(core); expect( @@ -2277,14 +1995,7 @@ describe("Polygon tag tests", async () => { // move fifth vertex D = [-9, 1]; - await core.requestAction({ - actionName: "movePolygon", - componentName: "/P", - args: { - pointCoords: { 4: D }, - }, - event: null, - }); + await movePolygon({ name: "/P", pointCoords: { 4: D }, core }); stateVariables = await returnAllStateVariables(core); expect( @@ -2321,14 +2032,7 @@ describe("Polygon tag tests", async () => { // move sixth vertex E = [-3, 6]; - await core.requestAction({ - actionName: "movePolygon", - componentName: "/P", - args: { - pointCoords: { 5: E }, - }, - event: null, - }); + await movePolygon({ name: "/P", pointCoords: { 5: E }, core }); stateVariables = await returnAllStateVariables(core); expect( @@ -2365,14 +2069,7 @@ describe("Polygon tag tests", async () => { // move seventh vertex A = [2, -4]; - await core.requestAction({ - actionName: "movePolygon", - componentName: "/P", - args: { - pointCoords: { 6: A }, - }, - event: null, - }); + await movePolygon({ name: "/P", pointCoords: { 6: A }, core }); stateVariables = await returnAllStateVariables(core); expect( @@ -2409,14 +2106,7 @@ describe("Polygon tag tests", async () => { // move eighth vertex F = [6, 7]; - await core.requestAction({ - actionName: "movePolygon", - componentName: "/P", - args: { - pointCoords: { 7: F }, - }, - event: null, - }); + await movePolygon({ name: "/P", pointCoords: { 7: F }, core }); stateVariables = await returnAllStateVariables(core); expect( @@ -2453,14 +2143,7 @@ describe("Polygon tag tests", async () => { // move ninth vertex G = [1, -8]; - await core.requestAction({ - actionName: "movePolygon", - componentName: "/P", - args: { - pointCoords: { 8: G }, - }, - event: null, - }); + await movePolygon({ name: "/P", pointCoords: { 8: G }, core }); stateVariables = await returnAllStateVariables(core); expect( @@ -2497,14 +2180,7 @@ describe("Polygon tag tests", async () => { // move tenth vertex A = [-6, 10]; - await core.requestAction({ - actionName: "movePolygon", - componentName: "/P", - args: { - pointCoords: { 9: A }, - }, - event: null, - }); + await movePolygon({ name: "/P", pointCoords: { 9: A }, core }); stateVariables = await returnAllStateVariables(core); expect( @@ -2600,14 +2276,7 @@ describe("Polygon tag tests", async () => { A2 = [A[0] + 2, A[1] + 2]; A3 = [A[0] + 3, A[1] + 3]; - await core.requestAction({ - actionName: "movePolygon", - componentName: "/P", - args: { - pointCoords: { 0: A3 }, - }, - event: null, - }); + await movePolygon({ name: "/P", pointCoords: { 0: A3 }, core }); stateVariables = await returnAllStateVariables(core); expect( @@ -2644,14 +2313,7 @@ describe("Polygon tag tests", async () => { // move second vertex B = [8, 9]; - await core.requestAction({ - actionName: "movePolygon", - componentName: "/P", - args: { - pointCoords: { 1: B }, - }, - event: null, - }); + await movePolygon({ name: "/P", pointCoords: { 1: B }, core }); stateVariables = await returnAllStateVariables(core); expect( @@ -2688,14 +2350,7 @@ describe("Polygon tag tests", async () => { // move third vertex C = [-3, 7]; - await core.requestAction({ - actionName: "movePolygon", - componentName: "/P", - args: { - pointCoords: { 2: C }, - }, - event: null, - }); + await movePolygon({ name: "/P", pointCoords: { 2: C }, core }); stateVariables = await returnAllStateVariables(core); expect( @@ -2735,14 +2390,7 @@ describe("Polygon tag tests", async () => { A2 = [A[0] + 2, A[1] + 2]; A3 = [A[0] + 3, A[1] + 3]; - await core.requestAction({ - actionName: "movePolygon", - componentName: "/P", - args: { - pointCoords: { 3: A2 }, - }, - event: null, - }); + await movePolygon({ name: "/P", pointCoords: { 3: A2 }, core }); stateVariables = await returnAllStateVariables(core); expect( @@ -2779,14 +2427,7 @@ describe("Polygon tag tests", async () => { // move fifth vertex D = [-9, 1]; - await core.requestAction({ - actionName: "movePolygon", - componentName: "/P", - args: { - pointCoords: { 4: D }, - }, - event: null, - }); + await movePolygon({ name: "/P", pointCoords: { 4: D }, core }); stateVariables = await returnAllStateVariables(core); expect( @@ -2823,14 +2464,7 @@ describe("Polygon tag tests", async () => { // move sixth vertex E = [-3, 6]; - await core.requestAction({ - actionName: "movePolygon", - componentName: "/P", - args: { - pointCoords: { 5: E }, - }, - event: null, - }); + await movePolygon({ name: "/P", pointCoords: { 5: E }, core }); stateVariables = await returnAllStateVariables(core); expect( @@ -2870,14 +2504,7 @@ describe("Polygon tag tests", async () => { A2 = [A[0] + 2, A[1] + 2]; A3 = [A[0] + 3, A[1] + 3]; - await core.requestAction({ - actionName: "movePolygon", - componentName: "/P", - args: { - pointCoords: { 6: A1 }, - }, - event: null, - }); + await movePolygon({ name: "/P", pointCoords: { 6: A1 }, core }); stateVariables = await returnAllStateVariables(core); expect( @@ -2914,14 +2541,7 @@ describe("Polygon tag tests", async () => { // move eighth vertex F = [6, 7]; - await core.requestAction({ - actionName: "movePolygon", - componentName: "/P", - args: { - pointCoords: { 7: F }, - }, - event: null, - }); + await movePolygon({ name: "/P", pointCoords: { 7: F }, core }); stateVariables = await returnAllStateVariables(core); expect( @@ -2958,14 +2578,7 @@ describe("Polygon tag tests", async () => { // move ninth vertex G = [1, -8]; - await core.requestAction({ - actionName: "movePolygon", - componentName: "/P", - args: { - pointCoords: { 8: G }, - }, - event: null, - }); + await movePolygon({ name: "/P", pointCoords: { 8: G }, core }); stateVariables = await returnAllStateVariables(core); expect( @@ -3005,14 +2618,7 @@ describe("Polygon tag tests", async () => { A2 = [A[0] + 2, A[1] + 2]; A3 = [A[0] + 3, A[1] + 3]; - await core.requestAction({ - actionName: "movePolygon", - componentName: "/P", - args: { - pointCoords: { 9: A }, - }, - event: null, - }); + await movePolygon({ name: "/P", pointCoords: { 9: A }, core }); stateVariables = await returnAllStateVariables(core); expect( @@ -3082,12 +2688,7 @@ describe("Polygon tag tests", async () => { let mseg1 = (y2 - y1) / (x2 - x1); let y = mseg1 * (x - x1) + y1 + 0.3; - await core.requestAction({ - actionName: "movePoint", - componentName: `/_point1`, - args: { x, y }, - event: null, - }); + await movePoint({ name: `/_point1`, x, y, core }); stateVariables = await returnAllStateVariables(core); @@ -3101,12 +2702,7 @@ describe("Polygon tag tests", async () => { let mseg2 = (y2 - y3) / (x2 - x3); y = mseg2 * (x - x2) + y2 + 0.4; - await core.requestAction({ - actionName: "movePoint", - componentName: `/_point1`, - args: { x, y }, - event: null, - }); + await movePoint({ name: `/_point1`, x, y, core }); stateVariables = await returnAllStateVariables(core); px = stateVariables["/_point1"].stateValues.xs[0].tree; @@ -3119,12 +2715,7 @@ describe("Polygon tag tests", async () => { let mseg3 = (y1 - y3) / (x1 - x3); y = mseg3 * (x - x3) + y3 + 0.2; - await core.requestAction({ - actionName: "movePoint", - componentName: `/_point1`, - args: { x, y }, - event: null, - }); + await movePoint({ name: `/_point1`, x, y, core }); stateVariables = await returnAllStateVariables(core); px = stateVariables["/_point1"].stateValues.xs[0].tree; @@ -3136,12 +2727,7 @@ describe("Polygon tag tests", async () => { x = x1 + 0.2; y = y1 + 0.3; - await core.requestAction({ - actionName: "movePoint", - componentName: `/_point1`, - args: { x, y }, - event: null, - }); + await movePoint({ name: `/_point1`, x, y, core }); stateVariables = await returnAllStateVariables(core); px = stateVariables["/_point1"].stateValues.xs[0].tree; @@ -3155,12 +2741,7 @@ describe("Polygon tag tests", async () => { mseg1 = (y2 - y1) / (x2 - x1); y = mseg1 * (x - x1) + y1 + 0.3; - await core.requestAction({ - actionName: "movePoint", - componentName: `/_point1`, - args: { x, y }, - event: null, - }); + await movePoint({ name: `/_point1`, x, y, core }); stateVariables = await returnAllStateVariables(core); px = stateVariables["/_point1"].stateValues.xs[0].tree; @@ -3173,12 +2754,7 @@ describe("Polygon tag tests", async () => { mseg1 = (y2 - y1) / (x2 - x1); y = mseg1 * (x - x1) + y1 - 0.3; - await core.requestAction({ - actionName: "movePoint", - componentName: `/_point1`, - args: { x, y }, - event: null, - }); + await movePoint({ name: `/_point1`, x, y, core }); stateVariables = await returnAllStateVariables(core); px = stateVariables["/_point1"].stateValues.xs[0].tree; @@ -3191,12 +2767,7 @@ describe("Polygon tag tests", async () => { x = x2 - 0.2; y = y2 - 0.3; - await core.requestAction({ - actionName: "movePoint", - componentName: `/_point1`, - args: { x, y }, - event: null, - }); + await movePoint({ name: `/_point1`, x, y, core }); stateVariables = await returnAllStateVariables(core); px = stateVariables["/_point1"].stateValues.xs[0].tree; @@ -3210,12 +2781,7 @@ describe("Polygon tag tests", async () => { mseg2 = (y2 - y3) / (x2 - x3); y = mseg2 * (x - x2) + y2 + 0.3; - await core.requestAction({ - actionName: "movePoint", - componentName: `/_point1`, - args: { x, y }, - event: null, - }); + await movePoint({ name: `/_point1`, x, y, core }); stateVariables = await returnAllStateVariables(core); px = stateVariables["/_point1"].stateValues.xs[0].tree; @@ -3228,12 +2794,7 @@ describe("Polygon tag tests", async () => { mseg2 = (y2 - y3) / (x2 - x3); y = mseg2 * (x - x2) + y2 - 0.3; - await core.requestAction({ - actionName: "movePoint", - componentName: `/_point1`, - args: { x, y }, - event: null, - }); + await movePoint({ name: `/_point1`, x, y, core }); stateVariables = await returnAllStateVariables(core); px = stateVariables["/_point1"].stateValues.xs[0].tree; @@ -3253,17 +2814,14 @@ describe("Polygon tag tests", async () => { y2 += moveY; y3 += moveY; - await core.requestAction({ - actionName: "movePolygon", - componentName: "/_polygon1", - args: { - pointCoords: [ - [x1, y1], - [x2, y2], - [x3, y3], - ], - }, - event: null, + await movePolygon({ + name: "/_polygon1", + pointCoords: [ + [x1, y1], + [x2, y2], + [x3, y3], + ], + core, }); stateVariables = await returnAllStateVariables(core); @@ -3281,13 +2839,10 @@ describe("Polygon tag tests", async () => { x2 += moveX; y2 += moveY; - await core.requestAction({ - actionName: "movePolygon", - componentName: "/_polygon1", - args: { - pointCoords: { 1: [x2, y2] }, - }, - event: null, + await movePolygon({ + name: "/_polygon1", + pointCoords: { 1: [x2, y2] }, + core, }); stateVariables = await returnAllStateVariables(core); @@ -3335,12 +2890,7 @@ describe("Polygon tag tests", async () => { let mseg1 = (y2 - y1) / (x2 - x1); let y = mseg1 * (x - x1) + y1 + 0.3; - await core.requestAction({ - actionName: "movePoint", - componentName: `/_point1`, - args: { x, y }, - event: null, - }); + await movePoint({ name: `/_point1`, x, y, core }); stateVariables = await returnAllStateVariables(core); @@ -3354,12 +2904,7 @@ describe("Polygon tag tests", async () => { let mseg2 = (y2 - y3) / (x2 - x3); y = mseg2 * (x - x2) + y2 + 0.4; - await core.requestAction({ - actionName: "movePoint", - componentName: `/_point1`, - args: { x, y }, - event: null, - }); + await movePoint({ name: `/_point1`, x, y, core }); stateVariables = await returnAllStateVariables(core); px = stateVariables["/_point1"].stateValues.xs[0].tree; @@ -3373,12 +2918,7 @@ describe("Polygon tag tests", async () => { let mseg3 = (y1 - y3) / (x1 - x3); y = mseg3 * (x - x3) + y3 + 0.2; - await core.requestAction({ - actionName: "movePoint", - componentName: `/_point1`, - args: { x, y }, - event: null, - }); + await movePoint({ name: `/_point1`, x, y, core }); stateVariables = await returnAllStateVariables(core); px = stateVariables["/_point1"].stateValues.xs[0].tree; @@ -3390,12 +2930,7 @@ describe("Polygon tag tests", async () => { x = x1 + 0.2; y = y1 + 0.3; - await core.requestAction({ - actionName: "movePoint", - componentName: `/_point1`, - args: { x, y }, - event: null, - }); + await movePoint({ name: `/_point1`, x, y, core }); stateVariables = await returnAllStateVariables(core); px = stateVariables["/_point1"].stateValues.xs[0].tree; @@ -3410,12 +2945,7 @@ describe("Polygon tag tests", async () => { mseg1 = (y2 - y1) / (x2 - x1); y = mseg1 * (x - x1) + y1 + 0.3; - await core.requestAction({ - actionName: "movePoint", - componentName: `/_point1`, - args: { x, y }, - event: null, - }); + await movePoint({ name: `/_point1`, x, y, core }); stateVariables = await returnAllStateVariables(core); px = stateVariables["/_point1"].stateValues.xs[0].tree; @@ -3427,12 +2957,7 @@ describe("Polygon tag tests", async () => { mseg1 = (y2 - y1) / (x2 - x1); y = mseg1 * (x - x1) + y1 - 0.3; - await core.requestAction({ - actionName: "movePoint", - componentName: `/_point1`, - args: { x, y }, - event: null, - }); + await movePoint({ name: `/_point1`, x, y, core }); stateVariables = await returnAllStateVariables(core); px = stateVariables["/_point1"].stateValues.xs[0].tree; @@ -3446,12 +2971,7 @@ describe("Polygon tag tests", async () => { x = x2 - 0.2; y = y2 - 0.3; - await core.requestAction({ - actionName: "movePoint", - componentName: `/_point1`, - args: { x, y }, - event: null, - }); + await movePoint({ name: `/_point1`, x, y, core }); stateVariables = await returnAllStateVariables(core); px = stateVariables["/_point1"].stateValues.xs[0].tree; @@ -3466,12 +2986,7 @@ describe("Polygon tag tests", async () => { mseg2 = (y2 - y3) / (x2 - x3); y = mseg2 * (x - x2) + y2 + 0.3; - await core.requestAction({ - actionName: "movePoint", - componentName: `/_point1`, - args: { x, y }, - event: null, - }); + await movePoint({ name: `/_point1`, x, y, core }); stateVariables = await returnAllStateVariables(core); px = stateVariables["/_point1"].stateValues.xs[0].tree; @@ -3484,12 +2999,7 @@ describe("Polygon tag tests", async () => { mseg2 = (y2 - y3) / (x2 - x3); y = mseg2 * (x - x2) + y2 - 0.3; - await core.requestAction({ - actionName: "movePoint", - componentName: `/_point1`, - args: { x, y }, - event: null, - }); + await movePoint({ name: `/_point1`, x, y, core }); stateVariables = await returnAllStateVariables(core); px = stateVariables["/_point1"].stateValues.xs[0].tree; @@ -3510,17 +3020,14 @@ describe("Polygon tag tests", async () => { y2 += moveY; y3 += moveY; - await core.requestAction({ - actionName: "movePolygon", - componentName: "/_polygon1", - args: { - pointCoords: [ - [x1, y1], - [x2, y2], - [x3, y3], - ], - }, - event: null, + await movePolygon({ + name: "/_polygon1", + pointCoords: [ + [x1, y1], + [x2, y2], + [x3, y3], + ], + core, }); stateVariables = await returnAllStateVariables(core); @@ -3539,13 +3046,10 @@ describe("Polygon tag tests", async () => { x2 += moveX; y2 += moveY; - await core.requestAction({ - actionName: "movePolygon", - componentName: "/_polygon1", - args: { - pointCoords: { 1: [x2, y2] }, - }, - event: null, + await movePolygon({ + name: "/_polygon1", + pointCoords: { 1: [x2, y2] }, + core, }); stateVariables = await returnAllStateVariables(core); @@ -3596,12 +3100,7 @@ describe("Polygon tag tests", async () => { let mseg1 = (y2 - y1) / (x2 - x1); - await core.requestAction({ - actionName: "movePoint", - componentName: `/A`, - args: { x: -20, y: 0.02 }, - event: null, - }); + await movePoint({ name: `/A`, x: -20, y: 0.02, core }); stateVariables = await returnAllStateVariables(core); px = stateVariables["/A"].stateValues.xs[0].tree; @@ -3613,12 +3112,7 @@ describe("Polygon tag tests", async () => { let mseg2 = (y2 - y3) / (x2 - x3); - await core.requestAction({ - actionName: "movePoint", - componentName: `/A`, - args: { x: 0, y: 0.04 }, - event: null, - }); + await movePoint({ name: `/A`, x: 0, y: 0.04, core }); stateVariables = await returnAllStateVariables(core); px = stateVariables["/A"].stateValues.xs[0].tree; @@ -3630,12 +3124,7 @@ describe("Polygon tag tests", async () => { let mseg4 = (y4 - y1) / (x4 - x1); - await core.requestAction({ - actionName: "movePoint", - componentName: `/A`, - args: { x: -10, y: 0.02 }, - event: null, - }); + await movePoint({ name: `/A`, x: -10, y: 0.02, core }); stateVariables = await returnAllStateVariables(core); px = stateVariables["/A"].stateValues.xs[0].tree; @@ -3678,12 +3167,7 @@ describe("Polygon tag tests", async () => { let mseg1 = (y2 - y1) / (x2 - x1); let y = mseg1 * (x - x1) + y1 + 0.3; - await core.requestAction({ - actionName: "movePoint", - componentName: `/_point1`, - args: { x, y }, - event: null, - }); + await movePoint({ name: `/_point1`, x, y, core }); stateVariables = await returnAllStateVariables(core); @@ -3697,12 +3181,7 @@ describe("Polygon tag tests", async () => { x = 3; y = 1.5; - await core.requestAction({ - actionName: "movePoint", - componentName: `/_point1`, - args: { x, y }, - event: null, - }); + await movePoint({ name: `/_point1`, x, y, core }); stateVariables = await returnAllStateVariables(core); px = stateVariables["/_point1"].stateValues.xs[0].tree; @@ -3717,12 +3196,7 @@ describe("Polygon tag tests", async () => { let mseg3 = (y1 - y3) / (x1 - x3); y = mseg3 * (x - x3) + y3 + 0.2; - await core.requestAction({ - actionName: "movePoint", - componentName: `/_point1`, - args: { x, y }, - event: null, - }); + await movePoint({ name: `/_point1`, x, y, core }); stateVariables = await returnAllStateVariables(core); px = stateVariables["/_point1"].stateValues.xs[0].tree; @@ -3735,12 +3209,7 @@ describe("Polygon tag tests", async () => { x = x1 + 0.2; y = y1 + 0.3; - await core.requestAction({ - actionName: "movePoint", - componentName: `/_point1`, - args: { x, y }, - event: null, - }); + await movePoint({ name: `/_point1`, x, y, core }); stateVariables = await returnAllStateVariables(core); px = stateVariables["/_point1"].stateValues.xs[0].tree; @@ -3755,12 +3224,7 @@ describe("Polygon tag tests", async () => { mseg1 = (y2 - y1) / (x2 - x1); y = mseg1 * (x - x1) + y1 + 0.3; - await core.requestAction({ - actionName: "movePoint", - componentName: `/_point1`, - args: { x, y }, - event: null, - }); + await movePoint({ name: `/_point1`, x, y, core }); stateVariables = await returnAllStateVariables(core); px = stateVariables["/_point1"].stateValues.xs[0].tree; @@ -3773,12 +3237,7 @@ describe("Polygon tag tests", async () => { mseg1 = (y2 - y1) / (x2 - x1); y = mseg1 * (x - x1) + y1 - 0.3; - await core.requestAction({ - actionName: "movePoint", - componentName: `/_point1`, - args: { x, y }, - event: null, - }); + await movePoint({ name: `/_point1`, x, y, core }); stateVariables = await returnAllStateVariables(core); px = stateVariables["/_point1"].stateValues.xs[0].tree; @@ -3792,12 +3251,7 @@ describe("Polygon tag tests", async () => { x = x2 - 0.2; y = y2 - 0.3; - await core.requestAction({ - actionName: "movePoint", - componentName: `/_point1`, - args: { x, y }, - event: null, - }); + await movePoint({ name: `/_point1`, x, y, core }); stateVariables = await returnAllStateVariables(core); px = stateVariables["/_point1"].stateValues.xs[0].tree; @@ -3812,12 +3266,7 @@ describe("Polygon tag tests", async () => { let mseg2 = (y2 - y3) / (x2 - x3); y = mseg2 * (x - x2) + y2 + 0.3; - await core.requestAction({ - actionName: "movePoint", - componentName: `/_point1`, - args: { x, y }, - event: null, - }); + await movePoint({ name: `/_point1`, x, y, core }); stateVariables = await returnAllStateVariables(core); px = stateVariables["/_point1"].stateValues.xs[0].tree; @@ -3833,12 +3282,7 @@ describe("Polygon tag tests", async () => { mseg2 = (y2 - y3) / (x2 - x3); y = mseg2 * (x - x2) + y2 - 0.3; - await core.requestAction({ - actionName: "movePoint", - componentName: `/_point1`, - args: { x, y }, - event: null, - }); + await movePoint({ name: `/_point1`, x, y, core }); stateVariables = await returnAllStateVariables(core); px = stateVariables["/_point1"].stateValues.xs[0].tree; @@ -3863,17 +3307,14 @@ describe("Polygon tag tests", async () => { y2 += moveY; y3 += moveY; - await core.requestAction({ - actionName: "movePolygon", - componentName: "/_polygon1", - args: { - pointCoords: [ - [x1, y1], - [x2, y2], - [x3, y3], - ], - }, - event: null, + await movePolygon({ + name: "/_polygon1", + pointCoords: [ + [x1, y1], + [x2, y2], + [x3, y3], + ], + core, }); stateVariables = await returnAllStateVariables(core); @@ -3892,13 +3333,10 @@ describe("Polygon tag tests", async () => { x2 += moveX; y2 += moveY; - await core.requestAction({ - actionName: "movePolygon", - componentName: "/_polygon1", - args: { - pointCoords: { 1: [x2, y2] }, - }, - event: null, + await movePolygon({ + name: "/_polygon1", + pointCoords: { 1: [x2, y2] }, + core, }); stateVariables = await returnAllStateVariables(core); @@ -3915,13 +3353,10 @@ describe("Polygon tag tests", async () => { x3 = -4; y3 = -6; - await core.requestAction({ - actionName: "movePolygon", - componentName: "/_polygon1", - args: { - pointCoords: { 2: [x3, y3] }, - }, - event: null, + await movePolygon({ + name: "/_polygon1", + pointCoords: { 2: [x3, y3] }, + core, }); stateVariables = await returnAllStateVariables(core); @@ -3958,12 +3393,7 @@ describe("Polygon tag tests", async () => { // move point above polygon - await core.requestAction({ - actionName: "movePoint", - componentName: `/P`, - args: { x: 3, y: 10 }, - event: null, - }); + await movePoint({ name: `/P`, x: 3, y: 10, core }); stateVariables = await returnAllStateVariables(core); @@ -3974,12 +3404,7 @@ describe("Polygon tag tests", async () => { // move point inside doubly wound region - await core.requestAction({ - actionName: "movePoint", - componentName: `/P`, - args: { x: 3, y: 5 }, - event: null, - }); + await movePoint({ name: `/P`, x: 3, y: 5, core }); stateVariables = await returnAllStateVariables(core); px = stateVariables["/P"].stateValues.xs[0].tree; @@ -3989,12 +3414,7 @@ describe("Polygon tag tests", async () => { // attempt to move point inside zero wound region - await core.requestAction({ - actionName: "movePoint", - componentName: `/P`, - args: { x: 4.9, y: 3 }, - event: null, - }); + await movePoint({ name: `/P`, x: 4.9, y: 3, core }); stateVariables = await returnAllStateVariables(core); px = stateVariables["/P"].stateValues.xs[0].tree; @@ -4026,17 +3446,14 @@ describe("Polygon tag tests", async () => { // cannot move vertices - await core.requestAction({ - actionName: "movePolygon", - componentName: "/p", - args: { - pointCoords: [ - [4, 7], - [8, 10], - [1, 9], - ], - }, - event: null, + await movePolygon({ + name: "/p", + pointCoords: [ + [4, 7], + [8, 10], + [1, 9], + ], + core, }); stateVariables = await returnAllStateVariables(core); @@ -4084,7 +3501,7 @@ describe("Polygon tag tests", async () => { expect(stateVariables["/x"]).eq(undefined); expect(stateVariables["/xa"]).eq(undefined); - await updateMathInputValue({ latex: "1", componentName: "/n", core }); + await updateMathInputValue({ latex: "1", name: "/n", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/P1"].stateValues.xs.map((x) => x.tree)).eqls([ @@ -4096,7 +3513,7 @@ describe("Polygon tag tests", async () => { expect(stateVariables["/x"].stateValues.value.tree).eq(t2x); expect(stateVariables["/xa"].stateValues.value.tree).eq(t2x); - await updateMathInputValue({ latex: "2", componentName: "/n", core }); + await updateMathInputValue({ latex: "2", name: "/n", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/P1"].stateValues.xs.map((x) => x.tree)).eqls([ @@ -4108,7 +3525,7 @@ describe("Polygon tag tests", async () => { expect(stateVariables["/x"].stateValues.value.tree).eq(t2y); expect(stateVariables["/xa"].stateValues.value.tree).eq(t2y); - await updateMathInputValue({ latex: "3", componentName: "/n", core }); + await updateMathInputValue({ latex: "3", name: "/n", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/P1"].stateValues.xs.map((x) => x.tree)).eqls([ @@ -4120,7 +3537,7 @@ describe("Polygon tag tests", async () => { expect(stateVariables["/x"]).eq(undefined); expect(stateVariables["/xa"]).eq(undefined); - await updateMathInputValue({ latex: "4", componentName: "/n", core }); + await updateMathInputValue({ latex: "4", name: "/n", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/P1"]).eq(undefined); @@ -4154,13 +3571,10 @@ describe("Polygon tag tests", async () => { [-4, 5], ]); - await core.requestAction({ - actionName: "movePolygon", - componentName: "/polygon", - args: { - pointCoords: { 0: [3, 5] }, - }, - event: null, + await movePolygon({ + name: "/polygon", + pointCoords: { 0: [3, 5] }, + core, }); stateVariables = await returnAllStateVariables(core); @@ -4174,13 +3588,10 @@ describe("Polygon tag tests", async () => { [-4, 5], ]); - await core.requestAction({ - actionName: "movePolygon", - componentName: "/polygon", - args: { - pointCoords: { 1: [-9, -6] }, - }, - event: null, + await movePolygon({ + name: "/polygon", + pointCoords: { 1: [-9, -6] }, + core, }); stateVariables = await returnAllStateVariables(core); @@ -4194,13 +3605,10 @@ describe("Polygon tag tests", async () => { [-9, 2], ]); - await core.requestAction({ - actionName: "movePolygon", - componentName: "/polygon", - args: { - pointCoords: { 2: [-3, 1] }, - }, - event: null, + await movePolygon({ + name: "/polygon", + pointCoords: { 2: [-3, 1] }, + core, }); stateVariables = await returnAllStateVariables(core); @@ -4242,13 +3650,10 @@ describe("Polygon tag tests", async () => { [-4, 5], ]); - await core.requestAction({ - actionName: "movePolygon", - componentName: "/polygon", - args: { - pointCoords: { 0: [3, 5] }, - }, - event: null, + await movePolygon({ + name: "/polygon", + pointCoords: { 0: [3, 5] }, + core, }); stateVariables = await returnAllStateVariables(core); @@ -4262,13 +3667,10 @@ describe("Polygon tag tests", async () => { [-4, 5], ]); - await core.requestAction({ - actionName: "movePolygon", - componentName: "/polygon", - args: { - pointCoords: { 1: [-9, -6] }, - }, - event: null, + await movePolygon({ + name: "/polygon", + pointCoords: { 1: [-9, -6] }, + core, }); stateVariables = await returnAllStateVariables(core); @@ -4282,13 +3684,10 @@ describe("Polygon tag tests", async () => { [-9, 2], ]); - await core.requestAction({ - actionName: "movePolygon", - componentName: "/polygon", - args: { - pointCoords: { 2: [-3, 1] }, - }, - event: null, + await movePolygon({ + name: "/polygon", + pointCoords: { 2: [-3, 1] }, + core, }); stateVariables = await returnAllStateVariables(core); @@ -4447,14 +3846,7 @@ describe("Polygon tag tests", async () => { // cannot move single vertex - await core.requestAction({ - actionName: "movePolygon", - componentName: "/p", - args: { - pointCoords: { 0: [4, 7] }, - }, - event: null, - }); + await movePolygon({ name: "/p", pointCoords: { 0: [4, 7] }, core }); stateVariables = await returnAllStateVariables(core); expect( @@ -4471,17 +3863,14 @@ describe("Polygon tag tests", async () => { // cannot move all vertices - await core.requestAction({ - actionName: "movePolygon", - componentName: "/p", - args: { - pointCoords: [ - [4, 7], - [8, 10], - [1, 9], - ], - }, - event: null, + await movePolygon({ + name: "/p", + pointCoords: [ + [4, 7], + [8, 10], + [1, 9], + ], + core, }); stateVariables = await returnAllStateVariables(core); @@ -4501,20 +3890,13 @@ describe("Polygon tag tests", async () => { await updateBooleanInputValue({ boolean: true, - componentName: "/verticesDraggable", + name: "/verticesDraggable", core, }); // can move single vertex - await core.requestAction({ - actionName: "movePolygon", - componentName: "/p", - args: { - pointCoords: { 0: [4, 7] }, - }, - event: null, - }); + await movePolygon({ name: "/p", pointCoords: { 0: [4, 7] }, core }); stateVariables = await returnAllStateVariables(core); expect( @@ -4531,17 +3913,14 @@ describe("Polygon tag tests", async () => { // cannot move all vertices - await core.requestAction({ - actionName: "movePolygon", - componentName: "/p", - args: { - pointCoords: [ - [3, 8], - [8, 10], - [1, 9], - ], - }, - event: null, + await movePolygon({ + name: "/p", + pointCoords: [ + [3, 8], + [8, 10], + [1, 9], + ], + core, }); stateVariables = await returnAllStateVariables(core); @@ -4561,20 +3940,13 @@ describe("Polygon tag tests", async () => { await updateBooleanInputValue({ boolean: true, - componentName: "/draggable", + name: "/draggable", core, }); // can move single vertex - await core.requestAction({ - actionName: "movePolygon", - componentName: "/p", - args: { - pointCoords: { 1: [-3, 2] }, - }, - event: null, - }); + await movePolygon({ name: "/p", pointCoords: { 1: [-3, 2] }, core }); stateVariables = await returnAllStateVariables(core); expect( @@ -4591,17 +3963,14 @@ describe("Polygon tag tests", async () => { // can move all vertices - await core.requestAction({ - actionName: "movePolygon", - componentName: "/p", - args: { - pointCoords: [ - [3, 8], - [8, 10], - [1, 9], - ], - }, - event: null, + await movePolygon({ + name: "/p", + pointCoords: [ + [3, 8], + [8, 10], + [1, 9], + ], + core, }); stateVariables = await returnAllStateVariables(core); @@ -4621,20 +3990,13 @@ describe("Polygon tag tests", async () => { await updateBooleanInputValue({ boolean: false, - componentName: "/verticesDraggable", + name: "/verticesDraggable", core, }); // cannot move single vertex - await core.requestAction({ - actionName: "movePolygon", - componentName: "/p", - args: { - pointCoords: { 2: [9, 3] }, - }, - event: null, - }); + await movePolygon({ name: "/p", pointCoords: { 2: [9, 3] }, core }); stateVariables = await returnAllStateVariables(core); expect( @@ -4651,17 +4013,14 @@ describe("Polygon tag tests", async () => { // can move all vertices - await core.requestAction({ - actionName: "movePolygon", - componentName: "/p", - args: { - pointCoords: [ - [-4, 1], - [9, -4], - [0, 7], - ], - }, - event: null, + await movePolygon({ + name: "/p", + pointCoords: [ + [-4, 1], + [9, -4], + [0, 7], + ], + core, }); stateVariables = await returnAllStateVariables(core); @@ -4712,13 +4071,10 @@ describe("Polygon tag tests", async () => { vertices[1] = [4, 7]; - await core.requestAction({ - actionName: "movePolygon", - componentName: "/g1/pg", - args: { - pointCoords: { 1: vertices[1] }, - }, - event: null, + await movePolygon({ + name: "/g1/pg", + pointCoords: { 1: vertices[1] }, + core, }); await testPolygonCopiedTwice({ core, vertices }); @@ -4733,14 +4089,7 @@ describe("Polygon tag tests", async () => { vertices[i][1] = vertices[i][1] + moveY; } - await core.requestAction({ - actionName: "movePolygon", - componentName: "/g2/pg", - args: { - pointCoords: vertices, - }, - event: null, - }); + await movePolygon({ name: "/g2/pg", pointCoords: vertices, core }); // adjustment due to constraint moveX = -1; @@ -4762,14 +4111,7 @@ describe("Polygon tag tests", async () => { vertices[i][1] = vertices[i][1] + moveY; } - await core.requestAction({ - actionName: "movePolygon", - componentName: "/g3/pg", - args: { - pointCoords: vertices, - }, - event: null, - }); + await movePolygon({ name: "/g3/pg", pointCoords: vertices, core }); // adjustment due to constraint moveX = -1; @@ -4820,13 +4162,10 @@ describe("Polygon tag tests", async () => { vertices[1] = [4, 7]; - await core.requestAction({ - actionName: "movePolygon", - componentName: "/g1/pg", - args: { - pointCoords: { 1: vertices[1] }, - }, - event: null, + await movePolygon({ + name: "/g1/pg", + pointCoords: { 1: vertices[1] }, + core, }); await testPolygonCopiedTwice({ core, vertices }); @@ -4841,14 +4180,7 @@ describe("Polygon tag tests", async () => { vertices[i][1] = vertices[i][1] + moveY; } - await core.requestAction({ - actionName: "movePolygon", - componentName: "/g2/pg", - args: { - pointCoords: vertices, - }, - event: null, - }); + await movePolygon({ name: "/g2/pg", pointCoords: vertices, core }); // adjustment due to constraint moveX = -1; @@ -4870,14 +4202,7 @@ describe("Polygon tag tests", async () => { vertices[i][1] = vertices[i][1] + moveY; } - await core.requestAction({ - actionName: "movePolygon", - componentName: "/g3/pg", - args: { - pointCoords: vertices, - }, - event: null, - }); + await movePolygon({ name: "/g3/pg", pointCoords: vertices, core }); // adjustment due to constraint moveX = -1; @@ -4932,13 +4257,10 @@ describe("Polygon tag tests", async () => { vertices[1] = [4, 7]; - await core.requestAction({ - actionName: "movePolygon", - componentName: "/g1/pg", - args: { - pointCoords: { 1: vertices[1] }, - }, - event: null, + await movePolygon({ + name: "/g1/pg", + pointCoords: { 1: vertices[1] }, + core, }); // adjust for constraint @@ -4956,14 +4278,7 @@ describe("Polygon tag tests", async () => { vertices[i][1] = vertices[i][1] + moveY; } - await core.requestAction({ - actionName: "movePolygon", - componentName: "/g2/pg", - args: { - pointCoords: vertices, - }, - event: null, - }); + await movePolygon({ name: "/g2/pg", pointCoords: vertices, core }); // adjustment due to constraint moveX = -1; @@ -4985,14 +4300,7 @@ describe("Polygon tag tests", async () => { vertices[i][1] = vertices[i][1] + moveY; } - await core.requestAction({ - actionName: "movePolygon", - componentName: "/g3/pg", - args: { - pointCoords: vertices, - }, - event: null, - }); + await movePolygon({ name: "/g3/pg", pointCoords: vertices, core }); // adjustment due to constraint moveX = -1; @@ -5052,13 +4360,10 @@ describe("Polygon tag tests", async () => { } } - await core.requestAction({ - actionName: "movePolygon", - componentName: "/g2/pg", - args: { - pointCoords: desiredVertices, - }, - event: null, + await movePolygon({ + name: "/g2/pg", + pointCoords: desiredVertices, + core, }); await testPolygonCopiedTwice({ vertices, core }); @@ -5096,22 +4401,8 @@ describe("Polygon tag tests", async () => { expect(stateVariables["/area"].stateValues.value).eq(area); expect(stateVariables["/perimeter"].stateValues.value).eq(perimeter); - await core.requestAction({ - actionName: "movePolygon", - componentName: "/p", - args: { - pointCoords: { 1: [-8, -4] }, - }, - event: null, - }); - await core.requestAction({ - actionName: "movePolygon", - componentName: "/p", - args: { - pointCoords: { 2: [-8, 2] }, - }, - event: null, - }); + await movePolygon({ name: "/p", pointCoords: { 1: [-8, -4] }, core }); + await movePolygon({ name: "/p", pointCoords: { 2: [-8, 2] }, core }); area = 2 * 8 + (4 * 8) / 2 - (5 * 8) / 2; perimeter = 13 + 6 + Math.sqrt(16 + 64) + 10 + Math.sqrt(25 + 64); @@ -5120,14 +4411,7 @@ describe("Polygon tag tests", async () => { expect(stateVariables["/area"].stateValues.value).eq(area); expect(stateVariables["/perimeter"].stateValues.value).eq(perimeter); - await core.requestAction({ - actionName: "movePolygon", - componentName: "/p", - args: { - pointCoords: { 3: [8, 2] }, - }, - event: null, - }); + await movePolygon({ name: "/p", pointCoords: { 3: [8, 2] }, core }); area = 0; perimeter = 16 + 6 + Math.sqrt(16 + 64) + 10 + Math.sqrt(64 + 64); @@ -5136,14 +4420,7 @@ describe("Polygon tag tests", async () => { expect(stateVariables["/area"].stateValues.value).eq(area); expect(stateVariables["/perimeter"].stateValues.value).eq(perimeter); - await core.requestAction({ - actionName: "movePolygon", - componentName: "/p", - args: { - pointCoords: { 0: [0, 2] }, - }, - event: null, - }); + await movePolygon({ name: "/p", pointCoords: { 0: [0, 2] }, core }); area = (8 * 8) / 2 - (8 * 6) / 2; perimeter = 16 + 6 + Math.sqrt(36 + 64) + 8 + Math.sqrt(64 + 64); @@ -5198,13 +4475,10 @@ describe("Polygon tag tests", async () => { v[0] - centroid[0] + centroid[1], ]); - await core.requestAction({ - actionName: "movePolygon", - componentName: "/g1/pg", - args: { - pointCoords: { 1: requested_vertex_1 }, - }, - event: null, + await movePolygon({ + name: "/g1/pg", + pointCoords: { 1: requested_vertex_1 }, + core, }); await testPolygonCopiedTwice({ vertices, core }); @@ -5224,13 +4498,10 @@ describe("Polygon tag tests", async () => { ]); } - await core.requestAction({ - actionName: "movePolygon", - componentName: "/g2/pg", - args: { - pointCoords: requested_vertices, - }, - event: null, + await movePolygon({ + name: "/g2/pg", + pointCoords: requested_vertices, + core, }); await testPolygonCopiedTwice({ vertices, core }); @@ -5254,13 +4525,10 @@ describe("Polygon tag tests", async () => { -(v[1] - centroid[1]) + centroid[1], ]); - await core.requestAction({ - actionName: "movePolygon", - componentName: "/g3/pg", - args: { - pointCoords: { 2: requested_vertex_2 }, - }, - event: null, + await movePolygon({ + name: "/g3/pg", + pointCoords: { 2: requested_vertex_2 }, + core, }); await testPolygonCopiedTwice({ vertices, core }); @@ -5284,11 +4552,11 @@ describe("Polygon tag tests", async () => { -(v[0] - centroid[0]) + centroid[1], ]); - await core.requestAction({ - actionName: "movePoint", - componentName: "/g2/v4", - args: { x: requested_vertex_3[0], y: requested_vertex_3[1] }, - event: null, + await movePoint({ + name: "/g2/v4", + x: requested_vertex_3[0], + y: requested_vertex_3[1], + core, }); await testPolygonCopiedTwice({ vertices, core }); @@ -5296,11 +4564,11 @@ describe("Polygon tag tests", async () => { // moving defining vertex deforms polygon vertices[0] = [4, 6]; - await core.requestAction({ - actionName: "movePoint", - componentName: "/g1/_point1", - args: { x: vertices[0][0], y: vertices[0][1] }, - event: null, + await movePoint({ + name: "/g1/_point1", + x: vertices[0][0], + y: vertices[0][1], + core, }); await testPolygonCopiedTwice({ vertices, core }); @@ -5352,13 +4620,10 @@ describe("Polygon tag tests", async () => { 0.5 * (v[0] - centroid[0]) + centroid[1], ]); - await core.requestAction({ - actionName: "movePolygon", - componentName: "/g1/pg", - args: { - pointCoords: { 1: requested_vertex_1 }, - }, - event: null, + await movePolygon({ + name: "/g1/pg", + pointCoords: { 1: requested_vertex_1 }, + core, }); await testPolygonCopiedTwice({ vertices, core }); @@ -5378,13 +4643,10 @@ describe("Polygon tag tests", async () => { ]); } - await core.requestAction({ - actionName: "movePolygon", - componentName: "/g2/pg", - args: { - pointCoords: requested_vertices, - }, - event: null, + await movePolygon({ + name: "/g2/pg", + pointCoords: requested_vertices, + core, }); await testPolygonCopiedTwice({ vertices, core }); @@ -5409,13 +4671,10 @@ describe("Polygon tag tests", async () => { -2 * (v[1] - centroid[1]) + centroid[1], ]); - await core.requestAction({ - actionName: "movePolygon", - componentName: "/g3/pg", - args: { - pointCoords: { 2: requested_vertex_2 }, - }, - event: null, + await movePolygon({ + name: "/g3/pg", + pointCoords: { 2: requested_vertex_2 }, + core, }); await testPolygonCopiedTwice({ vertices, core }); @@ -5439,11 +4698,11 @@ describe("Polygon tag tests", async () => { -0.25 * (v[0] - centroid[0]) + centroid[1], ]); - await core.requestAction({ - actionName: "movePoint", - componentName: "/g2/v4", - args: { x: requested_vertex_3[0], y: requested_vertex_3[1] }, - event: null, + await movePoint({ + name: "/g2/v4", + x: requested_vertex_3[0], + y: requested_vertex_3[1], + core, }); await testPolygonCopiedTwice({ vertices, core }); @@ -5451,11 +4710,11 @@ describe("Polygon tag tests", async () => { // moving defining vertex deforms polygon vertices[0] = [4, 6]; - await core.requestAction({ - actionName: "movePoint", - componentName: "/g1/_point1", - args: { x: vertices[0][0], y: vertices[0][1] }, - event: null, + await movePoint({ + name: "/g1/_point1", + x: vertices[0][0], + y: vertices[0][1], + core, }); await testPolygonCopiedTwice({ vertices, core }); @@ -5507,13 +4766,10 @@ describe("Polygon tag tests", async () => { v[0] - centroid[0] + centroid[1], ]); - await core.requestAction({ - actionName: "movePolygon", - componentName: "/g1/pg", - args: { - pointCoords: { 1: requested_vertex_1 }, - }, - event: null, + await movePolygon({ + name: "/g1/pg", + pointCoords: { 1: requested_vertex_1 }, + core, }); await testPolygonCopiedTwice({ vertices, core }); @@ -5533,13 +4789,10 @@ describe("Polygon tag tests", async () => { ]); } - await core.requestAction({ - actionName: "movePolygon", - componentName: "/g2/pg", - args: { - pointCoords: requested_vertices, - }, - event: null, + await movePolygon({ + name: "/g2/pg", + pointCoords: requested_vertices, + core, }); await testPolygonCopiedTwice({ vertices, core }); @@ -5591,13 +4844,10 @@ describe("Polygon tag tests", async () => { v[0] - centroid[0] + centroid[1], ]); - await core.requestAction({ - actionName: "movePolygon", - componentName: "/g1/pg", - args: { - pointCoords: { 1: requested_vertex_1 }, - }, - event: null, + await movePolygon({ + name: "/g1/pg", + pointCoords: { 1: requested_vertex_1 }, + core, }); await testPolygonCopiedTwice({ vertices, core }); @@ -5617,13 +4867,10 @@ describe("Polygon tag tests", async () => { ]); } - await core.requestAction({ - actionName: "movePolygon", - componentName: "/g2/pg", - args: { - pointCoords: requested_vertices, - }, - event: null, + await movePolygon({ + name: "/g2/pg", + pointCoords: requested_vertices, + core, }); await testPolygonCopiedTwice({ vertices, core }); @@ -5685,13 +4932,10 @@ describe("Polygon tag tests", async () => { 0.5 * (v[1] - centroid[1]) + centroid[1], ]); - await core.requestAction({ - actionName: "movePolygon", - componentName: "/g1/pg", - args: { - pointCoords: { 1: requested_vertex_1 }, - }, - event: null, + await movePolygon({ + name: "/g1/pg", + pointCoords: { 1: requested_vertex_1 }, + core, }); await testPolygonCopiedTwice({ vertices, core }); @@ -5711,13 +4955,10 @@ describe("Polygon tag tests", async () => { ]); } - await core.requestAction({ - actionName: "movePolygon", - componentName: "/g2/pg", - args: { - pointCoords: requested_vertices, - }, - event: null, + await movePolygon({ + name: "/g2/pg", + pointCoords: requested_vertices, + core, }); await testPolygonCopiedTwice({ vertices, core }); @@ -5750,13 +4991,10 @@ describe("Polygon tag tests", async () => { shrink_factor * (v[1] - centroid[1]) + centroid[1], ]); - await core.requestAction({ - actionName: "movePolygon", - componentName: "/g3/pg", - args: { - pointCoords: { 2: requested_vertex_2 }, - }, - event: null, + await movePolygon({ + name: "/g3/pg", + pointCoords: { 2: requested_vertex_2 }, + core, }); await testPolygonCopiedTwice({ vertices, core }); @@ -5790,11 +5028,11 @@ describe("Polygon tag tests", async () => { 10 * (v[1] - centroid[1]) + centroid[1], ]); - await core.requestAction({ - actionName: "movePoint", - componentName: "/g2/v4", - args: { x: requested_vertex_3[0], y: requested_vertex_3[1] }, - event: null, + await movePoint({ + name: "/g2/v4", + x: requested_vertex_3[0], + y: requested_vertex_3[1], + core, }); await testPolygonCopiedTwice({ vertices, core }); @@ -5802,11 +5040,11 @@ describe("Polygon tag tests", async () => { // moving defining vertex deforms polygon vertices[0] = [4, 6]; - await core.requestAction({ - actionName: "movePoint", - componentName: "/g1/_point1", - args: { x: vertices[0][0], y: vertices[0][1] }, - event: null, + await movePoint({ + name: "/g1/_point1", + x: vertices[0][0], + y: vertices[0][1], + core, }); await testPolygonCopiedTwice({ vertices, core }); @@ -5868,13 +5106,10 @@ describe("Polygon tag tests", async () => { 0.5 * (v[1] - centroid[1]) + centroid[1], ]); - await core.requestAction({ - actionName: "movePolygon", - componentName: "/g1/pg", - args: { - pointCoords: { 1: requested_vertex_1 }, - }, - event: null, + await movePolygon({ + name: "/g1/pg", + pointCoords: { 1: requested_vertex_1 }, + core, }); await testPolygonCopiedTwice({ vertices, core }); @@ -5894,13 +5129,10 @@ describe("Polygon tag tests", async () => { ]); } - await core.requestAction({ - actionName: "movePolygon", - componentName: "/g2/pg", - args: { - pointCoords: requested_vertices, - }, - event: null, + await movePolygon({ + name: "/g2/pg", + pointCoords: requested_vertices, + core, }); await testPolygonCopiedTwice({ vertices, core }); @@ -5933,13 +5165,10 @@ describe("Polygon tag tests", async () => { shrink_factor * (v[1] - centroid[1]) + centroid[1], ]); - await core.requestAction({ - actionName: "movePolygon", - componentName: "/g3/pg", - args: { - pointCoords: { 2: requested_vertex_2 }, - }, - event: null, + await movePolygon({ + name: "/g3/pg", + pointCoords: { 2: requested_vertex_2 }, + core, }); await testPolygonCopiedTwice({ vertices, core }); @@ -5973,11 +5202,11 @@ describe("Polygon tag tests", async () => { 10 * (v[1] - centroid[1]) + centroid[1], ]); - await core.requestAction({ - actionName: "movePoint", - componentName: "/g2/v4", - args: { x: requested_vertex_3[0], y: requested_vertex_3[1] }, - event: null, + await movePoint({ + name: "/g2/v4", + x: requested_vertex_3[0], + y: requested_vertex_3[1], + core, }); await testPolygonCopiedTwice({ vertices, core }); @@ -5985,11 +5214,11 @@ describe("Polygon tag tests", async () => { // moving defining vertex deforms polygon vertices[0] = [4, 6]; - await core.requestAction({ - actionName: "movePoint", - componentName: "/g1/_point1", - args: { x: vertices[0][0], y: vertices[0][1] }, - event: null, + await movePoint({ + name: "/g1/_point1", + x: vertices[0][0], + y: vertices[0][1], + core, }); await testPolygonCopiedTwice({ vertices, core }); @@ -6041,13 +5270,10 @@ describe("Polygon tag tests", async () => { 0.5 * (v[0] - centroid[0]) + centroid[1], ]); - await core.requestAction({ - actionName: "movePolygon", - componentName: "/g1/pg", - args: { - pointCoords: { 1: requested_vertex_1 }, - }, - event: null, + await movePolygon({ + name: "/g1/pg", + pointCoords: { 1: requested_vertex_1 }, + core, }); await testPolygonCopiedTwice({ vertices, core }); @@ -6065,13 +5291,10 @@ describe("Polygon tag tests", async () => { ]); } - await core.requestAction({ - actionName: "movePolygon", - componentName: "/g2/pg", - args: { - pointCoords: requested_vertices, - }, - event: null, + await movePolygon({ + name: "/g2/pg", + pointCoords: requested_vertices, + core, }); await testPolygonCopiedTwice({ vertices, core }); @@ -6096,13 +5319,10 @@ describe("Polygon tag tests", async () => { -2 * (v[1] - centroid[1]) + centroid[1], ]); - await core.requestAction({ - actionName: "movePolygon", - componentName: "/g3/pg", - args: { - pointCoords: { 2: requested_vertex_2 }, - }, - event: null, + await movePolygon({ + name: "/g3/pg", + pointCoords: { 2: requested_vertex_2 }, + core, }); await testPolygonCopiedTwice({ vertices, core }); @@ -6126,11 +5346,11 @@ describe("Polygon tag tests", async () => { -0.25 * (v[0] - centroid[0]) + centroid[1], ]); - await core.requestAction({ - actionName: "movePoint", - componentName: "/g2/v4", - args: { x: requested_vertex_3[0], y: requested_vertex_3[1] }, - event: null, + await movePoint({ + name: "/g2/v4", + x: requested_vertex_3[0], + y: requested_vertex_3[1], + core, }); await testPolygonCopiedTwice({ vertices, core }); @@ -6138,11 +5358,11 @@ describe("Polygon tag tests", async () => { // moving defining vertex deforms polygon vertices[0] = [4, 6]; - await core.requestAction({ - actionName: "movePoint", - componentName: "/g1/_point1", - args: { x: vertices[0][0], y: vertices[0][1] }, - event: null, + await movePoint({ + name: "/g1/_point1", + x: vertices[0][0], + y: vertices[0][1], + core, }); await testPolygonCopiedTwice({ vertices, core }); @@ -6188,13 +5408,10 @@ describe("Polygon tag tests", async () => { vertices[i][1] = vertices[i][1] + moveY; } - await core.requestAction({ - actionName: "movePolygon", - componentName: "/g1/pg", - args: { - pointCoords: { 1: requested_vertex_1 }, - }, - event: null, + await movePolygon({ + name: "/g1/pg", + pointCoords: { 1: requested_vertex_1 }, + core, }); await testPolygonCopiedTwice({ vertices, core }); @@ -6214,13 +5431,10 @@ describe("Polygon tag tests", async () => { ]); } - await core.requestAction({ - actionName: "movePolygon", - componentName: "/g2/pg", - args: { - pointCoords: requested_vertices, - }, - event: null, + await movePolygon({ + name: "/g2/pg", + pointCoords: requested_vertices, + core, }); await testPolygonCopiedTwice({ vertices, core }); @@ -6238,13 +5452,10 @@ describe("Polygon tag tests", async () => { vertices[i][1] = vertices[i][1] + moveY; } - await core.requestAction({ - actionName: "movePolygon", - componentName: "/g3/pg", - args: { - pointCoords: { 2: requested_vertex_2 }, - }, - event: null, + await movePolygon({ + name: "/g3/pg", + pointCoords: { 2: requested_vertex_2 }, + core, }); await testPolygonCopiedTwice({ vertices, core }); @@ -6262,11 +5473,11 @@ describe("Polygon tag tests", async () => { vertices[i][1] = vertices[i][1] + moveY; } - await core.requestAction({ - actionName: "movePoint", - componentName: "/g2/v4", - args: { x: requested_vertex_3[0], y: requested_vertex_3[1] }, - event: null, + await movePoint({ + name: "/g2/v4", + x: requested_vertex_3[0], + y: requested_vertex_3[1], + core, }); await testPolygonCopiedTwice({ vertices, core }); @@ -6274,11 +5485,11 @@ describe("Polygon tag tests", async () => { // moving defining vertex deforms polygon vertices[0] = [4, 6]; - await core.requestAction({ - actionName: "movePoint", - componentName: "/g1/_point1", - args: { x: vertices[0][0], y: vertices[0][1] }, - event: null, + await movePoint({ + name: "/g1/_point1", + x: vertices[0][0], + y: vertices[0][1], + core, }); await testPolygonCopiedTwice({ vertices, core }); @@ -6327,13 +5538,10 @@ describe("Polygon tag tests", async () => { 0.5 * (vertices[1][0] - centroid[0]) + centroid[1], ]; - await core.requestAction({ - actionName: "movePolygon", - componentName: "/g1/pg", - args: { - pointCoords: { 1: requested_vertex_1 }, - }, - event: null, + await movePolygon({ + name: "/g1/pg", + pointCoords: { 1: requested_vertex_1 }, + core, }); await testPolygonCopiedTwice({ vertices, core }); @@ -6351,13 +5559,10 @@ describe("Polygon tag tests", async () => { ]); } - await core.requestAction({ - actionName: "movePolygon", - componentName: "/g2/pg", - args: { - pointCoords: requested_vertices, - }, - event: null, + await movePolygon({ + name: "/g2/pg", + pointCoords: requested_vertices, + core, }); await testPolygonCopiedTwice({ vertices, core }); @@ -6378,13 +5583,10 @@ describe("Polygon tag tests", async () => { -2 * (vertices[2][1] - centroid[1]) + centroid[1], ]; - await core.requestAction({ - actionName: "movePolygon", - componentName: "/g3/pg", - args: { - pointCoords: { 2: requested_vertex_2 }, - }, - event: null, + await movePolygon({ + name: "/g3/pg", + pointCoords: { 2: requested_vertex_2 }, + core, }); await testPolygonCopiedTwice({ vertices, core }); @@ -6405,11 +5607,11 @@ describe("Polygon tag tests", async () => { -0.25 * (vertices[3][0] - centroid[0]) + centroid[1], ]; - await core.requestAction({ - actionName: "movePoint", - componentName: "/g2/v4", - args: { x: requested_vertex_3[0], y: requested_vertex_3[1] }, - event: null, + await movePoint({ + name: "/g2/v4", + x: requested_vertex_3[0], + y: requested_vertex_3[1], + core, }); await testPolygonCopiedTwice({ vertices, core }); @@ -6417,11 +5619,11 @@ describe("Polygon tag tests", async () => { // moving defining vertex deforms polygon vertices[0] = [4, 6]; - await core.requestAction({ - actionName: "movePoint", - componentName: "/g1/_point1", - args: { x: vertices[0][0], y: vertices[0][1] }, - event: null, + await movePoint({ + name: "/g1/_point1", + x: vertices[0][0], + y: vertices[0][1], + core, }); await testPolygonCopiedTwice({ vertices, core }); @@ -6468,13 +5670,10 @@ describe("Polygon tag tests", async () => { 0.5 * (v[0] - rotationPoint[0]) + rotationPoint[1], ]); - await core.requestAction({ - actionName: "movePolygon", - componentName: "/g1/pg", - args: { - pointCoords: { 0: requested_vertex_0 }, - }, - event: null, + await movePolygon({ + name: "/g1/pg", + pointCoords: { 0: requested_vertex_0 }, + core, }); await testPolygonCopiedTwice({ vertices, core }); @@ -6494,13 +5693,10 @@ describe("Polygon tag tests", async () => { ]); } - await core.requestAction({ - actionName: "movePolygon", - componentName: "/g2/pg", - args: { - pointCoords: requested_vertices, - }, - event: null, + await movePolygon({ + name: "/g2/pg", + pointCoords: requested_vertices, + core, }); await testPolygonCopiedTwice({ vertices, core }); @@ -6520,13 +5716,10 @@ describe("Polygon tag tests", async () => { -2 * (v[1] - rotationPoint[1]) + rotationPoint[1], ]); - await core.requestAction({ - actionName: "movePolygon", - componentName: "/g3/pg", - args: { - pointCoords: { 2: requested_vertex_2 }, - }, - event: null, + await movePolygon({ + name: "/g3/pg", + pointCoords: { 2: requested_vertex_2 }, + core, }); await testPolygonCopiedTwice({ vertices, core }); @@ -6545,11 +5738,11 @@ describe("Polygon tag tests", async () => { -0.25 * (v[0] - rotationPoint[0]) + rotationPoint[1], ]); - await core.requestAction({ - actionName: "movePoint", - componentName: "/g2/v4", - args: { x: requested_vertex_3[0], y: requested_vertex_3[1] }, - event: null, + await movePoint({ + name: "/g2/v4", + x: requested_vertex_3[0], + y: requested_vertex_3[1], + core, }); await testPolygonCopiedTwice({ vertices, core }); @@ -6557,11 +5750,11 @@ describe("Polygon tag tests", async () => { // moving defining vertex deforms polygon vertices[0] = [4, 6]; - await core.requestAction({ - actionName: "movePoint", - componentName: "/g1/_point1", - args: { x: vertices[0][0], y: vertices[0][1] }, - event: null, + await movePoint({ + name: "/g1/_point1", + x: vertices[0][0], + y: vertices[0][1], + core, }); await testPolygonCopiedTwice({ vertices, core }); @@ -6610,13 +5803,10 @@ describe("Polygon tag tests", async () => { 0.5 * (v[0] - rotationPoint[0]) + rotationPoint[1], ]); - await core.requestAction({ - actionName: "movePolygon", - componentName: "/g1/pg", - args: { - pointCoords: { 0: requested_vertex_0 }, - }, - event: null, + await movePolygon({ + name: "/g1/pg", + pointCoords: { 0: requested_vertex_0 }, + core, }); await testPolygonCopiedTwice({ vertices, core }); @@ -6636,13 +5826,10 @@ describe("Polygon tag tests", async () => { ]); } - await core.requestAction({ - actionName: "movePolygon", - componentName: "/g2/pg", - args: { - pointCoords: requested_vertices, - }, - event: null, + await movePolygon({ + name: "/g2/pg", + pointCoords: requested_vertices, + core, }); await testPolygonCopiedTwice({ vertices, core }); @@ -6662,25 +5849,17 @@ describe("Polygon tag tests", async () => { -2 * (v[1] - rotationPoint[1]) + rotationPoint[1], ]); - await core.requestAction({ - actionName: "movePolygon", - componentName: "/g3/pg", - args: { - pointCoords: { 2: requested_vertex_2 }, - }, - event: null, + await movePolygon({ + name: "/g3/pg", + pointCoords: { 2: requested_vertex_2 }, + core, }); await testPolygonCopiedTwice({ vertices, core }); // change rotation point, then moving single copied vertex gets rotation and dilation - await core.requestAction({ - actionName: "movePoint", - componentName: "/g1/rotationPoint", - args: { x: 6, y: -2 }, - event: null, - }); + await movePoint({ name: "/g1/rotationPoint", x: 6, y: -2, core }); rotationPoint = [6, -2]; @@ -6695,11 +5874,11 @@ describe("Polygon tag tests", async () => { -0.25 * (v[0] - rotationPoint[0]) + rotationPoint[1], ]); - await core.requestAction({ - actionName: "movePoint", - componentName: "/g2/v4", - args: { x: requested_vertex_3[0], y: requested_vertex_3[1] }, - event: null, + await movePoint({ + name: "/g2/v4", + x: requested_vertex_3[0], + y: requested_vertex_3[1], + core, }); await testPolygonCopiedTwice({ vertices, core }); @@ -6707,11 +5886,11 @@ describe("Polygon tag tests", async () => { // moving defining vertex deforms polygon vertices[0] = [4, 6]; - await core.requestAction({ - actionName: "movePoint", - componentName: "/g1/_point1", - args: { x: vertices[0][0], y: vertices[0][1] }, - event: null, + await movePoint({ + name: "/g1/_point1", + x: vertices[0][0], + y: vertices[0][1], + core, }); await testPolygonCopiedTwice({ vertices, core }); diff --git a/packages/doenetml-worker/src/test/tagSpecific/polyline.test.ts b/packages/doenetml-worker/src/test/tagSpecific/polyline.test.ts index 413dcf9d9..d4be4d01c 100644 --- a/packages/doenetml-worker/src/test/tagSpecific/polyline.test.ts +++ b/packages/doenetml-worker/src/test/tagSpecific/polyline.test.ts @@ -1,13 +1,17 @@ import { describe, expect, it, vi } from "vitest"; import { createTestCore, returnAllStateVariables } from "../utils/test-core"; import { + movePoint, + movePolyline, updateBooleanInputValue, updateMathInputValue, } from "../utils/actions"; import me from "math-expressions"; +import Core from "../../Core"; const Mock = vi.fn(); vi.stubGlobal("postMessage", Mock); +vi.mock("hyperformula"); async function testPolylineCopiedTwice({ core, @@ -17,7 +21,7 @@ async function testPolylineCopiedTwice({ graph2Name = "/g2", graph3Name = "/g3", }: { - core: any; + core: Core; vertices: (number | string)[][]; polylineName?: string; graph1Name?: string; @@ -159,13 +163,10 @@ describe("Polyline tag tests", async () => { // move individual vertex vertices[1] = [4, 7]; - await core.requestAction({ - actionName: "movePolyline", - componentName: "/g1/pg", - args: { - pointCoords: { 1: vertices[1] }, - }, - event: null, + await movePolyline({ + name: "/g1/pg", + pointCoords: { 1: vertices[1] }, + core, }); await testPolylineCopiedTwice({ core, vertices }); @@ -179,27 +180,17 @@ describe("Polyline tag tests", async () => { vertices[i][1] = vertices[i][1] + moveY; } - await core.requestAction({ - actionName: "movePolyline", - componentName: "/g2/pg", - args: { - pointCoords: vertices, - }, - event: null, - }); + await movePolyline({ name: "/g2/pg", pointCoords: vertices, core }); await testPolylineCopiedTwice({ core, vertices }); // move double copied individual vertex vertices[2] = [-9, -8]; - await core.requestAction({ - actionName: "movePolyline", - componentName: "/g3/pg", - args: { - pointCoords: { 2: vertices[2] }, - }, - event: null, + await movePolyline({ + name: "/g3/pg", + pointCoords: { 2: vertices[2] }, + core, }); await testPolylineCopiedTwice({ core, vertices }); @@ -231,13 +222,10 @@ describe("Polyline tag tests", async () => { // move individual vertex vertices[1] = [4, 7]; - await core.requestAction({ - actionName: "movePolyline", - componentName: "/g1/pg", - args: { - pointCoords: { 1: vertices[1] }, - }, - event: null, + await movePolyline({ + name: "/g1/pg", + pointCoords: { 1: vertices[1] }, + core, }); await testPolylineCopiedTwice({ core, vertices }); @@ -251,27 +239,17 @@ describe("Polyline tag tests", async () => { vertices[i][1] = vertices[i][1] + moveY; } - await core.requestAction({ - actionName: "movePolyline", - componentName: "/g2/pg", - args: { - pointCoords: vertices, - }, - event: null, - }); + await movePolyline({ name: "/g2/pg", pointCoords: vertices, core }); await testPolylineCopiedTwice({ core, vertices }); // move double copied individual vertex vertices[2] = [-9, -8]; - await core.requestAction({ - actionName: "movePolyline", - componentName: "/g3/pg", - args: { - pointCoords: { 2: vertices[2] }, - }, - event: null, + await movePolyline({ + name: "/g3/pg", + pointCoords: { 2: vertices[2] }, + core, }); await testPolylineCopiedTwice({ core, vertices }); @@ -300,7 +278,7 @@ describe("Polyline tag tests", async () => { await updateMathInputValue({ latex: "1", - componentName: "/length", + name: "/length", core, }); vertices[0] = [0, 5 * Math.sin(0)]; @@ -308,7 +286,7 @@ describe("Polyline tag tests", async () => { await updateMathInputValue({ latex: "2", - componentName: "/length", + name: "/length", core, }); vertices[1] = [1, 5 * Math.sin(1)]; @@ -316,7 +294,7 @@ describe("Polyline tag tests", async () => { await updateMathInputValue({ latex: "3", - componentName: "/length", + name: "/length", core, }); vertices[2] = [2, 5 * Math.sin(2)]; @@ -324,7 +302,7 @@ describe("Polyline tag tests", async () => { await updateMathInputValue({ latex: "2", - componentName: "/length", + name: "/length", core, }); vertices.splice(2, 1); @@ -332,7 +310,7 @@ describe("Polyline tag tests", async () => { await updateMathInputValue({ latex: "0", - componentName: "/length", + name: "/length", core, }); vertices = []; @@ -340,7 +318,7 @@ describe("Polyline tag tests", async () => { await updateMathInputValue({ latex: "5", - componentName: "/length", + name: "/length", core, }); for (let i = 0; i < 5; i++) { @@ -372,7 +350,7 @@ describe("Polyline tag tests", async () => { await updateMathInputValue({ latex: "10", - componentName: "/length", + name: "/length", core, }); for (let i = 0; i < 10; i++) { @@ -382,7 +360,7 @@ describe("Polyline tag tests", async () => { await updateMathInputValue({ latex: "1", - componentName: "/length", + name: "/length", core, }); vertices = [[0, 5 * Math.sin(0)]]; @@ -415,7 +393,7 @@ describe("Polyline tag tests", async () => { await updateMathInputValue({ latex: "-2", - componentName: "/mi", + name: "/mi", core, }); @@ -447,18 +425,8 @@ describe("Polyline tag tests", async () => { await testPolylineCopiedTwice({ core, vertices }); // can't move points - await core.requestAction({ - actionName: "movePoint", - componentName: "/g1/mp1", - args: { x: 9, y: -8 }, - event: null, - }); - await core.requestAction({ - actionName: "movePoint", - componentName: "/g1/mp9", - args: { x: -8, y: 4 }, - event: null, - }); + await movePoint({ name: "/g1/mp1", x: 9, y: -8, core }); + await movePoint({ name: "/g1/mp9", x: -8, y: 4, core }); // can't move polyline1 let moveX = 3; @@ -466,14 +434,7 @@ describe("Polyline tag tests", async () => { let vertices2 = vertices.map((v) => [v[0] + moveX, v[1] + moveY]); - await core.requestAction({ - actionName: "movePolyline", - componentName: "/g1/pg", - args: { - pointCoords: vertices2, - }, - event: null, - }); + await movePolyline({ name: "/g1/pg", pointCoords: vertices2, core }); await testPolylineCopiedTwice({ core, vertices }); @@ -483,14 +444,7 @@ describe("Polyline tag tests", async () => { vertices2 = vertices.map((v) => [v[0] + moveX, v[1] + moveY]); - await core.requestAction({ - actionName: "movePolyline", - componentName: "/g2/pg", - args: { - pointCoords: vertices2, - }, - event: null, - }); + await movePolyline({ name: "/g2/pg", pointCoords: vertices2, core }); await testPolylineCopiedTwice({ core, vertices }); @@ -500,14 +454,7 @@ describe("Polyline tag tests", async () => { vertices2 = vertices.map((v) => [v[0] + moveX, v[1] + moveY]); - await core.requestAction({ - actionName: "movePolyline", - componentName: "/g3/pg", - args: { - pointCoords: vertices2, - }, - event: null, - }); + await movePolyline({ name: "/g3/pg", pointCoords: vertices2, core }); await testPolylineCopiedTwice({ core, vertices }); }); @@ -540,17 +487,17 @@ describe("Polyline tag tests", async () => { vertices[0] = [9, -8]; vertices[8] = [-8, 4]; - await core.requestAction({ - actionName: "movePoint", - componentName: "/g1/mp1", - args: { x: vertices[0][0], y: vertices[0][1] }, - event: null, + await movePoint({ + name: "/g1/mp1", + x: vertices[0][0], + y: vertices[0][1], + core, }); - await core.requestAction({ - actionName: "movePoint", - componentName: "/g1/mp9", - args: { x: vertices[8][0], y: vertices[8][1] }, - event: null, + await movePoint({ + name: "/g1/mp9", + x: vertices[8][0], + y: vertices[8][1], + core, }); await testPolylineCopiedTwice({ core, vertices }); @@ -564,14 +511,7 @@ describe("Polyline tag tests", async () => { vertices[i][1] += moveY; } - await core.requestAction({ - actionName: "movePolyline", - componentName: "/g1/pg", - args: { - pointCoords: vertices, - }, - event: null, - }); + await movePolyline({ name: "/g1/pg", pointCoords: vertices, core }); await testPolylineCopiedTwice({ core, vertices }); @@ -584,14 +524,7 @@ describe("Polyline tag tests", async () => { vertices[i][1] += moveY; } - await core.requestAction({ - actionName: "movePolyline", - componentName: "/g2/pg", - args: { - pointCoords: vertices, - }, - event: null, - }); + await movePolyline({ name: "/g2/pg", pointCoords: vertices, core }); await testPolylineCopiedTwice({ core, vertices }); @@ -603,14 +536,7 @@ describe("Polyline tag tests", async () => { vertices[i][1] += moveY; } - await core.requestAction({ - actionName: "movePolyline", - componentName: "/g2/pg", - args: { - pointCoords: vertices, - }, - event: null, - }); + await movePolyline({ name: "/g2/pg", pointCoords: vertices, core }); await testPolylineCopiedTwice({ core, vertices }); }); @@ -665,11 +591,11 @@ describe("Polyline tag tests", async () => { ]; for (let i = 0; i < 4; i++) { - await core.requestAction({ - actionName: "movePoint", - componentName: `/v${i + 1}`, - args: { x: ps[i][0], y: ps[i][1] }, - event: null, + await movePoint({ + name: `/v${i + 1}`, + x: ps[i][0], + y: ps[i][1], + core, }); } @@ -698,11 +624,11 @@ describe("Polyline tag tests", async () => { ]; for (let i = 0; i < 4; i++) { - await core.requestAction({ - actionName: "movePoint", - componentName: `/v${i + 1}a`, - args: { x: ps[i][0], y: ps[i][1] }, - event: null, + await movePoint({ + name: `/v${i + 1}a`, + x: ps[i][0], + y: ps[i][1], + core, }); } @@ -754,14 +680,7 @@ describe("Polyline tag tests", async () => { vertices[i][1] += moveY; } - await core.requestAction({ - actionName: "movePolyline", - componentName: "/g1/pg", - args: { - pointCoords: vertices, - }, - event: null, - }); + await movePolyline({ name: "/g1/pg", pointCoords: vertices, core }); await testPolylineCopiedTwice({ core, vertices }); @@ -774,14 +693,7 @@ describe("Polyline tag tests", async () => { vertices[i][1] += moveY; } - await core.requestAction({ - actionName: "movePolyline", - componentName: "/g2/pg", - args: { - pointCoords: vertices, - }, - event: null, - }); + await movePolyline({ name: "/g2/pg", pointCoords: vertices, core }); await testPolylineCopiedTwice({ core, vertices }); @@ -794,14 +706,7 @@ describe("Polyline tag tests", async () => { vertices[i][1] += moveY; } - await core.requestAction({ - actionName: "movePolyline", - componentName: "/g3/pg", - args: { - pointCoords: vertices, - }, - event: null, - }); + await movePolyline({ name: "/g3/pg", pointCoords: vertices, core }); await testPolylineCopiedTwice({ core, vertices }); }); @@ -920,13 +825,10 @@ describe("Polyline tag tests", async () => { [6, 3], ]; - await core.requestAction({ - actionName: "movePolyline", - componentName: "/_polyline1", - args: { - pointCoords: vertices, - }, - event: null, + await movePolyline({ + name: "/_polyline1", + pointCoords: vertices, + core, }); await testPolylines({ vertices, transX, transY }); @@ -939,13 +841,10 @@ describe("Polyline tag tests", async () => { [2, -1], ]; - await core.requestAction({ - actionName: "movePolyline", - componentName: "/_polyline2", - args: { - pointCoords: vertices2, - }, - event: null, + await movePolyline({ + name: "/_polyline2", + pointCoords: vertices2, + core, }); vertices = vertices2.map((v) => [v[0] - transX, v[1] - transY]); @@ -955,12 +854,12 @@ describe("Polyline tag tests", async () => { // change translation await updateMathInputValue({ latex: "2", - componentName: "/transx", + name: "/transx", core, }); await updateMathInputValue({ latex: "10", - componentName: "/transy", + name: "/transy", core, }); @@ -1010,13 +909,10 @@ describe("Polyline tag tests", async () => { A = [-4, -1]; D = [A[0] + C[0] - B[0], A[1] + C[1] - B[1]]; - await core.requestAction({ - actionName: "movePolyline", - componentName: "/parallelogram", - args: { - pointCoords: { 0: A }, - }, - event: null, + await movePolyline({ + name: "/parallelogram", + pointCoords: { 0: A }, + core, }); stateVariables = await returnAllStateVariables(core); @@ -1044,13 +940,10 @@ describe("Polyline tag tests", async () => { B = [8, 9]; D = [A[0] + C[0] - B[0], A[1] + C[1] - B[1]]; - await core.requestAction({ - actionName: "movePolyline", - componentName: "/parallelogram", - args: { - pointCoords: { 1: B }, - }, - event: null, + await movePolyline({ + name: "/parallelogram", + pointCoords: { 1: B }, + core, }); stateVariables = await returnAllStateVariables(core); @@ -1079,13 +972,10 @@ describe("Polyline tag tests", async () => { C = [-3, 7]; D = [A[0] + C[0] - B[0], A[1] + C[1] - B[1]]; - await core.requestAction({ - actionName: "movePolyline", - componentName: "/parallelogram", - args: { - pointCoords: { 2: C }, - }, - event: null, + await movePolyline({ + name: "/parallelogram", + pointCoords: { 2: C }, + core, }); stateVariables = await returnAllStateVariables(core); @@ -1114,13 +1004,10 @@ describe("Polyline tag tests", async () => { D = [7, 0]; B = [A[0] + C[0] - D[0], A[1] + C[1] - D[1]]; - await core.requestAction({ - actionName: "movePolyline", - componentName: "/parallelogram", - args: { - pointCoords: { 3: D }, - }, - event: null, + await movePolyline({ + name: "/parallelogram", + pointCoords: { 3: D }, + core, }); stateVariables = await returnAllStateVariables(core); @@ -1246,13 +1133,10 @@ describe("Polyline tag tests", async () => { [-4, -3], ]; - await core.requestAction({ - actionName: "movePolyline", - componentName: "/_polyline1", - args: { - pointCoords: vertices, - }, - event: null, + await movePolyline({ + name: "/_polyline1", + pointCoords: vertices, + core, }); await testPolylines({ vertices }); @@ -1265,13 +1149,10 @@ describe("Polyline tag tests", async () => { [-7, 6], ]; - await core.requestAction({ - actionName: "movePolyline", - componentName: "/_polyline2", - args: { - pointCoords: vertices2, - }, - event: null, + await movePolyline({ + name: "/_polyline2", + pointCoords: vertices2, + core, }); vertices = [...vertices2]; @@ -1322,13 +1203,10 @@ describe("Polyline tag tests", async () => { A = [-4, -1]; D = [C[0] + B[0] - A[0], C[1] + B[1] - A[1]]; - await core.requestAction({ - actionName: "movePolyline", - componentName: "/_polyline1", - args: { - pointCoords: { 0: A }, - }, - event: null, + await movePolyline({ + name: "/_polyline1", + pointCoords: { 0: A }, + core, }); stateVariables = await returnAllStateVariables(core); @@ -1357,13 +1235,10 @@ describe("Polyline tag tests", async () => { B = [8, 9]; D = [C[0] + B[0] - A[0], C[1] + B[1] - A[1]]; - await core.requestAction({ - actionName: "movePolyline", - componentName: "/_polyline1", - args: { - pointCoords: { 1: B }, - }, - event: null, + await movePolyline({ + name: "/_polyline1", + pointCoords: { 1: B }, + core, }); stateVariables = await returnAllStateVariables(core); @@ -1392,13 +1267,10 @@ describe("Polyline tag tests", async () => { C = [-3, 7]; D = [C[0] + B[0] - A[0], C[1] + B[1] - A[1]]; - await core.requestAction({ - actionName: "movePolyline", - componentName: "/_polyline1", - args: { - pointCoords: { 2: C }, - }, - event: null, + await movePolyline({ + name: "/_polyline1", + pointCoords: { 2: C }, + core, }); stateVariables = await returnAllStateVariables(core); @@ -1427,13 +1299,10 @@ describe("Polyline tag tests", async () => { D = [7, 0]; A = [C[0] + B[0] - D[0], C[1] + B[1] - D[1]]; - await core.requestAction({ - actionName: "movePolyline", - componentName: "/_polyline1", - args: { - pointCoords: { 3: D }, - }, - event: null, + await movePolyline({ + name: "/_polyline1", + pointCoords: { 3: D }, + core, }); stateVariables = await returnAllStateVariables(core); @@ -1498,13 +1367,10 @@ describe("Polyline tag tests", async () => { // move first vertex A = [-4, -1]; - await core.requestAction({ - actionName: "movePolyline", - componentName: "/_polyline1", - args: { - pointCoords: { 0: A }, - }, - event: null, + await movePolyline({ + name: "/_polyline1", + pointCoords: { 0: A }, + core, }); stateVariables = await returnAllStateVariables(core); @@ -1532,13 +1398,10 @@ describe("Polyline tag tests", async () => { // move second vertex B = [8, 9]; - await core.requestAction({ - actionName: "movePolyline", - componentName: "/_polyline1", - args: { - pointCoords: { 1: B }, - }, - event: null, + await movePolyline({ + name: "/_polyline1", + pointCoords: { 1: B }, + core, }); stateVariables = await returnAllStateVariables(core); @@ -1566,13 +1429,10 @@ describe("Polyline tag tests", async () => { // move third vertex C = [-3, 7]; - await core.requestAction({ - actionName: "movePolyline", - componentName: "/_polyline1", - args: { - pointCoords: { 2: C }, - }, - event: null, + await movePolyline({ + name: "/_polyline1", + pointCoords: { 2: C }, + core, }); stateVariables = await returnAllStateVariables(core); @@ -1599,13 +1459,10 @@ describe("Polyline tag tests", async () => { // move fourth vertex A = [7, 0]; - await core.requestAction({ - actionName: "movePolyline", - componentName: "/_polyline1", - args: { - pointCoords: { 3: A }, - }, - event: null, + await movePolyline({ + name: "/_polyline1", + pointCoords: { 3: A }, + core, }); stateVariables = await returnAllStateVariables(core); @@ -1671,13 +1528,10 @@ describe("Polyline tag tests", async () => { // move first vertex A = [-4, -1]; - await core.requestAction({ - actionName: "movePolyline", - componentName: "/_polyline1", - args: { - pointCoords: { 0: A }, - }, - event: null, + await movePolyline({ + name: "/_polyline1", + pointCoords: { 0: A }, + core, }); stateVariables = await returnAllStateVariables(core); @@ -1705,13 +1559,10 @@ describe("Polyline tag tests", async () => { // move second vertex B = [8, 9]; - await core.requestAction({ - actionName: "movePolyline", - componentName: "/_polyline1", - args: { - pointCoords: { 1: B }, - }, - event: null, + await movePolyline({ + name: "/_polyline1", + pointCoords: { 1: B }, + core, }); stateVariables = await returnAllStateVariables(core); @@ -1739,13 +1590,10 @@ describe("Polyline tag tests", async () => { // move third vertex C = [-3, 7]; - await core.requestAction({ - actionName: "movePolyline", - componentName: "/_polyline1", - args: { - pointCoords: { 2: C }, - }, - event: null, + await movePolyline({ + name: "/_polyline1", + pointCoords: { 2: C }, + core, }); stateVariables = await returnAllStateVariables(core); @@ -1773,13 +1621,10 @@ describe("Polyline tag tests", async () => { // move fourth vertex A = [7, 0]; - await core.requestAction({ - actionName: "movePolyline", - componentName: "/_polyline1", - args: { - pointCoords: { 3: A }, - }, - event: null, + await movePolyline({ + name: "/_polyline1", + pointCoords: { 3: A }, + core, }); stateVariables = await returnAllStateVariables(core); @@ -1853,13 +1698,10 @@ describe("Polyline tag tests", async () => { A = [-4, -1]; D[0] = A[0] + 1; - await core.requestAction({ - actionName: "movePolyline", - componentName: "/_polyline1", - args: { - pointCoords: { 0: A }, - }, - event: null, + await movePolyline({ + name: "/_polyline1", + pointCoords: { 0: A }, + core, }); stateVariables = await returnAllStateVariables(core); @@ -1892,13 +1734,10 @@ describe("Polyline tag tests", async () => { // move second vertex B = [8, 9]; - await core.requestAction({ - actionName: "movePolyline", - componentName: "/_polyline1", - args: { - pointCoords: { 1: B }, - }, - event: null, + await movePolyline({ + name: "/_polyline1", + pointCoords: { 1: B }, + core, }); stateVariables = await returnAllStateVariables(core); @@ -1931,13 +1770,10 @@ describe("Polyline tag tests", async () => { // move third vertex C = [-3, 7]; - await core.requestAction({ - actionName: "movePolyline", - componentName: "/_polyline1", - args: { - pointCoords: { 2: C }, - }, - event: null, + await movePolyline({ + name: "/_polyline1", + pointCoords: { 2: C }, + core, }); stateVariables = await returnAllStateVariables(core); @@ -1971,13 +1807,10 @@ describe("Polyline tag tests", async () => { A = [7, 0]; D[0] = A[0] + 1; - await core.requestAction({ - actionName: "movePolyline", - componentName: "/_polyline1", - args: { - pointCoords: { 3: A }, - }, - event: null, + await movePolyline({ + name: "/_polyline1", + pointCoords: { 3: A }, + core, }); stateVariables = await returnAllStateVariables(core); @@ -2011,13 +1844,10 @@ describe("Polyline tag tests", async () => { D = [-5, 9]; A[0] = D[0] - 1; - await core.requestAction({ - actionName: "movePolyline", - componentName: "/_polyline1", - args: { - pointCoords: { 4: D }, - }, - event: null, + await movePolyline({ + name: "/_polyline1", + pointCoords: { 4: D }, + core, }); stateVariables = await returnAllStateVariables(core); @@ -2101,14 +1931,7 @@ describe("Polyline tag tests", async () => { // move first vertex A = [-4, -9]; - await core.requestAction({ - actionName: "movePolyline", - componentName: "/P", - args: { - pointCoords: { 0: A }, - }, - event: null, - }); + await movePolyline({ name: "/P", pointCoords: { 0: A }, core }); stateVariables = await returnAllStateVariables(core); expect( @@ -2145,14 +1968,7 @@ describe("Polyline tag tests", async () => { // move second vertex B = [8, 9]; - await core.requestAction({ - actionName: "movePolyline", - componentName: "/P", - args: { - pointCoords: { 1: B }, - }, - event: null, - }); + await movePolyline({ name: "/P", pointCoords: { 1: B }, core }); stateVariables = await returnAllStateVariables(core); expect( @@ -2189,14 +2005,7 @@ describe("Polyline tag tests", async () => { // move third vertex C = [-3, 7]; - await core.requestAction({ - actionName: "movePolyline", - componentName: "/P", - args: { - pointCoords: { 2: C }, - }, - event: null, - }); + await movePolyline({ name: "/P", pointCoords: { 2: C }, core }); stateVariables = await returnAllStateVariables(core); expect( @@ -2233,14 +2042,7 @@ describe("Polyline tag tests", async () => { // move fourth vertex A = [7, 0]; - await core.requestAction({ - actionName: "movePolyline", - componentName: "/P", - args: { - pointCoords: { 3: A }, - }, - event: null, - }); + await movePolyline({ name: "/P", pointCoords: { 3: A }, core }); stateVariables = await returnAllStateVariables(core); expect( @@ -2277,14 +2079,7 @@ describe("Polyline tag tests", async () => { // move fifth vertex D = [-9, 1]; - await core.requestAction({ - actionName: "movePolyline", - componentName: "/P", - args: { - pointCoords: { 4: D }, - }, - event: null, - }); + await movePolyline({ name: "/P", pointCoords: { 4: D }, core }); stateVariables = await returnAllStateVariables(core); expect( @@ -2321,14 +2116,7 @@ describe("Polyline tag tests", async () => { // move sixth vertex E = [-3, 6]; - await core.requestAction({ - actionName: "movePolyline", - componentName: "/P", - args: { - pointCoords: { 5: E }, - }, - event: null, - }); + await movePolyline({ name: "/P", pointCoords: { 5: E }, core }); stateVariables = await returnAllStateVariables(core); expect( @@ -2365,14 +2153,7 @@ describe("Polyline tag tests", async () => { // move seventh vertex A = [2, -4]; - await core.requestAction({ - actionName: "movePolyline", - componentName: "/P", - args: { - pointCoords: { 6: A }, - }, - event: null, - }); + await movePolyline({ name: "/P", pointCoords: { 6: A }, core }); stateVariables = await returnAllStateVariables(core); expect( @@ -2409,14 +2190,7 @@ describe("Polyline tag tests", async () => { // move eighth vertex F = [6, 7]; - await core.requestAction({ - actionName: "movePolyline", - componentName: "/P", - args: { - pointCoords: { 7: F }, - }, - event: null, - }); + await movePolyline({ name: "/P", pointCoords: { 7: F }, core }); stateVariables = await returnAllStateVariables(core); expect( @@ -2453,14 +2227,7 @@ describe("Polyline tag tests", async () => { // move ninth vertex G = [1, -8]; - await core.requestAction({ - actionName: "movePolyline", - componentName: "/P", - args: { - pointCoords: { 8: G }, - }, - event: null, - }); + await movePolyline({ name: "/P", pointCoords: { 8: G }, core }); stateVariables = await returnAllStateVariables(core); expect( @@ -2497,14 +2264,7 @@ describe("Polyline tag tests", async () => { // move tenth vertex A = [-6, 10]; - await core.requestAction({ - actionName: "movePolyline", - componentName: "/P", - args: { - pointCoords: { 9: A }, - }, - event: null, - }); + await movePolyline({ name: "/P", pointCoords: { 9: A }, core }); stateVariables = await returnAllStateVariables(core); expect( @@ -2600,14 +2360,7 @@ describe("Polyline tag tests", async () => { A2 = [A[0] + 2, A[1] + 2]; A3 = [A[0] + 3, A[1] + 3]; - await core.requestAction({ - actionName: "movePolyline", - componentName: "/P", - args: { - pointCoords: { 0: A3 }, - }, - event: null, - }); + await movePolyline({ name: "/P", pointCoords: { 0: A3 }, core }); stateVariables = await returnAllStateVariables(core); expect( @@ -2644,14 +2397,7 @@ describe("Polyline tag tests", async () => { // move second vertex B = [8, 9]; - await core.requestAction({ - actionName: "movePolyline", - componentName: "/P", - args: { - pointCoords: { 1: B }, - }, - event: null, - }); + await movePolyline({ name: "/P", pointCoords: { 1: B }, core }); stateVariables = await returnAllStateVariables(core); expect( @@ -2688,14 +2434,7 @@ describe("Polyline tag tests", async () => { // move third vertex C = [-3, 7]; - await core.requestAction({ - actionName: "movePolyline", - componentName: "/P", - args: { - pointCoords: { 2: C }, - }, - event: null, - }); + await movePolyline({ name: "/P", pointCoords: { 2: C }, core }); stateVariables = await returnAllStateVariables(core); expect( @@ -2735,14 +2474,7 @@ describe("Polyline tag tests", async () => { A2 = [A[0] + 2, A[1] + 2]; A3 = [A[0] + 3, A[1] + 3]; - await core.requestAction({ - actionName: "movePolyline", - componentName: "/P", - args: { - pointCoords: { 3: A2 }, - }, - event: null, - }); + await movePolyline({ name: "/P", pointCoords: { 3: A2 }, core }); stateVariables = await returnAllStateVariables(core); expect( @@ -2779,14 +2511,7 @@ describe("Polyline tag tests", async () => { // move fifth vertex D = [-9, 1]; - await core.requestAction({ - actionName: "movePolyline", - componentName: "/P", - args: { - pointCoords: { 4: D }, - }, - event: null, - }); + await movePolyline({ name: "/P", pointCoords: { 4: D }, core }); stateVariables = await returnAllStateVariables(core); expect( @@ -2823,14 +2548,7 @@ describe("Polyline tag tests", async () => { // move sixth vertex E = [-3, 6]; - await core.requestAction({ - actionName: "movePolyline", - componentName: "/P", - args: { - pointCoords: { 5: E }, - }, - event: null, - }); + await movePolyline({ name: "/P", pointCoords: { 5: E }, core }); stateVariables = await returnAllStateVariables(core); expect( @@ -2870,14 +2588,7 @@ describe("Polyline tag tests", async () => { A2 = [A[0] + 2, A[1] + 2]; A3 = [A[0] + 3, A[1] + 3]; - await core.requestAction({ - actionName: "movePolyline", - componentName: "/P", - args: { - pointCoords: { 6: A1 }, - }, - event: null, - }); + await movePolyline({ name: "/P", pointCoords: { 6: A1 }, core }); stateVariables = await returnAllStateVariables(core); expect( @@ -2914,14 +2625,7 @@ describe("Polyline tag tests", async () => { // move eighth vertex F = [6, 7]; - await core.requestAction({ - actionName: "movePolyline", - componentName: "/P", - args: { - pointCoords: { 7: F }, - }, - event: null, - }); + await movePolyline({ name: "/P", pointCoords: { 7: F }, core }); stateVariables = await returnAllStateVariables(core); expect( @@ -2958,14 +2662,7 @@ describe("Polyline tag tests", async () => { // move ninth vertex G = [1, -8]; - await core.requestAction({ - actionName: "movePolyline", - componentName: "/P", - args: { - pointCoords: { 8: G }, - }, - event: null, - }); + await movePolyline({ name: "/P", pointCoords: { 8: G }, core }); stateVariables = await returnAllStateVariables(core); expect( @@ -3005,14 +2702,7 @@ describe("Polyline tag tests", async () => { A2 = [A[0] + 2, A[1] + 2]; A3 = [A[0] + 3, A[1] + 3]; - await core.requestAction({ - actionName: "movePolyline", - componentName: "/P", - args: { - pointCoords: { 9: A }, - }, - event: null, - }); + await movePolyline({ name: "/P", pointCoords: { 9: A }, core }); stateVariables = await returnAllStateVariables(core); expect( @@ -3082,12 +2772,7 @@ describe("Polyline tag tests", async () => { let mseg1 = (y2 - y1) / (x2 - x1); let y = mseg1 * (x - x1) + y1 + 0.3; - await core.requestAction({ - actionName: "movePoint", - componentName: `/_point1`, - args: { x, y }, - event: null, - }); + await movePoint({ name: `/_point1`, x, y, core }); stateVariables = await returnAllStateVariables(core); @@ -3101,12 +2786,7 @@ describe("Polyline tag tests", async () => { let mseg2 = (y2 - y3) / (x2 - x3); y = mseg2 * (x - x2) + y2 + 0.4; - await core.requestAction({ - actionName: "movePoint", - componentName: `/_point1`, - args: { x, y }, - event: null, - }); + await movePoint({ name: `/_point1`, x, y, core }); stateVariables = await returnAllStateVariables(core); px = stateVariables["/_point1"].stateValues.xs[0].tree; @@ -3119,12 +2799,7 @@ describe("Polyline tag tests", async () => { let mseg3 = (y1 - y3) / (x1 - x3); y = mseg3 * (x - x3) + y3 + 0.2; - await core.requestAction({ - actionName: "movePoint", - componentName: `/_point1`, - args: { x, y }, - event: null, - }); + await movePoint({ name: `/_point1`, x, y, core }); stateVariables = await returnAllStateVariables(core); px = stateVariables["/_point1"].stateValues.xs[0].tree; @@ -3137,12 +2812,7 @@ describe("Polyline tag tests", async () => { x = x1 + 0.2; y = y1 + 0.3; - await core.requestAction({ - actionName: "movePoint", - componentName: `/_point1`, - args: { x, y }, - event: null, - }); + await movePoint({ name: `/_point1`, x, y, core }); stateVariables = await returnAllStateVariables(core); px = stateVariables["/_point1"].stateValues.xs[0].tree; @@ -3156,12 +2826,7 @@ describe("Polyline tag tests", async () => { mseg1 = (y2 - y1) / (x2 - x1); y = mseg1 * (x - x1) + y1 + 0.3; - await core.requestAction({ - actionName: "movePoint", - componentName: `/_point1`, - args: { x, y }, - event: null, - }); + await movePoint({ name: `/_point1`, x, y, core }); stateVariables = await returnAllStateVariables(core); px = stateVariables["/_point1"].stateValues.xs[0].tree; @@ -3174,12 +2839,7 @@ describe("Polyline tag tests", async () => { mseg1 = (y2 - y1) / (x2 - x1); y = mseg1 * (x - x1) + y1 - 0.3; - await core.requestAction({ - actionName: "movePoint", - componentName: `/_point1`, - args: { x, y }, - event: null, - }); + await movePoint({ name: `/_point1`, x, y, core }); stateVariables = await returnAllStateVariables(core); px = stateVariables["/_point1"].stateValues.xs[0].tree; @@ -3192,12 +2852,7 @@ describe("Polyline tag tests", async () => { x = x2 - 0.2; y = y2 - 0.3; - await core.requestAction({ - actionName: "movePoint", - componentName: `/_point1`, - args: { x, y }, - event: null, - }); + await movePoint({ name: `/_point1`, x, y, core }); stateVariables = await returnAllStateVariables(core); px = stateVariables["/_point1"].stateValues.xs[0].tree; @@ -3211,12 +2866,7 @@ describe("Polyline tag tests", async () => { mseg2 = (y2 - y3) / (x2 - x3); y = mseg2 * (x - x2) + y2 + 0.3; - await core.requestAction({ - actionName: "movePoint", - componentName: `/_point1`, - args: { x, y }, - event: null, - }); + await movePoint({ name: `/_point1`, x, y, core }); stateVariables = await returnAllStateVariables(core); px = stateVariables["/_point1"].stateValues.xs[0].tree; @@ -3229,12 +2879,7 @@ describe("Polyline tag tests", async () => { mseg2 = (y2 - y3) / (x2 - x3); y = mseg2 * (x - x2) + y2 - 0.3; - await core.requestAction({ - actionName: "movePoint", - componentName: `/_point1`, - args: { x, y }, - event: null, - }); + await movePoint({ name: `/_point1`, x, y, core }); stateVariables = await returnAllStateVariables(core); px = stateVariables["/_point1"].stateValues.xs[0].tree; @@ -3254,17 +2899,14 @@ describe("Polyline tag tests", async () => { y2 += moveY; y3 += moveY; - await core.requestAction({ - actionName: "movePolyline", - componentName: "/_polyline1", - args: { - pointCoords: [ - [x1, y1], - [x2, y2], - [x3, y3], - ], - }, - event: null, + await movePolyline({ + name: "/_polyline1", + pointCoords: [ + [x1, y1], + [x2, y2], + [x3, y3], + ], + core, }); stateVariables = await returnAllStateVariables(core); @@ -3282,13 +2924,10 @@ describe("Polyline tag tests", async () => { x2 += moveX; y2 += moveY; - await core.requestAction({ - actionName: "movePolyline", - componentName: "/_polyline1", - args: { - pointCoords: { 1: [x2, y2] }, - }, - event: null, + await movePolyline({ + name: "/_polyline1", + pointCoords: { 1: [x2, y2] }, + core, }); stateVariables = await returnAllStateVariables(core); @@ -3336,12 +2975,7 @@ describe("Polyline tag tests", async () => { let mseg1 = (y2 - y1) / (x2 - x1); let y = mseg1 * (x - x1) + y1 + 0.3; - await core.requestAction({ - actionName: "movePoint", - componentName: `/_point1`, - args: { x, y }, - event: null, - }); + await movePoint({ name: `/_point1`, x, y, core }); stateVariables = await returnAllStateVariables(core); @@ -3355,12 +2989,7 @@ describe("Polyline tag tests", async () => { let mseg2 = (y2 - y3) / (x2 - x3); y = mseg2 * (x - x2) + y2 + 0.4; - await core.requestAction({ - actionName: "movePoint", - componentName: `/_point1`, - args: { x, y }, - event: null, - }); + await movePoint({ name: `/_point1`, x, y, core }); stateVariables = await returnAllStateVariables(core); px = stateVariables["/_point1"].stateValues.xs[0].tree; @@ -3374,12 +3003,7 @@ describe("Polyline tag tests", async () => { let mseg3 = (y1 - y3) / (x1 - x3); y = mseg3 * (x - x3) + y3 + 0.2; - await core.requestAction({ - actionName: "movePoint", - componentName: `/_point1`, - args: { x, y }, - event: null, - }); + await movePoint({ name: `/_point1`, x, y, core }); stateVariables = await returnAllStateVariables(core); px = stateVariables["/_point1"].stateValues.xs[0].tree; @@ -3392,12 +3016,7 @@ describe("Polyline tag tests", async () => { x = x1 + 0.2; y = y1 + 0.3; - await core.requestAction({ - actionName: "movePoint", - componentName: `/_point1`, - args: { x, y }, - event: null, - }); + await movePoint({ name: `/_point1`, x, y, core }); stateVariables = await returnAllStateVariables(core); px = stateVariables["/_point1"].stateValues.xs[0].tree; @@ -3412,12 +3031,7 @@ describe("Polyline tag tests", async () => { mseg1 = (y2 - y1) / (x2 - x1); y = mseg1 * (x - x1) + y1 + 0.3; - await core.requestAction({ - actionName: "movePoint", - componentName: `/_point1`, - args: { x, y }, - event: null, - }); + await movePoint({ name: `/_point1`, x, y, core }); stateVariables = await returnAllStateVariables(core); px = stateVariables["/_point1"].stateValues.xs[0].tree; @@ -3429,12 +3043,7 @@ describe("Polyline tag tests", async () => { mseg1 = (y2 - y1) / (x2 - x1); y = mseg1 * (x - x1) + y1 - 0.3; - await core.requestAction({ - actionName: "movePoint", - componentName: `/_point1`, - args: { x, y }, - event: null, - }); + await movePoint({ name: `/_point1`, x, y, core }); stateVariables = await returnAllStateVariables(core); px = stateVariables["/_point1"].stateValues.xs[0].tree; @@ -3448,12 +3057,7 @@ describe("Polyline tag tests", async () => { x = x2 - 0.2; y = y2 - 0.3; - await core.requestAction({ - actionName: "movePoint", - componentName: `/_point1`, - args: { x, y }, - event: null, - }); + await movePoint({ name: `/_point1`, x, y, core }); stateVariables = await returnAllStateVariables(core); px = stateVariables["/_point1"].stateValues.xs[0].tree; @@ -3468,12 +3072,7 @@ describe("Polyline tag tests", async () => { mseg2 = (y2 - y3) / (x2 - x3); y = mseg2 * (x - x2) + y2 + 0.3; - await core.requestAction({ - actionName: "movePoint", - componentName: `/_point1`, - args: { x, y }, - event: null, - }); + await movePoint({ name: `/_point1`, x, y, core }); stateVariables = await returnAllStateVariables(core); px = stateVariables["/_point1"].stateValues.xs[0].tree; @@ -3486,12 +3085,7 @@ describe("Polyline tag tests", async () => { mseg2 = (y2 - y3) / (x2 - x3); y = mseg2 * (x - x2) + y2 - 0.3; - await core.requestAction({ - actionName: "movePoint", - componentName: `/_point1`, - args: { x, y }, - event: null, - }); + await movePoint({ name: `/_point1`, x, y, core }); stateVariables = await returnAllStateVariables(core); px = stateVariables["/_point1"].stateValues.xs[0].tree; @@ -3512,17 +3106,14 @@ describe("Polyline tag tests", async () => { y2 += moveY; y3 += moveY; - await core.requestAction({ - actionName: "movePolyline", - componentName: "/_polyline1", - args: { - pointCoords: [ - [x1, y1], - [x2, y2], - [x3, y3], - ], - }, - event: null, + await movePolyline({ + name: "/_polyline1", + pointCoords: [ + [x1, y1], + [x2, y2], + [x3, y3], + ], + core, }); stateVariables = await returnAllStateVariables(core); @@ -3541,13 +3132,10 @@ describe("Polyline tag tests", async () => { x2 += moveX; y2 += moveY; - await core.requestAction({ - actionName: "movePolyline", - componentName: "/_polyline1", - args: { - pointCoords: { 1: [x2, y2] }, - }, - event: null, + await movePolyline({ + name: "/_polyline1", + pointCoords: { 1: [x2, y2] }, + core, }); stateVariables = await returnAllStateVariables(core); @@ -3598,12 +3186,7 @@ describe("Polyline tag tests", async () => { let mseg1 = (y2 - y1) / (x2 - x1); - await core.requestAction({ - actionName: "movePoint", - componentName: `/A`, - args: { x: -20, y: 0.02 }, - event: null, - }); + await movePoint({ name: `/A`, x: -20, y: 0.02, core }); stateVariables = await returnAllStateVariables(core); px = stateVariables["/A"].stateValues.xs[0].tree; @@ -3615,12 +3198,7 @@ describe("Polyline tag tests", async () => { let mseg2 = (y2 - y3) / (x2 - x3); - await core.requestAction({ - actionName: "movePoint", - componentName: `/A`, - args: { x: 0, y: 0.04 }, - event: null, - }); + await movePoint({ name: `/A`, x: 0, y: 0.04, core }); stateVariables = await returnAllStateVariables(core); px = stateVariables["/A"].stateValues.xs[0].tree; @@ -3632,12 +3210,7 @@ describe("Polyline tag tests", async () => { mseg3 = (y4 - y3) / (x4 - x3); - await core.requestAction({ - actionName: "movePoint", - componentName: `/A`, - args: { x: -10, y: 0.02 }, - event: null, - }); + await movePoint({ name: `/A`, x: -10, y: 0.02, core }); stateVariables = await returnAllStateVariables(core); px = stateVariables["/A"].stateValues.xs[0].tree; @@ -3669,17 +3242,14 @@ describe("Polyline tag tests", async () => { // cannot move vertices - await core.requestAction({ - actionName: "movePolyline", - componentName: "/p", - args: { - pointCoords: [ - [4, 7], - [8, 10], - [1, 9], - ], - }, - event: null, + await movePolyline({ + name: "/p", + pointCoords: [ + [4, 7], + [8, 10], + [1, 9], + ], + core, }); stateVariables = await returnAllStateVariables(core); @@ -3727,7 +3297,7 @@ describe("Polyline tag tests", async () => { expect(stateVariables["/x"]).eq(undefined); expect(stateVariables["/xa"]).eq(undefined); - await updateMathInputValue({ latex: "1", componentName: "/n", core }); + await updateMathInputValue({ latex: "1", name: "/n", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/P1"].stateValues.xs.map((x) => x.tree)).eqls([ @@ -3739,7 +3309,7 @@ describe("Polyline tag tests", async () => { expect(stateVariables["/x"].stateValues.value.tree).eq(t2x); expect(stateVariables["/xa"].stateValues.value.tree).eq(t2x); - await updateMathInputValue({ latex: "2", componentName: "/n", core }); + await updateMathInputValue({ latex: "2", name: "/n", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/P1"].stateValues.xs.map((x) => x.tree)).eqls([ @@ -3751,7 +3321,7 @@ describe("Polyline tag tests", async () => { expect(stateVariables["/x"].stateValues.value.tree).eq(t2y); expect(stateVariables["/xa"].stateValues.value.tree).eq(t2y); - await updateMathInputValue({ latex: "3", componentName: "/n", core }); + await updateMathInputValue({ latex: "3", name: "/n", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/P1"].stateValues.xs.map((x) => x.tree)).eqls([ @@ -3763,7 +3333,7 @@ describe("Polyline tag tests", async () => { expect(stateVariables["/x"]).eq(undefined); expect(stateVariables["/xa"]).eq(undefined); - await updateMathInputValue({ latex: "4", componentName: "/n", core }); + await updateMathInputValue({ latex: "4", name: "/n", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/P1"]).eq(undefined); @@ -3797,13 +3367,10 @@ describe("Polyline tag tests", async () => { [-4, 5], ]); - await core.requestAction({ - actionName: "movePolyline", - componentName: "/polyline", - args: { - pointCoords: { 0: [3, 5] }, - }, - event: null, + await movePolyline({ + name: "/polyline", + pointCoords: { 0: [3, 5] }, + core, }); stateVariables = await returnAllStateVariables(core); @@ -3817,13 +3384,10 @@ describe("Polyline tag tests", async () => { [-4, 5], ]); - await core.requestAction({ - actionName: "movePolyline", - componentName: "/polyline", - args: { - pointCoords: { 1: [-9, -6] }, - }, - event: null, + await movePolyline({ + name: "/polyline", + pointCoords: { 1: [-9, -6] }, + core, }); stateVariables = await returnAllStateVariables(core); @@ -3837,13 +3401,10 @@ describe("Polyline tag tests", async () => { [-9, 2], ]); - await core.requestAction({ - actionName: "movePolyline", - componentName: "/polyline", - args: { - pointCoords: { 2: [-3, 1] }, - }, - event: null, + await movePolyline({ + name: "/polyline", + pointCoords: { 2: [-3, 1] }, + core, }); stateVariables = await returnAllStateVariables(core); @@ -3884,14 +3445,7 @@ describe("Polyline tag tests", async () => { // cannot move single vertex - await core.requestAction({ - actionName: "movePolyline", - componentName: "/p", - args: { - pointCoords: { 0: [4, 7] }, - }, - event: null, - }); + await movePolyline({ name: "/p", pointCoords: { 0: [4, 7] }, core }); stateVariables = await returnAllStateVariables(core); expect( @@ -3908,17 +3462,14 @@ describe("Polyline tag tests", async () => { // cannot move all vertices - await core.requestAction({ - actionName: "movePolyline", - componentName: "/p", - args: { - pointCoords: [ - [4, 7], - [8, 10], - [1, 9], - ], - }, - event: null, + await movePolyline({ + name: "/p", + pointCoords: [ + [4, 7], + [8, 10], + [1, 9], + ], + core, }); stateVariables = await returnAllStateVariables(core); @@ -3938,20 +3489,13 @@ describe("Polyline tag tests", async () => { await updateBooleanInputValue({ boolean: true, - componentName: "/verticesDraggable", + name: "/verticesDraggable", core, }); // can move single vertex - await core.requestAction({ - actionName: "movePolyline", - componentName: "/p", - args: { - pointCoords: { 0: [4, 7] }, - }, - event: null, - }); + await movePolyline({ name: "/p", pointCoords: { 0: [4, 7] }, core }); stateVariables = await returnAllStateVariables(core); expect( @@ -3968,17 +3512,14 @@ describe("Polyline tag tests", async () => { // cannot move all vertices - await core.requestAction({ - actionName: "movePolyline", - componentName: "/p", - args: { - pointCoords: [ - [3, 8], - [8, 10], - [1, 9], - ], - }, - event: null, + await movePolyline({ + name: "/p", + pointCoords: [ + [3, 8], + [8, 10], + [1, 9], + ], + core, }); stateVariables = await returnAllStateVariables(core); @@ -3998,20 +3539,13 @@ describe("Polyline tag tests", async () => { await updateBooleanInputValue({ boolean: true, - componentName: "/draggable", + name: "/draggable", core, }); // can move single vertex - await core.requestAction({ - actionName: "movePolyline", - componentName: "/p", - args: { - pointCoords: { 1: [-3, 2] }, - }, - event: null, - }); + await movePolyline({ name: "/p", pointCoords: { 1: [-3, 2] }, core }); stateVariables = await returnAllStateVariables(core); expect( @@ -4028,17 +3562,14 @@ describe("Polyline tag tests", async () => { // can move all vertices - await core.requestAction({ - actionName: "movePolyline", - componentName: "/p", - args: { - pointCoords: [ - [3, 8], - [8, 10], - [1, 9], - ], - }, - event: null, + await movePolyline({ + name: "/p", + pointCoords: [ + [3, 8], + [8, 10], + [1, 9], + ], + core, }); stateVariables = await returnAllStateVariables(core); @@ -4058,20 +3589,13 @@ describe("Polyline tag tests", async () => { await updateBooleanInputValue({ boolean: false, - componentName: "/verticesDraggable", + name: "/verticesDraggable", core, }); // cannot move single vertex - await core.requestAction({ - actionName: "movePolyline", - componentName: "/p", - args: { - pointCoords: { 2: [9, 3] }, - }, - event: null, - }); + await movePolyline({ name: "/p", pointCoords: { 2: [9, 3] }, core }); stateVariables = await returnAllStateVariables(core); expect( @@ -4088,17 +3612,14 @@ describe("Polyline tag tests", async () => { // can move all vertices - await core.requestAction({ - actionName: "movePolyline", - componentName: "/p", - args: { - pointCoords: [ - [-4, 1], - [9, -4], - [0, 7], - ], - }, - event: null, + await movePolyline({ + name: "/p", + pointCoords: [ + [-4, 1], + [9, -4], + [0, 7], + ], + core, }); stateVariables = await returnAllStateVariables(core); @@ -4149,13 +3670,10 @@ describe("Polyline tag tests", async () => { vertices[1] = [4, 7]; - await core.requestAction({ - actionName: "movePolyline", - componentName: "/g1/pg", - args: { - pointCoords: { 1: vertices[1] }, - }, - event: null, + await movePolyline({ + name: "/g1/pg", + pointCoords: { 1: vertices[1] }, + core, }); await testPolylineCopiedTwice({ core, vertices }); @@ -4170,14 +3688,7 @@ describe("Polyline tag tests", async () => { vertices[i][1] = vertices[i][1] + moveY; } - await core.requestAction({ - actionName: "movePolyline", - componentName: "/g2/pg", - args: { - pointCoords: vertices, - }, - event: null, - }); + await movePolyline({ name: "/g2/pg", pointCoords: vertices, core }); // adjustment due to constraint moveX = -1; @@ -4199,14 +3710,7 @@ describe("Polyline tag tests", async () => { vertices[i][1] = vertices[i][1] + moveY; } - await core.requestAction({ - actionName: "movePolyline", - componentName: "/g3/pg", - args: { - pointCoords: vertices, - }, - event: null, - }); + await movePolyline({ name: "/g3/pg", pointCoords: vertices, core }); // adjustment due to constraint moveX = -1; @@ -4257,13 +3761,10 @@ describe("Polyline tag tests", async () => { vertices[1] = [4, 7]; - await core.requestAction({ - actionName: "movePolyline", - componentName: "/g1/pg", - args: { - pointCoords: { 1: vertices[1] }, - }, - event: null, + await movePolyline({ + name: "/g1/pg", + pointCoords: { 1: vertices[1] }, + core, }); await testPolylineCopiedTwice({ core, vertices }); @@ -4278,14 +3779,7 @@ describe("Polyline tag tests", async () => { vertices[i][1] = vertices[i][1] + moveY; } - await core.requestAction({ - actionName: "movePolyline", - componentName: "/g2/pg", - args: { - pointCoords: vertices, - }, - event: null, - }); + await movePolyline({ name: "/g2/pg", pointCoords: vertices, core }); // adjustment due to constraint moveX = -1; @@ -4307,14 +3801,7 @@ describe("Polyline tag tests", async () => { vertices[i][1] = vertices[i][1] + moveY; } - await core.requestAction({ - actionName: "movePolyline", - componentName: "/g3/pg", - args: { - pointCoords: vertices, - }, - event: null, - }); + await movePolyline({ name: "/g3/pg", pointCoords: vertices, core }); // adjustment due to constraint moveX = -1; @@ -4369,13 +3856,10 @@ describe("Polyline tag tests", async () => { vertices[1] = [4, 7]; - await core.requestAction({ - actionName: "movePolyline", - componentName: "/g1/pg", - args: { - pointCoords: { 1: vertices[1] }, - }, - event: null, + await movePolyline({ + name: "/g1/pg", + pointCoords: { 1: vertices[1] }, + core, }); // adjust for constraint @@ -4393,14 +3877,7 @@ describe("Polyline tag tests", async () => { vertices[i][1] = vertices[i][1] + moveY; } - await core.requestAction({ - actionName: "movePolyline", - componentName: "/g2/pg", - args: { - pointCoords: vertices, - }, - event: null, - }); + await movePolyline({ name: "/g2/pg", pointCoords: vertices, core }); // adjustment due to constraint moveX = -1; @@ -4422,14 +3899,7 @@ describe("Polyline tag tests", async () => { vertices[i][1] = vertices[i][1] + moveY; } - await core.requestAction({ - actionName: "movePolyline", - componentName: "/g3/pg", - args: { - pointCoords: vertices, - }, - event: null, - }); + await movePolyline({ name: "/g3/pg", pointCoords: vertices, core }); // adjustment due to constraint moveX = -1; @@ -4471,22 +3941,8 @@ describe("Polyline tag tests", async () => { let stateVariables = await returnAllStateVariables(core); expect(stateVariables["/length"].stateValues.value).eq(length); - await core.requestAction({ - actionName: "movePolyline", - componentName: "/p", - args: { - pointCoords: { 1: [-8, -4] }, - }, - event: null, - }); - await core.requestAction({ - actionName: "movePolyline", - componentName: "/p", - args: { - pointCoords: { 2: [-8, 2] }, - }, - event: null, - }); + await movePolyline({ name: "/p", pointCoords: { 1: [-8, -4] }, core }); + await movePolyline({ name: "/p", pointCoords: { 2: [-8, 2] }, core }); length = 13 + 6 + Math.sqrt(16 + 64) + Math.sqrt(25 + 64); @@ -4524,13 +3980,10 @@ describe("Polyline tag tests", async () => { // move individual vertex vertices[1] = [4, 7]; - await core.requestAction({ - actionName: "movePolyline", - componentName: "/g1/pg", - args: { - pointCoords: { 1: vertices[1] }, - }, - event: null, + await movePolyline({ + name: "/g1/pg", + pointCoords: { 1: vertices[1] }, + core, }); await testPolylineCopiedTwice({ core, vertices }); @@ -4544,27 +3997,17 @@ describe("Polyline tag tests", async () => { vertices[i][1] = vertices[i][1] + moveY; } - await core.requestAction({ - actionName: "movePolyline", - componentName: "/g2/pg", - args: { - pointCoords: vertices, - }, - event: null, - }); + await movePolyline({ name: "/g2/pg", pointCoords: vertices, core }); await testPolylineCopiedTwice({ core, vertices }); // move double copied individual vertex vertices[2] = [-9, -8]; - await core.requestAction({ - actionName: "movePolyline", - componentName: "/g3/pg", - args: { - pointCoords: { 2: vertices[2] }, - }, - event: null, + await movePolyline({ + name: "/g3/pg", + pointCoords: { 2: vertices[2] }, + core, }); await testPolylineCopiedTwice({ core, vertices }); @@ -4572,7 +4015,7 @@ describe("Polyline tag tests", async () => { // change last vertices via math input await updateMathInputValue({ latex: "(3,2), (-6,5), (4,9), (-2,0)", - componentName: "/vertexInput", + name: "/vertexInput", core, }); diff --git a/packages/doenetml-worker/src/test/tagSpecific/regionBetweenCurves.test.ts b/packages/doenetml-worker/src/test/tagSpecific/regionBetweenCurves.test.ts new file mode 100644 index 000000000..b859d1579 --- /dev/null +++ b/packages/doenetml-worker/src/test/tagSpecific/regionBetweenCurves.test.ts @@ -0,0 +1,195 @@ +import { describe, expect, it, vi } from "vitest"; +import { createTestCore, returnAllStateVariables } from "../utils/test-core"; +import { movePoint, updateMathInputValue } from "../utils/actions"; + +const Mock = vi.fn(); +vi.stubGlobal("postMessage", Mock); +vi.mock("hyperformula"); + +describe("RegionBetweenCurves tag tests", async () => { + it("region between two curves", async () => { + let core = await createTestCore({ + doenetML: ` + a + + sin(x) + cos(x) + $f1 $f2 + + + + $(../g1/r{name="r"}) + + + $g2{name="g3"} + + + `, + }); + + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/t"].stateValues.value).eq("a"); + + // Not sure what to test until can test jsxgraph output + }); + + it("region between two curves, flipped", async () => { + let core = await createTestCore({ + doenetML: ` + a + + sin(x) + cos(x) + $f1 $f2 + + + + $(../g1/r{name="r"}) + + + $g2{name="g3"} + + + `, + }); + + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/t"].stateValues.value).eq("a"); + + // Not sure what to test until can test jsxgraph output + }); + + it("constrain point to region between two curves", async () => { + let core = await createTestCore({ + doenetML: ` + + + + sin(pi x/4) + cos(pi x/4) + $f1 $f2 + + (0,5) + + $r + + + + `, + }); + + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/P"].stateValues.xs.map((v) => v.tree)).eqls([ + 0, 1, + ]); + + // move point below + await movePoint({ name: `/P`, x: -2, y: -6, core }); + stateVariables = await returnAllStateVariables(core); + + let px = stateVariables["/P"].stateValues.xs[0].tree; + let py = stateVariables["/P"].stateValues.xs[1].tree; + expect(px).closeTo(-2, 1e-12); + expect(py).closeTo(-1, 1e-12); + + // move point to upper left + await movePoint({ name: `/P`, x: -9, y: 3, core }); + stateVariables = await returnAllStateVariables(core); + + px = stateVariables["/P"].stateValues.xs[0].tree; + py = stateVariables["/P"].stateValues.xs[1].tree; + expect(px).closeTo(-6, 1e-12); + expect(py).closeTo(1, 1e-12); + + // move point to lower left + await movePoint({ name: `/P`, x: -8, y: -6, core }); + stateVariables = await returnAllStateVariables(core); + + px = stateVariables["/P"].stateValues.xs[0].tree; + py = stateVariables["/P"].stateValues.xs[1].tree; + expect(px).closeTo(-6, 1e-12); + expect(py).closeTo(0, 1e-12); + + // move point to left + await movePoint({ name: `/P`, x: -10, y: 0.4, core }); + stateVariables = await returnAllStateVariables(core); + + px = stateVariables["/P"].stateValues.xs[0].tree; + py = stateVariables["/P"].stateValues.xs[1].tree; + expect(px).closeTo(-6, 1e-12); + expect(py).closeTo(0.4, 1e-12); + + // move point to right + await movePoint({ name: `/P`, x: 10, y: -0.2, core }); + stateVariables = await returnAllStateVariables(core); + + px = stateVariables["/P"].stateValues.xs[0].tree; + py = stateVariables["/P"].stateValues.xs[1].tree; + expect(px).closeTo(4, 1e-12); + expect(py).closeTo(-0.2, 1e-12); + + // move point to upper right + await movePoint({ name: `/P`, x: 5, y: 4, core }); + stateVariables = await returnAllStateVariables(core); + + px = stateVariables["/P"].stateValues.xs[0].tree; + py = stateVariables["/P"].stateValues.xs[1].tree; + expect(px).closeTo(4, 1e-12); + expect(py).closeTo(0, 1e-12); + + // move point to lower right + await movePoint({ name: `/P`, x: 6, y: -9, core }); + stateVariables = await returnAllStateVariables(core); + + px = stateVariables["/P"].stateValues.xs[0].tree; + py = stateVariables["/P"].stateValues.xs[1].tree; + expect(px).closeTo(4, 1e-12); + expect(py).closeTo(-1, 1e-12); + + // change boundaries + await updateMathInputValue({ latex: "-8", name: "/a", core }); + await updateMathInputValue({ latex: "-2", name: "/b", core }); + stateVariables = await returnAllStateVariables(core); + + px = stateVariables["/P"].stateValues.xs[0].tree; + py = stateVariables["/P"].stateValues.xs[1].tree; + expect(px).closeTo(-2, 1e-12); + expect(py).closeTo(-1, 1e-12); + + // move point to upper right + await movePoint({ name: `/P`, x: 5, y: 4, core }); + stateVariables = await returnAllStateVariables(core); + + px = stateVariables["/P"].stateValues.xs[0].tree; + py = stateVariables["/P"].stateValues.xs[1].tree; + expect(px).closeTo(-2, 1e-12); + expect(py).closeTo(0, 1e-12); + + // move point to middle + await movePoint({ name: `/P`, x: -5.2, y: 0.1, core }); + stateVariables = await returnAllStateVariables(core); + + px = stateVariables["/P"].stateValues.xs[0].tree; + py = stateVariables["/P"].stateValues.xs[1].tree; + expect(px).closeTo(-5.2, 1e-12); + expect(py).closeTo(0.1, 1e-12); + + // move point to top + await movePoint({ name: `/P`, x: -6, y: 3, core }); + stateVariables = await returnAllStateVariables(core); + + px = stateVariables["/P"].stateValues.xs[0].tree; + py = stateVariables["/P"].stateValues.xs[1].tree; + expect(px).closeTo(-6, 1e-12); + expect(py).closeTo(1, 1e-12); + + // move point to left + await movePoint({ name: `/P`, x: -9.2, y: 0.6, core }); + stateVariables = await returnAllStateVariables(core); + + px = stateVariables["/P"].stateValues.xs[0].tree; + py = stateVariables["/P"].stateValues.xs[1].tree; + expect(px).closeTo(-8, 1e-12); + expect(py).closeTo(0.6, 1e-12); + }); +}); diff --git a/packages/doenetml-worker/src/test/tagSpecific/sampleprimenumbers.test.ts b/packages/doenetml-worker/src/test/tagSpecific/sampleprimenumbers.test.ts index aee95084a..59c602a8d 100644 --- a/packages/doenetml-worker/src/test/tagSpecific/sampleprimenumbers.test.ts +++ b/packages/doenetml-worker/src/test/tagSpecific/sampleprimenumbers.test.ts @@ -1,9 +1,10 @@ import { describe, expect, it, vi } from "vitest"; import { createTestCore, returnAllStateVariables } from "../utils/test-core"; -import { updateMathInputValue } from "../utils/actions"; +import { callAction, updateMathInputValue } from "../utils/actions"; const Mock = vi.fn(); vi.stubGlobal("postMessage", Mock); +vi.mock("hyperformula"); describe("SamplePrimeNumbers tag tests", async () => { async function test_values_separately({ @@ -220,12 +221,12 @@ describe("SamplePrimeNumbers tag tests", async () => { // Get new samples when change number of samples await updateMathInputValue({ latex: "70", - componentName: "/numSamples", + name: "/numSamples", core, }); await updateMathInputValue({ latex: "160", - componentName: "/numSamples2", + name: "/numSamples2", core, }); @@ -263,12 +264,12 @@ describe("SamplePrimeNumbers tag tests", async () => { // Get new samples when change parameters await updateMathInputValue({ latex: "20", - componentName: "/maxNum", + name: "/maxNum", core, }); await updateMathInputValue({ latex: "7, 19, 29, 37, 47, 2, 11, 23, 31, 41", - componentName: "/exclude", + name: "/exclude", core, }); @@ -375,22 +376,22 @@ describe("SamplePrimeNumbers tag tests", async () => { await check_sampled_numbers([]); // sample one variable - await updateMathInputValue({ latex: "1", componentName: "/mi1", core }); + await updateMathInputValue({ latex: "1", name: "/mi1", core }); let stateVariables = await returnAllStateVariables(core); sampledNumbers.push(stateVariables["/a/n"].stateValues.value); await check_sampled_numbers(sampledNumbers); // go back to nothing - await updateMathInputValue({ latex: "0", componentName: "/mi1", core }); + await updateMathInputValue({ latex: "0", name: "/mi1", core }); await check_sampled_numbers([]); // get same number back - await updateMathInputValue({ latex: "1", componentName: "/mi1", core }); + await updateMathInputValue({ latex: "1", name: "/mi1", core }); await check_sampled_numbers(sampledNumbers); // get two more samples - await updateMathInputValue({ latex: "3", componentName: "/mi1", core }); + await updateMathInputValue({ latex: "3", name: "/mi1", core }); stateVariables = await returnAllStateVariables(core); let n1 = stateVariables["/a/n"].stateValues.value; @@ -402,15 +403,15 @@ describe("SamplePrimeNumbers tag tests", async () => { await check_sampled_numbers(sampledNumbers); // go back to nothing - await updateMathInputValue({ latex: "0", componentName: "/mi1", core }); + await updateMathInputValue({ latex: "0", name: "/mi1", core }); await check_sampled_numbers([]); // get first two numbers back - await updateMathInputValue({ latex: "2", componentName: "/mi1", core }); + await updateMathInputValue({ latex: "2", name: "/mi1", core }); await check_sampled_numbers(sampledNumbers.slice(0, 2)); // get six total samples - await updateMathInputValue({ latex: "6", componentName: "/mi1", core }); + await updateMathInputValue({ latex: "6", name: "/mi1", core }); stateVariables = await returnAllStateVariables(core); n1 = stateVariables["/a/n"].stateValues.value; @@ -428,11 +429,11 @@ describe("SamplePrimeNumbers tag tests", async () => { await check_sampled_numbers(sampledNumbers); // go back to nothing - await updateMathInputValue({ latex: "0", componentName: "/mi1", core }); + await updateMathInputValue({ latex: "0", name: "/mi1", core }); await check_sampled_numbers([]); // get all six back - await updateMathInputValue({ latex: "6", componentName: "/mi1", core }); + await updateMathInputValue({ latex: "6", name: "/mi1", core }); await check_sampled_numbers(sampledNumbers); }); @@ -560,12 +561,7 @@ describe("SamplePrimeNumbers tag tests", async () => { expect(stateVariables["/pn1"].stateValues.text).eq(pn1.toString()); - await core.requestAction({ - componentName: "/resamp1", - actionName: "callAction", - args: {}, - event: null, - }); + await callAction({ name: "/resamp1", core }); stateVariables = await returnAllStateVariables(core); @@ -583,12 +579,7 @@ describe("SamplePrimeNumbers tag tests", async () => { expect(stateVariables["/pn3"].stateValues.text).eq(pn3.toString()); - await core.requestAction({ - componentName: "/resamp2", - actionName: "callAction", - args: {}, - event: null, - }); + await callAction({ name: "/resamp2", core }); stateVariables = await returnAllStateVariables(core); diff --git a/packages/doenetml-worker/src/test/tagSpecific/select.test.ts b/packages/doenetml-worker/src/test/tagSpecific/select.test.ts index 9f091547d..57565d69a 100644 --- a/packages/doenetml-worker/src/test/tagSpecific/select.test.ts +++ b/packages/doenetml-worker/src/test/tagSpecific/select.test.ts @@ -8,6 +8,7 @@ import me from "math-expressions"; const Mock = vi.fn(); vi.stubGlobal("postMessage", Mock); +vi.mock("hyperformula"); describe("Select tag tests", async () => { async function test_values_separately({ @@ -439,7 +440,7 @@ describe("Select tag tests", async () => { // Nothing changes when change number to select await updateMathInputValue({ latex: "7", - componentName: "/numToSelect", + name: "/numToSelect", core, }); @@ -467,17 +468,17 @@ describe("Select tag tests", async () => { }; await updateMathInputValue({ latex: newValues.a, - componentName: "/x", + name: "/x", core, }); await updateMathInputValue({ latex: newValues.b, - componentName: "/y", + name: "/y", core, }); await updateMathInputValue({ latex: newValues.c, - componentName: "/z", + name: "/z", core, }); @@ -570,22 +571,22 @@ describe("Select tag tests", async () => { await check_sampled_numbers([]); // sample one variable - await updateMathInputValue({ latex: "1", componentName: "/mi1", core }); + await updateMathInputValue({ latex: "1", name: "/mi1", core }); let stateVariables = await returnAllStateVariables(core); sampledNumbers.push(stateVariables["/a/n"].stateValues.value); await check_sampled_numbers(sampledNumbers); // go back to nothing - await updateMathInputValue({ latex: "0", componentName: "/mi1", core }); + await updateMathInputValue({ latex: "0", name: "/mi1", core }); await check_sampled_numbers([]); // get same number back - await updateMathInputValue({ latex: "1", componentName: "/mi1", core }); + await updateMathInputValue({ latex: "1", name: "/mi1", core }); await check_sampled_numbers(sampledNumbers); // get two more samples - await updateMathInputValue({ latex: "3", componentName: "/mi1", core }); + await updateMathInputValue({ latex: "3", name: "/mi1", core }); stateVariables = await returnAllStateVariables(core); let n1 = stateVariables["/a/n"].stateValues.value; @@ -597,15 +598,15 @@ describe("Select tag tests", async () => { await check_sampled_numbers(sampledNumbers); // go back to nothing - await updateMathInputValue({ latex: "0", componentName: "/mi1", core }); + await updateMathInputValue({ latex: "0", name: "/mi1", core }); await check_sampled_numbers([]); // get first two numbers back - await updateMathInputValue({ latex: "2", componentName: "/mi1", core }); + await updateMathInputValue({ latex: "2", name: "/mi1", core }); await check_sampled_numbers(sampledNumbers.slice(0, 2)); // get six total samples - await updateMathInputValue({ latex: "6", componentName: "/mi1", core }); + await updateMathInputValue({ latex: "6", name: "/mi1", core }); stateVariables = await returnAllStateVariables(core); n1 = stateVariables["/a/n"].stateValues.value; @@ -623,11 +624,11 @@ describe("Select tag tests", async () => { await check_sampled_numbers(sampledNumbers); // go back to nothing - await updateMathInputValue({ latex: "0", componentName: "/mi1", core }); + await updateMathInputValue({ latex: "0", name: "/mi1", core }); await check_sampled_numbers([]); // get all six back - await updateMathInputValue({ latex: "6", componentName: "/mi1", core }); + await updateMathInputValue({ latex: "6", name: "/mi1", core }); await check_sampled_numbers(sampledNumbers); }); @@ -2875,12 +2876,12 @@ describe("Select tag tests", async () => { await updateBooleanInputValue({ boolean: true, - componentName: "/h1", + name: "/h1", core, }); await updateBooleanInputValue({ boolean: false, - componentName: "/h2", + name: "/h2", core, }); @@ -2890,12 +2891,12 @@ describe("Select tag tests", async () => { await updateBooleanInputValue({ boolean: false, - componentName: "/h1", + name: "/h1", core, }); await updateBooleanInputValue({ boolean: true, - componentName: "/h2", + name: "/h2", core, }); diff --git a/packages/doenetml-worker/src/test/tagSpecific/selectfromsequence.test.ts b/packages/doenetml-worker/src/test/tagSpecific/selectfromsequence.test.ts index 113398753..0915507a0 100644 --- a/packages/doenetml-worker/src/test/tagSpecific/selectfromsequence.test.ts +++ b/packages/doenetml-worker/src/test/tagSpecific/selectfromsequence.test.ts @@ -9,6 +9,7 @@ import me from "math-expressions"; const Mock = vi.fn(); vi.stubGlobal("postMessage", Mock); +vi.mock("hyperformula"); describe("SelectFromSequence tag tests", async () => { async function test_values_separately({ @@ -595,22 +596,22 @@ describe("SelectFromSequence tag tests", async () => { // Nothing changes when change mathInputs await updateMathInputValue({ latex: "7", - componentName: "/numToSelect", + name: "/numToSelect", core, }); await updateMathInputValue({ latex: "11", - componentName: "/maxNum", + name: "/maxNum", core, }); await updateMathInputValue({ latex: "16", - componentName: "/numToSelect2", + name: "/numToSelect2", core, }); await updateMathInputValue({ latex: "18", - componentName: "/maxNum2", + name: "/maxNum2", core, }); @@ -703,22 +704,22 @@ describe("SelectFromSequence tag tests", async () => { await check_sampled_numbers([]); // sample one variable - await updateMathInputValue({ latex: "1", componentName: "/mi1", core }); + await updateMathInputValue({ latex: "1", name: "/mi1", core }); let stateVariables = await returnAllStateVariables(core); sampledNumbers.push(stateVariables["/a/n"].stateValues.value); await check_sampled_numbers(sampledNumbers); // go back to nothing - await updateMathInputValue({ latex: "0", componentName: "/mi1", core }); + await updateMathInputValue({ latex: "0", name: "/mi1", core }); await check_sampled_numbers([]); // get same number back - await updateMathInputValue({ latex: "1", componentName: "/mi1", core }); + await updateMathInputValue({ latex: "1", name: "/mi1", core }); await check_sampled_numbers(sampledNumbers); // get two more samples - await updateMathInputValue({ latex: "3", componentName: "/mi1", core }); + await updateMathInputValue({ latex: "3", name: "/mi1", core }); stateVariables = await returnAllStateVariables(core); let n1 = stateVariables["/a/n"].stateValues.value; @@ -730,15 +731,15 @@ describe("SelectFromSequence tag tests", async () => { await check_sampled_numbers(sampledNumbers); // go back to nothing - await updateMathInputValue({ latex: "0", componentName: "/mi1", core }); + await updateMathInputValue({ latex: "0", name: "/mi1", core }); await check_sampled_numbers([]); // get first two numbers back - await updateMathInputValue({ latex: "2", componentName: "/mi1", core }); + await updateMathInputValue({ latex: "2", name: "/mi1", core }); await check_sampled_numbers(sampledNumbers.slice(0, 2)); // get six total samples - await updateMathInputValue({ latex: "6", componentName: "/mi1", core }); + await updateMathInputValue({ latex: "6", name: "/mi1", core }); stateVariables = await returnAllStateVariables(core); n1 = stateVariables["/a/n"].stateValues.value; @@ -756,11 +757,11 @@ describe("SelectFromSequence tag tests", async () => { await check_sampled_numbers(sampledNumbers); // go back to nothing - await updateMathInputValue({ latex: "0", componentName: "/mi1", core }); + await updateMathInputValue({ latex: "0", name: "/mi1", core }); await check_sampled_numbers([]); // get all six back - await updateMathInputValue({ latex: "6", componentName: "/mi1", core }); + await updateMathInputValue({ latex: "6", name: "/mi1", core }); await check_sampled_numbers(sampledNumbers); }); @@ -1086,12 +1087,12 @@ describe("SelectFromSequence tag tests", async () => { await updateBooleanInputValue({ boolean: true, - componentName: "/h1", + name: "/h1", core, }); await updateBooleanInputValue({ boolean: false, - componentName: "/h2", + name: "/h2", core, }); @@ -1101,12 +1102,12 @@ describe("SelectFromSequence tag tests", async () => { await updateBooleanInputValue({ boolean: false, - componentName: "/h1", + name: "/h1", core, }); await updateBooleanInputValue({ boolean: true, - componentName: "/h2", + name: "/h2", core, }); @@ -1176,9 +1177,9 @@ describe("SelectFromSequence tag tests", async () => { expect(stateVariables["/b2f"].stateValues.value).eq(false); expect(stateVariables["/c2f"].stateValues.value).eq(true); - await updateTextInputValue({ text: "f", componentName: "/a3", core }); - await updateTextInputValue({ text: "g", componentName: "/b3", core }); - await updateTextInputValue({ text: "h", componentName: "/c3", core }); + await updateTextInputValue({ text: "f", name: "/a3", core }); + await updateTextInputValue({ text: "g", name: "/b3", core }); + await updateTextInputValue({ text: "h", name: "/c3", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/a"].stateValues.value).eq(a); @@ -1188,9 +1189,9 @@ describe("SelectFromSequence tag tests", async () => { expect(stateVariables["/b2"].stateValues.value).eq("g"); expect(stateVariables["/c2"].stateValues.value).eq(c); - await updateTextInputValue({ text: "i", componentName: "/a4", core }); - await updateTextInputValue({ text: "j", componentName: "/b4", core }); - await updateTextInputValue({ text: "k", componentName: "/c4", core }); + await updateTextInputValue({ text: "i", name: "/a4", core }); + await updateTextInputValue({ text: "j", name: "/b4", core }); + await updateTextInputValue({ text: "k", name: "/c4", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/a"].stateValues.value).eq(a); @@ -1202,12 +1203,12 @@ describe("SelectFromSequence tag tests", async () => { await updateBooleanInputValue({ boolean: true, - componentName: "/f1", + name: "/f1", core, }); await updateBooleanInputValue({ boolean: false, - componentName: "/f2", + name: "/f2", core, }); stateVariables = await returnAllStateVariables(core); @@ -1219,9 +1220,9 @@ describe("SelectFromSequence tag tests", async () => { expect(stateVariables["/b2f"].stateValues.value).eq(true); expect(stateVariables["/c2f"].stateValues.value).eq(false); - await updateTextInputValue({ text: "l", componentName: "/a3", core }); - await updateTextInputValue({ text: "m", componentName: "/b3", core }); - await updateTextInputValue({ text: "n", componentName: "/c3", core }); + await updateTextInputValue({ text: "l", name: "/a3", core }); + await updateTextInputValue({ text: "m", name: "/b3", core }); + await updateTextInputValue({ text: "n", name: "/c3", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/a"].stateValues.value).eq(a); @@ -1231,9 +1232,9 @@ describe("SelectFromSequence tag tests", async () => { expect(stateVariables["/b2"].stateValues.value).eq("j"); expect(stateVariables["/c2"].stateValues.value).eq("n"); - await updateTextInputValue({ text: "o", componentName: "/a4", core }); - await updateTextInputValue({ text: "p", componentName: "/b4", core }); - await updateTextInputValue({ text: "q", componentName: "/c4", core }); + await updateTextInputValue({ text: "o", name: "/a4", core }); + await updateTextInputValue({ text: "p", name: "/b4", core }); + await updateTextInputValue({ text: "q", name: "/c4", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/a"].stateValues.value).eq(a); diff --git a/packages/doenetml-worker/src/test/tagSpecific/selectprimenumbers.test.ts b/packages/doenetml-worker/src/test/tagSpecific/selectprimenumbers.test.ts index 8f07d0471..24eb1cebf 100644 --- a/packages/doenetml-worker/src/test/tagSpecific/selectprimenumbers.test.ts +++ b/packages/doenetml-worker/src/test/tagSpecific/selectprimenumbers.test.ts @@ -7,6 +7,7 @@ import { const Mock = vi.fn(); vi.stubGlobal("postMessage", Mock); +vi.mock("hyperformula"); describe("selectPrimeNumbers tag tests", async () => { async function test_values_separately({ @@ -608,22 +609,22 @@ describe("selectPrimeNumbers tag tests", async () => { // Nothing changes when change mathInputs await updateMathInputValue({ latex: "7", - componentName: "/numToSelect", + name: "/numToSelect", core, }); await updateMathInputValue({ latex: "11", - componentName: "/maxNum", + name: "/maxNum", core, }); await updateMathInputValue({ latex: "16", - componentName: "/numToSelect2", + name: "/numToSelect2", core, }); await updateMathInputValue({ latex: "18", - componentName: "/maxNum2", + name: "/maxNum2", core, }); @@ -716,22 +717,22 @@ describe("selectPrimeNumbers tag tests", async () => { await check_sampled_numbers([]); // sample one variable - await updateMathInputValue({ latex: "1", componentName: "/mi1", core }); + await updateMathInputValue({ latex: "1", name: "/mi1", core }); let stateVariables = await returnAllStateVariables(core); sampledNumbers.push(stateVariables["/a/n"].stateValues.value); await check_sampled_numbers(sampledNumbers); // go back to nothing - await updateMathInputValue({ latex: "0", componentName: "/mi1", core }); + await updateMathInputValue({ latex: "0", name: "/mi1", core }); await check_sampled_numbers([]); // get same number back - await updateMathInputValue({ latex: "1", componentName: "/mi1", core }); + await updateMathInputValue({ latex: "1", name: "/mi1", core }); await check_sampled_numbers(sampledNumbers); // get two more samples - await updateMathInputValue({ latex: "3", componentName: "/mi1", core }); + await updateMathInputValue({ latex: "3", name: "/mi1", core }); stateVariables = await returnAllStateVariables(core); let n1 = stateVariables["/a/n"].stateValues.value; @@ -743,15 +744,15 @@ describe("selectPrimeNumbers tag tests", async () => { await check_sampled_numbers(sampledNumbers); // go back to nothing - await updateMathInputValue({ latex: "0", componentName: "/mi1", core }); + await updateMathInputValue({ latex: "0", name: "/mi1", core }); await check_sampled_numbers([]); // get first two numbers back - await updateMathInputValue({ latex: "2", componentName: "/mi1", core }); + await updateMathInputValue({ latex: "2", name: "/mi1", core }); await check_sampled_numbers(sampledNumbers.slice(0, 2)); // get six total samples - await updateMathInputValue({ latex: "6", componentName: "/mi1", core }); + await updateMathInputValue({ latex: "6", name: "/mi1", core }); stateVariables = await returnAllStateVariables(core); n1 = stateVariables["/a/n"].stateValues.value; @@ -769,11 +770,11 @@ describe("selectPrimeNumbers tag tests", async () => { await check_sampled_numbers(sampledNumbers); // go back to nothing - await updateMathInputValue({ latex: "0", componentName: "/mi1", core }); + await updateMathInputValue({ latex: "0", name: "/mi1", core }); await check_sampled_numbers([]); // get all six back - await updateMathInputValue({ latex: "6", componentName: "/mi1", core }); + await updateMathInputValue({ latex: "6", name: "/mi1", core }); await check_sampled_numbers(sampledNumbers); }); @@ -860,12 +861,12 @@ describe("selectPrimeNumbers tag tests", async () => { await updateBooleanInputValue({ boolean: true, - componentName: "/h1", + name: "/h1", core, }); await updateBooleanInputValue({ boolean: false, - componentName: "/h2", + name: "/h2", core, }); @@ -875,12 +876,12 @@ describe("selectPrimeNumbers tag tests", async () => { await updateBooleanInputValue({ boolean: false, - componentName: "/h1", + name: "/h1", core, }); await updateBooleanInputValue({ boolean: true, - componentName: "/h2", + name: "/h2", core, }); diff --git a/packages/doenetml-worker/src/test/tagSpecific/selectsamplerandomnumbers.test.ts b/packages/doenetml-worker/src/test/tagSpecific/selectsamplerandomnumbers.test.ts index beef9d10f..3b26d15bf 100644 --- a/packages/doenetml-worker/src/test/tagSpecific/selectsamplerandomnumbers.test.ts +++ b/packages/doenetml-worker/src/test/tagSpecific/selectsamplerandomnumbers.test.ts @@ -1,15 +1,20 @@ import { describe, expect, it, vi } from "vitest"; import { createTestCore, returnAllStateVariables } from "../utils/test-core"; -import { updateMathInputValue, updateTextInputValue } from "../utils/actions"; +import { + callAction, + updateMathInputValue, + updateTextInputValue, +} from "../utils/actions"; import me from "math-expressions"; const Mock = vi.fn(); vi.stubGlobal("postMessage", Mock); +vi.mock("hyperformula"); describe("SelectRandomNumbers and SampleRandomNumbers tag tests", async () => { async function test_combined_statistics({ doenetML, - componentName, + name, numSamplesPerComponent, numRepetitions, minValue, @@ -25,7 +30,7 @@ describe("SelectRandomNumbers and SampleRandomNumbers tag tests", async () => { expectedVariance, }: { doenetML: string; - componentName: string; + name: string; numSamplesPerComponent: number; numRepetitions: number; minValue?: number; @@ -48,24 +53,25 @@ describe("SelectRandomNumbers and SampleRandomNumbers tag tests", async () => { }); const stateVariables = await returnAllStateVariables(core); samples.push( - ...stateVariables[componentName].replacements!.map( + ...stateVariables[name].replacements!.map( (x) => stateVariables[x.componentName].stateValues.value, ), ); if (expectedMean !== undefined && i == 0) { - expect(stateVariables[componentName].stateValues.mean).closeTo( + expect(stateVariables[name].stateValues.mean).closeTo( expectedMean, 1e-10, ); } if (expectedVariance !== undefined && i == 0) { + expect(stateVariables[name].stateValues.variance).closeTo( + expectedVariance, + 1e-10, + ); expect( - stateVariables[componentName].stateValues.variance, - ).closeTo(expectedVariance, 1e-10); - expect( - stateVariables[componentName].stateValues.standardDeviation, + stateVariables[name].stateValues.standardDeviation, ).closeTo(Math.sqrt(expectedVariance), 1e-10); } } @@ -121,7 +127,7 @@ describe("SelectRandomNumbers and SampleRandomNumbers tag tests", async () => { for (let doenetML of doenetMLs) { await test_combined_statistics({ doenetML, - componentName: "/s", + name: "/s", numSamplesPerComponent: 1, numRepetitions: 400, minValue: 0, @@ -147,7 +153,7 @@ describe("SelectRandomNumbers and SampleRandomNumbers tag tests", async () => { for (let doenetML of doenetMLs) { await test_combined_statistics({ doenetML, - componentName: "/s", + name: "/s", numSamplesPerComponent: 5, numRepetitions: 80, minValue: 0, @@ -173,7 +179,7 @@ describe("SelectRandomNumbers and SampleRandomNumbers tag tests", async () => { for (let doenetML of doenetMLs) { await test_combined_statistics({ doenetML, - componentName: "/s", + name: "/s", numSamplesPerComponent: 5, numRepetitions: 80, minValue: -5, @@ -199,7 +205,7 @@ describe("SelectRandomNumbers and SampleRandomNumbers tag tests", async () => { for (let doenetML of doenetMLs) { await test_combined_statistics({ doenetML, - componentName: "/s", + name: "/s", numSamplesPerComponent: 10, numRepetitions: 40, minValue: -4, @@ -225,7 +231,7 @@ describe("SelectRandomNumbers and SampleRandomNumbers tag tests", async () => { for (let doenetML of doenetMLs) { await test_combined_statistics({ doenetML, - componentName: "/s", + name: "/s", numSamplesPerComponent: 10, numRepetitions: 10, minValue: -4, @@ -251,7 +257,7 @@ describe("SelectRandomNumbers and SampleRandomNumbers tag tests", async () => { for (let doenetML of doenetMLs) { await test_combined_statistics({ doenetML, - componentName: "/s", + name: "/s", numSamplesPerComponent: 20, numRepetitions: 20, allowedMeanMid: 0, @@ -273,7 +279,7 @@ describe("SelectRandomNumbers and SampleRandomNumbers tag tests", async () => { for (let doenetML of doenetMLs) { await test_combined_statistics({ doenetML, - componentName: "/s", + name: "/s", numSamplesPerComponent: 5, numRepetitions: 80, allowedMeanMid: 0, @@ -295,7 +301,7 @@ describe("SelectRandomNumbers and SampleRandomNumbers tag tests", async () => { for (let doenetML of doenetMLs) { await test_combined_statistics({ doenetML, - componentName: "/s", + name: "/s", numSamplesPerComponent: 1, numRepetitions: 400, allowedMeanMid: -50, @@ -317,7 +323,7 @@ describe("SelectRandomNumbers and SampleRandomNumbers tag tests", async () => { for (let doenetML of doenetMLs) { await test_combined_statistics({ doenetML, - componentName: "/s", + name: "/s", numSamplesPerComponent: 20, numRepetitions: 5, allowedMeanMid: -3, @@ -339,7 +345,7 @@ describe("SelectRandomNumbers and SampleRandomNumbers tag tests", async () => { for (let doenetML of doenetMLs) { await test_combined_statistics({ doenetML, - componentName: "/s", + name: "/s", numSamplesPerComponent: 1, numRepetitions: 400, validValues: [0, 1], @@ -362,7 +368,7 @@ describe("SelectRandomNumbers and SampleRandomNumbers tag tests", async () => { for (let doenetML of doenetMLs) { await test_combined_statistics({ doenetML, - componentName: "/s", + name: "/s", numSamplesPerComponent: 1, numRepetitions: 400, validValues: [0.5, 1.5, 2.5, 3.5, 4.5, 5.5], @@ -385,7 +391,7 @@ describe("SelectRandomNumbers and SampleRandomNumbers tag tests", async () => { for (let doenetML of doenetMLs) { await test_combined_statistics({ doenetML, - componentName: "/s", + name: "/s", numSamplesPerComponent: 1, numRepetitions: 400, validValues: [8.5, 9.5], @@ -408,7 +414,7 @@ describe("SelectRandomNumbers and SampleRandomNumbers tag tests", async () => { for (let doenetML of doenetMLs) { await test_combined_statistics({ doenetML, - componentName: "/s", + name: "/s", numSamplesPerComponent: 5, numRepetitions: 80, validValues: [-3, -2, -1, 0, 1, 2, 3, 4, 5], @@ -431,7 +437,7 @@ describe("SelectRandomNumbers and SampleRandomNumbers tag tests", async () => { for (let doenetML of doenetMLs) { await test_combined_statistics({ doenetML, - componentName: "/s", + name: "/s", numSamplesPerComponent: 0, numRepetitions: 20, }); @@ -447,7 +453,7 @@ describe("SelectRandomNumbers and SampleRandomNumbers tag tests", async () => { for (let doenetML of doenetMLs) { await test_combined_statistics({ doenetML, - componentName: "/s", + name: "/s", numSamplesPerComponent: 10, numRepetitions: 40, validValues: [-3, -1, 1, 3, 5], @@ -470,7 +476,7 @@ describe("SelectRandomNumbers and SampleRandomNumbers tag tests", async () => { for (let doenetML of doenetMLs) { await test_combined_statistics({ doenetML, - componentName: "/s", + name: "/s", numSamplesPerComponent: 1, numRepetitions: 400, validValues: [1, 3], @@ -493,7 +499,7 @@ describe("SelectRandomNumbers and SampleRandomNumbers tag tests", async () => { for (let doenetML of doenetMLs) { await test_combined_statistics({ doenetML, - componentName: "/s", + name: "/s", numSamplesPerComponent: 1, numRepetitions: 400, validValues: [0.5, 2.5, 4.5], @@ -516,7 +522,7 @@ describe("SelectRandomNumbers and SampleRandomNumbers tag tests", async () => { for (let doenetML of doenetMLs) { await test_combined_statistics({ doenetML, - componentName: "/s", + name: "/s", numSamplesPerComponent: 1, numRepetitions: 400, validValues: [7.5, 9.5], @@ -543,7 +549,7 @@ describe("SelectRandomNumbers and SampleRandomNumbers tag tests", async () => { for (let doenetML of doenetMLs) { await test_combined_statistics({ doenetML, - componentName: "/s", + name: "/s", numSamplesPerComponent: 5, numRepetitions: 80, validValues: vals, @@ -570,7 +576,7 @@ describe("SelectRandomNumbers and SampleRandomNumbers tag tests", async () => { for (let doenetML of doenetMLs) { await test_combined_statistics({ doenetML, - componentName: "/s", + name: "/s", numSamplesPerComponent: 10, numRepetitions: 40, validValues: vals, @@ -673,22 +679,22 @@ describe("SelectRandomNumbers and SampleRandomNumbers tag tests", async () => { // Nothing changes when change mathInputs await updateMathInputValue({ latex: "7", - componentName: "/numToSelect", + name: "/numToSelect", core, }); await updateMathInputValue({ latex: "11", - componentName: "/maxNum", + name: "/maxNum", core, }); await updateMathInputValue({ latex: "16", - componentName: "/numToSelect2", + name: "/numToSelect2", core, }); await updateMathInputValue({ latex: "18", - componentName: "/maxNum2", + name: "/maxNum2", core, }); @@ -754,12 +760,12 @@ describe("SelectRandomNumbers and SampleRandomNumbers tag tests", async () => { // Get new samples when change number of samples await updateMathInputValue({ latex: "70", - componentName: "/numSamples", + name: "/numSamples", core, }); await updateMathInputValue({ latex: "160", - componentName: "/numSamples2", + name: "/numSamples2", core, }); @@ -801,12 +807,12 @@ describe("SelectRandomNumbers and SampleRandomNumbers tag tests", async () => { // Get new samples when sample parameters await updateMathInputValue({ latex: "4", - componentName: "/maxNum", + name: "/maxNum", core, }); await updateMathInputValue({ latex: "18", - componentName: "/standardDeviation", + name: "/standardDeviation", core, }); @@ -901,22 +907,22 @@ describe("SelectRandomNumbers and SampleRandomNumbers tag tests", async () => { await check_sampled_numbers([]); // sample one variable - await updateMathInputValue({ latex: "1", componentName: "/mi1", core }); + await updateMathInputValue({ latex: "1", name: "/mi1", core }); let stateVariables = await returnAllStateVariables(core); sampledNumbers.push(stateVariables["/a/n"].stateValues.value); await check_sampled_numbers(sampledNumbers); // go back to nothing - await updateMathInputValue({ latex: "0", componentName: "/mi1", core }); + await updateMathInputValue({ latex: "0", name: "/mi1", core }); await check_sampled_numbers([]); // get same number back - await updateMathInputValue({ latex: "1", componentName: "/mi1", core }); + await updateMathInputValue({ latex: "1", name: "/mi1", core }); await check_sampled_numbers(sampledNumbers); // get two more samples - await updateMathInputValue({ latex: "3", componentName: "/mi1", core }); + await updateMathInputValue({ latex: "3", name: "/mi1", core }); stateVariables = await returnAllStateVariables(core); let n1 = stateVariables["/a/n"].stateValues.value; @@ -928,15 +934,15 @@ describe("SelectRandomNumbers and SampleRandomNumbers tag tests", async () => { await check_sampled_numbers(sampledNumbers); // go back to nothing - await updateMathInputValue({ latex: "0", componentName: "/mi1", core }); + await updateMathInputValue({ latex: "0", name: "/mi1", core }); await check_sampled_numbers([]); // get first two numbers back - await updateMathInputValue({ latex: "2", componentName: "/mi1", core }); + await updateMathInputValue({ latex: "2", name: "/mi1", core }); await check_sampled_numbers(sampledNumbers.slice(0, 2)); // get six total samples - await updateMathInputValue({ latex: "6", componentName: "/mi1", core }); + await updateMathInputValue({ latex: "6", name: "/mi1", core }); stateVariables = await returnAllStateVariables(core); n1 = stateVariables["/a/n"].stateValues.value; @@ -954,11 +960,11 @@ describe("SelectRandomNumbers and SampleRandomNumbers tag tests", async () => { await check_sampled_numbers(sampledNumbers); // go back to nothing - await updateMathInputValue({ latex: "0", componentName: "/mi1", core }); + await updateMathInputValue({ latex: "0", name: "/mi1", core }); await check_sampled_numbers([]); // get all six back - await updateMathInputValue({ latex: "6", componentName: "/mi1", core }); + await updateMathInputValue({ latex: "6", name: "/mi1", core }); await check_sampled_numbers(sampledNumbers); } @@ -1525,7 +1531,7 @@ describe("SelectRandomNumbers and SampleRandomNumbers tag tests", async () => { numSamples = 50; await updateMathInputValue({ latex: numSamples.toString(), - componentName: "/numSamples", + name: "/numSamples", core, }); @@ -1551,12 +1557,12 @@ describe("SelectRandomNumbers and SampleRandomNumbers tag tests", async () => { specifiedTo = 0; await updateMathInputValue({ latex: specifiedFrom.toString(), - componentName: "/specifiedFrom", + name: "/specifiedFrom", core, }); await updateMathInputValue({ latex: specifiedTo.toString(), - componentName: "/specifiedTo", + name: "/specifiedTo", core, }); @@ -1582,7 +1588,7 @@ describe("SelectRandomNumbers and SampleRandomNumbers tag tests", async () => { await updateTextInputValue({ text: specifiedType, - componentName: "/type", + name: "/type", core, }); @@ -1610,17 +1616,17 @@ describe("SelectRandomNumbers and SampleRandomNumbers tag tests", async () => { await updateMathInputValue({ latex: specifiedFrom.toString(), - componentName: "/specifiedFrom", + name: "/specifiedFrom", core, }); await updateMathInputValue({ latex: specifiedTo.toString(), - componentName: "/specifiedTo", + name: "/specifiedTo", core, }); await updateMathInputValue({ latex: specifiedStep.toString(), - componentName: "/specifiedStep", + name: "/specifiedStep", core, }); @@ -1646,7 +1652,7 @@ describe("SelectRandomNumbers and SampleRandomNumbers tag tests", async () => { await updateTextInputValue({ text: specifiedType, - componentName: "/type", + name: "/type", core, }); @@ -1673,12 +1679,12 @@ describe("SelectRandomNumbers and SampleRandomNumbers tag tests", async () => { await updateMathInputValue({ latex: specifiedMean.toString(), - componentName: "/specifiedMean", + name: "/specifiedMean", core, }); await updateMathInputValue({ latex: specifiedVariance.toString(), - componentName: "/specifiedVariance", + name: "/specifiedVariance", core, }); @@ -1703,7 +1709,7 @@ describe("SelectRandomNumbers and SampleRandomNumbers tag tests", async () => { numSamples = 200; await updateMathInputValue({ latex: numSamples.toString(), - componentName: "/numSamples", + name: "/numSamples", core, }); @@ -1728,7 +1734,7 @@ describe("SelectRandomNumbers and SampleRandomNumbers tag tests", async () => { numSamples = 20; await updateMathInputValue({ latex: numSamples.toString(), - componentName: "/numSamples", + name: "/numSamples", core, }); @@ -1844,12 +1850,7 @@ describe("SelectRandomNumbers and SampleRandomNumbers tag tests", async () => { rn1Rounded.toString(), ); - await core.requestAction({ - componentName: "/resamp1", - actionName: "callAction", - args: {}, - event: null, - }); + await callAction({ name: "/resamp1", core }); stateVariables = await returnAllStateVariables(core); @@ -1871,12 +1872,7 @@ describe("SelectRandomNumbers and SampleRandomNumbers tag tests", async () => { rn3Rounded.toString(), ); - await core.requestAction({ - componentName: "/resamp2", - actionName: "callAction", - args: {}, - event: null, - }); + await callAction({ name: "/resamp2", core }); stateVariables = await returnAllStateVariables(core); diff --git a/packages/doenetml-worker/src/test/tagSpecific/sequence.test.ts b/packages/doenetml-worker/src/test/tagSpecific/sequence.test.ts index 2de1fc868..cbc8f2c05 100644 --- a/packages/doenetml-worker/src/test/tagSpecific/sequence.test.ts +++ b/packages/doenetml-worker/src/test/tagSpecific/sequence.test.ts @@ -9,6 +9,7 @@ import me from "math-expressions"; const Mock = vi.fn(); vi.stubGlobal("postMessage", Mock); +vi.mock("hyperformula"); describe("Sequence tag tests", async () => { it("number sequence, no parameters", async () => { @@ -548,7 +549,7 @@ describe("Sequence tag tests", async () => { // also exclude 7 await updateMathInputValue({ latex: "7", - componentName: "/exclude2", + name: "/exclude2", core, }); @@ -569,7 +570,7 @@ describe("Sequence tag tests", async () => { // also exclude 6 twice await updateMathInputValue({ latex: "6", - componentName: "/exclude2", + name: "/exclude2", core, }); @@ -590,7 +591,7 @@ describe("Sequence tag tests", async () => { // also exclude 12 await updateMathInputValue({ latex: "12", - componentName: "/exclude2", + name: "/exclude2", core, }); @@ -611,7 +612,7 @@ describe("Sequence tag tests", async () => { // also exclude 3 await updateMathInputValue({ latex: "3", - componentName: "/exclude2", + name: "/exclude2", core, }); @@ -632,7 +633,7 @@ describe("Sequence tag tests", async () => { // don't exclude anything else await updateMathInputValue({ latex: "", - componentName: "/exclude2", + name: "/exclude2", core, }); @@ -697,7 +698,7 @@ describe("Sequence tag tests", async () => { } // also exclude i - await updateTextInputValue({ text: "i", componentName: "/e", core }); + await updateTextInputValue({ text: "i", name: "/e", core }); stateVariables = await returnAllStateVariables(core); children = stateVariables["/p"].activeChildren.map( (x) => stateVariables[x.componentName], @@ -715,7 +716,7 @@ describe("Sequence tag tests", async () => { } // also exclude f twice - await updateTextInputValue({ text: "f", componentName: "/e", core }); + await updateTextInputValue({ text: "f", name: "/e", core }); stateVariables = await returnAllStateVariables(core); children = stateVariables["/p"].activeChildren.map( @@ -734,7 +735,7 @@ describe("Sequence tag tests", async () => { } // also exclude l - await updateTextInputValue({ text: "l", componentName: "/e", core }); + await updateTextInputValue({ text: "l", name: "/e", core }); stateVariables = await returnAllStateVariables(core); children = stateVariables["/p"].activeChildren.map( @@ -753,7 +754,7 @@ describe("Sequence tag tests", async () => { } // also exclude C - await updateTextInputValue({ text: "C", componentName: "/e", core }); + await updateTextInputValue({ text: "C", name: "/e", core }); stateVariables = await returnAllStateVariables(core); children = stateVariables["/p"].activeChildren.map( @@ -772,7 +773,7 @@ describe("Sequence tag tests", async () => { } // don't exclude anything else - await updateTextInputValue({ text: "", componentName: "/e", core }); + await updateTextInputValue({ text: "", name: "/e", core }); stateVariables = await returnAllStateVariables(core); children = stateVariables["/p"].activeChildren.map( @@ -818,7 +819,7 @@ describe("Sequence tag tests", async () => { } // also exclude 9x - await updateMathInputValue({ latex: "9x", componentName: "/e", core }); + await updateMathInputValue({ latex: "9x", name: "/e", core }); stateVariables = await returnAllStateVariables(core); children = stateVariables["/p"].activeChildren.map( @@ -839,7 +840,7 @@ describe("Sequence tag tests", async () => { } // also exclude 6x twice - await updateMathInputValue({ latex: "6x", componentName: "/e", core }); + await updateMathInputValue({ latex: "6x", name: "/e", core }); stateVariables = await returnAllStateVariables(core); children = stateVariables["/p"].activeChildren.map( @@ -860,7 +861,7 @@ describe("Sequence tag tests", async () => { } // also exclude 12x - await updateMathInputValue({ latex: "12x", componentName: "/e", core }); + await updateMathInputValue({ latex: "12x", name: "/e", core }); stateVariables = await returnAllStateVariables(core); children = stateVariables["/p"].activeChildren.map( @@ -881,7 +882,7 @@ describe("Sequence tag tests", async () => { } // also exclude 3x - await updateMathInputValue({ latex: "3x", componentName: "/e", core }); + await updateMathInputValue({ latex: "3x", name: "/e", core }); stateVariables = await returnAllStateVariables(core); children = stateVariables["/p"].activeChildren.map( @@ -902,7 +903,7 @@ describe("Sequence tag tests", async () => { } // don't exclude anything else - await updateMathInputValue({ latex: "", componentName: "/e", core }); + await updateMathInputValue({ latex: "", name: "/e", core }); stateVariables = await returnAllStateVariables(core); children = stateVariables["/p"].activeChildren.map( @@ -1009,7 +1010,7 @@ describe("Sequence tag tests", async () => { ); expect(children.length).eq(0); - await updateMathInputValue({ latex: "2", componentName: "/n", core }); + await updateMathInputValue({ latex: "2", name: "/n", core }); stateVariables = await returnAllStateVariables(core); children = stateVariables["/p"].activeChildren.map( @@ -1068,8 +1069,8 @@ describe("Sequence tag tests", async () => { ); expect(stateVariables["/s2"].stateValues.text).eq("sequence 2: "); - await updateMathInputValue({ latex: "6", componentName: "/n1", core }); - await updateMathInputValue({ latex: "6", componentName: "/n2", core }); + await updateMathInputValue({ latex: "6", name: "/n1", core }); + await updateMathInputValue({ latex: "6", name: "/n2", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/s1"].stateValues.text).eq( "sequence 1: 1, 2, 3, 4, 5, 6", @@ -1078,12 +1079,12 @@ describe("Sequence tag tests", async () => { await updateBooleanInputValue({ boolean: true, - componentName: "/h1", + name: "/h1", core, }); await updateBooleanInputValue({ boolean: false, - componentName: "/h2", + name: "/h2", core, }); stateVariables = await returnAllStateVariables(core); @@ -1092,8 +1093,8 @@ describe("Sequence tag tests", async () => { "sequence 2: 1, 2, 3, 4, 5, 6", ); - await updateMathInputValue({ latex: "8", componentName: "/n1", core }); - await updateMathInputValue({ latex: "8", componentName: "/n2", core }); + await updateMathInputValue({ latex: "8", name: "/n1", core }); + await updateMathInputValue({ latex: "8", name: "/n2", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/s1"].stateValues.text).eq("sequence 1: "); expect(stateVariables["/s2"].stateValues.text).eq( @@ -1102,12 +1103,12 @@ describe("Sequence tag tests", async () => { await updateBooleanInputValue({ boolean: false, - componentName: "/h1", + name: "/h1", core, }); await updateBooleanInputValue({ boolean: true, - componentName: "/h2", + name: "/h2", core, }); stateVariables = await returnAllStateVariables(core); @@ -1116,8 +1117,8 @@ describe("Sequence tag tests", async () => { ); expect(stateVariables["/s2"].stateValues.text).eq("sequence 2: "); - await updateMathInputValue({ latex: "3", componentName: "/n1", core }); - await updateMathInputValue({ latex: "3", componentName: "/n2", core }); + await updateMathInputValue({ latex: "3", name: "/n1", core }); + await updateMathInputValue({ latex: "3", name: "/n2", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/s1"].stateValues.text).eq( "sequence 1: 1, 2, 3", @@ -1126,12 +1127,12 @@ describe("Sequence tag tests", async () => { await updateBooleanInputValue({ boolean: true, - componentName: "/h1", + name: "/h1", core, }); await updateBooleanInputValue({ boolean: false, - componentName: "/h2", + name: "/h2", core, }); stateVariables = await returnAllStateVariables(core); @@ -1140,8 +1141,8 @@ describe("Sequence tag tests", async () => { "sequence 2: 1, 2, 3", ); - await updateMathInputValue({ latex: "4", componentName: "/n1", core }); - await updateMathInputValue({ latex: "4", componentName: "/n2", core }); + await updateMathInputValue({ latex: "4", name: "/n1", core }); + await updateMathInputValue({ latex: "4", name: "/n2", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/s1"].stateValues.text).eq("sequence 1: "); expect(stateVariables["/s2"].stateValues.text).eq( @@ -1166,43 +1167,43 @@ describe("Sequence tag tests", async () => { let stateVariables = await returnAllStateVariables(core); expect(stateVariables["/theList"].stateValues.text).eq("1, 3, 5, 7"); - await updateMathInputValue({ latex: "21", componentName: "/a2", core }); + await updateMathInputValue({ latex: "21", name: "/a2", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/theList"].stateValues.text).eq("1, 3, 5, 7"); - await updateMathInputValue({ latex: "2", componentName: "/b2", core }); + await updateMathInputValue({ latex: "2", name: "/b2", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/theList"].stateValues.text).eq("1, 3, 5, 7"); await updateMathInputValue({ latex: "4", - componentName: "/from", + name: "/from", core, }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/theList"].stateValues.text).eq("4, 6"); - await updateMathInputValue({ latex: "8", componentName: "/a2", core }); + await updateMathInputValue({ latex: "8", name: "/a2", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/theList"].stateValues.text).eq("4, 6"); - await updateMathInputValue({ latex: "2", componentName: "/b2", core }); + await updateMathInputValue({ latex: "2", name: "/b2", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/theList"].stateValues.text).eq("4, 6"); await updateMathInputValue({ latex: "6", - componentName: "/step", + name: "/step", core, }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/theList"].stateValues.text).eq("4"); - await updateMathInputValue({ latex: "9", componentName: "/a2", core }); + await updateMathInputValue({ latex: "9", name: "/a2", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/theList"].stateValues.text).eq("4"); - await updateMathInputValue({ latex: "41", componentName: "/b2", core }); + await updateMathInputValue({ latex: "41", name: "/b2", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/theList"].stateValues.text).eq("4"); }); @@ -1225,79 +1226,79 @@ describe("Sequence tag tests", async () => { let stateVariables = await returnAllStateVariables(core); expect(stateVariables["/theList"].stateValues.text).eq("1, 3, 5, 7"); - await updateMathInputValue({ latex: "21", componentName: "/a2", core }); + await updateMathInputValue({ latex: "21", name: "/a2", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/theList"].stateValues.text).eq("21, 3, 5, 7"); - await updateMathInputValue({ latex: "0", componentName: "/b2", core }); + await updateMathInputValue({ latex: "0", name: "/b2", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/theList"].stateValues.text).eq("21, 0, 5, 7"); await updateMathInputValue({ latex: "4", - componentName: "/from", + name: "/from", core, }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/theList"].stateValues.text).eq("4, 6"); - await updateMathInputValue({ latex: "8", componentName: "/a2", core }); + await updateMathInputValue({ latex: "8", name: "/a2", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/theList"].stateValues.text).eq("8, 6"); - await updateMathInputValue({ latex: "2", componentName: "/b2", core }); + await updateMathInputValue({ latex: "2", name: "/b2", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/theList"].stateValues.text).eq("8, 2"); await updateMathInputValue({ latex: "6", - componentName: "/step", + name: "/step", core, }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/theList"].stateValues.text).eq("4"); - await updateMathInputValue({ latex: "9", componentName: "/a2", core }); + await updateMathInputValue({ latex: "9", name: "/a2", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/theList"].stateValues.text).eq("9"); - await updateMathInputValue({ latex: "41", componentName: "/b2", core }); + await updateMathInputValue({ latex: "41", name: "/b2", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/theList"].stateValues.text).eq("9"); await updateBooleanInputValue({ boolean: true, - componentName: "/fx", + name: "/fx", core, }); await updateMathInputValue({ latex: "1", - componentName: "/step", + name: "/step", core, }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/theList"].stateValues.text).eq("4, 5, 6, 7"); - await updateMathInputValue({ latex: "9", componentName: "/a2", core }); + await updateMathInputValue({ latex: "9", name: "/a2", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/theList"].stateValues.text).eq("4, 5, 6, 7"); - await updateMathInputValue({ latex: "41", componentName: "/b2", core }); + await updateMathInputValue({ latex: "41", name: "/b2", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/theList"].stateValues.text).eq("4, 5, 6, 7"); await updateBooleanInputValue({ boolean: false, - componentName: "/fx", + name: "/fx", core, }); - await updateMathInputValue({ latex: "9", componentName: "/a2", core }); + await updateMathInputValue({ latex: "9", name: "/a2", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/theList"].stateValues.text).eq("9, 5, 6, 7"); - await updateMathInputValue({ latex: "41", componentName: "/b2", core }); + await updateMathInputValue({ latex: "41", name: "/b2", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/theList"].stateValues.text).eq("9, 41, 6, 7"); }); @@ -1326,7 +1327,7 @@ describe("Sequence tag tests", async () => { expect(stateVariables["/a3"].stateValues.value.tree).eq(1); expect(stateVariables["/b3"].stateValues.value.tree).eq(3); - await updateMathInputValue({ latex: "21", componentName: "/a2", core }); + await updateMathInputValue({ latex: "21", name: "/a2", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/theList"].stateValues.text).eq("1, 3, 5, 7"); expect(stateVariables["/a2"].stateValues.value.tree).eq(21); @@ -1334,7 +1335,7 @@ describe("Sequence tag tests", async () => { expect(stateVariables["/a3"].stateValues.value.tree).eq(21); expect(stateVariables["/b3"].stateValues.value.tree).eq(3); - await updateMathInputValue({ latex: "0", componentName: "/b2", core }); + await updateMathInputValue({ latex: "0", name: "/b2", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/theList"].stateValues.text).eq("1, 3, 5, 7"); expect(stateVariables["/a2"].stateValues.value.tree).eq(21); @@ -1344,7 +1345,7 @@ describe("Sequence tag tests", async () => { await updateMathInputValue({ latex: "4", - componentName: "/from", + name: "/from", core, }); stateVariables = await returnAllStateVariables(core); @@ -1354,7 +1355,7 @@ describe("Sequence tag tests", async () => { expect(stateVariables["/a3"].stateValues.value.tree).eq(21); expect(stateVariables["/b3"].stateValues.value.tree).eq(0); - await updateMathInputValue({ latex: "8", componentName: "/a2", core }); + await updateMathInputValue({ latex: "8", name: "/a2", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/theList"].stateValues.text).eq("4, 6"); expect(stateVariables["/a2"].stateValues.value.tree).eq(8); @@ -1364,7 +1365,7 @@ describe("Sequence tag tests", async () => { await updateMathInputValue({ latex: "6", - componentName: "/step", + name: "/step", core, }); stateVariables = await returnAllStateVariables(core); @@ -1374,7 +1375,7 @@ describe("Sequence tag tests", async () => { expect(stateVariables["/a3"].stateValues.value.tree).eq(8); expect(stateVariables["/b3"].stateValues.value.tree).eq(0); - await updateMathInputValue({ latex: "9", componentName: "/a2", core }); + await updateMathInputValue({ latex: "9", name: "/a2", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/theList"].stateValues.text).eq("4"); expect(stateVariables["/a2"].stateValues.value.tree).eq(9); @@ -1382,7 +1383,7 @@ describe("Sequence tag tests", async () => { expect(stateVariables["/a3"].stateValues.value.tree).eq(9); expect(stateVariables["/b3"].stateValues.value.tree).eq(0); - await updateMathInputValue({ latex: "2", componentName: "/b2", core }); + await updateMathInputValue({ latex: "2", name: "/b2", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/theList"].stateValues.text).eq("4"); expect(stateVariables["/a2"].stateValues.value.tree).eq(9); @@ -1392,7 +1393,7 @@ describe("Sequence tag tests", async () => { await updateMathInputValue({ latex: "8", - componentName: "/from", + name: "/from", core, }); stateVariables = await returnAllStateVariables(core); @@ -1402,7 +1403,7 @@ describe("Sequence tag tests", async () => { expect(stateVariables["/a3"].stateValues.value.tree).eq(9); expect(stateVariables["/b3"].stateValues.value.tree).eq(2); - await updateMathInputValue({ latex: "3", componentName: "/a2", core }); + await updateMathInputValue({ latex: "3", name: "/a2", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/theList"].stateValues.text).eq(""); expect(stateVariables["/a2"].stateValues.value.tree).eq(3); @@ -1412,12 +1413,12 @@ describe("Sequence tag tests", async () => { await updateMathInputValue({ latex: "3", - componentName: "/step", + name: "/step", core, }); await updateMathInputValue({ latex: "0", - componentName: "/from", + name: "/from", core, }); stateVariables = await returnAllStateVariables(core); @@ -1427,8 +1428,8 @@ describe("Sequence tag tests", async () => { expect(stateVariables["/a3"].stateValues.value.tree).eq(3); expect(stateVariables["/b3"].stateValues.value.tree).eq(2); - await updateMathInputValue({ latex: "8", componentName: "/a2", core }); - await updateMathInputValue({ latex: "7", componentName: "/b2", core }); + await updateMathInputValue({ latex: "8", name: "/a2", core }); + await updateMathInputValue({ latex: "7", name: "/b2", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/theList"].stateValues.text).eq("0, 3, 6"); expect(stateVariables["/a2"].stateValues.value.tree).eq(8); diff --git a/packages/doenetml-worker/src/test/tagSpecific/shuffle.test.ts b/packages/doenetml-worker/src/test/tagSpecific/shuffle.test.ts index 78057180b..ce8f6b5a7 100644 --- a/packages/doenetml-worker/src/test/tagSpecific/shuffle.test.ts +++ b/packages/doenetml-worker/src/test/tagSpecific/shuffle.test.ts @@ -4,6 +4,7 @@ import { updateMathInputValue } from "../utils/actions"; const Mock = vi.fn(); vi.stubGlobal("postMessage", Mock); +vi.mock("hyperformula"); describe("Shuffle tag tests", async () => { it("consistent order for n elements for given variant", async () => { @@ -46,7 +47,7 @@ describe("Shuffle tag tests", async () => { n = 8; await updateMathInputValue({ latex: n.toString(), - componentName: "/n", + name: "/n", core, }); @@ -69,7 +70,7 @@ describe("Shuffle tag tests", async () => { m = 3; await updateMathInputValue({ latex: m.toString(), - componentName: "/m", + name: "/m", core, }); @@ -90,7 +91,7 @@ describe("Shuffle tag tests", async () => { n = 10; await updateMathInputValue({ latex: n.toString(), - componentName: "/n", + name: "/n", core, }); @@ -138,7 +139,7 @@ describe("Shuffle tag tests", async () => { n = 8; await updateMathInputValue({ latex: n.toString(), - componentName: "/n", + name: "/n", core, }); @@ -163,7 +164,7 @@ describe("Shuffle tag tests", async () => { m = 3; await updateMathInputValue({ latex: m.toString(), - componentName: "/m", + name: "/m", core, }); @@ -184,7 +185,7 @@ describe("Shuffle tag tests", async () => { n = 10; await updateMathInputValue({ latex: n.toString(), - componentName: "/n", + name: "/n", core, }); diff --git a/packages/doenetml-worker/src/test/tagSpecific/sidebyside.test.ts b/packages/doenetml-worker/src/test/tagSpecific/sidebyside.test.ts new file mode 100644 index 000000000..7dd26721f --- /dev/null +++ b/packages/doenetml-worker/src/test/tagSpecific/sidebyside.test.ts @@ -0,0 +1,5440 @@ +import { describe, expect, it, vi } from "vitest"; +import { createTestCore, returnAllStateVariables } from "../utils/test-core"; +import { updateMathInputValue, updateTextInputValue } from "../utils/actions"; +import Core from "../../Core"; + +const Mock = vi.fn(); +vi.stubGlobal("postMessage", Mock); +vi.mock("hyperformula"); + +let checkSingleColumnSbs = async function ({ + core, + specifiedWidth = null, + specifiedMargins = [null, null], + specifiedValign = null, + sbsName = "/sbs", + isSbsGroup = false, +}: { + core: Core; + specifiedWidth?: number | null; + specifiedMargins?: (number | null)[]; + specifiedValign?: null | "top" | "middle" | "bottom"; + sbsName?: string; + isSbsGroup?: boolean; +}) { + let actualWidth = specifiedWidth; + let actualLeftMargin = specifiedMargins[0]; + let actualRightMargin = specifiedMargins[1]; + + if (actualWidth === null) { + if (actualLeftMargin === null) { + if (actualRightMargin === null) { + actualWidth = 100; + actualLeftMargin = actualRightMargin = 0; + } else { + actualLeftMargin = 0; + actualWidth = Math.max(0, 100 - actualRightMargin); + } + } else { + if (actualRightMargin === null) { + actualRightMargin = 0; + actualWidth = Math.max(0, 100 - actualLeftMargin); + } else { + actualWidth = Math.max( + 0, + 100 - actualLeftMargin - actualRightMargin, + ); + } + } + } else { + if (actualLeftMargin === null) { + if (actualRightMargin === null) { + actualLeftMargin = actualRightMargin = Math.max( + 0, + (100 - actualWidth) / 2, + ); + } else { + actualLeftMargin = Math.max( + 0, + 100 - actualWidth - actualRightMargin, + ); + } + } else { + if (actualRightMargin === null) { + actualRightMargin = Math.max( + 0, + 100 - actualWidth - actualLeftMargin, + ); + } + } + } + + let originalTotal = actualWidth + actualLeftMargin + actualRightMargin; + + if (originalTotal > 100) { + // rescale to 100 + let rescale = 100 / originalTotal; + actualWidth *= rescale; + actualLeftMargin *= rescale; + actualRightMargin *= rescale; + } else if (originalTotal < 100) { + // add to right margin + actualRightMargin += 100 - originalTotal; + } + + let valign = specifiedValign ? specifiedValign : "top"; + + let stateVariables = await returnAllStateVariables(core); + + let specifiedWidthName = isSbsGroup + ? "specifiedWidths" + : "allWidthsSpecified"; + let specifiedMarginName = isSbsGroup + ? "specifiedMargins" + : "allMarginsSpecified"; + + expect(stateVariables[sbsName].stateValues.widths.length).eq(1); + expect(stateVariables[sbsName].stateValues[specifiedWidthName]).eqls([ + specifiedWidth, + ]); + expect(stateVariables[sbsName].stateValues.widths[0]).closeTo( + actualWidth, + 1e-5, + ); + expect(stateVariables[sbsName].stateValues[specifiedMarginName]).eqls( + specifiedMargins, + ); + expect(stateVariables[sbsName].stateValues.margins.length).eq(2); + expect(stateVariables[sbsName].stateValues.margins[0]).closeTo( + actualLeftMargin, + 1e-5, + ); + expect(stateVariables[sbsName].stateValues.margins[1]).closeTo( + actualRightMargin, + 1e-5, + ); + expect(stateVariables[sbsName].stateValues.valigns).eqls([valign]); +}; + +let checkTwoColumnSbs = async function ({ + core, + specifiedWidths = [null, null], + specifiedMargins = [null, null], + specifiedValigns = [null, null], + sbsName = "/sbs", + isSbsGroup = false, +}: { + core: Core; + specifiedWidths?: (number | null)[]; + specifiedMargins?: (number | null)[]; + specifiedValigns?: (null | "top" | "middle" | "bottom")[]; + sbsName?: string; + isSbsGroup?: boolean; +}) { + let actualWidth1 = specifiedWidths[0]; + let actualWidth2 = specifiedWidths[1]; + let actualLeftMargin = specifiedMargins[0]; + let actualRightMargin = specifiedMargins[1]; + let actualGap = 0; + + if (actualWidth1 === null) { + if (actualWidth2 === null) { + if (actualLeftMargin === null) { + if (actualRightMargin === null) { + actualWidth1 = actualWidth2 = 50; + actualLeftMargin = actualRightMargin = 0; + } else { + actualWidth1 = actualWidth2 = Math.max( + 0, + (100 - 2 * actualRightMargin) / 2, + ); + actualLeftMargin = 0; + } + } else { + if (actualRightMargin === null) { + actualWidth1 = actualWidth2 = Math.max( + 0, + (100 - 2 * actualLeftMargin) / 2, + ); + actualRightMargin = 0; + } else { + actualWidth1 = actualWidth2 = Math.max( + 0, + (100 - 2 * (actualLeftMargin + actualRightMargin)) / 2, + ); + } + } + } else { + if (actualLeftMargin === null) { + if (actualRightMargin === null) { + actualWidth1 = Math.max(0, 100 - actualWidth2); + actualLeftMargin = actualRightMargin = 0; + } else { + actualWidth1 = Math.max( + 0, + 100 - actualWidth2 - 2 * actualRightMargin, + ); + actualLeftMargin = 0; + } + } else { + if (actualRightMargin === null) { + actualWidth1 = Math.max( + 0, + 100 - actualWidth2 - 2 * actualLeftMargin, + ); + actualRightMargin = 0; + } else { + actualWidth1 = Math.max( + 0, + 100 - + actualWidth2 - + 2 * (actualLeftMargin + actualRightMargin), + ); + } + } + } + } else { + if (actualWidth2 === null) { + if (actualLeftMargin === null) { + if (actualRightMargin === null) { + actualWidth2 = Math.max(0, 100 - actualWidth1); + actualLeftMargin = actualRightMargin = 0; + } else { + actualWidth2 = Math.max( + 0, + 100 - actualWidth1 - 2 * actualRightMargin, + ); + actualLeftMargin = 0; + } + } else { + if (actualRightMargin === null) { + actualWidth2 = Math.max( + 0, + 100 - actualWidth1 - 2 * actualLeftMargin, + ); + actualRightMargin = 0; + } else { + actualWidth2 = Math.max( + 0, + 100 - + actualWidth1 - + 2 * (actualLeftMargin + actualRightMargin), + ); + } + } + } else { + if (actualLeftMargin === null) { + if (actualRightMargin === null) { + actualLeftMargin = actualRightMargin = Math.max( + 0, + (100 - actualWidth1 - actualWidth2) / 4, + ); + } else { + actualLeftMargin = Math.max( + 0, + (100 - + actualWidth1 - + actualWidth2 - + 2 * actualRightMargin) / + 2, + ); + } + } else { + if (actualRightMargin === null) { + actualRightMargin = Math.max( + 0, + (100 - + actualWidth1 - + actualWidth2 - + 2 * actualLeftMargin) / + 2, + ); + } + } + } + } + + let originalTotal = + actualWidth1 + + actualWidth2 + + 2 * (actualLeftMargin + actualRightMargin); + + if (originalTotal > 100) { + // rescale to 100 + let rescale = 100 / originalTotal; + actualWidth1 *= rescale; + actualWidth2 *= rescale; + actualLeftMargin *= rescale; + actualRightMargin *= rescale; + } else if (originalTotal < 100) { + // add to gap + actualGap = 100 - originalTotal; + } + + let valigns = [ + specifiedValigns[0] ? specifiedValigns[0] : "top", + specifiedValigns[1] ? specifiedValigns[1] : "top", + ]; + + let specifiedWidthName = isSbsGroup + ? "specifiedWidths" + : "allWidthsSpecified"; + let specifiedMarginName = isSbsGroup + ? "specifiedMargins" + : "allMarginsSpecified"; + + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables[sbsName].stateValues[specifiedWidthName]).eqls( + specifiedWidths, + ); + expect(stateVariables[sbsName].stateValues.widths.length).eq(2); + expect(stateVariables[sbsName].stateValues.widths[0]).closeTo( + actualWidth1, + 1e-5, + ); + expect(stateVariables[sbsName].stateValues.widths[1]).closeTo( + actualWidth2, + 1e-5, + ); + expect(stateVariables[sbsName].stateValues[specifiedMarginName]).eqls( + specifiedMargins, + ); + expect(stateVariables[sbsName].stateValues.margins.length).eq(2); + expect(stateVariables[sbsName].stateValues.margins[0]).closeTo( + actualLeftMargin, + 1e-5, + ); + expect(stateVariables[sbsName].stateValues.margins[1]).closeTo( + actualRightMargin, + 1e-5, + ); + expect(stateVariables[sbsName].stateValues.gapWidth).closeTo( + actualGap, + 1e-5, + ); + expect(stateVariables[sbsName].stateValues.valigns).eqls(valigns); +}; + +let checkFourColumnSbs = async function ({ + core, + specifiedWidths = [null, null, null, null], + specifiedMargins = [null, null], + specifiedValigns = [null, null, null, null], + sbsName = "/sbs", + isSbsGroup = false, +}: { + core: Core; + specifiedWidths?: (number | null)[]; + specifiedMargins?: (number | null)[]; + specifiedValigns?: (null | "top" | "middle" | "bottom")[]; + sbsName?: string; + isSbsGroup?: boolean; +}) { + let totalWidthSpecified = 0; + let nWidthsUndefined = 0; + + for (let ind = 0; ind < 4; ind++) { + let width = specifiedWidths[ind]; + if (width === null) { + nWidthsUndefined++; + } else { + totalWidthSpecified += width; + } + } + + let totalMarginSpecified = 0; + let nMarginsUndefined = 0; + + for (let ind = 0; ind < 2; ind++) { + let margin = specifiedMargins[ind]; + if (margin === null) { + nMarginsUndefined++; + } else { + totalMarginSpecified += margin; + } + } + totalMarginSpecified *= 4; + + let actualWidths: number[]; + let actualMargins: number[]; + let actualGap = 0; + + if (totalWidthSpecified + totalMarginSpecified >= 100) { + // we are already over 100% + // anything null becomes width 0 + // everything else is normalized to add up to 100 + + let normalization = 100 / (totalWidthSpecified + totalMarginSpecified); + actualWidths = specifiedWidths.map((v) => + v === null ? 0 : v * normalization, + ); + actualMargins = specifiedMargins.map((v) => + v === null ? 0 : v * normalization, + ); + } else { + // since we are under 100%, we try the following to get to 100% + // 1. if there are any null widths, + // define them to be the same value that makes the total 100% + // and make any null margins be zero + // 2. else, if there are any null margins, + // define them to be the same value that makes the total 100% + // 3. else set gapWidth to make the total 100% + + if (nWidthsUndefined > 0) { + let newWidth = + (100 - (totalWidthSpecified + totalMarginSpecified)) / + nWidthsUndefined; + actualWidths = specifiedWidths.map((v) => + v === null ? newWidth : v, + ); + + actualMargins = specifiedMargins.map((v) => (v === null ? 0 : v)); + } else if (nMarginsUndefined > 0) { + // Note: there aren't any null specified widths since nWidthsUndefined == 0, but typescript doesn't know it + actualWidths = specifiedWidths.map(Number); + let newMargin = + (100 - (totalWidthSpecified + totalMarginSpecified)) / + (nMarginsUndefined * 4); + actualMargins = specifiedMargins.map((v) => + v === null ? newMargin : v, + ); + } else { + // Note: there aren't any null specified widths since nWidthsUndefined == 0, but typescript doesn't know it + actualWidths = specifiedWidths.map(Number); + // Note: there aren't any null specified margins since nMarginsUndefined == 0, but typescript doesn't know it + actualMargins = specifiedMargins.map(Number); + actualGap = + (100 - (totalWidthSpecified + totalMarginSpecified)) / 3; + } + } + + let actualLeftMargin = actualMargins[0]; + let actualRightMargin = actualMargins[1]; + + let valigns = specifiedValigns.map((x) => (x ? x : "top")); + + let specifiedWidthName = isSbsGroup + ? "specifiedWidths" + : "allWidthsSpecified"; + let specifiedMarginName = isSbsGroup + ? "specifiedMargins" + : "allMarginsSpecified"; + + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables[sbsName].stateValues[specifiedWidthName]).eqls( + specifiedWidths, + ); + expect(stateVariables[sbsName].stateValues.widths.length).eq(4); + expect(stateVariables[sbsName].stateValues.widths[0]).closeTo( + actualWidths[0], + 1e-5, + ); + expect(stateVariables[sbsName].stateValues.widths[1]).closeTo( + actualWidths[1], + 1e-5, + ); + expect(stateVariables[sbsName].stateValues.widths[2]).closeTo( + actualWidths[2], + 1e-5, + ); + expect(stateVariables[sbsName].stateValues.widths[3]).closeTo( + actualWidths[3], + 1e-5, + ); + expect(stateVariables[sbsName].stateValues[specifiedMarginName]).eqls( + specifiedMargins, + ); + expect(stateVariables[sbsName].stateValues.margins.length).eq(2); + expect(stateVariables[sbsName].stateValues.margins[0]).closeTo( + actualLeftMargin, + 1e-5, + ); + expect(stateVariables[sbsName].stateValues.margins[1]).closeTo( + actualRightMargin, + 1e-5, + ); + expect(stateVariables[sbsName].stateValues.gapWidth).closeTo( + actualGap, + 1e-5, + ); + expect(stateVariables[sbsName].stateValues.valigns).eqls(valigns); +}; + +describe("SideBySide tag tests", async () => { + it("sideBySide with no arguments, one panel, change margins first", async () => { + let core = await createTestCore({ + doenetML: ` + + + + +

Width: + +

+ +

Margins: + + +

+ +

Valign: + +

+ `, + }); + + await checkSingleColumnSbs({ core, sbsName: "/sbs" }); + + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/sbs"].stateValues.absoluteMeasurements).eq( + false, + ); + + // change left margin first, unspecified width adjusts + await updateMathInputValue({ latex: "10", name: "/m1", core }); + await checkSingleColumnSbs({ + core, + specifiedMargins: [10, null], + sbsName: "/sbs", + }); + + // change right margin, unspecified width adjusts + await updateMathInputValue({ latex: "20", name: "/m2", core }); + await checkSingleColumnSbs({ + core, + specifiedMargins: [10, 20], + sbsName: "/sbs", + }); + + // change width to be smaller, add extra to right margin + // Note: add to right margin since with one panel, there is not gapWidth to set + await updateMathInputValue({ latex: "60", name: "/w1", core }); + await checkSingleColumnSbs({ + core, + specifiedWidth: 60, + specifiedMargins: [10, 20], + sbsName: "/sbs", + }); + + // change width to be larger, rescale to 100% + // Note: this rescaling ignores the extra width added to the right margin, + // as it was an indirect consequence of changing the width. + // Computations assume the right margin is at the origin 20% specified + await updateMathInputValue({ latex: "95", name: "/w1", core }); + await checkSingleColumnSbs({ + core, + specifiedWidth: 95, + specifiedMargins: [10, 20], + sbsName: "/sbs", + }); + + // shrink margins to make specified values add back to 100% + await updateMathInputValue({ latex: "3", name: "/m1", core }); + await updateMathInputValue({ latex: "2", name: "/m2", core }); + await checkSingleColumnSbs({ + core, + specifiedWidth: 95, + specifiedMargins: [3, 2], + sbsName: "/sbs", + }); + + // shrink right margin to 1, gets recreated to make 100% + await updateMathInputValue({ latex: "1", name: "/m2", core }); + await checkSingleColumnSbs({ + core, + specifiedWidth: 95, + specifiedMargins: [3, 1], + sbsName: "/sbs", + }); + + // increase left margin to make specified total be 100% + await updateMathInputValue({ latex: "4", name: "/m1", core }); + await checkSingleColumnSbs({ + core, + specifiedWidth: 95, + specifiedMargins: [4, 1], + sbsName: "/sbs", + }); + + // change totals to keep at 100% + await updateMathInputValue({ latex: "80", name: "/w1", core }); + await updateMathInputValue({ latex: "5", name: "/m2", core }); + await updateMathInputValue({ latex: "15", name: "/m1", core }); + await checkSingleColumnSbs({ + core, + specifiedWidth: 80, + specifiedMargins: [15, 5], + sbsName: "/sbs", + }); + + // increasing right margin rescales + await updateMathInputValue({ latex: "30", name: "/m2", core }); + await checkSingleColumnSbs({ + core, + specifiedWidth: 80, + specifiedMargins: [15, 30], + sbsName: "/sbs", + }); + + // increasing left margin rescales + await updateMathInputValue({ latex: "50", name: "/m1", core }); + await checkSingleColumnSbs({ + core, + specifiedWidth: 80, + specifiedMargins: [50, 30], + sbsName: "/sbs", + }); + + // shrink width to get specified back to 100% + await updateMathInputValue({ latex: "20", name: "/w1", core }); + await checkSingleColumnSbs({ + core, + specifiedWidth: 20, + specifiedMargins: [50, 30], + sbsName: "/sbs", + }); + + // change valign + await updateTextInputValue({ text: "bottom", name: "/v1", core }); + await checkSingleColumnSbs({ + core, + specifiedWidth: 20, + specifiedMargins: [50, 30], + specifiedValign: "bottom", + sbsName: "/sbs", + }); + + // invalid valign ignored + await updateTextInputValue({ text: "invalid", name: "/v1", core }); + await checkSingleColumnSbs({ + core, + specifiedWidth: 20, + specifiedMargins: [50, 30], + specifiedValign: "bottom", + sbsName: "/sbs", + }); + }); + + it("sideBySide with no arguments, one panel, change width first", async () => { + let core = await createTestCore({ + doenetML: ` + + + + +

Width: + +

+ +

Margins: + + +

+ +

Valign: + +

+ `, + }); + + await checkSingleColumnSbs({ core, sbsName: "/sbs" }); + + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/sbs"].stateValues.absoluteMeasurements).eq( + false, + ); + expect(stateVariables["/sbs"].stateValues.allWidthsSpecified).eqls([ + null, + ]); + expect(stateVariables["/sbs"].stateValues.widths).eqls([100]); + expect(stateVariables["/sbs"].stateValues.allMarginsSpecified).eqls([ + null, + null, + ]); + expect(stateVariables["/sbs"].stateValues.margins).eqls([0, 0]); + expect(stateVariables["/sbs"].stateValues.valigns).eqls(["top"]); + + // change width first, unspecified margins adjusts + await updateMathInputValue({ latex: "70", name: "/w1", core }); + await checkSingleColumnSbs({ + core, + specifiedWidth: 70, + sbsName: "/sbs", + }); + + // change width larger than 100%, scaled back to 100% + await updateMathInputValue({ latex: "170", name: "/w1", core }); + await checkSingleColumnSbs({ + core, + specifiedWidth: 170, + sbsName: "/sbs", + }); + + // change width smaller again + await updateMathInputValue({ latex: "60", name: "/w1", core }); + await checkSingleColumnSbs({ + core, + specifiedWidth: 60, + sbsName: "/sbs", + }); + + // change right margin, unspecified left margin adjusts + await updateMathInputValue({ latex: "10", name: "/m2", core }); + await checkSingleColumnSbs({ + core, + specifiedWidth: 60, + specifiedMargins: [null, 10], + sbsName: "/sbs", + }); + + // change right margin so total is larger than 100%, rescales + await updateMathInputValue({ latex: "60", name: "/m2", core }); + await checkSingleColumnSbs({ + core, + specifiedWidth: 60, + specifiedMargins: [null, 60], + sbsName: "/sbs", + }); + + // change left margin to be large, rescaling adjusts + // Note: add to right margin since with one panel, there is not gapWidth to set + await updateMathInputValue({ latex: "120", name: "/m1", core }); + await checkSingleColumnSbs({ + core, + specifiedWidth: 60, + specifiedMargins: [120, 60], + sbsName: "/sbs", + }); + }); + + it("sideBySide with singular relative arguments, one panel", async () => { + let core = await createTestCore({ + doenetML: ` + + + + +

Width: + +

+ +

Margins: + + +

+ +

Valign: + +

+ `, + }); + + await checkSingleColumnSbs({ + core, + specifiedWidth: 80, + specifiedMargins: [10, 10], + specifiedValign: "middle", + sbsName: "/sbs", + }); + + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/sbs"].stateValues.absoluteMeasurements).eq( + false, + ); + + // change left margin, specified margins stay symmetric, get rescaling + await updateMathInputValue({ latex: "40", name: "/m1", core }); + await checkSingleColumnSbs({ + core, + specifiedWidth: 80, + specifiedMargins: [40, 40], + specifiedValign: "middle", + sbsName: "/sbs", + }); + + // change right margin, specified margins stay symmetric, extra added to right + await updateMathInputValue({ latex: "5", name: "/m2", core }); + await checkSingleColumnSbs({ + core, + specifiedWidth: 80, + specifiedMargins: [5, 5], + specifiedValign: "middle", + sbsName: "/sbs", + }); + + // symmetry regained by increasing width + await updateMathInputValue({ latex: "90", name: "/w1", core }); + await checkSingleColumnSbs({ + core, + specifiedWidth: 90, + specifiedMargins: [5, 5], + specifiedValign: "middle", + sbsName: "/sbs", + }); + + // change valign + await updateTextInputValue({ text: "bottom", name: "/v1", core }); + await checkSingleColumnSbs({ + core, + specifiedWidth: 90, + specifiedMargins: [5, 5], + specifiedValign: "bottom", + sbsName: "/sbs", + }); + + // ignore invalid valign + await updateTextInputValue({ text: "green", name: "/v1", core }); + await checkSingleColumnSbs({ + core, + specifiedWidth: 90, + specifiedMargins: [5, 5], + specifiedValign: "bottom", + sbsName: "/sbs", + }); + }); + + it("sideBySide with plural relative arguments, one panel", async () => { + let core = await createTestCore({ + doenetML: ` + + + + +

Width: + +

+ +

Margins: + + +

+ +

Valign: + +

+ `, + }); + + await checkSingleColumnSbs({ + core, + specifiedWidth: 80, + specifiedMargins: [15, 5], + specifiedValign: "middle", + sbsName: "/sbs", + }); + + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/sbs"].stateValues.absoluteMeasurements).eq( + false, + ); + + // decrease left margin, space added to right margin + await updateMathInputValue({ latex: "10", name: "/m1", core }); + await checkSingleColumnSbs({ + core, + specifiedWidth: 80, + specifiedMargins: [10, 5], + specifiedValign: "middle", + sbsName: "/sbs", + }); + + // increase right margin, get rescaling + await updateMathInputValue({ latex: "35", name: "/m2", core }); + await checkSingleColumnSbs({ + core, + specifiedWidth: 80, + specifiedMargins: [10, 35], + specifiedValign: "middle", + sbsName: "/sbs", + }); + + // decrease width to return to 100% + await updateMathInputValue({ latex: "55", name: "/w1", core }); + await checkSingleColumnSbs({ + core, + specifiedWidth: 55, + specifiedMargins: [10, 35], + specifiedValign: "middle", + sbsName: "/sbs", + }); + + // change valign + await updateTextInputValue({ text: "bottom", name: "/v1", core }); + await checkSingleColumnSbs({ + core, + specifiedWidth: 55, + specifiedMargins: [10, 35], + specifiedValign: "bottom", + sbsName: "/sbs", + }); + + // ignore invalid valign + await updateTextInputValue({ text: "green", name: "/v1", core }); + await checkSingleColumnSbs({ + core, + specifiedWidth: 55, + specifiedMargins: [10, 35], + specifiedValign: "bottom", + sbsName: "/sbs", + }); + }); + + it("sideBySide with singular relative arguments and auto margins, one panel", async () => { + let core = await createTestCore({ + doenetML: ` + + + + +

Width: + +

+ +

Margins: + + +

+ +

Valign: + +

+ `, + }); + + await checkSingleColumnSbs({ + core, + specifiedWidth: 80, + specifiedValign: "middle", + sbsName: "/sbs", + }); + + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/sbs"].stateValues.absoluteMeasurements).eq( + false, + ); + + // change left margin, specified margins stay symmetric, get rescaling + await updateMathInputValue({ latex: "40", name: "/m1", core }); + await checkSingleColumnSbs({ + core, + specifiedWidth: 80, + specifiedMargins: [40, 40], + specifiedValign: "middle", + sbsName: "/sbs", + }); + + // change right margin, specified margins stay symmetric, extra added to right + await updateMathInputValue({ latex: "5", name: "/m2", core }); + await checkSingleColumnSbs({ + core, + specifiedWidth: 80, + specifiedMargins: [5, 5], + specifiedValign: "middle", + sbsName: "/sbs", + }); + + // symmetry regained by increasing width + await updateMathInputValue({ latex: "90", name: "/w1", core }); + await checkSingleColumnSbs({ + core, + specifiedWidth: 90, + specifiedMargins: [5, 5], + specifiedValign: "middle", + sbsName: "/sbs", + }); + + // change valign + await updateTextInputValue({ text: "bottom", name: "/v1", core }); + await checkSingleColumnSbs({ + core, + specifiedWidth: 90, + specifiedMargins: [5, 5], + specifiedValign: "bottom", + sbsName: "/sbs", + }); + + // ignore invalid valign + await updateTextInputValue({ text: "green", name: "/v1", core }); + await checkSingleColumnSbs({ + core, + specifiedWidth: 90, + specifiedMargins: [5, 5], + specifiedValign: "bottom", + sbsName: "/sbs", + }); + }); + + it("sideBySide with no arguments, two panels, change margins first", async () => { + let core = await createTestCore({ + doenetML: ` + + + + + +

Widths: + + +

+ +

Margins: + + +

+ +

Valigns: + + +

+ `, + }); + + await checkTwoColumnSbs({ core, sbsName: "/sbs" }); + + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/sbs"].stateValues.absoluteMeasurements).eq( + false, + ); + + // change left margin first, unspecified widths adjust + await updateMathInputValue({ latex: "10", name: "/m1", core }); + await checkTwoColumnSbs({ + core, + specifiedMargins: [10, null], + sbsName: "/sbs", + }); + + // change right margin, unspecified widths adjust + await updateMathInputValue({ latex: "5", name: "/m2", core }); + await checkTwoColumnSbs({ + core, + specifiedMargins: [10, 5], + sbsName: "/sbs", + }); + + // change first width to be smaller, add extra to second width + await updateMathInputValue({ latex: "20", name: "/w1", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [20, null], + specifiedMargins: [10, 5], + sbsName: "/sbs", + }); + + // change first width to be larger, second width shrinks to zero, rescale to 100% + await updateMathInputValue({ latex: "95", name: "/w1", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [95, null], + specifiedMargins: [10, 5], + sbsName: "/sbs", + }); + + // change first width to be smaller again + await updateMathInputValue({ latex: "10", name: "/w1", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [10, null], + specifiedMargins: [10, 5], + sbsName: "/sbs", + }); + + // change second width to be smaller, extra added to gap + await updateMathInputValue({ latex: "50", name: "/w2", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [10, 50], + specifiedMargins: [10, 5], + sbsName: "/sbs", + }); + + // change second width to be larger, rescaled to 100% + await updateMathInputValue({ latex: "85", name: "/w2", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [10, 85], + specifiedMargins: [10, 5], + sbsName: "/sbs", + }); + + // shrink margins to make specified values add back to 100% + await updateMathInputValue({ latex: "1.5", name: "/m1", core }); + await updateMathInputValue({ latex: "1", name: "/m2", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [10, 85], + specifiedMargins: [1.5, 1], + sbsName: "/sbs", + }); + + // shrink right margin to 0.5, extra added to gap + await updateMathInputValue({ latex: "0.5", name: "/m2", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [10, 85], + specifiedMargins: [1.5, 0.5], + sbsName: "/sbs", + }); + + // increase left margin to make specified total be 100% + await updateMathInputValue({ latex: "2", name: "/m1", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [10, 85], + specifiedMargins: [2, 0.5], + sbsName: "/sbs", + }); + + // change totals to keep at 100% + await updateMathInputValue({ latex: "30", name: "/w1", core }); + await updateMathInputValue({ latex: "50", name: "/w2", core }); + await updateMathInputValue({ latex: "4", name: "/m1", core }); + await updateMathInputValue({ latex: "6", name: "/m2", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [30, 50], + specifiedMargins: [4, 6], + sbsName: "/sbs", + }); + + // increasing right margin rescales + await updateMathInputValue({ latex: "18.5", name: "/m2", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [30, 50], + specifiedMargins: [4, 18.5], + sbsName: "/sbs", + }); + + // increasing left margin rescales + await updateMathInputValue({ latex: "21.5", name: "/m1", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [30, 50], + specifiedMargins: [21.5, 18.5], + sbsName: "/sbs", + }); + + // shrink widths to get specified below 100% + await updateMathInputValue({ latex: "5", name: "/w1", core }); + await updateMathInputValue({ latex: "10", name: "/w2", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [5, 10], + specifiedMargins: [21.5, 18.5], + sbsName: "/sbs", + }); + + // change first valign + await updateTextInputValue({ text: "bottom", name: "/v1", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [5, 10], + specifiedMargins: [21.5, 18.5], + specifiedValigns: ["bottom", null], + sbsName: "/sbs", + }); + + // change second valign + await updateTextInputValue({ text: "middle", name: "/v2", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [5, 10], + specifiedMargins: [21.5, 18.5], + specifiedValigns: ["bottom", "middle"], + sbsName: "/sbs", + }); + + // invalid valign ignored + await updateTextInputValue({ text: "invalid", name: "/v2", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [5, 10], + specifiedMargins: [21.5, 18.5], + specifiedValigns: ["bottom", "middle"], + sbsName: "/sbs", + }); + }); + + it("sideBySide with no arguments, two panels, change widths first", async () => { + let core = await createTestCore({ + doenetML: ` + + + + + +

Widths: + + +

+ +

Margins: + + +

+ +

Valigns: + + +

+ `, + }); + + await checkTwoColumnSbs({ core, sbsName: "/sbs" }); + + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/sbs"].stateValues.absoluteMeasurements).eq( + false, + ); + + // change second width past 100%, unspecified first width shrinks to zero, rescales + await updateMathInputValue({ latex: "130", name: "/w2", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [null, 130], + sbsName: "/sbs", + }); + + // change second width, unspecified first width adjusts + await updateMathInputValue({ latex: "10", name: "/w2", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [null, 10], + sbsName: "/sbs", + }); + + // change first width, unspecified margins adjust + await updateMathInputValue({ latex: "30", name: "/w1", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [30, 10], + sbsName: "/sbs", + }); + + // change right margin, unspecified left margin adjusts + await updateMathInputValue({ latex: "5", name: "/m2", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [30, 10], + specifiedMargins: [null, 5], + sbsName: "/sbs", + }); + + // increase second width so total is past 100%, rescaling + await updateMathInputValue({ latex: "85", name: "/w2", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [30, 85], + specifiedMargins: [null, 5], + sbsName: "/sbs", + }); + + // decrease second width + await updateMathInputValue({ latex: "20", name: "/w2", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [30, 20], + specifiedMargins: [null, 5], + sbsName: "/sbs", + }); + + // specify first margin to be smaller, remainder in gap + await updateMathInputValue({ latex: "10", name: "/m1", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [30, 20], + specifiedMargins: [10, 5], + sbsName: "/sbs", + }); + }); + + it("sideBySide with singular relative arguments, two panels", async () => { + let core = await createTestCore({ + doenetML: ` + + + + + +

Widths: + + +

+ +

Margins: + + +

+ +

Valigns: + + +

+ `, + }); + + await checkTwoColumnSbs({ + core, + specifiedWidths: [20, 20], + specifiedMargins: [10, 10], + specifiedValigns: ["middle", "middle"], + sbsName: "/sbs", + }); + + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/sbs"].stateValues.absoluteMeasurements).eq( + false, + ); + + // change first width, second matches + await updateMathInputValue({ latex: "30", name: "/w1", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [30, 30], + specifiedMargins: [10, 10], + specifiedValigns: ["middle", "middle"], + sbsName: "/sbs", + }); + + // change second width, first matches, rescaling + await updateMathInputValue({ latex: "80", name: "/w2", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [80, 80], + specifiedMargins: [10, 10], + specifiedValigns: ["middle", "middle"], + sbsName: "/sbs", + }); + + // shrink width, rest in gap + await updateMathInputValue({ latex: "10", name: "/w1", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [10, 10], + specifiedMargins: [10, 10], + specifiedValigns: ["middle", "middle"], + sbsName: "/sbs", + }); + + // increase left margin, right margin matches + await updateMathInputValue({ latex: "20", name: "/m1", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [10, 10], + specifiedMargins: [20, 20], + specifiedValigns: ["middle", "middle"], + sbsName: "/sbs", + }); + + // increase right margin, left margin matches, rescaling + await updateMathInputValue({ latex: "45", name: "/m2", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [10, 10], + specifiedMargins: [45, 45], + specifiedValigns: ["middle", "middle"], + sbsName: "/sbs", + }); + + // change first valign + await updateTextInputValue({ text: "top", name: "/v1", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [10, 10], + specifiedMargins: [45, 45], + specifiedValigns: ["top", "top"], + sbsName: "/sbs", + }); + + // change second valign + await updateTextInputValue({ text: "bottom", name: "/v2", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [10, 10], + specifiedMargins: [45, 45], + specifiedValigns: ["bottom", "bottom"], + sbsName: "/sbs", + }); + + // invalid valign ignored + await updateTextInputValue({ text: "invalid", name: "/v1", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [10, 10], + specifiedMargins: [45, 45], + specifiedValigns: ["bottom", "bottom"], + sbsName: "/sbs", + }); + }); + + it("sideBySide with plural relative arguments, two panels", async () => { + let core = await createTestCore({ + doenetML: ` + + + + + +

Widths: + + +

+ +

Margins: + + +

+ +

Valigns: + + +

+ `, + }); + + await checkTwoColumnSbs({ + core, + specifiedWidths: [20, 10], + specifiedMargins: [10, 20], + specifiedValigns: ["middle", "bottom"], + sbsName: "/sbs", + }); + + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/sbs"].stateValues.absoluteMeasurements).eq( + false, + ); + + // change first width + await updateMathInputValue({ latex: "30", name: "/w1", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [30, 10], + specifiedMargins: [10, 20], + specifiedValigns: ["middle", "bottom"], + sbsName: "/sbs", + }); + + // change second width, rescaling + await updateMathInputValue({ latex: "110", name: "/w2", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [30, 110], + specifiedMargins: [10, 20], + specifiedValigns: ["middle", "bottom"], + sbsName: "/sbs", + }); + + // shrink second width + await updateMathInputValue({ latex: "5", name: "/w2", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [30, 5], + specifiedMargins: [10, 20], + specifiedValigns: ["middle", "bottom"], + sbsName: "/sbs", + }); + + // decrease right margin + await updateMathInputValue({ latex: "5", name: "/m2", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [30, 5], + specifiedMargins: [10, 5], + specifiedValigns: ["middle", "bottom"], + sbsName: "/sbs", + }); + + // increase left margin, rescaling + await updateMathInputValue({ latex: "77.5", name: "/m1", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [30, 5], + specifiedMargins: [77.5, 5], + specifiedValigns: ["middle", "bottom"], + sbsName: "/sbs", + }); + + // change first valign + await updateTextInputValue({ text: "top", name: "/v1", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [30, 5], + specifiedMargins: [77.5, 5], + specifiedValigns: ["top", "bottom"], + sbsName: "/sbs", + }); + + // change second valign + await updateTextInputValue({ text: "middle", name: "/v2", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [30, 5], + specifiedMargins: [77.5, 5], + specifiedValigns: ["top", "middle"], + sbsName: "/sbs", + }); + + // invalid valign ignored + await updateTextInputValue({ text: "invalid", name: "/v2", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [30, 5], + specifiedMargins: [77.5, 5], + specifiedValigns: ["top", "middle"], + sbsName: "/sbs", + }); + }); + + it("sideBySide with half-specified plural relative arguments and auto margins", async () => { + let core = await createTestCore({ + doenetML: ` + + + + + +

Widths: + + +

+ +

Margins: + + +

+ +

Valigns: + + +

+ `, + }); + + await checkTwoColumnSbs({ + core, + specifiedWidths: [20, null], + specifiedMargins: [null, null], + specifiedValigns: ["middle", null], + sbsName: "/sbs", + }); + + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/sbs"].stateValues.absoluteMeasurements).eq( + false, + ); + + // change first width, unspecified second width adjusts + await updateMathInputValue({ latex: "30", name: "/w1", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [30, null], + specifiedMargins: [null, null], + specifiedValigns: ["middle", null], + sbsName: "/sbs", + }); + + // change right margin, left is symmetric, unspecified second width adjusts + await updateMathInputValue({ latex: "10", name: "/m2", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [30, null], + specifiedMargins: [10, 10], + specifiedValigns: ["middle", null], + sbsName: "/sbs", + }); + + // change second width, rest in gap + await updateMathInputValue({ latex: "20", name: "/w2", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [30, 20], + specifiedMargins: [10, 10], + specifiedValigns: ["middle", null], + sbsName: "/sbs", + }); + + // change first width, rescaling + await updateMathInputValue({ latex: "140", name: "/w1", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [140, 20], + specifiedMargins: [10, 10], + specifiedValigns: ["middle", null], + sbsName: "/sbs", + }); + + // shrink first width + await updateMathInputValue({ latex: "10", name: "/w1", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [10, 20], + specifiedMargins: [10, 10], + specifiedValigns: ["middle", null], + sbsName: "/sbs", + }); + + // decrease right margin, left matches + await updateMathInputValue({ latex: "5", name: "/m2", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [10, 20], + specifiedMargins: [5, 5], + specifiedValigns: ["middle", null], + sbsName: "/sbs", + }); + + // increase left margin, right matches, rescaling + await updateMathInputValue({ latex: "42.5", name: "/m1", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [10, 20], + specifiedMargins: [42.5, 42.5], + specifiedValigns: ["middle", null], + sbsName: "/sbs", + }); + + // change first valign + await updateTextInputValue({ text: "top", name: "/v1", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [10, 20], + specifiedMargins: [42.5, 42.5], + specifiedValigns: ["top", null], + sbsName: "/sbs", + }); + + // change second valign + await updateTextInputValue({ text: "bottom", name: "/v2", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [10, 20], + specifiedMargins: [42.5, 42.5], + specifiedValigns: ["top", "bottom"], + sbsName: "/sbs", + }); + + // invalid valign ignored + await updateTextInputValue({ text: "invalid", name: "/v2", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [10, 20], + specifiedMargins: [42.5, 42.5], + specifiedValigns: ["top", "bottom"], + sbsName: "/sbs", + }); + }); + + it("sideBySide with no arguments, four panels", async () => { + let core = await createTestCore({ + doenetML: ` + + + + + + + +

Widths: + + + + +

+ +

Margins: + + +

+ +

Valigns: + + + + +

+ `, + }); + + await checkFourColumnSbs({ core, sbsName: "/sbs" }); + + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/sbs"].stateValues.absoluteMeasurements).eq( + false, + ); + + // change left margin first, unspecified widths adjust + await updateMathInputValue({ latex: "2", name: "/m1", core }); + await checkFourColumnSbs({ + core, + specifiedMargins: [2, null], + sbsName: "/sbs", + }); + + // change right margin, unspecified widths adjust + await updateMathInputValue({ latex: "3", name: "/m2", core }); + await checkFourColumnSbs({ + core, + specifiedMargins: [2, 3], + sbsName: "/sbs", + }); + + // change 3rd width to be smaller, add extra to other widths + await updateMathInputValue({ latex: "14", name: "/w3", core }); + await checkFourColumnSbs({ + core, + specifiedWidths: [null, null, 14, null], + specifiedMargins: [2, 3], + sbsName: "/sbs", + }); + + // change 3rd width to be larger, others widths shrinks to zero, rescale to 100% + await updateMathInputValue({ latex: "180", name: "/w3", core }); + await checkFourColumnSbs({ + core, + specifiedWidths: [null, null, 180, null], + specifiedMargins: [2, 3], + sbsName: "/sbs", + }); + + // change 3rd width to be smaller again + await updateMathInputValue({ latex: "11", name: "/w3", core }); + await checkFourColumnSbs({ + core, + specifiedWidths: [null, null, 11, null], + specifiedMargins: [2, 3], + sbsName: "/sbs", + }); + + // change 2nd width to be smaller + await updateMathInputValue({ latex: "15", name: "/w2", core }); + await checkFourColumnSbs({ + core, + specifiedWidths: [null, 15, 11, null], + specifiedMargins: [2, 3], + sbsName: "/sbs", + }); + + // change 1st width to be smaller + await updateMathInputValue({ latex: "20", name: "/w1", core }); + await checkFourColumnSbs({ + core, + specifiedWidths: [20, 15, 11, null], + specifiedMargins: [2, 3], + sbsName: "/sbs", + }); + + // change 4th width to be smaller, remainder added to gap + await updateMathInputValue({ latex: "19", name: "/w4", core }); + await checkFourColumnSbs({ + core, + specifiedWidths: [20, 15, 11, 19], + specifiedMargins: [2, 3], + sbsName: "/sbs", + }); + + // change 2nd width to be larger, rescaled to 100% + await updateMathInputValue({ latex: "55", name: "/w2", core }); + await checkFourColumnSbs({ + core, + specifiedWidths: [20, 55, 11, 19], + specifiedMargins: [2, 3], + sbsName: "/sbs", + }); + + // shrink width 2 to make specified values add back to 100% + await updateMathInputValue({ latex: "30", name: "/w2", core }); + await checkFourColumnSbs({ + core, + specifiedWidths: [20, 30, 11, 19], + specifiedMargins: [2, 3], + sbsName: "/sbs", + }); + + // shrink right margin, extra added to gap + await updateMathInputValue({ latex: "1", name: "/m2", core }); + await checkFourColumnSbs({ + core, + specifiedWidths: [20, 30, 11, 19], + specifiedMargins: [2, 1], + sbsName: "/sbs", + }); + + // change fourth valign + await updateTextInputValue({ text: "bottom", name: "/v4", core }); + await checkFourColumnSbs({ + core, + specifiedWidths: [20, 30, 11, 19], + specifiedMargins: [2, 1], + specifiedValigns: [null, null, null, "bottom"], + sbsName: "/sbs", + }); + + // change second valign + await updateTextInputValue({ text: "middle", name: "/v2", core }); + await checkFourColumnSbs({ + core, + specifiedWidths: [20, 30, 11, 19], + specifiedMargins: [2, 1], + specifiedValigns: [null, "middle", null, "bottom"], + sbsName: "/sbs", + }); + + // change first valign + await updateTextInputValue({ text: "middle", name: "/v1", core }); + await checkFourColumnSbs({ + core, + specifiedWidths: [20, 30, 11, 19], + specifiedMargins: [2, 1], + specifiedValigns: ["middle", "middle", null, "bottom"], + sbsName: "/sbs", + }); + + // change third valign + await updateTextInputValue({ text: "bottom", name: "/v3", core }); + await checkFourColumnSbs({ + core, + specifiedWidths: [20, 30, 11, 19], + specifiedMargins: [2, 1], + specifiedValigns: ["middle", "middle", "bottom", "bottom"], + sbsName: "/sbs", + }); + + // invalid valign ignored + await updateTextInputValue({ text: "invalid", name: "/v3", core }); + await checkFourColumnSbs({ + core, + specifiedWidths: [20, 30, 11, 19], + specifiedMargins: [2, 1], + specifiedValigns: ["middle", "middle", "bottom", "bottom"], + sbsName: "/sbs", + }); + }); + + it("sideBySide with singular relative arguments, four panels", async () => { + let core = await createTestCore({ + doenetML: ` + + + + + + + +

Widths: + + + + +

+ +

Margins: + + +

+ +

Valigns: + + + + +

+ `, + }); + + await checkFourColumnSbs({ + core, + specifiedWidths: [15, 15, 15, 15], + specifiedMargins: [5, 5], + specifiedValigns: ["middle", "middle", "middle", "middle"], + sbsName: "/sbs", + }); + + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/sbs"].stateValues.absoluteMeasurements).eq( + false, + ); + + // change 4th width, rest match, remainder added to gap + await updateMathInputValue({ latex: "10", name: "/w4", core }); + await checkFourColumnSbs({ + core, + specifiedWidths: [10, 10, 10, 10], + specifiedMargins: [5, 5], + specifiedValigns: ["middle", "middle", "middle", "middle"], + sbsName: "/sbs", + }); + + // change right margin, rescaled + await updateMathInputValue({ latex: "20", name: "/m2", core }); + await checkFourColumnSbs({ + core, + specifiedWidths: [10, 10, 10, 10], + specifiedMargins: [20, 20], + specifiedValigns: ["middle", "middle", "middle", "middle"], + sbsName: "/sbs", + }); + + // shrink left margin + await updateMathInputValue({ latex: "2", name: "/m1", core }); + await checkFourColumnSbs({ + core, + specifiedWidths: [10, 10, 10, 10], + specifiedMargins: [2, 2], + specifiedValigns: ["middle", "middle", "middle", "middle"], + sbsName: "/sbs", + }); + + // change fourth valign + await updateTextInputValue({ text: "bottom", name: "/v4", core }); + await checkFourColumnSbs({ + core, + specifiedWidths: [10, 10, 10, 10], + specifiedMargins: [2, 2], + specifiedValigns: ["bottom", "bottom", "bottom", "bottom"], + sbsName: "/sbs", + }); + }); + + it("sideBySide with plural relative arguments, four panels", async () => { + let core = await createTestCore({ + doenetML: ` + + + + + + + +

Widths: + + + + +

+ +

Margins: + + +

+ +

Valigns: + + + + +

+ `, + }); + + await checkFourColumnSbs({ + core, + specifiedWidths: [5, 10, 15, 20], + specifiedMargins: [5, 2], + specifiedValigns: ["middle", null, null, null], + sbsName: "/sbs", + }); + + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/sbs"].stateValues.absoluteMeasurements).eq( + false, + ); + + // change 4th width, remainder added to gap + await updateMathInputValue({ latex: "9", name: "/w4", core }); + await checkFourColumnSbs({ + core, + specifiedWidths: [5, 10, 15, 9], + specifiedMargins: [5, 2], + specifiedValigns: ["middle", null, null, null], + sbsName: "/sbs", + }); + + // change 1st width, rescaled + await updateMathInputValue({ latex: "63", name: "/w1", core }); + await checkFourColumnSbs({ + core, + specifiedWidths: [63, 10, 15, 9], + specifiedMargins: [5, 2], + specifiedValigns: ["middle", null, null, null], + sbsName: "/sbs", + }); + + // change more widths, remainder added to gap + await updateMathInputValue({ latex: "3", name: "/w1", core }); + await updateMathInputValue({ latex: "8", name: "/w2", core }); + await updateMathInputValue({ latex: "13", name: "/w3", core }); + await checkFourColumnSbs({ + core, + specifiedWidths: [3, 8, 13, 9], + specifiedMargins: [5, 2], + specifiedValigns: ["middle", null, null, null], + sbsName: "/sbs", + }); + + // change margins + await updateMathInputValue({ latex: "7", name: "/m1", core }); + await updateMathInputValue({ latex: "6", name: "/m2", core }); + await checkFourColumnSbs({ + core, + specifiedWidths: [3, 8, 13, 9], + specifiedMargins: [7, 6], + specifiedValigns: ["middle", null, null, null], + sbsName: "/sbs", + }); + + // change valigns + await updateTextInputValue({ text: "top", name: "/v1", core }); + await updateTextInputValue({ text: "middle", name: "/v2", core }); + await updateTextInputValue({ text: "bottom", name: "/v3", core }); + await updateTextInputValue({ text: "middle", name: "/v4", core }); + await checkFourColumnSbs({ + core, + specifiedWidths: [3, 8, 13, 9], + specifiedMargins: [7, 6], + specifiedValigns: ["top", "middle", "bottom", "middle"], + sbsName: "/sbs", + }); + }); + + it("copy sideBySide and overwrite parameters", async () => { + let core = await createTestCore({ + doenetML: ` + + + + + + + + + `, + }); + + await checkFourColumnSbs({ + core, + specifiedWidths: [5, 10, 15, 20], + specifiedMargins: [5, 2], + specifiedValigns: ["middle", null, null, null], + sbsName: "/sbs", + }); + + await checkFourColumnSbs({ + core, + specifiedWidths: [30, 10, null, null], + specifiedMargins: [1, 3], + specifiedValigns: ["bottom", "middle", "top", "bottom"], + sbsName: "/sbs2", + }); + + await checkFourColumnSbs({ + core, + specifiedWidths: [7, 8, 11, 12], + specifiedMargins: [1, 3], + specifiedValigns: ["top", "bottom", null, null], + sbsName: "/sbs3", + }); + }); + + it("sideBySide absolute measurements turned to absolute with warning", async () => { + let core = await createTestCore({ + doenetML: ` + +

Hello

+

Hello

+
+ + `, + }); + + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/_sidebyside1"].stateValues.marginsAbsolute).eq( + false, + ); + expect(stateVariables["/_sidebyside1"].stateValues.widthsAbsolute).eq( + false, + ); + + let errorWarnings = core.errorWarnings; + + expect(errorWarnings.errors.length).eq(0); + expect(errorWarnings.warnings.length).eq(2); + + expect(errorWarnings.warnings[0].message).contain( + ` is not implemented for absolute measurements. Setting widths to relative`, + ); + expect(errorWarnings.warnings[0].level).eq(1); + expect(errorWarnings.warnings[0].doenetMLrange.lineBegin).eq(2); + expect(errorWarnings.warnings[0].doenetMLrange.charBegin).eq(5); + expect(errorWarnings.warnings[0].doenetMLrange.lineEnd).eq(5); + expect(errorWarnings.warnings[0].doenetMLrange.charEnd).eq(17); + + expect(errorWarnings.warnings[1].message).contain( + ` is not implemented for absolute measurements. Setting margins to relative`, + ); + expect(errorWarnings.warnings[1].level).eq(1); + expect(errorWarnings.warnings[1].doenetMLrange.lineBegin).eq(2); + expect(errorWarnings.warnings[1].doenetMLrange.charBegin).eq(5); + expect(errorWarnings.warnings[1].doenetMLrange.lineEnd).eq(5); + expect(errorWarnings.warnings[1].doenetMLrange.charEnd).eq(17); + }); + + it("sbsGroup with no arguments, one panel", async () => { + let core = await createTestCore({ + doenetML: ` + + + + + + + + + +

Width for sbsg: + +

+ +

Margins for sbsg: + + +

+ +

Valign for sbsg: + +

+ +

Width for sbs1: + +

+ +

Margins for sbs1: + + +

+ +

Valign for sbs1: + +

+ +

Width for sbs2: + +

+ +

Margins for sbs2: + + +

+ +

Valign for sbs2: + +

+ `, + }); + + await checkSingleColumnSbs({ + core, + sbsName: "/sbsg", + isSbsGroup: true, + }); + await checkSingleColumnSbs({ core, sbsName: "/sbs1" }); + await checkSingleColumnSbs({ core, sbsName: "/sbs2" }); + + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/sbsg"].stateValues.absoluteMeasurements).eq( + false, + ); + expect(stateVariables["/sbs1"].stateValues.absoluteMeasurements).eq( + false, + ); + expect(stateVariables["/sbs1"].stateValues.essentialWidths).eqls([ + null, + ]); + expect(stateVariables["/sbs1"].stateValues.essentialMargins).eqls([ + null, + null, + ]); + expect(stateVariables["/sbs2"].stateValues.absoluteMeasurements).eq( + false, + ); + expect(stateVariables["/sbs2"].stateValues.essentialWidths).eqls([ + null, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialMargins).eqls([ + null, + null, + ]); + + // change left margin of sbs1, unspecified width of sbs1 adjusts + await updateMathInputValue({ latex: "10", name: "/m11", core }); + await checkSingleColumnSbs({ + core, + sbsName: "/sbsg", + isSbsGroup: true, + }); + await checkSingleColumnSbs({ + core, + specifiedMargins: [10, null], + sbsName: "/sbs1", + }); + await checkSingleColumnSbs({ core, sbsName: "/sbs2" }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/sbs1"].stateValues.essentialWidths).eqls([ + null, + ]); + expect(stateVariables["/sbs1"].stateValues.essentialMargins).eqls([ + 10, + null, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialWidths).eqls([ + null, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialMargins).eqls([ + null, + null, + ]); + + // change width of sbsg, unspecified margin(s) adjust + await updateMathInputValue({ latex: "70", name: "/w1g", core }); + await checkSingleColumnSbs({ + core, + specifiedWidth: 70, + sbsName: "/sbsg", + isSbsGroup: true, + }); + await checkSingleColumnSbs({ + core, + specifiedWidth: 70, + specifiedMargins: [10, null], + sbsName: "/sbs1", + }); + await checkSingleColumnSbs({ + core, + specifiedWidth: 70, + sbsName: "/sbs2", + }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/sbs1"].stateValues.essentialWidths).eqls([ + null, + ]); + expect(stateVariables["/sbs1"].stateValues.essentialMargins).eqls([ + 10, + null, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialWidths).eqls([ + null, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialMargins).eqls([ + null, + null, + ]); + + // change right margin of sbs2, unspecified margin adjusts + await updateMathInputValue({ latex: "25", name: "/m22", core }); + await checkSingleColumnSbs({ + core, + specifiedWidth: 70, + sbsName: "/sbsg", + isSbsGroup: true, + }); + await checkSingleColumnSbs({ + core, + specifiedWidth: 70, + specifiedMargins: [10, null], + sbsName: "/sbs1", + }); + await checkSingleColumnSbs({ + core, + specifiedWidth: 70, + specifiedMargins: [null, 25], + sbsName: "/sbs2", + }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/sbs1"].stateValues.essentialWidths).eqls([ + null, + ]); + expect(stateVariables["/sbs1"].stateValues.essentialMargins).eqls([ + 10, + null, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialWidths).eqls([ + null, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialMargins).eqls([ + null, + 25, + ]); + + // change left margin of sbsg, affects only sbs2 + await updateMathInputValue({ latex: "4", name: "/m1g", core }); + await checkSingleColumnSbs({ + core, + specifiedWidth: 70, + specifiedMargins: [4, null], + sbsName: "/sbsg", + isSbsGroup: true, + }); + await checkSingleColumnSbs({ + core, + specifiedWidth: 70, + specifiedMargins: [10, null], + sbsName: "/sbs1", + }); + await checkSingleColumnSbs({ + core, + specifiedWidth: 70, + specifiedMargins: [4, 25], + sbsName: "/sbs2", + }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/sbs1"].stateValues.essentialWidths).eqls([ + null, + ]); + expect(stateVariables["/sbs1"].stateValues.essentialMargins).eqls([ + 10, + null, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialWidths).eqls([ + null, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialMargins).eqls([ + null, + 25, + ]); + + // change sbsg width to be smaller, adds to unspecified or right margins + await updateMathInputValue({ latex: "60", name: "/w1g", core }); + await checkSingleColumnSbs({ + core, + specifiedWidth: 60, + specifiedMargins: [4, null], + sbsName: "/sbsg", + isSbsGroup: true, + }); + await checkSingleColumnSbs({ + core, + specifiedWidth: 60, + specifiedMargins: [10, null], + sbsName: "/sbs1", + }); + await checkSingleColumnSbs({ + core, + specifiedWidth: 60, + specifiedMargins: [4, 25], + sbsName: "/sbs2", + }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/sbs1"].stateValues.essentialWidths).eqls([ + null, + ]); + expect(stateVariables["/sbs1"].stateValues.essentialMargins).eqls([ + 10, + null, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialWidths).eqls([ + null, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialMargins).eqls([ + null, + 25, + ]); + + // change sbs1 width to be smaller, adds to unspecified right margin + await updateMathInputValue({ latex: "50", name: "/w11", core }); + await checkSingleColumnSbs({ + core, + specifiedWidth: 60, + specifiedMargins: [4, null], + sbsName: "/sbsg", + isSbsGroup: true, + }); + await checkSingleColumnSbs({ + core, + specifiedWidth: 50, + specifiedMargins: [10, null], + sbsName: "/sbs1", + }); + await checkSingleColumnSbs({ + core, + specifiedWidth: 60, + specifiedMargins: [4, 25], + sbsName: "/sbs2", + }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/sbs1"].stateValues.essentialWidths).eqls([50]); + expect(stateVariables["/sbs1"].stateValues.essentialMargins).eqls([ + 10, + null, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialWidths).eqls([ + null, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialMargins).eqls([ + null, + 25, + ]); + + // increase sbsg left margin, cause rescaling just in sbs2 + await updateMathInputValue({ latex: "20", name: "/m1g", core }); + await checkSingleColumnSbs({ + core, + specifiedWidth: 60, + specifiedMargins: [20, null], + sbsName: "/sbsg", + isSbsGroup: true, + }); + await checkSingleColumnSbs({ + core, + specifiedWidth: 50, + specifiedMargins: [10, null], + sbsName: "/sbs1", + }); + await checkSingleColumnSbs({ + core, + specifiedWidth: 60, + specifiedMargins: [20, 25], + sbsName: "/sbs2", + }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/sbs1"].stateValues.essentialWidths).eqls([50]); + expect(stateVariables["/sbs1"].stateValues.essentialMargins).eqls([ + 10, + null, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialWidths).eqls([ + null, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialMargins).eqls([ + null, + 25, + ]); + + // increase sbsg width, causing rescaling in sbsg and a second in sbs2 + await updateMathInputValue({ latex: "90", name: "/w1g", core }); + await checkSingleColumnSbs({ + core, + specifiedWidth: 90, + specifiedMargins: [20, null], + sbsName: "/sbsg", + isSbsGroup: true, + }); + await checkSingleColumnSbs({ + core, + specifiedWidth: 50, + specifiedMargins: [10, null], + sbsName: "/sbs1", + }); + await checkSingleColumnSbs({ + core, + specifiedWidth: 90, + specifiedMargins: [20, 25], + sbsName: "/sbs2", + }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/sbs1"].stateValues.essentialWidths).eqls([50]); + expect(stateVariables["/sbs1"].stateValues.essentialMargins).eqls([ + 10, + null, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialWidths).eqls([ + null, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialMargins).eqls([ + null, + 25, + ]); + + // shrink sbsg width to remove rescaling + await updateMathInputValue({ latex: "40", name: "/w1g", core }); + await checkSingleColumnSbs({ + core, + specifiedWidth: 40, + specifiedMargins: [20, null], + sbsName: "/sbsg", + isSbsGroup: true, + }); + await checkSingleColumnSbs({ + core, + specifiedWidth: 50, + specifiedMargins: [10, null], + sbsName: "/sbs1", + }); + await checkSingleColumnSbs({ + core, + specifiedWidth: 40, + specifiedMargins: [20, 25], + sbsName: "/sbs2", + }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/sbs1"].stateValues.essentialWidths).eqls([50]); + expect(stateVariables["/sbs1"].stateValues.essentialMargins).eqls([ + 10, + null, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialWidths).eqls([ + null, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialMargins).eqls([ + null, + 25, + ]); + + // change valign of sbs1 + await updateTextInputValue({ text: "bottom", name: "/v11", core }); + await checkSingleColumnSbs({ + core, + specifiedWidth: 40, + specifiedMargins: [20, null], + sbsName: "/sbsg", + isSbsGroup: true, + }); + await checkSingleColumnSbs({ + core, + specifiedWidth: 50, + specifiedMargins: [10, null], + specifiedValign: "bottom", + sbsName: "/sbs1", + }); + await checkSingleColumnSbs({ + core, + specifiedWidth: 40, + specifiedMargins: [20, 25], + sbsName: "/sbs2", + }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/sbs1"].stateValues.essentialWidths).eqls([50]); + expect(stateVariables["/sbs1"].stateValues.essentialMargins).eqls([ + 10, + null, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialWidths).eqls([ + null, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialMargins).eqls([ + null, + 25, + ]); + + // change valign of sbsg + await updateTextInputValue({ text: "middle", name: "/v1g", core }); + await checkSingleColumnSbs({ + core, + specifiedWidth: 40, + specifiedMargins: [20, null], + specifiedValign: "middle", + sbsName: "/sbsg", + isSbsGroup: true, + }); + await checkSingleColumnSbs({ + core, + specifiedWidth: 50, + specifiedMargins: [10, null], + specifiedValign: "bottom", + sbsName: "/sbs1", + }); + await checkSingleColumnSbs({ + core, + specifiedWidth: 40, + specifiedMargins: [20, 25], + specifiedValign: "middle", + sbsName: "/sbs2", + }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/sbs1"].stateValues.essentialWidths).eqls([50]); + expect(stateVariables["/sbs1"].stateValues.essentialMargins).eqls([ + 10, + null, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialWidths).eqls([ + null, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialMargins).eqls([ + null, + 25, + ]); + + // change valign of sbs2 + await updateTextInputValue({ text: "top", name: "/v12", core }); + await checkSingleColumnSbs({ + core, + specifiedWidth: 40, + specifiedMargins: [20, null], + specifiedValign: "middle", + sbsName: "/sbsg", + isSbsGroup: true, + }); + await checkSingleColumnSbs({ + core, + specifiedWidth: 50, + specifiedMargins: [10, null], + specifiedValign: "bottom", + sbsName: "/sbs1", + }); + await checkSingleColumnSbs({ + core, + specifiedWidth: 40, + specifiedMargins: [20, 25], + specifiedValign: "top", + sbsName: "/sbs2", + }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/sbs1"].stateValues.essentialWidths).eqls([50]); + expect(stateVariables["/sbs1"].stateValues.essentialMargins).eqls([ + 10, + null, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialWidths).eqls([ + null, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialMargins).eqls([ + null, + 25, + ]); + + // valign of sbsg ignores invalid + await updateTextInputValue({ text: "banana", name: "/v1g", core }); + await checkSingleColumnSbs({ + core, + specifiedWidth: 40, + specifiedMargins: [20, null], + specifiedValign: "middle", + sbsName: "/sbsg", + isSbsGroup: true, + }); + await checkSingleColumnSbs({ + core, + specifiedWidth: 50, + specifiedMargins: [10, null], + specifiedValign: "bottom", + sbsName: "/sbs1", + }); + await checkSingleColumnSbs({ + core, + specifiedWidth: 40, + specifiedMargins: [20, 25], + specifiedValign: "top", + sbsName: "/sbs2", + }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/sbs1"].stateValues.essentialWidths).eqls([50]); + expect(stateVariables["/sbs1"].stateValues.essentialMargins).eqls([ + 10, + null, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialWidths).eqls([ + null, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialMargins).eqls([ + null, + 25, + ]); + }); + + it("sbsGroup with no arguments, two panels", async () => { + let core = await createTestCore({ + doenetML: ` + + + + + + + + + + + +

Widths for sbsg: + + +

+ +

Margins for sbsg: + + +

+ +

Valigns for sbsg: + + +

+ +

Widths for sbs1: + + +

+ +

Margins for sbs1: + + +

+ +

Valigns for sbs1: + + +

+ +

Widths for sbs2: + + +

+ +

Margins for sbs2: + + +

+ +

Valigns for sbs2: + + +

+ `, + }); + + await checkTwoColumnSbs({ core, sbsName: "/sbsg", isSbsGroup: true }); + await checkTwoColumnSbs({ core, sbsName: "/sbs1" }); + await checkTwoColumnSbs({ core, sbsName: "/sbs2" }); + + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/sbsg"].stateValues.absoluteMeasurements).eq( + false, + ); + expect(stateVariables["/sbs1"].stateValues.absoluteMeasurements).eq( + false, + ); + expect(stateVariables["/sbs1"].stateValues.essentialWidths).eqls([ + null, + null, + ]); + expect(stateVariables["/sbs1"].stateValues.essentialMargins).eqls([ + null, + null, + ]); + expect(stateVariables["/sbs2"].stateValues.absoluteMeasurements).eq( + false, + ); + expect(stateVariables["/sbs2"].stateValues.essentialWidths).eqls([ + null, + null, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialMargins).eqls([ + null, + null, + ]); + + // change width1 of sbsg + await updateMathInputValue({ latex: "40", name: "/w1g", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [40, null], + sbsName: "/sbsg", + isSbsGroup: true, + }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [40, null], + sbsName: "/sbs1", + }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [40, null], + sbsName: "/sbs2", + }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/sbs1"].stateValues.essentialWidths).eqls([ + null, + null, + ]); + expect(stateVariables["/sbs1"].stateValues.essentialMargins).eqls([ + null, + null, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialWidths).eqls([ + null, + null, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialMargins).eqls([ + null, + null, + ]); + + // override width1 of sbs1 + await updateMathInputValue({ latex: "30", name: "/w11", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [40, null], + sbsName: "/sbsg", + isSbsGroup: true, + }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [30, null], + sbsName: "/sbs1", + }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [40, null], + sbsName: "/sbs2", + }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/sbs1"].stateValues.essentialWidths).eqls([ + 30, + null, + ]); + expect(stateVariables["/sbs1"].stateValues.essentialMargins).eqls([ + null, + null, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialWidths).eqls([ + null, + null, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialMargins).eqls([ + null, + null, + ]); + + // override width2 of sbs2 + await updateMathInputValue({ latex: "50", name: "/w22", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [40, null], + sbsName: "/sbsg", + isSbsGroup: true, + }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [30, null], + sbsName: "/sbs1", + }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [40, 50], + sbsName: "/sbs2", + }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/sbs1"].stateValues.essentialWidths).eqls([ + 30, + null, + ]); + expect(stateVariables["/sbs1"].stateValues.essentialMargins).eqls([ + null, + null, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialWidths).eqls([ + null, + 50, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialMargins).eqls([ + null, + null, + ]); + + // change left margin of sbs1 + await updateMathInputValue({ latex: "5", name: "/m11", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [40, null], + sbsName: "/sbsg", + isSbsGroup: true, + }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [30, null], + specifiedMargins: [5, null], + sbsName: "/sbs1", + }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [40, 50], + sbsName: "/sbs2", + }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/sbs1"].stateValues.essentialWidths).eqls([ + 30, + null, + ]); + expect(stateVariables["/sbs1"].stateValues.essentialMargins).eqls([ + 5, + null, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialWidths).eqls([ + null, + 50, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialMargins).eqls([ + null, + null, + ]); + + // change left margin of sbsg + await updateMathInputValue({ latex: "3", name: "/m1g", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [40, null], + specifiedMargins: [3, null], + sbsName: "/sbsg", + isSbsGroup: true, + }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [30, null], + specifiedMargins: [5, null], + sbsName: "/sbs1", + }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [40, 50], + specifiedMargins: [3, null], + sbsName: "/sbs2", + }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/sbs1"].stateValues.essentialWidths).eqls([ + 30, + null, + ]); + expect(stateVariables["/sbs1"].stateValues.essentialMargins).eqls([ + 5, + null, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialWidths).eqls([ + null, + 50, + ]); + expect(stateVariables["/sbs2"].stateValues.allMarginsSpecified).eqls([ + 3, + null, + ]); + + // change right margin of sbsg + await updateMathInputValue({ latex: "1", name: "/m2g", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [40, null], + specifiedMargins: [3, 1], + sbsName: "/sbsg", + isSbsGroup: true, + }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [30, null], + specifiedMargins: [5, 1], + sbsName: "/sbs1", + }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [40, 50], + specifiedMargins: [3, 1], + sbsName: "/sbs2", + }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/sbs1"].stateValues.essentialWidths).eqls([ + 30, + null, + ]); + expect(stateVariables["/sbs1"].stateValues.essentialMargins).eqls([ + 5, + null, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialWidths).eqls([ + null, + 50, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialMargins).eqls([ + null, + null, + ]); + + // change second width of sbsg + await updateMathInputValue({ latex: "45", name: "/w2g", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [40, 45], + specifiedMargins: [3, 1], + sbsName: "/sbsg", + isSbsGroup: true, + }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [30, 45], + specifiedMargins: [5, 1], + sbsName: "/sbs1", + }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [40, 50], + specifiedMargins: [3, 1], + sbsName: "/sbs2", + }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/sbs1"].stateValues.essentialWidths).eqls([ + 30, + null, + ]); + expect(stateVariables["/sbs1"].stateValues.essentialMargins).eqls([ + 5, + null, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialWidths).eqls([ + null, + 50, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialMargins).eqls([ + null, + null, + ]); + + // increase second width of sbsg to cause rescaling + await updateMathInputValue({ latex: "65", name: "/w2g", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [40, 65], + specifiedMargins: [3, 1], + sbsName: "/sbsg", + isSbsGroup: true, + }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [30, 65], + specifiedMargins: [5, 1], + sbsName: "/sbs1", + }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [40, 50], + specifiedMargins: [3, 1], + sbsName: "/sbs2", + }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/sbs1"].stateValues.essentialWidths).eqls([ + 30, + null, + ]); + expect(stateVariables["/sbs1"].stateValues.essentialMargins).eqls([ + 5, + null, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialWidths).eqls([ + null, + 50, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialMargins).eqls([ + null, + null, + ]); + + // decrease second width of sbs1 to drop below 100% + await updateMathInputValue({ latex: "55", name: "/w21", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [40, 65], + specifiedMargins: [3, 1], + sbsName: "/sbsg", + isSbsGroup: true, + }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [30, 55], + specifiedMargins: [5, 1], + sbsName: "/sbs1", + }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [40, 50], + specifiedMargins: [3, 1], + sbsName: "/sbs2", + }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/sbs1"].stateValues.essentialWidths).eqls([ + 30, 55, + ]); + expect(stateVariables["/sbs1"].stateValues.essentialMargins).eqls([ + 5, + null, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialWidths).eqls([ + null, + 50, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialMargins).eqls([ + null, + null, + ]); + + // decrease first width of sbsg to drop below 100% + await updateMathInputValue({ latex: "25", name: "/w1g", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [25, 65], + specifiedMargins: [3, 1], + sbsName: "/sbsg", + isSbsGroup: true, + }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [30, 55], + specifiedMargins: [5, 1], + sbsName: "/sbs1", + }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [25, 50], + specifiedMargins: [3, 1], + sbsName: "/sbs2", + }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/sbs1"].stateValues.essentialWidths).eqls([ + 30, 55, + ]); + expect(stateVariables["/sbs1"].stateValues.essentialMargins).eqls([ + 5, + null, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialWidths).eqls([ + null, + 50, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialMargins).eqls([ + null, + null, + ]); + + // change first valign of sbsg + await updateTextInputValue({ text: "bottom", name: "/v1g", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [25, 65], + specifiedMargins: [3, 1], + specifiedValigns: ["bottom", null], + sbsName: "/sbsg", + isSbsGroup: true, + }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [30, 55], + specifiedMargins: [5, 1], + specifiedValigns: ["bottom", null], + sbsName: "/sbs1", + }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [25, 50], + specifiedMargins: [3, 1], + specifiedValigns: ["bottom", null], + sbsName: "/sbs2", + }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/sbs1"].stateValues.essentialWidths).eqls([ + 30, 55, + ]); + expect(stateVariables["/sbs1"].stateValues.essentialMargins).eqls([ + 5, + null, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialWidths).eqls([ + null, + 50, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialMargins).eqls([ + null, + null, + ]); + + // change first valign of sbs2 + await updateTextInputValue({ text: "middle", name: "/v12", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [25, 65], + specifiedMargins: [3, 1], + specifiedValigns: ["bottom", null], + sbsName: "/sbsg", + isSbsGroup: true, + }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [30, 55], + specifiedMargins: [5, 1], + specifiedValigns: ["bottom", null], + sbsName: "/sbs1", + }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [25, 50], + specifiedMargins: [3, 1], + specifiedValigns: ["middle", null], + sbsName: "/sbs2", + }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/sbs1"].stateValues.essentialWidths).eqls([ + 30, 55, + ]); + expect(stateVariables["/sbs1"].stateValues.essentialMargins).eqls([ + 5, + null, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialWidths).eqls([ + null, + 50, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialMargins).eqls([ + null, + null, + ]); + + // change second valign of sbs1 + await updateTextInputValue({ text: "middle", name: "/v21", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [25, 65], + specifiedMargins: [3, 1], + specifiedValigns: ["bottom", null], + sbsName: "/sbsg", + isSbsGroup: true, + }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [30, 55], + specifiedMargins: [5, 1], + specifiedValigns: ["bottom", "middle"], + sbsName: "/sbs1", + }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [25, 50], + specifiedMargins: [3, 1], + specifiedValigns: ["middle", null], + sbsName: "/sbs2", + }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/sbs1"].stateValues.essentialWidths).eqls([ + 30, 55, + ]); + expect(stateVariables["/sbs1"].stateValues.essentialMargins).eqls([ + 5, + null, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialWidths).eqls([ + null, + 50, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialMargins).eqls([ + null, + null, + ]); + + // change second valign of sbsg + await updateTextInputValue({ text: "bottom", name: "/v2g", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [25, 65], + specifiedMargins: [3, 1], + specifiedValigns: ["bottom", "bottom"], + sbsName: "/sbsg", + isSbsGroup: true, + }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [30, 55], + specifiedMargins: [5, 1], + specifiedValigns: ["bottom", "middle"], + sbsName: "/sbs1", + }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [25, 50], + specifiedMargins: [3, 1], + specifiedValigns: ["middle", "bottom"], + sbsName: "/sbs2", + }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/sbs1"].stateValues.essentialWidths).eqls([ + 30, 55, + ]); + expect(stateVariables["/sbs1"].stateValues.essentialMargins).eqls([ + 5, + null, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialWidths).eqls([ + null, + 50, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialMargins).eqls([ + null, + null, + ]); + }); + + it("sbsGroup with singular arguments, sidebysides with plural or no arguments, two panels", async () => { + let core = await createTestCore({ + doenetML: ` + + + + + + + + + + + +

Widths for sbsg: + + +

+ +

Margins for sbsg: + + +

+ +

Valigns for sbsg: + + +

+ +

Widths for sbs1: + + +

+ +

Margins for sbs1: + + +

+ +

Valigns for sbs1: + + +

+ +

Widths for sbs2: + + +

+ +

Margins for sbs2: + + +

+ +

Valigns for sbs2: + + +

+ `, + }); + + await checkTwoColumnSbs({ + core, + specifiedWidths: [25, 25], + specifiedMargins: [10, 10], + specifiedValigns: ["middle", "middle"], + sbsName: "/sbsg", + isSbsGroup: true, + }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [40, 20], + specifiedMargins: [10, 10], + specifiedValigns: ["top", "middle"], + sbsName: "/sbs1", + }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [25, 25], + specifiedMargins: [15, 5], + specifiedValigns: ["bottom", "top"], + sbsName: "/sbs2", + }); + + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/sbsg"].stateValues.absoluteMeasurements).eq( + false, + ); + expect(stateVariables["/sbs1"].stateValues.absoluteMeasurements).eq( + false, + ); + expect(stateVariables["/sbs1"].stateValues.essentialWidths).eqls([ + null, + null, + ]); + expect(stateVariables["/sbs1"].stateValues.essentialMargins).eqls([ + null, + null, + ]); + expect(stateVariables["/sbs2"].stateValues.absoluteMeasurements).eq( + false, + ); + expect(stateVariables["/sbs2"].stateValues.essentialWidths).eqls([ + null, + null, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialMargins).eqls([ + null, + null, + ]); + + // change width1 of sbsg + await updateMathInputValue({ latex: "20", name: "/w1g", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [20, 20], + specifiedMargins: [10, 10], + specifiedValigns: ["middle", "middle"], + sbsName: "/sbsg", + isSbsGroup: true, + }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [40, 20], + specifiedMargins: [10, 10], + specifiedValigns: ["top", "middle"], + sbsName: "/sbs1", + }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [20, 20], + specifiedMargins: [15, 5], + specifiedValigns: ["bottom", "top"], + sbsName: "/sbs2", + }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/sbs1"].stateValues.essentialWidths).eqls([ + null, + null, + ]); + expect(stateVariables["/sbs1"].stateValues.essentialMargins).eqls([ + null, + null, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialWidths).eqls([ + null, + null, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialMargins).eqls([ + null, + null, + ]); + + // change width2 of sbs2 + await updateMathInputValue({ latex: "15", name: "/w22", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [20, 20], + specifiedMargins: [10, 10], + specifiedValigns: ["middle", "middle"], + sbsName: "/sbsg", + isSbsGroup: true, + }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [40, 20], + specifiedMargins: [10, 10], + specifiedValigns: ["top", "middle"], + sbsName: "/sbs1", + }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [20, 15], + specifiedMargins: [15, 5], + specifiedValigns: ["bottom", "top"], + sbsName: "/sbs2", + }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/sbs1"].stateValues.essentialWidths).eqls([ + null, + null, + ]); + expect(stateVariables["/sbs1"].stateValues.essentialMargins).eqls([ + null, + null, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialWidths).eqls([ + null, + 15, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialMargins).eqls([ + null, + null, + ]); + + // change width2 of sbsg + await updateMathInputValue({ latex: "12", name: "/w2g", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [12, 12], + specifiedMargins: [10, 10], + specifiedValigns: ["middle", "middle"], + sbsName: "/sbsg", + isSbsGroup: true, + }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [40, 20], + specifiedMargins: [10, 10], + specifiedValigns: ["top", "middle"], + sbsName: "/sbs1", + }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [12, 15], + specifiedMargins: [15, 5], + specifiedValigns: ["bottom", "top"], + sbsName: "/sbs2", + }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/sbs1"].stateValues.essentialWidths).eqls([ + null, + null, + ]); + expect(stateVariables["/sbs1"].stateValues.essentialMargins).eqls([ + null, + null, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialWidths).eqls([ + null, + 15, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialMargins).eqls([ + null, + null, + ]); + + // change width1 of sbs1 + await updateMathInputValue({ latex: "35", name: "/w11", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [12, 12], + specifiedMargins: [10, 10], + specifiedValigns: ["middle", "middle"], + sbsName: "/sbsg", + isSbsGroup: true, + }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [35, 20], + specifiedMargins: [10, 10], + specifiedValigns: ["top", "middle"], + sbsName: "/sbs1", + }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [12, 15], + specifiedMargins: [15, 5], + specifiedValigns: ["bottom", "top"], + sbsName: "/sbs2", + }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/sbs1"].stateValues.essentialWidths).eqls([ + null, + null, + ]); + expect(stateVariables["/sbs1"].stateValues.essentialMargins).eqls([ + null, + null, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialWidths).eqls([ + null, + 15, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialMargins).eqls([ + null, + null, + ]); + + // change margins of sbs2 + await updateMathInputValue({ latex: "22", name: "/m12", core }); + await updateMathInputValue({ latex: "11", name: "/m22", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [12, 12], + specifiedMargins: [10, 10], + specifiedValigns: ["middle", "middle"], + sbsName: "/sbsg", + isSbsGroup: true, + }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [35, 20], + specifiedMargins: [10, 10], + specifiedValigns: ["top", "middle"], + sbsName: "/sbs1", + }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [12, 15], + specifiedMargins: [22, 11], + specifiedValigns: ["bottom", "top"], + sbsName: "/sbs2", + }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/sbs1"].stateValues.essentialWidths).eqls([ + null, + null, + ]); + expect(stateVariables["/sbs1"].stateValues.essentialMargins).eqls([ + null, + null, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialWidths).eqls([ + null, + 15, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialMargins).eqls([ + null, + null, + ]); + + // change right margin of sbsg + await updateMathInputValue({ latex: "8", name: "/m2g", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [12, 12], + specifiedMargins: [8, 8], + specifiedValigns: ["middle", "middle"], + sbsName: "/sbsg", + isSbsGroup: true, + }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [35, 20], + specifiedMargins: [8, 8], + specifiedValigns: ["top", "middle"], + sbsName: "/sbs1", + }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [12, 15], + specifiedMargins: [22, 11], + specifiedValigns: ["bottom", "top"], + sbsName: "/sbs2", + }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/sbs1"].stateValues.essentialWidths).eqls([ + null, + null, + ]); + expect(stateVariables["/sbs1"].stateValues.essentialMargins).eqls([ + null, + null, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialWidths).eqls([ + null, + 15, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialMargins).eqls([ + null, + null, + ]); + + // change right margin of sbs1 + await updateMathInputValue({ latex: "7", name: "/m21", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [12, 12], + specifiedMargins: [8, 8], + specifiedValigns: ["middle", "middle"], + sbsName: "/sbsg", + isSbsGroup: true, + }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [35, 20], + specifiedMargins: [8, 7], + specifiedValigns: ["top", "middle"], + sbsName: "/sbs1", + }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [12, 15], + specifiedMargins: [22, 11], + specifiedValigns: ["bottom", "top"], + sbsName: "/sbs2", + }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/sbs1"].stateValues.essentialWidths).eqls([ + null, + null, + ]); + expect(stateVariables["/sbs1"].stateValues.essentialMargins).eqls([ + null, + 7, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialWidths).eqls([ + null, + 15, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialMargins).eqls([ + null, + null, + ]); + + // change left margin of sbsg + await updateMathInputValue({ latex: "9", name: "/m1g", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [12, 12], + specifiedMargins: [9, 9], + specifiedValigns: ["middle", "middle"], + sbsName: "/sbsg", + isSbsGroup: true, + }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [35, 20], + specifiedMargins: [9, 7], + specifiedValigns: ["top", "middle"], + sbsName: "/sbs1", + }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [12, 15], + specifiedMargins: [22, 11], + specifiedValigns: ["bottom", "top"], + sbsName: "/sbs2", + }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/sbs1"].stateValues.essentialWidths).eqls([ + null, + null, + ]); + expect(stateVariables["/sbs1"].stateValues.essentialMargins).eqls([ + null, + 7, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialWidths).eqls([ + null, + 15, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialMargins).eqls([ + null, + null, + ]); + + // change left margin of sbs1 + await updateMathInputValue({ latex: "6", name: "/m11", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [12, 12], + specifiedMargins: [9, 9], + specifiedValigns: ["middle", "middle"], + sbsName: "/sbsg", + isSbsGroup: true, + }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [35, 20], + specifiedMargins: [6, 7], + specifiedValigns: ["top", "middle"], + sbsName: "/sbs1", + }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [12, 15], + specifiedMargins: [22, 11], + specifiedValigns: ["bottom", "top"], + sbsName: "/sbs2", + }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/sbs1"].stateValues.essentialWidths).eqls([ + null, + null, + ]); + expect(stateVariables["/sbs1"].stateValues.essentialMargins).eqls([ + 6, 7, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialWidths).eqls([ + null, + 15, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialMargins).eqls([ + null, + null, + ]); + + // change valign1 of sbsg + await updateTextInputValue({ text: "bottom", name: "/v1g", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [12, 12], + specifiedMargins: [9, 9], + specifiedValigns: ["bottom", "bottom"], + sbsName: "/sbsg", + isSbsGroup: true, + }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [35, 20], + specifiedMargins: [6, 7], + specifiedValigns: ["top", "bottom"], + sbsName: "/sbs1", + }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [12, 15], + specifiedMargins: [22, 11], + specifiedValigns: ["bottom", "top"], + sbsName: "/sbs2", + }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/sbs1"].stateValues.essentialWidths).eqls([ + null, + null, + ]); + expect(stateVariables["/sbs1"].stateValues.essentialMargins).eqls([ + 6, 7, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialWidths).eqls([ + null, + 15, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialMargins).eqls([ + null, + null, + ]); + + // change valign2 of sbs1 + await updateTextInputValue({ text: "middle", name: "/v21", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [12, 12], + specifiedMargins: [9, 9], + specifiedValigns: ["bottom", "bottom"], + sbsName: "/sbsg", + isSbsGroup: true, + }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [35, 20], + specifiedMargins: [6, 7], + specifiedValigns: ["top", "middle"], + sbsName: "/sbs1", + }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [12, 15], + specifiedMargins: [22, 11], + specifiedValigns: ["bottom", "top"], + sbsName: "/sbs2", + }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/sbs1"].stateValues.essentialWidths).eqls([ + null, + null, + ]); + expect(stateVariables["/sbs1"].stateValues.essentialMargins).eqls([ + 6, 7, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialWidths).eqls([ + null, + 15, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialMargins).eqls([ + null, + null, + ]); + + // change valign2 of sbsg + await updateTextInputValue({ text: "top", name: "/v2g", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [12, 12], + specifiedMargins: [9, 9], + specifiedValigns: ["top", "top"], + sbsName: "/sbsg", + isSbsGroup: true, + }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [35, 20], + specifiedMargins: [6, 7], + specifiedValigns: ["top", "middle"], + sbsName: "/sbs1", + }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [12, 15], + specifiedMargins: [22, 11], + specifiedValigns: ["bottom", "top"], + sbsName: "/sbs2", + }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/sbs1"].stateValues.essentialWidths).eqls([ + null, + null, + ]); + expect(stateVariables["/sbs1"].stateValues.essentialMargins).eqls([ + 6, 7, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialWidths).eqls([ + null, + 15, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialMargins).eqls([ + null, + null, + ]); + + // change valign1 of sbs1 + await updateTextInputValue({ text: "bottom", name: "/v11", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [12, 12], + specifiedMargins: [9, 9], + specifiedValigns: ["top", "top"], + sbsName: "/sbsg", + isSbsGroup: true, + }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [35, 20], + specifiedMargins: [6, 7], + specifiedValigns: ["bottom", "middle"], + sbsName: "/sbs1", + }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [12, 15], + specifiedMargins: [22, 11], + specifiedValigns: ["bottom", "top"], + sbsName: "/sbs2", + }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/sbs1"].stateValues.essentialWidths).eqls([ + null, + null, + ]); + expect(stateVariables["/sbs1"].stateValues.essentialMargins).eqls([ + 6, 7, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialWidths).eqls([ + null, + 15, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialMargins).eqls([ + null, + null, + ]); + + // change valigns of sbs2 + await updateTextInputValue({ text: "middle", name: "/v12", core }); + await updateTextInputValue({ text: "bottom", name: "/v22", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [12, 12], + specifiedMargins: [9, 9], + specifiedValigns: ["top", "top"], + sbsName: "/sbsg", + isSbsGroup: true, + }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [35, 20], + specifiedMargins: [6, 7], + specifiedValigns: ["bottom", "middle"], + sbsName: "/sbs1", + }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [12, 15], + specifiedMargins: [22, 11], + specifiedValigns: ["middle", "bottom"], + sbsName: "/sbs2", + }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/sbs1"].stateValues.essentialWidths).eqls([ + null, + null, + ]); + expect(stateVariables["/sbs1"].stateValues.essentialMargins).eqls([ + 6, 7, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialWidths).eqls([ + null, + 15, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialMargins).eqls([ + null, + null, + ]); + }); + + it("sbsGroup with plural arguments, sidebysides with singular or no arguments, two panels", async () => { + let core = await createTestCore({ + doenetML: ` + + + + + + + + + + + +

Widths for sbsg: + + +

+ +

Margins for sbsg: + + +

+ +

Valigns for sbsg: + + +

+ +

Widths for sbs1: + + +

+ +

Margins for sbs1: + + +

+ +

Valigns for sbs1: + + +

+ +

Widths for sbs2: + + +

+ +

Margins for sbs2: + + +

+ +

Valigns for sbs2: + + +

+ `, + }); + + await checkTwoColumnSbs({ + core, + specifiedWidths: [25, 15], + specifiedMargins: [5, 10], + specifiedValigns: ["middle", "top"], + sbsName: "/sbsg", + isSbsGroup: true, + }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [20, 20], + specifiedMargins: [5, 10], + specifiedValigns: ["top", "top"], + sbsName: "/sbs1", + }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [25, 15], + specifiedMargins: [8, 8], + specifiedValigns: ["middle", "top"], + sbsName: "/sbs2", + }); + + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/sbsg"].stateValues.absoluteMeasurements).eq( + false, + ); + expect(stateVariables["/sbs1"].stateValues.absoluteMeasurements).eq( + false, + ); + expect(stateVariables["/sbs1"].stateValues.essentialWidths).eqls([ + null, + null, + ]); + expect(stateVariables["/sbs1"].stateValues.essentialMargins).eqls([ + null, + null, + ]); + expect(stateVariables["/sbs2"].stateValues.absoluteMeasurements).eq( + false, + ); + expect(stateVariables["/sbs2"].stateValues.essentialWidths).eqls([ + null, + null, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialMargins).eqls([ + null, + null, + ]); + + // change width1 of sbsg + await updateMathInputValue({ latex: "20", name: "/w1g", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [20, 15], + specifiedMargins: [5, 10], + specifiedValigns: ["middle", "top"], + sbsName: "/sbsg", + isSbsGroup: true, + }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [20, 20], + specifiedMargins: [5, 10], + specifiedValigns: ["top", "top"], + sbsName: "/sbs1", + }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [20, 15], + specifiedMargins: [8, 8], + specifiedValigns: ["middle", "top"], + sbsName: "/sbs2", + }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/sbs1"].stateValues.essentialWidths).eqls([ + null, + null, + ]); + expect(stateVariables["/sbs1"].stateValues.essentialMargins).eqls([ + null, + null, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialWidths).eqls([ + null, + null, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialMargins).eqls([ + null, + null, + ]); + + // change width2 of sbs2 + await updateMathInputValue({ latex: "25", name: "/w22", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [20, 15], + specifiedMargins: [5, 10], + specifiedValigns: ["middle", "top"], + sbsName: "/sbsg", + isSbsGroup: true, + }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [20, 20], + specifiedMargins: [5, 10], + specifiedValigns: ["top", "top"], + sbsName: "/sbs1", + }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [20, 25], + specifiedMargins: [8, 8], + specifiedValigns: ["middle", "top"], + sbsName: "/sbs2", + }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/sbs1"].stateValues.essentialWidths).eqls([ + null, + null, + ]); + expect(stateVariables["/sbs1"].stateValues.essentialMargins).eqls([ + null, + null, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialWidths).eqls([ + null, + 25, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialMargins).eqls([ + null, + null, + ]); + + // change width2 of sbsg + await updateMathInputValue({ latex: "12", name: "/w2g", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [20, 12], + specifiedMargins: [5, 10], + specifiedValigns: ["middle", "top"], + sbsName: "/sbsg", + isSbsGroup: true, + }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [20, 20], + specifiedMargins: [5, 10], + specifiedValigns: ["top", "top"], + sbsName: "/sbs1", + }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [20, 25], + specifiedMargins: [8, 8], + specifiedValigns: ["middle", "top"], + sbsName: "/sbs2", + }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/sbs1"].stateValues.essentialWidths).eqls([ + null, + null, + ]); + expect(stateVariables["/sbs1"].stateValues.essentialMargins).eqls([ + null, + null, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialWidths).eqls([ + null, + 25, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialMargins).eqls([ + null, + null, + ]); + + // change width1 of sbs1 + await updateMathInputValue({ latex: "35", name: "/w11", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [20, 12], + specifiedMargins: [5, 10], + specifiedValigns: ["middle", "top"], + sbsName: "/sbsg", + isSbsGroup: true, + }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [35, 35], + specifiedMargins: [5, 10], + specifiedValigns: ["top", "top"], + sbsName: "/sbs1", + }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [20, 25], + specifiedMargins: [8, 8], + specifiedValigns: ["middle", "top"], + sbsName: "/sbs2", + }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/sbs1"].stateValues.essentialWidths).eqls([ + null, + null, + ]); + expect(stateVariables["/sbs1"].stateValues.essentialMargins).eqls([ + null, + null, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialWidths).eqls([ + null, + 25, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialMargins).eqls([ + null, + null, + ]); + + // change width2 of sbs1 + await updateMathInputValue({ latex: "30", name: "/w21", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [20, 12], + specifiedMargins: [5, 10], + specifiedValigns: ["middle", "top"], + sbsName: "/sbsg", + isSbsGroup: true, + }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [30, 30], + specifiedMargins: [5, 10], + specifiedValigns: ["top", "top"], + sbsName: "/sbs1", + }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [20, 25], + specifiedMargins: [8, 8], + specifiedValigns: ["middle", "top"], + sbsName: "/sbs2", + }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/sbs1"].stateValues.essentialWidths).eqls([ + null, + null, + ]); + expect(stateVariables["/sbs1"].stateValues.essentialMargins).eqls([ + null, + null, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialWidths).eqls([ + null, + 25, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialMargins).eqls([ + null, + null, + ]); + + // change width1 of sbs2 + await updateMathInputValue({ latex: "22", name: "/w12", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [20, 12], + specifiedMargins: [5, 10], + specifiedValigns: ["middle", "top"], + sbsName: "/sbsg", + isSbsGroup: true, + }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [30, 30], + specifiedMargins: [5, 10], + specifiedValigns: ["top", "top"], + sbsName: "/sbs1", + }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [22, 25], + specifiedMargins: [8, 8], + specifiedValigns: ["middle", "top"], + sbsName: "/sbs2", + }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/sbs1"].stateValues.essentialWidths).eqls([ + null, + null, + ]); + expect(stateVariables["/sbs1"].stateValues.essentialMargins).eqls([ + null, + null, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialWidths).eqls([ + 22, 25, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialMargins).eqls([ + null, + null, + ]); + + // change right margin of sbsg + await updateMathInputValue({ latex: "8", name: "/m2g", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [20, 12], + specifiedMargins: [5, 8], + specifiedValigns: ["middle", "top"], + sbsName: "/sbsg", + isSbsGroup: true, + }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [30, 30], + specifiedMargins: [5, 8], + specifiedValigns: ["top", "top"], + sbsName: "/sbs1", + }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [22, 25], + specifiedMargins: [8, 8], + specifiedValigns: ["middle", "top"], + sbsName: "/sbs2", + }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/sbs1"].stateValues.essentialWidths).eqls([ + null, + null, + ]); + expect(stateVariables["/sbs1"].stateValues.essentialMargins).eqls([ + null, + null, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialWidths).eqls([ + 22, 25, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialMargins).eqls([ + null, + null, + ]); + + // change right margin of sbs1 + await updateMathInputValue({ latex: "7", name: "/m21", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [20, 12], + specifiedMargins: [5, 8], + specifiedValigns: ["middle", "top"], + sbsName: "/sbsg", + isSbsGroup: true, + }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [30, 30], + specifiedMargins: [5, 7], + specifiedValigns: ["top", "top"], + sbsName: "/sbs1", + }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [22, 25], + specifiedMargins: [8, 8], + specifiedValigns: ["middle", "top"], + sbsName: "/sbs2", + }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/sbs1"].stateValues.essentialWidths).eqls([ + null, + null, + ]); + expect(stateVariables["/sbs1"].stateValues.essentialMargins).eqls([ + null, + 7, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialWidths).eqls([ + 22, 25, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialMargins).eqls([ + null, + null, + ]); + + // change left margin of sbsg + await updateMathInputValue({ latex: "9", name: "/m1g", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [20, 12], + specifiedMargins: [9, 8], + specifiedValigns: ["middle", "top"], + sbsName: "/sbsg", + isSbsGroup: true, + }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [30, 30], + specifiedMargins: [9, 7], + specifiedValigns: ["top", "top"], + sbsName: "/sbs1", + }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [22, 25], + specifiedMargins: [8, 8], + specifiedValigns: ["middle", "top"], + sbsName: "/sbs2", + }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/sbs1"].stateValues.essentialWidths).eqls([ + null, + null, + ]); + expect(stateVariables["/sbs1"].stateValues.essentialMargins).eqls([ + null, + 7, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialWidths).eqls([ + 22, 25, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialMargins).eqls([ + null, + null, + ]); + + // change left margin of sbs1 + await updateMathInputValue({ latex: "6", name: "/m11", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [20, 12], + specifiedMargins: [9, 8], + specifiedValigns: ["middle", "top"], + sbsName: "/sbsg", + isSbsGroup: true, + }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [30, 30], + specifiedMargins: [6, 7], + specifiedValigns: ["top", "top"], + sbsName: "/sbs1", + }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [22, 25], + specifiedMargins: [8, 8], + specifiedValigns: ["middle", "top"], + sbsName: "/sbs2", + }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/sbs1"].stateValues.essentialWidths).eqls([ + null, + null, + ]); + expect(stateVariables["/sbs1"].stateValues.essentialMargins).eqls([ + 6, 7, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialWidths).eqls([ + 22, 25, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialMargins).eqls([ + null, + null, + ]); + + // change right margin of sbs2 + await updateMathInputValue({ latex: "3", name: "/m22", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [20, 12], + specifiedMargins: [9, 8], + specifiedValigns: ["middle", "top"], + sbsName: "/sbsg", + isSbsGroup: true, + }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [30, 30], + specifiedMargins: [6, 7], + specifiedValigns: ["top", "top"], + sbsName: "/sbs1", + }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [22, 25], + specifiedMargins: [3, 3], + specifiedValigns: ["middle", "top"], + sbsName: "/sbs2", + }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/sbs1"].stateValues.essentialWidths).eqls([ + null, + null, + ]); + expect(stateVariables["/sbs1"].stateValues.essentialMargins).eqls([ + 6, 7, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialWidths).eqls([ + 22, 25, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialMargins).eqls([ + null, + null, + ]); + + // change left margin of sbs2 + await updateMathInputValue({ latex: "10", name: "/m12", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [20, 12], + specifiedMargins: [9, 8], + specifiedValigns: ["middle", "top"], + sbsName: "/sbsg", + isSbsGroup: true, + }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [30, 30], + specifiedMargins: [6, 7], + specifiedValigns: ["top", "top"], + sbsName: "/sbs1", + }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [22, 25], + specifiedMargins: [10, 10], + specifiedValigns: ["middle", "top"], + sbsName: "/sbs2", + }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/sbs1"].stateValues.essentialWidths).eqls([ + null, + null, + ]); + expect(stateVariables["/sbs1"].stateValues.essentialMargins).eqls([ + 6, 7, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialWidths).eqls([ + 22, 25, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialMargins).eqls([ + null, + null, + ]); + + // change valign1 of sbsg + await updateTextInputValue({ text: "bottom", name: "/v1g", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [20, 12], + specifiedMargins: [9, 8], + specifiedValigns: ["bottom", "top"], + sbsName: "/sbsg", + isSbsGroup: true, + }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [30, 30], + specifiedMargins: [6, 7], + specifiedValigns: ["top", "top"], + sbsName: "/sbs1", + }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [22, 25], + specifiedMargins: [10, 10], + specifiedValigns: ["bottom", "top"], + sbsName: "/sbs2", + }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/sbs1"].stateValues.essentialWidths).eqls([ + null, + null, + ]); + expect(stateVariables["/sbs1"].stateValues.essentialMargins).eqls([ + 6, 7, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialWidths).eqls([ + 22, 25, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialMargins).eqls([ + null, + null, + ]); + + // change valign2 of sbs1 + await updateTextInputValue({ text: "middle", name: "/v21", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [20, 12], + specifiedMargins: [9, 8], + specifiedValigns: ["bottom", "top"], + sbsName: "/sbsg", + isSbsGroup: true, + }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [30, 30], + specifiedMargins: [6, 7], + specifiedValigns: ["middle", "middle"], + sbsName: "/sbs1", + }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [22, 25], + specifiedMargins: [10, 10], + specifiedValigns: ["bottom", "top"], + sbsName: "/sbs2", + }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/sbs1"].stateValues.essentialWidths).eqls([ + null, + null, + ]); + expect(stateVariables["/sbs1"].stateValues.essentialMargins).eqls([ + 6, 7, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialWidths).eqls([ + 22, 25, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialMargins).eqls([ + null, + null, + ]); + + // change valign2 of sbsg + await updateTextInputValue({ text: "middle", name: "/v2g", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [20, 12], + specifiedMargins: [9, 8], + specifiedValigns: ["bottom", "middle"], + sbsName: "/sbsg", + isSbsGroup: true, + }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [30, 30], + specifiedMargins: [6, 7], + specifiedValigns: ["middle", "middle"], + sbsName: "/sbs1", + }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [22, 25], + specifiedMargins: [10, 10], + specifiedValigns: ["bottom", "middle"], + sbsName: "/sbs2", + }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/sbs1"].stateValues.essentialWidths).eqls([ + null, + null, + ]); + expect(stateVariables["/sbs1"].stateValues.essentialMargins).eqls([ + 6, 7, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialWidths).eqls([ + 22, 25, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialMargins).eqls([ + null, + null, + ]); + + // change valign1 of sbs1 + await updateTextInputValue({ text: "top", name: "/v11", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [20, 12], + specifiedMargins: [9, 8], + specifiedValigns: ["bottom", "middle"], + sbsName: "/sbsg", + isSbsGroup: true, + }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [30, 30], + specifiedMargins: [6, 7], + specifiedValigns: ["top", "top"], + sbsName: "/sbs1", + }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [22, 25], + specifiedMargins: [10, 10], + specifiedValigns: ["bottom", "middle"], + sbsName: "/sbs2", + }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/sbs1"].stateValues.essentialWidths).eqls([ + null, + null, + ]); + expect(stateVariables["/sbs1"].stateValues.essentialMargins).eqls([ + 6, 7, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialWidths).eqls([ + 22, 25, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialMargins).eqls([ + null, + null, + ]); + + // change valigns of sbs2 + await updateTextInputValue({ text: "middle", name: "/v12", core }); + await updateTextInputValue({ text: "bottom", name: "/v22", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [20, 12], + specifiedMargins: [9, 8], + specifiedValigns: ["bottom", "middle"], + sbsName: "/sbsg", + isSbsGroup: true, + }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [30, 30], + specifiedMargins: [6, 7], + specifiedValigns: ["top", "top"], + sbsName: "/sbs1", + }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [22, 25], + specifiedMargins: [10, 10], + specifiedValigns: ["middle", "bottom"], + sbsName: "/sbs2", + }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/sbs1"].stateValues.essentialWidths).eqls([ + null, + null, + ]); + expect(stateVariables["/sbs1"].stateValues.essentialMargins).eqls([ + 6, 7, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialWidths).eqls([ + 22, 25, + ]); + expect(stateVariables["/sbs2"].stateValues.essentialMargins).eqls([ + null, + null, + ]); + }); + + it("copy sbsGroup and overwrite parameters", async () => { + let core = await createTestCore({ + doenetML: ` + + + + + + + + + + + + + + `, + }); + + await checkTwoColumnSbs({ + core, + specifiedWidths: [25, 15], + specifiedMargins: [5, 10], + specifiedValigns: ["middle", "top"], + sbsName: "/sbsg", + isSbsGroup: true, + }); + + await checkTwoColumnSbs({ + core, + specifiedWidths: [20, 20], + specifiedMargins: [5, 10], + specifiedValigns: ["top", "top"], + sbsName: "/sbsg/sbs1", + }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [25, 15], + specifiedMargins: [8, 8], + specifiedValigns: ["middle", "top"], + sbsName: "/sbsg/sbs2", + }); + + await checkTwoColumnSbs({ + core, + specifiedWidths: [30, 10], + specifiedMargins: [1, 3], + specifiedValigns: ["bottom", "middle"], + sbsName: "/sbsg2", + isSbsGroup: true, + }); + + await checkTwoColumnSbs({ + core, + specifiedWidths: [20, 20], + specifiedMargins: [1, 3], + specifiedValigns: ["top", "top"], + sbsName: "/sbsg2/sbs1", + }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [30, 10], + specifiedMargins: [8, 8], + specifiedValigns: ["bottom", "middle"], + sbsName: "/sbsg2/sbs2", + }); + + await checkTwoColumnSbs({ + core, + specifiedWidths: [7, null], + specifiedMargins: [1, 3], + specifiedValigns: ["top", "bottom"], + sbsName: "/sbsg3", + isSbsGroup: true, + }); + + await checkTwoColumnSbs({ + core, + specifiedWidths: [20, 20], + specifiedMargins: [1, 3], + specifiedValigns: ["top", "top"], + sbsName: "/sbsg3/sbs1", + }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [7, null], + specifiedMargins: [8, 8], + specifiedValigns: ["top", "bottom"], + sbsName: "/sbsg3/sbs2", + }); + }); + + it("sbsGroup absolute measurements turned to absolute with warning", async () => { + let core = await createTestCore({ + doenetML: ` + + +

Hello

+

Hello

+
+
+ + `, + }); + + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/_sbsgroup1"].stateValues.marginsAbsolute).eq( + false, + ); + expect(stateVariables["/_sbsgroup1"].stateValues.widthsAbsolute).eq( + false, + ); + + let errorWarnings = core.errorWarnings; + + expect(errorWarnings.errors.length).eq(0); + expect(errorWarnings.warnings.length).eq(2); + + expect(errorWarnings.warnings[0].message).contain( + ` is not implemented for absolute measurements. Setting widths to relative`, + ); + expect(errorWarnings.warnings[0].level).eq(1); + expect(errorWarnings.warnings[0].doenetMLrange.lineBegin).eq(2); + expect(errorWarnings.warnings[0].doenetMLrange.charBegin).eq(5); + expect(errorWarnings.warnings[0].doenetMLrange.lineEnd).eq(7); + expect(errorWarnings.warnings[0].doenetMLrange.charEnd).eq(15); + + expect(errorWarnings.warnings[1].message).contain( + ` is not implemented for absolute measurements. Setting margins to relative`, + ); + expect(errorWarnings.warnings[1].level).eq(1); + expect(errorWarnings.warnings[1].doenetMLrange.lineBegin).eq(2); + expect(errorWarnings.warnings[1].doenetMLrange.charBegin).eq(5); + expect(errorWarnings.warnings[1].doenetMLrange.lineEnd).eq(7); + expect(errorWarnings.warnings[1].doenetMLrange.charEnd).eq(15); + }); + + it("sideBySide with a stack", async () => { + let core = await createTestCore({ + doenetML: ` + + +

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

+

Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.

+

Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

+
+

Fringilla ut morbi tincidunt augue interdum velit euismod in. Mattis molestie a iaculis at erat. Pharetra magna ac placerat vestibulum lectus mauris ultrices. Nisl rhoncus mattis rhoncus urna neque viverra justo nec ultrices. Congue quisque egestas diam in arcu cursus euismod quis viverra. Et leo duis ut diam quam nulla porttitor massa. Dolor sit amet consectetur adipiscing elit. Ullamcorper malesuada proin libero nunc consequat interdum varius. Nunc lobortis mattis aliquam faucibus purus. Amet commodo nulla facilisi nullam vehicula. Massa placerat duis ultricies lacus sed turpis.

+
+ + + `, + }); + + await checkTwoColumnSbs({ + core, + specifiedWidths: [49, 49], + specifiedMargins: [0, 0], + sbsName: "/sbs", + }); + + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/sbs"].stateValues.absoluteMeasurements).eq( + false, + ); + }); + + it("sideBySide with singular relative arguments from inputs, two panels", async () => { + let core = await createTestCore({ + doenetML: ` + +

Width:

+

Margin:

+

Valign:

+ + + + + + +

Widths: + + +

+ +

Margins: + + +

+ +

Valigns: + + +

+ `, + }); + + await checkTwoColumnSbs({ + core, + specifiedWidths: [20, 20], + specifiedMargins: [10, 10], + specifiedValigns: ["middle", "middle"], + sbsName: "/sbs", + }); + + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/sbs"].stateValues.absoluteMeasurements).eq( + false, + ); + + // change first width, second matches + await updateMathInputValue({ latex: "30", name: "/w1", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [30, 30], + specifiedMargins: [10, 10], + specifiedValigns: ["middle", "middle"], + sbsName: "/sbs", + }); + + // change second width, first matches, rescaling + await updateMathInputValue({ latex: "80", name: "/w2", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [80, 80], + specifiedMargins: [10, 10], + specifiedValigns: ["middle", "middle"], + sbsName: "/sbs", + }); + + // change defining width + await updateMathInputValue({ latex: "25", name: "/w", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [25, 25], + specifiedMargins: [10, 10], + specifiedValigns: ["middle", "middle"], + sbsName: "/sbs", + }); + + // invalid defining width treated as null + await updateMathInputValue({ latex: "x", name: "/w", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [null, null], + specifiedMargins: [10, 10], + specifiedValigns: ["middle", "middle"], + sbsName: "/sbs", + }); + + // reset width by changing second width + await updateMathInputValue({ latex: "10", name: "/w2", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [10, 10], + specifiedMargins: [10, 10], + specifiedValigns: ["middle", "middle"], + sbsName: "/sbs", + }); + + // decrease defining margin + await updateMathInputValue({ latex: "5", name: "/m", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [10, 10], + specifiedMargins: [5, 5], + specifiedValigns: ["middle", "middle"], + sbsName: "/sbs", + }); + + // invalid defining margin treated as null + await updateMathInputValue({ latex: "none", name: "/m", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [10, 10], + specifiedMargins: [null, null], + specifiedValigns: ["middle", "middle"], + sbsName: "/sbs", + }); + + // reset from left margin, right margin matches + await updateMathInputValue({ latex: "15", name: "/m1", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [10, 10], + specifiedMargins: [15, 15], + specifiedValigns: ["middle", "middle"], + sbsName: "/sbs", + }); + + // increase right margin, left margin matches, rescaling + await updateMathInputValue({ latex: "45", name: "/m2", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [10, 10], + specifiedMargins: [45, 45], + specifiedValigns: ["middle", "middle"], + sbsName: "/sbs", + }); + + // change first valign + await updateTextInputValue({ text: "top", name: "/v1", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [10, 10], + specifiedMargins: [45, 45], + specifiedValigns: ["top", "top"], + sbsName: "/sbs", + }); + + // change defining valign + await updateTextInputValue({ text: "bottom", name: "/v1", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [10, 10], + specifiedMargins: [45, 45], + specifiedValigns: ["bottom", "bottom"], + sbsName: "/sbs", + }); + + // change second valign + await updateTextInputValue({ text: "middle", name: "/v2", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [10, 10], + specifiedMargins: [45, 45], + specifiedValigns: ["middle", "middle"], + sbsName: "/sbs", + }); + + // invalid defining valign becomes top + await updateTextInputValue({ text: "invalid", name: "/v", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [10, 10], + specifiedMargins: [45, 45], + specifiedValigns: [null, null], + sbsName: "/sbs", + }); + + // reset from first valign + await updateTextInputValue({ text: "bottom", name: "/v1", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [10, 10], + specifiedMargins: [45, 45], + specifiedValigns: ["bottom", "bottom"], + sbsName: "/sbs", + }); + }); + + it("sideBySide with plural relative arguments from inputs, two panels", async () => { + let core = await createTestCore({ + doenetML: ` +

Defining widths: + + +

+

Defining margins: + + +

+

Defining valigns: + + +

+ + + + + + +

Widths: + + +

+ +

Margins: + + +

+ +

Valigns: + + +

+ `, + }); + + await checkTwoColumnSbs({ + core, + specifiedWidths: [20, 10], + specifiedMargins: [10, 20], + specifiedValigns: ["middle", "bottom"], + sbsName: "/sbs", + }); + + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/sbs"].stateValues.absoluteMeasurements).eq( + false, + ); + + // change first width + await updateMathInputValue({ latex: "30", name: "/w1", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [30, 10], + specifiedMargins: [10, 20], + specifiedValigns: ["middle", "bottom"], + sbsName: "/sbs", + }); + + // change second defining width, rescaling + await updateMathInputValue({ latex: "110", name: "/dw2", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [30, 110], + specifiedMargins: [10, 20], + specifiedValigns: ["middle", "bottom"], + sbsName: "/sbs", + }); + + // make second defining width be invalid, treated as null + await updateMathInputValue({ latex: "hello", name: "/dw2", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [30, null], + specifiedMargins: [10, 20], + specifiedValigns: ["middle", "bottom"], + sbsName: "/sbs", + }); + + // make first defining width be invalid, treated as null + await updateMathInputValue({ latex: "bye", name: "/dw1", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [null, null], + specifiedMargins: [10, 20], + specifiedValigns: ["middle", "bottom"], + sbsName: "/sbs", + }); + + // reset second width + await updateMathInputValue({ latex: "5", name: "/w2", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [null, 5], + specifiedMargins: [10, 20], + specifiedValigns: ["middle", "bottom"], + sbsName: "/sbs", + }); + + // reset first width + await updateMathInputValue({ latex: "30", name: "/w1", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [30, 5], + specifiedMargins: [10, 20], + specifiedValigns: ["middle", "bottom"], + sbsName: "/sbs", + }); + + // decrease right margin + await updateMathInputValue({ latex: "5", name: "/m2", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [30, 5], + specifiedMargins: [10, 5], + specifiedValigns: ["middle", "bottom"], + sbsName: "/sbs", + }); + + // increase left defining margin, rescaling + await updateMathInputValue({ latex: "77.5", name: "/dm1", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [30, 5], + specifiedMargins: [77.5, 5], + specifiedValigns: ["middle", "bottom"], + sbsName: "/sbs", + }); + + // decrease left margin + await updateMathInputValue({ latex: "7", name: "/m1", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [30, 5], + specifiedMargins: [7, 5], + specifiedValigns: ["middle", "bottom"], + sbsName: "/sbs", + }); + + // invalid left defining margin, treated as null + await updateMathInputValue({ latex: "hello", name: "/dm1", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [30, 5], + specifiedMargins: [null, 5], + specifiedValigns: ["middle", "bottom"], + sbsName: "/sbs", + }); + + // invalid right defining margin, treated as null + await updateMathInputValue({ latex: "bye", name: "/dm2", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [30, 5], + specifiedMargins: [null, null], + specifiedValigns: ["middle", "bottom"], + sbsName: "/sbs", + }); + + // reset left margin + await updateMathInputValue({ latex: "12", name: "/m1", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [30, 5], + specifiedMargins: [12, null], + specifiedValigns: ["middle", "bottom"], + sbsName: "/sbs", + }); + + // reset right margin + await updateMathInputValue({ latex: "8", name: "/m2", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [30, 5], + specifiedMargins: [12, 8], + specifiedValigns: ["middle", "bottom"], + sbsName: "/sbs", + }); + + // change first valign + await updateTextInputValue({ text: "top", name: "/v1", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [30, 5], + specifiedMargins: [12, 8], + specifiedValigns: ["top", "bottom"], + sbsName: "/sbs", + }); + + // change second valign + await updateTextInputValue({ text: "middle", name: "/v2", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [30, 5], + specifiedMargins: [12, 8], + specifiedValigns: ["top", "middle"], + sbsName: "/sbs", + }); + + // change first defining valign + await updateTextInputValue({ text: "bottom", name: "/dv1", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [30, 5], + specifiedMargins: [12, 8], + specifiedValigns: ["bottom", "middle"], + sbsName: "/sbs", + }); + + // change second defining valign + await updateTextInputValue({ text: "bottom", name: "/dv2", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [30, 5], + specifiedMargins: [12, 8], + specifiedValigns: ["bottom", "bottom"], + sbsName: "/sbs", + }); + + // invalid second defining valign treated as null + await updateTextInputValue({ text: "banana", name: "/dv2", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [30, 5], + specifiedMargins: [12, 8], + specifiedValigns: ["bottom", null], + sbsName: "/sbs", + }); + + // invalid first defining valign treated as null + await updateTextInputValue({ text: "apple", name: "/dv1", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [30, 5], + specifiedMargins: [12, 8], + specifiedValigns: [null, null], + sbsName: "/sbs", + }); + + // reset first valign + await updateTextInputValue({ text: "middle", name: "/v1", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [30, 5], + specifiedMargins: [12, 8], + specifiedValigns: ["middle", null], + sbsName: "/sbs", + }); + + // reset second valign + await updateTextInputValue({ text: "bottom", name: "/v2", core }); + await checkTwoColumnSbs({ + core, + specifiedWidths: [30, 5], + specifiedMargins: [12, 8], + specifiedValigns: ["middle", "bottom"], + sbsName: "/sbs", + }); + }); +}); diff --git a/packages/doenetml-worker/src/test/tagSpecific/singlecharactercomponents.test.ts b/packages/doenetml-worker/src/test/tagSpecific/singlecharactercomponents.test.ts index b2ca29578..d3be5b219 100644 --- a/packages/doenetml-worker/src/test/tagSpecific/singlecharactercomponents.test.ts +++ b/packages/doenetml-worker/src/test/tagSpecific/singlecharactercomponents.test.ts @@ -3,6 +3,7 @@ import { createTestCore, returnAllStateVariables } from "../utils/test-core"; const Mock = vi.fn(); vi.stubGlobal("postMessage", Mock); +vi.mock("hyperformula"); describe("Single character tag tests", async () => { it("dashes", async () => { diff --git a/packages/doenetml-worker/src/test/tagSpecific/slider.test.ts b/packages/doenetml-worker/src/test/tagSpecific/slider.test.ts new file mode 100644 index 000000000..b463092ad --- /dev/null +++ b/packages/doenetml-worker/src/test/tagSpecific/slider.test.ts @@ -0,0 +1,326 @@ +import { describe, expect, it, vi } from "vitest"; +import { createTestCore, returnAllStateVariables } from "../utils/test-core"; +import { updateMathInputValue } from "../utils/actions"; +import Core from "../../Core"; + +const Mock = vi.fn(); +vi.stubGlobal("postMessage", Mock); +vi.mock("hyperformula"); + +async function changeValue({ + value, + name, + core, +}: { + value: number | string; + name: string; + core: Core; +}) { + await core.requestAction({ + actionName: "changeValue", + componentName: name, + args: { value }, + event: null, + }); +} + +describe("Slider tag tests", async () => { + it("two number slider", async () => { + let core = await createTestCore({ + doenetML: ` + + 1 + 2 + +

Value: $s

+ `, + }); + + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/s"].stateValues.type).eqls("number"); + expect(stateVariables["/s"].stateValues.items).eqls([1, 2]); + expect(stateVariables["/s"].stateValues.firstItem).eqls(1); + expect(stateVariables["/s"].stateValues.lastItem).eqls(2); + expect(stateVariables["/s"].stateValues.index).eq(0); + expect(stateVariables["/s"].stateValues.value).eq(1); + + // less than halfway, stays at 1 + await changeValue({ value: 1.49, name: "/s", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/s"].stateValues.index).eq(0); + expect(stateVariables["/s"].stateValues.value).eq(1); + + // more than halfway, goes to 2 + await changeValue({ value: 1.51, name: "/s", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/s"].stateValues.index).eq(1); + expect(stateVariables["/s"].stateValues.value).eq(2); + + // below one, goes to 1 + await changeValue({ value: -5, name: "/s", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/s"].stateValues.index).eq(0); + expect(stateVariables["/s"].stateValues.value).eq(1); + + // above 2, goes to 2 + await changeValue({ value: 9, name: "/s", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/s"].stateValues.index).eq(1); + expect(stateVariables["/s"].stateValues.value).eq(2); + }); + + it("no arguments, default to number slider from 0 to 10", async () => { + let core = await createTestCore({ + doenetML: ` + +

Value: $s

+

Change value:

+ + `, + }); + + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/s"].stateValues.type).eqls("number"); + expect(stateVariables["/s"].stateValues.items).eqls([]); + expect(stateVariables["/s"].stateValues.from).eq(0); + expect(stateVariables["/s"].stateValues.to).eq(10); + expect(stateVariables["/s"].stateValues.step).eq(1); + expect(stateVariables["/s"].stateValues.firstItem).eqls(0); + expect(stateVariables["/s"].stateValues.lastItem).eqls(10); + expect(stateVariables["/s"].stateValues.index).eq(0); + expect(stateVariables["/s"].stateValues.value).eq(0); + expect(stateVariables["/sv"].stateValues.value).eq(0); + + // change to 1 + await changeValue({ value: 1.4, name: "/s", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/s"].stateValues.index).eq(1); + expect(stateVariables["/s"].stateValues.value).eq(1); + expect(stateVariables["/sv"].stateValues.value).eq(1); + + // change to 9 + await changeValue({ value: 8.6, name: "/s", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/s"].stateValues.index).eq(9); + expect(stateVariables["/s"].stateValues.value).eq(9); + expect(stateVariables["/sv"].stateValues.value).eq(9); + + // enter 2.5 + await updateMathInputValue({ latex: "2.5", name: "/mi", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/s"].stateValues.index).eq(3); + expect(stateVariables["/s"].stateValues.value).eq(3); + expect(stateVariables["/sv"].stateValues.value).eq(3); + + // enter -103.9 + await updateMathInputValue({ latex: "-103.9", name: "/mi", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/s"].stateValues.index).eq(0); + expect(stateVariables["/s"].stateValues.value).eq(0); + expect(stateVariables["/sv"].stateValues.value).eq(0); + + // enter x, ignored + await updateMathInputValue({ latex: "x", name: "/mi", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/s"].stateValues.index).eq(0); + expect(stateVariables["/s"].stateValues.value).eq(0); + expect(stateVariables["/sv"].stateValues.value).eq(0); + + // set to maximum + await changeValue({ value: 20, name: "/s", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/s"].stateValues.index).eq(10); + expect(stateVariables["/s"].stateValues.value).eq(10); + expect(stateVariables["/sv"].stateValues.value).eq(10); + }); + + it("step 0.1", async () => { + let core = await createTestCore({ + doenetML: ` + + `, + }); + + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/s"].stateValues.type).eqls("number"); + expect(stateVariables["/s"].stateValues.items).eqls([]); + expect(stateVariables["/s"].stateValues.from).eq(0); + expect(stateVariables["/s"].stateValues.to).eq(10); + expect(stateVariables["/s"].stateValues.step).eq(0.1); + expect(stateVariables["/s"].stateValues.firstItem).eqls(0); + expect(stateVariables["/s"].stateValues.lastItem).eqls(10); + expect(stateVariables["/s"].stateValues.index).eq(0); + expect(stateVariables["/s"].stateValues.value).eq(0); + + // change to 1.4 + await changeValue({ value: 1.44, name: "/s", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/s"].stateValues.index).eq(14); + expect(stateVariables["/s"].stateValues.value).closeTo(1.4, 1e-12); + + // change to 9.3 + await changeValue({ value: 9.26, name: "/s", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/s"].stateValues.index).eq(93); + expect(stateVariables["/s"].stateValues.value).closeTo(9.3, 1e-12); + }); + + it("from 100 to 200", async () => { + let core = await createTestCore({ + doenetML: ` + + `, + }); + + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/s"].stateValues.type).eqls("number"); + expect(stateVariables["/s"].stateValues.items).eqls([]); + expect(stateVariables["/s"].stateValues.from).eq(100); + expect(stateVariables["/s"].stateValues.to).eq(200); + expect(stateVariables["/s"].stateValues.step).eq(1); + expect(stateVariables["/s"].stateValues.firstItem).eqls(100); + expect(stateVariables["/s"].stateValues.lastItem).eqls(200); + expect(stateVariables["/s"].stateValues.index).eq(0); + expect(stateVariables["/s"].stateValues.value).eq(100); + + // change to 137 + await changeValue({ value: 136.8, name: "/s", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/s"].stateValues.index).eq(37); + expect(stateVariables["/s"].stateValues.value).eq(137); + + // change to 199 + await changeValue({ value: 199.1, name: "/s", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/s"].stateValues.index).eq(99); + expect(stateVariables["/s"].stateValues.value).eq(199); + }); + + it("initial value", async () => { + let core = await createTestCore({ + doenetML: ` + + `, + }); + + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/s"].stateValues.type).eqls("number"); + expect(stateVariables["/s"].stateValues.items).eqls([]); + expect(stateVariables["/s"].stateValues.from).eq(0); + expect(stateVariables["/s"].stateValues.to).eq(10); + expect(stateVariables["/s"].stateValues.step).eq(1); + expect(stateVariables["/s"].stateValues.firstItem).eqls(0); + expect(stateVariables["/s"].stateValues.lastItem).eqls(10); + expect(stateVariables["/s"].stateValues.index).eq(7); + expect(stateVariables["/s"].stateValues.value).eq(7); + + // change to 3 + await changeValue({ value: 2.8, name: "/s", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/s"].stateValues.index).eq(3); + expect(stateVariables["/s"].stateValues.value).eq(3); + }); + + it("bind value to", async () => { + let core = await createTestCore({ + doenetML: ` + + + + `, + }); + + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/s"].stateValues.type).eqls("number"); + expect(stateVariables["/s"].stateValues.items).eqls([]); + expect(stateVariables["/s"].stateValues.from).eq(0); + expect(stateVariables["/s"].stateValues.to).eq(10); + expect(stateVariables["/s"].stateValues.step).eq(1); + expect(stateVariables["/s"].stateValues.firstItem).eqls(0); + expect(stateVariables["/s"].stateValues.lastItem).eqls(10); + expect(stateVariables["/s"].stateValues.index).eq(0); + expect(stateVariables["/s"].stateValues.value).eq(0); + expect(stateVariables["/mi0"].stateValues.value.tree).eq("\uff3f"); + expect(stateVariables["/mi"].stateValues.value.tree).eq(0); + + // change to 3 + await changeValue({ value: 2.8, name: "/s", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/s"].stateValues.index).eq(3); + expect(stateVariables["/s"].stateValues.value).eq(3); + expect(stateVariables["/mi0"].stateValues.value.tree).eq(3); + expect(stateVariables["/mi"].stateValues.value.tree).eq(3); + + // enter 4.2 in post math input + await updateMathInputValue({ latex: "4.2", name: "/mi", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/s"].stateValues.index).eq(4); + expect(stateVariables["/s"].stateValues.value).eq(4); + expect(stateVariables["/mi0"].stateValues.value.tree).eq(4); + expect(stateVariables["/mi"].stateValues.value.tree).eq(4); + + // enter 8.7 in pre math input + await updateMathInputValue({ latex: "8.7", name: "/mi0", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/s"].stateValues.index).eq(9); + expect(stateVariables["/s"].stateValues.value).eq(9); + expect(stateVariables["/mi0"].stateValues.value.tree).eq(8.7); + expect(stateVariables["/mi"].stateValues.value.tree).eq(9); + + // enter x in pre math input + await updateMathInputValue({ latex: "x", name: "/mi0", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/s"].stateValues.index).eq(0); + expect(stateVariables["/s"].stateValues.value).eq(0); + expect(stateVariables["/mi0"].stateValues.value.tree).eq("x"); + expect(stateVariables["/mi"].stateValues.value.tree).eq(0); + + // change to 5 + await changeValue({ value: 5.3, name: "/s", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/s"].stateValues.index).eq(5); + expect(stateVariables["/s"].stateValues.value).eq(5); + expect(stateVariables["/mi0"].stateValues.value.tree).eq(5); + expect(stateVariables["/mi"].stateValues.value.tree).eq(5); + + // enter y in post math input, ignored + await updateMathInputValue({ latex: "y", name: "/mi", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/s"].stateValues.index).eq(5); + expect(stateVariables["/s"].stateValues.value).eq(5); + expect(stateVariables["/mi0"].stateValues.value.tree).eq(5); + expect(stateVariables["/mi"].stateValues.value.tree).eq(5); + + // enter 9999 in pre math input + await updateMathInputValue({ latex: "999", name: "/mi0", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/s"].stateValues.index).eq(10); + expect(stateVariables["/s"].stateValues.value).eq(10); + expect(stateVariables["/mi0"].stateValues.value.tree).eq(999); + expect(stateVariables["/mi"].stateValues.value.tree).eq(10); + }); + + it("label with math", async () => { + let core = await createTestCore({ + doenetML: ` + + `, + }); + + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/s"].stateValues.value).eq(0); + expect(stateVariables["/s"].stateValues.label).eq("Hello \\(x^2\\)"); + }); + + it("label is name", async () => { + let core = await createTestCore({ + doenetML: ` + + `, + }); + + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/mySlider"].stateValues.value).eq(0); + expect(stateVariables["/mySlider"].stateValues.label).eq("my slider"); + }); +}); diff --git a/packages/doenetml-worker/src/test/tagSpecific/solution.test.ts b/packages/doenetml-worker/src/test/tagSpecific/solution.test.ts new file mode 100644 index 000000000..bccd5d392 --- /dev/null +++ b/packages/doenetml-worker/src/test/tagSpecific/solution.test.ts @@ -0,0 +1,90 @@ +import { describe, expect, it, vi } from "vitest"; +import { createTestCore, returnAllStateVariables } from "../utils/test-core"; +import Core from "../../Core"; + +const Mock = vi.fn(); +vi.stubGlobal("postMessage", Mock); +vi.mock("hyperformula"); + +async function revealSolution({ name, core }: { name: string; core: Core }) { + await core.requestAction({ + actionName: "revealSolution", + componentName: name, + args: {}, + event: null, + }); +} +async function closeSolution({ name, core }: { name: string; core: Core }) { + await core.requestAction({ + actionName: "closeSolution", + componentName: name, + args: {}, + event: null, + }); +} + +describe("Solution tag tests", async () => { + it("solution isn't created before opening", async () => { + let core = await createTestCore({ + doenetML: ` + +

This is the text of the solution.

+
+ `, + }); + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/solutionText"]).be.undefined; + + await revealSolution({ name: "/sol", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/solutionText"].stateValues.text).eq( + "This is the text of the solution.", + ); + }); + + it("Can open solution in read only mode", async () => { + let core = await createTestCore({ + doenetML: ` + + Hello +

Content

+
+ +

+ `, + flags: { readOnly: true }, + }); + + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/title"]).be.undefined; + expect(stateVariables["/p"]).be.undefined; + expect(stateVariables["/ti"].stateValues.disabled).eq(true); + + await revealSolution({ name: "/solution1", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/title"].stateValues.text).eq("Hello"); + expect(stateVariables["/p"].stateValues.text).eq("Content"); + }); + + it("empty solution", async () => { + // an empty solution was creating an infinite loop + // as the zero replacements were always treated as uninitialized + let core = await createTestCore({ + doenetML: ` + + + `, + }); + + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/solOpen"].stateValues.value).eq(false); + + await revealSolution({ name: "/sol", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/solOpen"].stateValues.value).eq(true); + + await closeSolution({ name: "/sol", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/solOpen"].stateValues.value).eq(false); + }); +}); diff --git a/packages/doenetml-worker/src/test/tagSpecific/solveequations.test.ts b/packages/doenetml-worker/src/test/tagSpecific/solveequations.test.ts new file mode 100644 index 000000000..41961735e --- /dev/null +++ b/packages/doenetml-worker/src/test/tagSpecific/solveequations.test.ts @@ -0,0 +1,308 @@ +import { describe, expect, it, vi } from "vitest"; +import { createTestCore, returnAllStateVariables } from "../utils/test-core"; +import { updateMathInputValue } from "../utils/actions"; +import me from "math-expressions"; +import Core from "../../Core"; + +const Mock = vi.fn(); +vi.stubGlobal("postMessage", Mock); +vi.mock("hyperformula"); + +async function check_solutions(core: Core, solutions: number[]) { + const numSolutions = solutions.length; + const stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/solve"].stateValues.numSolutions).eq(numSolutions); + expect(stateVariables["/solve"].stateValues.solutions.length).eq( + numSolutions, + ); + for (let i = 0; i < numSolutions; i++) { + expect(stateVariables["/solve"].stateValues.solutions[i].tree).closeTo( + solutions[i], + 1e-5, + ); + } + expect(stateVariables["/num"].stateValues.value).eq(numSolutions); + expect(stateVariables["/sols"].stateValues.text).eq( + `Solutions: ${solutions.map((x) => me.round_numbers_to_precision_plus_decimals(x, 5)).join(", ")}`, + ); +} + +describe("SolveEquations tag tests", async () => { + it("solve single equation", async () => { + let core = await createTestCore({ + doenetML: ` +

variable:

+

equation:

+ $equation +

Number of solutions: $solve.numSolutions{assignNames="num"}

+

Solutions: $solve.solutions{displayDigits="5"}

+ `, + }); + + await check_solutions(core, [-1, 1]); + + await updateMathInputValue({ + latex: "\\exp(-a)a^2 = e^{-a}a", + name: "/equation", + core, + }); + await check_solutions(core, []); + + await updateMathInputValue({ latex: "a", name: "/var", core }); + await check_solutions(core, [0, 1]); + + await updateMathInputValue({ latex: "x_1", name: "/var", core }); + await check_solutions(core, []); + + await updateMathInputValue({ + latex: "x_1 - 0.1\\exp(x_1)=0", + name: "/equation", + core, + }); + await check_solutions(core, [0.111833, 3.57715]); + + await updateMathInputValue({ + latex: "ab=0", + name: "/equation", + core, + }); + await check_solutions(core, []); + + await updateMathInputValue({ latex: "b", name: "/var", core }); + await check_solutions(core, [0]); + + await updateMathInputValue({ + latex: "\\sin(10b) = b^3", + name: "/equation", + core, + }); + + await check_solutions( + core, + [-0.870457, -0.657084, -0.311147, 0, 0.311147, 0.657084, 0.870457], + ); + + await updateMathInputValue({ + latex: "b^2+0.1b=0", + name: "/equation", + core, + }); + await check_solutions(core, [-0.1, 0]); + }); + + it("solve single equation, minVar and maxVar", async () => { + let core = await createTestCore({ + doenetML: ` +

variable:

+

minVar:

+

maxVar:

+

equation:

+ $equation +

Number of solutions: $solve.numSolutions{assignNames="num"}

+

Solutions: $solve.solutions{displayDigits="5" displaySmallAsZero="10^(-9)"}

+ `, + }); + + await check_solutions(core, []); + + await updateMathInputValue({ + latex: "x^2-a=0", + name: "/equation", + core, + }); + await check_solutions(core, []); + + await updateMathInputValue({ latex: "x_1", name: "/var", core }); + await check_solutions(core, []); + + await updateMathInputValue({ + latex: "x_1 - 0.1\\exp(x_1)=0", + name: "/equation", + core, + }); + await check_solutions(core, [0.111833]); + + await updateMathInputValue({ latex: "100", name: "/maxVar", core }); + await check_solutions(core, [0.111833, 3.57715]); + + await updateMathInputValue({ latex: "1", name: "/minVar", core }); + await check_solutions(core, [3.57715]); + + await updateMathInputValue({ + latex: "ab=0", + name: "/equation", + core, + }); + await updateMathInputValue({ latex: "b", name: "/var", core }); + await check_solutions(core, []); + + await updateMathInputValue({ latex: "0", name: "/minVar", core }); + await check_solutions(core, [0]); + + await updateMathInputValue({ latex: "10", name: "/maxVar", core }); + + await updateMathInputValue({ + latex: "\\sin(10b) = b^3", + name: "/equation", + core, + }); + await check_solutions(core, [0, 0.311147, 0.657084, 0.870457]); + await updateMathInputValue({ latex: "-10", name: "/minVar", core }); + await check_solutions( + core, + [-0.870457, -0.657084, -0.311147, 0, 0.311147, 0.657084, 0.870457], + ); + + await updateMathInputValue({ + latex: "\\sin(\\pi b) = 0", + name: "/equation", + core, + }); + await updateMathInputValue({ latex: "-10.1", name: "/minVar", core }); + await updateMathInputValue({ latex: "10.1", name: "/maxVar", core }); + await check_solutions( + core, + [...Array(21).keys()].map((i) => i - 10), + ); + + await updateMathInputValue({ + latex: "b^2-0.001b = 0", + name: "/equation", + core, + }); + // TODO: maybe shouldn't hard code this exact answer, + // as the real solution should be 0.001. + await check_solutions(core, [0, 0.00099997]); + + await updateMathInputValue({ + latex: "(b+0.03)(b+0.0301) = 0", + name: "/equation", + core, + }); + await check_solutions(core, [-0.0301, -0.03]); + + await updateMathInputValue({ + latex: "(b+0.03)(b+0.0301) = -0.1", + name: "/equation", + core, + }); + await check_solutions(core, []); + + await updateMathInputValue({ + latex: "43.241(b+4.52352)(b+4.52365)(b-8.58230)(b-8.58263) = 0", + name: "/equation", + core, + }); + await check_solutions(core, [-4.52365, -4.52352, 8.5823, 8.58263]); + + await updateMathInputValue({ + latex: "\\exp(43.241(b+4.52352)(b+4.52365)(b-8.58230)(b-8.58263)) = 1", + name: "/equation", + core, + }); + await check_solutions(core, [-4.52365, -4.52352, 8.5823, 8.58263]); + + await updateMathInputValue({ + latex: "\\cos(\\pi b) + 1=0", + name: "/equation", + core, + }); + await check_solutions( + core, + [...Array(10).keys()].map((i) => 2 * i - 9), + ); + + await updateMathInputValue({ + latex: "\\cos(\\pi b)=1", + name: "/equation", + core, + }); + await check_solutions( + core, + [...Array(11).keys()].map((i) => 2 * i - 10), + ); + + await updateMathInputValue({ latex: "-10", name: "/minVar", core }); + await check_solutions( + core, + [...Array(11).keys()].map((i) => 2 * i - 10), + ); + + await updateMathInputValue({ latex: "10", name: "/maxVar", core }); + await check_solutions( + core, + [...Array(11).keys()].map((i) => 2 * i - 10), + ); + + await updateMathInputValue({ + latex: "-10.0001", + name: "/minVar", + core, + }); + await check_solutions( + core, + [...Array(11).keys()].map((i) => 2 * i - 10), + ); + + await updateMathInputValue({ + latex: "\\sqrt{b-\\pi}=0", + name: "/equation", + core, + }); + await check_solutions(core, [Math.PI]); + + await updateMathInputValue({ + latex: "\\sqrt{b^2-\\pi^2}=0", + name: "/equation", + core, + }); + await check_solutions(core, [-Math.PI, Math.PI]); + + await updateMathInputValue({ + latex: "\\sqrt{\\pi^2-b^2}=0", + name: "/equation", + core, + }); + await check_solutions(core, [-Math.PI, Math.PI]); + + await updateMathInputValue({ + latex: "10000000000\\sqrt{\\pi^2-b^2}=0", + name: "/equation", + core, + }); + await check_solutions(core, [-Math.PI, Math.PI]); + + await updateMathInputValue({ + latex: "0.00000000000000000001\\sqrt{\\pi^2-b^2}=0", + name: "/equation", + core, + }); + await check_solutions(core, [-Math.PI, Math.PI]); + }); + + it("handle bad equation", async () => { + let core = await createTestCore({ + doenetML: ` + x_(2t)=1 +

Number of solutions: $solve.numSolutions{assignNames="num"}

+

Solutions: $solve.solutions

+ `, + }); + + await check_solutions(core, []); + + let errorWarnings = core.errorWarnings; + + expect(errorWarnings.errors.length).eq(0); + expect(errorWarnings.warnings.length).eq(1); + + expect(errorWarnings.warnings[0].message).contain( + `Cannot solve equation`, + ); + expect(errorWarnings.warnings[0].level).eq(1); + expect(errorWarnings.warnings[0].doenetMLrange.lineBegin).eq(2); + expect(errorWarnings.warnings[0].doenetMLrange.charBegin).eq(3); + expect(errorWarnings.warnings[0].doenetMLrange.lineEnd).eq(2); + expect(errorWarnings.warnings[0].doenetMLrange.charEnd).eq(56); + }); +}); diff --git a/packages/doenetml-worker/src/test/tagSpecific/sort.test.ts b/packages/doenetml-worker/src/test/tagSpecific/sort.test.ts index ba7b59d24..0a92c8be3 100644 --- a/packages/doenetml-worker/src/test/tagSpecific/sort.test.ts +++ b/packages/doenetml-worker/src/test/tagSpecific/sort.test.ts @@ -1,9 +1,10 @@ import { describe, expect, it, vi } from "vitest"; import { createTestCore, returnAllStateVariables } from "../utils/test-core"; -import { updateMathInputValue } from "../utils/actions"; +import { movePoint, updateMathInputValue } from "../utils/actions"; const Mock = vi.fn(); vi.stubGlobal("postMessage", Mock); +vi.mock("hyperformula"); describe("Sort tag tests", async () => { async function test_sort({ @@ -91,7 +92,7 @@ describe("Sort tag tests", async () => { await test_sort({ core, sorted_result }); // change first value - await updateMathInputValue({ latex: "-5", componentName: "/m1", core }); + await updateMathInputValue({ latex: "-5", name: "/m1", core }); sorted_result = ["-∞", "-5", "-π", "5/6", "70", "∞"]; await test_sort({ core, sorted_result }); @@ -99,7 +100,7 @@ describe("Sort tag tests", async () => { // change second value await updateMathInputValue({ latex: "e^5", - componentName: "/m2", + name: "/m2", core, }); @@ -109,7 +110,7 @@ describe("Sort tag tests", async () => { // change third value await updateMathInputValue({ latex: "-100", - componentName: "/m3", + name: "/m3", core, }); @@ -119,7 +120,7 @@ describe("Sort tag tests", async () => { // change fourth value await updateMathInputValue({ latex: "0", - componentName: "/m4", + name: "/m4", core, }); @@ -199,12 +200,7 @@ describe("Sort tag tests", async () => { await test_sort({ core, sorted_result }); - await core.requestAction({ - actionName: "movePoint", - componentName: "/A", - args: { x: -8, y: 9 }, - event: null, - }); + await movePoint({ name: "/A", x: -8, y: 9, core }); sorted_result = [ "( -8, 9 )", @@ -216,12 +212,7 @@ describe("Sort tag tests", async () => { await test_sort({ core, sorted_result }); - await core.requestAction({ - actionName: "movePoint", - componentName: "/B", - args: { x: 8, y: -3 }, - event: null, - }); + await movePoint({ name: "/B", x: 8, y: -3, core }); sorted_result = [ "( -8, 9 )", @@ -233,12 +224,7 @@ describe("Sort tag tests", async () => { await test_sort({ core, sorted_result }); - await core.requestAction({ - actionName: "movePoint", - componentName: "/C", - args: { x: 4, y: 5 }, - event: null, - }); + await movePoint({ name: "/C", x: 4, y: 5, core }); sorted_result = [ "( -8, 9 )", @@ -250,12 +236,7 @@ describe("Sort tag tests", async () => { await test_sort({ core, sorted_result }); - await core.requestAction({ - actionName: "movePoint", - componentName: "/D", - args: { x: -9, y: 0 }, - event: null, - }); + await movePoint({ name: "/D", x: -9, y: 0, core }); sorted_result = [ "( -9, 0 )", @@ -267,12 +248,7 @@ describe("Sort tag tests", async () => { await test_sort({ core, sorted_result }); - await core.requestAction({ - actionName: "movePoint", - componentName: "/E", - args: { x: -2, y: -1 }, - event: null, - }); + await movePoint({ name: "/E", x: -2, y: -1, core }); sorted_result = [ "( -9, 0 )", @@ -509,7 +485,7 @@ describe("Sort tag tests", async () => { await updateMathInputValue({ latex: "(a,b,c,d)", - componentName: "/cs", + name: "/cs", core, }); @@ -524,7 +500,7 @@ describe("Sort tag tests", async () => { await updateMathInputValue({ latex: "(3,4,5)", - componentName: "/cs", + name: "/cs", core, }); diff --git a/packages/doenetml-worker/src/test/tagSpecific/spreadsheet.test.ts b/packages/doenetml-worker/src/test/tagSpecific/spreadsheet.test.ts new file mode 100644 index 000000000..7255db5f3 --- /dev/null +++ b/packages/doenetml-worker/src/test/tagSpecific/spreadsheet.test.ts @@ -0,0 +1,1817 @@ +import { describe, expect, it, vi } from "vitest"; +import { createTestCore, returnAllStateVariables } from "../utils/test-core"; +import { updateMathInputValue } from "../utils/actions"; +import Core from "../../Core"; +import me from "math-expressions"; + +const Mock = vi.fn(); +vi.stubGlobal("postMessage", Mock); +vi.mock("hyperformula"); + +async function changeSpreadsheetText({ + name = "/spreadsheet1", + row, + column, + text = "", + prevText = "", + core, +}: { + name?: string; + row: number; + column: number; + text?: string; + prevText?: string; + core: Core; +}) { + await core.requestAction({ + actionName: "onChange", + componentName: name, + args: { changes: [[row - 1, column - 1, prevText, text]] }, + event: null, + }); +} + +describe("Spreadsheet tag tests", async () => { + it("empty spreadsheet", async () => { + let core = await createTestCore({ + doenetML: ` + + `, + }); + + // check have spreadsheet cells + let stateVariables = await returnAllStateVariables(core); + expect( + Array.isArray(stateVariables["/spreadsheet1"].stateValues.cells), + ).eq(true); + + // enter text in B3 + await changeSpreadsheetText({ row: 3, column: 2, text: "hello", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/spreadsheet1"].stateValues.cells[2][1]).eq( + "hello", + ); + + // delete text in B3 + await changeSpreadsheetText({ row: 3, column: 2, text: "", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/spreadsheet1"].stateValues.cells[2][1]).eq(""); + + // enter text in A1 + await changeSpreadsheetText({ row: 1, column: 1, text: "first", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/spreadsheet1"].stateValues.cells[0][0]).eq( + "first", + ); + expect(stateVariables["/spreadsheet1"].stateValues.cells[2][1]).eq(""); + + // enter text in D2 + await changeSpreadsheetText({ row: 2, column: 4, text: "right", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/spreadsheet1"].stateValues.cells[0][0]).eq( + "first", + ); + expect(stateVariables["/spreadsheet1"].stateValues.cells[2][1]).eq(""); + expect(stateVariables["/spreadsheet1"].stateValues.cells[1][3]).eq( + "right", + ); + }); + + it("spreadsheet with cell children", async () => { + let core = await createTestCore({ + doenetML: ` + + first + hello + bye + before + above + + `, + }); + + async function check_items(A1: string, C1: string, C3: string) { + const stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/spreadsheet1"].stateValues.cells[0][0]).eq( + A1, + ); + expect(stateVariables["/spreadsheet1"].stateValues.cells[2][2]).eq( + C3, + ); + expect(stateVariables["/spreadsheet1"].stateValues.cells[2][3]).eq( + "bye", + ); + expect(stateVariables["/spreadsheet1"].stateValues.cells[2][1]).eq( + "before", + ); + expect(stateVariables["/spreadsheet1"].stateValues.cells[1][1]).eq( + "above", + ); + expect(stateVariables["/spreadsheet1"].stateValues.cells[0][2]).eq( + C1, + ); + expect(stateVariables["/cell1"].stateValues.text).eq(A1); + expect(stateVariables["/cell2"].stateValues.text).eq(C3); + expect(stateVariables["/cell3"].stateValues.text).eq("bye"); + expect(stateVariables["/cell4"].stateValues.text).eq("before"); + expect(stateVariables["/cell5"].stateValues.text).eq("above"); + } + + let A1 = "first"; + let C1 = ""; + let C3 = "hello"; + + await check_items(A1, C1, C3); + + // overwrite text in A1 + await changeSpreadsheetText({ + row: 1, + column: 1, + prevText: A1, + text: "new", + core, + }); + A1 = "new"; + await check_items(A1, C1, C3); + + // enter text in new cell C1 + C1 = "third"; + await changeSpreadsheetText({ row: 1, column: 3, text: C1, core }); + await check_items(A1, C1, C3); + + // delete text in C3 + await changeSpreadsheetText({ + row: 3, + column: 3, + prevText: C3, + text: "", + core, + }); + C3 = ""; + await check_items(A1, C1, C3); + }); + + it("copy individual cells into new spreadsheet", async () => { + let core = await createTestCore({ + doenetML: ` + + first + hello + bye + last + mid + + + + $c4{name="c4a"} + + $c3{name="c3a"} + + + + + + + + + `, + }); + + let cellLocations = { + 1: [ + [1, 1], + [4, 2], + ], + 2: [ + [3, 3], + [3, 1], + ], + 3: [ + [3, 4], + [3, 2], + ], + 4: [ + [4, 4], + [4, 4], + ], + 5: [ + [1, 2], + [2, 2], + ], + 6: [ + [2, 2], + [1, 3], + ], + }; + + let cellNames = { + 1: ["/c1", "/c1a"], + 2: ["/c2", "/c2a"], + 3: ["/c3", "/c3a"], + 4: ["/c4", "/c4a"], + 5: ["/c5", "/c5a"], + }; + + let spreadsheetNames = { + 0: ["/ss1", "/ss1a", "/ss1b"], + 1: ["/ss2", "/ss2a", "/ss2b"], + }; + + async function check_items(cellValues: Record) { + const stateVariables = await returnAllStateVariables(core); + for (let cellNum in cellNames) { + for (let ind in cellNames[cellNum]) { + expect( + stateVariables[cellNames[cellNum][ind]].stateValues + .text, + ).eq(cellValues[cellNum]); + } + } + + for (let ssNum in spreadsheetNames) { + for (let ssInd in spreadsheetNames[ssNum]) { + let ssName = spreadsheetNames[ssNum][ssInd]; + for (let cellNum in cellLocations) { + let cLoc = cellLocations[cellNum][ssNum]; + let effectiveNum = + cellNum === "6" && ssNum === "1" ? "6b" : cellNum; + expect( + stateVariables[ssName].stateValues.cells[ + cLoc[0] - 1 + ][cLoc[1] - 1], + ).eq(cellValues[effectiveNum]); + } + } + } + } + + let cellValues: Record = { + 1: "first", + 2: "hello", + 3: "bye", + 4: "last", + 5: "mid", + 6: "", + "6b": "", + }; + + await check_items(cellValues); + + let allCellValues = { + 1: ["apple", "red", "up", "soft", "happy", "monday"], + 2: ["banana", "purple", "down", "hard", "sad", "tuesday"], + 3: ["grape", "black", "left", "smooth", "serious", "wednesday"], + 4: [ + "orange", + "green", + "right", + "prickly", + "determined", + "thursday", + ], + 5: ["melon", "yellow", "middle", "rough", "impulsive", "friday"], + 6: ["pear", "brown", "back", "dimpled", "passive", "saturday"], + }; + + let valueInd = -1; + for (let ssNumChange in spreadsheetNames) { + for (let ssIndChange in spreadsheetNames[ssNumChange]) { + let ssNameChange = spreadsheetNames[ssNumChange][ssIndChange]; + valueInd++; + + for (let cellNum in allCellValues) { + let cLoc = cellLocations[cellNum][ssNumChange]; + let effectiveNum = + cellNum === "6" && ssNumChange === "1" ? "6b" : cellNum; + await changeSpreadsheetText({ + name: ssNameChange, + row: cLoc[0], + column: cLoc[1], + prevText: cellValues[effectiveNum], + text: allCellValues[cellNum][valueInd], + core, + }); + cellValues[effectiveNum] = allCellValues[cellNum][valueInd]; + } + + await check_items(cellValues); + } + } + }); + + it("copy spreadsheet cells into new spreadsheet", async () => { + let core = await createTestCore({ + doenetML: ` + + first + hello + bye + last + mid + + + + + + + + + + + + + + + `, + }); + + let cellLocations = { + 1: [ + [1, 1], + [4, 2], + ], + 2: [ + [3, 3], + [3, 1], + ], + 3: [ + [3, 4], + [3, 2], + ], + 4: [ + [4, 4], + [4, 4], + ], + 5: [ + [1, 2], + [2, 2], + ], + 6: [ + [2, 2], + [1, 3], + ], + }; + + let cellNames = { + 1: ["/c1", "/c1a"], + 2: ["/c2", "/c2a"], + 3: ["/c3", "/c3a"], + 4: ["/c4", "/c4a"], + 5: ["/c5", "/c5a"], + }; + + let spreadsheetNames = { + 0: ["/ss1", "/ss1a", "/ss1b"], + 1: ["/ss2", "/ss2a", "/ss2b"], + }; + + async function check_items(cellValues: Record) { + const stateVariables = await returnAllStateVariables(core); + for (let cellNum in cellNames) { + for (let ind in cellNames[cellNum]) { + expect( + stateVariables[cellNames[cellNum][ind]].stateValues + .text, + ).eq(cellValues[cellNum]); + } + } + + for (let ssNum in spreadsheetNames) { + for (let ssInd in spreadsheetNames[ssNum]) { + let ssName = spreadsheetNames[ssNum][ssInd]; + for (let cellNum in cellLocations) { + let cLoc = cellLocations[cellNum][ssNum]; + let effectiveNum = + cellNum === "6" && ssNum === "1" ? "6b" : cellNum; + expect( + stateVariables[ssName].stateValues.cells[ + cLoc[0] - 1 + ][cLoc[1] - 1], + ).eq(cellValues[effectiveNum]); + } + } + } + } + + let cellValues: Record = { + 1: "first", + 2: "hello", + 3: "bye", + 4: "last", + 5: "mid", + 6: "", + "6b": "", + }; + + await check_items(cellValues); + + let allCellValues = { + 1: ["apple", "red", "up", "soft", "happy", "monday"], + 2: ["banana", "purple", "down", "hard", "sad", "tuesday"], + 3: ["grape", "black", "left", "smooth", "serious", "wednesday"], + 4: [ + "orange", + "green", + "right", + "prickly", + "determined", + "thursday", + ], + 5: ["melon", "yellow", "middle", "rough", "impulsive", "friday"], + 6: ["pear", "brown", "back", "dimpled", "passive", "saturday"], + }; + + let valueInd = -1; + for (let ssNumChange in spreadsheetNames) { + for (let ssIndChange in spreadsheetNames[ssNumChange]) { + let ssNameChange = spreadsheetNames[ssNumChange][ssIndChange]; + valueInd++; + + for (let cellNum in allCellValues) { + let cLoc = cellLocations[cellNum][ssNumChange]; + let effectiveNum = + cellNum === "6" && ssNumChange === "1" ? "6b" : cellNum; + await changeSpreadsheetText({ + name: ssNameChange, + row: cLoc[0], + column: cLoc[1], + prevText: cellValues[effectiveNum], + text: allCellValues[cellNum][valueInd], + core, + }); + cellValues[effectiveNum] = allCellValues[cellNum][valueInd]; + } + + await check_items(cellValues); + } + } + }); + + it("build spreadsheet from cells and rows", async () => { + let core = await createTestCore({ + doenetML: ` + + A1B1D1 + B2C2 + A5F5 + C6D6 + A7 + + `, + }); + + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/spreadsheet1"].stateValues.numRows).eq(7); + expect(stateVariables["/spreadsheet1"].stateValues.numColumns).eq(6); + expect(stateVariables["/spreadsheet1"].stateValues.cells[0][0]).eq( + "A1", + ); + expect(stateVariables["/spreadsheet1"].stateValues.cells[0][1]).eq( + "B1", + ); + expect(stateVariables["/spreadsheet1"].stateValues.cells[0][3]).eq( + "D1", + ); + expect(stateVariables["/spreadsheet1"].stateValues.cells[1][1]).eq( + "B2", + ); + expect(stateVariables["/spreadsheet1"].stateValues.cells[1][2]).eq( + "C2", + ); + expect(stateVariables["/spreadsheet1"].stateValues.cells[4][0]).eq( + "A5", + ); + expect(stateVariables["/spreadsheet1"].stateValues.cells[4][5]).eq( + "F5", + ); + expect(stateVariables["/spreadsheet1"].stateValues.cells[5][2]).eq( + "C6", + ); + expect(stateVariables["/spreadsheet1"].stateValues.cells[5][3]).eq( + "D6", + ); + expect(stateVariables["/spreadsheet1"].stateValues.cells[6][0]).eq( + "A7", + ); + expect(stateVariables["/cell1"].stateValues.text).eq("A1"); + expect(stateVariables["/cell2"].stateValues.text).eq("B1"); + expect(stateVariables["/cell3"].stateValues.text).eq("D1"); + expect(stateVariables["/cell4"].stateValues.text).eq("B2"); + expect(stateVariables["/cell5"].stateValues.text).eq("C2"); + expect(stateVariables["/cell6"].stateValues.text).eq("A5"); + expect(stateVariables["/cell7"].stateValues.text).eq("F5"); + expect(stateVariables["/cell8"].stateValues.text).eq("C6"); + expect(stateVariables["/cell9"].stateValues.text).eq("D6"); + expect(stateVariables["/cell10"].stateValues.text).eq("A7"); + + for (let ind = 1; ind <= 7; ind++) { + await changeSpreadsheetText({ + row: ind, + column: 1, + text: `row${ind}`, + core, + }); + } + + stateVariables = await returnAllStateVariables(core); + for (let ind = 1; ind <= 7; ind++) { + expect( + stateVariables["/spreadsheet1"].stateValues.cells[ind - 1][0], + ).eq(`row${ind}`); + } + expect(stateVariables["/cell1"].stateValues.text).eq("row1"); + expect(stateVariables["/cell6"].stateValues.text).eq("row5"); + expect(stateVariables["/cell10"].stateValues.text).eq("row7"); + }); + + it("build spreadsheet from cells and columns", async () => { + let core = await createTestCore({ + doenetML: ` + + A1A2A4 + B2B3 + E1E6 + F3F4 + G1 + + `, + }); + + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/spreadsheet1"].stateValues.numRows).eq(6); + expect(stateVariables["/spreadsheet1"].stateValues.numColumns).eq(7); + expect(stateVariables["/spreadsheet1"].stateValues.cells[0][0]).eq( + "A1", + ); + expect(stateVariables["/spreadsheet1"].stateValues.cells[1][0]).eq( + "A2", + ); + expect(stateVariables["/spreadsheet1"].stateValues.cells[3][0]).eq( + "A4", + ); + expect(stateVariables["/spreadsheet1"].stateValues.cells[1][1]).eq( + "B2", + ); + expect(stateVariables["/spreadsheet1"].stateValues.cells[2][1]).eq( + "B3", + ); + expect(stateVariables["/spreadsheet1"].stateValues.cells[0][4]).eq( + "E1", + ); + expect(stateVariables["/spreadsheet1"].stateValues.cells[5][4]).eq( + "E6", + ); + expect(stateVariables["/spreadsheet1"].stateValues.cells[2][5]).eq( + "F3", + ); + expect(stateVariables["/spreadsheet1"].stateValues.cells[3][5]).eq( + "F4", + ); + expect(stateVariables["/spreadsheet1"].stateValues.cells[0][6]).eq( + "G1", + ); + expect(stateVariables["/cell1"].stateValues.text).eq("A1"); + expect(stateVariables["/cell2"].stateValues.text).eq("A2"); + expect(stateVariables["/cell3"].stateValues.text).eq("A4"); + expect(stateVariables["/cell4"].stateValues.text).eq("B2"); + expect(stateVariables["/cell5"].stateValues.text).eq("B3"); + expect(stateVariables["/cell6"].stateValues.text).eq("E1"); + expect(stateVariables["/cell7"].stateValues.text).eq("E6"); + expect(stateVariables["/cell8"].stateValues.text).eq("F3"); + expect(stateVariables["/cell9"].stateValues.text).eq("F4"); + expect(stateVariables["/cell10"].stateValues.text).eq("G1"); + + for (let ind = 1; ind <= 7; ind++) { + await changeSpreadsheetText({ + row: 1, + column: ind, + text: `column${ind}`, + core, + }); + } + stateVariables = await returnAllStateVariables(core); + for (let ind = 1; ind <= 7; ind++) { + expect( + stateVariables["/spreadsheet1"].stateValues.cells[0][ind - 1], + ).eq(`column${ind}`); + } + expect(stateVariables["/cell1"].stateValues.text).eq("column1"); + expect(stateVariables["/cell6"].stateValues.text).eq("column5"); + expect(stateVariables["/cell10"].stateValues.text).eq("column7"); + }); + + it("build spreadsheet with cellblocks", async () => { + let core = await createTestCore({ + doenetML: ` + + + C3D3 + E2E3 + + F2 + + G2 + G4 + + + G5H5 + + + A5 + B6 + + C5 + + `, + }); + + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/spreadsheet1"].stateValues.numRows).eq(6); + expect(stateVariables["/spreadsheet1"].stateValues.numColumns).eq(8); + expect(stateVariables["/spreadsheet1"].stateValues.cells[2][2]).eq( + "C3", + ); + expect(stateVariables["/spreadsheet1"].stateValues.cells[2][3]).eq( + "D3", + ); + expect(stateVariables["/spreadsheet1"].stateValues.cells[1][4]).eq( + "E2", + ); + expect(stateVariables["/spreadsheet1"].stateValues.cells[2][4]).eq( + "E3", + ); + expect(stateVariables["/spreadsheet1"].stateValues.cells[1][5]).eq( + "F2", + ); + expect(stateVariables["/spreadsheet1"].stateValues.cells[1][6]).eq( + "G2", + ); + expect(stateVariables["/spreadsheet1"].stateValues.cells[3][6]).eq( + "G4", + ); + expect(stateVariables["/spreadsheet1"].stateValues.cells[4][6]).eq( + "G5", + ); + expect(stateVariables["/spreadsheet1"].stateValues.cells[4][7]).eq( + "H5", + ); + expect(stateVariables["/spreadsheet1"].stateValues.cells[4][0]).eq( + "A5", + ); + expect(stateVariables["/spreadsheet1"].stateValues.cells[5][1]).eq( + "B6", + ); + expect(stateVariables["/spreadsheet1"].stateValues.cells[4][2]).eq( + "C5", + ); + expect(stateVariables["/cell1"].stateValues.text).eq("C3"); + expect(stateVariables["/cell2"].stateValues.text).eq("D3"); + expect(stateVariables["/cell3"].stateValues.text).eq("E2"); + expect(stateVariables["/cell4"].stateValues.text).eq("E3"); + expect(stateVariables["/cell5"].stateValues.text).eq("F2"); + expect(stateVariables["/cell6"].stateValues.text).eq("G2"); + expect(stateVariables["/cell7"].stateValues.text).eq("G4"); + expect(stateVariables["/cell8"].stateValues.text).eq("G5"); + expect(stateVariables["/cell9"].stateValues.text).eq("H5"); + expect(stateVariables["/cell10"].stateValues.text).eq("A5"); + expect(stateVariables["/cell11"].stateValues.text).eq("B6"); + expect(stateVariables["/cell12"].stateValues.text).eq("C5"); + + for (let ind = 1; ind <= 8; ind++) { + await changeSpreadsheetText({ + row: 5, + column: ind, + text: `column${ind}`, + core, + }); + } + + stateVariables = await returnAllStateVariables(core); + for (let ind = 1; ind <= 8; ind++) { + expect( + stateVariables["/spreadsheet1"].stateValues.cells[4][ind - 1], + ).eq(`column${ind}`); + } + expect(stateVariables["/cell8"].stateValues.text).eq("column7"); + expect(stateVariables["/cell9"].stateValues.text).eq("column8"); + expect(stateVariables["/cell10"].stateValues.text).eq("column1"); + expect(stateVariables["/cell12"].stateValues.text).eq("column3"); + }); + + it("copy spreadsheet with cellblocks", async () => { + let core = await createTestCore({ + doenetML: ` + + A1B1C1 + A2A3A4 + + B2C2D2 + B3C3D3 + + B4C4D4 + D1 + + + + + + + + `, + }); + + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/spreadsheet1"].stateValues.numRows).eq(4); + expect(stateVariables["/spreadsheet1"].stateValues.numColumns).eq(4); + expect(stateVariables["/spreadsheet2"].stateValues.numRows).eq(4); + expect(stateVariables["/spreadsheet2"].stateValues.numColumns).eq(4); + for (let row = 1; row <= 4; row++) { + for (let col = 1; col <= 4; col++) { + expect( + stateVariables["/spreadsheet1"].stateValues.cells[row - 1][ + col - 1 + ], + ).eq(`${String.fromCharCode(64 + col)}${row}`); + } + } + for (let row = 3; row <= 4; row++) { + for (let col = 3; col <= 4; col++) { + expect( + stateVariables["/spreadsheet2"].stateValues.cells[row - 3][ + col - 3 + ], + ).eq(`${String.fromCharCode(64 + col)}${row}`); + } + } + for (let row = 1; row <= 4; row++) { + for (let col = 1; col <= 2; col++) { + expect( + stateVariables["/spreadsheet2"].stateValues.cells[row - 1][ + col + 1 + ], + ).eq(`${String.fromCharCode(64 + col)}${row}`); + } + } + for (let row = 1; row <= 2; row++) { + for (let col = 3; col <= 4; col++) { + expect( + stateVariables["/spreadsheet2"].stateValues.cells[row + 1][ + col - 3 + ], + ).eq(`${String.fromCharCode(64 + col)}${row}`); + } + } + + // enter text into third row of first spreadsheet + for (let ind = 1; ind <= 4; ind++) { + await changeSpreadsheetText({ + name: "/spreadsheet1", + row: 3, + column: ind, + text: `column${ind}`, + core, + }); + } + stateVariables = await returnAllStateVariables(core); + for (let ind = 1; ind <= 4; ind++) { + expect( + stateVariables["/spreadsheet1"].stateValues.cells[2][ind - 1], + ).eq(`column${ind}`); + } + expect(stateVariables["/spreadsheet2"].stateValues.cells[2][2]).eq( + `column1`, + ); + expect(stateVariables["/spreadsheet2"].stateValues.cells[2][3]).eq( + `column2`, + ); + expect(stateVariables["/spreadsheet2"].stateValues.cells[0][0]).eq( + `column3`, + ); + expect(stateVariables["/spreadsheet2"].stateValues.cells[0][1]).eq( + `column4`, + ); + + // enter text into second column of second spreadsheet + for (let ind = 1; ind <= 4; ind++) { + await changeSpreadsheetText({ + name: "/spreadsheet2", + row: ind, + column: 2, + text: `row${ind}`, + core, + }); + } + + stateVariables = await returnAllStateVariables(core); + for (let ind = 1; ind <= 4; ind++) { + expect( + stateVariables["/spreadsheet2"].stateValues.cells[ind - 1][1], + ).eq(`row${ind}`); + } + expect(stateVariables["/spreadsheet1"].stateValues.cells[2][3]).eq( + `row1`, + ); + expect(stateVariables["/spreadsheet1"].stateValues.cells[3][3]).eq( + `row2`, + ); + expect(stateVariables["/spreadsheet1"].stateValues.cells[0][3]).eq( + `row3`, + ); + expect(stateVariables["/spreadsheet1"].stateValues.cells[1][3]).eq( + `row4`, + ); + }); + + it("copy spreadsheet with rows and columns", async () => { + let core = await createTestCore({ + doenetML: ` + + A1B1C1 + A2A3A4 + + B2C2D2 + B3C3D3 + + B4C4D4 + D1 + + + + + + + + + `, + }); + + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/spreadsheet1"].stateValues.numRows).eq(4); + expect(stateVariables["/spreadsheet1"].stateValues.numColumns).eq(4); + expect(stateVariables["/spreadsheet2"].stateValues.numRows).eq(4); + expect(stateVariables["/spreadsheet2"].stateValues.numColumns).eq(6); + for (let row = 1; row <= 4; row++) { + for (let col = 1; col <= 4; col++) { + expect( + stateVariables["/spreadsheet1"].stateValues.cells[row - 1][ + col - 1 + ], + ).eq(`${String.fromCharCode(64 + col)}${row}`); + } + } + for (let row = 1; row <= 2; row++) { + for (let col = 1; col <= 4; col++) { + expect( + stateVariables["/spreadsheet2"].stateValues.cells[row - 1][ + col - 1 + ], + ).eq(""); + } + } + for (let col = 1; col <= 4; col++) { + expect( + stateVariables["/spreadsheet2"].stateValues.cells[2][col - 1], + ).eq(`${String.fromCharCode(64 + col)}2`); + expect( + stateVariables["/spreadsheet2"].stateValues.cells[3][col - 1], + ).eq(`${String.fromCharCode(64 + col)}1`); + } + for (let row = 1; row <= 4; row++) { + expect( + stateVariables["/spreadsheet2"].stateValues.cells[row - 1][4], + ).eq(`C${row}`); + expect( + stateVariables["/spreadsheet2"].stateValues.cells[row - 1][5], + ).eq(`B${row}`); + } + + // enter text into second row of first spreadsheet + for (let ind = 1; ind <= 4; ind++) { + await changeSpreadsheetText({ + name: "/spreadsheet1", + row: 2, + column: ind, + text: `column${ind}`, + core, + }); + } + stateVariables = await returnAllStateVariables(core); + for (let ind = 1; ind <= 4; ind++) { + expect( + stateVariables["/spreadsheet1"].stateValues.cells[1][ind - 1], + ).eq(`column${ind}`); + } + // becomes third row of second spreadsheet + expect(stateVariables["/spreadsheet2"].stateValues.cells[2][0]).eq( + `column1`, + ); + expect(stateVariables["/spreadsheet2"].stateValues.cells[2][1]).eq( + `column2`, + ); + expect(stateVariables["/spreadsheet2"].stateValues.cells[2][2]).eq( + `column3`, + ); + expect(stateVariables["/spreadsheet2"].stateValues.cells[2][3]).eq( + `column4`, + ); + + // fifth and sixth column ref third and second column + expect(stateVariables["/spreadsheet2"].stateValues.cells[1][4]).eq( + `column3`, + ); + expect(stateVariables["/spreadsheet2"].stateValues.cells[1][5]).eq( + `column2`, + ); + + // enter text into fifth column of second spreadsheet + for (let ind = 1; ind <= 4; ind++) { + await changeSpreadsheetText({ + name: "/spreadsheet2", + row: ind, + column: 5, + text: `row${ind}`, + core, + }); + } + + stateVariables = await returnAllStateVariables(core); + for (let ind = 1; ind <= 4; ind++) { + expect( + stateVariables["/spreadsheet2"].stateValues.cells[ind - 1][4], + ).eq(`row${ind}`); + } + + //comes third column of first spreadsheet + expect(stateVariables["/spreadsheet1"].stateValues.cells[0][2]).eq( + `row1`, + ); + expect(stateVariables["/spreadsheet1"].stateValues.cells[1][2]).eq( + `row2`, + ); + expect(stateVariables["/spreadsheet1"].stateValues.cells[2][2]).eq( + `row3`, + ); + expect(stateVariables["/spreadsheet1"].stateValues.cells[3][2]).eq( + `row4`, + ); + + // third and fourth row of second spreadsheet also change due + // changes in second and first row of first spreadsheet + expect(stateVariables["/spreadsheet2"].stateValues.cells[2][2]).eq( + `row2`, + ); + expect(stateVariables["/spreadsheet2"].stateValues.cells[3][2]).eq( + `row1`, + ); + }); + + it("copy all spreadsheet cells shifted", async () => { + let core = await createTestCore({ + doenetML: ` + + A1B1C1 + A2B2C2 + A3B3C3 + + + + + + `, + }); + + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/spreadsheet1"].stateValues.numRows).eq(3); + expect(stateVariables["/spreadsheet1"].stateValues.numColumns).eq(3); + expect(stateVariables["/spreadsheet2"].stateValues.numRows).eq(5); + expect(stateVariables["/spreadsheet2"].stateValues.numColumns).eq(4); + for (let row = 1; row <= 3; row++) { + for (let col = 1; col <= 3; col++) { + expect( + stateVariables["/spreadsheet1"].stateValues.cells[row - 1][ + col - 1 + ], + ).eq(`${String.fromCharCode(64 + col)}${row}`); + } + } + for (let row = 1; row <= 3; row++) { + for (let col = 1; col <= 3; col++) { + expect( + stateVariables["/spreadsheet2"].stateValues.cells[row + 1][ + col + ], + ).eq(`${String.fromCharCode(64 + col)}${row}`); + } + } + + // enter text into second row of first spreadsheet + for (let ind = 1; ind <= 3; ind++) { + await changeSpreadsheetText({ + name: "/spreadsheet1", + row: 2, + column: ind, + text: `column${ind}`, + core, + }); + } + + stateVariables = await returnAllStateVariables(core); + for (let ind = 1; ind <= 3; ind++) { + expect( + stateVariables["/spreadsheet1"].stateValues.cells[1][ind - 1], + ).eq(`column${ind}`); + } + for (let ind = 1; ind <= 3; ind++) { + expect( + stateVariables["/spreadsheet2"].stateValues.cells[3][ind], + ).eq(`column${ind}`); + } + + // enter text into fourth column of second spreadsheet + for (let ind = 1; ind <= 5; ind++) { + await changeSpreadsheetText({ + name: "/spreadsheet2", + row: ind, + column: 4, + text: `row${ind}`, + core, + }); + } + + stateVariables = await returnAllStateVariables(core); + for (let ind = 1; ind <= 5; ind++) { + expect( + stateVariables["/spreadsheet2"].stateValues.cells[ind - 1][3], + ).eq(`row${ind}`); + } + + //becomes third column of first spreadsheet + expect(stateVariables["/spreadsheet1"].stateValues.cells[0][2]).eq( + `row3`, + ); + expect(stateVariables["/spreadsheet1"].stateValues.cells[1][2]).eq( + `row4`, + ); + expect(stateVariables["/spreadsheet1"].stateValues.cells[2][2]).eq( + `row5`, + ); + }); + + it("copy spreadsheet cells ignores cell col/row num", async () => { + let core = await createTestCore({ + doenetML: ` + + alpha + beta + gamma + + + + + + + + `, + }); + + let cellBlockUpperLefts = [ + [0, 0], + [0, 3], + [2, 3], + ]; + + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/spreadsheet1"].stateValues.cells[0][4]).eq( + "alpha", + ); + expect(stateVariables["/spreadsheet1"].stateValues.cells[0][5]).eq( + "beta", + ); + expect(stateVariables["/spreadsheet1"].stateValues.cells[1][5]).eq( + "gamma", + ); + for (let inds of cellBlockUpperLefts) { + let row = inds[0]; + let col = inds[1]; + expect( + stateVariables["/spreadsheet2"].stateValues.cells[row][col], + ).eq("alpha"); + expect( + stateVariables["/spreadsheet2"].stateValues.cells[row][col + 1], + ).eq("beta"); + expect( + stateVariables["/spreadsheet2"].stateValues.cells[row + 1][ + col + 1 + ], + ).eq("gamma"); + } + + // enter text in first spreadsheet block + await changeSpreadsheetText({ + name: "/spreadsheet1", + row: 1, + column: 5, + text: `a`, + core, + }); + await changeSpreadsheetText({ + name: "/spreadsheet1", + row: 1, + column: 6, + text: `b`, + core, + }); + await changeSpreadsheetText({ + name: "/spreadsheet1", + row: 2, + column: 5, + text: `c`, + core, + }); + await changeSpreadsheetText({ + name: "/spreadsheet1", + row: 2, + column: 6, + text: `d`, + core, + }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/spreadsheet1"].stateValues.cells[0][4]).eq("a"); + expect(stateVariables["/spreadsheet1"].stateValues.cells[0][5]).eq("b"); + expect(stateVariables["/spreadsheet1"].stateValues.cells[1][4]).eq("c"); + expect(stateVariables["/spreadsheet1"].stateValues.cells[1][5]).eq("d"); + for (let inds of cellBlockUpperLefts) { + let row = inds[0]; + let col = inds[1]; + expect( + stateVariables["/spreadsheet2"].stateValues.cells[row][col], + ).eq("a"); + expect( + stateVariables["/spreadsheet2"].stateValues.cells[row][col + 1], + ).eq("b"); + expect( + stateVariables["/spreadsheet2"].stateValues.cells[row + 1][col], + ).eq("c"); + expect( + stateVariables["/spreadsheet2"].stateValues.cells[row + 1][ + col + 1 + ], + ).eq("d"); + } + + // enter text in other spreadsheet blocks + await changeSpreadsheetText({ + name: "/spreadsheet2", + row: 1, + column: 1, + text: `first`, + core, + }); + await changeSpreadsheetText({ + name: "/spreadsheet2", + row: 1, + column: 5, + text: `second`, + core, + }); + await changeSpreadsheetText({ + name: "/spreadsheet2", + row: 4, + column: 4, + text: `third`, + core, + }); + await changeSpreadsheetText({ + name: "/spreadsheet2", + row: 4, + column: 5, + text: `fourth`, + core, + }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/spreadsheet1"].stateValues.cells[0][4]).eq( + "first", + ); + expect(stateVariables["/spreadsheet1"].stateValues.cells[0][5]).eq( + "second", + ); + expect(stateVariables["/spreadsheet1"].stateValues.cells[1][4]).eq( + "third", + ); + expect(stateVariables["/spreadsheet1"].stateValues.cells[1][5]).eq( + "fourth", + ); + for (let inds of cellBlockUpperLefts) { + let row = inds[0]; + let col = inds[1]; + expect( + stateVariables["/spreadsheet2"].stateValues.cells[row][col], + ).eq("first"); + expect( + stateVariables["/spreadsheet2"].stateValues.cells[row][col + 1], + ).eq("second"); + expect( + stateVariables["/spreadsheet2"].stateValues.cells[row + 1][col], + ).eq("third"); + expect( + stateVariables["/spreadsheet2"].stateValues.cells[row + 1][ + col + 1 + ], + ).eq("fourth"); + } + }); + + it("spreadsheet prefill", async () => { + let core = await createTestCore({ + doenetML: ` + x^2 + hello + 5 + true + + + + + + + + + + `, + }); + + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/m1"].stateValues.latex).eq("x^{2}"); + expect(stateVariables["/t1"].stateValues.value).eq("hello"); + expect(stateVariables["/n1"].stateValues.value).eq(5); + expect(stateVariables["/b1"].stateValues.value).eq(true); + expect(stateVariables["/spreadsheet1"].stateValues.cells[0][0]).eq( + "x²", + ); + expect(stateVariables["/spreadsheet1"].stateValues.cells[0][1]).eq( + "hello", + ); + expect(stateVariables["/spreadsheet1"].stateValues.cells[0][2]).eq("5"); + expect(stateVariables["/spreadsheet1"].stateValues.cells[0][3]).eq( + "true", + ); + + // changing spreadsheet doesn't change prefill sources + await changeSpreadsheetText({ + name: "/spreadsheet1", + row: 1, + column: 1, + text: "3(-", + core, + }); + await changeSpreadsheetText({ + name: "/spreadsheet1", + row: 1, + column: 2, + text: "bye", + core, + }); + await changeSpreadsheetText({ + name: "/spreadsheet1", + row: 1, + column: 3, + text: "ab", + core, + }); + await changeSpreadsheetText({ + name: "/spreadsheet1", + row: 1, + column: 4, + text: "1+q", + core, + }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/m1"].stateValues.latex).eq("x^{2}"); + expect(stateVariables["/t1"].stateValues.value).eq("hello"); + expect(stateVariables["/n1"].stateValues.value).eq(5); + expect(stateVariables["/b1"].stateValues.value).eq(true); + expect(stateVariables["/spreadsheet1"].stateValues.cells[0][0]).eq( + "3(-", + ); + expect(stateVariables["/spreadsheet1"].stateValues.cells[0][1]).eq( + "bye", + ); + expect(stateVariables["/spreadsheet1"].stateValues.cells[0][2]).eq( + "ab", + ); + expect(stateVariables["/spreadsheet1"].stateValues.cells[0][3]).eq( + "1+q", + ); + }); + + it("references to cells, adapter to math, number, or text", async () => { + let core = await createTestCore({ + doenetML: ` + + 1 + + +

A

+ +

+ $spreadsheet1.cellA1+1 + $spreadsheet1.cellA1+1 + $spreadsheet1.cellA1 B +

+ +

+ $spreadsheet1.cellA1+$spreadsheet1.cellA2 + $spreadsheet1.cellA1+$spreadsheet1.cellA2 + $spreadsheet1.cellA1 + $spreadsheet1.cellA2 +

+ `, + }); + + // check initial cell values + + async function check_values(A1 = "", A2 = "") { + let A1tree, A2tree; + try { + A1tree = me.fromText(A1).tree; + } catch (e) { + A1tree = "\uff3f"; + } + try { + A2tree = me.fromText(A2).tree; + } catch (e) { + A2tree = "\uff3f"; + } + let m1 = me.fromAst(["+", A1tree, 1]); + let m2 = me.fromAst(["+", A1tree, A2tree]); + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p1"].stateValues.text).eq(`${A1} A`); + expect(stateVariables["/math1"].stateValues.value.tree).eqls( + m1.simplify().tree, + ); + expect(stateVariables["/number1"].stateValues.value).eqls( + m1.evaluate_to_constant(), + ); + expect(stateVariables["/text1"].stateValues.value).eq(`${A1} B`); + expect(stateVariables["/math2"].stateValues.value.tree).eqls( + m2.simplify().tree, + ); + expect(stateVariables["/number2"].stateValues.value).eqls( + m2.evaluate_to_constant(), + ); + expect(stateVariables["/text2"].stateValues.value).eq( + `${A1} + ${A2}`, + ); + expect(stateVariables["/spreadsheet1"].stateValues.cells[0][0]).eq( + A1, + ); + expect(stateVariables["/spreadsheet1"].stateValues.cells[1][0]).eq( + A2, + ); + } + + let A1 = "1"; + let A2 = ""; + + await check_values(A1, A2); + + // different numbers in cells + A1 = "5"; + A2 = "7"; + await changeSpreadsheetText({ + name: "/spreadsheet1", + column: 1, + row: 1, + text: A1, + core, + }); + await changeSpreadsheetText({ + name: "/spreadsheet1", + column: 1, + row: 2, + text: A2, + core, + }); + await check_values(A1, A2); + + // different variables in cells + A1 = "x"; + A2 = "y"; + await changeSpreadsheetText({ + name: "/spreadsheet1", + column: 1, + row: 1, + text: A1, + core, + }); + await changeSpreadsheetText({ + name: "/spreadsheet1", + column: 1, + row: 2, + text: A2, + core, + }); + await check_values(A1, A2); + + // non-valid math in one cell + A1 = "q("; + A2 = "sin(w)"; + await changeSpreadsheetText({ + name: "/spreadsheet1", + column: 1, + row: 1, + text: A1, + core, + }); + await changeSpreadsheetText({ + name: "/spreadsheet1", + column: 1, + row: 2, + text: A2, + core, + }); + await check_values(A1, A2); + + // one cell is blank + A1 = ""; + A2 = "5"; + await changeSpreadsheetText({ + name: "/spreadsheet1", + column: 1, + row: 1, + text: A1, + core, + }); + await changeSpreadsheetText({ + name: "/spreadsheet1", + column: 1, + row: 2, + text: A2, + core, + }); + await check_values(A1, A2); + }); + + it("references to cells are not adapted", async () => { + let core = await createTestCore({ + doenetML: ` + + 12 + + +$s.cellB1{assignNames="c1"} +$s.cellA1{assignNames="c2"} + + + + $s.cellB1{assignNames="c11"} + Hello + $s.cellB2{assignNames="c13"} + + + Bye + $s.cellA1{assignNames="c22"} + + + + `, + }); + + async function check_items(A1 = "", B1 = "", B2 = "") { + const stateVariables = await returnAllStateVariables(core); + expect( + stateVariables["/row1"].activeChildren.map( + (v) => v.componentName, + ), + ).eqls(["/c11", "/c12", "/c13"]); + expect( + stateVariables["/row2"].activeChildren.map( + (v) => v.componentName, + ), + ).eqls(["/c21", "/c22"]); + expect(stateVariables["/c1"].stateValues.text).eq(B1); + expect(stateVariables["/c2"].stateValues.text).eq(A1); + expect(stateVariables["/c11"].stateValues.text).eq(B1); + expect(stateVariables["/c12"].stateValues.text).eq("Hello"); + expect(stateVariables["/c13"].stateValues.text).eq(B2); + expect(stateVariables["/c21"].stateValues.text).eq("Bye"); + expect(stateVariables["/c22"].stateValues.text).eq(A1); + } + + let A1 = "1", + B1 = "2", + B2 = ""; + + await check_items(A1, B1, B2); + + // change cells + A1 = "A"; + B1 = "B"; + B2 = "C"; + await changeSpreadsheetText({ + name: "/s", + column: 1, + row: 1, + text: A1, + core, + }); + await changeSpreadsheetText({ + name: "/s", + column: 2, + row: 1, + text: B1, + core, + }); + await changeSpreadsheetText({ + name: "/s", + column: 2, + row: 2, + text: B2, + core, + }); + + await check_items(A1, B1, B2); + }); + + async function test_merge_coordinates(core: Core) { + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/coords"].stateValues.text).eq("( 1, 2 )"); + expect(stateVariables["/t1"].stateValues.value).eq("( 1, 2 )"); + + await updateMathInputValue({ latex: "3", name: "/x1", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/coords"].stateValues.text).eq("( 3, 2 )"); + expect(stateVariables["/t1"].stateValues.value).eq("( 3, 2 )"); + + await updateMathInputValue({ latex: "4", name: "/x2", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/coords"].stateValues.text).eq("( 3, 4 )"); + expect(stateVariables["/t1"].stateValues.value).eq("( 3, 4 )"); + } + + it("spreadsheet can merge coordinates", async () => { + let core = await createTestCore({ + doenetML: ` + + + + + + + +

Change x-coordinate:

+

Change y-coordinate:

+ `, + }); + await test_merge_coordinates(core); + }); + + it("spreadsheet can merge coordinates, with math child", async () => { + let core = await createTestCore({ + doenetML: ` + + + (1,2) + + + + +

Change x-coordinate:

+

Change y-coordinate:

+ `, + }); + + await test_merge_coordinates(core); + }); + + async function test_copy_prop_index(core: Core) { + let row = ["A", "B", "C"]; + let column = ["B", "E", "H"]; + + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/R1"]).be.undefined; + expect(stateVariables["/R2"]).be.undefined; + expect(stateVariables["/R3"]).be.undefined; + expect(stateVariables["/C1"]).be.undefined; + expect(stateVariables["/C2"]).be.undefined; + expect(stateVariables["/C3"]).be.undefined; + + await updateMathInputValue({ latex: "1", name: "/n", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/R1"].stateValues.value).eq(row[0]); + expect(stateVariables["/R2"]).be.undefined; + expect(stateVariables["/R3"]).be.undefined; + expect(stateVariables["/C1"].stateValues.value).eq(column[0]); + expect(stateVariables["/C2"]).be.undefined; + expect(stateVariables["/C3"]).be.undefined; + + await updateMathInputValue({ latex: "2", name: "/n", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/R1"].stateValues.value).eq(row[1]); + expect(stateVariables["/R2"]).be.undefined; + expect(stateVariables["/R3"]).be.undefined; + expect(stateVariables["/C1"].stateValues.value).eq(column[1]); + expect(stateVariables["/C2"]).be.undefined; + expect(stateVariables["/C3"]).be.undefined; + + await updateMathInputValue({ latex: "3", name: "/n", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/R1"].stateValues.value).eq(row[2]); + expect(stateVariables["/R2"]).be.undefined; + expect(stateVariables["/R3"]).be.undefined; + expect(stateVariables["/C1"].stateValues.value).eq(column[2]); + expect(stateVariables["/C2"]).be.undefined; + expect(stateVariables["/C3"]).be.undefined; + + await updateMathInputValue({ latex: "4", name: "/n", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/R1"]).be.undefined; + expect(stateVariables["/R2"]).be.undefined; + expect(stateVariables["/R3"]).be.undefined; + expect(stateVariables["/C1"]).be.undefined; + expect(stateVariables["/C2"]).be.undefined; + expect(stateVariables["/C3"]).be.undefined; + } + it("copy propIndex of cells, dot and array notation", async () => { + let core = await createTestCore({ + doenetML: ` + + ABC + DEF + GHI + + +

+ + + + + + + + + + + + + + + + + `, + }); + + await test_copy_prop_index(core); + }); + + it("copy multidimensional propIndex of cells, array notation", async () => { + let core = await createTestCore({ + doenetML: ` + + ABC + DEF + GHI + + +

+ + + + + + + + + + + + + + + + + `, + }); + + await test_copy_prop_index(core); + }); + + it("copy multidimensional propIndex of rows and columns, dot and array notation", async () => { + let core = await createTestCore({ + doenetML: ` + + ABC + DEF + GHI + + +

+ + + + + + + + + + + + + + + + + `, + }); + + await test_copy_prop_index(core); + }); + + it("copy single propIndex of rows and columns, dot and array notation", async () => { + let core = await createTestCore({ + doenetML: ` + + ABC + DEF + GHI + + +

+ + + + + + + + + + + + + + + + + `, + }); + + let rows = [ + ["A", "B", "C"], + ["D", "E", "F"], + ["G", "H", "I"], + ]; + let columns = [ + ["A", "D", "G"], + ["B", "E", "H"], + ["C", "F", "I"], + ]; + + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/R1"]).be.undefined; + expect(stateVariables["/R2"]).be.undefined; + expect(stateVariables["/R3"]).be.undefined; + expect(stateVariables["/C1"]).be.undefined; + expect(stateVariables["/C2"]).be.undefined; + expect(stateVariables["/C3"]).be.undefined; + + await updateMathInputValue({ latex: "1", name: "/n", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/R1"].stateValues.value).eq(rows[0][0]); + expect(stateVariables["/R2"].stateValues.value).eq(rows[0][1]); + expect(stateVariables["/R3"].stateValues.value).eq(rows[0][2]); + expect(stateVariables["/C1"].stateValues.value).eq(columns[0][0]); + expect(stateVariables["/C2"].stateValues.value).eq(columns[0][1]); + expect(stateVariables["/C3"].stateValues.value).eq(columns[0][2]); + + await updateMathInputValue({ latex: "2", name: "/n", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/R1"].stateValues.value).eq(rows[1][0]); + expect(stateVariables["/R2"].stateValues.value).eq(rows[1][1]); + expect(stateVariables["/R3"].stateValues.value).eq(rows[1][2]); + expect(stateVariables["/C1"].stateValues.value).eq(columns[1][0]); + expect(stateVariables["/C2"].stateValues.value).eq(columns[1][1]); + expect(stateVariables["/C3"].stateValues.value).eq(columns[1][2]); + + await updateMathInputValue({ latex: "3", name: "/n", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/R1"].stateValues.value).eq(rows[2][0]); + expect(stateVariables["/R2"].stateValues.value).eq(rows[2][1]); + expect(stateVariables["/R3"].stateValues.value).eq(rows[2][2]); + expect(stateVariables["/C1"].stateValues.value).eq(columns[2][0]); + expect(stateVariables["/C2"].stateValues.value).eq(columns[2][1]); + expect(stateVariables["/C3"].stateValues.value).eq(columns[2][2]); + + await updateMathInputValue({ latex: "4", name: "/n", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/R1"]).be.undefined; + expect(stateVariables["/R2"]).be.undefined; + expect(stateVariables["/R3"]).be.undefined; + expect(stateVariables["/C1"]).be.undefined; + expect(stateVariables["/C2"]).be.undefined; + expect(stateVariables["/C3"]).be.undefined; + }); +}); diff --git a/packages/doenetml-worker/src/test/tagSpecific/stickygroup.test.ts b/packages/doenetml-worker/src/test/tagSpecific/stickygroup.test.ts new file mode 100644 index 000000000..49f4d2340 --- /dev/null +++ b/packages/doenetml-worker/src/test/tagSpecific/stickygroup.test.ts @@ -0,0 +1,1614 @@ +import { describe, expect, it, vi } from "vitest"; +import { createTestCore, returnAllStateVariables } from "../utils/test-core"; +import { + moveLineSegment, + movePoint, + movePolygon, + movePolyline, +} from "../utils/actions"; +import Core from "../../Core"; + +const Mock = vi.fn(); +vi.stubGlobal("postMessage", Mock); +vi.mock("hyperformula"); + +async function testScene({ + core, + vertices1, + vertices2, + point, + polygon1Name = "/pg1", + polygon2Name = "/pg2", + pointName = "/A", + stickyGroupName = "/sg", + graph1Name = "/g1", + graph2Name = "/g2", + graph3Name = "/g3", + graph4Name = "/g4", +}: { + core: Core; + vertices1: number[][]; + vertices2: number[][]; + point: number[]; + polygon1Name?: string; + polygon2Name?: string; + pointName?: string; + stickyGroupName?: string; + graph1Name?: string; + graph2Name?: string; + graph3Name?: string; + graph4Name?: string; +}) { + let stateVariables = await returnAllStateVariables(core); + expect( + stateVariables[graph1Name + stickyGroupName + polygon1Name].stateValues + .numVertices, + ).eq(vertices1.length); + expect( + stateVariables[graph2Name + stickyGroupName + polygon1Name].stateValues + .numVertices, + ).eq(vertices1.length); + expect( + stateVariables[graph3Name + stickyGroupName + polygon1Name].stateValues + .numVertices, + ).eq(vertices1.length); + expect( + stateVariables[graph4Name + stickyGroupName + polygon1Name].stateValues + .numVertices, + ).eq(vertices1.length); + + expect( + stateVariables[graph1Name + stickyGroupName + polygon2Name].stateValues + .numVertices, + ).eq(vertices2.length); + expect( + stateVariables[graph2Name + stickyGroupName + polygon2Name].stateValues + .numVertices, + ).eq(vertices2.length); + expect( + stateVariables[graph3Name + stickyGroupName + polygon2Name].stateValues + .numVertices, + ).eq(vertices2.length); + expect( + stateVariables[graph4Name + stickyGroupName + polygon2Name].stateValues + .numVertices, + ).eq(vertices2.length); + + for (let i in vertices1) { + for (let dim in vertices1[i]) { + if (Number.isFinite(vertices1[i][dim])) { + expect( + stateVariables[ + graph1Name + stickyGroupName + polygon1Name + ].stateValues.vertices[i][dim].evaluate_to_constant(), + ).closeTo(vertices1[i][dim], 1e-12); + expect( + stateVariables[ + graph2Name + stickyGroupName + polygon1Name + ].stateValues.vertices[i][dim].evaluate_to_constant(), + ).closeTo(vertices1[i][dim], 1e-12); + expect( + stateVariables[ + graph3Name + stickyGroupName + polygon1Name + ].stateValues.vertices[i][dim].evaluate_to_constant(), + ).closeTo(vertices1[i][dim], 1e-12); + expect( + stateVariables[ + graph4Name + stickyGroupName + polygon1Name + ].stateValues.vertices[i][dim].evaluate_to_constant(), + ).closeTo(vertices1[i][dim], 1e-12); + } else { + expect( + stateVariables[graph1Name + stickyGroupName + polygon1Name] + .stateValues.vertices[i][dim].tree, + ).eq(vertices1[i][dim]); + expect( + stateVariables[graph2Name + stickyGroupName + polygon1Name] + .stateValues.vertices[i][dim].tree, + ).eq(vertices1[i][dim]); + expect( + stateVariables[graph3Name + stickyGroupName + polygon1Name] + .stateValues.vertices[i][dim].tree, + ).eq(vertices1[i][dim]); + expect( + stateVariables[graph4Name + stickyGroupName + polygon1Name] + .stateValues.vertices[i][dim].tree, + ).eq(vertices1[i][dim]); + } + } + } + + for (let i in vertices2) { + for (let dim in vertices2[i]) { + if (Number.isFinite(vertices2[i][dim])) { + expect( + stateVariables[ + graph1Name + stickyGroupName + polygon2Name + ].stateValues.vertices[i][dim].evaluate_to_constant(), + ).closeTo(vertices2[i][dim], 1e-12); + expect( + stateVariables[ + graph2Name + stickyGroupName + polygon2Name + ].stateValues.vertices[i][dim].evaluate_to_constant(), + ).closeTo(vertices2[i][dim], 1e-12); + expect( + stateVariables[ + graph3Name + stickyGroupName + polygon2Name + ].stateValues.vertices[i][dim].evaluate_to_constant(), + ).closeTo(vertices2[i][dim], 1e-12); + expect( + stateVariables[ + graph4Name + stickyGroupName + polygon2Name + ].stateValues.vertices[i][dim].evaluate_to_constant(), + ).closeTo(vertices2[i][dim], 1e-12); + } else { + expect( + stateVariables[graph1Name + stickyGroupName + polygon2Name] + .stateValues.vertices[i][dim].tree, + ).eq(vertices2[i][dim]); + expect( + stateVariables[graph2Name + stickyGroupName + polygon2Name] + .stateValues.vertices[i][dim].tree, + ).eq(vertices2[i][dim]); + expect( + stateVariables[graph3Name + stickyGroupName + polygon2Name] + .stateValues.vertices[i][dim].tree, + ).eq(vertices2[i][dim]); + expect( + stateVariables[graph4Name + stickyGroupName + polygon2Name] + .stateValues.vertices[i][dim].tree, + ).eq(vertices2[i][dim]); + } + } + } + + for (let dim in point) { + if (Number.isFinite(point[dim])) { + expect( + stateVariables[ + graph1Name + stickyGroupName + pointName + ].stateValues.xs[dim].evaluate_to_constant(), + ).closeTo(point[dim], 1e-12); + expect( + stateVariables[ + graph2Name + stickyGroupName + pointName + ].stateValues.xs[dim].evaluate_to_constant(), + ).closeTo(point[dim], 1e-12); + expect( + stateVariables[ + graph3Name + stickyGroupName + pointName + ].stateValues.xs[dim].evaluate_to_constant(), + ).closeTo(point[dim], 1e-12); + expect( + stateVariables[ + graph4Name + stickyGroupName + pointName + ].stateValues.xs[dim].evaluate_to_constant(), + ).closeTo(point[dim], 1e-12); + } else { + expect( + stateVariables[graph1Name + stickyGroupName + pointName] + .stateValues.xs[dim].tree, + ).eq(point[dim]); + expect( + stateVariables[graph2Name + stickyGroupName + pointName] + .stateValues.xs[dim].tree, + ).eq(point[dim]); + expect( + stateVariables[graph3Name + stickyGroupName + pointName] + .stateValues.xs[dim].tree, + ).eq(point[dim]); + expect( + stateVariables[graph4Name + stickyGroupName + pointName] + .stateValues.xs[dim].tree, + ).eq(point[dim]); + } + } +} + +describe("StickyGroup tag tests", async () => { + it("attract polygons and point when translating", async () => { + let core = await createTestCore({ + doenetML: ` + + + + + + + (-6,2) + + + + + + + + + + + + + + + + + `, + }); + + let vertices1 = [ + [1, 2], + [4, 5], + [-2, 5], + ]; + + let vertices2 = [ + [7, 8], + [5, 4], + [9, 1], + [7, 3], + ]; + + let point = [-6, 2]; + + await testScene({ core, vertices1, vertices2, point }); + + // move polygon 1 vertex near vertex of polygon 2 + let moveX = 0.8; + let moveY = -1.2; + + let requested_vertices1 = vertices1.map((vertex) => [ + vertex[0] + moveX, + vertex[1] + moveY, + ]); + + await movePolygon({ + name: "/g1/sg/pg1", + pointCoords: requested_vertices1, + core, + }); + + let actualMoveX = 1; + let actualMoveY = -1; + + vertices1 = vertices1.map((vertex) => [ + vertex[0] + actualMoveX, + vertex[1] + actualMoveY, + ]); + await testScene({ core, vertices1, vertices2, point }); + + // move polygon 2 vertex near vertex of polygon 1 + moveX = -5.2; + moveY = -2.3; + + let requested_vertices2 = vertices2.map((vertex) => [ + vertex[0] + moveX, + vertex[1] + moveY, + ]); + + await movePolygon({ + name: "/g2/sg/pg2", + pointCoords: requested_vertices2, + core, + }); + + actualMoveX = -5; + actualMoveY = -2; + + vertices2 = vertices2.map((vertex) => [ + vertex[0] + actualMoveX, + vertex[1] + actualMoveY, + ]); + + await testScene({ core, vertices1, vertices2, point }); + + // move polygon 2 left further to unstick from polygon 1 + moveX = -1; + moveY = 0; + + requested_vertices2 = vertices2.map((vertex) => [ + vertex[0] + moveX, + vertex[1] + moveY, + ]); + + await movePolygon({ + name: "/g3/sg/pg2", + pointCoords: requested_vertices2, + core, + }); + + actualMoveX = -1; + actualMoveY = 0; + + vertices2 = vertices2.map((vertex) => [ + vertex[0] + actualMoveX, + vertex[1] + actualMoveY, + ]); + + await testScene({ core, vertices1, vertices2, point }); + + // move polygon 1 vertex near point + moveX = -4.8; + moveY = -1.8; + + requested_vertices1 = vertices1.map((vertex) => [ + vertex[0] + moveX, + vertex[1] + moveY, + ]); + + await movePolygon({ + name: "/g4/sg/pg1", + pointCoords: requested_vertices1, + core, + }); + + actualMoveX = -5; + actualMoveY = -2; + + vertices1 = vertices1.map((vertex) => [ + vertex[0] + actualMoveX, + vertex[1] + actualMoveY, + ]); + + await testScene({ core, vertices1, vertices2, point }); + + // move point near polygon2 vertex + await movePoint({ name: "/g1/sg/A", x: 0.8, y: 0.8, core }); + + point = [1, 1]; + + await testScene({ core, vertices1, vertices2, point }); + + // move point away from vertices and edges + await movePoint({ name: "/g2/sg/A", x: -2, y: 1, core }); + + point = [-2, 1]; + + await testScene({ core, vertices1, vertices2, point }); + + // move polygon 1 vertex near edge of polygon 2 + moveX = 1.4; + moveY = 3; + + requested_vertices1 = vertices1.map((vertex) => [ + vertex[0] + moveX, + vertex[1] + moveY, + ]); + + await movePolygon({ + name: "/g3/sg/pg1", + pointCoords: requested_vertices1, + core, + }); + + actualMoveX = 1; + actualMoveY = 3; + + vertices1 = vertices1.map((vertex) => [ + vertex[0] + actualMoveX, + vertex[1] + actualMoveY, + ]); + await testScene({ core, vertices1, vertices2, point }); + + // move polygon 2 vertex next edge of polygon 1 + moveX = 1.2; + moveY = 1.8; + + requested_vertices2 = vertices2.map((vertex) => [ + vertex[0] + moveX, + vertex[1] + moveY, + ]); + + await movePolygon({ + name: "/g4/sg/pg2", + pointCoords: requested_vertices2, + core, + }); + + actualMoveX = 1; + actualMoveY = 2; + + vertices2 = vertices2.map((vertex) => [ + vertex[0] + actualMoveX, + vertex[1] + actualMoveY, + ]); + + await testScene({ core, vertices1, vertices2, point }); + + // move polygon 1 edge near vertex of polygon 2 + moveX = 2.8; + moveY = 0.2; + + requested_vertices1 = vertices1.map((vertex) => [ + vertex[0] + moveX, + vertex[1] + moveY, + ]); + + await movePolygon({ + name: "/g1/sg/pg1", + pointCoords: requested_vertices1, + core, + }); + + actualMoveX = 3; + actualMoveY = 0; + + vertices1 = vertices1.map((vertex) => [ + vertex[0] + actualMoveX, + vertex[1] + actualMoveY, + ]); + await testScene({ core, vertices1, vertices2, point }); + + // move polygon 2 edge next vertex of polygon 1 + moveX = 2.2; + moveY = -2; + + requested_vertices2 = vertices2.map((vertex) => [ + vertex[0] + moveX, + vertex[1] + moveY, + ]); + + await movePolygon({ + name: "/g2/sg/pg2", + pointCoords: requested_vertices2, + core, + }); + + actualMoveX = 2; + actualMoveY = -2; + + vertices2 = vertices2.map((vertex) => [ + vertex[0] + actualMoveX, + vertex[1] + actualMoveY, + ]); + + await testScene({ core, vertices1, vertices2, point }); + }); + + it("attract parallel edges of polygons when translating", async () => { + let core = await createTestCore({ + doenetML: ` + + + + + + + + + + + + + + + + + + + + + + `, + }); + + let vertices1 = [ + [1, 2], + [4, 5], + [-2, 5], + ]; + + let vertices2 = [ + [9, 8], + [3, 2], + [9, 1], + ]; + + let point = []; + + await testScene({ core, vertices1, vertices2, point }); + + // move polygon 1 edge near edge of polygon 2 + + let moveX = 1.5; + let moveY = 0; + + let requested_vertices1 = vertices1.map((vertex) => [ + vertex[0] + moveX, + vertex[1] + moveY, + ]); + + await movePolygon({ + name: "/g1/sg/pg1", + pointCoords: requested_vertices1, + core, + }); + + let actualMoveX = 1.75; + let actualMoveY = -0.25; + + vertices1 = vertices1.map((vertex) => [ + vertex[0] + actualMoveX, + vertex[1] + actualMoveY, + ]); + await testScene({ core, vertices1, vertices2, point }); + + // move polygon 1 so edge is just past edge of polygon 2, vertices attract + + moveX = 0; + moveY = 0.25; + + requested_vertices1 = vertices1.map((vertex) => [ + vertex[0] + moveX, + vertex[1] + moveY, + ]); + + await movePolygon({ + name: "/g2/sg/pg1", + pointCoords: requested_vertices1, + core, + }); + + actualMoveX = 0.25; + actualMoveY = 0.25; + + vertices1 = vertices1.map((vertex) => [ + vertex[0] + actualMoveX, + vertex[1] + actualMoveY, + ]); + + await testScene({ core, vertices1, vertices2, point }); + + // move polygon 1 edge so that polygon 2 edge is just past edge of polygon 1, vertices attract + + moveX = 2.9; + moveY = 2.8; + + requested_vertices1 = vertices1.map((vertex) => [ + vertex[0] + moveX, + vertex[1] + moveY, + ]); + + await movePolygon({ + name: "/g3/sg/pg1", + pointCoords: requested_vertices1, + core, + }); + + actualMoveX = 3; + actualMoveY = 3; + + vertices1 = vertices1.map((vertex) => [ + vertex[0] + actualMoveX, + vertex[1] + actualMoveY, + ]); + + await testScene({ core, vertices1, vertices2, point }); + + // move polygon 1 edge further so vertices don't attract + + moveX = -0.5; + moveY = -0.4; + + requested_vertices1 = vertices1.map((vertex) => [ + vertex[0] + moveX, + vertex[1] + moveY, + ]); + + await movePolygon({ + name: "/g4/sg/pg1", + pointCoords: requested_vertices1, + core, + }); + + actualMoveX = -0.45; + actualMoveY = -0.45; + + vertices1 = vertices1.map((vertex) => [ + vertex[0] + actualMoveX, + vertex[1] + actualMoveY, + ]); + + await testScene({ core, vertices1, vertices2, point }); + + // move polygon 2 edge so just past edge of polygon 1 + + moveX = 2.5; + moveY = 2.0; + + let requested_vertices2 = vertices2.map((vertex) => [ + vertex[0] + moveX, + vertex[1] + moveY, + ]); + + await movePolygon({ + name: "/g3/sg/pg2", + pointCoords: requested_vertices2, + core, + }); + + actualMoveX = 2.25; + actualMoveY = 2.25; + + vertices2 = vertices2.map((vertex) => [ + vertex[0] + actualMoveX, + vertex[1] + actualMoveY, + ]); + await testScene({ core, vertices1, vertices2, point }); + + // move polygon 2 less past edge of polygon 1 so vertices attract + + moveX = 0.2; + moveY = 0.0; + + requested_vertices2 = vertices2.map((vertex) => [ + vertex[0] + moveX, + vertex[1] + moveY, + ]); + + await movePolygon({ + name: "/g4/sg/pg2", + pointCoords: requested_vertices2, + core, + }); + + actualMoveX = 0.3; + actualMoveY = 0.3; + + vertices2 = vertices2.map((vertex) => [ + vertex[0] + actualMoveX, + vertex[1] + actualMoveY, + ]); + + await testScene({ core, vertices1, vertices2, point }); + + // move polygon 2 edge so that polygon 1 edge is just past edge of polygon 2, vertices attract + + moveX = -2.8; + moveY = -2.6; + + requested_vertices2 = vertices2.map((vertex) => [ + vertex[0] + moveX, + vertex[1] + moveY, + ]); + + await movePolygon({ + name: "/g1/sg/pg2", + pointCoords: requested_vertices2, + core, + }); + + actualMoveX = -3; + actualMoveY = -3; + + vertices2 = vertices2.map((vertex) => [ + vertex[0] + actualMoveX, + vertex[1] + actualMoveY, + ]); + await testScene({ core, vertices1, vertices2, point }); + + // move polygon 2 edge further so vertices don't attract + + moveX = -0.5; + moveY = -0.3; + + requested_vertices2 = vertices2.map((vertex) => [ + vertex[0] + moveX, + vertex[1] + moveY, + ]); + + await movePolygon({ + name: "/g2/sg/pg2", + pointCoords: requested_vertices2, + core, + }); + + actualMoveX = -0.4; + actualMoveY = -0.4; + + vertices2 = vertices2.map((vertex) => [ + vertex[0] + actualMoveX, + vertex[1] + actualMoveY, + ]); + + await testScene({ core, vertices1, vertices2, point }); + }); + + it("attract polygons when moving vertices, rigid and non-rigid", async () => { + let core = await createTestCore({ + doenetML: ` + + + + + + + + + +

$pg1.vertices

+

$pg2.vertices

+ + `, + }); + + let vertices2 = [ + [7, 8], + [5, 4], + [9, 1], + [7, 3], + ]; + + // rotate polygon 1 to nearly match slope of nearby polygon 2 edge + await movePolygon({ + name: "/pg1", + pointCoords: { 0: [1.6, 2] }, + core, + }); + + let desired_slope = 2; + + let stateVariables = await returnAllStateVariables(core); + + let pg1 = stateVariables["/pg1"].stateValues.numericalVertices; + let actual_slope = (pg1[1][1] - pg1[0][1]) / (pg1[1][0] - pg1[0][0]); + + expect(actual_slope).closeTo(desired_slope, 1e-12); + + // if move polygon 1 further away, slope does not attract to polygon 2 edge + stateVariables = await returnAllStateVariables(core); + + let moveX = -1; + let moveY = 1; + + let desired_vertices = stateVariables[ + "/pg1" + ].stateValues.numericalVertices.map((vertex) => [ + vertex[0] + moveX, + vertex[1] + moveY, + ]); + await movePolygon({ + name: "/pg1", + pointCoords: desired_vertices, + core, + }); + + // perturb vertex slightly + await movePolygon({ + name: "/pg1", + pointCoords: { + 0: [ + desired_vertices[0][0] + 0.01, + desired_vertices[0][1] - 0.01, + ], + }, + core, + }); + + desired_slope = 2; + + stateVariables = await returnAllStateVariables(core); + + pg1 = stateVariables["/pg1"].stateValues.numericalVertices; + actual_slope = (pg1[1][1] - pg1[0][1]) / (pg1[1][0] - pg1[0][0]); + + expect(actual_slope).not.closeTo(desired_slope, 0.001); + + // polygon 1 edge still attracts to vertical + await movePolygon({ + name: "/pg1", + pointCoords: { + 0: [-2.1, 5.1], + }, + core, + }); + + stateVariables = await returnAllStateVariables(core); + + pg1 = stateVariables["/pg1"].stateValues.numericalVertices; + + // edge should be vertical + expect(pg1[2][0]).closeTo(pg1[1][0], 1e-12); + + // polygon 1 edge still attracts to horizontal + await movePolygon({ + name: "/pg1", + pointCoords: { + 0: [0.2, 7.2], + }, + core, + }); + + stateVariables = await returnAllStateVariables(core); + + pg1 = stateVariables["/pg1"].stateValues.numericalVertices; + + // edge should be horizontal + expect(pg1[2][1]).closeTo(pg1[1][1], 1e-12); + + // move polygon 1 and rotate so vertex is very close to polygon 2 vertex to attract + stateVariables = await returnAllStateVariables(core); + + moveX = 7; + moveY = -6.5; + + desired_vertices = stateVariables[ + "/pg1" + ].stateValues.numericalVertices.map((vertex) => [ + vertex[0] + moveX, + vertex[1] + moveY, + ]); + await movePolygon({ + name: "/pg1", + pointCoords: desired_vertices, + core, + }); + + await movePolygon({ + name: "/pg1", + pointCoords: { + 0: [5.2, -0.8], + }, + core, + }); + + stateVariables = await returnAllStateVariables(core); + + pg1 = stateVariables["/pg1"].stateValues.numericalVertices; + + // point3 should be at polygon 2's vertex + expect(pg1[2][0]).closeTo(9, 1e-12); + expect(pg1[2][1]).closeTo(1, 1e-12); + + // small change in rotation stops attraction + await movePolygon({ + name: "/pg1", + pointCoords: { + 0: [5.3, -0.8], + }, + core, + }); + + stateVariables = await returnAllStateVariables(core); + + pg1 = stateVariables["/pg1"].stateValues.numericalVertices; + + // point3 should be a small distance from polygon 2's vertex + expect(pg1[2][0]).not.closeTo(9, 0.01); + expect(pg1[2][1]).not.closeTo(1, 0.01); + + // rotate and move polygon 1 to have vertical edge + await movePolygon({ + name: "/pg1", + pointCoords: { + 0: [4.9, -1.6], + }, + core, + }); + + stateVariables = await returnAllStateVariables(core); + + moveX = -7; + moveY = 0.5; + + desired_vertices = stateVariables[ + "/pg1" + ].stateValues.numericalVertices.map((vertex) => [ + vertex[0] + moveX, + vertex[1] + moveY, + ]); + await movePolygon({ + name: "/pg1", + pointCoords: desired_vertices, + core, + }); + + stateVariables = await returnAllStateVariables(core); + + pg1 = stateVariables["/pg1"].stateValues.numericalVertices; + + // edge should be vertical + expect(pg1[2][0]).closeTo(pg1[1][0], 1e-12); + + // move polygon 2 vertex so edge attracts to polygon 1 vertex + await movePolygon({ + name: "/pg2", + pointCoords: { 2: [-0.3, -6] }, + core, + }); + + stateVariables = await returnAllStateVariables(core); + + pg1 = stateVariables["/pg1"].stateValues.numericalVertices; + let pg2 = stateVariables["/pg2"].stateValues.numericalVertices; + + // vertex 2 of pg1 should be along edge of vertices of 2 and 3 of pg2 + let slope1 = (pg2[2][1] - pg2[1][1]) / (pg2[2][0] - pg2[1][0]); + let slope2 = (pg1[1][1] - pg2[1][1]) / (pg1[1][0] - pg2[1][0]); + + expect(slope2).closeTo(slope1, 1e-12); + + // vertices other than 3 of polygon 2 should be in original positions + expect(pg2[0]).eqls(vertices2[0]); + expect(pg2[1]).eqls(vertices2[1]); + expect(pg2[3]).eqls(vertices2[3]); + + vertices2[2] = pg2[2]; + + // move polygon 2 vertex so edge almost attracts to polygon 1 vertex + await movePolygon({ + name: "/pg2", + pointCoords: { 0: [-10, 0.8] }, + core, + }); + + stateVariables = await returnAllStateVariables(core); + pg2 = stateVariables["/pg2"].stateValues.numericalVertices; + + // vertex did not attract + expect(pg2[0]).eqls([-10, 0.8]); + + expect(pg2[1]).eqls(vertices2[1]); + expect(pg2[2]).eqls(vertices2[2]); + expect(pg2[3]).eqls(vertices2[3]); + + vertices2[0] = pg2[0]; + + // move polygon 2 vertex closer so edge does attract to polygon 1 vertex + await movePolygon({ + name: "/pg2", + pointCoords: { 0: [-10, 0.6] }, + core, + }); + + stateVariables = await returnAllStateVariables(core); + + pg1 = stateVariables["/pg1"].stateValues.numericalVertices; + pg2 = stateVariables["/pg2"].stateValues.numericalVertices; + + // vertex 3 of pg1 should be along edge of vertices of 1 and 4 of pg2 + slope1 = (pg2[3][1] - pg2[0][1]) / (pg2[3][0] - pg2[0][0]); + slope2 = (pg1[2][1] - pg2[0][1]) / (pg1[2][0] - pg2[0][0]); + + expect(slope2).closeTo(slope1, 1e-12); + + // vertices other than 1 of polygon 2 should be in original positions + expect(pg2[1]).eqls(vertices2[1]); + expect(pg2[2]).eqls(vertices2[2]); + expect(pg2[3]).eqls(vertices2[3]); + + vertices2[0] = pg2[0]; + + // move polygon 2 vertex so almost attracts to polygon 1 edge + await movePolygon({ + name: "/pg2", + pointCoords: { 1: [1.7, -1] }, + core, + }); + + stateVariables = await returnAllStateVariables(core); + pg2 = stateVariables["/pg2"].stateValues.numericalVertices; + + // vertex did not attract + expect(pg2[1]).eqls([1.7, -1]); + + expect(pg2[0]).eqls(vertices2[0]); + expect(pg2[2]).eqls(vertices2[2]); + expect(pg2[3]).eqls(vertices2[3]); + + vertices2[1] = pg2[1]; + + // move polygon 2 vertex closer so does attract to polygon 1 edge + await movePolygon({ + name: "/pg2", + pointCoords: { 1: [1.4, -1] }, + core, + }); + + stateVariables = await returnAllStateVariables(core); + pg2 = stateVariables["/pg2"].stateValues.numericalVertices; + + // vertex did attract + expect(pg2[1][0]).closeTo(1, 1e-12); + expect(pg2[1][1]).closeTo(-1, 1e-12); + + expect(pg2[0]).eqls(vertices2[0]); + expect(pg2[2]).eqls(vertices2[2]); + expect(pg2[3]).eqls(vertices2[3]); + + vertices2[1] = pg2[1]; + + // move polygon 2 vertex close so attracts to polygon 1 vertex + await movePolygon({ + name: "/pg2", + pointCoords: { 3: [1.2, 1.8] }, + core, + }); + + stateVariables = await returnAllStateVariables(core); + pg2 = stateVariables["/pg2"].stateValues.numericalVertices; + + // vertex did attract + expect(pg2[3][0]).closeTo(1, 1e-12); + expect(pg2[3][1]).closeTo(2, 1e-12); + + expect(pg2[0]).eqls(vertices2[0]); + expect(pg2[1]).eqls(vertices2[1]); + expect(pg2[2]).eqls(vertices2[2]); + + vertices2[3] = pg2[3]; + + // move polygon 2 vertex slightly away so no longer attracts to polygon 1 vertex + await movePolygon({ + name: "/pg2", + pointCoords: { 3: [1.7, 2.8] }, + core, + }); + + stateVariables = await returnAllStateVariables(core); + pg2 = stateVariables["/pg2"].stateValues.numericalVertices; + + // vertex did not attract + expect(pg2[3]).eqls([1.7, 2.8]); + + expect(pg2[0]).eqls(vertices2[0]); + expect(pg2[1]).eqls(vertices2[1]); + expect(pg2[2]).eqls(vertices2[2]); + + vertices2[3] = pg2[3]; + + // move polygon 2 vertex so edge attracts to polygon 1 edge + await movePolygon({ + name: "/pg2", + pointCoords: { 0: [-6, -5.1] }, + core, + }); + + stateVariables = await returnAllStateVariables(core); + pg1 = stateVariables["/pg1"].stateValues.numericalVertices; + pg2 = stateVariables["/pg2"].stateValues.numericalVertices; + + // vertex 1 and 4 of pg2 should be along edge of vertices of 1 and 3 of pg1 + slope1 = (pg1[2][1] - pg1[0][1]) / (pg1[2][0] - pg1[0][0]); + slope2 = (pg2[0][1] - pg1[0][1]) / (pg2[0][0] - pg1[0][0]); + let slope3 = (pg2[3][1] - pg1[0][1]) / (pg2[3][0] - pg1[0][0]); + + expect(slope2).closeTo(slope1, 1e-12); + expect(slope3).closeTo(slope1, 1e-12); + + // vertex 4 moved + expect(pg2[3][0]).not.closeTo(vertices2[3][0], 0.01); + expect(pg2[3][1]).not.closeTo(vertices2[3][1], 0.01); + + // other vertices did not move + expect(pg2[1]).eqls(vertices2[1]); + expect(pg2[2]).eqls(vertices2[2]); + + vertices2[0] = pg2[0]; + vertices2[3] = pg2[3]; + + // make sure don't move point along polygon 1 edge when attracting polygon 2 point to vertex + await movePolygon({ + name: "/pg2", + pointCoords: { 2: [1.2, -4.2] }, + core, + }); + + stateVariables = await returnAllStateVariables(core); + pg2 = stateVariables["/pg2"].stateValues.numericalVertices; + + // vertex did attract + expect(pg2[2][0]).closeTo(1, 1e-12); + expect(pg2[2][1]).closeTo(-4, 1e-12); + + // other vertices did not move + expect(pg2[0][0]).closeTo(vertices2[0][0], 1e-12); + expect(pg2[0][1]).closeTo(vertices2[0][1], 1e-12); + expect(pg2[1][0]).closeTo(vertices2[1][0], 1e-12); + expect(pg2[1][1]).closeTo(vertices2[1][1], 1e-12); + expect(pg2[3][0]).closeTo(vertices2[3][0], 1e-12); + expect(pg2[3][1]).closeTo(vertices2[3][1], 1e-12); + + vertices2[2] = pg2[2]; + + // attract from other side of vertex + await movePolygon({ + name: "/pg2", + pointCoords: { 2: [0.8, -3.8] }, + core, + }); + + stateVariables = await returnAllStateVariables(core); + pg2 = stateVariables["/pg2"].stateValues.numericalVertices; + + // vertices did not move + expect(pg2[0][0]).closeTo(vertices2[0][0], 1e-12); + expect(pg2[0][1]).closeTo(vertices2[0][1], 1e-12); + expect(pg2[1][0]).closeTo(vertices2[1][0], 1e-12); + expect(pg2[1][1]).closeTo(vertices2[1][1], 1e-12); + expect(pg2[2][0]).closeTo(vertices2[2][0], 1e-12); + expect(pg2[2][1]).closeTo(vertices2[2][1], 1e-12); + expect(pg2[3][0]).closeTo(vertices2[3][0], 1e-12); + expect(pg2[3][1]).closeTo(vertices2[3][1], 1e-12); + }); + + it("attract rigid polyline and preserveSimilarity polygon when moving vertices", async () => { + let core = await createTestCore({ + doenetML: ` + + + + + + + + + +

$pg1.vertices

+

$pg2.vertices

+ `, + }); + + // rotate polyline 1 to nearly match slope of nearby polygon edge + await movePolyline({ + name: "/pl", + pointCoords: { 1: [1.6, 2] }, + core, + }); + + let desired_slope = 2; + + let stateVariables = await returnAllStateVariables(core); + + let pl = stateVariables["/pl"].stateValues.numericalVertices; + let actual_slope = (pl[1][1] - pl[2][1]) / (pl[1][0] - pl[2][0]); + + expect(actual_slope).closeTo(desired_slope, 1e-12); + + // if move polyline further away, slope does not attract to polygon edge + stateVariables = await returnAllStateVariables(core); + + let moveX = -1; + let moveY = 1; + + let desired_vertices = stateVariables[ + "/pl" + ].stateValues.numericalVertices.map((vertex) => [ + vertex[0] + moveX, + vertex[1] + moveY, + ]); + await movePolyline({ + name: "/pl", + pointCoords: desired_vertices, + core, + }); + + // perturb vertex slightly + await movePolyline({ + name: "/pl", + pointCoords: { + 1: [ + desired_vertices[1][0] + 0.01, + desired_vertices[1][1] - 0.01, + ], + }, + core, + }); + + desired_slope = 2; + + stateVariables = await returnAllStateVariables(core); + + pl = stateVariables["/pl"].stateValues.numericalVertices; + actual_slope = (pl[1][1] - pl[2][1]) / (pl[1][0] - pl[2][0]); + + expect(actual_slope).closeTo(desired_slope, 0.05); + expect(actual_slope).not.closeTo(desired_slope, 0.001); + + // missing edge of polyline does not attract to slope of polygon edge + stateVariables = await returnAllStateVariables(core); + + moveX = 3.5; + moveY = 0; + + desired_vertices = stateVariables[ + "/pl" + ].stateValues.numericalVertices.map((vertex) => [ + vertex[0] + moveX, + vertex[1] + moveY, + ]); + await movePolyline({ + name: "/pl", + pointCoords: desired_vertices, + core, + }); + + await movePolyline({ + name: "/pl", + pointCoords: { + 1: [2.1, 5.71], + }, + core, + }); + + desired_slope = 2; + + stateVariables = await returnAllStateVariables(core); + + pl = stateVariables["/pl"].stateValues.numericalVertices; + actual_slope = (pl[0][1] - pl[2][1]) / (pl[0][0] - pl[2][0]); + + expect(actual_slope).closeTo(desired_slope, 0.05); + expect(actual_slope).not.closeTo(desired_slope, 0.001); + + // missing edge of polyline does not attract to polygon vertex + await movePolyline({ + name: "/pl", + pointCoords: { + 1: [2.1, 4.3], + }, + core, + }); + + stateVariables = await returnAllStateVariables(core); + + moveX = -0.13; + moveY = 0; + + desired_vertices = stateVariables[ + "/pl" + ].stateValues.numericalVertices.map((vertex) => [ + vertex[0] + moveX, + vertex[1] + moveY, + ]); + await movePolyline({ + name: "/pl", + pointCoords: desired_vertices, + core, + }); + + stateVariables = await returnAllStateVariables(core); + + pl = stateVariables["/pl"].stateValues.numericalVertices; + + // expect point [5,4] of polygon 2 to be nearly but not quite colinear + // with the first and last vertex (so would have attracted if edge existed) + let slope1 = (4 - pl[0][1]) / (5 - pl[0][0]); + let slope2 = (4 - pl[2][1]) / (5 - pl[2][0]); + + expect(slope1).closeTo(slope2, 0.1); + expect(slope1).not.closeTo(slope2, 0.001); + + // rotate polyline to have vertical edge + await movePolyline({ + name: "/pl", + pointCoords: { + 1: [2.3, 6.3], + }, + core, + }); + + stateVariables = await returnAllStateVariables(core); + + pl = stateVariables["/pl"].stateValues.numericalVertices; + + // edge should be vertical + expect(pl[2][0]).closeTo(pl[1][0], 1e-12); + + // polyline's missing edge does not snap to vertical + await movePolyline({ + name: "/pl", + pointCoords: { + 1: [5.8, 4.8], + }, + core, + }); + + stateVariables = await returnAllStateVariables(core); + + pl = stateVariables["/pl"].stateValues.numericalVertices; + + // edge should be vertical + expect(pl[2][0]).closeTo(pl[0][0], 0.1); + expect(pl[2][0]).not.closeTo(pl[0][0], 0.001); + + // move polygon vertex so edge attracts to polyline vertex + await movePolygon({ + name: "/pg", + pointCoords: { 0: [0, 2.4] }, + core, + }); + + stateVariables = await returnAllStateVariables(core); + + pl = stateVariables["/pl"].stateValues.numericalVertices; + let pg = stateVariables["/pg"].stateValues.numericalVertices; + + // vertex 2 of pg1 should be along edge of vertices of 2 and 3 of pg2 + slope1 = (pg[1][1] - pg[0][1]) / (pg[1][0] - pg[0][0]); + slope2 = (pl[0][1] - pg[0][1]) / (pl[0][0] - pg[0][0]); + + expect(slope2).closeTo(slope1, 1e-12); + + // vertex1 should be close to where moved it + expect(pg[0][0]).closeTo(0, 0.1); + expect(pg[0][1]).closeTo(2.4, 0.1); + + // move polygon vertex so edge almost attracts to polyline vertex + await movePolygon({ + name: "/pg", + pointCoords: { 0: [0, 2.2] }, + core, + }); + + stateVariables = await returnAllStateVariables(core); + pg = stateVariables["/pg"].stateValues.numericalVertices; + + // vertex did not attract + expect(pg[0][0]).closeTo(0, 1e-10); + expect(pg[0][1]).closeTo(2.2, 1e-10); + + // move polygon vertex so almost attracts to polyline edge + await movePolygon({ + name: "/pg", + pointCoords: { 0: [4.4, 3.1] }, + core, + }); + + stateVariables = await returnAllStateVariables(core); + pg = stateVariables["/pg"].stateValues.numericalVertices; + + // vertex did not attract + expect(pg[0][0]).closeTo(4.4, 1e-10); + expect(pg[0][1]).closeTo(3.1, 1e-10); + + // move polygon vertex closer so does attract to polyline edge + await movePolygon({ + name: "/pg", + pointCoords: { 0: [4.4, 3.3] }, + core, + }); + + stateVariables = await returnAllStateVariables(core); + + pl = stateVariables["/pl"].stateValues.numericalVertices; + pg = stateVariables["/pg"].stateValues.numericalVertices; + + // vertex 1 of pg should be along edge of vertices of 1 and 2 of pg + slope1 = (pl[1][1] - pl[0][1]) / (pl[1][0] - pl[0][0]); + slope2 = (pg[0][1] - pl[0][1]) / (pg[0][0] - pl[0][0]); + + expect(slope2).closeTo(slope1, 1e-12); + + // move polygon vertex does not attract missing polyline edge + await movePolygon({ + name: "/pg", + pointCoords: { 0: [2.8, 5.2] }, + core, + }); + + stateVariables = await returnAllStateVariables(core); + + pl = stateVariables["/pl"].stateValues.numericalVertices; + pg = stateVariables["/pg"].stateValues.numericalVertices; + + // vertex 1 of pg should close but not exactly along edge of vertices of 1 and 3 of pg + let invslope1 = (pl[2][0] - pl[0][0]) / (pl[2][1] - pl[0][1]); + let invslope2 = (pg[0][0] - pl[0][0]) / (pg[0][1] - pl[0][1]); + + expect(invslope2).closeTo(invslope1, 0.1); + expect(invslope2).not.closeTo(invslope1, 0.001); + + // vertex did not attract + expect(pg[0][0]).closeTo(2.8, 1e-10); + expect(pg[0][1]).closeTo(5.2, 1e-10); + }); + + it("attract line segments", async () => { + let core = await createTestCore({ + doenetML: ` + + + + + + + `, + }); + + // move endpoint of segment 1 to attract to edge of segment 2 + await moveLineSegment({ + name: "/ls1", + point1coords: [-2.2, 2.8], + core, + }); + + let stateVariables = await returnAllStateVariables(core); + + let ls1 = stateVariables["/ls1"].stateValues.numericalEndpoints; + + expect(ls1[0][0]).closeTo(-2, 1e-12); + expect(ls1[0][1]).closeTo(3, 1e-12); + + // move endpoint of segment 1 further away so does not attract to edge of segment 2 + await moveLineSegment({ + name: "/ls1", + point1coords: [-2.5, 2.5], + core, + }); + + stateVariables = await returnAllStateVariables(core); + + ls1 = stateVariables["/ls1"].stateValues.numericalEndpoints; + + expect(ls1[0][0]).closeTo(-2.5, 1e-12); + expect(ls1[0][1]).closeTo(2.5, 1e-12); + + // move endpoint of segment 1 so edge attracts to endpoint of segment 2 + await moveLineSegment({ + name: "/ls1", + point1coords: [-3.2, 1.2], + core, + }); + + stateVariables = await returnAllStateVariables(core); + + ls1 = stateVariables["/ls1"].stateValues.numericalEndpoints; + + let desired_slope = 0.5; + let actual_slope = (ls1[1][1] - ls1[0][1]) / (ls1[1][0] - ls1[0][0]); + + expect(actual_slope).closeTo(desired_slope, 1e-12); + + // move endpoint of segment 1 so edge no longer attracts to endpoint of segment 2 + await moveLineSegment({ + name: "/ls1", + point1coords: [-3.4, 1.4], + core, + }); + + stateVariables = await returnAllStateVariables(core); + + ls1 = stateVariables["/ls1"].stateValues.numericalEndpoints; + + expect(ls1[0][0]).closeTo(-3.4, 1e-12); + expect(ls1[0][1]).closeTo(1.4, 1e-12); + + // extension of edge of segment 1 does not attract to endpoint of segment 2 + await moveLineSegment({ name: "/ls1", point1coords: [0, 2.45], core }); + + stateVariables = await returnAllStateVariables(core); + + ls1 = stateVariables["/ls1"].stateValues.numericalEndpoints; + + expect(ls1[0][0]).closeTo(0, 1e-12); + expect(ls1[0][1]).closeTo(2.45, 1e-12); + + // move endpoint of segment 1 so attracts to endpoint of segment 2 + await moveLineSegment({ + name: "/ls1", + point1coords: [-2.8, 4.2], + core, + }); + + stateVariables = await returnAllStateVariables(core); + + ls1 = stateVariables["/ls1"].stateValues.numericalEndpoints; + + expect(ls1[0][0]).closeTo(-3, 1e-12); + expect(ls1[0][1]).closeTo(4, 1e-12); + + // translate segment 2 so edge attracts to endpoint of segment 1 + let dx = 0.2; + let dy = 0.2; + await moveLineSegment({ + name: "/ls2", + point1coords: [-2 + dx, 3 + dy], + point2coords: [-4 + dx, 5 + dy], + core, + }); + + stateVariables = await returnAllStateVariables(core); + + let ls2 = stateVariables["/ls2"].stateValues.numericalEndpoints; + + expect(ls2[0][0]).closeTo(-2, 1e-12); + expect(ls2[0][1]).closeTo(3, 1e-12); + expect(ls2[1][0]).closeTo(-4, 1e-12); + expect(ls2[1][1]).closeTo(5, 1e-12); + + // translate segment 2 so edge no longer attracts to endpoint of segment 1 + dx = 0.4; + dy = 0.4; + await moveLineSegment({ + name: "/ls2", + point1coords: [-2 + dx, 3 + dy], + point2coords: [-4 + dx, 5 + dy], + core, + }); + + stateVariables = await returnAllStateVariables(core); + + ls2 = stateVariables["/ls2"].stateValues.numericalEndpoints; + + expect(ls2[0][0]).closeTo(-2 + dx, 1e-12); + expect(ls2[0][1]).closeTo(3 + dy, 1e-12); + expect(ls2[1][0]).closeTo(-4 + dx, 1e-12); + expect(ls2[1][1]).closeTo(5 + dy, 1e-12); + + // translate segment 2 so endpoint attracts to edge of segment 1 + dx = 0.0; + dy = 0.4; + await moveLineSegment({ + name: "/ls2", + point1coords: [-1 + dx, 4 + dy], + point2coords: [-3 + dx, 6 + dy], + core, + }); + + stateVariables = await returnAllStateVariables(core); + + ls2 = stateVariables["/ls2"].stateValues.numericalEndpoints; + + expect(ls2[0][0]).closeTo(-1, 1e-12); + expect(ls2[0][1]).closeTo(4, 1e-12); + expect(ls2[1][0]).closeTo(-3, 1e-12); + expect(ls2[1][1]).closeTo(6, 1e-12); + + // translate segment 2 so endpoint no longer attracts to edge of segment 1 + dx = 0.0; + dy = 0.6; + await moveLineSegment({ + name: "/ls2", + point1coords: [-1 + dx, 4 + dy], + point2coords: [-3 + dx, 6 + dy], + core, + }); + + stateVariables = await returnAllStateVariables(core); + + ls2 = stateVariables["/ls2"].stateValues.numericalEndpoints; + + expect(ls2[0][0]).closeTo(-1 + dx, 1e-12); + expect(ls2[0][1]).closeTo(4 + dy, 1e-12); + expect(ls2[1][0]).closeTo(-3 + dx, 1e-12); + expect(ls2[1][1]).closeTo(6 + dy, 1e-12); + + // translate segment 2 so endpoint attracts to endpoint of segment 1 + dx = 0.2; + dy = 0.3; + await moveLineSegment({ + name: "/ls2", + point1coords: [3 + dx, 4 + dy], + point2coords: [1 + dx, 6 + dy], + core, + }); + + stateVariables = await returnAllStateVariables(core); + + ls2 = stateVariables["/ls2"].stateValues.numericalEndpoints; + + expect(ls2[0][0]).closeTo(3, 1e-12); + expect(ls2[0][1]).closeTo(4, 1e-12); + expect(ls2[1][0]).closeTo(1, 1e-12); + expect(ls2[1][1]).closeTo(6, 1e-12); + + // translate segment 2 so endpoint no longer attracts to endpoint of segment 1 + dx = 0.5; + dy = 0.3; + await moveLineSegment({ + name: "/ls2", + point1coords: [3 + dx, 4 + dy], + point2coords: [1 + dx, 6 + dy], + core, + }); + + stateVariables = await returnAllStateVariables(core); + + ls2 = stateVariables["/ls2"].stateValues.numericalEndpoints; + + expect(ls2[0][0]).closeTo(3 + dx, 1e-12); + expect(ls2[0][1]).closeTo(4 + dy, 1e-12); + expect(ls2[1][0]).closeTo(1 + dx, 1e-12); + expect(ls2[1][1]).closeTo(6 + dy, 1e-12); + }); +}); diff --git a/packages/doenetml-worker/src/test/tagSpecific/subsetofreals.test.ts b/packages/doenetml-worker/src/test/tagSpecific/subsetofreals.test.ts new file mode 100644 index 000000000..ae7e5976c --- /dev/null +++ b/packages/doenetml-worker/src/test/tagSpecific/subsetofreals.test.ts @@ -0,0 +1,1410 @@ +import { describe, expect, it, vi } from "vitest"; +import { createTestCore, returnAllStateVariables } from "../utils/test-core"; +import { cleanLatex } from "../utils/math"; +import { updateMathInputValue, updateSelectedIndices } from "../utils/actions"; +import Core from "../../Core"; +import me from "math-expressions"; + +const Mock = vi.fn(); +vi.stubGlobal("postMessage", Mock); +vi.mock("hyperformula"); + +function createInterval(string: string) { + return me.fromText(string).to_intervals().tree; +} + +describe("SubsetOfReals tag tests", async () => { + async function test_display_as_interval({ + core, + repeat = [], + includeNe = false, + }: { + core: Core; + repeat?: string[]; + includeNe?: boolean; + }) { + const limits: Record = { + "1": [4, 5], + "2": [5, 4], + "3": [5, 5], + "4": [4, Infinity], + "5": [4, -Infinity], + "6": [-Infinity, 5], + "7": [Infinity, 5], + "8": [-Infinity, Infinity], + "9": ["a", "b"], + }; + + for (let ind of repeat) { + limits[`${ind}a`] = limits[ind]; + } + + let closedByPre = { + "/o": [false, false], + "/c": [true, true], + "/oc": [false, true], + "/co": [true, false], + }; + let allPre = ["/o", "/c", "/oc", "/co"]; + + const stateVariables = await returnAllStateVariables(core); + + for (let ind in limits) { + let lim = limits[ind]; + if (typeof lim[0] === "string" || typeof lim[1] === "string") { + for (let pre of allPre) { + expect( + stateVariables[`${pre}${ind}`].stateValues.value.tree, + ).eq("\uff3f"); + } + } else if (lim[0] === -Infinity && lim[1] === Infinity) { + for (let pre of allPre) { + expect( + stateVariables[`${pre}${ind}`].stateValues.value.tree, + ).eq("R"); + } + } else if (lim[0] > lim[1]) { + for (let pre of allPre) { + expect( + stateVariables[`${pre}${ind}`].stateValues.value.tree, + ).eq("emptyset"); + } + } else if (lim[0] < lim[1]) { + for (let pre of allPre) { + let closed = [...closedByPre[pre]]; + // don't have closed intervals at infinity + if (lim[0] === -Infinity) { + closed[0] = false; + } + if (lim[1] === Infinity) { + closed[1] = false; + } + console.log({ pre, closed }); + expect( + stateVariables[`${pre}${ind}`].stateValues.value.tree, + ).eqls([ + "interval", + ["tuple", ...lim], + ["tuple", ...closed], + ]); + } + } else { + // equal limits + for (let pre of allPre) { + if (pre === "/c") { + expect( + stateVariables[`${pre}${ind}`].stateValues.value + .tree, + ).eqls(["set", lim[0]]); + } else { + expect( + stateVariables[`${pre}${ind}`].stateValues.value + .tree, + ).eq("emptyset"); + } + } + } + } + + if (includeNe) { + let neNumbers: Record = { + "1": 6, + "2": Infinity, + "3": -Infinity, + "4": 6, + "5": Infinity, + "6": -Infinity, + }; + + for (let ind in neNumbers) { + let num = neNumbers[ind]; + + if (Number.isFinite(num)) { + expect( + stateVariables[`/ne${ind}`].stateValues.value.tree, + ).eqls([ + "union", + createInterval(`(-infinity, ${num})`), + createInterval(`(${num}, infinity)`), + ]); + } else { + expect( + stateVariables[`/ne${ind}`].stateValues.value.tree, + ).eq("R"); + } + } + } + } + + it("single intervals", async () => { + let core = await createTestCore({ + doenetML: ` +

(4,5)

+

(5,4)

+

(5,5)

+

(4,infinity)

+

(4,-infinity)

+

(-infinity,5)

+

(infinity,5)

+

(-infinity,infinity)

+

(a,b)

+ +

[4,5]

+

[5,4]

+

[5,5]

+

[4,infinity]

+

[4,-infinity]

+

[-infinity,5]

+

[infinity,5]

+

[-infinity,infinity]

+

[a,b]

+ +

(4,5]

+

(5,4]

+

(5,5]

+

(4,infinity]

+

(4,-infinity]

+

(-infinity,5]

+

(infinity,5]

+

(-infinity,infinity]

+

(a,b]

+ +

[4,5)

+

[5,4)

+

[5,5)

+

[4,infinity)

+

[4,-infinity)

+

[-infinity,5)

+

[infinity,5)

+

[-infinity,infinity)

+

[a,b)

+ `, + }); + + await test_display_as_interval({ core }); + }); + + async function test_display_as_inequality( + core: Core, + variable: string = "x", + ) { + const limits = [ + [4, 5], + [5, 4], + [5, 5], + [4, Infinity], + [4, -Infinity], + [-Infinity, 5], + [Infinity, 5], + [-Infinity, Infinity], + ["a", "b"], + ]; + + let closedByPre = { + "/o": [false, false], + "/c": [true, true], + "/oc": [false, true], + "/co": [true, false], + }; + let allPre = ["/o", "/c", "/oc", "/co"]; + + const stateVariables = await returnAllStateVariables(core); + + for (let [ind, lim] of limits.entries()) { + if (typeof lim[0] === "string" || typeof lim[1] === "string") { + for (let pre of allPre) { + expect( + stateVariables[`${pre}${ind + 1}`].stateValues.value + .tree, + ).eq("\uff3f"); + } + } else if (lim[0] === -Infinity && lim[1] === Infinity) { + for (let pre of allPre) { + expect( + stateVariables[`${pre}${ind + 1}`].stateValues.value + .tree, + ).eqls(["in", variable, "R"]); + } + } else if (lim[0] > lim[1]) { + for (let pre of allPre) { + expect( + stateVariables[`${pre}${ind + 1}`].stateValues.value + .tree, + ).eqls(["in", variable, "emptyset"]); + } + } else if (lim[0] < lim[1]) { + for (let pre of allPre) { + let strict = closedByPre[pre].map((v) => !v); + if (lim[0] === -Infinity) { + let op = strict[1] ? "<" : "le"; + expect( + stateVariables[`${pre}${ind + 1}`].stateValues.value + .tree, + ).eqls([op, variable, lim[1]]); + } else if (lim[1] === Infinity) { + let op = strict[0] ? ">" : "ge"; + expect( + stateVariables[`${pre}${ind + 1}`].stateValues.value + .tree, + ).eqls([op, variable, lim[0]]); + } else { + expect( + stateVariables[`${pre}${ind + 1}`].stateValues.value + .tree, + ).eqls([ + "lts", + ["tuple", lim[0], variable, lim[1]], + ["tuple", ...strict], + ]); + } + } + } else { + // equal limits + for (let pre of allPre) { + if (pre === "/c") { + expect( + stateVariables[`${pre}${ind + 1}`].stateValues.value + .tree, + ).eqls(["=", variable, lim[0]]); + } else { + expect( + stateVariables[`${pre}${ind + 1}`].stateValues.value + .tree, + ).eqls(["in", variable, "emptyset"]); + } + } + } + } + } + + it("single intervals, display as inequality", async () => { + let core = await createTestCore({ + doenetML: ` +

(4,5)

+

(5,4)

+

(5,5)

+

(4,infinity)

+

(4,-infinity)

+

(-infinity,5)

+

(infinity,5)

+

(-infinity,infinity)

+

(a,b)

+ +

[4,5]

+

[5,4]

+

[5,5]

+

[4,infinity]

+

[4,-infinity]

+

[-infinity,5]

+

[infinity,5]

+

[-infinity,infinity]

+

[a,b]

+ +

(4,5]

+

(5,4]

+

(5,5]

+

(4,infinity]

+

(4,-infinity]

+

(-infinity,5]

+

(infinity,5]

+

(-infinity,infinity]

+

(a,b]

+ +

[4,5)

+

[5,4)

+

[5,5)

+

[4,infinity)

+

[4,-infinity)

+

[-infinity,5)

+

[infinity,5)

+

[-infinity,infinity)

+

[a,b)

+ `, + }); + + await test_display_as_inequality(core); + }); + + it("single intervals, display as inequality, change variable", async () => { + let core = await createTestCore({ + doenetML: ` +

(4,5)

+

(5,4)

+

(5,5)

+

(4,infinity)

+

(4,-infinity)

+

(-infinity,5)

+

(infinity,5)

+

(-infinity,infinity)

+

(a,b)

+ +

[4,5]

+

[5,4]

+

[5,5]

+

[4,infinity]

+

[4,-infinity]

+

[-infinity,5]

+

[infinity,5]

+

[-infinity,infinity]

+

[a,b]

+ +

(4,5]

+

(5,4]

+

(5,5]

+

(4,infinity]

+

(4,-infinity]

+

(-infinity,5]

+

(infinity,5]

+

(-infinity,infinity]

+

(a,b]

+ +

[4,5)

+

[5,4)

+

[5,5)

+

[4,infinity)

+

[4,-infinity)

+

[-infinity,5)

+

[infinity,5)

+

[-infinity,infinity)

+

[a,b)

+ `, + }); + + await test_display_as_inequality(core, "v"); + }); + + it("single inequality", async () => { + let core = await createTestCore({ + doenetML: ` +

4 < x < 5

+

5 < x < 4

+

5 < x < 5

+

x > 4

+

infinity > x > 4

+

x < -infinity

+

x < 5

+

-infinity < x < 5

+

x > infinity

+

-infinity < x < infinity

+

x < a

+ +

4 <= x <= 5

+

5 <= x <= 4

+

5 <= x <= 5

+

x >= 4

+

infinity >= x >= 4

+

x <= -infinity

+

x <= 5

+

-infinity <= x <= 5

+

x >= infinity

+

-infinity <= x <= infinity

+

x <= a

+ +

4 < x <= 5

+

5 < x <= 4

+

5 < x <= 5

+

x > 4

+

infinity >= x > 4

+

x <= -infinity

+

x <= 5

+

-infinity < x <= 5

+

x > infinity

+

-infinity < x <= infinity

+

x <= a

+ +

4 <= x < 5

+

5 <= x < 4

+

5 <= x < 5

+

x >= 4

+

infinity > x >= 4

+

x < -infinity

+

x < 5

+

-infinity <= x < 5

+

x >= infinity

+

-infinity <= x < infinity

+

x < a

+ +

x != 6

+

x != infinity

+

x != -infinity

+

6 != x

+

infinity != x

+

-infinity != x

+ `, + }); + + await test_display_as_interval({ + core, + repeat: ["4", "6"], + includeNe: true, + }); + }); + + it("single inequality, change variable", async () => { + let core = await createTestCore({ + doenetML: ` +

4 < q < 5

+

5 < q < 4

+

5 < q < 5

+

q > 4

+

infinity > q > 4

+

q < -infinity

+

q < 5

+

-infinity < q < 5

+

q > infinity

+

-infinity < q < infinity

+

q < a

+ +

4 <= q <= 5

+

5 <= q <= 4

+

5 <= q <= 5

+

q >= 4

+

infinity >= q >= 4

+

q <= -infinity

+

q <= 5

+

-infinity <= q <= 5

+

q >= infinity

+

-infinity <= q <= infinity

+

q <= a

+ +

4 < q <= 5

+

5 < q <= 4

+

5 < q <= 5

+

q > 4

+

infinity >= q > 4

+

q <= -infinity

+

q <= 5

+

-infinity < q <= 5

+

q > infinity

+

-infinity < q <= infinity

+

q <= a

+ +

4 <= q < 5

+

5 <= q < 4

+

5 <= q < 5

+

q >= 4

+

infinity > q >= 4

+

q < -infinity

+

q < 5

+

-infinity <= q < 5

+

q >= infinity

+

-infinity <= q < infinity

+

q < a

+ +

q != 6

+

q != infinity

+

q != -infinity

+

6 != q

+

infinity != q

+

-infinity != q

+ `, + }); + + await test_display_as_interval({ + core, + repeat: ["4", "6"], + includeNe: true, + }); + }); + + it("single inequality, in set notation", async () => { + let core = await createTestCore({ + doenetML: ` +

{q | 4 < q < 5}

+

{q | 5 < q < 4}

+

{q | 5 < q < 5}

+

{q | q > 4}

+

{q | infinity > q > 4}

+

{q | q < -infinity}

+

{q | q < 5}

+

{q | -infinity < q < 5}

+

{q | q > infinity}

+

{q | -infinity < q < infinity}

+

{q | q < a}

+ +

{q | 4 <= q <= 5}

+

{q | 5 <= q <= 4}

+

{q | 5 <= q <= 5}

+

{q | q >= 4}

+

{q | infinity >= q >= 4}

+

{q | q <= -infinity}

+

{q | q <= 5}

+

{q | -infinity <= q <= 5}

+

{q | q >= infinity}

+

{q | -infinity <= q <= infinity}

+

{q | q <= a}

+ +

{q | 4 < q <= 5}

+

{q | 5 < q <= 4}

+

{q | 5 < q <= 5}

+

{q | q > 4}

+

{q | infinity >= q > 4}

+

{q | q <= -infinity}

+

{q | q <= 5}

+

{q | -infinity < q <= 5}

+

{q | q > infinity}

+

{q | -infinity < q <= infinity}

+

{q | q <= a}

+ +

{q | 4 <= q < 5}

+

{q | 5 <= q < 4}

+

{q | 5 <= q < 5}

+

{q | q >= 4}

+

{q | infinity > q >= 4}

+

{q | q < -infinity}

+

{q | q < 5}

+

{q | -infinity <= q < 5}

+

{q | q >= infinity}

+

{q | -infinity <= q < infinity}

+

{q | q < a}

+ +

{q | q != 6}

+

{q | q != infinity}

+

{q | q != -infinity}

+

{q | 6 != q}

+

{q | infinity != q}

+

{q | -infinity != q}

+ `, + }); + + await test_display_as_interval({ + core, + repeat: ["4", "6"], + includeNe: true, + }); + }); + + it("single equality", async () => { + let core = await createTestCore({ + doenetML: ` +

x=5

+

x=infinity

+

x=-infinity

+

x=a

+ + + `, + }); + + const stateVariables = await returnAllStateVariables(core); + + expect(stateVariables["/e1"].stateValues.value.tree).eqls(["set", 5]); + expect(stateVariables["/e2"].stateValues.value.tree).eq("emptyset"); + expect(stateVariables["/e3"].stateValues.value.tree).eq("emptyset"); + expect(stateVariables["/e4"].stateValues.value.tree).eq("\uff3f"); + }); + + async function test_union_interactions(core: Core) { + const res = { + "/u1": ["union", createInterval("(4,5)"), createInterval("(6,7)")], + "/u2": ["union", createInterval("(4,5)"), createInterval("(5,6)")], + "/u3": createInterval("(4,6]"), + "/u4": createInterval("(4,6]"), + "/u5": createInterval("(4,7)"), + "/u6": createInterval("(4,8)"), + "/u7": createInterval("(4,7]"), + "/u8": createInterval("[4,6)"), + "/u9": createInterval("(4,6)"), + "/u10": createInterval("(4,6]"), + "/u11": ["union", createInterval("(4,6)"), ["set", 7]], + "/u12": createInterval("(4,6)"), + "/u13": "R", + "/u14": "R", + "/u15": "R", + "/u16": "R", + "/u17": "R", + "/u18": createInterval("[-4,-2)"), + "/i1": "emptyset", + "/i2": "emptyset", + "/i3": "emptyset", + "/i4": ["set", 5], + "/i5": createInterval("(5,6)"), + "/i6": createInterval("(5,7)"), + "/i7": createInterval("(5,7)"), + "/i8": "emptyset", + "/i9": ["set", 5], + "/i10": "emptyset", + "/i11": "emptyset", + "/i12": "emptyset", + "/i13": createInterval("(2,5)"), + "/i14": createInterval("[2,5)"), + "/i15": createInterval("(2,5]"), + "/i16": createInterval("[2,5]"), + "/i17": "emptyset", + "/i18": ["set", -4], + }; + + const stateVariables = await returnAllStateVariables(core); + + for (let name in res) { + expect(stateVariables[name].stateValues.value.tree).eqls(res[name]); + } + } + + it("union and intersections of intervals and singletons", async () => { + let core = await createTestCore({ + doenetML: ` +

(4,5) union (6,7)

+

(4,5) union (5,6)

+

(4,5] union (5,6]

+

(4,5] union [5,6]

+

(4,6) union (5,7)

+

(4,8) union (5,7)

+

(4,7) union (5,7]

+

(4,6) union {4}

+

(4,6) union {5}

+

(4,6) union {6}

+

(4,6) union {7}

+

(4,5) union (5,6) union {5}

+

(-infinity,5) union (2,infinity)

+

(-infinity,5) union [2,infinity)

+

(-infinity,5] union (2,infinity)

+

(-infinity,5] union [2,infinity)

+

(-infinity,5) union (9,infinity) union (4,10)

+

[-4,-2) union {-4}

+ +

(4,5) intersect (6,7)

+

(4,5) intersect (5,6)

+

(4,5] intersect (5,6]

+

(4,5] intersect [5,6]

+

(4,6) intersect (5,7)

+

(4,8) intersect (5,7)

+

(4,7) intersect (5,7]

+

(4,6) intersect {4}

+

(4,6) intersect {5}

+

(4,6) intersect {6}

+

(4,6) intersect {7}

+

(4,5) intersect (5,6) intersect {5}

+

(-infinity,5) intersect (2,infinity)

+

(-infinity,5) intersect [2,infinity)

+

(-infinity,5] intersect (2,infinity)

+

(-infinity,5] intersect [2,infinity)

+

(-infinity,5) intersect (9,infinity) intersect (4,10)

+

[-4,-2) intersect {-4}

+ `, + }); + + await test_union_interactions(core); + }); + + it("union and intersections of intervals and singletons, latex format", async () => { + let core = await createTestCore({ + doenetML: ` +

(4,5) \\cup (6,7)

+

(4,5) \\cup (5,6)

+

(4,5] \\cup (5,6]

+

(4,5] \\cup [5,6]

+

(4,6) \\cup (5,7)

+

(4,8) \\cup (5,7)

+

(4,7) \\cup (5,7]

+

(4,6) \\cup {4}

+

(4,6) \\cup {5}

+

(4,6) \\cup {6}

+

(4,6) \\cup {7}

+

(4,5) \\cup (5,6) \\cup {5}

+

(-\\infty,5) \\cup (2,\\infty)

+

(-\\infty,5) \\cup [2,\\infty)

+

(-\\infty,5] \\cup (2,\\infty)

+

(-\\infty,5] \\cup [2,\\infty)

+

(-\\infty,5) \\cup (9,\\infty) \\cup (4,10)

+

[-4,-2) \\cup {-4}

+ +

(4,5) \\cap (6,7)

+

(4,5) \\cap (5,6)

+

(4,5] \\cap (5,6]

+

(4,5] \\cap [5,6]

+

(4,6) \\cap (5,7)

+

(4,8) \\cap (5,7)

+

(4,7) \\cap (5,7]

+

(4,6) \\cap {4}

+

(4,6) \\cap {5}

+

(4,6) \\cap {6}

+

(4,6) \\cap {7}

+

(4,5) \\cap (5,6) \\cap {5}

+

(-\\infty,5) \\cap (2,\\infty)

+

(-\\infty,5) \\cap [2,\\infty)

+

(-\\infty,5] \\cap (2,\\infty)

+

(-\\infty,5] \\cap [2,\\infty)

+

(-\\infty,5) \\cap (9,\\infty) \\cap (4,10)

+

[-4,-2) \\cap {-4}

+ `, + }); + + await test_union_interactions(core); + }); + + it("x element of union and intersections of intervals and singletons", async () => { + let core = await createTestCore({ + doenetML: ` +

x elementof (4,5) union (6,7)

+

x elementof (4,5) union (5,6)

+

x elementof (4,5] union (5,6]

+

x elementof (4,5] union [5,6]

+

x elementof (4,6) union (5,7)

+

x elementof (4,8) union (5,7)

+

x elementof (4,7) union (5,7]

+

x elementof (4,6) union {4}

+

x elementof (4,6) union {5}

+

x elementof (4,6) union {6}

+

x elementof (4,6) union {7}

+

x elementof (4,5) union (5,6) union {5}

+

x elementof (-infinity,5) union (2,infinity)

+

x elementof (-infinity,5) union [2,infinity)

+

x elementof (-infinity,5] union (2,infinity)

+

x elementof (-infinity,5] union [2,infinity)

+

x elementof (-infinity,5) union (9,infinity) union (4,10)

+

x elementof [-4,-2) union {-4}

+ +

x elementof (4,5) intersect (6,7)

+

x elementof (4,5) intersect (5,6)

+

x elementof (4,5] intersect (5,6]

+

x elementof (4,5] intersect [5,6]

+

x elementof (4,6) intersect (5,7)

+

x elementof (4,8) intersect (5,7)

+

x elementof (4,7) intersect (5,7]

+

x elementof (4,6) intersect {4}

+

x elementof (4,6) intersect {5}

+

x elementof (4,6) intersect {6}

+

x elementof (4,6) intersect {7}

+

x elementof (4,5) intersect (5,6) intersect {5}

+

x elementof (-infinity,5) intersect (2,infinity)

+

x elementof (-infinity,5) intersect [2,infinity)

+

x elementof (-infinity,5] intersect (2,infinity)

+

x elementof (-infinity,5] intersect [2,infinity)

+

x elementof (-infinity,5) intersect (9,infinity) intersect (4,10)

+

x elementof [-4,-2) intersect {-4}

+ `, + }); + + await test_union_interactions(core); + }); + + it("union and intersections of intervals and singletons contains element x", async () => { + let core = await createTestCore({ + doenetML: ` +

(4,5) union (6,7) containselement x

+

(4,5) union (5,6) containselement x

+

(4,5] union (5,6] containselement x

+

(4,5] union [5,6] containselement x

+

(4,6) union (5,7) containselement x

+

(4,8) union (5,7) containselement x

+

(4,7) union (5,7] containselement x

+

(4,6) union {4} containselement x

+

(4,6) union {5} containselement x

+

(4,6) union {6} containselement x

+

(4,6) union {7} containselement x

+

(4,5) union (5,6) union {5} containselement x

+

(-infinity,5) union (2,infinity) containselement x

+

(-infinity,5) union [2,infinity) containselement x

+

(-infinity,5] union (2,infinity) containselement x

+

(-infinity,5] union [2,infinity) containselement x

+

(-infinity,5) union (9,infinity) union (4,10) containselement x

+

[-4,-2) union {-4} containselement x

+ +

(4,5) intersect (6,7) containselement x

+

(4,5) intersect (5,6) containselement x

+

(4,5] intersect (5,6] containselement x

+

(4,5] intersect [5,6] containselement x

+

(4,6) intersect (5,7) containselement x

+

(4,8) intersect (5,7) containselement x

+

(4,7) intersect (5,7] containselement x

+

(4,6) intersect {4} containselement x

+

(4,6) intersect {5} containselement x

+

(4,6) intersect {6} containselement x

+

(4,6) intersect {7} containselement x

+

(4,5) intersect (5,6) intersect {5} containselement x

+

(-infinity,5) intersect (2,infinity) containselement x

+

(-infinity,5) intersect [2,infinity) containselement x

+

(-infinity,5] intersect (2,infinity) containselement x

+

(-infinity,5] intersect [2,infinity) containselement x

+

(-infinity,5) intersect (9,infinity) intersect (4,10) containselement x

+

[-4,-2) intersect {-4} containselement x

+ `, + }); + + await test_union_interactions(core); + }); + + it("x in union and intersections of intervals and singletons, latex format", async () => { + let core = await createTestCore({ + doenetML: ` +

x \\in (4,5) \\cup (6,7)

+

x \\in (4,5) \\cup (5,6)

+

x \\in (4,5] \\cup (5,6]

+

x \\in (4,5] \\cup [5,6]

+

x \\in (4,6) \\cup (5,7)

+

x \\in (4,8) \\cup (5,7)

+

x \\in (4,7) \\cup (5,7]

+

x \\in (4,6) \\cup {4}

+

x \\in (4,6) \\cup {5}

+

x \\in (4,6) \\cup {6}

+

x \\in (4,6) \\cup {7}

+

x \\in (4,5) \\cup (5,6) \\cup {5}

+

x \\in (-\\infty,5) \\cup (2,\\infty)

+

x \\in (-\\infty,5) \\cup [2,\\infty)

+

x \\in (-\\infty,5] \\cup (2,\\infty)

+

x \\in (-\\infty,5] \\cup [2,\\infty)

+

x \\in (-\\infty,5) \\cup (9,\\infty) \\cup (4,10)

+

x \\in [-4,-2) \\cup {-4}

+ +

x \\in (4,5) \\cap (6,7)

+

x \\in (4,5) \\cap (5,6)

+

x \\in (4,5] \\cap (5,6]

+

x \\in (4,5] \\cap [5,6]

+

x \\in (4,6) \\cap (5,7)

+

x \\in (4,8) \\cap (5,7)

+

x \\in (4,7) \\cap (5,7]

+

x \\in (4,6) \\cap {4}

+

x \\in (4,6) \\cap {5}

+

x \\in (4,6) \\cap {6}

+

x \\in (4,6) \\cap {7}

+

x \\in (4,5) \\cap (5,6) \\cap {5}

+

x \\in (-\\infty,5) \\cap (2,\\infty)

+

x \\in (-\\infty,5) \\cap [2,\\infty)

+

x \\in (-\\infty,5] \\cap (2,\\infty)

+

x \\in (-\\infty,5] \\cap [2,\\infty)

+

x \\in (-\\infty,5) \\cap (9,\\infty) \\cap (4,10)

+

x \\in [-4,-2) \\cap {-4}

+ `, + }); + + await test_union_interactions(core); + }); + + it("union and intersections of intervals and singletons ni x, latex format", async () => { + let core = await createTestCore({ + doenetML: ` +

(4,5) \\cup (6,7) \\ni x

+

(4,5) \\cup (5,6) \\ni x

+

(4,5] \\cup (5,6] \\ni x

+

(4,5] \\cup [5,6] \\ni x

+

(4,6) \\cup (5,7) \\ni x

+

(4,8) \\cup (5,7) \\ni x

+

(4,7) \\cup (5,7] \\ni x

+

(4,6) \\cup {4} \\ni x

+

(4,6) \\cup {5} \\ni x

+

(4,6) \\cup {6} \\ni x

+

(4,6) \\cup {7} \\ni x

+

(4,5) \\cup (5,6) \\cup {5} \\ni x

+

(-\\infty,5) \\cup (2,\\infty) \\ni x

+

(-\\infty,5) \\cup [2,\\infty) \\ni x

+

(-\\infty,5] \\cup (2,\\infty) \\ni x

+

(-\\infty,5] \\cup [2,\\infty) \\ni x

+

(-\\infty,5) \\cup (9,\\infty) \\cup (4,10) \\ni x

+

[-4,-2) \\cup {-4} \\ni x

+ +

(4,5) \\cap (6,7) \\ni x

+

(4,5) \\cap (5,6) \\ni x

+

(4,5] \\cap (5,6] \\ni x

+

(4,5] \\cap [5,6] \\ni x

+

(4,6) \\cap (5,7) \\ni x

+

(4,8) \\cap (5,7) \\ni x

+

(4,7) \\cap (5,7] \\ni x

+

(4,6) \\cap {4} \\ni x

+

(4,6) \\cap {5} \\ni x

+

(4,6) \\cap {6} \\ni x

+

(4,6) \\cap {7} \\ni x

+

(4,5) \\cap (5,6) \\cap {5} \\ni x

+

(-\\infty,5) \\cap (2,\\infty) \\ni x

+

(-\\infty,5) \\cap [2,\\infty) \\ni x

+

(-\\infty,5] \\cap (2,\\infty) \\ni x

+

(-\\infty,5] \\cap [2,\\infty) \\ni x

+

(-\\infty,5) \\cap (9,\\infty) \\cap (4,10) \\ni x

+

[-4,-2) \\cap {-4} \\ni x

+ `, + }); + + await test_union_interactions(core); + }); + + it("ands and ors with inequalities", async () => { + let core = await createTestCore({ + doenetML: ` +

(4 < x < 5) or (6 < x < 7)

+

(4 < x < 5) or (5 < x < 6)

+

(4 < x <= 5) or (5 < x <= 6)

+

(4 < x <= 5) or (5 <= x <= 6)

+

(4 < x < 6) or (5 < x < 7)

+

(4 < x < 8) or (5 < x < 7)

+

(4 < x < 7) or (5 < x <= 7)

+

(4 < x < 6) or (x = 4)

+

(4 < x < 6) or (x = 5)

+

(4 < x < 6) or (x = 6)

+

(4 < x < 6) or (x = 7)

+

(4 < x < 5) or (5 < x < 6) or (x = 5)

+

(x < 5) or (x > 2)

+

(x < 5) or (x >= 2)

+

(x <= 5) or (x > 2)

+

(x <= 5) or (x >= 2)

+

(x < 5) or (x > 9) or (4 < x < 10)

+

(x != 5) or (4 < x < 10)

+ +

(4 < x < 5) and (6 < x < 7)

+

(4 < x < 5) and (5 < x < 6)

+

(4 < x <= 5) and (5 < x <= 6)

+

(4 < x <= 5) and (5 <= x <= 6)

+

(4 < x < 6) and (5 < x < 7)

+

(4 < x < 8) and (5 < x < 7)

+

(4 < x < 7) and (5 < x <= 7)

+

(4 < x < 6) and (x = 4)

+

(4 < x < 6) and (x = 5)

+

(4 < x < 6) and (x = 6)

+

(4 < x < 6) and (x = 7)

+

(4 < x < 5) and (5 < x < 6) and (x = 5)

+

(x < 5) and (x > 2)

+

(x < 5) and (x >= 2)

+

(x <= 5) and (x > 2)

+

(x <= 5) and (x >= 2)

+

(x < 5) and (x > 9) and (4 < x < 10)

+

(x != 5) and (4 < x < 10)

+ + + `, + }); + + const res = { + "/o1": ["union", createInterval("(4,5)"), createInterval("(6,7)")], + "/o2": ["union", createInterval("(4,5)"), createInterval("(5,6)")], + "/o3": createInterval("(4,6]"), + "/o4": createInterval("(4,6]"), + "/o5": createInterval("(4,7)"), + "/o6": createInterval("(4,8)"), + "/o7": createInterval("(4,7]"), + "/o8": createInterval("[4,6)"), + "/o9": createInterval("(4,6)"), + "/o10": createInterval("(4,6]"), + "/o11": ["union", createInterval("(4,6)"), ["set", 7]], + "/o12": createInterval("(4,6)"), + "/o13": "R", + "/o14": "R", + "/o15": "R", + "/o16": "R", + "/o17": "R", + "/o18": "R", + + "/a1": "emptyset", + "/a2": "emptyset", + "/a3": "emptyset", + "/a4": createInterval("{5}"), + "/a5": createInterval("(5,6)"), + "/a6": createInterval("(5,7)"), + "/a7": createInterval("(5,7)"), + "/a8": "emptyset", + "/a9": ["set", 5], + "/a10": "emptyset", + "/a11": "emptyset", + "/a12": "emptyset", + "/a13": createInterval("(2,5)"), + "/a14": createInterval("[2,5)"), + "/a15": createInterval("(2,5]"), + "/a16": createInterval("[2,5]"), + "/a17": "emptyset", + "/a18": [ + "union", + createInterval("(4,5)"), + createInterval("(5,10)"), + ], + }; + + const stateVariables = await returnAllStateVariables(core); + + for (let name in res) { + expect(stateVariables[name].stateValues.value.tree).eqls(res[name]); + } + }); + + it("complements of intervals and singletons", async () => { + let core = await createTestCore({ + doenetML: ` +

(4,5)^c

+

(4,5)^C

+

(4,5]^c

+

(4,5]^C

+

[4,5)^c

+

[4,5)^C

+

[4,5]^c

+

[4,5]^C

+

{4}^c

+

{4}^C

+

((4,5) union (5,6))^c

+

(4,6)^c or (5,7)^c

+ + + `, + }); + + const res = { + "/c1": ["union", createInterval("(−∞,4]"), createInterval("[5,∞)")], + "/c2": ["union", createInterval("(−∞,4]"), createInterval("[5,∞)")], + "/c3": ["union", createInterval("(−∞,4]"), createInterval("(5,∞)")], + "/c4": ["union", createInterval("(−∞,4]"), createInterval("(5,∞)")], + "/c5": ["union", createInterval("(−∞,4)"), createInterval("[5,∞)")], + "/c6": ["union", createInterval("(−∞,4)"), createInterval("[5,∞)")], + "/c7": ["union", createInterval("(−∞,4)"), createInterval("(5,∞)")], + "/c8": ["union", createInterval("(−∞,4)"), createInterval("(5,∞)")], + "/c9": ["union", createInterval("(−∞,4)"), createInterval("(4,∞)")], + "/c10": [ + "union", + createInterval("(−∞,4)"), + createInterval("(4,∞)"), + ], + "/c11": [ + "union", + createInterval("(−∞,4]"), + ["set", 5], + createInterval("[6,∞)"), + ], + "/c12": [ + "union", + createInterval("(−∞,5]"), + createInterval("[6,∞)"), + ], + }; + + const stateVariables = await returnAllStateVariables(core); + + for (let name in res) { + expect(stateVariables[name].stateValues.value.tree).eqls(res[name]); + } + }); + + it("dynamic subsets", async () => { + let core = await createTestCore({ + doenetML: ` +

Variable:

+

Input:

+

Display mode:

+ + intervals + inequalities + +

Result: $input

+ + + `, + }); + + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/result"].stateValues.text).eq("( 1, ∞ )"); + + await updateSelectedIndices({ + name: "/displayMode", + selectedIndices: [2], + core, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/result"].stateValues.text).eq("x > 1"); + + await updateMathInputValue({ latex: "y", name: "/variable", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/result"].stateValues.text).eq("_"); + + await updateMathInputValue({ latex: "y>1", name: "/input", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/result"].stateValues.text).eq("y > 1"); + + await updateSelectedIndices({ + name: "/displayMode", + selectedIndices: [1], + core, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/result"].stateValues.text).eq("( 1, ∞ )"); + + await updateMathInputValue({ latex: "y \\ne 1", name: "/input", core }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/result"].stateValues.text).eq( + "( -∞, 1 ) ∪ ( 1, ∞ )", + ); + + await updateMathInputValue({ + latex: "(y>1)\\land(y<3)", + name: "/input", + core, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/result"].stateValues.text).eq("( 1, 3 )"); + + await updateSelectedIndices({ + name: "/displayMode", + selectedIndices: [2], + core, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/result"].stateValues.text).eq("1 < y < 3"); + + await updateMathInputValue({ + latex: "(y>1)\\land(y<3)\\lor(y>6)", + name: "/input", + core, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/result"].stateValues.text).eq( + "(1 < y < 3) or (y > 6)", + ); + + await updateSelectedIndices({ + name: "/displayMode", + selectedIndices: [1], + core, + }); + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/result"].stateValues.text).eq( + "( 1, 3 ) ∪ ( 6, ∞ )", + ); + }); + + it("modifying copies of subsets", async () => { + let core = await createTestCore({ + doenetML: ` +

Enter subset:

+

Subset 1: $input0

+

Subset 2: $s1{name="s2"}

+

Subset 3: $s1.value{assignNames="s3"}

+

Subset 4: $s2{name="s4"}

+

Subset 5: $s2.value{assignNames="s5"}

+

Subset 6: $s3{name="s6"}

+

Subset 7: $s3.value{assignNames="s7"}

+

Subset 8: $s1

+

Subset 9: $(s1.value)

+

Modify subset 1:

+

Modify subset 2:

+

Modify subset 3:

+

Modify subset 4:

+

Modify subset 5:

+

Modify subset 6:

+

Modify subset 7:

+

Modify subset 8:

+

Modify subset 9:

+ + `, + }); + + async function check_items(str: string, str0?: string) { + if (str0 === undefined) { + str0 = str; + } + + const stateVariables = await returnAllStateVariables(core); + + for (let i = 1; i <= 9; i++) { + expect( + cleanLatex(stateVariables[`/s${i}`].stateValues.latex), + ).eq(str); + + expect( + cleanLatex( + stateVariables[`/input${i}`].stateValues + .rawRendererValue, + ), + ).eq(str); + } + + expect( + cleanLatex( + stateVariables[`/input0`].stateValues.rawRendererValue, + ), + ).eq(str0); + } + + await check_items("(0,1)"); + + await updateMathInputValue({ + latex: "x \\ge 3", + name: "/input0", + core, + }); + await check_items("[3,\\infty)", "x\\ge3"); + + await updateMathInputValue({ + latex: "{q\\mid q=5}", + name: "/input1", + core, + }); + await check_items("\\{5\\}"); + + await updateMathInputValue({ + latex: "[-\\infty, \\pi)", + name: "/input2", + core, + }); + await check_items("(-\\infty,3.141592654)"); + + await updateMathInputValue({ + latex: "(-\\infty,\\infty)", + name: "/input3", + core, + }); + await check_items("R"); + + await updateMathInputValue({ + latex: "x\\in \\emptyset", + name: "/input4", + core, + }); + await check_items("\\varnothing"); + + await updateMathInputValue({ + latex: "x\\notin [9, \\infty)", + name: "/input5", + core, + }); + await check_items("(-\\infty,9)"); + + await updateMathInputValue({ + latex: "{7}\\ni x", + name: "/input6", + core, + }); + await check_items("\\{7\\}"); + + await updateMathInputValue({ + latex: "(-\\infty, -2) \\not\\ni x", + name: "/input7", + core, + }); + await check_items("[-2,\\infty)"); + + await updateMathInputValue({ + latex: "\\{1\\}^c \\cap \\{v \\mid v \\ge 1 \\}", + name: "/input8", + core, + }); + await check_items("(1,\\infty)"); + + await updateMathInputValue({ + latex: "x \\ne -6", + name: "/input9", + core, + }); + await check_items("(-\\infty,-6)\\cup(-6,\\infty)"); + }); + + it("union of subset with numbers", async () => { + let core = await createTestCore({ + doenetML: ` + -9 + -6 + -1 + 8 + + ($x1,$x2) union ($x3,$x4) + + $S union $x1 union $x2 union $x3 union $x4 + + + `, + }); + + const stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/S"].stateValues.text).eq( + "( -9, -6 ) ∪ ( -1, 8 )", + ); + expect(stateVariables["/Sclosed"].stateValues.text).eq( + "[ -9, -6 ] ∪ [ -1, 8 ]", + ); + }); + + it("point and interval properties", async () => { + let core = await createTestCore({ + doenetML: ` + emptyset + [-1,2) + (-1,2] union {-5} + (-infinity,3) union (3,infinity) + R + +

empty points: $empty.points

+

empty points closed: $empty.pointsClosed

+

empty intervals: $empty.intervals

+

empty isolated points: $empty.isolatedPoints

+ +

interval points: $interval.points

+

interval points closed: $interval.pointsClosed

+

interval intervals: $interval.intervals

+

interval isolated points: $interval.isolatedPoints

+ +

intervalSingleton points: $intervalSingleton.points

+

intervalSingleton points closed: $intervalSingleton.pointsClosed

+

intervalSingleton intervals: $intervalSingleton.intervals

+

intervalSingleton isolated points: $intervalSingleton.isolatedPoints

+ +

missPoint points: $missPoint.points

+

missPoint points closed: $missPoint.pointsClosed

+

missPoint intervals: $missPoint.intervals

+

missPoint isolated points: $missPoint.isolatedPoints

+ +

R points: $R.points

+

R points closed: $R.pointsClosed

+

R intervals: $R.intervals

+

R isolated points: $R.isolatedPoints

+ + + `, + }); + + const stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/emptyPoints"].stateValues.text).eq( + "empty points: ", + ); + expect(stateVariables["/emptyPointsClosed"].stateValues.text).eq( + "empty points closed: ", + ); + expect(stateVariables["/emptyIntervals"].stateValues.text).eq( + "empty intervals: ", + ); + expect(stateVariables["/emptyIsolated"].stateValues.text).eq( + "empty isolated points: ", + ); + + expect(stateVariables["/intervalPoints"].stateValues.text).eq( + "interval points: -1, 2", + ); + expect(stateVariables["/intervalPointsClosed"].stateValues.text).eq( + "interval points closed: true, false", + ); + expect(stateVariables["/intervalIntervals"].stateValues.text).eq( + "interval intervals: [ -1, 2 )", + ); + expect(stateVariables["/intervalIsolated"].stateValues.text).eq( + "interval isolated points: ", + ); + + expect(stateVariables["/intervalSingletonPoints"].stateValues.text).eq( + "intervalSingleton points: -5, -1, 2", + ); + expect( + stateVariables["/intervalSingletonPointsClosed"].stateValues.text, + ).eq("intervalSingleton points closed: true, false, true"); + expect( + stateVariables["/intervalSingletonIntervals"].stateValues.text, + ).eq("intervalSingleton intervals: ( -1, 2 ]"); + expect( + stateVariables["/intervalSingletonIsolated"].stateValues.text, + ).eq("intervalSingleton isolated points: -5"); + + expect(stateVariables["/missPointPoints"].stateValues.text).eq( + "missPoint points: 3", + ); + expect(stateVariables["/missPointPointsClosed"].stateValues.text).eq( + "missPoint points closed: false", + ); + expect(stateVariables["/missPointIntervals"].stateValues.text).eq( + "missPoint intervals: ( -∞, 3 ), ( 3, ∞ )", + ); + expect(stateVariables["/missPointIsolated"].stateValues.text).eq( + "missPoint isolated points: ", + ); + + expect(stateVariables["/RPoints"].stateValues.text).eq("R points: "); + expect(stateVariables["/RPointsClosed"].stateValues.text).eq( + "R points closed: ", + ); + expect(stateVariables["/RIntervals"].stateValues.text).eq( + "R intervals: ( -∞, ∞ )", + ); + expect(stateVariables["/RIsolated"].stateValues.text).eq( + "R isolated points: ", + ); + }); +}); diff --git a/packages/doenetml-worker/src/test/tagSpecific/substitute.test.ts b/packages/doenetml-worker/src/test/tagSpecific/substitute.test.ts index 6eb52aa26..cf0e074d0 100644 --- a/packages/doenetml-worker/src/test/tagSpecific/substitute.test.ts +++ b/packages/doenetml-worker/src/test/tagSpecific/substitute.test.ts @@ -4,13 +4,13 @@ import { cleanLatex } from "../utils/math"; import { updateBooleanInputValue, updateMathInputValue, - updateMatrixInputValue, updateTextInputValue, } from "../utils/actions"; import me from "math-expressions"; const Mock = vi.fn(); vi.stubGlobal("postMessage", Mock); +vi.mock("hyperformula"); describe("Substitute tag tests", async () => { async function test_sub_alpha_x2(core) { @@ -113,7 +113,7 @@ describe("Substitute tag tests", async () => { // set simplify to full await updateTextInputValue({ text: "full", - componentName: "/simplify", + name: "/simplify", core, }); @@ -132,7 +132,7 @@ describe("Substitute tag tests", async () => { // set simplify back to none await updateTextInputValue({ text: "none", - componentName: "/simplify", + name: "/simplify", core, }); @@ -204,7 +204,7 @@ describe("Substitute tag tests", async () => { // change original await updateMathInputValue({ latex: "q/x", - componentName: "/original", + name: "/original", core, }); @@ -223,7 +223,7 @@ describe("Substitute tag tests", async () => { // change match so does not match await updateMathInputValue({ latex: "c", - componentName: "/match", + name: "/match", core, }); @@ -242,7 +242,7 @@ describe("Substitute tag tests", async () => { // change match so matches again await updateMathInputValue({ latex: "q", - componentName: "/match", + name: "/match", core, }); @@ -261,7 +261,7 @@ describe("Substitute tag tests", async () => { // change replacement await updateMathInputValue({ latex: "m^2", - componentName: "/replacement", + name: "/replacement", core, }); @@ -496,7 +496,7 @@ describe("Substitute tag tests", async () => { // change original await updateTextInputValue({ text: "The bicycle belongs to me.", - componentName: "/original", + name: "/original", core, }); let s2 = "The bicycle cHelongs to me."; @@ -508,7 +508,7 @@ describe("Substitute tag tests", async () => { // change match so does not match await updateTextInputValue({ text: "bike", - componentName: "/match", + name: "/match", core, }); let s3 = "The bicycle belongs to me."; @@ -520,7 +520,7 @@ describe("Substitute tag tests", async () => { // change match so matches again await updateTextInputValue({ text: "e b", - componentName: "/match", + name: "/match", core, }); let s4 = "ThcHeicyclcHeelongs to me."; @@ -532,12 +532,12 @@ describe("Substitute tag tests", async () => { // change match and replacement await updateTextInputValue({ text: "bicycle", - componentName: "/match", + name: "/match", core, }); await updateTextInputValue({ text: "scooter", - componentName: "/replacement", + name: "/replacement", core, }); let s5 = "The scooter belongs to me."; @@ -579,7 +579,7 @@ describe("Substitute tag tests", async () => { // change original await updateMathInputValue({ latex: "x^2+2x+3x", - componentName: "/orig", + name: "/orig", core, }); @@ -598,7 +598,7 @@ describe("Substitute tag tests", async () => { // change subbed await updateMathInputValue({ latex: "b^2+2b+3v/b", - componentName: "/subbed2", + name: "/subbed2", core, }); @@ -617,7 +617,7 @@ describe("Substitute tag tests", async () => { // change replacement so that it is in original await updateMathInputValue({ latex: "v", - componentName: "/replacement", + name: "/replacement", core, }); @@ -636,7 +636,7 @@ describe("Substitute tag tests", async () => { // Cannot modify subbed await updateMathInputValue({ latex: "v^2+2v+3v/(v+1)", - componentName: "/subbed2", + name: "/subbed2", core, }); @@ -655,7 +655,7 @@ describe("Substitute tag tests", async () => { // change original to not contain replacement await updateMathInputValue({ latex: "x^2+2x+3u/x", - componentName: "/orig", + name: "/orig", core, }); @@ -674,7 +674,7 @@ describe("Substitute tag tests", async () => { // Can modify subbed again await updateMathInputValue({ latex: "v^5+2v+3u/v", - componentName: "/subbed2", + name: "/subbed2", core, }); @@ -693,7 +693,7 @@ describe("Substitute tag tests", async () => { // change replacement to be more than a variable await updateMathInputValue({ latex: "v+1", - componentName: "/replacement", + name: "/replacement", core, }); @@ -712,7 +712,7 @@ describe("Substitute tag tests", async () => { // Cannot modify subbed await updateMathInputValue({ latex: "+7(v+1)^5+2(v+1)+3u/(v+1)", - componentName: "/subbed2", + name: "/subbed2", core, }); @@ -731,7 +731,7 @@ describe("Substitute tag tests", async () => { // change replacement to involve a subscript await updateMathInputValue({ latex: "v_3", - componentName: "/replacement", + name: "/replacement", core, }); @@ -750,7 +750,7 @@ describe("Substitute tag tests", async () => { // Can modify subbed once more await updateMathInputValue({ latex: "v_9^5+2v_3+3u/v_3", - componentName: "/subbed2", + name: "/subbed2", core, }); @@ -769,7 +769,7 @@ describe("Substitute tag tests", async () => { // change match to involve a subscript await updateMathInputValue({ latex: "v_9", - componentName: "/match", + name: "/match", core, }); @@ -788,7 +788,7 @@ describe("Substitute tag tests", async () => { // Can still modify subbed await updateMathInputValue({ latex: "v_3^5+2x+3u/v_3", - componentName: "/subbed2", + name: "/subbed2", core, }); @@ -807,7 +807,7 @@ describe("Substitute tag tests", async () => { // Cannot modify subbed to include match await updateMathInputValue({ latex: "v_3^5+2x+3u/v_3+v_9", - componentName: "/subbed2", + name: "/subbed2", core, }); @@ -853,7 +853,7 @@ describe("Substitute tag tests", async () => { // change original await updateTextInputValue({ text: "hello thereHello", - componentName: "/orig", + name: "/orig", core, }); @@ -870,7 +870,7 @@ describe("Substitute tag tests", async () => { // change subbed await updateTextInputValue({ text: "bye therebyeBye", - componentName: "/subbed2", + name: "/subbed2", core, }); @@ -891,7 +891,7 @@ describe("Substitute tag tests", async () => { // change replacement so that it is in original await updateTextInputValue({ text: "There", - componentName: "/replacement", + name: "/replacement", core, }); @@ -912,7 +912,7 @@ describe("Substitute tag tests", async () => { // Cannot modify subbed await updateTextInputValue({ text: "There thereThereThere extra", - componentName: "/subbed2", + name: "/subbed2", core, }); @@ -933,7 +933,7 @@ describe("Substitute tag tests", async () => { // change original to not contain replacement await updateTextInputValue({ text: "hello thenhellohello", - componentName: "/orig", + name: "/orig", core, }); @@ -954,7 +954,7 @@ describe("Substitute tag tests", async () => { // Can modify subbed again await updateTextInputValue({ text: "There thenThereThe", - componentName: "/subbed2", + name: "/subbed2", core, }); @@ -975,7 +975,7 @@ describe("Substitute tag tests", async () => { // Cannot modify subbed to include match await updateTextInputValue({ text: "There thenThereTheHELLO", - componentName: "/subbed2", + name: "/subbed2", core, }); @@ -995,7 +995,7 @@ describe("Substitute tag tests", async () => { await updateBooleanInputValue({ boolean: true, - componentName: "/wholeWord", + name: "/wholeWord", core, }); @@ -1016,7 +1016,7 @@ describe("Substitute tag tests", async () => { //change replacement so matches original, but not as a whole word await updateTextInputValue({ text: "Then", - componentName: "/replacement", + name: "/replacement", core, }); @@ -1037,7 +1037,7 @@ describe("Substitute tag tests", async () => { // Can still modify subbed await updateTextInputValue({ text: "Then thenhelloThere", - componentName: "/subbed2", + name: "/subbed2", core, }); @@ -1058,7 +1058,7 @@ describe("Substitute tag tests", async () => { // Cannot modify subbed by adding spaces to separate match await updateTextInputValue({ text: "Then then hello There", - componentName: "/subbed2", + name: "/subbed2", core, }); @@ -1079,7 +1079,7 @@ describe("Substitute tag tests", async () => { // change original so that replacement matches original as a whole word await updateTextInputValue({ text: "hello then helloThere", - componentName: "/orig", + name: "/orig", core, }); @@ -1100,7 +1100,7 @@ describe("Substitute tag tests", async () => { // Cannot modify subbed due to replacement match await updateTextInputValue({ text: "Then then helloTherenothing", - componentName: "/subbed2", + name: "/subbed2", core, }); @@ -1121,7 +1121,7 @@ describe("Substitute tag tests", async () => { // match case await updateBooleanInputValue({ boolean: true, - componentName: "/matchCase", + name: "/matchCase", core, }); @@ -1142,7 +1142,7 @@ describe("Substitute tag tests", async () => { // Now can modify subbed due to replacement not matching original case await updateTextInputValue({ text: "Then then helloThere Hello", - componentName: "/subbed2", + name: "/subbed2", core, }); @@ -1163,7 +1163,7 @@ describe("Substitute tag tests", async () => { // Cannot add match to subbed await updateTextInputValue({ text: "Then then helloThere Hello hello", - componentName: "/subbed2", + name: "/subbed2", core, }); @@ -1184,7 +1184,7 @@ describe("Substitute tag tests", async () => { // Change subbed to switch cases await updateTextInputValue({ text: "then Then helloThere Hello", - componentName: "/subbed2", + name: "/subbed2", core, }); @@ -1205,7 +1205,7 @@ describe("Substitute tag tests", async () => { // preserve case await updateBooleanInputValue({ boolean: true, - componentName: "/preserveCase", + name: "/preserveCase", core, }); @@ -1226,7 +1226,7 @@ describe("Substitute tag tests", async () => { // Cannot change subbed since original contains effective replacement await updateTextInputValue({ text: "then Then helloThere Hello more", - componentName: "/subbed2", + name: "/subbed2", core, }); @@ -1247,7 +1247,7 @@ describe("Substitute tag tests", async () => { // change case of match so that effective replacement is not in original await updateTextInputValue({ text: "Hello", - componentName: "/match", + name: "/match", core, }); @@ -1268,7 +1268,7 @@ describe("Substitute tag tests", async () => { // Can now change subbed await updateTextInputValue({ text: "Then HELLO THEN helloThere Then", - componentName: "/subbed2", + name: "/subbed2", core, }); @@ -1289,7 +1289,7 @@ describe("Substitute tag tests", async () => { // change case of match so that effective replacement is again in original await updateTextInputValue({ text: "HELLO", - componentName: "/match", + name: "/match", core, }); @@ -1310,7 +1310,7 @@ describe("Substitute tag tests", async () => { // Cannot change subbed await updateTextInputValue({ text: "Hello THEN THEN helloThere Hello ineffective", - componentName: "/subbed2", + name: "/subbed2", core, }); @@ -1331,7 +1331,7 @@ describe("Substitute tag tests", async () => { // change original so no longer has effective replacement await updateTextInputValue({ text: "Hello HELLO Then helloThere Hello", - componentName: "/orig", + name: "/orig", core, }); @@ -1352,7 +1352,7 @@ describe("Substitute tag tests", async () => { // Can change subbed once more await updateTextInputValue({ text: "Hello THEN Then helloThere THEN", - componentName: "/subbed2", + name: "/subbed2", core, }); @@ -1373,7 +1373,7 @@ describe("Substitute tag tests", async () => { // Cannot add match to subbed await updateTextInputValue({ text: "Hello THEN Then helloThere THEN HELLO", - componentName: "/subbed2", + name: "/subbed2", core, }); diff --git a/packages/doenetml-worker/src/test/tagSpecific/tabular.test.ts b/packages/doenetml-worker/src/test/tagSpecific/tabular.test.ts new file mode 100644 index 000000000..7be7a30d5 --- /dev/null +++ b/packages/doenetml-worker/src/test/tagSpecific/tabular.test.ts @@ -0,0 +1,36 @@ +import { describe, expect, it, vi } from "vitest"; +import { createTestCore, returnAllStateVariables } from "../utils/test-core"; + +const Mock = vi.fn(); +vi.stubGlobal("postMessage", Mock); +vi.mock("hyperformula"); + +describe("Tabular tag tests", async () => { + it("inHeader attribute", async () => { + let core = await createTestCore({ + doenetML: ` + + + A + B + + + 🟣 + 🔴 + + + +

Top: inHeader = $_cell1.inHeader

+

Bottom: inHeader = $_cell3.inHeader

+`, + }); + + const stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p1"].stateValues.text).eq( + "Top: inHeader = true", + ); + expect(stateVariables["/p2"].stateValues.text).eq( + "Bottom: inHeader = false", + ); + }); +}); diff --git a/packages/doenetml-worker/src/test/tagSpecific/text.test.ts b/packages/doenetml-worker/src/test/tagSpecific/text.test.ts index fd3c30729..9f5daa732 100644 --- a/packages/doenetml-worker/src/test/tagSpecific/text.test.ts +++ b/packages/doenetml-worker/src/test/tagSpecific/text.test.ts @@ -2,13 +2,17 @@ import { describe, expect, it, vi } from "vitest"; import { createTestCore, returnAllStateVariables } from "../utils/test-core"; import { cleanLatex } from "../utils/math"; import { + moveText, updateBooleanInputValue, updateMathInputValue, + updateSelectedIndices, updateTextInputValue, } from "../utils/actions"; +import { test_in_graph } from "../utils/in-graph"; const Mock = vi.fn(); vi.stubGlobal("postMessage", Mock); +vi.mock("hyperformula"); describe("Text tag tests", async () => { it("spaces preserved between tags", async () => { @@ -154,436 +158,14 @@ describe("Text tag tests", async () => { }); it("text in graph", async () => { - let core = await createTestCore({ - doenetML: ` + const doenetMLsnippet = ` - $content1 - bye + hello + bye + `; -

Anchor 1 coordinates:

-

Anchor 2 coordinates:

-

Change anchor 1 coordinates:

-

Change anchor 2 coordinates:

-

Position from anchor 1: $text1.positionFromAnchor

-

Position from anchor 2: $text2.positionFromAnchor

-

Change position from anchor 1 - - upperRight - upperLeft - lowerRight - lowerLeft - left - right - top - bottom - center - -

-

Change position from anchor 2 - - upperRight - upperLeft - lowerRight - lowerLeft - left - right - top - bottom - center - -

-

Draggable 1: $draggable1

-

Draggable 2: $draggable2

-

Change draggable 1

-

Change draggable 2

-

Content 1: $text1

-

Content 2: $text2

-

Content 1

-

Content 2

-

Fixed 1: $fixed1

-

Fixed 2: $fixed2

-

Change fixed 1

-

Change fixed 2

-

FixLocation 1: $fixLocation1

-

FixLocation 2: $fixLocation2

-

Change fixLocation 1

-

Change fixLocation 2

- - `, - }); - - let stateVariables = await returnAllStateVariables(core); - - expect(cleanLatex(stateVariables["/text1anchor"].stateValues.latex)).eq( - "(1,3)", - ); - expect(cleanLatex(stateVariables["/text2anchor"].stateValues.latex)).eq( - "(0,0)", - ); - - expect(stateVariables["/pPositionFromAnchor1"].stateValues.text).eq( - "Position from anchor 1: upperright", - ); - expect(stateVariables["/pPositionFromAnchor2"].stateValues.text).eq( - "Position from anchor 2: center", - ); - expect( - stateVariables["/positionFromAnchor1"].stateValues.selectedIndices, - ).eqls([1]); - expect( - stateVariables["/positionFromAnchor2"].stateValues.selectedIndices, - ).eqls([9]); - - expect(stateVariables["/pDraggable1"].stateValues.text).eq( - "Draggable 1: true", - ); - expect(stateVariables["/pDraggable2"].stateValues.text).eq( - "Draggable 2: true", - ); - - expect(stateVariables["/pContent1"].stateValues.text).eq( - "Content 1: hello", - ); - expect(stateVariables["/pContent2"].stateValues.text).eq( - "Content 2: bye", - ); - - // move texts by dragging - await core.requestAction({ - actionName: "moveText", - componentName: "/text1", - args: { x: -2, y: 3 }, - event: null, - }); - await core.requestAction({ - actionName: "moveText", - componentName: "/text2", - args: { x: 4, y: -5 }, - event: null, - }); - - stateVariables = await returnAllStateVariables(core); - - expect(cleanLatex(stateVariables["/text1anchor"].stateValues.latex)).eq( - "(-2,3)", - ); - expect(cleanLatex(stateVariables["/text2anchor"].stateValues.latex)).eq( - "(4,-5)", - ); - - // move texts by entering coordinates - await updateMathInputValue({ - latex: "(6,7)", - componentName: "/anchorCoords1", - core, - }); - await updateMathInputValue({ - latex: "(8,9)", - componentName: "/anchorCoords2", - core, - }); - - stateVariables = await returnAllStateVariables(core); - - expect(cleanLatex(stateVariables["/text1anchor"].stateValues.latex)).eq( - "(6,7)", - ); - expect(cleanLatex(stateVariables["/text2anchor"].stateValues.latex)).eq( - "(8,9)", - ); - - // change position from anchor - await core.requestAction({ - actionName: "updateSelectedIndices", - componentName: "/positionFromAnchor1", - args: { selectedIndices: [4] }, - event: null, - }); - await core.requestAction({ - actionName: "updateSelectedIndices", - componentName: "/positionFromAnchor2", - args: { selectedIndices: [3] }, - event: null, - }); - - stateVariables = await returnAllStateVariables(core); - - expect(stateVariables["/pPositionFromAnchor1"].stateValues.text).eq( - "Position from anchor 1: lowerleft", - ); - expect(stateVariables["/pPositionFromAnchor2"].stateValues.text).eq( - "Position from anchor 2: lowerright", - ); - - // make not draggable - await updateBooleanInputValue({ - boolean: false, - componentName: "/draggable1", - core, - }); - await updateBooleanInputValue({ - boolean: false, - componentName: "/draggable2", - core, - }); - stateVariables = await returnAllStateVariables(core); - - expect(stateVariables["/pDraggable1"].stateValues.text).eq( - "Draggable 1: false", - ); - expect(stateVariables["/pDraggable2"].stateValues.text).eq( - "Draggable 2: false", - ); - - // cannot move texts by dragging - await core.requestAction({ - actionName: "moveText", - componentName: "/text1", - args: { x: -10, y: -9 }, - event: null, - }); - await core.requestAction({ - actionName: "moveText", - componentName: "/text2", - args: { x: -8, y: -7 }, - event: null, - }); - - stateVariables = await returnAllStateVariables(core); - - expect(cleanLatex(stateVariables["/text1anchor"].stateValues.latex)).eq( - "(6,7)", - ); - expect(cleanLatex(stateVariables["/text2anchor"].stateValues.latex)).eq( - "(8,9)", - ); - - // change content of text - await updateTextInputValue({ - text: "hello there", - componentName: "/content1", - core, - }); - await updateTextInputValue({ - text: "bye now", - componentName: "/content2", - core, - }); - stateVariables = await returnAllStateVariables(core); - - expect(stateVariables["/pContent1"].stateValues.text).eq( - "Content 1: hello there", - ); - expect(stateVariables["/pContent2"].stateValues.text).eq( - "Content 2: bye now", - ); - - // make draggable again - await updateBooleanInputValue({ - boolean: true, - componentName: "/draggable1", - core, - }); - await updateBooleanInputValue({ - boolean: true, - componentName: "/draggable2", - core, - }); - stateVariables = await returnAllStateVariables(core); - - expect(stateVariables["/pDraggable1"].stateValues.text).eq( - "Draggable 1: true", - ); - expect(stateVariables["/pDraggable2"].stateValues.text).eq( - "Draggable 2: true", - ); - await core.requestAction({ - actionName: "moveText", - componentName: "/text1", - args: { x: -10, y: -9 }, - event: null, - }); - await core.requestAction({ - actionName: "moveText", - componentName: "/text2", - args: { x: -8, y: -7 }, - event: null, - }); - stateVariables = await returnAllStateVariables(core); - - expect(cleanLatex(stateVariables["/text1anchor"].stateValues.latex)).eq( - "(-10,-9)", - ); - expect(cleanLatex(stateVariables["/text2anchor"].stateValues.latex)).eq( - "(-8,-7)", - ); - - // fix location - await updateBooleanInputValue({ - boolean: true, - componentName: "/fixLocation1", - core, - }); - await updateBooleanInputValue({ - boolean: true, - componentName: "/fixLocation2", - core, - }); - stateVariables = await returnAllStateVariables(core); - - expect(stateVariables["/pFixLocation1"].stateValues.text).eq( - "FixLocation 1: true", - ); - expect(stateVariables["/pFixLocation2"].stateValues.text).eq( - "FixLocation 2: true", - ); - - // can change coordinates entering coordinates only for text 1 - await updateMathInputValue({ - latex: "(3,4)", - componentName: "/anchorCoords2", - core, - }); - await updateMathInputValue({ - latex: "(1,2)", - componentName: "/anchorCoords1", - core, - }); - - stateVariables = await returnAllStateVariables(core); - - expect(cleanLatex(stateVariables["/text1anchor"].stateValues.latex)).eq( - "(1,2)", - ); - expect(cleanLatex(stateVariables["/text2anchor"].stateValues.latex)).eq( - "(-8,-7)", - ); - - // cannot move texts by dragging - await core.requestAction({ - actionName: "moveText", - componentName: "/text1", - args: { x: 4, y: 6 }, - event: null, - }); - await core.requestAction({ - actionName: "moveText", - componentName: "/text2", - args: { x: 7, y: 8 }, - event: null, - }); - stateVariables = await returnAllStateVariables(core); - - expect(cleanLatex(stateVariables["/text1anchor"].stateValues.latex)).eq( - "(1,2)", - ); - expect(cleanLatex(stateVariables["/text2anchor"].stateValues.latex)).eq( - "(-8,-7)", - ); - - // can change position from anchor only for math 1 - await core.requestAction({ - actionName: "updateSelectedIndices", - componentName: "/positionFromAnchor2", - args: { selectedIndices: [8] }, - event: null, - }); - await core.requestAction({ - actionName: "updateSelectedIndices", - componentName: "/positionFromAnchor1", - args: { selectedIndices: [7] }, - event: null, - }); - - stateVariables = await returnAllStateVariables(core); - - expect(stateVariables["/pPositionFromAnchor1"].stateValues.text).eq( - "Position from anchor 1: top", - ); - expect(stateVariables["/pPositionFromAnchor2"].stateValues.text).eq( - "Position from anchor 2: lowerright", - ); - - // make completely fixed - await updateBooleanInputValue({ - boolean: true, - componentName: "/fixed1", - core, - }); - await updateBooleanInputValue({ - boolean: true, - componentName: "/fixed2", - core, - }); - stateVariables = await returnAllStateVariables(core); - - expect(stateVariables["/pFixed1"].stateValues.text).eq("Fixed 1: true"); - expect(stateVariables["/pFixed2"].stateValues.text).eq("Fixed 2: true"); - - // can change coordinates entering coordinates only for text 1 - await updateMathInputValue({ - latex: "(7,8)", - componentName: "/anchorCoords2", - core, - }); - await updateMathInputValue({ - latex: "(5,6)", - componentName: "/anchorCoords1", - core, - }); - - stateVariables = await returnAllStateVariables(core); - - expect(cleanLatex(stateVariables["/text1anchor"].stateValues.latex)).eq( - "(5,6)", - ); - expect(cleanLatex(stateVariables["/text2anchor"].stateValues.latex)).eq( - "(-8,-7)", - ); - - // can change position from anchor only for math 1 - await core.requestAction({ - actionName: "updateSelectedIndices", - componentName: "/positionFromAnchor2", - args: { selectedIndices: [5] }, - event: null, - }); - await core.requestAction({ - actionName: "updateSelectedIndices", - componentName: "/positionFromAnchor1", - args: { selectedIndices: [6] }, - event: null, - }); - - stateVariables = await returnAllStateVariables(core); - - expect(stateVariables["/pPositionFromAnchor1"].stateValues.text).eq( - "Position from anchor 1: right", - ); - expect(stateVariables["/pPositionFromAnchor2"].stateValues.text).eq( - "Position from anchor 2: lowerright", - ); - - // can change content only for text 1 - await updateTextInputValue({ - text: "hello there again", - componentName: "/content1", - core, - }); - await updateTextInputValue({ - text: "bye now too", - componentName: "/content2", - core, - }); - stateVariables = await returnAllStateVariables(core); - - expect(stateVariables["/pContent1"].stateValues.text).eq( - "Content 1: hello there again", - ); - expect(stateVariables["/pContent2"].stateValues.text).eq( - "Content 2: bye now", - ); + await test_in_graph(doenetMLsnippet, moveText); }); it("text in graph, handle bad anchor coordinates", async () => { @@ -610,7 +192,7 @@ describe("Text tag tests", async () => { // give good anchor coords await updateMathInputValue({ latex: "(6,7)", - componentName: "/anchorCoords1", + name: "/anchorCoords1", core, }); @@ -623,7 +205,7 @@ describe("Text tag tests", async () => { // give good anchor coords await updateMathInputValue({ latex: "(6,7)", - componentName: "/anchorCoords1", + name: "/anchorCoords1", core, }); @@ -636,7 +218,7 @@ describe("Text tag tests", async () => { // give bad anchor coords again await updateMathInputValue({ latex: "q", - componentName: "/anchorCoords1", + name: "/anchorCoords1", core, }); @@ -692,7 +274,7 @@ describe("Text tag tests", async () => { "none", ); - await updateMathInputValue({ latex: "2", componentName: "/sn", core }); + await updateMathInputValue({ latex: "2", name: "/sn", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/tsd_variable_style"].stateValues.text).eq( @@ -713,7 +295,7 @@ describe("Text tag tests", async () => { expect(stateVariables["/tc_fixed_style"].stateValues.text).eq("green"); expect(stateVariables["/bc_fixed_style"].stateValues.text).eq("none"); - await updateMathInputValue({ latex: "3", componentName: "/sn", core }); + await updateMathInputValue({ latex: "3", name: "/sn", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/tsd_variable_style"].stateValues.text).eq( @@ -817,18 +399,8 @@ describe("Text tag tests", async () => { ); // move first texts - await core.requestAction({ - actionName: "moveText", - componentName: "/t1", - args: { x: -2, y: 3 }, - event: null, - }); - await core.requestAction({ - actionName: "moveText", - componentName: "/t2", - args: { x: 4, y: -5 }, - event: null, - }); + await moveText({ name: "/t1", x: -2, y: 3, core }); + await moveText({ name: "/t2", x: 4, y: -5, core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/t1coords"].stateValues.latex)).eq( @@ -851,18 +423,8 @@ describe("Text tag tests", async () => { ); // move second texts - await core.requestAction({ - actionName: "moveText", - componentName: "/t1a", - args: { x: 7, y: 1 }, - event: null, - }); - await core.requestAction({ - actionName: "moveText", - componentName: "/t2a", - args: { x: -8, y: 2 }, - event: null, - }); + await moveText({ name: "/t1a", x: 7, y: 1, core }); + await moveText({ name: "/t2a", x: -8, y: 2, core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/t1coords"].stateValues.latex)).eq( @@ -885,18 +447,8 @@ describe("Text tag tests", async () => { ); // move third texts - await core.requestAction({ - actionName: "moveText", - componentName: "/t1b", - args: { x: -6, y: 3 }, - event: null, - }); - await core.requestAction({ - actionName: "moveText", - componentName: "/t2b", - args: { x: -5, y: -4 }, - event: null, - }); + await moveText({ name: "/t1b", x: -6, y: 3, core }); + await moveText({ name: "/t2b", x: -5, y: -4, core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/t1coords"].stateValues.latex)).eq( diff --git a/packages/doenetml-worker/src/test/tagSpecific/textinput.test.ts b/packages/doenetml-worker/src/test/tagSpecific/textinput.test.ts index 17e0bf466..08e7d77bc 100644 --- a/packages/doenetml-worker/src/test/tagSpecific/textinput.test.ts +++ b/packages/doenetml-worker/src/test/tagSpecific/textinput.test.ts @@ -2,15 +2,19 @@ import { describe, expect, it, vi } from "vitest"; import { createTestCore, returnAllStateVariables } from "../utils/test-core"; import { cleanLatex } from "../utils/math"; import { + moveInput, updateBooleanInputValue, updateMathInputValue, + updateSelectedIndices, updateTextInputImmediateValue, updateTextInputValue, updateTextInputValueToImmediateValue, } from "../utils/actions"; +import { test_in_graph } from "../utils/in-graph"; const Mock = vi.fn(); vi.stubGlobal("postMessage", Mock); +vi.mock("hyperformula"); describe("TextInput tag tests", async () => { it("textInput references", async () => { @@ -44,7 +48,7 @@ describe("TextInput tag tests", async () => { // Type 2 in first textInput await updateTextInputImmediateValue({ text: "hello2", - componentName: "/ti1", + name: "/ti1", core, }); @@ -58,7 +62,7 @@ describe("TextInput tag tests", async () => { // Update value (e.g., by pressing Enter) in first textInput await updateTextInputValueToImmediateValue({ - componentName: "/ti1", + name: "/ti1", core, }); @@ -74,12 +78,12 @@ describe("TextInput tag tests", async () => { await updateTextInputImmediateValue({ text: "hello", - componentName: "/ti1a", + name: "/ti1a", core, }); await updateTextInputImmediateValue({ text: "hello you", - componentName: "/ti1a", + name: "/ti1a", core, }); @@ -97,7 +101,7 @@ describe("TextInput tag tests", async () => { // Update value (e.g., by changing focus) of second textInput await updateTextInputValueToImmediateValue({ - componentName: "/ti1a", + name: "/ti1a", core, }); @@ -116,7 +120,7 @@ describe("TextInput tag tests", async () => { // bye in third input await updateTextInputImmediateValue({ text: "bye", - componentName: "/ti2", + name: "/ti2", core, }); stateVariables = await returnAllStateVariables(core); @@ -133,7 +137,7 @@ describe("TextInput tag tests", async () => { // update value (e.g., press enter) in third textInput await updateTextInputValueToImmediateValue({ - componentName: "/ti2", + name: "/ti2", core, }); stateVariables = await returnAllStateVariables(core); @@ -151,7 +155,7 @@ describe("TextInput tag tests", async () => { // Type abc in second textInput await updateTextInputImmediateValue({ text: "abc", - componentName: "/ti1a", + name: "/ti1a", core, }); stateVariables = await returnAllStateVariables(core); @@ -164,7 +168,7 @@ describe("TextInput tag tests", async () => { // update value (e.g., blur) textInput 2 await updateTextInputValueToImmediateValue({ - componentName: "/ti1a", + name: "/ti1a", core, }); stateVariables = await returnAllStateVariables(core); @@ -179,7 +183,7 @@ describe("TextInput tag tests", async () => { await updateTextInputImmediateValue({ text: "", - componentName: "/ti1", + name: "/ti1", core, }); stateVariables = await returnAllStateVariables(core); @@ -187,21 +191,21 @@ describe("TextInput tag tests", async () => { await updateTextInputImmediateValue({ text: "a", - componentName: "/ti1", + name: "/ti1", core, }); await updateTextInputImmediateValue({ text: "ab", - componentName: "/ti1", + name: "/ti1", core, }); await updateTextInputImmediateValue({ text: "abc", - componentName: "/ti1", + name: "/ti1", core, }); await updateTextInputValueToImmediateValue({ - componentName: "/ti1", + name: "/ti1", core, }); stateVariables = await returnAllStateVariables(core); @@ -216,7 +220,7 @@ describe("TextInput tag tests", async () => { await updateTextInputImmediateValue({ text: "saludos", - componentName: "/ti2", + name: "/ti2", core, }); stateVariables = await returnAllStateVariables(core); @@ -229,12 +233,12 @@ describe("TextInput tag tests", async () => { // blur textInput 2 and type d in textInput 1 await updateTextInputValueToImmediateValue({ - componentName: "/ti2", + name: "/ti2", core, }); await updateTextInputImmediateValue({ text: "abcd", - componentName: "/ti1", + name: "/ti1", core, }); @@ -248,7 +252,7 @@ describe("TextInput tag tests", async () => { // Update value (e.g., blur) of first textInput await updateTextInputValueToImmediateValue({ - componentName: "/ti1", + name: "/ti1", core, }); stateVariables = await returnAllStateVariables(core); @@ -262,7 +266,7 @@ describe("TextInput tag tests", async () => { // Clearing second textInput await updateTextInputImmediateValue({ text: "", - componentName: "/ti1a", + name: "/ti1a", core, }); stateVariables = await returnAllStateVariables(core); @@ -275,7 +279,7 @@ describe("TextInput tag tests", async () => { // update value (e.g., by blurring) of second textInput await updateTextInputValueToImmediateValue({ - componentName: "/ti1a", + name: "/ti1a", core, }); @@ -311,7 +315,7 @@ describe("TextInput tag tests", async () => { // enter new values await updateTextInputValue({ text: "bye now", - componentName: "/ti1", + name: "/ti1", core, }); @@ -358,7 +362,7 @@ describe("TextInput tag tests", async () => { // enter new values await updateTextInputImmediateValue({ text: "disappear", - componentName: "/ti1", + name: "/ti1", core, }); stateVariables = await returnAllStateVariables(core); @@ -370,7 +374,7 @@ describe("TextInput tag tests", async () => { // values revert when press update value await updateTextInputValueToImmediateValue({ - componentName: "/ti1", + name: "/ti1", core, }); stateVariables = await returnAllStateVariables(core); @@ -404,7 +408,7 @@ describe("TextInput tag tests", async () => { // enter new values await updateTextInputValue({ text: "bye now", - componentName: "/ti1", + name: "/ti1", core, }); @@ -451,7 +455,7 @@ describe("TextInput tag tests", async () => { // enter new values await updateTextInputImmediateValue({ text: "disappear", - componentName: "/ti1", + name: "/ti1", core, }); stateVariables = await returnAllStateVariables(core); @@ -463,7 +467,7 @@ describe("TextInput tag tests", async () => { // values revert when press update value await updateTextInputValueToImmediateValue({ - componentName: "/ti1", + name: "/ti1", core, }); stateVariables = await returnAllStateVariables(core); @@ -499,7 +503,7 @@ describe("TextInput tag tests", async () => { // enter new values await updateTextInputValue({ text: "bye now", - componentName: "/ti1", + name: "/ti1", core, }); @@ -533,7 +537,7 @@ describe("TextInput tag tests", async () => { // type new values in first textInput await updateTextInputImmediateValue({ text: "bye now", - componentName: "/ti1", + name: "/ti1", core, }); stateVariables = await returnAllStateVariables(core); @@ -546,7 +550,7 @@ describe("TextInput tag tests", async () => { // update value await updateTextInputValueToImmediateValue({ - componentName: "/ti1", + name: "/ti1", core, }); stateVariables = await returnAllStateVariables(core); @@ -558,7 +562,7 @@ describe("TextInput tag tests", async () => { // type values input second textInput await updateTextInputImmediateValue({ text: "Hello again", - componentName: "/ti2", + name: "/ti2", core, }); stateVariables = await returnAllStateVariables(core); @@ -571,7 +575,7 @@ describe("TextInput tag tests", async () => { // update value of second textInput await updateTextInputValueToImmediateValue({ - componentName: "/ti2", + name: "/ti2", core, }); stateVariables = await returnAllStateVariables(core); @@ -606,7 +610,7 @@ describe("TextInput tag tests", async () => { // type new values in first textInput await updateTextInputImmediateValue({ text: "bye now", - componentName: "/ti1", + name: "/ti1", core, }); stateVariables = await returnAllStateVariables(core); @@ -617,7 +621,7 @@ describe("TextInput tag tests", async () => { // update value await updateTextInputValueToImmediateValue({ - componentName: "/ti1", + name: "/ti1", core, }); stateVariables = await returnAllStateVariables(core); @@ -629,7 +633,7 @@ describe("TextInput tag tests", async () => { // type values input second textInput await updateTextInputImmediateValue({ text: "Hello again", - componentName: "/ti2", + name: "/ti2", core, }); stateVariables = await returnAllStateVariables(core); @@ -642,7 +646,7 @@ describe("TextInput tag tests", async () => { // update value of second textInput await updateTextInputValueToImmediateValue({ - componentName: "/ti2", + name: "/ti2", core, }); stateVariables = await returnAllStateVariables(core); @@ -672,7 +676,7 @@ describe("TextInput tag tests", async () => { await updateTextInputImmediateValue({ text: " bye", - componentName: "/ti", + name: "/ti", core, }); stateVariables = await returnAllStateVariables(core); @@ -681,7 +685,7 @@ describe("TextInput tag tests", async () => { await updateTextInputImmediateValue({ text: " there", - componentName: "/ti", + name: "/ti", core, }); stateVariables = await returnAllStateVariables(core); @@ -689,7 +693,7 @@ describe("TextInput tag tests", async () => { expect(stateVariables["/h"].stateValues.value).eq("hello"); await updateTextInputValueToImmediateValue({ - componentName: "/ti", + name: "/ti", core, }); stateVariables = await returnAllStateVariables(core); @@ -698,7 +702,7 @@ describe("TextInput tag tests", async () => { await updateTextInputImmediateValue({ text: "?", - componentName: "/ti", + name: "/ti", core, }); stateVariables = await returnAllStateVariables(core); @@ -707,7 +711,7 @@ describe("TextInput tag tests", async () => { await updateTextInputImmediateValue({ text: "!", - componentName: "/ti", + name: "/ti", core, }); stateVariables = await returnAllStateVariables(core); @@ -715,7 +719,7 @@ describe("TextInput tag tests", async () => { expect(stateVariables["/h"].stateValues.value).eq("hello there"); await updateTextInputValueToImmediateValue({ - componentName: "/ti", + name: "/ti", core, }); stateVariables = await returnAllStateVariables(core); @@ -724,450 +728,14 @@ describe("TextInput tag tests", async () => { }); it("text input in graph", async () => { - let core = await createTestCore({ - doenetML: ` + const doenetMLsnippet = ` - - + + - -

Anchor 1 coordinates:

-

Anchor 2 coordinates:

-

Change anchor 1 coordinates:

-

Change anchor 2 coordinates:

-

Position from anchor 1: $textInput1.positionFromAnchor

-

Position from anchor 2: $textInput2.positionFromAnchor

-

Change position from anchor 1 - - upperRight - upperLeft - lowerRight - lowerLeft - left - right - top - bottom - center - -

-

Change position from anchor 2 - - upperRight - upperLeft - lowerRight - lowerLeft - left - right - top - bottom - center - -

-

Draggable 1: $draggable1

-

Draggable 2: $draggable2

-

Change draggable 1

-

Change draggable 2

-

Disabled 1: $disabled1

-

Disabled 2: $disabled2

-

Change disabled 1

-

Change disabled 2

-

Fixed 1: $fixed1

-

Fixed 2: $fixed2

-

Change fixed 1

-

Change fixed 2

-

FixLocation 1: $fixLocation1

-

FixLocation 2: $fixLocation2

-

Change fixLocation 1

-

Change fixLocation 2

- - `, - }); - - let stateVariables = await returnAllStateVariables(core); - - expect( - cleanLatex(stateVariables["/textInput1anchor"].stateValues.latex), - ).eq("(1,3)"); - expect( - cleanLatex(stateVariables["/textInput2anchor"].stateValues.latex), - ).eq("(0,0)"); - - expect(stateVariables["/pPositionFromAnchor1"].stateValues.text).eq( - "Position from anchor 1: upperright", - ); - expect(stateVariables["/pPositionFromAnchor2"].stateValues.text).eq( - "Position from anchor 2: center", - ); - expect( - stateVariables["/positionFromAnchor1"].stateValues.selectedIndices, - ).eqls([1]); - expect( - stateVariables["/positionFromAnchor2"].stateValues.selectedIndices, - ).eqls([9]); - - expect(stateVariables["/pDraggable1"].stateValues.text).eq( - "Draggable 1: true", - ); - expect(stateVariables["/pDraggable2"].stateValues.text).eq( - "Draggable 2: true", - ); - expect(stateVariables["/pDisabled1"].stateValues.text).eq( - "Disabled 1: true", - ); - expect(stateVariables["/pDisabled2"].stateValues.text).eq( - "Disabled 2: false", - ); - expect(stateVariables["/pFixed1"].stateValues.text).eq( - "Fixed 1: false", - ); - expect(stateVariables["/pFixed2"].stateValues.text).eq( - "Fixed 2: false", - ); - expect(stateVariables["/pFixLocation1"].stateValues.text).eq( - "FixLocation 1: false", - ); - expect(stateVariables["/pFixLocation2"].stateValues.text).eq( - "FixLocation 2: false", - ); - - // move textInputs by dragging - await core.requestAction({ - actionName: "moveInput", - componentName: "/textInput1", - args: { x: -2, y: 3 }, - event: null, - }); - await core.requestAction({ - actionName: "moveInput", - componentName: "/textInput2", - args: { x: 4, y: -5 }, - event: null, - }); - - stateVariables = await returnAllStateVariables(core); - - expect( - cleanLatex(stateVariables["/textInput1anchor"].stateValues.latex), - ).eq("(-2,3)"); - expect( - cleanLatex(stateVariables["/textInput2anchor"].stateValues.latex), - ).eq("(4,-5)"); - - // move textInputs by entering coordinates - - await updateMathInputValue({ - latex: "(6,7)", - componentName: "/anchorCoords1", - core, - }); - await updateMathInputValue({ - latex: "(8,9)", - componentName: "/anchorCoords2", - core, - }); - - stateVariables = await returnAllStateVariables(core); - - expect( - cleanLatex(stateVariables["/textInput1anchor"].stateValues.latex), - ).eq("(6,7)"); - expect( - cleanLatex(stateVariables["/textInput2anchor"].stateValues.latex), - ).eq("(8,9)"); - - // change position from anchor - await core.requestAction({ - actionName: "updateSelectedIndices", - componentName: "/positionFromAnchor1", - args: { selectedIndices: [4] }, - event: null, - }); - await core.requestAction({ - actionName: "updateSelectedIndices", - componentName: "/positionFromAnchor2", - args: { selectedIndices: [3] }, - event: null, - }); - - stateVariables = await returnAllStateVariables(core); - - expect(stateVariables["/pPositionFromAnchor1"].stateValues.text).eq( - "Position from anchor 1: lowerleft", - ); - expect(stateVariables["/pPositionFromAnchor2"].stateValues.text).eq( - "Position from anchor 2: lowerright", - ); - - // make not draggable - await updateBooleanInputValue({ - boolean: false, - componentName: "/draggable1", - core, - }); - await updateBooleanInputValue({ - boolean: false, - componentName: "/draggable2", - core, - }); - stateVariables = await returnAllStateVariables(core); - - expect(stateVariables["/pDraggable1"].stateValues.text).eq( - "Draggable 1: false", - ); - expect(stateVariables["/pDraggable2"].stateValues.text).eq( - "Draggable 2: false", - ); - - // cannot move textInputs by dragging - await core.requestAction({ - actionName: "moveInput", - componentName: "/textInput1", - args: { x: -10, y: -9 }, - event: null, - }); - await core.requestAction({ - actionName: "moveInput", - componentName: "/textInput2", - args: { x: -8, y: -7 }, - event: null, - }); - - stateVariables = await returnAllStateVariables(core); - - expect( - cleanLatex(stateVariables["/textInput1anchor"].stateValues.latex), - ).eq("(6,7)"); - expect( - cleanLatex(stateVariables["/textInput2anchor"].stateValues.latex), - ).eq("(8,9)"); - - // make draggable again - await updateBooleanInputValue({ - boolean: true, - componentName: "/draggable1", - core, - }); - await updateBooleanInputValue({ - boolean: true, - componentName: "/draggable2", - core, - }); - stateVariables = await returnAllStateVariables(core); - - expect(stateVariables["/pDraggable1"].stateValues.text).eq( - "Draggable 1: true", - ); - expect(stateVariables["/pDraggable2"].stateValues.text).eq( - "Draggable 2: true", - ); - - await core.requestAction({ - actionName: "moveInput", - componentName: "/textInput1", - args: { x: -10, y: -9 }, - event: null, - }); - await core.requestAction({ - actionName: "moveInput", - componentName: "/textInput2", - args: { x: -8, y: -7 }, - event: null, - }); - stateVariables = await returnAllStateVariables(core); - - expect( - cleanLatex(stateVariables["/textInput1anchor"].stateValues.latex), - ).eq("(-10,-9)"); - expect( - cleanLatex(stateVariables["/textInput2anchor"].stateValues.latex), - ).eq("(-8,-7)"); - - // fix location - await updateBooleanInputValue({ - boolean: true, - componentName: "/fixLocation1", - core, - }); - await updateBooleanInputValue({ - boolean: true, - componentName: "/fixLocation2", - core, - }); - stateVariables = await returnAllStateVariables(core); - - expect(stateVariables["/pFixLocation1"].stateValues.text).eq( - "FixLocation 1: true", - ); - expect(stateVariables["/pFixLocation2"].stateValues.text).eq( - "FixLocation 2: true", - ); - - // can change coordinates entering coordinates only for input 1 - await updateMathInputValue({ - latex: "(3,4)", - componentName: "/anchorCoords2", - core, - }); - await updateMathInputValue({ - latex: "(1,2)", - componentName: "/anchorCoords1", - core, - }); - - stateVariables = await returnAllStateVariables(core); - - expect( - cleanLatex(stateVariables["/textInput1anchor"].stateValues.latex), - ).eq("(1,2)"); - expect( - cleanLatex(stateVariables["/textInput2anchor"].stateValues.latex), - ).eq("(-8,-7)"); - - // cannot move textInputs by dragging - await core.requestAction({ - actionName: "moveInput", - componentName: "/textInput1", - args: { x: 4, y: 6 }, - event: null, - }); - await core.requestAction({ - actionName: "moveInput", - componentName: "/textInput2", - args: { x: 7, y: 8 }, - event: null, - }); - stateVariables = await returnAllStateVariables(core); - - expect( - cleanLatex(stateVariables["/textInput1anchor"].stateValues.latex), - ).eq("(1,2)"); - expect( - cleanLatex(stateVariables["/textInput2anchor"].stateValues.latex), - ).eq("(-8,-7)"); - - // can change position from anchor only for input 1 - await core.requestAction({ - actionName: "updateSelectedIndices", - componentName: "/positionFromAnchor2", - args: { selectedIndices: [8] }, - event: null, - }); - await core.requestAction({ - actionName: "updateSelectedIndices", - componentName: "/positionFromAnchor1", - args: { selectedIndices: [7] }, - event: null, - }); - - stateVariables = await returnAllStateVariables(core); - - expect(stateVariables["/pPositionFromAnchor1"].stateValues.text).eq( - "Position from anchor 1: top", - ); - expect(stateVariables["/pPositionFromAnchor2"].stateValues.text).eq( - "Position from anchor 2: lowerright", - ); - - // can change disabled attribute - await updateBooleanInputValue({ - boolean: false, - componentName: "/disabled1", - core, - }); - await updateBooleanInputValue({ - boolean: true, - componentName: "/disabled2", - core, - }); - stateVariables = await returnAllStateVariables(core); - - expect(stateVariables["/pDisabled1"].stateValues.text).eq( - "Disabled 1: false", - ); - expect(stateVariables["/pDisabled2"].stateValues.text).eq( - "Disabled 2: true", - ); - - // make completely fixed - await updateBooleanInputValue({ - boolean: true, - componentName: "/fixed1", - core, - }); - await updateBooleanInputValue({ - boolean: true, - componentName: "/fixed2", - core, - }); - stateVariables = await returnAllStateVariables(core); - - expect(stateVariables["/pFixed1"].stateValues.text).eq("Fixed 1: true"); - expect(stateVariables["/pFixed2"].stateValues.text).eq("Fixed 2: true"); - - // can change coordinates entering coordinates only for input 1 - await updateMathInputValue({ - latex: "(7,8)", - componentName: "/anchorCoords2", - core, - }); - await updateMathInputValue({ - latex: "(5,6)", - componentName: "/anchorCoords1", - core, - }); - - stateVariables = await returnAllStateVariables(core); - - expect( - cleanLatex(stateVariables["/textInput1anchor"].stateValues.latex), - ).eq("(5,6)"); - expect( - cleanLatex(stateVariables["/textInput2anchor"].stateValues.latex), - ).eq("(-8,-7)"); + `; - // can change position from anchor only for math 1 - await core.requestAction({ - actionName: "updateSelectedIndices", - componentName: "/positionFromAnchor2", - args: { selectedIndices: [5] }, - event: null, - }); - await core.requestAction({ - actionName: "updateSelectedIndices", - componentName: "/positionFromAnchor1", - args: { selectedIndices: [6] }, - event: null, - }); - - stateVariables = await returnAllStateVariables(core); - - expect(stateVariables["/pPositionFromAnchor1"].stateValues.text).eq( - "Position from anchor 1: right", - ); - expect(stateVariables["/pPositionFromAnchor2"].stateValues.text).eq( - "Position from anchor 2: lowerright", - ); - - // can change disabled attribute only for input 1 - - await updateBooleanInputValue({ - boolean: true, - componentName: "/disabled1", - core, - }); - await updateBooleanInputValue({ - boolean: false, - componentName: "/disabled2", - core, - }); - stateVariables = await returnAllStateVariables(core); - - expect(stateVariables["/pDisabled1"].stateValues.text).eq( - "Disabled 1: true", - ); - expect(stateVariables["/pDisabled2"].stateValues.text).eq( - "Disabled 2: true", - ); + await test_in_graph(doenetMLsnippet, moveInput); }); it("use textInput as basic math input", async () => { @@ -1189,14 +757,14 @@ describe("TextInput tag tests", async () => { expect(stateVariables["/n1"].stateValues.value).eqls(NaN); expect(stateVariables["/n2"].stateValues.value).eqls(NaN); - await updateTextInputValue({ text: "4/2", componentName: "/ti", core }); + await updateTextInputValue({ text: "4/2", name: "/ti", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/m1"].stateValues.value.tree).eqls(["/", 4, 2]); expect(stateVariables["/m2"].stateValues.value.tree).eqls(["/", 4, 2]); expect(stateVariables["/n1"].stateValues.value).eq(2); expect(stateVariables["/n2"].stateValues.value).eq(2); - await updateTextInputValue({ text: "xy", componentName: "/ti", core }); + await updateTextInputValue({ text: "xy", name: "/ti", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/m1"].stateValues.value.tree).eqls([ "*", @@ -1213,7 +781,7 @@ describe("TextInput tag tests", async () => { await updateTextInputValue({ text: "\\frac{a}{b}", - componentName: "/ti", + name: "/ti", core, }); stateVariables = await returnAllStateVariables(core); @@ -1228,7 +796,7 @@ describe("TextInput tag tests", async () => { await updateTextInputValue({ text: "\\frac{6}{2}", - componentName: "/ti", + name: "/ti", core, }); stateVariables = await returnAllStateVariables(core); @@ -1349,7 +917,7 @@ describe("TextInput tag tests", async () => { ti1ivchanged = true; await updateTextInputImmediateValue({ text: ti1iv, - componentName: "/ti1", + name: "/ti1", core, }); await check_items( @@ -1363,7 +931,7 @@ describe("TextInput tag tests", async () => { ti1 = ti3 = ti3iv = ti1iv; ti1changed = true; await updateTextInputValueToImmediateValue({ - componentName: "/ti1", + name: "/ti1", core, }); @@ -1380,7 +948,7 @@ describe("TextInput tag tests", async () => { ti2ivchanged = true; await updateTextInputImmediateValue({ text: ti2iv, - componentName: "/ti2", + name: "/ti2", core, }); await check_items( @@ -1394,7 +962,7 @@ describe("TextInput tag tests", async () => { ti2 = ti2iv; ti2changed = true; await updateTextInputValueToImmediateValue({ - componentName: "/ti2", + name: "/ti2", core, }); @@ -1410,7 +978,7 @@ describe("TextInput tag tests", async () => { ti3ivchanged = true; await updateTextInputImmediateValue({ text: ti3iv, - componentName: "/ti3", + name: "/ti3", core, }); await check_items( @@ -1424,7 +992,7 @@ describe("TextInput tag tests", async () => { ti1 = ti1iv = ti3 = ti3iv; ti3changed = true; await updateTextInputValueToImmediateValue({ - componentName: "/ti3", + name: "/ti3", core, }); @@ -1440,7 +1008,7 @@ describe("TextInput tag tests", async () => { ti4ivchanged = true; await updateTextInputImmediateValue({ text: ti4iv, - componentName: "/ti4", + name: "/ti4", core, }); await check_items( @@ -1454,7 +1022,7 @@ describe("TextInput tag tests", async () => { ti2 = ti2iv = ti4 = ti4iv; ti4changed = true; await updateTextInputValueToImmediateValue({ - componentName: "/ti4", + name: "/ti4", core, }); @@ -1499,7 +1067,7 @@ describe("TextInput tag tests", async () => { ti3ivchanged = true; await updateTextInputImmediateValue({ text: ti3iv, - componentName: "/ti3", + name: "/ti3", core, }); await check_items( @@ -1515,7 +1083,7 @@ describe("TextInput tag tests", async () => { ti1ivchanged = true; ti3changed = true; await updateTextInputValueToImmediateValue({ - componentName: "/ti3", + name: "/ti3", core, }); @@ -1531,7 +1099,7 @@ describe("TextInput tag tests", async () => { ti4ivchanged = true; await updateTextInputImmediateValue({ text: ti4iv, - componentName: "/ti4", + name: "/ti4", core, }); await check_items( @@ -1547,7 +1115,7 @@ describe("TextInput tag tests", async () => { ti2ivchanged = true; ti4changed = true; await updateTextInputValueToImmediateValue({ - componentName: "/ti4", + name: "/ti4", core, }); @@ -1654,7 +1222,7 @@ describe("TextInput tag tests", async () => { await updateTextInputValue({ text: string, - componentName: "/ti", + name: "/ti", core, }); await check_items(string); @@ -1662,7 +1230,7 @@ describe("TextInput tag tests", async () => { string = "black cat, green goblin,great big red dog"; await updateTextInputValue({ text: string, - componentName: "/ti", + name: "/ti", core, }); await check_items(string); diff --git a/packages/doenetml-worker/src/test/tagSpecific/textlist.test.ts b/packages/doenetml-worker/src/test/tagSpecific/textlist.test.ts index 9b2333276..76f4eb10f 100644 --- a/packages/doenetml-worker/src/test/tagSpecific/textlist.test.ts +++ b/packages/doenetml-worker/src/test/tagSpecific/textlist.test.ts @@ -1,9 +1,11 @@ import { describe, expect, it, vi } from "vitest"; import { createTestCore, returnAllStateVariables } from "../utils/test-core"; import { updateMathInputValue, updateTextInputValue } from "../utils/actions"; +import Core from "../../Core"; const Mock = vi.fn(); vi.stubGlobal("postMessage", Mock); +vi.mock("hyperformula"); describe("TextList tag tests", async () => { async function test_textList({ @@ -13,11 +15,11 @@ describe("TextList tag tests", async () => { text, texts, }: { - core: any; + core: Core; name?: string; pName?: string; text?: string; - texts?: any[]; + texts?: string[]; }) { const stateVariables = await returnAllStateVariables(core); @@ -99,7 +101,7 @@ describe("TextList tag tests", async () => { }); }); - async function test_nested_and_inverse(core: any) { + async function test_nested_and_inverse(core: Core) { await test_textList({ core, name: "/tl1", @@ -137,47 +139,47 @@ describe("TextList tag tests", async () => { // change values await updateTextInputValue({ - componentName: "/ti1", + name: "/ti1", text: "a", core, }); await updateTextInputValue({ - componentName: "/ti2", + name: "/ti2", text: "b", core, }); await updateTextInputValue({ - componentName: "/ti3", + name: "/ti3", text: "c", core, }); await updateTextInputValue({ - componentName: "/ti4", + name: "/ti4", text: "d", core, }); await updateTextInputValue({ - componentName: "/ti5", + name: "/ti5", text: "e", core, }); await updateTextInputValue({ - componentName: "/ti6", + name: "/ti6", text: "f", core, }); await updateTextInputValue({ - componentName: "/ti7", + name: "/ti7", text: "g", core, }); await updateTextInputValue({ - componentName: "/ti8", + name: "/ti8", text: "h", core, }); await updateTextInputValue({ - componentName: "/ti9", + name: "/ti9", text: "i", core, }); @@ -442,13 +444,13 @@ describe("TextList tag tests", async () => { await check_items(max1, max2); max1 = Infinity; - await updateMathInputValue({ latex: "", componentName: "/mn1", core }); + await updateMathInputValue({ latex: "", name: "/mn1", core }); await check_items(max1, max2); max2 = 3; await updateMathInputValue({ latex: max2.toString(), - componentName: "/mn2", + name: "/mn2", core, }); await check_items(max1, max2); @@ -456,7 +458,7 @@ describe("TextList tag tests", async () => { max1 = 4; await updateMathInputValue({ latex: max1.toString(), - componentName: "/mn1", + name: "/mn1", core, }); await check_items(max1, max2); @@ -464,7 +466,7 @@ describe("TextList tag tests", async () => { max1 = 1; await updateMathInputValue({ latex: max1.toString(), - componentName: "/mn1", + name: "/mn1", core, }); await check_items(max1, max2); @@ -472,7 +474,7 @@ describe("TextList tag tests", async () => { max2 = 10; await updateMathInputValue({ latex: max2.toString(), - componentName: "/mn2", + name: "/mn2", core, }); await check_items(max1, max2); @@ -527,7 +529,7 @@ describe("TextList tag tests", async () => { maxN = 4; await updateMathInputValue({ latex: maxN.toString(), - componentName: "/maxN", + name: "/maxN", core, }); await check_items(maxN); @@ -535,7 +537,7 @@ describe("TextList tag tests", async () => { maxN = 1; await updateMathInputValue({ latex: maxN.toString(), - componentName: "/maxN", + name: "/maxN", core, }); await check_items(maxN); @@ -671,7 +673,7 @@ describe("TextList tag tests", async () => { x2 = "d"; await updateTextInputValue({ text: `${x1}, ${x2}`, - componentName: "/ti", + name: "/ti", core, }); await test_textList({ diff --git a/packages/doenetml-worker/src/test/tagSpecific/triggerset.test.ts b/packages/doenetml-worker/src/test/tagSpecific/triggerset.test.ts index 284e0b660..36bafa163 100644 --- a/packages/doenetml-worker/src/test/tagSpecific/triggerset.test.ts +++ b/packages/doenetml-worker/src/test/tagSpecific/triggerset.test.ts @@ -2,13 +2,22 @@ import { describe, expect, it, vi } from "vitest"; import { createTestCore, returnAllStateVariables } from "../utils/test-core"; import { cleanLatex } from "../utils/math"; import { + moveButton, + movePoint, + clickPoint, + focusPoint, + triggerActions, updateBooleanInputValue, updateMathInputValue, + updateSelectedIndices, + updateValue, } from "../utils/actions"; import me from "math-expressions"; +import { test_in_graph } from "../utils/in-graph"; const Mock = vi.fn(); vi.stubGlobal("postMessage", Mock); +vi.mock("hyperformula"); describe("TriggerSet tag tests", async () => { async function test_5_triggered_actions(core) { @@ -36,12 +45,7 @@ describe("TriggerSet tag tests", async () => { expect(stateVariables["/hello"].stateValues.value).eq(""); expect(stateVariables["/n"].stateValues.value).eq(1); - await core.requestAction({ - componentName: "/tset", - actionName: "triggerActions", - args: {}, - event: null, - }); + await triggerActions({ name: "/tset", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/g"].activeChildren.length).eq(2); @@ -57,12 +61,7 @@ describe("TriggerSet tag tests", async () => { expect(stateVariables["/hello"].stateValues.value).eq(" hello"); expect(stateVariables["/n"].stateValues.value).eq(2); - await core.requestAction({ - componentName: "/tset", - actionName: "triggerActions", - args: {}, - event: null, - }); + await triggerActions({ name: "/tset", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/g"].activeChildren.length).eq(3); @@ -78,12 +77,7 @@ describe("TriggerSet tag tests", async () => { expect(stateVariables["/hello"].stateValues.value).eq(" hello hello"); expect(stateVariables["/n"].stateValues.value).eq(3); - await core.requestAction({ - componentName: "/tset", - actionName: "triggerActions", - args: {}, - event: null, - }); + await triggerActions({ name: "/tset", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/g"].activeChildren.length).eq(4); @@ -261,12 +255,7 @@ describe("TriggerSet tag tests", async () => { ); expect(stateVariables["/n"].stateValues.value).eq(4); - await core.requestAction({ - componentName: "/in", - actionName: "updateValue", - args: {}, - event: null, - }); + await updateValue({ name: "/in", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/n2"].stateValues.value).eq(2); @@ -308,89 +297,49 @@ describe("TriggerSet tag tests", async () => { expect(cleanLatex(stateVariables["/x"].stateValues.latex)).eq("x"); expect(cleanLatex(stateVariables["/y"].stateValues.latex)).eq("y"); - await core.requestAction({ - actionName: "movePoint", - componentName: "/P", - args: { x: -1, y: -7 }, - event: null, - }); + await movePoint({ name: "/P", x: -1, y: -7, core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/x"].stateValues.latex)).eq("x"); expect(cleanLatex(stateVariables["/y"].stateValues.latex)).eq("y"); - await core.requestAction({ - actionName: "movePoint", - componentName: "/P", - args: { x: 3, y: -4 }, - event: null, - }); + await movePoint({ name: "/P", x: 3, y: -4, core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/x"].stateValues.latex)).eq("x"); expect(cleanLatex(stateVariables["/y"].stateValues.latex)).eq("y"); - await core.requestAction({ - actionName: "movePoint", - componentName: "/P", - args: { x: 1, y: 7 }, - event: null, - }); + await movePoint({ name: "/P", x: 1, y: 7, core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/x"].stateValues.latex)).eq("3x"); expect(cleanLatex(stateVariables["/y"].stateValues.latex)).eq("4y"); - await core.requestAction({ - actionName: "movePoint", - componentName: "/P", - args: { x: 5, y: 9 }, - event: null, - }); + await movePoint({ name: "/P", x: 5, y: 9, core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/x"].stateValues.latex)).eq("3x"); expect(cleanLatex(stateVariables["/y"].stateValues.latex)).eq("4y"); - await core.requestAction({ - actionName: "movePoint", - componentName: "/P", - args: { x: -3, y: 4 }, - event: null, - }); + await movePoint({ name: "/P", x: -3, y: 4, core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/x"].stateValues.latex)).eq("3x"); expect(cleanLatex(stateVariables["/y"].stateValues.latex)).eq("4y"); - await core.requestAction({ - actionName: "movePoint", - componentName: "/P", - args: { x: -6, y: 5 }, - event: null, - }); + await movePoint({ name: "/P", x: -6, y: 5, core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/x"].stateValues.latex)).eq("3x"); expect(cleanLatex(stateVariables["/y"].stateValues.latex)).eq("4y"); - await core.requestAction({ - actionName: "movePoint", - componentName: "/P", - args: { x: 4, y: 2 }, - event: null, - }); + await movePoint({ name: "/P", x: 4, y: 2, core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/x"].stateValues.latex)).eq("9x"); expect(cleanLatex(stateVariables["/y"].stateValues.latex)).eq("16y"); - await core.requestAction({ - actionName: "movePoint", - componentName: "/P", - args: { x: 9, y: 7 }, - event: null, - }); + await movePoint({ name: "/P", x: 9, y: 7, core }); }); it("triggerSet triggered when click", async () => { @@ -416,56 +365,31 @@ describe("TriggerSet tag tests", async () => { expect(cleanLatex(stateVariables["/x"].stateValues.latex)).eq("x"); expect(cleanLatex(stateVariables["/y"].stateValues.latex)).eq("y"); - await core.requestAction({ - actionName: "movePoint", - componentName: "/P", - args: { x: -1, y: -7 }, - event: null, - }); + await movePoint({ name: "/P", x: -1, y: -7, core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/x"].stateValues.latex)).eq("x"); expect(cleanLatex(stateVariables["/y"].stateValues.latex)).eq("y"); - await core.requestAction({ - actionName: "pointClicked", - componentName: "/P", - args: { name: "/P" }, - event: null, - }); + await clickPoint({ name: "/P", core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/x"].stateValues.latex)).eq("3x"); expect(cleanLatex(stateVariables["/y"].stateValues.latex)).eq("4y"); - await core.requestAction({ - actionName: "movePoint", - componentName: "/P", - args: { x: 5, y: 9 }, - event: null, - }); + await movePoint({ name: "/P", x: 5, y: 9, core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/x"].stateValues.latex)).eq("3x"); expect(cleanLatex(stateVariables["/y"].stateValues.latex)).eq("4y"); - await core.requestAction({ - actionName: "pointClicked", - componentName: "/P", - args: { name: "/P" }, - event: null, - }); + await clickPoint({ name: "/P", core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/x"].stateValues.latex)).eq("9x"); expect(cleanLatex(stateVariables["/y"].stateValues.latex)).eq("16y"); - await core.requestAction({ - actionName: "movePoint", - componentName: "/P", - args: { x: 9, y: 7 }, - event: null, - }); + await movePoint({ name: "/P", x: 9, y: 7, core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/x"].stateValues.latex)).eq("9x"); @@ -495,56 +419,31 @@ describe("TriggerSet tag tests", async () => { expect(cleanLatex(stateVariables["/x"].stateValues.latex)).eq("x"); expect(cleanLatex(stateVariables["/y"].stateValues.latex)).eq("y"); - await core.requestAction({ - actionName: "movePoint", - componentName: "/P", - args: { x: -1, y: -7 }, - event: null, - }); + await movePoint({ name: "/P", x: -1, y: -7, core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/x"].stateValues.latex)).eq("x"); expect(cleanLatex(stateVariables["/y"].stateValues.latex)).eq("y"); - await core.requestAction({ - actionName: "pointFocused", - componentName: "/P", - args: { name: "/P" }, - event: null, - }); + await focusPoint({ name: "/P", core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/x"].stateValues.latex)).eq("3x"); expect(cleanLatex(stateVariables["/y"].stateValues.latex)).eq("4y"); - await core.requestAction({ - actionName: "movePoint", - componentName: "/P", - args: { x: 5, y: 9 }, - event: null, - }); + await movePoint({ name: "/P", x: 5, y: 9, core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/x"].stateValues.latex)).eq("3x"); expect(cleanLatex(stateVariables["/y"].stateValues.latex)).eq("4y"); - await core.requestAction({ - actionName: "pointFocused", - componentName: "/P", - args: { name: "/P" }, - event: null, - }); + await focusPoint({ name: "/P", core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/x"].stateValues.latex)).eq("9x"); expect(cleanLatex(stateVariables["/y"].stateValues.latex)).eq("16y"); - await core.requestAction({ - actionName: "movePoint", - componentName: "/P", - args: { x: 9, y: 7 }, - event: null, - }); + await movePoint({ name: "/P", x: 9, y: 7, core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/x"].stateValues.latex)).eq("9x"); @@ -588,12 +487,7 @@ describe("TriggerSet tag tests", async () => { expect(stateVariables["/n"].stateValues.value).eq(1); expect(stateVariables["/m"].stateValues.value).eq(5); - await core.requestAction({ - actionName: "movePoint", - componentName: "/P", - args: { x: -1, y: -7 }, - event: null, - }); + await movePoint({ name: "/P", x: -1, y: -7, core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/b"].stateValues.value).eq(false); @@ -601,12 +495,7 @@ describe("TriggerSet tag tests", async () => { expect(stateVariables["/n"].stateValues.value).eq(2); expect(stateVariables["/m"].stateValues.value).eq(4); - await core.requestAction({ - actionName: "movePoint", - componentName: "/P", - args: { x: 3, y: -4 }, - event: null, - }); + await movePoint({ name: "/P", x: 3, y: -4, core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/b"].stateValues.value).eq(false); @@ -614,12 +503,7 @@ describe("TriggerSet tag tests", async () => { expect(stateVariables["/n"].stateValues.value).eq(2); expect(stateVariables["/m"].stateValues.value).eq(4); - await core.requestAction({ - actionName: "movePoint", - componentName: "/P", - args: { x: 1, y: 7 }, - event: null, - }); + await movePoint({ name: "/P", x: 1, y: 7, core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/b"].stateValues.value).eq(true); @@ -627,12 +511,7 @@ describe("TriggerSet tag tests", async () => { expect(stateVariables["/n"].stateValues.value).eq(2); expect(stateVariables["/m"].stateValues.value).eq(4); - await core.requestAction({ - actionName: "movePoint", - componentName: "/P", - args: { x: 5, y: 9 }, - event: null, - }); + await movePoint({ name: "/P", x: 5, y: 9, core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/b"].stateValues.value).eq(true); @@ -640,12 +519,7 @@ describe("TriggerSet tag tests", async () => { expect(stateVariables["/n"].stateValues.value).eq(2); expect(stateVariables["/m"].stateValues.value).eq(4); - await core.requestAction({ - actionName: "movePoint", - componentName: "/P", - args: { x: -3, y: -4 }, - event: null, - }); + await movePoint({ name: "/P", x: -3, y: -4, core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/b"].stateValues.value).eq(true); @@ -653,12 +527,7 @@ describe("TriggerSet tag tests", async () => { expect(stateVariables["/n"].stateValues.value).eq(3); expect(stateVariables["/m"].stateValues.value).eq(3); - await core.requestAction({ - actionName: "movePoint", - componentName: "/P", - args: { x: -6, y: -5 }, - event: null, - }); + await movePoint({ name: "/P", x: -6, y: -5, core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/b"].stateValues.value).eq(true); @@ -666,12 +535,7 @@ describe("TriggerSet tag tests", async () => { expect(stateVariables["/n"].stateValues.value).eq(3); expect(stateVariables["/m"].stateValues.value).eq(3); - await core.requestAction({ - actionName: "movePoint", - componentName: "/P", - args: { x: 4, y: 2 }, - event: null, - }); + await movePoint({ name: "/P", x: 4, y: 2, core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/b"].stateValues.value).eq(false); @@ -679,12 +543,7 @@ describe("TriggerSet tag tests", async () => { expect(stateVariables["/n"].stateValues.value).eq(3); expect(stateVariables["/m"].stateValues.value).eq(3); - await core.requestAction({ - actionName: "movePoint", - componentName: "/P", - args: { x: 9, y: 7 }, - event: null, - }); + await movePoint({ name: "/P", x: 9, y: 7, core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/b"].stateValues.value).eq(false); @@ -748,12 +607,7 @@ describe("TriggerSet tag tests", async () => { expect(stateVariables["/hello"].stateValues.value).eq(""); expect(stateVariables["/n"].stateValues.value).eq(1); - await core.requestAction({ - actionName: "movePoint", - componentName: "/P", - args: { x: -1, y: -7 }, - event: null, - }); + await movePoint({ name: "/P", x: -1, y: -7, core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/g"].activeChildren.length).eq(1); @@ -767,12 +621,7 @@ describe("TriggerSet tag tests", async () => { expect(stateVariables["/hello"].stateValues.value).eq(""); expect(stateVariables["/n"].stateValues.value).eq(1); - await core.requestAction({ - actionName: "movePoint", - componentName: "/P", - args: { x: 3, y: -4 }, - event: null, - }); + await movePoint({ name: "/P", x: 3, y: -4, core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/g"].activeChildren.length).eq(1); @@ -786,12 +635,7 @@ describe("TriggerSet tag tests", async () => { expect(stateVariables["/hello"].stateValues.value).eq(""); expect(stateVariables["/n"].stateValues.value).eq(1); - await core.requestAction({ - actionName: "movePoint", - componentName: "/P", - args: { x: 1, y: 7 }, - event: null, - }); + await movePoint({ name: "/P", x: 1, y: 7, core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/g"].activeChildren.length).eq(2); @@ -806,12 +650,7 @@ describe("TriggerSet tag tests", async () => { expect(stateVariables["/hello"].stateValues.value).eq(" hello"); expect(stateVariables["/n"].stateValues.value).eq(2); - await core.requestAction({ - actionName: "movePoint", - componentName: "/P", - args: { x: 5, y: 9 }, - event: null, - }); + await movePoint({ name: "/P", x: 5, y: 9, core }); stateVariables = await returnAllStateVariables(core); @@ -826,12 +665,7 @@ describe("TriggerSet tag tests", async () => { expect(stateVariables["/hello"].stateValues.value).eq(" hello"); expect(stateVariables["/n"].stateValues.value).eq(2); - await core.requestAction({ - actionName: "movePoint", - componentName: "/P", - args: { x: -3, y: -4 }, - event: null, - }); + await movePoint({ name: "/P", x: -3, y: -4, core }); stateVariables = await returnAllStateVariables(core); @@ -846,12 +680,7 @@ describe("TriggerSet tag tests", async () => { expect(stateVariables["/hello"].stateValues.value).eq(" hello"); expect(stateVariables["/n"].stateValues.value).eq(2); - await core.requestAction({ - actionName: "movePoint", - componentName: "/P", - args: { x: -6, y: -5 }, - event: null, - }); + await movePoint({ name: "/P", x: -6, y: -5, core }); stateVariables = await returnAllStateVariables(core); @@ -862,12 +691,7 @@ describe("TriggerSet tag tests", async () => { .map(Number); expect(numbers2).eqls(numbers); - await core.requestAction({ - actionName: "movePoint", - componentName: "/P", - args: { x: 4, y: 2 }, - event: null, - }); + await movePoint({ name: "/P", x: 4, y: 2, core }); stateVariables = await returnAllStateVariables(core); @@ -883,12 +707,7 @@ describe("TriggerSet tag tests", async () => { expect(stateVariables["/hello"].stateValues.value).eq(" hello hello"); expect(stateVariables["/n"].stateValues.value).eq(3); - await core.requestAction({ - actionName: "movePoint", - componentName: "/P", - args: { x: 9, y: 7 }, - event: null, - }); + await movePoint({ name: "/P", x: 9, y: 7, core }); stateVariables = await returnAllStateVariables(core); @@ -943,12 +762,7 @@ describe("TriggerSet tag tests", async () => { expect(stateVariables["/n"].stateValues.value).eq(1); expect(stateVariables["/m"].stateValues.value).eq(5); - await core.requestAction({ - actionName: "movePoint", - componentName: "/P", - args: { x: -1, y: -7 }, - event: null, - }); + await movePoint({ name: "/P", x: -1, y: -7, core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/g"].activeChildren.length).eq(1); @@ -957,12 +771,7 @@ describe("TriggerSet tag tests", async () => { expect(stateVariables["/n"].stateValues.value).eq(1); expect(stateVariables["/m"].stateValues.value).eq(4); - await core.requestAction({ - actionName: "movePoint", - componentName: "/P", - args: { x: 3, y: -4 }, - event: null, - }); + await movePoint({ name: "/P", x: 3, y: -4, core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/g"].activeChildren.length).eq(1); @@ -971,12 +780,7 @@ describe("TriggerSet tag tests", async () => { expect(stateVariables["/n"].stateValues.value).eq(1); expect(stateVariables["/m"].stateValues.value).eq(4); - await core.requestAction({ - actionName: "movePoint", - componentName: "/P", - args: { x: 1, y: 7 }, - event: null, - }); + await movePoint({ name: "/P", x: 1, y: 7, core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/g"].activeChildren.length).eq(2); @@ -985,12 +789,7 @@ describe("TriggerSet tag tests", async () => { expect(stateVariables["/n"].stateValues.value).eq(2); expect(stateVariables["/m"].stateValues.value).eq(4); - await core.requestAction({ - actionName: "movePoint", - componentName: "/P", - args: { x: 5, y: 9 }, - event: null, - }); + await movePoint({ name: "/P", x: 5, y: 9, core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/g"].activeChildren.length).eq(2); @@ -999,12 +798,7 @@ describe("TriggerSet tag tests", async () => { expect(stateVariables["/n"].stateValues.value).eq(2); expect(stateVariables["/m"].stateValues.value).eq(4); - await core.requestAction({ - actionName: "movePoint", - componentName: "/P", - args: { x: -3, y: -4 }, - event: null, - }); + await movePoint({ name: "/P", x: -3, y: -4, core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/g"].activeChildren.length).eq(2); @@ -1013,12 +807,7 @@ describe("TriggerSet tag tests", async () => { expect(stateVariables["/n"].stateValues.value).eq(2); expect(stateVariables["/m"].stateValues.value).eq(3); - await core.requestAction({ - actionName: "movePoint", - componentName: "/P", - args: { x: -6, y: -5 }, - event: null, - }); + await movePoint({ name: "/P", x: -6, y: -5, core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/g"].activeChildren.length).eq(2); @@ -1027,12 +816,7 @@ describe("TriggerSet tag tests", async () => { expect(stateVariables["/n"].stateValues.value).eq(2); expect(stateVariables["/m"].stateValues.value).eq(3); - await core.requestAction({ - actionName: "movePoint", - componentName: "/P", - args: { x: 4, y: 2 }, - event: null, - }); + await movePoint({ name: "/P", x: 4, y: 2, core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/g"].activeChildren.length).eq(3); @@ -1041,12 +825,7 @@ describe("TriggerSet tag tests", async () => { expect(stateVariables["/n"].stateValues.value).eq(3); expect(stateVariables["/m"].stateValues.value).eq(3); - await core.requestAction({ - actionName: "movePoint", - componentName: "/P", - args: { x: 9, y: 7 }, - event: null, - }); + await movePoint({ name: "/P", x: 9, y: 7, core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/g"].activeChildren.length).eq(3); @@ -1092,18 +871,17 @@ describe("TriggerSet tag tests", async () => { }); it("triggerSet in graph", async () => { - let core = await createTestCore({ - doenetML: ` + const doenetMLsnippet = `

n: 1

- + (3,4) - + (-3,-4) @@ -1112,451 +890,9 @@ describe("TriggerSet tag tests", async () => { + `; -

Anchor 1 coordinates: $triggerSet1.anchor

-

Anchor 2 coordinates: $triggerSet2.anchor

-

Change anchor 1 coordinates:

-

Change anchor 2 coordinates:

-

Position from anchor 1: $triggerSet1.positionFromAnchor

-

Position from anchor 2: $triggerSet2.positionFromAnchor

-

Change position from anchor 1 - - upperRight - upperLeft - lowerRight - lowerLeft - left - right - top - bottom - center - -

-

Change position from anchor 2 - - upperRight - upperLeft - lowerRight - lowerLeft - left - right - top - bottom - center - -

-

Draggable 1: $draggable1

-

Draggable 2: $draggable2

-

Change draggable 1

-

Change draggable 2

-

Disabled 1: $disabled1

-

Disabled 2: $disabled2

-

Change disabled 1

-

Change disabled 2

-

Fixed 1: $fixed1

-

Fixed 2: $fixed2

-

Change fixed 1

-

Change fixed 2

-

FixLocation 1: $fixLocation1

-

FixLocation 2: $fixLocation2

-

Change fixLocation 1

-

Change fixLocation 2

- - `, - }); - - // TODO: how to click on the buttons and test if they are disabled? - - let stateVariables = await returnAllStateVariables(core); - expect(stateVariables["/pAnchor1"].stateValues.text).eq( - "Anchor 1 coordinates: ( 1, 3 )", - ); - expect(stateVariables["/pAnchor2"].stateValues.text).eq( - "Anchor 2 coordinates: ( 0, 0 )", - ); - expect(stateVariables["/pPositionFromAnchor1"].stateValues.text).eq( - "Position from anchor 1: upperright", - ); - expect(stateVariables["/pPositionFromAnchor2"].stateValues.text).eq( - "Position from anchor 2: center", - ); - expect( - stateVariables["/positionFromAnchor1"].stateValues.selectedIndices, - ).eqls([1]); - expect( - stateVariables["/positionFromAnchor2"].stateValues.selectedIndices, - ).eqls([9]); - expect(stateVariables["/pDraggable1"].stateValues.text).eq( - "Draggable 1: true", - ); - expect(stateVariables["/pDraggable2"].stateValues.text).eq( - "Draggable 2: true", - ); - expect(stateVariables["/pDisabled1"].stateValues.text).eq( - "Disabled 1: true", - ); - expect(stateVariables["/pDisabled2"].stateValues.text).eq( - "Disabled 2: false", - ); - expect(stateVariables["/pFixed1"].stateValues.text).eq( - "Fixed 1: false", - ); - expect(stateVariables["/pFixed2"].stateValues.text).eq( - "Fixed 2: false", - ); - expect(stateVariables["/pFixLocation1"].stateValues.text).eq( - "FixLocation 1: false", - ); - expect(stateVariables["/pFixLocation2"].stateValues.text).eq( - "FixLocation 2: false", - ); - - // move triggerSets by dragging - - await core.requestAction({ - actionName: "moveButton", - componentName: "/triggerSet1", - args: { x: -2, y: 3 }, - event: null, - }); - await core.requestAction({ - actionName: "moveButton", - componentName: "/triggerSet2", - args: { x: 4, y: -5 }, - event: null, - }); - - stateVariables = await returnAllStateVariables(core); - expect(stateVariables["/pAnchor1"].stateValues.text).eq( - "Anchor 1 coordinates: ( -2, 3 )", - ); - expect(stateVariables["/pAnchor2"].stateValues.text).eq( - "Anchor 2 coordinates: ( 4, -5 )", - ); - - // move triggerSets by entering coordinates - await updateMathInputValue({ - latex: "(6,7)", - componentName: "/anchorCoords1", - core, - }); - await updateMathInputValue({ - latex: "(8,9)", - componentName: "/anchorCoords2", - core, - }); - - stateVariables = await returnAllStateVariables(core); - expect(stateVariables["/pAnchor1"].stateValues.text).eq( - "Anchor 1 coordinates: ( 6, 7 )", - ); - expect(stateVariables["/pAnchor2"].stateValues.text).eq( - "Anchor 2 coordinates: ( 8, 9 )", - ); - - // change position from anchor - await core.requestAction({ - componentName: "/positionFromAnchor1", - actionName: "updateSelectedIndices", - args: { - selectedIndices: [4], - }, - event: null, - }); - await core.requestAction({ - componentName: "/positionFromAnchor2", - actionName: "updateSelectedIndices", - args: { - selectedIndices: [3], - }, - event: null, - }); - stateVariables = await returnAllStateVariables(core); - - expect(stateVariables["/pPositionFromAnchor1"].stateValues.text).eq( - "Position from anchor 1: lowerleft", - ); - expect(stateVariables["/pPositionFromAnchor2"].stateValues.text).eq( - "Position from anchor 2: lowerright", - ); - - // make not draggable - await updateBooleanInputValue({ - boolean: false, - componentName: "/draggable1", - core, - }); - await updateBooleanInputValue({ - boolean: false, - componentName: "/draggable2", - core, - }); - stateVariables = await returnAllStateVariables(core); - - expect(stateVariables["/pDraggable1"].stateValues.text).eq( - "Draggable 1: false", - ); - expect(stateVariables["/pDraggable2"].stateValues.text).eq( - "Draggable 2: false", - ); - - // cannot move triggerSets by dragging - await core.requestAction({ - actionName: "moveButton", - componentName: "/triggerSet1", - args: { x: -10, y: -9 }, - event: null, - }); - await core.requestAction({ - actionName: "moveButton", - componentName: "/triggerSet2", - args: { x: -8, y: -7 }, - event: null, - }); - - stateVariables = await returnAllStateVariables(core); - - expect(stateVariables["/pAnchor1"].stateValues.text).eq( - "Anchor 1 coordinates: ( 6, 7 )", - ); - expect(stateVariables["/pAnchor2"].stateValues.text).eq( - "Anchor 2 coordinates: ( 8, 9 )", - ); - - // make draggable again - await updateBooleanInputValue({ - boolean: true, - componentName: "/draggable1", - core, - }); - await updateBooleanInputValue({ - boolean: true, - componentName: "/draggable2", - core, - }); - stateVariables = await returnAllStateVariables(core); - - expect(stateVariables["/pDraggable1"].stateValues.text).eq( - "Draggable 1: true", - ); - expect(stateVariables["/pDraggable2"].stateValues.text).eq( - "Draggable 2: true", - ); - - await core.requestAction({ - actionName: "moveButton", - componentName: "/triggerSet1", - args: { x: -10, y: -9 }, - event: null, - }); - await core.requestAction({ - actionName: "moveButton", - componentName: "/triggerSet2", - args: { x: -8, y: -7 }, - event: null, - }); - - stateVariables = await returnAllStateVariables(core); - - expect(stateVariables["/pAnchor1"].stateValues.text).eq( - "Anchor 1 coordinates: ( -10, -9 )", - ); - expect(stateVariables["/pAnchor2"].stateValues.text).eq( - "Anchor 2 coordinates: ( -8, -7 )", - ); - - // fix location - await updateBooleanInputValue({ - boolean: true, - componentName: "/fixLocation1", - core, - }); - await updateBooleanInputValue({ - boolean: true, - componentName: "/fixLocation2", - core, - }); - stateVariables = await returnAllStateVariables(core); - - expect(stateVariables["/pFixLocation1"].stateValues.text).eq( - "FixLocation 1: true", - ); - expect(stateVariables["/pFixLocation2"].stateValues.text).eq( - "FixLocation 2: true", - ); - - // can change coordinates entering coordinates only for button 1 - - await updateMathInputValue({ - latex: "(1,2)", - componentName: "/anchorCoords1", - core, - }); - await updateMathInputValue({ - latex: "(3,4)", - componentName: "/anchorCoords2", - core, - }); - - stateVariables = await returnAllStateVariables(core); - - expect(stateVariables["/pAnchor1"].stateValues.text).eq( - "Anchor 1 coordinates: ( 1, 2 )", - ); - expect(stateVariables["/pAnchor2"].stateValues.text).eq( - "Anchor 2 coordinates: ( -8, -7 )", - ); - - // cannot move triggerSets by dragging - await core.requestAction({ - actionName: "moveButton", - componentName: "/updateValue1", - args: { x: 4, y: 6 }, - event: null, - }); - await core.requestAction({ - actionName: "moveButton", - componentName: "/updateValue2", - args: { x: 7, y: 8 }, - event: null, - }); - - stateVariables = await returnAllStateVariables(core); - - expect(stateVariables["/pAnchor1"].stateValues.text).eq( - "Anchor 1 coordinates: ( 1, 2 )", - ); - expect(stateVariables["/pAnchor2"].stateValues.text).eq( - "Anchor 2 coordinates: ( -8, -7 )", - ); - - // can change position from anchor only for button 1 - await core.requestAction({ - componentName: "/positionFromAnchor1", - actionName: "updateSelectedIndices", - args: { - selectedIndices: [7], - }, - event: null, - }); - await core.requestAction({ - componentName: "/positionFromAnchor2", - actionName: "updateSelectedIndices", - args: { - selectedIndices: [8], - }, - event: null, - }); - stateVariables = await returnAllStateVariables(core); - - expect(stateVariables["/pPositionFromAnchor1"].stateValues.text).eq( - "Position from anchor 1: top", - ); - expect(stateVariables["/pPositionFromAnchor2"].stateValues.text).eq( - "Position from anchor 2: lowerright", - ); - - // can change disabled attribute - await updateBooleanInputValue({ - boolean: false, - componentName: "/disabled1", - core, - }); - await updateBooleanInputValue({ - boolean: true, - componentName: "/disabled2", - core, - }); - stateVariables = await returnAllStateVariables(core); - - expect(stateVariables["/pDisabled1"].stateValues.text).eq( - "Disabled 1: false", - ); - expect(stateVariables["/pDisabled2"].stateValues.text).eq( - "Disabled 2: true", - ); - // make completely fixed - await updateBooleanInputValue({ - boolean: true, - componentName: "/fixed1", - core, - }); - await updateBooleanInputValue({ - boolean: true, - componentName: "/fixed2", - core, - }); - stateVariables = await returnAllStateVariables(core); - - expect(stateVariables["/pFixed1"].stateValues.text).eq("Fixed 1: true"); - expect(stateVariables["/pFixed2"].stateValues.text).eq("Fixed 2: true"); - - // can change coordinates entering coordinates only for button 1 - await updateMathInputValue({ - latex: "(5,6)", - componentName: "/anchorCoords1", - core, - }); - await updateMathInputValue({ - latex: "(7,8)", - componentName: "/anchorCoords2", - core, - }); - - stateVariables = await returnAllStateVariables(core); - - expect(stateVariables["/pAnchor1"].stateValues.text).eq( - "Anchor 1 coordinates: ( 5, 6 )", - ); - expect(stateVariables["/pAnchor2"].stateValues.text).eq( - "Anchor 2 coordinates: ( -8, -7 )", - ); - - // can change position from anchor only for button 1 - await core.requestAction({ - componentName: "/positionFromAnchor1", - actionName: "updateSelectedIndices", - args: { - selectedIndices: [6], - }, - event: null, - }); - await core.requestAction({ - componentName: "/positionFromAnchor2", - actionName: "updateSelectedIndices", - args: { - selectedIndices: [5], - }, - event: null, - }); - stateVariables = await returnAllStateVariables(core); - - expect(stateVariables["/pPositionFromAnchor1"].stateValues.text).eq( - "Position from anchor 1: right", - ); - expect(stateVariables["/pPositionFromAnchor2"].stateValues.text).eq( - "Position from anchor 2: lowerright", - ); - - // can change disabled attribute only for button 1 - await updateBooleanInputValue({ - boolean: true, - componentName: "/disabled1", - core, - }); - await updateBooleanInputValue({ - boolean: false, - componentName: "/disabled2", - core, - }); - stateVariables = await returnAllStateVariables(core); - - expect(stateVariables["/pDisabled1"].stateValues.text).eq( - "Disabled 1: true", - ); - expect(stateVariables["/pDisabled2"].stateValues.text).eq( - "Disabled 2: true", - ); + await test_in_graph(doenetMLsnippet, moveButton); }); it("buttons can be styled", async () => { diff --git a/packages/doenetml-worker/src/test/tagSpecific/updatevalue.test.ts b/packages/doenetml-worker/src/test/tagSpecific/updatevalue.test.ts index 5804474a9..d4387d9d4 100644 --- a/packages/doenetml-worker/src/test/tagSpecific/updatevalue.test.ts +++ b/packages/doenetml-worker/src/test/tagSpecific/updatevalue.test.ts @@ -2,13 +2,22 @@ import { describe, expect, it, vi } from "vitest"; import { createTestCore, returnAllStateVariables } from "../utils/test-core"; import { cleanLatex } from "../utils/math"; import { + moveButton, + movePoint, + clickPoint, + focusPoint, + triggerActions, updateBooleanInputValue, updateMathInputValue, + updateSelectedIndices, + updateValue, } from "../utils/actions"; import me from "math-expressions"; +import { test_in_graph } from "../utils/in-graph"; const Mock = vi.fn(); vi.stubGlobal("postMessage", Mock); +vi.mock("hyperformula"); describe("UpdateValue tag tests", async () => { it("incrementing graph of line segments", async () => { @@ -79,12 +88,7 @@ describe("UpdateValue tag tests", async () => { } // double number - await core.requestAction({ - componentName: "/uv", - actionName: "updateValue", - args: {}, - event: null, - }); + await updateValue({ name: "/uv", core }); stateVariables = await returnAllStateVariables(core); count = 4; @@ -129,12 +133,7 @@ describe("UpdateValue tag tests", async () => { } // double number a second time - await core.requestAction({ - componentName: "/uv", - actionName: "updateValue", - args: {}, - event: null, - }); + await updateValue({ name: "/uv", core }); stateVariables = await returnAllStateVariables(core); @@ -180,12 +179,7 @@ describe("UpdateValue tag tests", async () => { } // double number a third time - await core.requestAction({ - componentName: "/uv", - actionName: "updateValue", - args: {}, - event: null, - }); + await updateValue({ name: "/uv", core }); stateVariables = await returnAllStateVariables(core); count = 16; @@ -243,22 +237,12 @@ describe("UpdateValue tag tests", async () => { let stateVariables = await returnAllStateVariables(core); expect(stateVariables["/b"].stateValues.value).eq(false); - await core.requestAction({ - componentName: "/uv", - actionName: "updateValue", - args: {}, - event: null, - }); + await updateValue({ name: "/uv", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/b"].stateValues.value).eq(true); - await core.requestAction({ - componentName: "/uv", - actionName: "updateValue", - args: {}, - event: null, - }); + await updateValue({ name: "/uv", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/b"].stateValues.value).eq(false); @@ -282,48 +266,23 @@ describe("UpdateValue tag tests", async () => { let stateVariables = await returnAllStateVariables(core); expect(stateVariables["/b"].stateValues.value).eq(false); - await core.requestAction({ - componentName: "/setTrue", - actionName: "updateValue", - args: {}, - event: null, - }); + await updateValue({ name: "/setTrue", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/b"].stateValues.value).eq(true); - await core.requestAction({ - componentName: "/setTrue", - actionName: "updateValuesetTrue", - args: {}, - event: null, - }); + await updateValue({ name: "/setTrue", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/b"].stateValues.value).eq(true); - await core.requestAction({ - componentName: "/setFalse", - actionName: "updateValue", - args: {}, - event: null, - }); + await updateValue({ name: "/setFalse", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/b"].stateValues.value).eq(false); - await core.requestAction({ - componentName: "/setFalse", - actionName: "updateValue", - args: {}, - event: null, - }); + await updateValue({ name: "/setFalse", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/b"].stateValues.value).eq(false); - await core.requestAction({ - componentName: "/setTrue", - actionName: "updateValue", - args: {}, - event: null, - }); + await updateValue({ name: "/setTrue", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/b"].stateValues.value).eq(true); }); @@ -341,12 +300,7 @@ describe("UpdateValue tag tests", async () => { let stateVariables = await returnAllStateVariables(core); expect(stateVariables["/n"].stateValues.value).eq(1); - await core.requestAction({ - componentName: "/setToSum", - actionName: "updateValue", - args: {}, - event: null, - }); + await updateValue({ name: "/setToSum", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/n"].stateValues.value).eq(2); @@ -369,21 +323,11 @@ describe("UpdateValue tag tests", async () => { let stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/P"].stateValues.latex)).eq("(1,2)"); - await core.requestAction({ - componentName: "/uv1", - actionName: "updateValue", - args: {}, - event: null, - }); + await updateValue({ name: "/uv1", core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/P"].stateValues.latex)).eq("(2,2)"); - await core.requestAction({ - componentName: "/uv2", - actionName: "updateValue", - args: {}, - event: null, - }); + await updateValue({ name: "/uv2", core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/P"].stateValues.latex)).eq("(4,2)"); }); @@ -394,48 +338,28 @@ describe("UpdateValue tag tests", async () => { expect(cleanLatex(stateVariables["/p2"].stateValues.latex)).eq("(1,5)"); expect(cleanLatex(stateVariables["/p3"].stateValues.latex)).eq("(7,0)"); - await core.requestAction({ - componentName: "/uv1", - actionName: "updateValue", - args: {}, - event: null, - }); + await updateValue({ name: "/uv1", core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/p"].stateValues.latex)).eq("(3,2)"); expect(cleanLatex(stateVariables["/p2"].stateValues.latex)).eq("(6,5)"); expect(cleanLatex(stateVariables["/p3"].stateValues.latex)).eq("(7,0)"); - await core.requestAction({ - componentName: "/uv1", - actionName: "updateValue", - args: {}, - event: null, - }); + await updateValue({ name: "/uv1", core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/p"].stateValues.latex)).eq("(3,2)"); expect(cleanLatex(stateVariables["/p2"].stateValues.latex)).eq("(6,5)"); expect(cleanLatex(stateVariables["/p3"].stateValues.latex)).eq("(7,0)"); - await core.requestAction({ - componentName: "/uv2", - actionName: "updateValue", - args: {}, - event: null, - }); + await updateValue({ name: "/uv2", core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/p"].stateValues.latex)).eq("(3,2)"); expect(cleanLatex(stateVariables["/p2"].stateValues.latex)).eq("(6,5)"); expect(cleanLatex(stateVariables["/p3"].stateValues.latex)).eq("(6,0)"); - await core.requestAction({ - componentName: "/uv2", - actionName: "updateValue", - args: {}, - event: null, - }); + await updateValue({ name: "/uv2", core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/p"].stateValues.latex)).eq("(3,2)"); @@ -505,12 +429,7 @@ describe("UpdateValue tag tests", async () => { "Text hello and line 0 = x - y + 1.", ); - await core.requestAction({ - componentName: "/uv1", - actionName: "updateValue", - args: {}, - event: null, - }); + await updateValue({ name: "/uv1", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/p1"].stateValues.text).eq( @@ -520,12 +439,7 @@ describe("UpdateValue tag tests", async () => { "Text hello and line 0 = x - y + 1.", ); - await core.requestAction({ - componentName: "/uv2", - actionName: "updateValue", - args: {}, - event: null, - }); + await updateValue({ name: "/uv2", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/p1"].stateValues.text).eq( @@ -535,12 +449,7 @@ describe("UpdateValue tag tests", async () => { "Text hello and line 0 = x - y + 1.", ); - await core.requestAction({ - componentName: "/uv3", - actionName: "updateValue", - args: {}, - event: null, - }); + await updateValue({ name: "/uv3", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/p1"].stateValues.text).eq( @@ -550,12 +459,7 @@ describe("UpdateValue tag tests", async () => { "Text bye and line 0 = x - y + 1.", ); - await core.requestAction({ - componentName: "/uv4", - actionName: "updateValue", - args: {}, - event: null, - }); + await updateValue({ name: "/uv4", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/p1"].stateValues.text).eq( @@ -583,48 +487,28 @@ describe("UpdateValue tag tests", async () => { "(3,2,1)", ); - await core.requestAction({ - componentName: "/uv1", - actionName: "updateValue", - args: {}, - event: null, - }); + await updateValue({ name: "/uv1", core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/p"].stateValues.latex)).eq( "(3,6,1)", ); - await core.requestAction({ - componentName: "/uv1", - actionName: "updateValue", - args: {}, - event: null, - }); + await updateValue({ name: "/uv1", core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/p"].stateValues.latex)).eq( "(3,6,1)", ); - await core.requestAction({ - componentName: "/uv2", - actionName: "updateValue", - args: {}, - event: null, - }); + await updateValue({ name: "/uv2", core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/p"].stateValues.latex)).eq( "(3,6,6)", ); - await core.requestAction({ - componentName: "/uv2", - actionName: "updateValue", - args: {}, - event: null, - }); + await updateValue({ name: "/uv2", core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/p"].stateValues.latex)).eq( @@ -653,24 +537,14 @@ describe("UpdateValue tag tests", async () => { expect(cleanLatex(stateVariables["/p2"].stateValues.latex)).eq("(1,5)"); expect(cleanLatex(stateVariables["/p3"].stateValues.latex)).eq("(7,0)"); - await core.requestAction({ - componentName: "/uv1", - actionName: "updateValue", - args: {}, - event: null, - }); + await updateValue({ name: "/uv1", core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/p"].stateValues.latex)).eq("(6,2)"); expect(cleanLatex(stateVariables["/p2"].stateValues.latex)).eq("(6,5)"); expect(cleanLatex(stateVariables["/p3"].stateValues.latex)).eq("(6,0)"); - await core.requestAction({ - componentName: "/uv2", - actionName: "updateValue", - args: {}, - event: null, - }); + await updateValue({ name: "/uv2", core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/p"].stateValues.latex)).eq("(12,2)"); @@ -704,52 +578,27 @@ describe("UpdateValue tag tests", async () => { let stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/P"].stateValues.latex)).eq("(1,2)"); - await core.requestAction({ - componentName: "/uv1", - actionName: "updateValue", - args: {}, - event: null, - }); + await updateValue({ name: "/uv1", core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/P"].stateValues.latex)).eq("(2,2)"); - await core.requestAction({ - componentName: "/uv2", - actionName: "updateValue", - args: {}, - event: null, - }); + await updateValue({ name: "/uv2", core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/P"].stateValues.latex)).eq("(4,2)"); - await core.requestAction({ - componentName: "/uv3", - actionName: "updateValue", - args: {}, - event: null, - }); + await updateValue({ name: "/uv3", core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/P"].stateValues.latex)).eq("(3,7)"); - await core.requestAction({ - componentName: "/uv1", - actionName: "updateValue", - args: {}, - event: null, - }); + await updateValue({ name: "/uv1", core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/P"].stateValues.latex)).eq("(6,7)"); - await core.requestAction({ - componentName: "/uv2", - actionName: "updateValue", - args: {}, - event: null, - }); + await updateValue({ name: "/uv2", core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/P"].stateValues.latex)).eq("(12,7)"); @@ -773,23 +622,13 @@ describe("UpdateValue tag tests", async () => { expect(cleanLatex(stateVariables["/y"].stateValues.latex)).eq("y"); expect(stateVariables["/quad"].stateValues.hidden).eq(true); - await core.requestAction({ - componentName: "/trip", - actionName: "updateValue", - args: {}, - event: null, - }); + await updateValue({ name: "/trip", core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/x"].stateValues.latex)).eq("3x"); expect(cleanLatex(stateVariables["/y"].stateValues.latex)).eq("4y"); - await core.requestAction({ - componentName: "/trip", - actionName: "updateValue", - args: {}, - event: null, - }); + await updateValue({ name: "/trip", core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/x"].stateValues.latex)).eq("9x"); @@ -819,12 +658,7 @@ describe("UpdateValue tag tests", async () => { expect(cleanLatex(stateVariables["/z"].stateValues.latex)).eq("z"); expect(stateVariables["/quad"].stateValues.hidden).eq(true); - await core.requestAction({ - componentName: "/trip", - actionName: "updateValue", - args: {}, - event: null, - }); + await updateValue({ name: "/trip", core }); stateVariables = await returnAllStateVariables(core); @@ -832,12 +666,7 @@ describe("UpdateValue tag tests", async () => { expect(cleanLatex(stateVariables["/y"].stateValues.latex)).eq("4y"); expect(cleanLatex(stateVariables["/z"].stateValues.latex)).eq("z"); - await core.requestAction({ - componentName: "/doub", - actionName: "updateValue", - args: {}, - event: null, - }); + await updateValue({ name: "/doub", core }); stateVariables = await returnAllStateVariables(core); @@ -845,12 +674,7 @@ describe("UpdateValue tag tests", async () => { expect(cleanLatex(stateVariables["/y"].stateValues.latex)).eq("16y"); expect(cleanLatex(stateVariables["/z"].stateValues.latex)).eq("2z"); - await core.requestAction({ - componentName: "/trip", - actionName: "updateValue", - args: {}, - event: null, - }); + await updateValue({ name: "/trip", core }); stateVariables = await returnAllStateVariables(core); @@ -881,36 +705,21 @@ describe("UpdateValue tag tests", async () => { expect(stateVariables["/m1"].stateValues.value).eq(1); expect(stateVariables["/m2"].stateValues.value).eq(1); - await core.requestAction({ - componentName: "/uv", - actionName: "updateValue", - args: {}, - event: null, - }); + await updateValue({ name: "/uv", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/n"].stateValues.value).eq(2); expect(stateVariables["/m1"].stateValues.value).eq(2); expect(stateVariables["/m2"].stateValues.value).eq(1); - await core.requestAction({ - componentName: "/uv2", - actionName: "updateValue", - args: {}, - event: null, - }); + await updateValue({ name: "/uv2", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/n"].stateValues.value).eq(3); expect(stateVariables["/m1"].stateValues.value).eq(2); expect(stateVariables["/m2"].stateValues.value).eq(2); - await core.requestAction({ - componentName: "/uv3", - actionName: "updateValue", - args: {}, - event: null, - }); + await updateValue({ name: "/uv3", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/n"].stateValues.value).eq(4); @@ -922,12 +731,7 @@ describe("UpdateValue tag tests", async () => { let macroName = stateVariables["/pmacro"].activeChildren[0].componentName; - await core.requestAction({ - componentName: macroName, - actionName: "updateValue", - args: {}, - event: null, - }); + await updateValue({ name: macroName, core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/n"].stateValues.value).eq(5); @@ -951,82 +755,42 @@ describe("UpdateValue tag tests", async () => { expect(cleanLatex(stateVariables["/x"].stateValues.latex)).eq("x"); expect(stateVariables["/trip"].stateValues.hidden).eq(true); - await core.requestAction({ - componentName: "/P", - actionName: "movePoint", - args: { x: -1, y: -7 }, - event: null, - }); + await movePoint({ name: "/P", x: -1, y: -7, core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/x"].stateValues.latex)).eq("x"); - await core.requestAction({ - componentName: "/P", - actionName: "movePoint", - args: { x: 3, y: -4 }, - event: null, - }); + await movePoint({ name: "/P", x: 3, y: -4, core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/x"].stateValues.latex)).eq("x"); - await core.requestAction({ - componentName: "/P", - actionName: "movePoint", - args: { x: 1, y: 7 }, - event: null, - }); + await movePoint({ name: "/P", x: 1, y: 7, core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/x"].stateValues.latex)).eq("3x"); - await core.requestAction({ - componentName: "/P", - actionName: "movePoint", - args: { x: 5, y: 9 }, - event: null, - }); + await movePoint({ name: "/P", x: 5, y: 9, core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/x"].stateValues.latex)).eq("3x"); - await core.requestAction({ - componentName: "/P", - actionName: "movePoint", - args: { x: -4, y: 4 }, - event: null, - }); + await movePoint({ name: "/P", x: -4, y: 4, core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/x"].stateValues.latex)).eq("3x"); - await core.requestAction({ - componentName: "/P", - actionName: "movePoint", - args: { x: -6, y: 5 }, - event: null, - }); + await movePoint({ name: "/P", x: -6, y: 5, core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/x"].stateValues.latex)).eq("3x"); - await core.requestAction({ - componentName: "/P", - actionName: "movePoint", - args: { x: 4, y: 2 }, - event: null, - }); + await movePoint({ name: "/P", x: 4, y: 2, core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/x"].stateValues.latex)).eq("9x"); - await core.requestAction({ - componentName: "/P", - actionName: "movePoint", - args: { x: 9, y: 7 }, - event: null, - }); + await movePoint({ name: "/P", x: 9, y: 7, core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/x"].stateValues.latex)).eq("9x"); @@ -1048,52 +812,27 @@ describe("UpdateValue tag tests", async () => { expect(cleanLatex(stateVariables["/x"].stateValues.latex)).eq("x"); expect(stateVariables["/trip"].stateValues.hidden).eq(true); - await core.requestAction({ - componentName: "/P", - actionName: "movePoint", - args: { x: -1, y: -7 }, - event: null, - }); + await movePoint({ name: "/P", x: -1, y: -7, core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/x"].stateValues.latex)).eq("x"); - await core.requestAction({ - componentName: "/P", - actionName: "pointClicked", - args: { name: "/P" }, - event: null, - }); + await clickPoint({ name: "/P", core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/x"].stateValues.latex)).eq("3x"); - await core.requestAction({ - componentName: "/P", - actionName: "movePoint", - args: { x: 5, y: 9 }, - event: null, - }); + await movePoint({ name: "/P", x: 5, y: 9, core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/x"].stateValues.latex)).eq("3x"); - await core.requestAction({ - componentName: "/P", - actionName: "pointClicked", - args: { name: "/P" }, - event: null, - }); + await clickPoint({ name: "/P", core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/x"].stateValues.latex)).eq("9x"); - await core.requestAction({ - componentName: "/P", - actionName: "movePoint", - args: { x: 9, y: 7 }, - event: null, - }); + await movePoint({ name: "/P", x: 9, y: 7, core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/x"].stateValues.latex)).eq("9x"); @@ -1115,52 +854,27 @@ describe("UpdateValue tag tests", async () => { expect(cleanLatex(stateVariables["/x"].stateValues.latex)).eq("x"); expect(stateVariables["/trip"].stateValues.hidden).eq(true); - await core.requestAction({ - componentName: "/P", - actionName: "movePoint", - args: { x: -1, y: -7 }, - event: null, - }); + await movePoint({ name: "/P", x: -1, y: -7, core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/x"].stateValues.latex)).eq("x"); - await core.requestAction({ - componentName: "/P", - actionName: "pointFocused", - args: { name: "/P" }, - event: null, - }); + await focusPoint({ name: "/P", core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/x"].stateValues.latex)).eq("3x"); - await core.requestAction({ - componentName: "/P", - actionName: "movePoint", - args: { x: 5, y: 9 }, - event: null, - }); + await movePoint({ name: "/P", x: 5, y: 9, core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/x"].stateValues.latex)).eq("3x"); - await core.requestAction({ - componentName: "/P", - actionName: "pointFocused", - args: { name: "/P" }, - event: null, - }); + await focusPoint({ name: "/P", core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/x"].stateValues.latex)).eq("9x"); - await core.requestAction({ - componentName: "/P", - actionName: "movePoint", - args: { x: 9, y: 7 }, - event: null, - }); + await movePoint({ name: "/P", x: 9, y: 7, core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/x"].stateValues.latex)).eq("9x"); @@ -1198,45 +912,25 @@ describe("UpdateValue tag tests", async () => { // clicking unnamed copy triggers update - await core.requestAction({ - componentName: PcopyName, - actionName: "pointClicked", - args: { name: PcopyName }, - event: null, - }); + await clickPoint({ name: PcopyName, core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/x"].stateValues.latex)).eq("3x"); // clicking copy with an assignNames does not trigger update - await core.requestAction({ - componentName: "/P2", - actionName: "pointClicked", - args: { name: "/P2" }, - event: null, - }); + await clickPoint({ name: "/P2", core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/x"].stateValues.latex)).eq("3x"); // clicking point with copySource does not trigger update - await core.requestAction({ - componentName: "/_point2", - actionName: "pointClicked", - args: { name: "/_point2" }, - event: null, - }); + await clickPoint({ name: "/point2", core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/x"].stateValues.latex)).eq("3x"); // clicking unnamed copy triggers update again - await core.requestAction({ - componentName: PcopyName, - actionName: "pointClicked", - args: { name: PcopyName }, - event: null, - }); + await clickPoint({ name: PcopyName, core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/x"].stateValues.latex)).eq("9x"); @@ -1262,89 +956,49 @@ describe("UpdateValue tag tests", async () => { expect(stateVariables["/trip"].stateValues.hidden).eq(true); expect(stateVariables["/quad"].stateValues.hidden).eq(true); - await core.requestAction({ - componentName: "/P", - actionName: "movePoint", - args: { x: -1, y: -7 }, - event: null, - }); + await movePoint({ name: "/P", x: -1, y: -7, core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/x"].stateValues.latex)).eq("x"); expect(cleanLatex(stateVariables["/y"].stateValues.latex)).eq("y"); - await core.requestAction({ - componentName: "/P", - actionName: "movePoint", - args: { x: 3, y: -4 }, - event: null, - }); + await movePoint({ name: "/P", x: 3, y: -4, core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/x"].stateValues.latex)).eq("x"); expect(cleanLatex(stateVariables["/y"].stateValues.latex)).eq("y"); - await core.requestAction({ - componentName: "/P", - actionName: "movePoint", - args: { x: 1, y: 7 }, - event: null, - }); + await movePoint({ name: "/P", x: 1, y: 7, core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/x"].stateValues.latex)).eq("3x"); expect(cleanLatex(stateVariables["/y"].stateValues.latex)).eq("4y"); - await core.requestAction({ - componentName: "/P", - actionName: "movePoint", - args: { x: 5, y: 9 }, - event: null, - }); + await movePoint({ name: "/P", x: 5, y: 9, core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/x"].stateValues.latex)).eq("3x"); expect(cleanLatex(stateVariables["/y"].stateValues.latex)).eq("4y"); - await core.requestAction({ - componentName: "/P", - actionName: "movePoint", - args: { x: -3, y: 4 }, - event: null, - }); + await movePoint({ name: "/P", x: -3, y: 4, core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/x"].stateValues.latex)).eq("3x"); expect(cleanLatex(stateVariables["/y"].stateValues.latex)).eq("4y"); - await core.requestAction({ - componentName: "/P", - actionName: "movePoint", - args: { x: -6, y: 5 }, - event: null, - }); + await movePoint({ name: "/P", x: -6, y: 5, core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/x"].stateValues.latex)).eq("3x"); expect(cleanLatex(stateVariables["/y"].stateValues.latex)).eq("4y"); - await core.requestAction({ - componentName: "/P", - actionName: "movePoint", - args: { x: 4, y: 2 }, - event: null, - }); + await movePoint({ name: "/P", x: 4, y: 2, core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/x"].stateValues.latex)).eq("9x"); expect(cleanLatex(stateVariables["/y"].stateValues.latex)).eq("16y"); - await core.requestAction({ - componentName: "/P", - actionName: "movePoint", - args: { x: 9, y: 7 }, - event: null, - }); + await movePoint({ name: "/P", x: 9, y: 7, core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/x"].stateValues.latex)).eq("9x"); @@ -1369,82 +1023,42 @@ describe("UpdateValue tag tests", async () => { expect(stateVariables["/trip"].stateValues.hidden).eq(true); expect(stateVariables["/quad"].stateValues.hidden).eq(true); - await core.requestAction({ - componentName: "/P", - actionName: "movePoint", - args: { x: -1, y: -7 }, - event: null, - }); + await movePoint({ name: "/P", x: -1, y: -7, core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/x"].stateValues.latex)).eq("x"); - await core.requestAction({ - componentName: "/P", - actionName: "movePoint", - args: { x: 3, y: -4 }, - event: null, - }); + await movePoint({ name: "/P", x: 3, y: -4, core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/x"].stateValues.latex)).eq("x"); - await core.requestAction({ - componentName: "/P", - actionName: "movePoint", - args: { x: 1, y: 7 }, - event: null, - }); + await movePoint({ name: "/P", x: 1, y: 7, core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/x"].stateValues.latex)).eq("12x"); - await core.requestAction({ - componentName: "/P", - actionName: "movePoint", - args: { x: 5, y: 9 }, - event: null, - }); + await movePoint({ name: "/P", x: 5, y: 9, core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/x"].stateValues.latex)).eq("12x"); - await core.requestAction({ - componentName: "/P", - actionName: "movePoint", - args: { x: -3, y: 4 }, - event: null, - }); + await movePoint({ name: "/P", x: -3, y: 4, core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/x"].stateValues.latex)).eq("12x"); - await core.requestAction({ - componentName: "/P", - actionName: "movePoint", - args: { x: -6, y: 5 }, - event: null, - }); + await movePoint({ name: "/P", x: -6, y: 5, core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/x"].stateValues.latex)).eq("12x"); - await core.requestAction({ - componentName: "/P", - actionName: "movePoint", - args: { x: 4, y: 2 }, - event: null, - }); + await movePoint({ name: "/P", x: 4, y: 2, core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/x"].stateValues.latex)).eq("144x"); - await core.requestAction({ - componentName: "/P", - actionName: "movePoint", - args: { x: 9, y: 7 }, - event: null, - }); + await movePoint({ name: "/P", x: 9, y: 7, core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/x"].stateValues.latex)).eq("144x"); @@ -1470,89 +1084,49 @@ describe("UpdateValue tag tests", async () => { expect(stateVariables["/trip"].stateValues.hidden).eq(true); expect(stateVariables["/quad"].stateValues.hidden).eq(true); - await core.requestAction({ - componentName: "/P", - actionName: "movePoint", - args: { x: -1, y: -7 }, - event: null, - }); + await movePoint({ name: "/P", x: -1, y: -7, core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/x"].stateValues.latex)).eq("x"); expect(cleanLatex(stateVariables["/y"].stateValues.latex)).eq("4y"); - await core.requestAction({ - componentName: "/P", - actionName: "movePoint", - args: { x: 3, y: -4 }, - event: null, - }); + await movePoint({ name: "/P", x: 3, y: -4, core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/x"].stateValues.latex)).eq("x"); expect(cleanLatex(stateVariables["/y"].stateValues.latex)).eq("4y"); - await core.requestAction({ - componentName: "/P", - actionName: "movePoint", - args: { x: 1, y: 7 }, - event: null, - }); + await movePoint({ name: "/P", x: 1, y: 7, core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/x"].stateValues.latex)).eq("3x"); expect(cleanLatex(stateVariables["/y"].stateValues.latex)).eq("4y"); - await core.requestAction({ - componentName: "/P", - actionName: "movePoint", - args: { x: 5, y: 9 }, - event: null, - }); + await movePoint({ name: "/P", x: 5, y: 9, core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/x"].stateValues.latex)).eq("3x"); expect(cleanLatex(stateVariables["/y"].stateValues.latex)).eq("4y"); - await core.requestAction({ - componentName: "/P", - actionName: "movePoint", - args: { x: -3, y: -4 }, - event: null, - }); + await movePoint({ name: "/P", x: -3, y: -4, core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/x"].stateValues.latex)).eq("3x"); expect(cleanLatex(stateVariables["/y"].stateValues.latex)).eq("16y"); - await core.requestAction({ - componentName: "/P", - actionName: "movePoint", - args: { x: -6, y: -5 }, - event: null, - }); + await movePoint({ name: "/P", x: -6, y: -5, core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/x"].stateValues.latex)).eq("3x"); expect(cleanLatex(stateVariables["/y"].stateValues.latex)).eq("16y"); - await core.requestAction({ - componentName: "/P", - actionName: "movePoint", - args: { x: 4, y: 2 }, - event: null, - }); + await movePoint({ name: "/P", x: 4, y: 2, core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/x"].stateValues.latex)).eq("9x"); expect(cleanLatex(stateVariables["/y"].stateValues.latex)).eq("16y"); - await core.requestAction({ - componentName: "/P", - actionName: "movePoint", - args: { x: 9, y: 7 }, - event: null, - }); + await movePoint({ name: "/P", x: 9, y: 7, core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/x"].stateValues.latex)).eq("9x"); @@ -1565,36 +1139,21 @@ describe("UpdateValue tag tests", async () => { expect(stateVariables["/hello"].stateValues.value).eq(""); expect(stateVariables["/n"].stateValues.value).eq(1); - await core.requestAction({ - componentName: "/ts", - actionName: "triggerActions", - args: {}, - event: null, - }); + await triggerActions({ name: "/ts", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/b"].stateValues.value).eq(true); expect(stateVariables["/hello"].stateValues.value).eq(" hello"); expect(stateVariables["/n"].stateValues.value).eq(2); - await core.requestAction({ - componentName: "/ts", - actionName: "triggerActions", - args: {}, - event: null, - }); + await triggerActions({ name: "/ts", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/b"].stateValues.value).eq(false); expect(stateVariables["/hello"].stateValues.value).eq(" hello hello"); expect(stateVariables["/n"].stateValues.value).eq(3); - await core.requestAction({ - componentName: "/ts", - actionName: "triggerActions", - args: {}, - event: null, - }); + await triggerActions({ name: "/ts", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/b"].stateValues.value).eq(true); @@ -1676,12 +1235,7 @@ describe("UpdateValue tag tests", async () => { expect(stateVariables["/m"].stateValues.value).eq(5); expect(stateVariables["/ts2"].stateValues.hidden).eq(true); - await core.requestAction({ - componentName: "/ts", - actionName: "triggerActions", - args: {}, - event: null, - }); + await triggerActions({ name: "/ts", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/b"].stateValues.value).eq(true); @@ -1689,12 +1243,7 @@ describe("UpdateValue tag tests", async () => { expect(stateVariables["/n"].stateValues.value).eq(2); expect(stateVariables["/m"].stateValues.value).eq(4); - await core.requestAction({ - componentName: "/ts", - actionName: "triggerActions", - args: {}, - event: null, - }); + await triggerActions({ name: "/ts", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/b"].stateValues.value).eq(false); @@ -1702,12 +1251,7 @@ describe("UpdateValue tag tests", async () => { expect(stateVariables["/n"].stateValues.value).eq(3); expect(stateVariables["/m"].stateValues.value).eq(3); - await core.requestAction({ - componentName: "/ts", - actionName: "triggerActions", - args: {}, - event: null, - }); + await triggerActions({ name: "/ts", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/b"].stateValues.value).eq(true); @@ -1740,89 +1284,49 @@ describe("UpdateValue tag tests", async () => { expect(stateVariables["/trip"].stateValues.hidden).eq(true); expect(stateVariables["/quad"].stateValues.hidden).eq(true); - await core.requestAction({ - componentName: "/P", - actionName: "movePoint", - args: { x: -1, y: -7 }, - event: null, - }); + await movePoint({ name: "/P", x: -1, y: -7, core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/x"].stateValues.latex)).eq("x"); expect(cleanLatex(stateVariables["/y"].stateValues.latex)).eq("y"); - await core.requestAction({ - componentName: "/P", - actionName: "movePoint", - args: { x: 3, y: -4 }, - event: null, - }); + await movePoint({ name: "/P", x: 3, y: -4, core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/x"].stateValues.latex)).eq("x"); expect(cleanLatex(stateVariables["/y"].stateValues.latex)).eq("y"); - await core.requestAction({ - componentName: "/P", - actionName: "movePoint", - args: { x: 1, y: 7 }, - event: null, - }); + await movePoint({ name: "/P", x: 1, y: 7, core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/x"].stateValues.latex)).eq("3x"); expect(cleanLatex(stateVariables["/y"].stateValues.latex)).eq("4y"); - await core.requestAction({ - componentName: "/P", - actionName: "movePoint", - args: { x: 5, y: 9 }, - event: null, - }); + await movePoint({ name: "/P", x: 5, y: 9, core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/x"].stateValues.latex)).eq("3x"); expect(cleanLatex(stateVariables["/y"].stateValues.latex)).eq("4y"); - await core.requestAction({ - componentName: "/P", - actionName: "movePoint", - args: { x: -3, y: 4 }, - event: null, - }); + await movePoint({ name: "/P", x: -3, y: 4, core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/x"].stateValues.latex)).eq("3x"); expect(cleanLatex(stateVariables["/y"].stateValues.latex)).eq("4y"); - await core.requestAction({ - componentName: "/P", - actionName: "movePoint", - args: { x: -6, y: 5 }, - event: null, - }); + await movePoint({ name: "/P", x: -6, y: 5, core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/x"].stateValues.latex)).eq("3x"); expect(cleanLatex(stateVariables["/y"].stateValues.latex)).eq("4y"); - await core.requestAction({ - componentName: "/P", - actionName: "movePoint", - args: { x: 4, y: 2 }, - event: null, - }); + await movePoint({ name: "/P", x: 4, y: 2, core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/x"].stateValues.latex)).eq("9x"); expect(cleanLatex(stateVariables["/y"].stateValues.latex)).eq("16y"); - await core.requestAction({ - componentName: "/P", - actionName: "movePoint", - args: { x: 9, y: 7 }, - event: null, - }); + await movePoint({ name: "/P", x: 9, y: 7, core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/x"].stateValues.latex)).eq("9x"); @@ -1864,12 +1368,7 @@ describe("UpdateValue tag tests", async () => { expect(stateVariables["/ts1"].stateValues.hidden).eq(true); expect(stateVariables["/ts2"].stateValues.hidden).eq(true); - await core.requestAction({ - componentName: "/P", - actionName: "movePoint", - args: { x: -1, y: -7 }, - event: null, - }); + await movePoint({ name: "/P", x: -1, y: -7, core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/b"].stateValues.value).eq(false); @@ -1877,12 +1376,7 @@ describe("UpdateValue tag tests", async () => { expect(stateVariables["/n"].stateValues.value).eq(2); expect(stateVariables["/m"].stateValues.value).eq(4); - await core.requestAction({ - componentName: "/P", - actionName: "movePoint", - args: { x: 3, y: -4 }, - event: null, - }); + await movePoint({ name: "/P", x: 3, y: -4, core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/b"].stateValues.value).eq(false); @@ -1890,12 +1384,7 @@ describe("UpdateValue tag tests", async () => { expect(stateVariables["/n"].stateValues.value).eq(2); expect(stateVariables["/m"].stateValues.value).eq(4); - await core.requestAction({ - componentName: "/P", - actionName: "movePoint", - args: { x: 1, y: 7 }, - event: null, - }); + await movePoint({ name: "/P", x: 1, y: 7, core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/b"].stateValues.value).eq(true); @@ -1903,12 +1392,7 @@ describe("UpdateValue tag tests", async () => { expect(stateVariables["/n"].stateValues.value).eq(2); expect(stateVariables["/m"].stateValues.value).eq(4); - await core.requestAction({ - componentName: "/P", - actionName: "movePoint", - args: { x: 5, y: 9 }, - event: null, - }); + await movePoint({ name: "/P", x: 5, y: 9, core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/b"].stateValues.value).eq(true); @@ -1916,12 +1400,7 @@ describe("UpdateValue tag tests", async () => { expect(stateVariables["/n"].stateValues.value).eq(2); expect(stateVariables["/m"].stateValues.value).eq(4); - await core.requestAction({ - componentName: "/P", - actionName: "movePoint", - args: { x: -3, y: -4 }, - event: null, - }); + await movePoint({ name: "/P", x: -3, y: -4, core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/b"].stateValues.value).eq(true); @@ -1929,12 +1408,7 @@ describe("UpdateValue tag tests", async () => { expect(stateVariables["/n"].stateValues.value).eq(3); expect(stateVariables["/m"].stateValues.value).eq(3); - await core.requestAction({ - componentName: "/P", - actionName: "movePoint", - args: { x: -6, y: -5 }, - event: null, - }); + await movePoint({ name: "/P", x: -6, y: -5, core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/b"].stateValues.value).eq(true); @@ -1942,12 +1416,7 @@ describe("UpdateValue tag tests", async () => { expect(stateVariables["/n"].stateValues.value).eq(3); expect(stateVariables["/m"].stateValues.value).eq(3); - await core.requestAction({ - componentName: "/P", - actionName: "movePoint", - args: { x: 4, y: 2 }, - event: null, - }); + await movePoint({ name: "/P", x: 4, y: 2, core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/b"].stateValues.value).eq(false); @@ -1955,12 +1424,7 @@ describe("UpdateValue tag tests", async () => { expect(stateVariables["/n"].stateValues.value).eq(3); expect(stateVariables["/m"].stateValues.value).eq(3); - await core.requestAction({ - componentName: "/P", - actionName: "movePoint", - args: { x: 9, y: 7 }, - event: null, - }); + await movePoint({ name: "/P", x: 9, y: 7, core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/b"].stateValues.value).eq(false); @@ -1995,84 +1459,49 @@ describe("UpdateValue tag tests", async () => { expect(stateVariables["/hello"].stateValues.value).eq(""); expect(stateVariables["/n"].stateValues.value).eq(1); - await core.requestAction({ - componentName: "/P", - actionName: "movePoint", - args: { x: -1, y: -7 }, - event: null, - }); + await movePoint({ name: "/P", x: -1, y: -7, core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/b"].stateValues.value).eq(false); expect(stateVariables["/hello"].stateValues.value).eq(""); expect(stateVariables["/n"].stateValues.value).eq(1); - await core.requestAction({ - componentName: "/P", - actionName: "movePoint", - args: { x: 3, y: -4 }, - event: null, - }); + await movePoint({ name: "/P", x: 3, y: -4, core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/b"].stateValues.value).eq(false); expect(stateVariables["/hello"].stateValues.value).eq(""); expect(stateVariables["/n"].stateValues.value).eq(1); - await core.requestAction({ - componentName: "/P", - actionName: "movePoint", - args: { x: 1, y: 7 }, - event: null, - }); + await movePoint({ name: "/P", x: 1, y: 7, core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/b"].stateValues.value).eq(true); expect(stateVariables["/hello"].stateValues.value).eq(" hello"); expect(stateVariables["/n"].stateValues.value).eq(2); - await core.requestAction({ - componentName: "/P", - actionName: "movePoint", - args: { x: 5, y: 9 }, - event: null, - }); + await movePoint({ name: "/P", x: 5, y: 9, core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/b"].stateValues.value).eq(true); expect(stateVariables["/hello"].stateValues.value).eq(" hello"); expect(stateVariables["/n"].stateValues.value).eq(2); - await core.requestAction({ - componentName: "/P", - actionName: "movePoint", - args: { x: -3, y: -4 }, - event: null, - }); + await movePoint({ name: "/P", x: -3, y: -4, core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/b"].stateValues.value).eq(true); expect(stateVariables["/hello"].stateValues.value).eq(" hello"); expect(stateVariables["/n"].stateValues.value).eq(2); - await core.requestAction({ - componentName: "/P", - actionName: "movePoint", - args: { x: -6, y: -5 }, - event: null, - }); + await movePoint({ name: "/P", x: -6, y: -5, core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/b"].stateValues.value).eq(true); expect(stateVariables["/hello"].stateValues.value).eq(" hello"); expect(stateVariables["/n"].stateValues.value).eq(2); - await core.requestAction({ - componentName: "/P", - actionName: "movePoint", - args: { x: 9, y: 7 }, - event: null, - }); + await movePoint({ name: "/P", x: 9, y: 7, core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/b"].stateValues.value).eq(false); @@ -2112,12 +1541,7 @@ describe("UpdateValue tag tests", async () => { expect(stateVariables["/ts"].stateValues.hidden).eq(true); expect(stateVariables["/uv"].stateValues.hidden).eq(true); - await core.requestAction({ - componentName: "/P", - actionName: "movePoint", - args: { x: -1, y: -7 }, - event: null, - }); + await movePoint({ name: "/P", x: -1, y: -7, core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/b"].stateValues.value).eq(false); @@ -2125,12 +1549,7 @@ describe("UpdateValue tag tests", async () => { expect(stateVariables["/n"].stateValues.value).eq(1); expect(stateVariables["/m"].stateValues.value).eq(4); - await core.requestAction({ - componentName: "/P", - actionName: "movePoint", - args: { x: 3, y: -4 }, - event: null, - }); + await movePoint({ name: "/P", x: 3, y: -4, core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/b"].stateValues.value).eq(false); @@ -2138,12 +1557,7 @@ describe("UpdateValue tag tests", async () => { expect(stateVariables["/n"].stateValues.value).eq(1); expect(stateVariables["/m"].stateValues.value).eq(4); - await core.requestAction({ - componentName: "/P", - actionName: "movePoint", - args: { x: 1, y: 7 }, - event: null, - }); + await movePoint({ name: "/P", x: 1, y: 7, core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/b"].stateValues.value).eq(true); @@ -2151,12 +1565,7 @@ describe("UpdateValue tag tests", async () => { expect(stateVariables["/n"].stateValues.value).eq(2); expect(stateVariables["/m"].stateValues.value).eq(4); - await core.requestAction({ - componentName: "/P", - actionName: "movePoint", - args: { x: 5, y: 9 }, - event: null, - }); + await movePoint({ name: "/P", x: 5, y: 9, core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/b"].stateValues.value).eq(true); @@ -2164,12 +1573,7 @@ describe("UpdateValue tag tests", async () => { expect(stateVariables["/n"].stateValues.value).eq(2); expect(stateVariables["/m"].stateValues.value).eq(4); - await core.requestAction({ - componentName: "/P", - actionName: "movePoint", - args: { x: -3, y: -4 }, - event: null, - }); + await movePoint({ name: "/P", x: -3, y: -4, core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/b"].stateValues.value).eq(true); @@ -2177,12 +1581,7 @@ describe("UpdateValue tag tests", async () => { expect(stateVariables["/n"].stateValues.value).eq(2); expect(stateVariables["/m"].stateValues.value).eq(3); - await core.requestAction({ - componentName: "/P", - actionName: "movePoint", - args: { x: -6, y: -5 }, - event: null, - }); + await movePoint({ name: "/P", x: -6, y: -5, core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/b"].stateValues.value).eq(true); @@ -2190,12 +1589,7 @@ describe("UpdateValue tag tests", async () => { expect(stateVariables["/n"].stateValues.value).eq(2); expect(stateVariables["/m"].stateValues.value).eq(3); - await core.requestAction({ - componentName: "/P", - actionName: "movePoint", - args: { x: 4, y: 2 }, - event: null, - }); + await movePoint({ name: "/P", x: 4, y: 2, core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/b"].stateValues.value).eq(false); @@ -2203,12 +1597,7 @@ describe("UpdateValue tag tests", async () => { expect(stateVariables["/n"].stateValues.value).eq(3); expect(stateVariables["/m"].stateValues.value).eq(3); - await core.requestAction({ - componentName: "/P", - actionName: "movePoint", - args: { x: 9, y: 7 }, - event: null, - }); + await movePoint({ name: "/P", x: 9, y: 7, core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/b"].stateValues.value).eq(false); @@ -2231,12 +1620,7 @@ describe("UpdateValue tag tests", async () => { let stateVariables = await returnAllStateVariables(core); expect(stateVariables["/t"].stateValues.text).eq("something"); - await core.requestAction({ - componentName: "/toBlank", - actionName: "updateValue", - args: {}, - event: null, - }); + await updateValue({ name: "/toBlank", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/t"].stateValues.text).eq(""); @@ -2255,24 +1639,9 @@ describe("UpdateValue tag tests", async () => { }); // click the update value buttons - await core.requestAction({ - componentName: "/uv1", - actionName: "updateValue", - args: {}, - event: null, - }); - await core.requestAction({ - componentName: "/uv2", - actionName: "updateValue", - args: {}, - event: null, - }); - await core.requestAction({ - componentName: "/uv3", - actionName: "updateValue", - args: {}, - event: null, - }); + await updateValue({ name: "/uv1", core }); + await updateValue({ name: "/uv2", core }); + await updateValue({ name: "/uv3", core }); let errorWarnings = core.errorWarnings; @@ -2345,12 +1714,7 @@ describe("UpdateValue tag tests", async () => { let stateVariables = await returnAllStateVariables(core); expect(stateVariables["/uv"].stateValues.label).eq(""); - await core.requestAction({ - componentName: "/uv", - actionName: "updateValue", - args: {}, - event: null, - }); + await updateValue({ name: "/uv", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/uv"].stateValues.label).eq("Hello!"); @@ -2376,482 +1740,28 @@ describe("UpdateValue tag tests", async () => { expect(cleanLatex(stateVariables["/vh"].stateValues.latex)).eq("(1,0)"); expect(cleanLatex(stateVariables["/vt"].stateValues.latex)).eq("(0,0)"); - await core.requestAction({ - componentName: "/uv1", - actionName: "updateValue", - args: {}, - event: null, - }); + await updateValue({ name: "/uv1", core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/vh"].stateValues.latex)).eq("(4,4)"); expect(cleanLatex(stateVariables["/vt"].stateValues.latex)).eq("(3,4)"); - await core.requestAction({ - componentName: "/ts1", - actionName: "triggerActions", - args: {}, - event: null, - }); + await triggerActions({ name: "/ts1", core }); stateVariables = await returnAllStateVariables(core); expect(cleanLatex(stateVariables["/vh"].stateValues.latex)).eq("(9,4)"); expect(cleanLatex(stateVariables["/vt"].stateValues.latex)).eq("(7,2)"); }); it("updateValue in graph", async () => { - let core = await createTestCore({ - doenetML: ` + const doenetMLsnippet = `

n: 1

- - + + - -

Anchor 1 coordinates: $updateValue1.anchor

-

Anchor 2 coordinates: $updateValue2.anchor

-

Change anchor 1 coordinates:

-

Change anchor 2 coordinates:

-

Position from anchor 1: $updateValue1.positionFromAnchor

-

Position from anchor 2: $updateValue2.positionFromAnchor

-

Change position from anchor 1 - - upperRight - upperLeft - lowerRight - lowerLeft - left - right - top - bottom - center - -

-

Change position from anchor 2 - - upperRight - upperLeft - lowerRight - lowerLeft - left - right - top - bottom - center - -

-

Draggable 1: $draggable1

-

Draggable 2: $draggable2

-

Change draggable 1

-

Change draggable 2

-

Disabled 1: $disabled1

-

Disabled 2: $disabled2

-

Change disabled 1

-

Change disabled 2

-

Fixed 1: $fixed1

-

Fixed 2: $fixed2

-

Change fixed 1

-

Change fixed 2

-

FixLocation 1: $fixLocation1

-

FixLocation 2: $fixLocation2

-

Change fixLocation 1

-

Change fixLocation 2

- - `, - }); - + `; // TODO: how to click on the buttons and test if they are disabled? - let stateVariables = await returnAllStateVariables(core); - expect(stateVariables["/pAnchor1"].stateValues.text).eq( - "Anchor 1 coordinates: ( 1, 3 )", - ); - expect(stateVariables["/pAnchor2"].stateValues.text).eq( - "Anchor 2 coordinates: ( 0, 0 )", - ); - expect(stateVariables["/pPositionFromAnchor1"].stateValues.text).eq( - "Position from anchor 1: upperright", - ); - expect(stateVariables["/pPositionFromAnchor2"].stateValues.text).eq( - "Position from anchor 2: center", - ); - expect( - stateVariables["/positionFromAnchor1"].stateValues.selectedIndices, - ).eqls([1]); - expect( - stateVariables["/positionFromAnchor2"].stateValues.selectedIndices, - ).eqls([9]); - expect(stateVariables["/pDraggable1"].stateValues.text).eq( - "Draggable 1: true", - ); - expect(stateVariables["/pDraggable2"].stateValues.text).eq( - "Draggable 2: true", - ); - expect(stateVariables["/pDisabled1"].stateValues.text).eq( - "Disabled 1: true", - ); - expect(stateVariables["/pDisabled2"].stateValues.text).eq( - "Disabled 2: false", - ); - expect(stateVariables["/pFixed1"].stateValues.text).eq( - "Fixed 1: false", - ); - expect(stateVariables["/pFixed2"].stateValues.text).eq( - "Fixed 2: false", - ); - expect(stateVariables["/pFixLocation1"].stateValues.text).eq( - "FixLocation 1: false", - ); - expect(stateVariables["/pFixLocation2"].stateValues.text).eq( - "FixLocation 2: false", - ); - - // move updateValues by dragging - - await core.requestAction({ - actionName: "moveButton", - componentName: "/updateValue1", - args: { x: -2, y: 3 }, - event: null, - }); - await core.requestAction({ - actionName: "moveButton", - componentName: "/updateValue2", - args: { x: 4, y: -5 }, - event: null, - }); - - stateVariables = await returnAllStateVariables(core); - expect(stateVariables["/pAnchor1"].stateValues.text).eq( - "Anchor 1 coordinates: ( -2, 3 )", - ); - expect(stateVariables["/pAnchor2"].stateValues.text).eq( - "Anchor 2 coordinates: ( 4, -5 )", - ); - - // move updateValues by entering coordinates - - await updateMathInputValue({ - latex: "(6,7)", - componentName: "/anchorCoords1", - core, - }); - await updateMathInputValue({ - latex: "(8,9)", - componentName: "/anchorCoords2", - core, - }); - - stateVariables = await returnAllStateVariables(core); - expect(stateVariables["/pAnchor1"].stateValues.text).eq( - "Anchor 1 coordinates: ( 6, 7 )", - ); - expect(stateVariables["/pAnchor2"].stateValues.text).eq( - "Anchor 2 coordinates: ( 8, 9 )", - ); - - // change position from anchor - await core.requestAction({ - componentName: "/positionFromAnchor1", - actionName: "updateSelectedIndices", - args: { - selectedIndices: [4], - }, - event: null, - }); - await core.requestAction({ - componentName: "/positionFromAnchor2", - actionName: "updateSelectedIndices", - args: { - selectedIndices: [3], - }, - event: null, - }); - stateVariables = await returnAllStateVariables(core); - - expect(stateVariables["/pPositionFromAnchor1"].stateValues.text).eq( - "Position from anchor 1: lowerleft", - ); - expect(stateVariables["/pPositionFromAnchor2"].stateValues.text).eq( - "Position from anchor 2: lowerright", - ); - - // make not draggable - await updateBooleanInputValue({ - boolean: false, - componentName: "/draggable1", - core, - }); - await updateBooleanInputValue({ - boolean: false, - componentName: "/draggable2", - core, - }); - stateVariables = await returnAllStateVariables(core); - - expect(stateVariables["/pDraggable1"].stateValues.text).eq( - "Draggable 1: false", - ); - expect(stateVariables["/pDraggable2"].stateValues.text).eq( - "Draggable 2: false", - ); - - // cannot move updateValues by dragging - await core.requestAction({ - actionName: "moveButton", - componentName: "/updateValue1", - args: { x: -10, y: -9 }, - event: null, - }); - await core.requestAction({ - actionName: "moveButton", - componentName: "/updateValue2", - args: { x: -8, y: -7 }, - event: null, - }); - stateVariables = await returnAllStateVariables(core); - - expect(stateVariables["/pAnchor1"].stateValues.text).eq( - "Anchor 1 coordinates: ( 6, 7 )", - ); - expect(stateVariables["/pAnchor2"].stateValues.text).eq( - "Anchor 2 coordinates: ( 8, 9 )", - ); - - // make draggable again - await updateBooleanInputValue({ - boolean: true, - componentName: "/draggable1", - core, - }); - await updateBooleanInputValue({ - boolean: true, - componentName: "/draggable2", - core, - }); - stateVariables = await returnAllStateVariables(core); - - expect(stateVariables["/pDraggable1"].stateValues.text).eq( - "Draggable 1: true", - ); - expect(stateVariables["/pDraggable2"].stateValues.text).eq( - "Draggable 2: true", - ); - - await core.requestAction({ - actionName: "moveButton", - componentName: "/updateValue1", - args: { x: -10, y: -9 }, - event: null, - }); - await core.requestAction({ - actionName: "moveButton", - componentName: "/updateValue2", - args: { x: -8, y: -7 }, - event: null, - }); - stateVariables = await returnAllStateVariables(core); - - expect(stateVariables["/pAnchor1"].stateValues.text).eq( - "Anchor 1 coordinates: ( -10, -9 )", - ); - expect(stateVariables["/pAnchor2"].stateValues.text).eq( - "Anchor 2 coordinates: ( -8, -7 )", - ); - - // fix location - await updateBooleanInputValue({ - boolean: true, - componentName: "/fixLocation1", - core, - }); - await updateBooleanInputValue({ - boolean: true, - componentName: "/fixLocation2", - core, - }); - stateVariables = await returnAllStateVariables(core); - - expect(stateVariables["/pFixLocation1"].stateValues.text).eq( - "FixLocation 1: true", - ); - expect(stateVariables["/pFixLocation2"].stateValues.text).eq( - "FixLocation 2: true", - ); - - // can change coordinates entering coordinates only for button 1 - await updateMathInputValue({ - latex: "(1,2)", - componentName: "/anchorCoords1", - core, - }); - await updateMathInputValue({ - latex: "(3,4)", - componentName: "/anchorCoords2", - core, - }); - - stateVariables = await returnAllStateVariables(core); - - expect(stateVariables["/pAnchor1"].stateValues.text).eq( - "Anchor 1 coordinates: ( 1, 2 )", - ); - expect(stateVariables["/pAnchor2"].stateValues.text).eq( - "Anchor 2 coordinates: ( -8, -7 )", - ); - - // cannot move updateValues by dragging - await core.requestAction({ - actionName: "moveButton", - componentName: "/updateValue1", - args: { x: 4, y: 6 }, - event: null, - }); - await core.requestAction({ - actionName: "moveButton", - componentName: "/updateValue2", - args: { x: 7, y: 8 }, - event: null, - }); - - stateVariables = await returnAllStateVariables(core); - - expect(stateVariables["/pAnchor1"].stateValues.text).eq( - "Anchor 1 coordinates: ( 1, 2 )", - ); - expect(stateVariables["/pAnchor2"].stateValues.text).eq( - "Anchor 2 coordinates: ( -8, -7 )", - ); - - // can change position from anchor only for button 1 - await core.requestAction({ - componentName: "/positionFromAnchor1", - actionName: "updateSelectedIndices", - args: { - selectedIndices: [7], - }, - event: null, - }); - await core.requestAction({ - componentName: "/positionFromAnchor2", - actionName: "updateSelectedIndices", - args: { - selectedIndices: [8], - }, - event: null, - }); - stateVariables = await returnAllStateVariables(core); - - expect(stateVariables["/pPositionFromAnchor1"].stateValues.text).eq( - "Position from anchor 1: top", - ); - expect(stateVariables["/pPositionFromAnchor2"].stateValues.text).eq( - "Position from anchor 2: lowerright", - ); - - // can change disabled attribute - - await updateBooleanInputValue({ - boolean: false, - componentName: "/disabled1", - core, - }); - await updateBooleanInputValue({ - boolean: true, - componentName: "/disabled2", - core, - }); - stateVariables = await returnAllStateVariables(core); - - expect(stateVariables["/pDisabled1"].stateValues.text).eq( - "Disabled 1: false", - ); - expect(stateVariables["/pDisabled2"].stateValues.text).eq( - "Disabled 2: true", - ); - - // make completely fixed - - await updateBooleanInputValue({ - boolean: true, - componentName: "/fixed1", - core, - }); - await updateBooleanInputValue({ - boolean: true, - componentName: "/fixed2", - core, - }); - stateVariables = await returnAllStateVariables(core); - - expect(stateVariables["/pFixed1"].stateValues.text).eq("Fixed 1: true"); - expect(stateVariables["/pFixed2"].stateValues.text).eq("Fixed 2: true"); - - // can change coordinates entering coordinates only for button 1 - await updateMathInputValue({ - latex: "(5,6)", - componentName: "/anchorCoords1", - core, - }); - await updateMathInputValue({ - latex: "(7,8)", - componentName: "/anchorCoords2", - core, - }); - - stateVariables = await returnAllStateVariables(core); - - expect(stateVariables["/pAnchor1"].stateValues.text).eq( - "Anchor 1 coordinates: ( 5, 6 )", - ); - expect(stateVariables["/pAnchor2"].stateValues.text).eq( - "Anchor 2 coordinates: ( -8, -7 )", - ); - - // can change position from anchor only for button 1 - await core.requestAction({ - componentName: "/positionFromAnchor1", - actionName: "updateSelectedIndices", - args: { - selectedIndices: [6], - }, - event: null, - }); - await core.requestAction({ - componentName: "/positionFromAnchor2", - actionName: "updateSelectedIndices", - args: { - selectedIndices: [5], - }, - event: null, - }); - stateVariables = await returnAllStateVariables(core); - - expect(stateVariables["/pPositionFromAnchor1"].stateValues.text).eq( - "Position from anchor 1: right", - ); - expect(stateVariables["/pPositionFromAnchor2"].stateValues.text).eq( - "Position from anchor 2: lowerright", - ); - - // can change disabled attribute only for button 1 - - await updateBooleanInputValue({ - boolean: true, - componentName: "/disabled1", - core, - }); - await updateBooleanInputValue({ - boolean: false, - componentName: "/disabled2", - core, - }); - stateVariables = await returnAllStateVariables(core); - - expect(stateVariables["/pDisabled1"].stateValues.text).eq( - "Disabled 1: true", - ); - expect(stateVariables["/pDisabled2"].stateValues.text).eq( - "Disabled 2: true", - ); + await test_in_graph(doenetMLsnippet, moveButton); }); it("handle removed updateValue when shadowing", async () => { @@ -2884,12 +1794,7 @@ describe("UpdateValue tag tests", async () => { .length, ).eq(3); - await core.requestAction({ - componentName: "/grp2/uv", - actionName: "updateValue", - args: {}, - event: null, - }); + await updateValue({ name: "/grp2/uv", core }); stateVariables = await returnAllStateVariables(core); expect(stateVariables["/grp2/p"].stateValues.text).eq("false"); diff --git a/packages/doenetml-worker/src/test/tagSpecific/video.test.ts b/packages/doenetml-worker/src/test/tagSpecific/video.test.ts new file mode 100644 index 000000000..5223d169d --- /dev/null +++ b/packages/doenetml-worker/src/test/tagSpecific/video.test.ts @@ -0,0 +1,139 @@ +import { describe, expect, it, vi } from "vitest"; +import { createTestCore, returnAllStateVariables } from "../utils/test-core"; +import { widthsBySize } from "@doenet/utils"; + +const Mock = vi.fn(); +vi.stubGlobal("postMessage", Mock); +vi.mock("hyperformula"); + +describe("Video tag tests", async () => { + it("video sizes", async () => { + let core = await createTestCore({ + doenetML: ` +