diff --git a/packages/doenetml-worker/src/components/Function.js b/packages/doenetml-worker/src/components/Function.js index 27a8f5d73..9f8600206 100644 --- a/packages/doenetml-worker/src/components/Function.js +++ b/packages/doenetml-worker/src/components/Function.js @@ -2694,6 +2694,7 @@ export default class Function extends InlineComponent { stateVariableDefinitions.latex = { public: true, + forRenderer: true, shadowingInstructions: { createComponentOfType: "latex", }, @@ -2756,23 +2757,6 @@ export default class Function extends InlineComponent { }, }; - stateVariableDefinitions.latexWithInputChildren = { - forRenderer: true, - returnDependencies: () => ({ - latex: { - dependencyType: "stateVariable", - variableName: "latex", - }, - }), - definition: function ({ dependencyValues }) { - return { - setValue: { - latexWithInputChildren: [dependencyValues.latex], - }, - }; - }, - }; - stateVariableDefinitions.text = { public: true, shadowingInstructions: { diff --git a/packages/doenetml-worker/src/components/MMeMen.js b/packages/doenetml-worker/src/components/MMeMen.js index db4d495e9..6e3b3a1b2 100644 --- a/packages/doenetml-worker/src/components/MMeMen.js +++ b/packages/doenetml-worker/src/components/MMeMen.js @@ -10,6 +10,7 @@ import { returnAnchorStateVariableDefinition, } from "../utils/graphical"; import { latexToAst, superSubscriptsToUnicode } from "../utils/math"; +import { createInputStringFromChildren } from "../utils/parseMath"; export class M extends InlineComponent { constructor(args) { @@ -93,7 +94,6 @@ export class M extends InlineComponent { }, }), definition: function ({ dependencyValues }) { - let warnings = []; if (dependencyValues.inlineChildren.length === 0) { return { useEssentialOrDefaultValue: { @@ -102,33 +102,14 @@ export class M extends InlineComponent { }; } - let pieces = []; - for (let child of dependencyValues.inlineChildren) { - if (typeof child !== "object") { - let childtrim = String(child).trim(); - if (childtrim) { - pieces.push(childtrim); - } - } else if (typeof child.stateValues.latex === "string") { - let latex = child.stateValues.latex.trim(); - if (latex) { - pieces.push(latex); - } - } else if (typeof child.stateValues.text === "string") { - let text = child.stateValues.text.trim(); - if (text) { - pieces.push(text); - } - } else { - warnings.push({ - message: `Child <${child.componentType}> of <${componentClass.componentType}> ignored as it does not have a string "text" or "latex" state variable.`, - level: 1, - }); - } - } - let latex = pieces.join(" "); + let latex = createInputStringFromChildren({ + children: dependencyValues.inlineChildren, + codePre: "", + format: "latex", + createDisplayedMathString: true, + }).string; - return { setValue: { latex }, sendWarnings: warnings }; + return { setValue: { latex } }; }, inverseDefinition({ desiredStateVariableValues, @@ -198,74 +179,6 @@ export class M extends InlineComponent { }, }; - stateVariableDefinitions.latexWithInputChildren = { - forRenderer: true, - returnDependencies: () => ({ - inlineChildren: { - dependencyType: "child", - childGroups: ["inline"], - variableNames: ["latex", "text"], - variablesOptional: true, - }, - latex: { - dependencyType: "stateVariable", - variableName: "latex", - }, - }), - definition: function ({ dependencyValues, componentInfoObjects }) { - if (dependencyValues.inlineChildren.length === 0) { - return { - setValue: { - latexWithInputChildren: [dependencyValues.latex], - }, - }; - } - - let latexWithInputChildren = []; - let lastLatexPieces = []; - let inputInd = 0; - for (let child of dependencyValues.inlineChildren) { - if (typeof child !== "object") { - let childtrim = String(child).trim(); - if (childtrim) { - lastLatexPieces.push(childtrim); - } - } else if ( - componentInfoObjects.isInheritedComponentType({ - inheritedComponentType: child.componentType, - baseComponentType: "input", - }) - ) { - if (lastLatexPieces.length > 0) { - latexWithInputChildren.push( - lastLatexPieces.join(" "), - ); - lastLatexPieces = []; - } - latexWithInputChildren.push(inputInd); - inputInd++; - } else { - if (typeof child.stateValues.latex === "string") { - let latex = child.stateValues.latex.trim(); - if (latex) { - lastLatexPieces.push(latex); - } - } else if (typeof child.stateValues.text === "string") { - let text = child.stateValues.text.trim(); - if (text) { - lastLatexPieces.push(text); - } - } - } - } - if (lastLatexPieces.length > 0) { - latexWithInputChildren.push(lastLatexPieces.join(" ")); - } - - return { setValue: { latexWithInputChildren } }; - }, - }; - stateVariableDefinitions.renderMode = { forRenderer: true, returnDependencies: () => ({}), diff --git a/packages/doenetml-worker/src/components/Math.js b/packages/doenetml-worker/src/components/Math.js index d4f39b8ac..19d4776f3 100644 --- a/packages/doenetml-worker/src/components/Math.js +++ b/packages/doenetml-worker/src/components/Math.js @@ -836,23 +836,6 @@ export default class MathComponent extends InlineComponent { }, }; - stateVariableDefinitions.latexWithInputChildren = { - forRenderer: true, - returnDependencies: () => ({ - latex: { - dependencyType: "stateVariable", - variableName: "latex", - }, - }), - definition: function ({ dependencyValues }) { - return { - setValue: { - latexWithInputChildren: [dependencyValues.latex], - }, - }; - }, - }; - stateVariableDefinitions.text = { public: true, shadowingInstructions: { diff --git a/packages/doenetml-worker/src/components/MdMdnMrow.js b/packages/doenetml-worker/src/components/MdMdnMrow.js index 02d775207..f0249aaed 100644 --- a/packages/doenetml-worker/src/components/MdMdnMrow.js +++ b/packages/doenetml-worker/src/components/MdMdnMrow.js @@ -94,10 +94,10 @@ export class Md extends InlineComponent { stateVariableDefinitions.latex = { public: true, + forRenderer: true, shadowingInstructions: { createComponentOfType: "latex", }, - forRenderer: true, returnDependencies: () => ({ mrowChildren: { dependencyType: "child", @@ -128,70 +128,6 @@ export class Md extends InlineComponent { }, }; - stateVariableDefinitions.latexWithInputChildren = { - forRenderer: true, - returnDependencies: () => ({ - mrowChildren: { - dependencyType: "child", - childGroups: ["mrows"], - variableNames: [ - "latexWithInputChildren", - "hide", - "equationTag", - "numbered", - ], - }, - latex: { - dependencyType: "stateVariable", - variableName: "latex", - }, - }), - definition: function ({ dependencyValues }) { - if (dependencyValues.mrowChildren.length > 0) { - let latexWithInputChildren = []; - let inputInd = 0; - let lastLatex = ""; - - for (let mrow of dependencyValues.mrowChildren) { - if (mrow.stateValues.hide) { - continue; - } - if (lastLatex.length > 0) { - lastLatex += "\\\\"; - } - if (mrow.stateValues.numbered) { - lastLatex += `\\tag{${mrow.stateValues.equationTag}}`; - } else { - lastLatex += "\\notag "; - } - for (let latexOrChildInd of mrow.stateValues - .latexWithInputChildren) { - if (typeof latexOrChildInd === "number") { - if (lastLatex.length > 0) { - latexWithInputChildren.push(lastLatex); - lastLatex = ""; - } - latexWithInputChildren.push(inputInd); - inputInd++; - } else { - lastLatex += latexOrChildInd; - } - } - } - if (lastLatex.length > 0) { - latexWithInputChildren.push(lastLatex); - } - return { setValue: { latexWithInputChildren } }; - } else { - return { - setValue: { - latexWithInputChildren: [dependencyValues.latex], - }, - }; - } - }, - }; - stateVariableDefinitions.text = { public: true, shadowingInstructions: { @@ -210,9 +146,19 @@ export class Md extends InlineComponent { .replaceAll("\\notag", "") .replaceAll("\\amp", "") .split("\\\\") - .map((x) => - me.fromAst(latexToAst.convert(x)).toString(), - ) + .map((x) => { + let result = x.match(/\\tag\{(\w+)\}(.*)/); + if (result) { + x = result[2]; + } + let text = me + .fromAst(latexToAst.convert(x)) + .toString(); + if (result) { + text += ` (${result[1]})`; + } + return text; + }) .join("\\\\\n"); } catch (e) { // just return latex if can't parse with math-expressions diff --git a/packages/doenetml-worker/src/components/Ref.js b/packages/doenetml-worker/src/components/Ref.js index 8b66ceb12..bd3e02908 100644 --- a/packages/doenetml-worker/src/components/Ref.js +++ b/packages/doenetml-worker/src/components/Ref.js @@ -346,6 +346,11 @@ export default class Ref extends InlineComponent { }, }; + stateVariableDefinitions.text = { + isAlias: true, + targetVariableName: "linkText", + }; + return stateVariableDefinitions; } diff --git a/packages/doenetml-worker/src/components/chemistry/Atom.js b/packages/doenetml-worker/src/components/chemistry/Atom.js index 7349f218d..1368bff0f 100644 --- a/packages/doenetml-worker/src/components/chemistry/Atom.js +++ b/packages/doenetml-worker/src/components/chemistry/Atom.js @@ -809,15 +809,10 @@ export default class Atom extends InlineComponent { stateVariableDefinitions.latex = { public: true, + forRenderer: true, shadowingInstructions: { createComponentOfType: "latex", }, - additionalStateVariablesDefined: [ - { - variableName: "latexWithInputChildren", - forRenderer: true, - }, - ], returnDependencies: () => ({ symbol: { dependencyType: "stateVariable", @@ -832,7 +827,7 @@ export default class Atom extends InlineComponent { latex = "[\\text{Invalid Chemical Symbol}]"; } return { - setValue: { latex, latexWithInputChildren: [latex] }, + setValue: { latex }, }; }, }; diff --git a/packages/doenetml-worker/src/components/chemistry/Ion.js b/packages/doenetml-worker/src/components/chemistry/Ion.js index 35868f6b4..93a525d4c 100644 --- a/packages/doenetml-worker/src/components/chemistry/Ion.js +++ b/packages/doenetml-worker/src/components/chemistry/Ion.js @@ -649,15 +649,10 @@ export default class Ion extends InlineComponent { stateVariableDefinitions.latex = { public: true, + forRenderer: true, shadowingInstructions: { createComponentOfType: "latex", }, - additionalStateVariablesDefined: [ - { - variableName: "latexWithInputChildren", - forRenderer: true, - }, - ], returnDependencies: () => ({ symbol: { dependencyType: "stateVariable", @@ -687,7 +682,7 @@ export default class Ion extends InlineComponent { latex = "[\\text{Invalid Chemical Symbol}]"; } return { - setValue: { latex, latexWithInputChildren: [latex] }, + setValue: { latex }, }; }, }; diff --git a/packages/doenetml-worker/src/components/chemistry/IonicCompound.js b/packages/doenetml-worker/src/components/chemistry/IonicCompound.js index d06f37878..bb78f7e64 100644 --- a/packages/doenetml-worker/src/components/chemistry/IonicCompound.js +++ b/packages/doenetml-worker/src/components/chemistry/IonicCompound.js @@ -163,15 +163,10 @@ export default class IonicCompound extends InlineComponent { stateVariableDefinitions.latex = { public: true, + forRenderer: true, shadowingInstructions: { createComponentOfType: "latex", }, - additionalStateVariablesDefined: [ - { - variableName: "latexWithInputChildren", - forRenderer: true, - }, - ], returnDependencies: () => ({ ionicCompound: { dependencyType: "stateVariable", @@ -193,7 +188,7 @@ export default class IonicCompound extends InlineComponent { latex = "[\\text{Invalid Ionic Compound}]"; } return { - setValue: { latex, latexWithInputChildren: [latex] }, + setValue: { latex }, }; }, }; diff --git a/packages/doenetml-worker/src/components/dynamicalSystems/ODESystem.js b/packages/doenetml-worker/src/components/dynamicalSystems/ODESystem.js index 1b7fe4f08..bddbaba9e 100644 --- a/packages/doenetml-worker/src/components/dynamicalSystems/ODESystem.js +++ b/packages/doenetml-worker/src/components/dynamicalSystems/ODESystem.js @@ -411,6 +411,7 @@ export default class ODESystem extends InlineComponent { stateVariableDefinitions.latex = { public: true, + forRenderer: true, shadowingInstructions: { createComponentOfType: "latex", }, @@ -532,23 +533,6 @@ export default class ODESystem extends InlineComponent { }, }; - stateVariableDefinitions.latexWithInputChildren = { - forRenderer: true, - returnDependencies: () => ({ - latex: { - dependencyType: "stateVariable", - variableName: "latex", - }, - }), - definition: function ({ dependencyValues }) { - return { - setValue: { - latexWithInputChildren: [dependencyValues.latex], - }, - }; - }, - }; - stateVariableDefinitions.numericalRHSf = { additionalStateVariablesDefined: ["numericalRHSfDefinitions"], returnDependencies: () => ({ diff --git a/packages/doenetml-worker/src/test/tagSpecific/mathdisplay.test.ts b/packages/doenetml-worker/src/test/tagSpecific/mathdisplay.test.ts new file mode 100644 index 000000000..3e377aa9f --- /dev/null +++ b/packages/doenetml-worker/src/test/tagSpecific/mathdisplay.test.ts @@ -0,0 +1,1295 @@ +import { describe, expect, it, vi } from "vitest"; +import { createTestCore, returnAllStateVariables } from "../utils/test-core"; +import { cleanLatex } from "../utils/math"; +import { + moveMath, + moveText, + updateMathInputValue, + updateValue, +} from "../utils/actions"; +import { test_in_graph } from "../utils/in-graph"; +import Core from "../../Core"; + +const Mock = vi.fn(); +vi.stubGlobal("postMessage", Mock); +vi.mock("hyperformula"); + +describe("Displayed math tag tests", async () => { + it("inline and display", async () => { + let core = await createTestCore({ + doenetML: ` + \\sin(x) + \\cos(x) + `, + }); + + const stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/m"].stateValues.renderMode).eq("inline"); + expect(stateVariables["/m"].stateValues.latex).eq("\\sin(x)"); + expect(stateVariables["/m"].stateValues.text).eq("sin(x)"); + expect(stateVariables["/me"].stateValues.renderMode).eq("display"); + expect(stateVariables["/me"].stateValues.latex).eq("\\cos(x)"); + expect(stateVariables["/me"].stateValues.text).eq("cos(x)"); + }); + + it("numbered equations", async () => { + let core = await createTestCore({ + doenetML: ` + + \\sin(x) + + \\cos(x) + + \\tan(x) + + +

We have equation , equation , and equation .

+

From copying properties: $e1.equationTag{assignNames="te1"}, $e2.equationTag{assignNames="te2"}, and $e3.equationTag{assignNames="te3"}.

+ + + + `, + }); + + const stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/e1"].stateValues.renderMode).eq("numbered"); + expect(stateVariables["/e1"].stateValues.equationTag).eq("1"); + expect(stateVariables["/e1"].stateValues.latex).eq("\\sin(x)"); + expect(stateVariables["/e1"].stateValues.text).eq("sin(x)"); + expect(stateVariables["/e2"].stateValues.renderMode).eq("numbered"); + expect(stateVariables["/e2"].stateValues.equationTag).eq("2"); + expect(stateVariables["/e2"].stateValues.latex).eq("\\cos(x)"); + expect(stateVariables["/e2"].stateValues.text).eq("cos(x)"); + expect(stateVariables["/e3"].stateValues.renderMode).eq("numbered"); + expect(stateVariables["/e3"].stateValues.equationTag).eq("3"); + expect(stateVariables["/e3"].stateValues.latex).eq("\\tan(x)"); + expect(stateVariables["/e3"].stateValues.text).eq("tan(x)"); + + expect(stateVariables["/p1"].stateValues.text).eq( + "We have equation (1), equation (2), and equation (3).", + ); + + expect(stateVariables["/re1"].stateValues.linkText).eq("(1)"); + expect(stateVariables["/re2"].stateValues.linkText).eq("(2)"); + expect(stateVariables["/re3"].stateValues.linkText).eq("(3)"); + + expect(stateVariables["/p2"].stateValues.text).eq( + "From copying properties: 1, 2, and 3.", + ); + + expect(stateVariables["/te1"].stateValues.value).eq("1"); + expect(stateVariables["/te2"].stateValues.value).eq("2"); + expect(stateVariables["/te3"].stateValues.value).eq("3"); + }); + + it("dynamic numbered equations", async () => { + let core = await createTestCore({ + doenetML: ` +

Number of equations 1:

+

Number of equations 2:

+ + x + + + + + y + + + + + z + + +

x: , equation

+

m1: , equation

+

m2: , equation

+

m3: , equation

+

m4: , equation

+

m5: , equation

+

m6: , equation

+

y: , equation

+

n1: , equation

+

n2: , equation

+

n3: , equation

+

n4: , equation

+

n5: , equation

+

n6: , equation

+

z: , equation

+ + `, + }); + + async function checkEquationNumbering(m: number, n: number) { + let counter = 1; + const stateVariables = await returnAllStateVariables(core); + + expect(stateVariables["/x"].stateValues.latex).eq("x"); + expect(stateVariables["/x"].stateValues.equationTag).eq( + `${counter}`, + ); + expect(stateVariables["/px"].stateValues.text).eq( + `x: ${counter}, equation (${counter})`, + ); + expect(stateVariables["/etx"].stateValues.text).eq(`${counter}`); + expect(stateVariables["/rx"].stateValues.linkText).eq( + `(${counter})`, + ); + + for (let i = 1; i <= m; i++) { + counter++; + + expect(stateVariables[`/m${i}/eq`].stateValues.latex).eq( + `${i} m`, + ); + expect(stateVariables[`/m${i}/eq`].stateValues.equationTag).eq( + `${counter}`, + ); + + if (i <= 6) { + expect(stateVariables[`/pm${i}`].stateValues.text).eq( + `m${i}: ${counter}, equation (${counter})`, + ); + expect(stateVariables[`/etm${i}`].stateValues.text).eq( + `${counter}`, + ); + expect(stateVariables[`/rm${i}`].stateValues.linkText).eq( + `(${counter})`, + ); + } + } + for (let i = m + 1; i <= 6; i++) { + expect(stateVariables[`/pm${i}`].stateValues.text).eq( + `m${i}: , equation ???`, + ); + expect(stateVariables[`/etm${i}`].stateValues.text).eq(``); + expect(stateVariables[`/rm${i}`].stateValues.linkText).eq( + `???`, + ); + } + + counter++; + + expect(stateVariables["/y"].stateValues.latex).eq("y"); + expect(stateVariables["/y"].stateValues.equationTag).eq( + `${counter}`, + ); + expect(stateVariables["/py"].stateValues.text).eq( + `y: ${counter}, equation (${counter})`, + ); + expect(stateVariables["/ety"].stateValues.text).eq(`${counter}`); + expect(stateVariables["/ry"].stateValues.linkText).eq( + `(${counter})`, + ); + + for (let i = 1; i <= n; i++) { + counter++; + + expect(stateVariables[`/n${i}/eq`].stateValues.latex).eq( + `${i} n`, + ); + expect(stateVariables[`/n${i}/eq`].stateValues.equationTag).eq( + `${counter}`, + ); + + if (i <= 6) { + expect(stateVariables[`/pn${i}`].stateValues.text).eq( + `n${i}: ${counter}, equation (${counter})`, + ); + expect(stateVariables[`/etn${i}`].stateValues.text).eq( + `${counter}`, + ); + expect(stateVariables[`/rn${i}`].stateValues.linkText).eq( + `(${counter})`, + ); + } + } + + for (let i = n + 1; i <= 6; i++) { + expect(stateVariables[`/pn${i}`].stateValues.text).eq( + `n${i}: , equation ???`, + ); + expect(stateVariables[`/etn${i}`].stateValues.text).eq(``); + expect(stateVariables[`/rn${i}`].stateValues.linkText).eq( + `???`, + ); + } + + counter++; + + expect(stateVariables["/z"].stateValues.latex).eq("z"); + expect(stateVariables["/z"].stateValues.equationTag).eq( + `${counter}`, + ); + expect(stateVariables["/pz"].stateValues.text).eq( + `z: ${counter}, equation (${counter})`, + ); + expect(stateVariables["/etz"].stateValues.text).eq(`${counter}`); + expect(stateVariables["/rz"].stateValues.linkText).eq( + `(${counter})`, + ); + } + + let m = 2, + n = 1; + await checkEquationNumbering(m, n); + + m = 4; + await updateMathInputValue({ latex: m.toString(), name: "/m", core }); + await checkEquationNumbering(m, n); + + n = 2; + await updateMathInputValue({ latex: n.toString(), name: "/n", core }); + await checkEquationNumbering(m, n); + + m = 0; + await updateMathInputValue({ latex: m.toString(), name: "/m", core }); + await checkEquationNumbering(m, n); + + n = 6; + await updateMathInputValue({ latex: n.toString(), name: "/n", core }); + await checkEquationNumbering(m, n); + + m = 3; + await updateMathInputValue({ latex: m.toString(), name: "/m", core }); + await checkEquationNumbering(m, n); + + n = 1; + await updateMathInputValue({ latex: n.toString(), name: "/n", core }); + await checkEquationNumbering(m, n); + }); + + it("math inside", async () => { + let core = await createTestCore({ + doenetML: ` + x+x + y+y + z+z + `, + }); + + const stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/m"].stateValues.latex).eq("2 x"); + expect(stateVariables["/m"].stateValues.text).eq("2 x"); + expect(stateVariables["/me"].stateValues.latex).eq("2 y"); + expect(stateVariables["/me"].stateValues.text).eq("2 y"); + expect(stateVariables["/men"].stateValues.latex).eq("2 z"); + expect(stateVariables["/men"].stateValues.text).eq("2 z"); + expect(stateVariables["/men"].stateValues.equationTag).eq("1"); + }); + + it("number inside", async () => { + let core = await createTestCore({ + doenetML: ` + 1 + 2+0i + 3+4i + 5+1i + 6-1i + 0-i + `, + }); + + const stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/m1"].stateValues.latex).eq("1"); + expect(stateVariables["/m1"].stateValues.text).eq("1"); + expect(stateVariables["/me1"].stateValues.latex).eq("2"); + expect(stateVariables["/me1"].stateValues.text).eq("2"); + expect(stateVariables["/men1"].stateValues.latex).eq("3 + 4 i"); + expect(stateVariables["/men1"].stateValues.text).eq("3 + 4 i"); + expect(stateVariables["/men1"].stateValues.equationTag).eq("1"); + expect(stateVariables["/m2"].stateValues.latex).eq("5 + i"); + expect(stateVariables["/m2"].stateValues.text).eq("5 + i"); + expect(stateVariables["/me2"].stateValues.latex).eq("6 - i"); + expect(stateVariables["/me2"].stateValues.text).eq("6 - i"); + expect(stateVariables["/men2"].stateValues.latex).eq("-i"); + expect(stateVariables["/men2"].stateValues.text).eq("-i"); + expect(stateVariables["/men2"].stateValues.equationTag).eq("2"); + }); + + it("align equations", async () => { + let core = await createTestCore({ + doenetML: ` + + q \\amp = \\sin(x) + \\cos(x) \\amp = z + + + q \\amp = \\sin(x) + \\cos(x) \\amp = z + + + q \\amp = \\sin(x) + \\cos(x) \\amp = z + + + q \\amp = \\sin(x) + \\cos(x) \\amp = z + + `, + }); + + const stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/md1"].stateValues.latex).eq( + "\\notag q \\amp = \\sin(x)\\\\\\notag \\cos(x) \\amp = z", + ); + expect(stateVariables["/md1"].stateValues.text).eq( + "q = sin(x)\\\\\ncos(x) = z", + ); + expect(stateVariables["/mdn1"].stateValues.latex).eq( + "\\tag{1}q \\amp = \\sin(x)\\\\\\tag{2}\\cos(x) \\amp = z", + ); + expect(stateVariables["/mdn1"].stateValues.text).eq( + "q = sin(x) (1)\\\\\ncos(x) = z (2)", + ); + + expect(stateVariables["/md2"].stateValues.latex).eq( + "\\tag{3}q \\amp = \\sin(x)\\\\\\tag{4}\\cos(x) \\amp = z", + ); + expect(stateVariables["/md2"].stateValues.text).eq( + "q = sin(x) (3)\\\\\ncos(x) = z (4)", + ); + expect(stateVariables["/mdn2"].stateValues.latex).eq( + "\\notag q \\amp = \\sin(x)\\\\\\notag \\cos(x) \\amp = z", + ); + expect(stateVariables["/mdn2"].stateValues.text).eq( + "q = sin(x)\\\\\ncos(x) = z", + ); + }); + + it("dynamic numbered aligned equations", async () => { + let core = await createTestCore({ + doenetML: ` +

Number of equations 1:

+

Number of equations 2:

+ + x + + + + + + + y + + + + + + + z + +

x: , equation

+

m1: , equation

+

m2: , equation

+

m3: , equation

+

m4: , equation

+

m5: , equation

+

m6: , equation

+

y: , equation

+

n1: , equation

+

n2: , equation

+

n3: , equation

+

n4: , equation

+

n5: , equation

+

n6: , equation

+

z: , equation

+ + `, + }); + + async function checkEquationNumbering(m: number, n: number) { + let counter = 1; + const stateVariables = await returnAllStateVariables(core); + + expect(stateVariables["/x"].stateValues.latex).eq("x"); + expect(stateVariables["/x"].stateValues.equationTag).eq( + `${counter}`, + ); + expect(stateVariables["/px"].stateValues.text).eq( + `x: ${counter}, equation (${counter})`, + ); + expect(stateVariables["/etx"].stateValues.text).eq(`${counter}`); + expect(stateVariables["/rx"].stateValues.linkText).eq( + `(${counter})`, + ); + + for (let i = 1; i <= m; i++) { + counter++; + + expect(stateVariables[`/m${i}/eq`].stateValues.latex).eq( + `${i} m &= ${i + 10}`, + ); + expect(stateVariables[`/m${i}/eq`].stateValues.equationTag).eq( + `${counter}`, + ); + + if (i <= 6) { + expect(stateVariables[`/pm${i}`].stateValues.text).eq( + `m${i}: ${counter}, equation (${counter})`, + ); + expect(stateVariables[`/etm${i}`].stateValues.text).eq( + `${counter}`, + ); + expect(stateVariables[`/rm${i}`].stateValues.linkText).eq( + `(${counter})`, + ); + } + } + for (let i = m + 1; i <= 6; i++) { + expect(stateVariables[`/pm${i}`].stateValues.text).eq( + `m${i}: , equation ???`, + ); + expect(stateVariables[`/etm${i}`].stateValues.text).eq(``); + expect(stateVariables[`/rm${i}`].stateValues.linkText).eq( + `???`, + ); + } + + counter++; + + expect(stateVariables["/y"].stateValues.latex).eq("y"); + expect(stateVariables["/y"].stateValues.equationTag).eq( + `${counter}`, + ); + expect(stateVariables["/py"].stateValues.text).eq( + `y: ${counter}, equation (${counter})`, + ); + expect(stateVariables["/ety"].stateValues.text).eq(`${counter}`); + expect(stateVariables["/ry"].stateValues.linkText).eq( + `(${counter})`, + ); + + for (let i = 1; i <= n; i++) { + counter++; + + expect(stateVariables[`/n${i}/eq`].stateValues.latex).eq( + `${i} n &= ${i + 10}`, + ); + expect(stateVariables[`/n${i}/eq`].stateValues.equationTag).eq( + `${counter}`, + ); + + if (i <= 6) { + expect(stateVariables[`/pn${i}`].stateValues.text).eq( + `n${i}: ${counter}, equation (${counter})`, + ); + expect(stateVariables[`/etn${i}`].stateValues.text).eq( + `${counter}`, + ); + expect(stateVariables[`/rn${i}`].stateValues.linkText).eq( + `(${counter})`, + ); + } + } + + for (let i = n + 1; i <= 6; i++) { + expect(stateVariables[`/pn${i}`].stateValues.text).eq( + `n${i}: , equation ???`, + ); + expect(stateVariables[`/etn${i}`].stateValues.text).eq(``); + expect(stateVariables[`/rn${i}`].stateValues.linkText).eq( + `???`, + ); + } + + counter++; + + expect(stateVariables["/z"].stateValues.latex).eq("z"); + expect(stateVariables["/z"].stateValues.equationTag).eq( + `${counter}`, + ); + expect(stateVariables["/pz"].stateValues.text).eq( + `z: ${counter}, equation (${counter})`, + ); + expect(stateVariables["/etz"].stateValues.text).eq(`${counter}`); + expect(stateVariables["/rz"].stateValues.linkText).eq( + `(${counter})`, + ); + } + + let m = 2, + n = 1; + await checkEquationNumbering(m, n); + + m = 4; + await updateMathInputValue({ latex: m.toString(), name: "/m", core }); + await checkEquationNumbering(m, n); + + n = 2; + await updateMathInputValue({ latex: n.toString(), name: "/n", core }); + await checkEquationNumbering(m, n); + + m = 0; + await updateMathInputValue({ latex: m.toString(), name: "/m", core }); + await checkEquationNumbering(m, n); + + n = 6; + await updateMathInputValue({ latex: n.toString(), name: "/n", core }); + await checkEquationNumbering(m, n); + + m = 3; + await updateMathInputValue({ latex: m.toString(), name: "/m", core }); + await checkEquationNumbering(m, n); + + n = 1; + await updateMathInputValue({ latex: n.toString(), name: "/n", core }); + await checkEquationNumbering(m, n); + }); + + it("dynamic numbered aligned equations, numbering swapped", async () => { + let core = await createTestCore({ + doenetML: ` +

Number of equations 1:

+

Number of equations 2:

+ + x + + + + + + + y + + + + + + + z + +

x: , equation

+

m1: , equation

+

m2: , equation

+

m3: , equation

+

m4: , equation

+

m5: , equation

+

m6: , equation

+

y: , equation

+

n1: , equation

+

n2: , equation

+

n3: , equation

+

n4: , equation

+

n5: , equation

+

n6: , equation

+

z: , equation

+ + `, + }); + + async function checkEquationNumbering(m: number, n: number) { + let counter = 1; + const stateVariables = await returnAllStateVariables(core); + + expect(stateVariables["/x"].stateValues.latex).eq("x"); + expect(stateVariables["/x"].stateValues.equationTag).eq( + `${counter}`, + ); + expect(stateVariables["/px"].stateValues.text).eq( + `x: ${counter}, equation (${counter})`, + ); + expect(stateVariables["/etx"].stateValues.text).eq(`${counter}`); + expect(stateVariables["/rx"].stateValues.linkText).eq( + `(${counter})`, + ); + + for (let i = 1; i <= m; i++) { + if (i % 2 === 1) { + counter++; + + expect(stateVariables[`/m${i}/eq`].stateValues.latex).eq( + `${i} m &= ${i + 10}`, + ); + expect( + stateVariables[`/m${i}/eq`].stateValues.equationTag, + ).eq(`${counter}`); + + if (i <= 6) { + expect(stateVariables[`/pm${i}`].stateValues.text).eq( + `m${i}: ${counter}, equation (${counter})`, + ); + expect(stateVariables[`/etm${i}`].stateValues.text).eq( + `${counter}`, + ); + expect( + stateVariables[`/rm${i}`].stateValues.linkText, + ).eq(`(${counter})`); + } + } else { + expect(stateVariables[`/m${i}/eq`].stateValues.latex).eq( + `${i} m &= ${i + 10}`, + ); + + if (i <= 6) { + expect(stateVariables[`/pm${i}`].stateValues.text).eq( + `m${i}: , equation ???`, + ); + expect(stateVariables[`/etm${i}`].stateValues.text).eq( + ``, + ); + expect( + stateVariables[`/rm${i}`].stateValues.linkText, + ).eq(`???`); + } + } + } + for (let i = m + 1; i <= 6; i++) { + expect(stateVariables[`/pm${i}`].stateValues.text).eq( + `m${i}: , equation ???`, + ); + expect(stateVariables[`/etm${i}`].stateValues.text).eq(``); + expect(stateVariables[`/rm${i}`].stateValues.linkText).eq( + `???`, + ); + } + + counter++; + + expect(stateVariables["/y"].stateValues.latex).eq("y"); + expect(stateVariables["/y"].stateValues.equationTag).eq( + `${counter}`, + ); + expect(stateVariables["/py"].stateValues.text).eq( + `y: ${counter}, equation (${counter})`, + ); + expect(stateVariables["/ety"].stateValues.text).eq(`${counter}`); + expect(stateVariables["/ry"].stateValues.linkText).eq( + `(${counter})`, + ); + + for (let i = 1; i <= n; i++) { + if (i % 2 === 0) { + counter++; + + expect(stateVariables[`/n${i}/eq`].stateValues.latex).eq( + `${i} n &= ${i + 10}`, + ); + expect( + stateVariables[`/n${i}/eq`].stateValues.equationTag, + ).eq(`${counter}`); + + if (i <= 6) { + expect(stateVariables[`/pn${i}`].stateValues.text).eq( + `n${i}: ${counter}, equation (${counter})`, + ); + expect(stateVariables[`/etn${i}`].stateValues.text).eq( + `${counter}`, + ); + expect( + stateVariables[`/rn${i}`].stateValues.linkText, + ).eq(`(${counter})`); + } + } else { + expect(stateVariables[`/n${i}/eq`].stateValues.latex).eq( + `${i} n &= ${i + 10}`, + ); + + if (i <= 6) { + expect(stateVariables[`/pn${i}`].stateValues.text).eq( + `n${i}: , equation ???`, + ); + expect(stateVariables[`/etn${i}`].stateValues.text).eq( + ``, + ); + expect( + stateVariables[`/rn${i}`].stateValues.linkText, + ).eq(`???`); + } + } + } + + for (let i = n + 1; i <= 6; i++) { + expect(stateVariables[`/pn${i}`].stateValues.text).eq( + `n${i}: , equation ???`, + ); + expect(stateVariables[`/etn${i}`].stateValues.text).eq(``); + expect(stateVariables[`/rn${i}`].stateValues.linkText).eq( + `???`, + ); + } + + counter++; + + expect(stateVariables["/z"].stateValues.latex).eq("z"); + expect(stateVariables["/z"].stateValues.equationTag).eq( + `${counter}`, + ); + expect(stateVariables["/pz"].stateValues.text).eq( + `z: ${counter}, equation (${counter})`, + ); + expect(stateVariables["/etz"].stateValues.text).eq(`${counter}`); + expect(stateVariables["/rz"].stateValues.linkText).eq( + `(${counter})`, + ); + } + + let m = 2, + n = 1; + await checkEquationNumbering(m, n); + + m = 4; + await updateMathInputValue({ latex: m.toString(), name: "/m", core }); + await checkEquationNumbering(m, n); + + n = 2; + await updateMathInputValue({ latex: n.toString(), name: "/n", core }); + await checkEquationNumbering(m, n); + + m = 0; + await updateMathInputValue({ latex: m.toString(), name: "/m", core }); + await checkEquationNumbering(m, n); + + n = 6; + await updateMathInputValue({ latex: n.toString(), name: "/n", core }); + await checkEquationNumbering(m, n); + + m = 3; + await updateMathInputValue({ latex: m.toString(), name: "/m", core }); + await checkEquationNumbering(m, n); + + n = 1; + await updateMathInputValue({ latex: n.toString(), name: "/n", core }); + await checkEquationNumbering(m, n); + }); + + it("separate with spaces when concatenate children", async () => { + let core = await createTestCore({ + doenetML: ` + beta + s + $b$s + $b$s + + $b$s + + `, + }); + + const stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/m"].stateValues.latex).eq("\\beta s"); + expect(stateVariables["/m"].stateValues.text).eq("β s"); + expect(stateVariables["/me"].stateValues.latex).eq("\\beta s"); + expect(stateVariables["/me"].stateValues.text).eq("β s"); + expect(stateVariables["/md"].stateValues.latex).eq("\\notag \\beta s"); + expect(stateVariables["/md"].stateValues.text).eq("β s"); + }); + + it("add commas to large integers", async () => { + let core = await createTestCore({ + doenetML: ` +

25236501.35

+

25236501.35

+

25236501.35

+

25236501.35

+ `, + }); + + const stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p1"].stateValues.text).eq("25,236,501.35"); + expect(stateVariables["/p2"].stateValues.text).eq("25,236,501.35"); + expect(stateVariables["/p3"].stateValues.text).eq("25, 236, 501.35"); + expect(stateVariables["/p4"].stateValues.text).eq("25, 236, 501.35"); + + expect(stateVariables["/m1"].stateValues.latex).eq("25,236,501.35"); + expect(stateVariables["/m1"].stateValues.text).eq("25, 236, 501.35"); + expect(stateVariables["/m2"].stateValues.latex).eq("25,236,501.35"); + expect(stateVariables["/m2"].stateValues.text).eq("25, 236, 501.35"); + }); + + it("lists inside displayed math", async () => { + let core = await createTestCore({ + doenetML: ` + s = 123 + s = + s = + s = $al + s = $al + s = $sl + s = $snl + + s \\amp= $al + + + s \\amp= $sl + + + s \\amp= $snl + + `, + }); + + const stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/m1"].stateValues.latex).eq("s = 1, 2, 3"); + expect(stateVariables["/m1"].stateValues.text).eq("s = 1, 2, 3"); + expect(stateVariables["/m2"].stateValues.latex).eq("s = 1, 2, 3"); + expect(stateVariables["/m2"].stateValues.text).eq("s = 1, 2, 3"); + expect(stateVariables["/m3"].stateValues.latex).eq("s = 1 2 3"); + expect(stateVariables["/m3"].stateValues.text).eq("s = 1 * 2 * 3"); + expect(stateVariables["/m4"].stateValues.latex).eq("s = 1, 2, 3"); + expect(stateVariables["/m4"].stateValues.text).eq("s = 1, 2, 3"); + expect(stateVariables["/me1"].stateValues.latex).eq("s = 1, 2, 3"); + expect(stateVariables["/me1"].stateValues.text).eq("s = 1, 2, 3"); + expect(stateVariables["/me2"].stateValues.latex).eq("s = 1, 2, 3"); + expect(stateVariables["/me2"].stateValues.text).eq("s = 1, 2, 3"); + expect(stateVariables["/me3"].stateValues.latex).eq("s = 1 2 3"); + expect(stateVariables["/me3"].stateValues.text).eq("s = 1 * 2 * 3"); + expect(stateVariables["/md1"].stateValues.latex).eq( + "\\notag s \\amp= 1, 2, 3", + ); + expect(stateVariables["/md1"].stateValues.text).eq("s = 1, 2, 3"); + expect(stateVariables["/md2"].stateValues.latex).eq( + "\\notag s \\amp= 1, 2, 3", + ); + expect(stateVariables["/md2"].stateValues.text).eq("s = 1, 2, 3"); + expect(stateVariables["/md3"].stateValues.latex).eq( + "\\notag s \\amp= 1 2 3", + ); + expect(stateVariables["/md3"].stateValues.text).eq("s = 1 * 2 * 3"); + }); + + it("change essential latex", async () => { + let core = await createTestCore({ + doenetML: ` + +

+ `, + }); + + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/m"].stateValues.latex).eq(""); + + await updateValue({ name: "/uv", core }); + + stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/m"].stateValues.latex).eq("\\frac{1}{2}"); + }); + + it("subscripts and superscripts numbers to unicode text", async () => { + let core = await createTestCore({ + doenetML: ` +

2x_1y_{23}+z_{456}-a_{7+8-90}

+

2x^1y^{23}+z^{456}-a^{7+8-90}

+

$m1.text

+

$m2.text

+ + + +

+ 2x_1y_{23}+z_{456}-a_{7+8-90} + 2x^1y^{23}+z^{456}-a^{7+8-90} +

+

$md.text

+ + + `, + }); + + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/p1"].stateValues.text).eq( + "2 x₁ y₂₃ + z₄₅₆ - a₇₊₈₋₉₀", + ); + expect(stateVariables["/p2"].stateValues.text).eq( + "2 x¹ y²³ + z⁴⁵⁶ - a⁷⁺⁸⁻⁹⁰", + ); + expect(stateVariables["/p3"].stateValues.text).eq( + "2 x₁ y₂₃ + z₄₅₆ - a₇₊₈₋₉₀", + ); + expect(stateVariables["/p4"].stateValues.text).eq( + "2 x¹ y²³ + z⁴⁵⁶ - a⁷⁺⁸⁻⁹⁰", + ); + expect(stateVariables["/p5"].stateValues.text).eq( + "2 x₁ y₂₃ + z₄₅₆ - a₇₊₈₋₉₀\\\\\n2 x¹ y²³ + z⁴⁵⁶ - a⁷⁺⁸⁻⁹⁰", + ); + expect(stateVariables["/p6"].stateValues.text).eq( + "2 x₁ y₂₃ + z₄₅₆ - a₇₊₈₋₉₀\\\\\n2 x¹ y²³ + z⁴⁵⁶ - a⁷⁺⁸⁻⁹⁰", + ); + }); + + it("m in graph", async () => { + const doenetMLsnippet = ` + + \\frac{\\partial f}{\\partial x} + \\int_a^b f(x) dx + + `; + + await test_in_graph(doenetMLsnippet, moveMath); + }); + + it("m in graph, handle bad anchor coordinates", async () => { + let core = await createTestCore({ + doenetML: ` + + x^2 + + + +

Anchor 1 coordinates:

+

Change anchor 1 coordinates:

+ + + `, + }); + + let stateVariables = await returnAllStateVariables(core); + + expect(cleanLatex(stateVariables["/math1anchor"].stateValues.latex)).eq( + "x", + ); + + // give good anchor coords + await updateMathInputValue({ + latex: "(6,7)", + name: "/anchorCoords1", + core, + }); + + stateVariables = await returnAllStateVariables(core); + + expect(cleanLatex(stateVariables["/math1anchor"].stateValues.latex)).eq( + "(6,7)", + ); + + // give bad anchor coords again + await updateMathInputValue({ + latex: "q", + name: "/anchorCoords1", + core, + }); + + stateVariables = await returnAllStateVariables(core); + + expect(cleanLatex(stateVariables["/math1anchor"].stateValues.latex)).eq( + "q", + ); + }); + + it("me in graph", async () => { + const doenetMLsnippet = ` + + \\frac{\\partial f}{\\partial x} + \\int_a^b f(x) dx + + `; + + await test_in_graph(doenetMLsnippet, moveMath); + }); + + it("md in graph", async () => { + const doenetMLsnippet = ` + + + Q \\amp= \\frac{\\partial f}{\\partial x} + R \\amp= \\frac{\\partial g}{\\partial y} + + + F \\amp=\\int_a^b f(x) dx + G \\amp=\\int_c^d g(y) dy + + + `; + + await test_in_graph(doenetMLsnippet, moveMath); + }); + + async function test_color_via_style(core: Core) { + 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("color m via style", async () => { + let core = await createTestCore({ + doenetML: ` + + + + + + + +

Style number:

+ +

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

+

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

+

x^4 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 + + + `, + }); + + await test_color_via_style(core); + }); + + it("color me via style", async () => { + let core = await createTestCore({ + doenetML: ` + + + + + + + +

Style number:

+ +

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

+

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

+

x^4 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 + + + `, + }); + + await test_color_via_style(core); + }); + + it("color md via style", async () => { + let core = await createTestCore({ + doenetML: ` + + + + + + + +

Style number:

+ +

x^2y^2 is $no_style.textStyleDescription, i.e., the text color is $no_style.textColor and the background color is $no_style.backgroundColor.

+

x^3y^3 is $fixed_style.textStyleDescription, i.e., the text color is $fixed_style.textColor and the background color is $fixed_style.backgroundColor.

+

x^4y^4 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 + + + `, + }); + + await test_color_via_style(core); + }); + + it("math copied by plain macro, but not value, reflects style and anchor position", async () => { + let core = await createTestCore({ + doenetML: ` + + + + + + + + + x^2 + x^3 + + + + + + + $m1{name="m1a"} + $m2{name="m2a"} + + + + + + $m1.latex{assignNames="m1b"} + $m2.latex{assignNames="m2b"} + + + + +

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

+ +

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

+ + `, + }); + + let stateVariables = await returnAllStateVariables(core); + + expect(stateVariables["/m1"].stateValues.latex).eqls("x^2"); + expect(stateVariables["/m1a"].stateValues.latex).eqls("x^2"); + expect(stateVariables["/m1b"].stateValues.value).eqls("x^2"); + expect(stateVariables["/m1c"].stateValues.latex).eqls("x^2"); + expect(stateVariables["/m1d"].stateValues.value).eqls("x^2"); + expect(stateVariables["/m2"].stateValues.latex).eqls("x^3"); + expect(stateVariables["/m2a"].stateValues.latex).eqls("x^3"); + expect(stateVariables["/m2b"].stateValues.value).eqls("x^3"); + expect(stateVariables["/m2c"].stateValues.latex).eqls("x^3"); + expect(stateVariables["/m2d"].stateValues.value).eqls("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 ms + 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( + "(-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 ms + 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( + "(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 ms + await moveText({ name: "/m1b", x: -6, y: 3, core }); + await moveText({ 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/utils/parseMath.ts b/packages/doenetml-worker/src/utils/parseMath.ts index 9e14d9a4a..d52b29332 100644 --- a/packages/doenetml-worker/src/utils/parseMath.ts +++ b/packages/doenetml-worker/src/utils/parseMath.ts @@ -17,12 +17,14 @@ export function createInputStringFromChildren({ format, createInternalLists = false, parser, + createDisplayedMathString = false, }: { children: any; codePre: string; format: "latex" | "text"; createInternalLists?: boolean; - parser: (arg0: string) => any; + parser?: (arg0: string) => any; + createDisplayedMathString?: boolean; }) { let nonStringInd = 0; let nonStringIndByChild: (null | number)[] = []; @@ -46,10 +48,12 @@ export function createInputStringFromChildren({ createInternalLists, nextInternalListInd: nonStringInd, parser, + createDisplayedMathString, }); + let joinString = createDisplayedMathString ? " " : ""; return { - string: result.newChildren.join(""), + string: result.newChildren.join(joinString), internalLists: result.internalLists, }; } @@ -66,6 +70,7 @@ function createInputStringFromChildrenSub({ createInternalLists, nextInternalListInd, parser, + createDisplayedMathString, }: { compositeReplacementRange: CompositeReplacementRange[] | undefined; children: any; @@ -77,7 +82,8 @@ function createInputStringFromChildrenSub({ potentialListComponents?: boolean[]; createInternalLists: boolean; nextInternalListInd: number; - parser: (arg0: string) => any; + parser?: (arg0: string) => any; + createDisplayedMathString: boolean; }): { newChildren: string[]; newPotentialListComponents: boolean[]; @@ -120,6 +126,7 @@ function createInputStringFromChildrenSub({ nonStringIndByChild, format, codePre, + createDisplayedMathString, }), ); } @@ -162,6 +169,7 @@ function createInputStringFromChildrenSub({ createInternalLists, nextInternalListInd, parser, + createDisplayedMathString, }); Object.assign(internalLists, newInternalLists); @@ -263,10 +271,10 @@ function createInputStringFromChildrenSub({ // we will put a list into the ast at this point // (even though one wouldn't be able to get that by parsing a string into math) let code = codePre + nextInternalListInd; - internalLists[code] = parser(listString); + internalLists[code] = parser?.(listString) ?? ""; nextInternalListInd++; listString = returnStringForCode(format, code); - } else { + } else if (!createDisplayedMathString) { listString = "(" + listString + ")"; } } @@ -275,7 +283,8 @@ function createInputStringFromChildrenSub({ } else { // We are not turning the children in a list, // so just concatenate the strings - newChildren.push(childrenInRange.join("")); + let joinString = createDisplayedMathString ? " " : ""; + newChildren.push(childrenInRange.join(joinString)); } if (potentialListComponents) { @@ -301,6 +310,7 @@ function createInputStringFromChildrenSub({ nonStringIndByChild, format, codePre, + createDisplayedMathString, }), ); } @@ -333,6 +343,7 @@ function baseStringFromChildren({ nonStringIndByChild, format, codePre, + createDisplayedMathString, }: { children: any[]; startInd: number; @@ -340,7 +351,11 @@ function baseStringFromChildren({ nonStringIndByChild: (null | number)[]; format: "latex" | "text"; codePre: string; + createDisplayedMathString: boolean; }) { + if (createDisplayedMathString) { + return displayedMathStringFromChildren({ children, startInd, endInd }); + } let str = ""; for (let ind = startInd; ind <= endInd; ind++) { @@ -359,6 +374,7 @@ function baseStringFromChildren({ return str; } + function returnStringForCode(format: string, code: string) { let nextString; if (format === "latex") { @@ -373,3 +389,36 @@ function returnStringForCode(format: string, code: string) { } return nextString; } + +function displayedMathStringFromChildren({ + children, + startInd, + endInd, +}: { + children: any[]; + startInd: number; + endInd: number; +}) { + let pieces = []; + for (let ind = startInd; ind <= endInd; ind++) { + let child = children[ind]; + + if (typeof child !== "object") { + let childTrim = String(child).trim(); + if (childTrim) { + pieces.push(childTrim); + } + } else if (typeof child.stateValues.latex === "string") { + let latex = child.stateValues.latex.trim(); + if (latex) { + pieces.push(latex); + } + } else if (typeof child.stateValues.text === "string") { + let text = child.stateValues.text.trim(); + if (text) { + pieces.push(text); + } + } + } + return pieces.join(" "); +} diff --git a/packages/doenetml/src/Viewer/renderers/math.jsx b/packages/doenetml/src/Viewer/renderers/math.jsx index a12263d02..25ca160a6 100644 --- a/packages/doenetml/src/Viewer/renderers/math.jsx +++ b/packages/doenetml/src/Viewer/renderers/math.jsx @@ -518,23 +518,7 @@ export default React.memo(function MathComponent(props) { endDelim = "\\)"; } - // if element of latexOrInputChildren is a number, - // then that element is an index of which child (a mathInput) to render - // else, that element is a latex string - - // TODO: we don't want deliminers around each piece, - // instead, we want to be able to put the mathInput inside mathjax - // This is just a stopgap solution that works in a few simple cases!!! - - // Note: when a component type gets switched, sometimes state variables change before renderer - // so protect against case where the latexWithInputChildren is gone but renderer is still math - if (!SVs.latexWithInputChildren) { - return null; - } - - let latexOrInputChildren = SVs.latexWithInputChildren.map((x) => - typeof x === "number" ? this.children[x] : beginDelim + x + endDelim, - ); + let latexWithDelims = beginDelim + SVs.latex + endDelim; let anchors = []; if (SVs.mrowChildNames) { @@ -548,50 +532,14 @@ export default React.memo(function MathComponent(props) { let style = textRendererStyle(darkMode, SVs.selectedStyle); - // TODO: BADBADBAD - // Don't understand why MathJax isn't updating when using {latexOrInputChildren} - // so hard coded the only two cases using so far: with 1 or 2 entries - - if (latexOrInputChildren.length === 0) { - return ( - <> - {anchors} - - - ); - } else if (latexOrInputChildren.length === 1) { - return ( - <> - {anchors} - - - {latexOrInputChildren[0]} - - - - ); - } else if (latexOrInputChildren.length === 2) { - return ( - <> - {anchors} - - - {latexOrInputChildren[0]} - {latexOrInputChildren[1]} - - - - ); - } else { - return ( - <> - {anchors} - - - {latexOrInputChildren[0]} - - - - ); - } + return ( + <> + {anchors} + + + {latexWithDelims} + + + + ); }); diff --git a/packages/test-cypress/cypress/e2e/tagSpecific/mathdisplay.cy.js b/packages/test-cypress/cypress/e2e/tagSpecific/mathdisplay.cy.js index 14ad0b1f6..1806227b8 100644 --- a/packages/test-cypress/cypress/e2e/tagSpecific/mathdisplay.cy.js +++ b/packages/test-cypress/cypress/e2e/tagSpecific/mathdisplay.cy.js @@ -6,40 +6,6 @@ describe("Math Display Tag Tests", function () { cy.visit("/"); }); - it("inline and display", () => { - cy.window().then(async (win) => { - win.postMessage( - { - doenetML: ` - a - \\sin(x) - \\cos(x) - `, - }, - "*", - ); - }); - - cy.get(cesc("#\\/_text1")).should("have.text", "a"); // to wait until loaded - - cy.log("Test value displayed in browser"); - cy.get(cesc("#\\/_m1")) - .find(".mjx-mrow") - .eq(0) - .invoke("text") - .then((text) => { - expect(text.trim()).equal("sin(x)"); - }); - // not sure how to test that it is centered - cy.get(cesc("#\\/_me1")) - .find(".mjx-mrow") - .eq(0) - .invoke("text") - .then((text) => { - expect(text.trim()).equal("cos(x)"); - }); - }); - it("numbered equations", () => { cy.window().then(async (win) => { win.postMessage( @@ -377,229 +343,6 @@ describe("Math Display Tag Tests", function () { checkEquationNumbering(3, 1); }); - it("math inside", () => { - cy.window().then(async (win) => { - win.postMessage( - { - doenetML: ` - a - x+x - y+y - z+z - `, - }, - "*", - ); - }); - - cy.get(cesc("#\\/_text1")).should("have.text", "a"); // to wait until loaded - - cy.log("Test value displayed in browser"); - cy.get(cesc("#\\/_m1")) - .find(".mjx-mrow") - .eq(0) - .invoke("text") - .then((text) => { - expect(text.trim()).equal("2x"); - }); - cy.get(cesc("#\\/_me1")) - .find(".mjx-mrow") - .eq(0) - .invoke("text") - .then((text) => { - expect(text.trim()).equal("2y"); - }); - cy.get(cesc("#\\/_men1")) - .find(".mjx-mrow") - .eq(0) - .invoke("text") - .then((text) => { - expect(text.trim()).equal("2z(1)"); - }); - }); - - it("number inside", () => { - cy.window().then(async (win) => { - win.postMessage( - { - doenetML: ` - a - 1 - 2+0i - 3+4i - 5+1i - 6-1i - 0-i - `, - }, - "*", - ); - }); - - cy.get(cesc("#\\/_text1")).should("have.text", "a"); // to wait until loaded - - cy.log("Test value displayed in browser"); - cy.get(cesc("#\\/_m1")) - .find(".mjx-mrow") - .eq(0) - .invoke("text") - .then((text) => { - expect(text.trim()).equal("1"); - }); - cy.get(cesc("#\\/_me1")) - .find(".mjx-mrow") - .eq(0) - .invoke("text") - .then((text) => { - expect(text.trim()).equal("2"); - }); - cy.get(cesc("#\\/_men1")) - .find(".mjx-mtd") - .eq(0) - .invoke("text") - .then((text) => { - expect(text.trim()).equal("3+4i"); - }); - cy.get(cesc("#\\/_m2")) - .find(".mjx-mrow") - .eq(0) - .invoke("text") - .then((text) => { - expect(text.trim()).equal("5+i"); - }); - cy.get(cesc("#\\/_me2")) - .find(".mjx-mrow") - .eq(0) - .invoke("text") - .then((text) => { - expect(text.trim()).equal("6−i"); - }); - cy.get(cesc("#\\/_men2")) - .find(".mjx-mtd") - .eq(0) - .invoke("text") - .then((text) => { - expect(text.trim()).equal("−i"); - }); - }); - - it("align equations", () => { - cy.window().then(async (win) => { - win.postMessage( - { - doenetML: ` - a - - q \\amp = sin(x) - cos(x) \\amp = z - - - q \\amp = sin(x) - cos(x) \\amp = z - - - q \\amp = sin(x) - cos(x) \\amp = z - - - q \\amp = sin(x) - cos(x) \\amp = z - - `, - }, - "*", - ); - }); - - cy.get(cesc("#\\/_text1")).should("have.text", "a"); // to wait until loaded - - cy.log("Test value displayed in browser"); - cy.get(cesc("#\\/_md1")) - .find(".mjx-mtr") - .eq(0) - .invoke("text") - .then((text) => { - expect(text.trim()).equal("q=sin(x)"); - }); - cy.get(cesc("#\\/_md1")) - .find(".mjx-mtr") - .eq(1) - .invoke("text") - .then((text) => { - expect(text.trim()).equal("cos(x)=z"); - }); - cy.get(cesc("#\\/_mdn1")) - .find(".mjx-mlabeledtr") - .eq(0) - .invoke("text") - .then((text) => { - expect(text.trim()).equal("q=sin(x)"); - }); - cy.get(cesc("#\\/_mdn1")) - .find(".mjx-mlabeledtr") - .eq(1) - .invoke("text") - .then((text) => { - expect(text.trim()).equal("cos(x)=z"); - }); - cy.get(cesc("#\\/_mdn1")) - .find(".mjx-label") - .eq(0) - .invoke("text") - .then((text) => { - expect(text.trim()).equal("(1)"); - }); - cy.get(cesc("#\\/_mdn1")) - .find(".mjx-label") - .eq(1) - .invoke("text") - .then((text) => { - expect(text.trim()).equal("(2)"); - }); - cy.get(cesc("#\\/_md2")) - .find(".mjx-mlabeledtr") - .eq(0) - .invoke("text") - .then((text) => { - expect(text.trim()).equal("q=sin(x)"); - }); - cy.get(cesc("#\\/_md2")) - .find(".mjx-mlabeledtr") - .eq(1) - .invoke("text") - .then((text) => { - expect(text.trim()).equal("cos(x)=z"); - }); - cy.get(cesc("#\\/_md2")) - .find(".mjx-label") - .eq(0) - .invoke("text") - .then((text) => { - expect(text.trim()).equal("(3)"); - }); - cy.get(cesc("#\\/_md2")) - .find(".mjx-label") - .eq(1) - .invoke("text") - .then((text) => { - expect(text.trim()).equal("(4)"); - }); - cy.get(cesc("#\\/_mdn2")) - .find(".mjx-mtr") - .eq(0) - .invoke("text") - .then((text) => { - expect(text.trim()).equal("q=sin(x)"); - }); - cy.get(cesc("#\\/_mdn2")) - .find(".mjx-mtr") - .eq(1) - .invoke("text") - .then((text) => { - expect(text.trim()).equal("cos(x)=z"); - }); - }); - it("dynamic numbered aligned equations", () => { cy.window().then(async (win) => { win.postMessage( @@ -1208,1666 +951,4 @@ describe("Math Display Tag Tests", function () { cy.get(cesc("#\\/na")).should("contain.text", "1"); checkEquationNumbering(3, 1); }); - - it("add commas to large integers", () => { - cy.window().then(async (win) => { - win.postMessage( - { - doenetML: ` - a -

25236501.35

-

25236501.35

-

25236501.35

-

25236501.35

- `, - }, - "*", - ); - }); - - cy.get(cesc("#\\/_text1")).should("have.text", "a"); // to wait until loaded - - cy.get(cesc("#\\/_p1")).should("have.text", "25,236,501.35"); - cy.get(cesc("#\\/_p2")).should("have.text", "25,236,501.35"); - cy.get(cesc("#\\/_m1")) - .find(".mjx-mrow") - .eq(0) - .invoke("text") - .then((text) => { - expect(text.trim()).equal("25,236,501.35"); - }); - cy.get(cesc("#\\/_m2")) - .find(".mjx-mrow") - .eq(0) - .invoke("text") - .then((text) => { - expect(text.trim()).equal("25,236,501.35"); - }); - }); - - it("separate with spaces when concatenate children", () => { - cy.window().then(async (win) => { - win.postMessage( - { - doenetML: ` - a - beta - s - $b$s - $b$s - - $b$s - - `, - }, - "*", - ); - }); - - cy.get(cesc("#\\/_text1")).should("have.text", "a"); // to wait until loaded - - cy.get(cesc("#\\/_m1")) - .find(".mjx-mrow") - .eq(0) - .invoke("text") - .then((text) => { - expect(text.trim()).equal("βs"); - }); - cy.get(cesc("#\\/_me1")) - .find(".mjx-mrow") - .eq(0) - .invoke("text") - .then((text) => { - expect(text.trim()).equal("βs"); - }); - cy.get(cesc("#\\/_md1")) - .find(".mjx-mrow") - .eq(0) - .invoke("text") - .then((text) => { - expect(text.trim()).equal("βs"); - }); - - cy.window().then(async (win) => { - let stateVariables = await win.returnAllStateVariables1(); - expect(stateVariables["/_m1"].stateValues.latex).eq("\\beta s"); - expect(stateVariables["/_me1"].stateValues.latex).eq("\\beta s"); - expect(stateVariables["/_md1"].stateValues.latex).eq( - "\\notag \\beta s", - ); - }); - }); - - it("aslist inside displayed math", () => { - cy.window().then(async (win) => { - win.postMessage( - { - doenetML: ` - a - - s= - s=$al - s = $al - - s \\amp= $al - - `, - }, - "*", - ); - }); - - cy.get(cesc("#\\/_text1")).should("have.text", "a"); // to wait until loaded - - cy.get(cesc("#\\/_m1")) - .find(".mjx-mrow") - .eq(0) - .invoke("text") - .then((text) => { - expect(text.trim()).equal("s=1,2,3"); - }); - cy.get(cesc("#\\/_m2")) - .find(".mjx-mrow") - .eq(0) - .invoke("text") - .then((text) => { - expect(text.trim()).equal("s=1,2,3"); - }); - cy.get(cesc("#\\/_me1")) - .find(".mjx-mrow") - .eq(0) - .invoke("text") - .then((text) => { - expect(text.trim()).equal("s=1,2,3"); - }); - cy.get(cesc("#\\/_md1")) - .find(".mjx-mrow") - .eq(0) - .invoke("text") - .then((text) => { - expect(text.trim()).equal("s=1,2,3"); - }); - }); - - it("change essential latex", () => { - cy.window().then(async (win) => { - win.postMessage( - { - doenetML: ` - a - - -

- `, - }, - "*", - ); - }); - - cy.get(cesc("#\\/_text1")).should("have.text", "a"); // to wait until loaded - - cy.get(cesc("#\\/m")) - .find(".mjx-mrow") - .eq(0) - .invoke("text") - .then((text) => { - expect(text.trim()).equal(""); - }); - - cy.window().then(async (win) => { - let stateVariables = await win.returnAllStateVariables1(); - expect(stateVariables["/m"].stateValues.latex).eq(""); - }); - cy.get(cesc("#\\/uv")).click(); - - cy.get(cesc("#\\/m") + " .mjx-mrow").should("contain.text", "12"); - - cy.window().then(async (win) => { - let stateVariables = await win.returnAllStateVariables1(); - expect(stateVariables["/m"].stateValues.latex).eq("\\frac{1}{2}"); - }); - }); - - it("subscripts and superscripts numbers to unicode text", () => { - cy.window().then(async (win) => { - win.postMessage( - { - doenetML: ` -

a

-

2x_1y_{23}+z_{456}-a_{7+8-90}

-

2x^1y^{23}+z^{456}-a^{7+8-90}

-

$m1.text

-

$m2.text

- -

- 2x_1y_{23}+z_{456}-a_{7+8-90} - 2x^1y^{23}+z^{456}-a^{7+8-90} - -

-

$md.text

- - - `, - }, - "*", - ); - }); - - cy.get(cesc("#\\/_text1")).should("have.text", "a"); // to wait until loaded - cy.get(cesc("#\\/m1t")).should( - "have.text", - "2 x₁ y₂₃ + z₄₅₆ - a₇₊₈₋₉₀", - ); - cy.get(cesc("#\\/m2t")).should( - "have.text", - "2 x¹ y²³ + z⁴⁵⁶ - a⁷⁺⁸⁻⁹⁰", - ); - cy.get(cesc("#\\/mdt")).should( - "have.text", - "2 x₁ y₂₃ + z₄₅₆ - a₇₊₈₋₉₀\\\\\n2 x¹ y²³ + z⁴⁵⁶ - a⁷⁺⁸⁻⁹⁰", - ); - }); - - it("m in graph", () => { - cy.window().then(async (win) => { - win.postMessage( - { - doenetML: ` - a - - \\frac{\\partial f}{\\partial x} - \\int_a^b f(x) dx - - -

Anchor 1 coordinates: $math1.anchor

-

Anchor 2 coordinates: $math2.anchor

-

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: $math1

-

Content 2: $math2

-

- - `, - }, - "*", - ); - }); - - cy.get(cesc("#\\/_text1")).should("have.text", "a"); // to wait until loaded - - cy.get(cesc("#\\/pAnchor1") + " .mjx-mrow") - .eq(0) - .should("have.text", "(1,3)"); - cy.get(cesc("#\\/pAnchor2") + " .mjx-mrow") - .eq(0) - .should("have.text", "(0,0)"); - - cy.get(cesc("#\\/pPositionFromAnchor1")).should( - "have.text", - "Position from anchor 1: upperright", - ); - cy.get(cesc("#\\/pPositionFromAnchor2")).should( - "have.text", - "Position from anchor 2: center", - ); - cy.get(cesc("#\\/positionFromAnchor1")).should("have.value", "1"); - cy.get(cesc("#\\/positionFromAnchor2")).should("have.value", "9"); - cy.get(cesc("#\\/pDraggable1")).should( - "have.text", - "Draggable 1: true", - ); - cy.get(cesc("#\\/pDraggable2")).should( - "have.text", - "Draggable 2: true", - ); - cy.get(cesc("#\\/pContent1") + " .mjx-mrow") - .eq(0) - .should("have.text", "∂f∂x"); - cy.get(cesc("#\\/pContent2") + " .mjx-mrow") - .eq(0) - .should("have.text", "∫baf(x)dx"); - - cy.log("move maths by dragging"); - - cy.window().then(async (win) => { - win.callAction1({ - actionName: "moveMath", - componentName: "/math1", - args: { x: -2, y: 3 }, - }); - win.callAction1({ - actionName: "moveMath", - componentName: "/math2", - args: { x: 4, y: -5 }, - }); - }); - - cy.get(cesc("#\\/pAnchor2") + " .mjx-mrow").should( - "contain.text", - "(4,−5)", - ); - - cy.get(cesc("#\\/pAnchor1") + " .mjx-mrow") - .eq(0) - .should("have.text", "(−2,3)"); - cy.get(cesc("#\\/pAnchor2") + " .mjx-mrow") - .eq(0) - .should("have.text", "(4,−5)"); - - cy.log("move maths by entering coordinates"); - - cy.get(cesc("#\\/anchorCoords1") + " textarea").type( - "{home}{shift+end}{backspace}(6,7){enter}", - { force: true }, - ); - cy.get(cesc("#\\/anchorCoords2") + " textarea").type( - "{home}{shift+end}{backspace}(8,9){enter}", - { force: true }, - ); - - cy.get(cesc("#\\/pAnchor2") + " .mjx-mrow").should( - "contain.text", - "(8,9)", - ); - - cy.get(cesc("#\\/pAnchor1") + " .mjx-mrow") - .eq(0) - .should("have.text", "(6,7)"); - cy.get(cesc("#\\/pAnchor2") + " .mjx-mrow") - .eq(0) - .should("have.text", "(8,9)"); - - cy.log("change position from anchor"); - cy.get(cesc("#\\/positionFromAnchor1")).select("lowerLeft"); - cy.get(cesc("#\\/positionFromAnchor2")).select("lowerRight"); - - cy.get(cesc("#\\/pPositionFromAnchor1")).should( - "have.text", - "Position from anchor 1: lowerleft", - ); - cy.get(cesc("#\\/pPositionFromAnchor2")).should( - "have.text", - "Position from anchor 2: lowerright", - ); - - cy.log("make not draggable"); - - cy.get(cesc("#\\/draggable1")).click(); - cy.get(cesc("#\\/draggable2")).click(); - cy.get(cesc("#\\/pDraggable1")).should( - "have.text", - "Draggable 1: false", - ); - cy.get(cesc("#\\/pDraggable2")).should( - "have.text", - "Draggable 2: false", - ); - - cy.log("cannot move maths by dragging"); - cy.window().then(async (win) => { - win.callAction1({ - actionName: "moveMath", - componentName: "/math1", - args: { x: -10, y: -9 }, - }); - win.callAction1({ - actionName: "moveMath", - componentName: "/math2", - args: { x: -8, y: -7 }, - }); - }); - - // since nothing will change, wait for boolean input to change to know core has responded - cy.get(cesc("#\\/bi")).click(); - cy.get(cesc("#\\/b")).should("have.text", "true"); - - cy.get(cesc("#\\/pAnchor1") + " .mjx-mrow") - .eq(0) - .should("have.text", "(6,7)"); - cy.get(cesc("#\\/pAnchor2") + " .mjx-mrow") - .eq(0) - .should("have.text", "(8,9)"); - }); - - it("m in graph, handle bad anchor coordinates", () => { - cy.window().then(async (win) => { - win.postMessage( - { - doenetML: ` - a - - - x^2 - - - -

Anchor 1 coordinates: $m1.anchor

-

Change anchor 1 coordinates:

- - - `, - }, - "*", - ); - }); - - cy.get(cesc("#\\/_text1")).should("have.text", "a"); //wait for page to load - - cy.get(cesc("#\\/pAnchor1") + " .mjx-mrow") - .eq(0) - .should("have.text", "x"); - - cy.log("give good anchor coords"); - - cy.get(cesc("#\\/anchorCoords1") + " textarea").type( - "{home}{shift+end}{backspace}(6,7){enter}", - { force: true }, - ); - - cy.get(cesc("#\\/pAnchor1") + " .mjx-mrow").should( - "contain.text", - "(6,7)", - ); - - cy.get(cesc("#\\/pAnchor1") + " .mjx-mrow") - .eq(0) - .should("have.text", "(6,7)"); - - cy.log("give bad anchor coords again"); - - cy.get(cesc("#\\/anchorCoords1") + " textarea").type( - "{home}{shift+end}{backspace}q{enter}", - { force: true }, - ); - - cy.get(cesc("#\\/pAnchor1") + " .mjx-mrow").should("contain.text", "q"); - - cy.get(cesc("#\\/pAnchor1") + " .mjx-mrow") - .eq(0) - .should("have.text", "q"); - }); - - it("me in graph", () => { - cy.window().then(async (win) => { - win.postMessage( - { - doenetML: ` - a - - \\frac{\\partial f}{\\partial x} - \\int_a^b f(x) dx - - -

Anchor 1 coordinates: $math1.anchor

-

Anchor 2 coordinates: $math2.anchor

-

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: $math1

-

Content 2: $math2

-

- - `, - }, - "*", - ); - }); - - cy.get(cesc("#\\/_text1")).should("have.text", "a"); // to wait until loaded - - cy.get(cesc("#\\/pAnchor1") + " .mjx-mrow") - .eq(0) - .should("have.text", "(1,3)"); - cy.get(cesc("#\\/pAnchor2") + " .mjx-mrow") - .eq(0) - .should("have.text", "(0,0)"); - - cy.get(cesc("#\\/pPositionFromAnchor1")).should( - "have.text", - "Position from anchor 1: upperright", - ); - cy.get(cesc("#\\/pPositionFromAnchor2")).should( - "have.text", - "Position from anchor 2: center", - ); - cy.get(cesc("#\\/positionFromAnchor1")).should("have.value", "1"); - cy.get(cesc("#\\/positionFromAnchor2")).should("have.value", "9"); - cy.get(cesc("#\\/pDraggable1")).should( - "have.text", - "Draggable 1: true", - ); - cy.get(cesc("#\\/pDraggable2")).should( - "have.text", - "Draggable 2: true", - ); - cy.get(cesc("#\\/pContent1") + " .mjx-mrow") - .eq(0) - .should("have.text", "∂f∂x"); - cy.get(cesc("#\\/pContent2") + " .mjx-mrow") - .eq(0) - .should("have.text", "∫baf(x)dx"); - - cy.log("move maths by dragging"); - - cy.window().then(async (win) => { - win.callAction1({ - actionName: "moveMath", - componentName: "/math1", - args: { x: -2, y: 3 }, - }); - win.callAction1({ - actionName: "moveMath", - componentName: "/math2", - args: { x: 4, y: -5 }, - }); - }); - - cy.get(cesc("#\\/pAnchor2") + " .mjx-mrow").should( - "contain.text", - "(4,−5)", - ); - - cy.get(cesc("#\\/pAnchor1") + " .mjx-mrow") - .eq(0) - .should("have.text", "(−2,3)"); - cy.get(cesc("#\\/pAnchor2") + " .mjx-mrow") - .eq(0) - .should("have.text", "(4,−5)"); - - cy.log("move maths by entering coordinates"); - - cy.get(cesc("#\\/anchorCoords1") + " textarea").type( - "{home}{shift+end}{backspace}(6,7){enter}", - { force: true }, - ); - cy.get(cesc("#\\/anchorCoords2") + " textarea").type( - "{home}{shift+end}{backspace}(8,9){enter}", - { force: true }, - ); - - cy.get(cesc("#\\/pAnchor2") + " .mjx-mrow").should( - "contain.text", - "(8,9)", - ); - - cy.get(cesc("#\\/pAnchor1") + " .mjx-mrow") - .eq(0) - .should("have.text", "(6,7)"); - cy.get(cesc("#\\/pAnchor2") + " .mjx-mrow") - .eq(0) - .should("have.text", "(8,9)"); - - cy.log("change position from anchor"); - cy.get(cesc("#\\/positionFromAnchor1")).select("lowerLeft"); - cy.get(cesc("#\\/positionFromAnchor2")).select("lowerRight"); - - cy.get(cesc("#\\/pPositionFromAnchor1")).should( - "have.text", - "Position from anchor 1: lowerleft", - ); - cy.get(cesc("#\\/pPositionFromAnchor2")).should( - "have.text", - "Position from anchor 2: lowerright", - ); - - cy.log("make not draggable"); - - cy.get(cesc("#\\/draggable1")).click(); - cy.get(cesc("#\\/draggable2")).click(); - cy.get(cesc("#\\/pDraggable1")).should( - "have.text", - "Draggable 1: false", - ); - cy.get(cesc("#\\/pDraggable2")).should( - "have.text", - "Draggable 2: false", - ); - - cy.log("cannot move maths by dragging"); - cy.window().then(async (win) => { - win.callAction1({ - actionName: "moveMath", - componentName: "/math1", - args: { x: -10, y: -9 }, - }); - win.callAction1({ - actionName: "moveMath", - componentName: "/math2", - args: { x: -8, y: -7 }, - }); - }); - - // since nothing will change, wait for boolean input to change to know core has responded - cy.get(cesc("#\\/bi")).click(); - cy.get(cesc("#\\/b")).should("have.text", "true"); - - cy.get(cesc("#\\/pAnchor1") + " .mjx-mrow") - .eq(0) - .should("have.text", "(6,7)"); - cy.get(cesc("#\\/pAnchor2") + " .mjx-mrow") - .eq(0) - .should("have.text", "(8,9)"); - }); - - it("md in graph", () => { - cy.window().then(async (win) => { - win.postMessage( - { - doenetML: ` - a - - - Q \\amp= \\frac{\\partial f}{\\partial x} - R \\amp= \\frac{\\partial g}{\\partial y} - - - F \\amp=\\int_a^b f(x) dx - G \\amp=\\int_c^d g(y) dy - - - -

Anchor 1 coordinates: $math1.anchor

-

Anchor 2 coordinates: $math2.anchor

-

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: $math1

-

Content 2: $math2

-

- - `, - }, - "*", - ); - }); - - cy.get(cesc("#\\/_text1")).should("have.text", "a"); // to wait until loaded - - cy.get(cesc("#\\/pAnchor1") + " .mjx-mrow") - .eq(0) - .should("have.text", "(1,3)"); - cy.get(cesc("#\\/pAnchor2") + " .mjx-mrow") - .eq(0) - .should("have.text", "(0,0)"); - - cy.get(cesc("#\\/pPositionFromAnchor1")).should( - "have.text", - "Position from anchor 1: upperright", - ); - cy.get(cesc("#\\/pPositionFromAnchor2")).should( - "have.text", - "Position from anchor 2: center", - ); - cy.get(cesc("#\\/positionFromAnchor1")).should("have.value", "1"); - cy.get(cesc("#\\/positionFromAnchor2")).should("have.value", "9"); - cy.get(cesc("#\\/pDraggable1")).should( - "have.text", - "Draggable 1: true", - ); - cy.get(cesc("#\\/pDraggable2")).should( - "have.text", - "Draggable 2: true", - ); - cy.get(cesc("#\\/pContent1")) - .find(".mjx-mtr") - .eq(0) - .invoke("text") - .then((text) => { - expect(text.trim()).equal("Q=∂f∂x"); - }); - cy.get(cesc("#\\/pContent1")) - .find(".mjx-mtr") - .eq(1) - .invoke("text") - .then((text) => { - expect(text.trim()).equal("R=∂g∂y"); - }); - cy.get(cesc("#\\/pContent2")) - .find(".mjx-mtr") - .eq(0) - .invoke("text") - .then((text) => { - expect(text.trim()).equal("F=∫baf(x)dx"); - }); - cy.get(cesc("#\\/pContent2")) - .find(".mjx-mtr") - .eq(1) - .invoke("text") - .then((text) => { - expect(text.trim()).equal("G=∫dcg(y)dy"); - }); - - cy.log("move maths by dragging"); - - cy.window().then(async (win) => { - win.callAction1({ - actionName: "moveMath", - componentName: "/math1", - args: { x: -2, y: 3 }, - }); - win.callAction1({ - actionName: "moveMath", - componentName: "/math2", - args: { x: 4, y: -5 }, - }); - }); - - cy.get(cesc("#\\/pAnchor2") + " .mjx-mrow").should( - "contain.text", - "(4,−5)", - ); - - cy.get(cesc("#\\/pAnchor1") + " .mjx-mrow") - .eq(0) - .should("have.text", "(−2,3)"); - cy.get(cesc("#\\/pAnchor2") + " .mjx-mrow") - .eq(0) - .should("have.text", "(4,−5)"); - - cy.log("move maths by entering coordinates"); - - cy.get(cesc("#\\/anchorCoords1") + " textarea").type( - "{home}{shift+end}{backspace}(6,7){enter}", - { force: true }, - ); - cy.get(cesc("#\\/anchorCoords2") + " textarea").type( - "{home}{shift+end}{backspace}(8,9){enter}", - { force: true }, - ); - - cy.get(cesc("#\\/pAnchor2") + " .mjx-mrow").should( - "contain.text", - "(8,9)", - ); - - cy.get(cesc("#\\/pAnchor1") + " .mjx-mrow") - .eq(0) - .should("have.text", "(6,7)"); - cy.get(cesc("#\\/pAnchor2") + " .mjx-mrow") - .eq(0) - .should("have.text", "(8,9)"); - - cy.log("change position from anchor"); - cy.get(cesc("#\\/positionFromAnchor1")).select("lowerLeft"); - cy.get(cesc("#\\/positionFromAnchor2")).select("lowerRight"); - - cy.get(cesc("#\\/pPositionFromAnchor1")).should( - "have.text", - "Position from anchor 1: lowerleft", - ); - cy.get(cesc("#\\/pPositionFromAnchor2")).should( - "have.text", - "Position from anchor 2: lowerright", - ); - - cy.log("make not draggable"); - - cy.get(cesc("#\\/draggable1")).click(); - cy.get(cesc("#\\/draggable2")).click(); - cy.get(cesc("#\\/pDraggable1")).should( - "have.text", - "Draggable 1: false", - ); - cy.get(cesc("#\\/pDraggable2")).should( - "have.text", - "Draggable 2: false", - ); - - cy.log("cannot move maths by dragging"); - cy.window().then(async (win) => { - win.callAction1({ - actionName: "moveMath", - componentName: "/math1", - args: { x: -10, y: -9 }, - }); - win.callAction1({ - actionName: "moveMath", - componentName: "/math2", - args: { x: -8, y: -7 }, - }); - }); - - // since nothing will change, wait for boolean input to change to know core has responded - cy.get(cesc("#\\/bi")).click(); - cy.get(cesc("#\\/b")).should("have.text", "true"); - - cy.get(cesc("#\\/pAnchor1") + " .mjx-mrow") - .eq(0) - .should("have.text", "(6,7)"); - cy.get(cesc("#\\/pAnchor2") + " .mjx-mrow") - .eq(0) - .should("have.text", "(8,9)"); - }); - - it("color m via style", () => { - cy.window().then(async (win) => { - win.postMessage( - { - doenetML: ` - - - - - - - -

Style number:

- -

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

-

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

-

x^4 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 - - - `, - }, - "*", - ); - }); - - cy.get(cesc("#\\/tsd_no_style")).should("have.text", "black"); - cy.get(cesc("#\\/tc_no_style")).should("have.text", "black"); - cy.get(cesc("#\\/bc_no_style")).should("have.text", "none"); - - cy.get(cesc("#\\/tsd_fixed_style")).should("have.text", "green"); - cy.get(cesc("#\\/tc_fixed_style")).should("have.text", "green"); - cy.get(cesc("#\\/bc_fixed_style")).should("have.text", "none"); - - cy.get(cesc("#\\/tsd_variable_style")).should("have.text", "black"); - cy.get(cesc("#\\/tc_variable_style")).should("have.text", "black"); - cy.get(cesc("#\\/bc_variable_style")).should("have.text", "none"); - - cy.get(cesc("#\\/no_style")).should( - "have.css", - "color", - "rgb(0, 0, 0)", - ); - cy.get(cesc("#\\/no_style")).should( - "have.css", - "background-color", - "rgba(0, 0, 0, 0)", - ); - - cy.get(cesc("#\\/fixed_style")).should( - "have.css", - "color", - "rgb(0, 128, 0)", - ); - cy.get(cesc("#\\/fixed_style")).should( - "have.css", - "background-color", - "rgba(0, 0, 0, 0)", - ); - - cy.get(cesc("#\\/variable_style")).should( - "have.css", - "color", - "rgb(0, 0, 0)", - ); - cy.get(cesc("#\\/variable_style")).should( - "have.css", - "background-color", - "rgba(0, 0, 0, 0)", - ); - - // TODO: how to test color in graph - - cy.get(cesc("#\\/sn") + " textarea").type("{end}{backspace}2{enter}", { - force: true, - }); - - cy.get(cesc("#\\/tsd_variable_style")).should("have.text", "green"); - cy.get(cesc("#\\/tc_variable_style")).should("have.text", "green"); - cy.get(cesc("#\\/bc_variable_style")).should("have.text", "none"); - - cy.get(cesc("#\\/tsd_no_style")).should("have.text", "black"); - cy.get(cesc("#\\/tc_no_style")).should("have.text", "black"); - cy.get(cesc("#\\/bc_no_style")).should("have.text", "none"); - - cy.get(cesc("#\\/tsd_fixed_style")).should("have.text", "green"); - cy.get(cesc("#\\/tc_fixed_style")).should("have.text", "green"); - cy.get(cesc("#\\/bc_fixed_style")).should("have.text", "none"); - - cy.get(cesc("#\\/no_style")).should( - "have.css", - "color", - "rgb(0, 0, 0)", - ); - cy.get(cesc("#\\/no_style")).should( - "have.css", - "background-color", - "rgba(0, 0, 0, 0)", - ); - - cy.get(cesc("#\\/fixed_style")).should( - "have.css", - "color", - "rgb(0, 128, 0)", - ); - cy.get(cesc("#\\/fixed_style")).should( - "have.css", - "background-color", - "rgba(0, 0, 0, 0)", - ); - - cy.get(cesc("#\\/variable_style")).should( - "have.css", - "color", - "rgb(0, 128, 0)", - ); - cy.get(cesc("#\\/variable_style")).should( - "have.css", - "background-color", - "rgba(0, 0, 0, 0)", - ); - - cy.get(cesc("#\\/sn") + " textarea").type("{end}{backspace}3{enter}", { - force: true, - }); - - cy.get(cesc("#\\/tsd_variable_style")).should( - "have.text", - "red with a blue background", - ); - cy.get(cesc("#\\/tc_variable_style")).should("have.text", "red"); - cy.get(cesc("#\\/bc_variable_style")).should("have.text", "blue"); - - cy.get(cesc("#\\/tsd_no_style")).should("have.text", "black"); - cy.get(cesc("#\\/tc_no_style")).should("have.text", "black"); - cy.get(cesc("#\\/bc_no_style")).should("have.text", "none"); - - cy.get(cesc("#\\/tsd_fixed_style")).should("have.text", "green"); - cy.get(cesc("#\\/tc_fixed_style")).should("have.text", "green"); - cy.get(cesc("#\\/bc_fixed_style")).should("have.text", "none"); - - cy.get(cesc("#\\/no_style")).should( - "have.css", - "color", - "rgb(0, 0, 0)", - ); - cy.get(cesc("#\\/no_style")).should( - "have.css", - "background-color", - "rgba(0, 0, 0, 0)", - ); - - cy.get(cesc("#\\/fixed_style")).should( - "have.css", - "color", - "rgb(0, 128, 0)", - ); - cy.get(cesc("#\\/fixed_style")).should( - "have.css", - "background-color", - "rgba(0, 0, 0, 0)", - ); - - cy.get(cesc("#\\/variable_style")).should( - "have.css", - "color", - "rgb(255, 0, 0)", - ); - cy.get(cesc("#\\/variable_style")).should( - "have.css", - "background-color", - "rgb(0, 0, 255)", - ); - }); - - it("color me via style", () => { - cy.window().then(async (win) => { - win.postMessage( - { - doenetML: ` - - - - - - - -

Style number:

- -

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

-

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

-

x^4 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 - - - `, - }, - "*", - ); - }); - - cy.get(cesc("#\\/tsd_no_style")).should("have.text", "black"); - cy.get(cesc("#\\/tc_no_style")).should("have.text", "black"); - cy.get(cesc("#\\/bc_no_style")).should("have.text", "none"); - - cy.get(cesc("#\\/tsd_fixed_style")).should("have.text", "green"); - cy.get(cesc("#\\/tc_fixed_style")).should("have.text", "green"); - cy.get(cesc("#\\/bc_fixed_style")).should("have.text", "none"); - - cy.get(cesc("#\\/tsd_variable_style")).should("have.text", "black"); - cy.get(cesc("#\\/tc_variable_style")).should("have.text", "black"); - cy.get(cesc("#\\/bc_variable_style")).should("have.text", "none"); - - cy.get(cesc("#\\/no_style")).should( - "have.css", - "color", - "rgb(0, 0, 0)", - ); - cy.get(cesc("#\\/no_style")).should( - "have.css", - "background-color", - "rgba(0, 0, 0, 0)", - ); - - cy.get(cesc("#\\/fixed_style")).should( - "have.css", - "color", - "rgb(0, 128, 0)", - ); - cy.get(cesc("#\\/fixed_style")).should( - "have.css", - "background-color", - "rgba(0, 0, 0, 0)", - ); - - cy.get(cesc("#\\/variable_style")).should( - "have.css", - "color", - "rgb(0, 0, 0)", - ); - cy.get(cesc("#\\/variable_style")).should( - "have.css", - "background-color", - "rgba(0, 0, 0, 0)", - ); - - // TODO: how to test color in graph - - cy.get(cesc("#\\/sn") + " textarea").type("{end}{backspace}2{enter}", { - force: true, - }); - - cy.get(cesc("#\\/tsd_variable_style")).should("have.text", "green"); - cy.get(cesc("#\\/tc_variable_style")).should("have.text", "green"); - cy.get(cesc("#\\/bc_variable_style")).should("have.text", "none"); - - cy.get(cesc("#\\/tsd_no_style")).should("have.text", "black"); - cy.get(cesc("#\\/tc_no_style")).should("have.text", "black"); - cy.get(cesc("#\\/bc_no_style")).should("have.text", "none"); - - cy.get(cesc("#\\/tsd_fixed_style")).should("have.text", "green"); - cy.get(cesc("#\\/tc_fixed_style")).should("have.text", "green"); - cy.get(cesc("#\\/bc_fixed_style")).should("have.text", "none"); - - cy.get(cesc("#\\/no_style")).should( - "have.css", - "color", - "rgb(0, 0, 0)", - ); - cy.get(cesc("#\\/no_style")).should( - "have.css", - "background-color", - "rgba(0, 0, 0, 0)", - ); - - cy.get(cesc("#\\/fixed_style")).should( - "have.css", - "color", - "rgb(0, 128, 0)", - ); - cy.get(cesc("#\\/fixed_style")).should( - "have.css", - "background-color", - "rgba(0, 0, 0, 0)", - ); - - cy.get(cesc("#\\/variable_style")).should( - "have.css", - "color", - "rgb(0, 128, 0)", - ); - cy.get(cesc("#\\/variable_style")).should( - "have.css", - "background-color", - "rgba(0, 0, 0, 0)", - ); - - cy.get(cesc("#\\/sn") + " textarea").type("{end}{backspace}3{enter}", { - force: true, - }); - - cy.get(cesc("#\\/tsd_variable_style")).should( - "have.text", - "red with a blue background", - ); - cy.get(cesc("#\\/tc_variable_style")).should("have.text", "red"); - cy.get(cesc("#\\/bc_variable_style")).should("have.text", "blue"); - - cy.get(cesc("#\\/tsd_no_style")).should("have.text", "black"); - cy.get(cesc("#\\/tc_no_style")).should("have.text", "black"); - cy.get(cesc("#\\/bc_no_style")).should("have.text", "none"); - - cy.get(cesc("#\\/tsd_fixed_style")).should("have.text", "green"); - cy.get(cesc("#\\/tc_fixed_style")).should("have.text", "green"); - cy.get(cesc("#\\/bc_fixed_style")).should("have.text", "none"); - - cy.get(cesc("#\\/no_style")).should( - "have.css", - "color", - "rgb(0, 0, 0)", - ); - cy.get(cesc("#\\/no_style")).should( - "have.css", - "background-color", - "rgba(0, 0, 0, 0)", - ); - - cy.get(cesc("#\\/fixed_style")).should( - "have.css", - "color", - "rgb(0, 128, 0)", - ); - cy.get(cesc("#\\/fixed_style")).should( - "have.css", - "background-color", - "rgba(0, 0, 0, 0)", - ); - - cy.get(cesc("#\\/variable_style")).should( - "have.css", - "color", - "rgb(255, 0, 0)", - ); - cy.get(cesc("#\\/variable_style")).should( - "have.css", - "background-color", - "rgb(0, 0, 255)", - ); - }); - - it("color md via style", () => { - cy.window().then(async (win) => { - win.postMessage( - { - doenetML: ` - - - - - - - -

Style number:

- -

x^2y^2 is $no_style.textStyleDescription, i.e., the text color is $no_style.textColor and the background color is $no_style.backgroundColor.

-

x^3y^3 is $fixed_style.textStyleDescription, i.e., the text color is $fixed_style.textColor and the background color is $fixed_style.backgroundColor.

-

x^4y^4 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 - - - `, - }, - "*", - ); - }); - - cy.get(cesc("#\\/tsd_no_style")).should("have.text", "black"); - cy.get(cesc("#\\/tc_no_style")).should("have.text", "black"); - cy.get(cesc("#\\/bc_no_style")).should("have.text", "none"); - - cy.get(cesc("#\\/tsd_fixed_style")).should("have.text", "green"); - cy.get(cesc("#\\/tc_fixed_style")).should("have.text", "green"); - cy.get(cesc("#\\/bc_fixed_style")).should("have.text", "none"); - - cy.get(cesc("#\\/tsd_variable_style")).should("have.text", "black"); - cy.get(cesc("#\\/tc_variable_style")).should("have.text", "black"); - cy.get(cesc("#\\/bc_variable_style")).should("have.text", "none"); - - cy.get(cesc("#\\/no_style")).should( - "have.css", - "color", - "rgb(0, 0, 0)", - ); - cy.get(cesc("#\\/no_style")).should( - "have.css", - "background-color", - "rgba(0, 0, 0, 0)", - ); - - cy.get(cesc("#\\/fixed_style")).should( - "have.css", - "color", - "rgb(0, 128, 0)", - ); - cy.get(cesc("#\\/fixed_style")).should( - "have.css", - "background-color", - "rgba(0, 0, 0, 0)", - ); - - cy.get(cesc("#\\/variable_style")).should( - "have.css", - "color", - "rgb(0, 0, 0)", - ); - cy.get(cesc("#\\/variable_style")).should( - "have.css", - "background-color", - "rgba(0, 0, 0, 0)", - ); - - // TODO: how to test color in graph - - cy.get(cesc("#\\/sn") + " textarea").type("{end}{backspace}2{enter}", { - force: true, - }); - - cy.get(cesc("#\\/tsd_variable_style")).should("have.text", "green"); - cy.get(cesc("#\\/tc_variable_style")).should("have.text", "green"); - cy.get(cesc("#\\/bc_variable_style")).should("have.text", "none"); - - cy.get(cesc("#\\/tsd_no_style")).should("have.text", "black"); - cy.get(cesc("#\\/tc_no_style")).should("have.text", "black"); - cy.get(cesc("#\\/bc_no_style")).should("have.text", "none"); - - cy.get(cesc("#\\/tsd_fixed_style")).should("have.text", "green"); - cy.get(cesc("#\\/tc_fixed_style")).should("have.text", "green"); - cy.get(cesc("#\\/bc_fixed_style")).should("have.text", "none"); - - cy.get(cesc("#\\/no_style")).should( - "have.css", - "color", - "rgb(0, 0, 0)", - ); - cy.get(cesc("#\\/no_style")).should( - "have.css", - "background-color", - "rgba(0, 0, 0, 0)", - ); - - cy.get(cesc("#\\/fixed_style")).should( - "have.css", - "color", - "rgb(0, 128, 0)", - ); - cy.get(cesc("#\\/fixed_style")).should( - "have.css", - "background-color", - "rgba(0, 0, 0, 0)", - ); - - cy.get(cesc("#\\/variable_style")).should( - "have.css", - "color", - "rgb(0, 128, 0)", - ); - cy.get(cesc("#\\/variable_style")).should( - "have.css", - "background-color", - "rgba(0, 0, 0, 0)", - ); - - cy.get(cesc("#\\/sn") + " textarea").type("{end}{backspace}3{enter}", { - force: true, - }); - - cy.get(cesc("#\\/tsd_variable_style")).should( - "have.text", - "red with a blue background", - ); - cy.get(cesc("#\\/tc_variable_style")).should("have.text", "red"); - cy.get(cesc("#\\/bc_variable_style")).should("have.text", "blue"); - - cy.get(cesc("#\\/tsd_no_style")).should("have.text", "black"); - cy.get(cesc("#\\/tc_no_style")).should("have.text", "black"); - cy.get(cesc("#\\/bc_no_style")).should("have.text", "none"); - - cy.get(cesc("#\\/tsd_fixed_style")).should("have.text", "green"); - cy.get(cesc("#\\/tc_fixed_style")).should("have.text", "green"); - cy.get(cesc("#\\/bc_fixed_style")).should("have.text", "none"); - - cy.get(cesc("#\\/no_style")).should( - "have.css", - "color", - "rgb(0, 0, 0)", - ); - cy.get(cesc("#\\/no_style")).should( - "have.css", - "background-color", - "rgba(0, 0, 0, 0)", - ); - - cy.get(cesc("#\\/fixed_style")).should( - "have.css", - "color", - "rgb(0, 128, 0)", - ); - cy.get(cesc("#\\/fixed_style")).should( - "have.css", - "background-color", - "rgba(0, 0, 0, 0)", - ); - - cy.get(cesc("#\\/variable_style")).should( - "have.css", - "color", - "rgb(255, 0, 0)", - ); - cy.get(cesc("#\\/variable_style")).should( - "have.css", - "background-color", - "rgb(0, 0, 255)", - ); - }); - - it("m copied by plain macro, but not latex, reflects style and anchor position", () => { - cy.window().then(async (win) => { - win.postMessage( - { - doenetML: ` - - - - - - - - a - - - x^2 - x^3 - - - - - - - $m1 - $m2 - - - - - - $m1.latex - $m2.latex - - - - -

$m1 $m2

- -

$m1.latex $m2.latex

- - `, - }, - "*", - ); - }); - - cy.get(cesc("#\\/_text1")).should("have.text", "a"); // to wait until loaded - - cy.window().then(async (win) => { - let stateVariables = await win.returnAllStateVariables1(); - - let m1aName = stateVariables["/g2"].activeChildren[0].componentName; - let m2aName = stateVariables["/g2"].activeChildren[1].componentName; - let m1bName = stateVariables["/g3"].activeChildren[0].componentName; - let m2bName = stateVariables["/g3"].activeChildren[1].componentName; - let m1cName = stateVariables["/p1"].activeChildren[0].componentName; - let m2cName = stateVariables["/p1"].activeChildren[2].componentName; - let m1dName = stateVariables["/p2"].activeChildren[0].componentName; - let m2dName = stateVariables["/p2"].activeChildren[2].componentName; - - let m1cAnchor = "#" + cesc2(m1cName) + " .mjx-mrow"; - let m2cAnchor = "#" + cesc2(m2cName) + " .mjx-mrow"; - let m1dAnchor = "#" + cesc2(m1dName); - let m2dAnchor = "#" + cesc2(m2dName); - - cy.get(m1cAnchor).eq(0).should("have.text", "x2"); - cy.get(m1dAnchor).should("have.text", "x^2"); - cy.get(m2cAnchor).eq(0).should("have.text", "x3"); - cy.get(m2dAnchor).should("have.text", "x^3"); - - cy.get(m1cAnchor).should("have.css", "color", "rgb(0, 128, 0)"); - cy.get(m1dAnchor).should("have.css", "color", "rgb(0, 0, 0)"); - cy.get(m2cAnchor).should("have.css", "color", "rgb(255, 0, 0)"); - cy.get(m2dAnchor).should("have.css", "color", "rgb(0, 0, 0)"); - - cy.get(cesc("#\\/m1coords") + " .mjx-mrow") - .eq(0) - .should("have.text", "(0,0)"); - cy.get(cesc("#\\/m2coords") + " .mjx-mrow") - .eq(0) - .should("have.text", "(3,4)"); - cy.get(cesc("#\\/m1acoords") + " .mjx-mrow") - .eq(0) - .should("have.text", "(0,0)"); - cy.get(cesc("#\\/m2acoords") + " .mjx-mrow") - .eq(0) - .should("have.text", "(3,4)"); - cy.get(cesc("#\\/m1bcoords") + " .mjx-mrow") - .eq(0) - .should("have.text", "(0,0)"); - cy.get(cesc("#\\/m2bcoords") + " .mjx-mrow") - .eq(0) - .should("have.text", "(0,0)"); - - cy.log("move first ms"); - cy.window().then(async (win) => { - win.callAction1({ - actionName: "moveMath", - componentName: "/m1", - args: { x: -2, y: 3 }, - }); - win.callAction1({ - actionName: "moveMath", - componentName: "/m2", - args: { x: 4, y: -5 }, - }); - }); - - cy.get(cesc("#\\/m2coords") + " .mjx-mrow").should( - "contain.text", - "(4,−5)", - ); - - cy.get(cesc("#\\/m1coords") + " .mjx-mrow") - .eq(0) - .should("have.text", "(−2,3)"); - cy.get(cesc("#\\/m2coords") + " .mjx-mrow") - .eq(0) - .should("have.text", "(4,−5)"); - cy.get(cesc("#\\/m1acoords") + " .mjx-mrow") - .eq(0) - .should("have.text", "(−2,3)"); - cy.get(cesc("#\\/m2acoords") + " .mjx-mrow") - .eq(0) - .should("have.text", "(4,−5)"); - cy.get(cesc("#\\/m1bcoords") + " .mjx-mrow") - .eq(0) - .should("have.text", "(0,0)"); - cy.get(cesc("#\\/m2bcoords") + " .mjx-mrow") - .eq(0) - .should("have.text", "(0,0)"); - - cy.log("move second ms"); - cy.window().then(async (win) => { - win.callAction1({ - actionName: "moveMath", - componentName: m1aName, - args: { x: 7, y: 1 }, - }); - win.callAction1({ - actionName: "moveMath", - componentName: m2aName, - args: { x: -8, y: 2 }, - }); - }); - - cy.get(cesc("#\\/m2coords") + " .mjx-mrow").should( - "contain.text", - "(−8,2)", - ); - - cy.get(cesc("#\\/m1coords") + " .mjx-mrow") - .eq(0) - .should("have.text", "(7,1)"); - cy.get(cesc("#\\/m2coords") + " .mjx-mrow") - .eq(0) - .should("have.text", "(−8,2)"); - cy.get(cesc("#\\/m1acoords") + " .mjx-mrow") - .eq(0) - .should("have.text", "(7,1)"); - cy.get(cesc("#\\/m2acoords") + " .mjx-mrow") - .eq(0) - .should("have.text", "(−8,2)"); - cy.get(cesc("#\\/m1bcoords") + " .mjx-mrow") - .eq(0) - .should("have.text", "(0,0)"); - cy.get(cesc("#\\/m2bcoords") + " .mjx-mrow") - .eq(0) - .should("have.text", "(0,0)"); - - cy.log("move third ms"); - cy.window().then(async (win) => { - win.callAction1({ - actionName: "moveText", - componentName: m1bName, - args: { x: -6, y: 3 }, - }); - win.callAction1({ - actionName: "moveText", - componentName: m2bName, - args: { x: -5, y: -4 }, - }); - }); - - cy.get(cesc("#\\/m2bcoords") + " .mjx-mrow").should( - "contain.text", - "(−5,−4)", - ); - - cy.get(cesc("#\\/m1coords") + " .mjx-mrow") - .eq(0) - .should("have.text", "(7,1)"); - cy.get(cesc("#\\/m2coords") + " .mjx-mrow") - .eq(0) - .should("have.text", "(−8,2)"); - cy.get(cesc("#\\/m1acoords") + " .mjx-mrow") - .eq(0) - .should("have.text", "(7,1)"); - cy.get(cesc("#\\/m2acoords") + " .mjx-mrow") - .eq(0) - .should("have.text", "(−8,2)"); - cy.get(cesc("#\\/m1bcoords") + " .mjx-mrow") - .eq(0) - .should("have.text", "(−6,3)"); - cy.get(cesc("#\\/m2bcoords") + " .mjx-mrow") - .eq(0) - .should("have.text", "(−5,−4)"); - }); - }); - - it("warning if have child with string text or latex state variable", () => { - cy.window().then(async (win) => { - win.postMessage( - { - doenetML: `x = x`, - }, - "*", - ); - }); - - cy.get(cesc("#\\/_m1") + " .mjx-mrow") - .eq(0) - .should("have.text", "x="); - - cy.window().then(async (win) => { - let errorWarnings = await win.returnErrorWarnings1(); - - expect(errorWarnings.errors.length).eq(0); - expect(errorWarnings.warnings.length).eq(1); - - expect(errorWarnings.warnings[0].message).contain( - `Child of ignored as it does not have a string "text" or "latex" state variable`, - ); - expect(errorWarnings.warnings[0].level).eq(1); - expect(errorWarnings.warnings[0].doenetMLrange.lineBegin).eq(1); - expect(errorWarnings.warnings[0].doenetMLrange.charBegin).eq(1); - expect(errorWarnings.warnings[0].doenetMLrange.lineEnd).eq(1); - expect(errorWarnings.warnings[0].doenetMLrange.charEnd).eq(29); - }); - }); });