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
+
+ $i m
+
+
+ y
+
+ $i n
+
+
+ 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
+
+
+ $i m &= $v
+
+
+
+ y
+
+
+ $i n &= $v
+
+
+
+ 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
+
+
+ $i m &= $v
+
+
+
+ y
+
+
+ $i n &= $v
+
+
+
+ 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 = 1 2 3
+ 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: `
+
+ Change latex
+ `,
+ });
+
+ 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^2 y^2 is $no_style.textStyleDescription , i.e., the text color is $no_style.textColor and the background color is $no_style.backgroundColor .
+ x^3 y^3 is $fixed_style.textStyleDescription , i.e., the text color is $fixed_style.textColor and the background color is $fixed_style.backgroundColor .
+ x^4 y^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
-
-
- Change latex
- `,
- },
- "*",
- );
- });
-
- 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^2 y^2 is $no_style.textStyleDescription , i.e., the text color is $no_style.textColor and the background color is $no_style.backgroundColor .
- x^3 y^3 is $fixed_style.textStyleDescription , i.e., the text color is $fixed_style.textColor and the background color is $fixed_style.backgroundColor .
- x^4 y^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);
- });
- });
});