diff --git a/packages/doenetml-worker/src/test/graphical/labels.test.ts b/packages/doenetml-worker/src/test/graphical/labels.test.ts new file mode 100644 index 000000000..904dfce85 --- /dev/null +++ b/packages/doenetml-worker/src/test/graphical/labels.test.ts @@ -0,0 +1,893 @@ +import { describe, expect, it, vi } from "vitest"; +import { createTestCore, returnAllStateVariables } from "../utils/test-core"; +import { updateTextInputValue } from "../utils/actions"; +import Core from "../../Core"; + +const Mock = vi.fn(); +vi.stubGlobal("postMessage", Mock); +vi.mock("hyperformula"); + +describe("Label tests", async () => { + async function test_labelIsName_preserved_shadowed_or_no_link(core: Core) { + const stateVariables = await returnAllStateVariables(core); + + expect(stateVariables["/Plabel"].stateValues.value).eq("P"); + expect(stateVariables["/Qlabel"].stateValues.value).eq("Q"); + expect(stateVariables["/Rlabel"].stateValues.value).eq("R"); + expect(stateVariables["/Slabel"].stateValues.value).eq("S"); + expect(stateVariables["/g2Plabel"].stateValues.value).eq("P"); + expect(stateVariables["/g2Qlabel"].stateValues.value).eq("Q"); + expect(stateVariables["/g2Rlabel"].stateValues.value).eq("R"); + expect(stateVariables["/g2Slabel"].stateValues.value).eq("S"); + expect(stateVariables["/Q3label"].stateValues.value).eq("Q"); + expect(stateVariables["/S3label"].stateValues.value).eq("S"); + expect(stateVariables["/Q4label"].stateValues.value).eq("Q"); + expect(stateVariables["/S4label"].stateValues.value).eq("S"); + expect(stateVariables["/g5Plabel"].stateValues.value).eq("P"); + expect(stateVariables["/g5Qlabel"].stateValues.value).eq("Q"); + expect(stateVariables["/g5Rlabel"].stateValues.value).eq("R"); + expect(stateVariables["/g5Slabel"].stateValues.value).eq("S"); + expect(stateVariables["/g5Plabel"].stateValues.value).eq("P"); + expect(stateVariables["/g6Qlabel"].stateValues.value).eq("Q"); + expect(stateVariables["/g6Slabel"].stateValues.value).eq("S"); + expect(stateVariables["/g7Qlabel"].stateValues.value).eq("Q"); + expect(stateVariables["/g7Slabel"].stateValues.value).eq("S"); + + let P2Name = stateVariables["/g2"].activeChildren[0].componentName; + let Q2Name = stateVariables["/g2"].activeChildren[1].componentName; + let R2Name = stateVariables["/g2"].activeChildren[2].componentName; + let S2Name = stateVariables["/g2"].activeChildren[3].componentName; + let P3Name = stateVariables["/g3"].activeChildren[0].componentName; + let Q3Name = stateVariables["/g3"].activeChildren[1].componentName; + let R3Name = stateVariables["/g3"].activeChildren[2].componentName; + let S3Name = stateVariables["/g3"].activeChildren[3].componentName; + let P4Name = stateVariables["/g4"].activeChildren[0].componentName; + let Q4Name = stateVariables["/g4"].activeChildren[1].componentName; + let R4Name = stateVariables["/g4"].activeChildren[2].componentName; + let S4Name = stateVariables["/g4"].activeChildren[3].componentName; + let P5Name = stateVariables["/g5"].activeChildren[0].componentName; + let Q5Name = stateVariables["/g5"].activeChildren[1].componentName; + let R5Name = stateVariables["/g5"].activeChildren[2].componentName; + let S5Name = stateVariables["/g5"].activeChildren[3].componentName; + let P6Name = stateVariables["/g6"].activeChildren[0].componentName; + let Q6Name = stateVariables["/g6"].activeChildren[1].componentName; + let R6Name = stateVariables["/g6"].activeChildren[2].componentName; + let S6Name = stateVariables["/g6"].activeChildren[3].componentName; + let P7Name = stateVariables["/g7"].activeChildren[0].componentName; + let Q7Name = stateVariables["/g7"].activeChildren[1].componentName; + let R7Name = stateVariables["/g7"].activeChildren[2].componentName; + let S7Name = stateVariables["/g7"].activeChildren[3].componentName; + + expect(stateVariables["/P"].stateValues.label).eq("P"); + expect(stateVariables["/P"].stateValues.labelForGraph).eq("P"); + expect(stateVariables["/Q"].stateValues.label).eq("Q"); + expect(stateVariables["/Q"].stateValues.labelForGraph).eq("Q"); + expect(stateVariables["/R"].stateValues.label).eq("R"); + expect(stateVariables["/R"].stateValues.labelForGraph).eq("R"); + expect(stateVariables["/S"].stateValues.label).eq("S"); + expect(stateVariables["/S"].stateValues.labelForGraph).eq("S"); + expect(stateVariables[P2Name].stateValues.label).eq("P"); + expect(stateVariables[P2Name].stateValues.labelForGraph).eq("P"); + expect(stateVariables[Q2Name].stateValues.label).eq(`Q`); + expect(stateVariables[Q2Name].stateValues.labelForGraph).eq(`Q`); + expect(stateVariables[R2Name].stateValues.label).eq(`R`); + expect(stateVariables[R2Name].stateValues.labelForGraph).eq(`R`); + expect(stateVariables[S2Name].stateValues.label).eq(`S`); + expect(stateVariables[S2Name].stateValues.labelForGraph).eq(`S`); + expect(stateVariables["/Q3"].stateValues.label).eq(`Q`); + expect(stateVariables["/Q3"].stateValues.labelForGraph).eq(`Q`); + expect(stateVariables["/S3"].stateValues.label).eq(`S`); + expect(stateVariables["/S3"].stateValues.labelForGraph).eq(`S`); + expect(stateVariables[P3Name].stateValues.label).eq("P"); + expect(stateVariables[P3Name].stateValues.labelForGraph).eq("P"); + expect(stateVariables[Q3Name].stateValues.label).eq(`Q`); + expect(stateVariables[Q3Name].stateValues.labelForGraph).eq(`Q`); + expect(stateVariables[R3Name].stateValues.label).eq(`R`); + expect(stateVariables[R3Name].stateValues.labelForGraph).eq(`R`); + expect(stateVariables[S3Name].stateValues.label).eq(`S`); + expect(stateVariables[S3Name].stateValues.labelForGraph).eq(`S`); + expect(stateVariables["/Q4"].stateValues.label).eq(`Q`); + expect(stateVariables["/Q4"].stateValues.labelForGraph).eq(`Q`); + expect(stateVariables["/S4"].stateValues.label).eq(`S`); + expect(stateVariables["/S4"].stateValues.labelForGraph).eq(`S`); + expect(stateVariables[P4Name].stateValues.label).eq("P"); + expect(stateVariables[P4Name].stateValues.labelForGraph).eq("P"); + expect(stateVariables[Q4Name].stateValues.label).eq(`Q`); + expect(stateVariables[Q4Name].stateValues.labelForGraph).eq(`Q`); + expect(stateVariables[R4Name].stateValues.label).eq(`R`); + expect(stateVariables[R4Name].stateValues.labelForGraph).eq(`R`); + expect(stateVariables[S4Name].stateValues.label).eq(`S`); + expect(stateVariables[S4Name].stateValues.labelForGraph).eq(`S`); + expect(stateVariables[P5Name].stateValues.label).eq("P"); + expect(stateVariables[P5Name].stateValues.labelForGraph).eq("P"); + expect(stateVariables[Q5Name].stateValues.label).eq(`Q`); + expect(stateVariables[Q5Name].stateValues.labelForGraph).eq(`Q`); + expect(stateVariables[R5Name].stateValues.label).eq(`R`); + expect(stateVariables[R5Name].stateValues.labelForGraph).eq(`R`); + expect(stateVariables[S5Name].stateValues.label).eq(`S`); + expect(stateVariables[S5Name].stateValues.labelForGraph).eq(`S`); + expect(stateVariables[P6Name].stateValues.label).eq("P"); + expect(stateVariables[P6Name].stateValues.labelForGraph).eq("P"); + expect(stateVariables[Q6Name].stateValues.label).eq(`Q`); + expect(stateVariables[Q6Name].stateValues.labelForGraph).eq(`Q`); + expect(stateVariables[R6Name].stateValues.label).eq(`R`); + expect(stateVariables[R6Name].stateValues.labelForGraph).eq(`R`); + expect(stateVariables[S6Name].stateValues.label).eq(`S`); + expect(stateVariables[S6Name].stateValues.labelForGraph).eq(`S`); + expect(stateVariables[P7Name].stateValues.label).eq("P"); + expect(stateVariables[P7Name].stateValues.labelForGraph).eq("P"); + expect(stateVariables[Q7Name].stateValues.label).eq(`Q`); + expect(stateVariables[Q7Name].stateValues.labelForGraph).eq(`Q`); + expect(stateVariables[R7Name].stateValues.label).eq(`R`); + expect(stateVariables[R7Name].stateValues.labelForGraph).eq(`R`); + expect(stateVariables[S7Name].stateValues.label).eq(`S`); + expect(stateVariables[S7Name].stateValues.labelForGraph).eq(`S`); + } + + it("labels from labelIsName are preserved when shadowed", async () => { + let core = await createTestCore({ + doenetML: ` + + (1,2) + (3,4) + (5,6) + (7,8) + + + + + + $P + $Q{name="Q3"} + + + + + + $P{labelIsName="false"} + $Q{name="Q4" labelIsName="false"} + + + + + + + + +

P label:

+

Q label:

+

R label:

+

S label:

+

g2/P label:

+

g2/Q label:

+

g2/R label:

+

g2/S label:

+

Q3 label:

+

S3 label:

+

Q4 label:

+

S4 label:

+

g5/P label:

+

g5/Q label:

+

g5/R label:

+

g5/S label:

+

g6/Q3 label:

+

g6/S3 label:

+

g7/Q4 label:

+

g7/S4 label:

+ + `, + }); + + await test_labelIsName_preserved_shadowed_or_no_link(core); + }); + + it("labels from labelIsName are preserved when copy with link=false", async () => { + let core = await createTestCore({ + doenetML: ` + + (1,2) + (3,4) + (5,6) + (7,8) + + + + + + $P{link="false"} + $Q{name="Q3" link="false"} + + + + + + $P{labelIsName="false" link="false"} + $Q{name="Q4" labelIsName="false" link="false"} + + + + + + + + +

P label:

+

Q label:

+

R label:

+

S label:

+

g2/P label:

+

g2/Q label:

+

g2/R label:

+

g2/S label:

+

Q3 label:

+

S3 label:

+

Q4 label:

+

S4 label:

+

g5/P label:

+

g5/Q label:

+

g5/R label:

+

g5/S label:

+

g6/Q3 label:

+

g6/S3 label:

+

g7/Q4 label:

+

g7/S4 label:

+ + `, + }); + + await test_labelIsName_preserved_shadowed_or_no_link(core); + }); + + it("labelIsName in map", async () => { + let core = await createTestCore({ + doenetML: ` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + $g1{name="g7"} + $g2{name="g8"} + $g3{name="g9"} + $g4{name="g10"} + $g5{name="g11"} + $g6{name="g12"} + `, + }); + + let stateVariables = await returnAllStateVariables(core); + + let g1ChildNames = stateVariables["/g1"].activeChildren.map( + (x) => x.componentName, + ); + let g2ChildNames = stateVariables["/g2"].activeChildren.map( + (x) => x.componentName, + ); + let g3ChildNames = stateVariables["/g3"].activeChildren.map( + (x) => x.componentName, + ); + let g4ChildNames = stateVariables["/g4"].activeChildren.map( + (x) => x.componentName, + ); + let g5ChildNames = stateVariables["/g5"].activeChildren.map( + (x) => x.componentName, + ); + let g6ChildNames = stateVariables["/g6"].activeChildren.map( + (x) => x.componentName, + ); + let g7ChildNames = stateVariables["/g7"].activeChildren.map( + (x) => x.componentName, + ); + let g8ChildNames = stateVariables["/g8"].activeChildren.map( + (x) => x.componentName, + ); + let g9ChildNames = stateVariables["/g9"].activeChildren.map( + (x) => x.componentName, + ); + let g10ChildNames = stateVariables["/g10"].activeChildren.map( + (x) => x.componentName, + ); + let g11ChildNames = stateVariables["/g11"].activeChildren.map( + (x) => x.componentName, + ); + let g12ChildNames = stateVariables["/g12"].activeChildren.map( + (x) => x.componentName, + ); + + let g1ChildLabels = Array(5).fill(""); + let g2ChildLabels = Array(5).fill("P"); + let g3ChildLabels = ["A", "B", "C", "", ""]; + let g4ChildLabels = ["A", "B", "C", "P", "P"]; + let g5ChildLabels = Array(5).fill(""); + let g6ChildLabels = Array(5).fill("P"); + + for (let [ind, name] of g1ChildNames.entries()) { + expect(stateVariables[name].stateValues.label).eq( + g1ChildLabels[ind], + ); + } + for (let [ind, name] of g2ChildNames.entries()) { + expect(stateVariables[name].stateValues.label).eq( + g2ChildLabels[ind], + ); + } + for (let [ind, name] of g3ChildNames.entries()) { + expect(stateVariables[name].stateValues.label).eq( + g3ChildLabels[ind], + ); + } + for (let [ind, name] of g4ChildNames.entries()) { + expect(stateVariables[name].stateValues.label).eq( + g4ChildLabels[ind], + ); + } + for (let [ind, name] of g5ChildNames.entries()) { + expect(stateVariables[name].stateValues.label).eq( + g5ChildLabels[ind], + ); + } + for (let [ind, name] of g6ChildNames.entries()) { + expect(stateVariables[name].stateValues.label).eq( + g6ChildLabels[ind], + ); + } + + for (let [ind, name] of g7ChildNames.entries()) { + expect(stateVariables[name].stateValues.label).eq( + g1ChildLabels[ind], + ); + } + for (let [ind, name] of g8ChildNames.entries()) { + expect(stateVariables[name].stateValues.label).eq( + g2ChildLabels[ind], + ); + } + for (let [ind, name] of g9ChildNames.entries()) { + expect(stateVariables[name].stateValues.label).eq( + g3ChildLabels[ind], + ); + } + for (let [ind, name] of g10ChildNames.entries()) { + expect(stateVariables[name].stateValues.label).eq( + g4ChildLabels[ind], + ); + } + for (let [ind, name] of g11ChildNames.entries()) { + expect(stateVariables[name].stateValues.label).eq( + g5ChildLabels[ind], + ); + } + for (let [ind, name] of g12ChildNames.entries()) { + expect(stateVariables[name].stateValues.label).eq( + g6ChildLabels[ind], + ); + } + }); + + it("labelIsName in newNamespace", async () => { + let core = await createTestCore({ + doenetML: ` + + (5,6) + + `, + }); + + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/g/P"].stateValues.label).eq("P"); + }); + + it("labelIsName converts case", async () => { + let core = await createTestCore({ + doenetML: ` + + (5,6) + (1,3) + (-2,1) + (7,-5) + (-6,-8) + (9,0) + + `, + }); + + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/the_first_point"].stateValues.label).eq( + "the first point", + ); + expect(stateVariables["/the-second-point"].stateValues.label).eq( + "the second point", + ); + expect(stateVariables["/theThirdPoint"].stateValues.label).eq( + "the third point", + ); + expect(stateVariables["/TheFourthPoint"].stateValues.label).eq( + "The Fourth Point", + ); + expect(stateVariables["/the-FIFTH_Point"].stateValues.label).eq( + "the FIFTH Point", + ); + expect(stateVariables["/the_SiXiTH-Point"].stateValues.label).eq( + "the SiXiTH Point", + ); + }); + + it("labelIsName and copies", async () => { + // Note: we add groups just so that we can reference the children + // inside the DoenetML + let core = await createTestCore({ + doenetML: ` + + + + + + + + $A{name="C"} + + + + + + $A{name="E" labelIsName} + + + + + + $A{labelIsName} + + +

+

+

+

+

+

+

+ +

+

+

+

+

+

+

+ `, + }); + + async function check_items({ lA, lD, lE, l6, l7 }) { + const stateVariables = await returnAllStateVariables(core); + const p6Name = + stateVariables["/g6"].activeChildren[0].componentName; + const p7Name = + stateVariables["/g7"].activeChildren[0].componentName; + + expect(stateVariables["/lA"].stateValues.value).eq(lA); + expect(stateVariables["/lB"].stateValues.value).eq(lA); + expect(stateVariables["/lC"].stateValues.value).eq(lA); + expect(stateVariables["/lD"].stateValues.value).eq(lD); + expect(stateVariables["/lE"].stateValues.value).eq(lE); + expect(stateVariables["/lp6"].stateValues.value).eq(l6); + expect(stateVariables["/lp7"].stateValues.value).eq(l7); + + expect(stateVariables["/A"].stateValues.label).eq(lA); + expect(stateVariables["/A"].stateValues.labelForGraph).eq(lA); + expect(stateVariables["/B"].stateValues.label).eq(lA); + expect(stateVariables["/B"].stateValues.labelForGraph).eq(lA); + expect(stateVariables["/C"].stateValues.label).eq(lA); + expect(stateVariables["/C"].stateValues.labelForGraph).eq(lA); + expect(stateVariables["/D"].stateValues.label).eq(lD); + expect(stateVariables["/D"].stateValues.labelForGraph).eq(lD); + expect(stateVariables["/E"].stateValues.label).eq(lE); + expect(stateVariables["/E"].stateValues.labelForGraph).eq(lE); + expect(stateVariables[p6Name].stateValues.label).eq(l6); + expect(stateVariables[p6Name].stateValues.labelForGraph).eq(l6); + expect(stateVariables[p7Name].stateValues.label).eq(l7); + expect(stateVariables[p7Name].stateValues.labelForGraph).eq(l7); + } + + let lA = "A", + lD = "D", + lE = "E", + l6 = "A", + l7 = "A"; + await check_items({ lA, lD, lE, l6, l7 }); + + lA = "F"; + await updateTextInputValue({ text: lA, name: "/tiA", core }); + await check_items({ lA, lD, lE, l6, l7 }); + + lA = "G"; + await updateTextInputValue({ name: "/tiB", text: lA, core }); + await check_items({ lA, lD, lE, l6, l7 }); + + lA = "H"; + await updateTextInputValue({ name: "/tiC", text: lA, core }); + await check_items({ lA, lD, lE, l6, l7 }); + + lD = "I"; + await updateTextInputValue({ name: "/tiD", text: lD, core }); + await check_items({ lA, lD, lE, l6, l7 }); + + lE = "J"; + await updateTextInputValue({ name: "/tiE", text: lE, core }); + await check_items({ lA, lD, lE, l6, l7 }); + + l6 = "K"; + await updateTextInputValue({ name: "/tip6", text: l6, core }); + await check_items({ lA, lD, lE, l6, l7 }); + + l7 = "L"; + await updateTextInputValue({ name: "/tip7", text: l7, core }); + await check_items({ lA, lD, lE, l6, l7 }); + }); + + it("copy and add labelIsName", async () => { + let core = await createTestCore({ + doenetML: ` + + + + + + + + $A{name="C" labelIsName} + + + + + + $A{labelIsName} + + +

+

+

+

+

+ +

+

+

+

+

+`, + }); + + async function check_items({ lA, lB, lC, l4, l5 }) { + const stateVariables = await returnAllStateVariables(core); + const p4Name = + stateVariables["/g4"].activeChildren[0].componentName; + const p5Name = + stateVariables["/g5"].activeChildren[0].componentName; + + expect(stateVariables["/lA"].stateValues.value).eq(lA); + expect(stateVariables["/lB"].stateValues.value).eq(lB); + expect(stateVariables["/lC"].stateValues.value).eq(lC); + expect(stateVariables["/lp4"].stateValues.value).eq(l4); + expect(stateVariables["/lp5"].stateValues.value).eq(l5); + + expect(stateVariables["/A"].stateValues.label).eq(lA); + expect(stateVariables["/A"].stateValues.labelForGraph).eq(lA); + expect(stateVariables["/B"].stateValues.label).eq(lB); + expect(stateVariables["/B"].stateValues.labelForGraph).eq(lB); + expect(stateVariables["/C"].stateValues.label).eq(lC); + expect(stateVariables["/C"].stateValues.labelForGraph).eq(lC); + expect(stateVariables[p4Name].stateValues.label).eq(l4); + expect(stateVariables[p4Name].stateValues.labelForGraph).eq(l4); + expect(stateVariables[p5Name].stateValues.label).eq(l5); + expect(stateVariables[p5Name].stateValues.labelForGraph).eq(l5); + } + + let lA = "", + lB = "B", + lC = "C", + l4 = "A", + l5 = "A"; + await check_items({ lA, lB, lC, l4, l5 }); + + lA = "F"; + await updateTextInputValue({ text: lA, name: "/tiA", core }); + await check_items({ lA, lB, lC, l4, l5 }); + + lB = "G"; + await updateTextInputValue({ name: "/tiB", text: lB, core }); + await check_items({ lA, lB, lC, l4, l5 }); + + lC = "H"; + await updateTextInputValue({ name: "/tiC", text: lC, core }); + await check_items({ lA, lB, lC, l4, l5 }); + + l4 = "I"; + await updateTextInputValue({ name: "/tip4", text: l4, core }); + await check_items({ lA, lB, lC, l4, l5 }); + + l5 = "J"; + await updateTextInputValue({ name: "/tip5", text: l5, core }); + await check_items({ lA, lB, lC, l4, l5 }); + }); + + async function test_label_labelIsName_copies( + doenetMLForA: string, + useLabelCopies = false, + ) { + const labelD = useLabelCopies + ? `