From 049da57c9baf60e5b6b0676415864995393affc7 Mon Sep 17 00:00:00 2001 From: Charles Nykamp Date: Sat, 30 Nov 2024 12:07:47 -0600 Subject: [PATCH] Convert unlinked copying tests to vitest (#264) * also added fix: do not expand replacements when returning state variables --------- Co-authored-by: Duane Nykamp --- packages/doenetml-worker/src/CoreWorker.js | 14 + .../src/test/copying/unlinked.test.ts | 574 +++++++++ .../src/test/utils/test-core.ts | 14 + .../cypress/e2e/copying/unlinked.cy.js | 1082 ----------------- 4 files changed, 602 insertions(+), 1082 deletions(-) create mode 100644 packages/doenetml-worker/src/test/copying/unlinked.test.ts delete mode 100644 packages/test-cypress/cypress/e2e/copying/unlinked.cy.js diff --git a/packages/doenetml-worker/src/CoreWorker.js b/packages/doenetml-worker/src/CoreWorker.js index f63ba598f..dbf26142b 100644 --- a/packages/doenetml-worker/src/CoreWorker.js +++ b/packages/doenetml-worker/src/CoreWorker.js @@ -220,6 +220,20 @@ async function returnAllStateVariables(core) { stateValues: {}, }); for (let vName in component.state) { + if ( + [ + "replacements", + "recursiveReplacements", + "fullRecursiveReplacements", + ].includes(vName) && + core.componentInfoObjects.isCompositeComponent({ + componentType: component.componentType, + }) && + !component.isExpanded + ) { + // don't expand a composite to get these replacement state variables + continue; + } compObj.stateValues[vName] = removeFunctionsMathExpressionClass( await component.state[vName].value, ); diff --git a/packages/doenetml-worker/src/test/copying/unlinked.test.ts b/packages/doenetml-worker/src/test/copying/unlinked.test.ts new file mode 100644 index 000000000..69cb3952c --- /dev/null +++ b/packages/doenetml-worker/src/test/copying/unlinked.test.ts @@ -0,0 +1,574 @@ +import { describe, expect, it, vi } from "vitest"; +import { createTestCore, returnAllStateVariables } from "../utils/test-core"; +import Core from "../../Core"; +import { callAction, movePoint, updateValue } from "../utils/actions"; + +const Mock = vi.fn(); +vi.stubGlobal("postMessage", Mock); +vi.mock("hyperformula"); + +async function test_no_overwritten_attributes({ + core, + namespaceInsideGraph = false, +}: { + core: Core; + namespaceInsideGraph?: boolean; +}) { + const namespaceName = namespaceInsideGraph ? "/gr" : "/g"; + const graphNamePostfix = namespaceInsideGraph ? "/g" : ""; + + const stateVariables = await returnAllStateVariables(core); + expect( + stateVariables[`${namespaceName}${graphNamePostfix}`].stateValues.xmax, + ).eq(5); + expect( + stateVariables[`${namespaceName}/A`].stateValues.xs.map((v) => v.tree), + ).eqls([1, 2]); + expect(stateVariables[`${namespaceName}/A`].stateValues.styleNumber).eq(2); + expect(stateVariables[`${namespaceName}/A`].stateValues.label).eq("A"); + expect(stateVariables[`${namespaceName}/A`].stateValues.labelPosition).eq( + "upperleft", + ); + + expect( + stateVariables[`${namespaceName}2${graphNamePostfix}`].stateValues.xmax, + ).eq(5); + expect( + stateVariables[`${namespaceName}2/A`].stateValues.xs.map((v) => v.tree), + ).eqls([1, 2]); + expect(stateVariables[`${namespaceName}2/A`].stateValues.styleNumber).eq(2); + expect(stateVariables[`${namespaceName}2/A`].stateValues.label).eq("A"); + expect(stateVariables[`${namespaceName}2/A`].stateValues.labelPosition).eq( + "upperleft", + ); + + expect( + stateVariables[`${namespaceName}3${graphNamePostfix}`].stateValues.xmax, + ).eq(5); + expect( + stateVariables[`${namespaceName}3/A`].stateValues.xs.map((v) => v.tree), + ).eqls([1, 2]); + expect(stateVariables[`${namespaceName}3/A`].stateValues.styleNumber).eq(2); + expect(stateVariables[`${namespaceName}3/A`].stateValues.label).eq("A"); + expect(stateVariables[`${namespaceName}3/A`].stateValues.labelPosition).eq( + "upperleft", + ); +} + +async function test_linked_copy_overwrites_attributes({ + core, +}: { + core: Core; +}) { + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/g"].stateValues.xmin).eq(-10); + expect(stateVariables["/g"].stateValues.xmax).eq(5); + expect(stateVariables["/g/A"].stateValues.xs.map((v) => v.tree)).eqls([ + 1, 2, + ]); + expect(stateVariables["/g/A"].stateValues.styleNumber).eq(2); + expect(stateVariables["/g/B"].stateValues.xs.map((v) => v.tree)).eqls([ + 3, 4, + ]); + expect(stateVariables["/g/B"].stateValues.styleNumber).eq(1); + + expect(stateVariables["/g2"].stateValues.xmin).eq(-3); + expect(stateVariables["/g2"].stateValues.xmax).eq(7); + expect(stateVariables["/g2/A"].stateValues.xs.map((v) => v.tree)).eqls([ + 1, 2, + ]); + expect(stateVariables["/g2/A"].stateValues.styleNumber).eq(2); + expect(stateVariables["/g2/B"].stateValues.xs.map((v) => v.tree)).eqls([ + 3, 4, + ]); + expect(stateVariables["/g2/B"].stateValues.styleNumber).eq(3); + + expect(stateVariables["/g3"].stateValues.xmin).eq(-3); + expect(stateVariables["/g3"].stateValues.xmax).eq(7); + expect(stateVariables["/g3/A"].stateValues.xs.map((v) => v.tree)).eqls([ + 1, 2, + ]); + expect(stateVariables["/g3/A"].stateValues.styleNumber).eq(2); + expect(stateVariables["/g3/B"].stateValues.xs.map((v) => v.tree)).eqls([ + 3, 4, + ]); + expect(stateVariables["/g3/B"].stateValues.styleNumber).eq(3); +} + +async function test_unlinked_copy_overwrites_attributes({ + core, +}: { + core: Core; +}) { + // TODO: overwriting attributes of unlinked copy of linked copy isn't working as we'd like. + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/g"].stateValues.xmin).eq(-10); + expect(stateVariables["/g"].stateValues.xmax).eq(5); + expect(stateVariables["/g"].stateValues.ymax).eq(10); + expect(stateVariables["/g/A"].stateValues.xs.map((v) => v.tree)).eqls([ + 1, 2, + ]); + expect(stateVariables["/g/A"].stateValues.styleNumber).eqls(2); + expect(stateVariables["/g/B"].stateValues.xs.map((v) => v.tree)).eqls([ + 3, 4, + ]); + expect(stateVariables["/g/B"].stateValues.styleNumber).eqls(1); + + expect(stateVariables["/g2"].stateValues.xmax).eq(5); + expect(stateVariables["/g2"].stateValues.xmin).eq(-3); + expect(stateVariables["/g2"].stateValues.ymax).eq(10); + expect(stateVariables["/g2/A"].stateValues.xs.map((v) => v.tree)).eqls([ + 1, 2, + ]); + expect(stateVariables["/g2/A"].stateValues.styleNumber).eqls(2); + expect(stateVariables["/g2/B"].stateValues.xs.map((v) => v.tree)).eqls([ + 3, 4, + ]); + expect(stateVariables["/g2/B"].stateValues.styleNumber).eqls(1); + + expect(stateVariables["/g3"].stateValues.xmax).eq(7); + expect(stateVariables["/g3"].stateValues.xmin).eq(-5); + expect(stateVariables["/g3"].stateValues.ymax).eq(8); + expect(stateVariables["/g3/A"].stateValues.xs.map((v) => v.tree)).eqls([ + 1, 2, + ]); + expect(stateVariables["/g3/A"].stateValues.styleNumber).eqls(2); + expect(stateVariables["/g3/B"].stateValues.xs.map((v) => v.tree)).eqls([ + 3, 4, + ]); + // TODO: uncomment when fix the behavior so this passes + // expect(stateVariables["/g3/B"].stateValues.styleNumber).eqls(4); +} + +describe("Unlinked Copying Tests", async () => { + it("unlinked copy of linked copy copies state variables from uncopied children and attributes", async () => { + // When creating a linked copy, often children and attributes are not copied + // but instead the resulting state variables are just shadowed. + // Then, when subsequently creating an unlinked copy of that linked copy, + // there are no children or attributes to copy. + // Instead, the unlinked copy needs to copy the state variables values directly + let core = await createTestCore({ + doenetML: ` + + (1,2) + + + + `, + }); + + await test_no_overwritten_attributes({ + core, + }); + }); + + it("unlinked copy of linked copy copies state variables from uncopied children and attributes, group inside", async () => { + // When creating a linked copy, often children and attributes are not copied + // but instead the resulting state variables are just shadowed. + // Then, when subsequently creating an unlinked copy of that linked copy, + // there are no children or attributes to copy. + // Instead, the unlinked copy needs to copy the state variables values directly + let core = await createTestCore({ + doenetML: ` + + + (1,2) + + + + + `, + }); + + await test_no_overwritten_attributes({ + core, + }); + }); + + it("unlinked copy of linked copy copies state variables from uncopied children and attributes, group outside", async () => { + // When creating a linked copy, often children and attributes are not copied + // but instead the resulting state variables are just shadowed. + // Then, when subsequently creating an unlinked copy of that linked copy, + // there are no children or attributes to copy. + // Instead, the unlinked copy needs to copy the state variables values directly + let core = await createTestCore({ + doenetML: ` + + + (1,2) + + + + + `, + }); + + await test_no_overwritten_attributes({ + namespaceInsideGraph: true, + core, + }); + }); + + it("unlinked copy of linked copy, overwrite attributes of linked copy", async () => { + let core = await createTestCore({ + doenetML: ` + + (1,2) + (3,4) + + + + `, + }); + + await test_linked_copy_overwrites_attributes({ core }); + }); + + it("unlinked copy of linked copy, overwrite attributes of linked copy, group inside", async () => { + let core = await createTestCore({ + doenetML: ` + + + (1,2) + (3,4) + + + + + `, + }); + + await test_linked_copy_overwrites_attributes({ core }); + }); + + // TODO: overwriting attributes of unlinked copy of linked copy isn't working as we'd like. + it("unlinked copy of linked copy, overwrite attributes of unlinked copy", async () => { + let core = await createTestCore({ + doenetML: ` + + (1,2) + (3,4) + + + + `, + }); + + await test_unlinked_copy_overwrites_attributes({ core }); + }); + + it("unlinked copy of linked copy, overwrite attributes of unlinked copy, group inside", async () => { + let core = await createTestCore({ + doenetML: ` + + + (1,2) + (3,4) + + + + + `, + }); + + await test_unlinked_copy_overwrites_attributes({ core }); + }); + + async function test_snapshot({ + core, + snapshotType, + point_p_initialVal = [0, 0], + point_q_initialVal = [0, 0], + }: { + core: Core; + snapshotType: "updateValue" | "callAction"; + point_p_initialVal?: number[]; + point_q_initialVal?: number[]; + }) { + let p = point_p_initialVal; + let q = point_q_initialVal; + let p2 = [NaN, NaN]; + let q2 = [NaN, NaN]; + + async function check_snapshot() { + let stateVariables = await returnAllStateVariables(core); + + expect(stateVariables["/P"].stateValues.xs.map((v) => v.tree)).eqls( + p, + ); + expect(stateVariables["/Q"].stateValues.xs.map((v) => v.tree)).eqls( + q, + ); + + let graph2Children = stateVariables["/graph2"].activeChildren; + + if (Number.isNaN(p2[0])) { + // Unlinked copied content does not exist yet + expect(graph2Children.length).eqls(0); + } else { + let P2 = graph2Children[0].componentName; + let Q2 = graph2Children[1].componentName; + expect( + stateVariables[P2].stateValues.xs.map((v) => v.tree), + ).eqls(p2); + expect( + stateVariables[Q2].stateValues.xs.map((v) => v.tree), + ).eqls(q2); + } + } + await check_snapshot(); + + // move points + p = [-1, 7]; + q = [5, 3]; + await movePoint({ name: "/P", x: p[0], y: p[1], core }); + await movePoint({ name: "/Q", x: q[0], y: q[1], core }); + await check_snapshot(); + + // Take snapshot, which should create conditional content using current + // state var values of graph1 + p2 = p; + q2 = q; + if (snapshotType === "updateValue") { + await updateValue({ name: "/takeSnapshot", core }); + } else { + await callAction({ name: "/takeSnapshot", core }); + } + await check_snapshot(); + + // move points again, unlinked copied points should not move + p = [2, -9]; + q = [1, 8]; + await movePoint({ name: "/P", x: p[0], y: p[1], core }); + await movePoint({ name: "/Q", x: q[0], y: q[1], core }); + await check_snapshot(); + } + + it("create snapshot of group with conditionalContent", async () => { + let core = await createTestCore({ + doenetML: ` + + + + + + + + false + + + + + + + $gr{link="false"} + + + `, + }); + + await test_snapshot({ core, snapshotType: "updateValue" }); + }); + + it("create snapshot of group with callAction", async () => { + let core = await createTestCore({ + doenetML: ` + + + + + + + + + + $gr{link="false"} + + + + + `, + }); + + await test_snapshot({ core, snapshotType: "callAction" }); + }); + + it("create snapshot of map with conditionalContent", async () => { + let core = await createTestCore({ + doenetML: ` + + + + + + + + + + false + + + + + + + $mp{link="false"} + + + `, + }); + + await test_snapshot({ + core, + snapshotType: "updateValue", + point_p_initialVal: [1, 1], + point_q_initialVal: [1, 2], + }); + }); + + it("create snapshot of map with callAction", async () => { + let core = await createTestCore({ + doenetML: ` + + + + + + + + + + + + $mp{link="false"} + + + + + + `, + }); + + await test_snapshot({ + core, + snapshotType: "callAction", + point_p_initialVal: [1, 1], + point_q_initialVal: [1, 2], + }); + }); + + it("create snapshot of map in a group with conditionalContent", async () => { + let core = await createTestCore({ + doenetML: ` + + + + + + + + + + + + false + + + + + + + + $gr{link="false"} + + + `, + }); + + await test_snapshot({ + core, + snapshotType: "updateValue", + point_p_initialVal: [1, 1], + point_q_initialVal: [1, 2], + }); + }); + + it("create snapshot of map in a group with callAction", async () => { + let core = await createTestCore({ + doenetML: ` + + + + + + + + + + + + + + $gr{link="false"} + + + + + + `, + }); + + await test_snapshot({ + core, + snapshotType: "callAction", + point_p_initialVal: [1, 1], + point_q_initialVal: [1, 2], + }); + }); + + it("create snapshot of map with conditionalContent", async () => { + let core = await createTestCore({ + doenetML: ` + + + + + + + + + + false + + + + + + + + $mp{link="false"} + + + `, + }); + + await test_snapshot({ + core, + snapshotType: "updateValue", + point_p_initialVal: [1, 1], + point_q_initialVal: [1, 2], + }); + }); +}); diff --git a/packages/doenetml-worker/src/test/utils/test-core.ts b/packages/doenetml-worker/src/test/utils/test-core.ts index 4222c0027..6b6e79b2f 100644 --- a/packages/doenetml-worker/src/test/utils/test-core.ts +++ b/packages/doenetml-worker/src/test/utils/test-core.ts @@ -116,6 +116,20 @@ export async function returnAllStateVariables(core: Core) { }; for (let vName in component.state) { + if ( + [ + "replacements", + "recursiveReplacements", + "fullRecursiveReplacements", + ].includes(vName) && + core.componentInfoObjects.isCompositeComponent({ + componentType: component.componentType, + }) && + !component.isExpanded + ) { + // don't expand a composite to get these replacement state variables + continue; + } compObj.stateValues[vName] = await component.state[vName].value; } compObj.activeChildren = component.activeChildren.map((x) => diff --git a/packages/test-cypress/cypress/e2e/copying/unlinked.cy.js b/packages/test-cypress/cypress/e2e/copying/unlinked.cy.js deleted file mode 100644 index f0383272a..000000000 --- a/packages/test-cypress/cypress/e2e/copying/unlinked.cy.js +++ /dev/null @@ -1,1082 +0,0 @@ -import me from "math-expressions"; -import { cesc, cesc2 } from "@doenet/utils"; - -describe("Unlinked Copying Tests", function () { - beforeEach(() => { - cy.clearIndexedDB(); - cy.visit("/"); - }); - - it("unlinked copy of linked copy copies state variables from uncopied children and attributes", () => { - // When creating a linked copy, often children and attributes are not copied - // but instead the resulting state variables are just shadowed. - // Then, when subsequently creating an unlinked copy of that linked copy, - // there are no children or attributes to copy. - // Instead, the unlinked copy needs to copy the state variables values directly - cy.window().then(async (win) => { - win.postMessage( - { - doenetML: ` - a - - (1,2) - - - - - - `, - }, - "*", - ); - }); - - cy.get(cesc("#\\/_text1")).should("have.text", "a"); // to wait for page to load - - cy.window().then(async (win) => { - let stateVariables = await win.returnAllStateVariables1(); - expect(stateVariables["/g"].stateValues.xmax).eq(5); - expect(stateVariables["/g/A"].stateValues.xs).eqls([1, 2]); - expect(stateVariables["/g/A"].stateValues.styleNumber).eq(2); - expect(stateVariables["/g/A"].stateValues.label).eq("A"); - expect(stateVariables["/g/A"].stateValues.labelPosition).eq( - "upperleft", - ); - - expect(stateVariables["/g2"].stateValues.xmax).eq(5); - expect(stateVariables["/g2/A"].stateValues.xs).eqls([1, 2]); - expect(stateVariables["/g2/A"].stateValues.styleNumber).eq(2); - expect(stateVariables["/g2/A"].stateValues.label).eq("A"); - expect(stateVariables["/g2/A"].stateValues.labelPosition).eq( - "upperleft", - ); - - expect(stateVariables["/g3"].stateValues.xmax).eq(5); - expect(stateVariables["/g3/A"].stateValues.xs).eqls([1, 2]); - expect(stateVariables["/g3/A"].stateValues.styleNumber).eq(2); - expect(stateVariables["/g3/A"].stateValues.label).eq("A"); - expect(stateVariables["/g3/A"].stateValues.labelPosition).eq( - "upperleft", - ); - }); - }); - - it("unlinked copy of linked copy copies state variables from uncopied children and attributes, group inside", () => { - // When creating a linked copy, often children and attributes are not copied - // but instead the resulting state variables are just shadowed. - // Then, when subsequently creating an unlinked copy of that linked copy, - // there are no children or attributes to copy. - // Instead, the unlinked copy needs to copy the state variables values directly - cy.window().then(async (win) => { - win.postMessage( - { - doenetML: ` - a - - - (1,2) - - - - - - - `, - }, - "*", - ); - }); - - cy.get(cesc("#\\/_text1")).should("have.text", "a"); // to wait for page to load - - cy.window().then(async (win) => { - let stateVariables = await win.returnAllStateVariables1(); - expect(stateVariables["/g"].stateValues.xmax).eq(5); - expect(stateVariables["/g/A"].stateValues.xs).eqls([1, 2]); - expect(stateVariables["/g/A"].stateValues.styleNumber).eq(2); - expect(stateVariables["/g/A"].stateValues.label).eq("A"); - expect(stateVariables["/g/A"].stateValues.labelPosition).eq( - "upperleft", - ); - - expect(stateVariables["/g2"].stateValues.xmax).eq(5); - expect(stateVariables["/g2/A"].stateValues.xs).eqls([1, 2]); - expect(stateVariables["/g2/A"].stateValues.styleNumber).eq(2); - expect(stateVariables["/g2/A"].stateValues.label).eq("A"); - expect(stateVariables["/g2/A"].stateValues.labelPosition).eq( - "upperleft", - ); - - expect(stateVariables["/g3"].stateValues.xmax).eq(5); - expect(stateVariables["/g3/A"].stateValues.xs).eqls([1, 2]); - expect(stateVariables["/g3/A"].stateValues.styleNumber).eq(2); - expect(stateVariables["/g3/A"].stateValues.label).eq("A"); - expect(stateVariables["/g3/A"].stateValues.labelPosition).eq( - "upperleft", - ); - }); - }); - - it("unlinked copy of linked copy copies state variables from uncopied children and attributes, group outside", () => { - // When creating a linked copy, often children and attributes are not copied - // but instead the resulting state variables are just shadowed. - // Then, when subsequently creating an unlinked copy of that linked copy, - // there are no children or attributes to copy. - // Instead, the unlinked copy needs to copy the state variables values directly - cy.window().then(async (win) => { - win.postMessage( - { - doenetML: ` - a - - - (1,2) - - - - - - - `, - }, - "*", - ); - }); - - cy.get(cesc("#\\/_text1")).should("have.text", "a"); // to wait for page to load - - cy.window().then(async (win) => { - let stateVariables = await win.returnAllStateVariables1(); - expect(stateVariables["/gr/g"].stateValues.xmax).eq(5); - expect(stateVariables["/gr/A"].stateValues.xs).eqls([1, 2]); - expect(stateVariables["/gr/A"].stateValues.styleNumber).eq(2); - expect(stateVariables["/gr/A"].stateValues.label).eq("A"); - expect(stateVariables["/gr/A"].stateValues.labelPosition).eq( - "upperleft", - ); - - expect(stateVariables["/gr2/g"].stateValues.xmax).eq(5); - expect(stateVariables["/gr2/A"].stateValues.xs).eqls([1, 2]); - expect(stateVariables["/gr2/A"].stateValues.styleNumber).eq(2); - expect(stateVariables["/gr2/A"].stateValues.label).eq("A"); - expect(stateVariables["/gr2/A"].stateValues.labelPosition).eq( - "upperleft", - ); - - expect(stateVariables["/gr3/g"].stateValues.xmax).eq(5); - expect(stateVariables["/gr3/A"].stateValues.xs).eqls([1, 2]); - expect(stateVariables["/gr3/A"].stateValues.styleNumber).eq(2); - expect(stateVariables["/gr3/A"].stateValues.label).eq("A"); - expect(stateVariables["/gr3/A"].stateValues.labelPosition).eq( - "upperleft", - ); - }); - }); - - it("unlinked copy of linked copy, overwrite attributes of linked copy", () => { - cy.window().then(async (win) => { - win.postMessage( - { - doenetML: ` - a - - (1,2) - (3,4) - - - - - - `, - }, - "*", - ); - }); - - cy.get(cesc("#\\/_text1")).should("have.text", "a"); // to wait for page to load - - cy.window().then(async (win) => { - let stateVariables = await win.returnAllStateVariables1(); - expect(stateVariables["/g"].stateValues.xmin).eq(-10); - expect(stateVariables["/g"].stateValues.xmax).eq(5); - expect(stateVariables["/g/A"].stateValues.xs).eqls([1, 2]); - expect(stateVariables["/g/A"].stateValues.styleNumber).eq(2); - expect(stateVariables["/g/B"].stateValues.xs).eqls([3, 4]); - expect(stateVariables["/g/B"].stateValues.styleNumber).eq(1); - - expect(stateVariables["/g2"].stateValues.xmin).eq(-3); - expect(stateVariables["/g2"].stateValues.xmax).eq(7); - expect(stateVariables["/g2/A"].stateValues.xs).eqls([1, 2]); - expect(stateVariables["/g2/A"].stateValues.styleNumber).eq(2); - expect(stateVariables["/g2/B"].stateValues.xs).eqls([3, 4]); - expect(stateVariables["/g2/B"].stateValues.styleNumber).eq(3); - - expect(stateVariables["/g3"].stateValues.xmin).eq(-3); - expect(stateVariables["/g3"].stateValues.xmax).eq(7); - expect(stateVariables["/g3/A"].stateValues.xs).eqls([1, 2]); - expect(stateVariables["/g3/A"].stateValues.styleNumber).eq(2); - expect(stateVariables["/g3/B"].stateValues.xs).eqls([3, 4]); - expect(stateVariables["/g3/B"].stateValues.styleNumber).eq(3); - }); - }); - - it("unlinked copy of linked copy, overwrite attributes of linked copy, group inside", () => { - cy.window().then(async (win) => { - win.postMessage( - { - doenetML: ` - a - - - (1,2) - (3,4) - - - - - - - `, - }, - "*", - ); - }); - - cy.get(cesc("#\\/_text1")).should("have.text", "a"); // to wait for page to load - - cy.window().then(async (win) => { - let stateVariables = await win.returnAllStateVariables1(); - expect(stateVariables["/g"].stateValues.xmin).eq(-10); - expect(stateVariables["/g"].stateValues.xmax).eq(5); - expect(stateVariables["/g/A"].stateValues.xs).eqls([1, 2]); - expect(stateVariables["/g/A"].stateValues.styleNumber).eq(2); - expect(stateVariables["/g/B"].stateValues.xs).eqls([3, 4]); - expect(stateVariables["/g/B"].stateValues.styleNumber).eq(1); - - expect(stateVariables["/g2"].stateValues.xmin).eq(-3); - expect(stateVariables["/g2"].stateValues.xmax).eq(7); - expect(stateVariables["/g2/A"].stateValues.xs).eqls([1, 2]); - expect(stateVariables["/g2/A"].stateValues.styleNumber).eq(2); - expect(stateVariables["/g2/B"].stateValues.xs).eqls([3, 4]); - expect(stateVariables["/g2/B"].stateValues.styleNumber).eq(3); - - expect(stateVariables["/g3"].stateValues.xmin).eq(-3); - expect(stateVariables["/g3"].stateValues.xmax).eq(7); - expect(stateVariables["/g3/A"].stateValues.xs).eqls([1, 2]); - expect(stateVariables["/g3/A"].stateValues.styleNumber).eq(2); - expect(stateVariables["/g3/B"].stateValues.xs).eqls([3, 4]); - expect(stateVariables["/g3/B"].stateValues.styleNumber).eq(3); - }); - }); - - // TODO: overwriting attributes of unlinked copy of linked copy isn't working as we'd like. - it("unlinked copy of linked copy, overwrite attributes of unlinked copy", () => { - cy.window().then(async (win) => { - win.postMessage( - { - doenetML: ` - a - - (1,2) - (3,4) - - - - - - `, - }, - "*", - ); - }); - - cy.get(cesc("#\\/_text1")).should("have.text", "a"); // to wait for page to load - - cy.window().then(async (win) => { - let stateVariables = await win.returnAllStateVariables1(); - expect(stateVariables["/g"].stateValues.xmax).eq(5); - expect(stateVariables["/g"].stateValues.xmin).eq(-10); - expect(stateVariables["/g"].stateValues.ymax).eq(10); - expect(stateVariables["/g/A"].stateValues.xs).eqls([1, 2]); - expect(stateVariables["/g/A"].stateValues.styleNumber).eqls(2); - expect(stateVariables["/g/B"].stateValues.xs).eqls([3, 4]); - expect(stateVariables["/g/B"].stateValues.styleNumber).eqls(1); - - expect(stateVariables["/g2"].stateValues.xmax).eq(5); - expect(stateVariables["/g2"].stateValues.xmin).eq(-3); - expect(stateVariables["/g2"].stateValues.ymax).eq(10); - expect(stateVariables["/g2/A"].stateValues.xs).eqls([1, 2]); - expect(stateVariables["/g2/A"].stateValues.styleNumber).eqls(2); - expect(stateVariables["/g2/B"].stateValues.xs).eqls([3, 4]); - expect(stateVariables["/g2/B"].stateValues.styleNumber).eqls(1); - - expect(stateVariables["/g3"].stateValues.xmax).eq(7); - expect(stateVariables["/g3"].stateValues.xmin).eq(-5); - expect(stateVariables["/g3"].stateValues.ymax).eq(8); - expect(stateVariables["/g3/A"].stateValues.xs).eqls([1, 2]); - expect(stateVariables["/g3/A"].stateValues.styleNumber).eqls(2); - expect(stateVariables["/g3/B"].stateValues.xs).eqls([3, 4]); - // TODO: uncomment when fix the behavior so this passes - // expect(stateVariables["/g3/B"].stateValues.styleNumber).eqls(4); - }); - }); - - it("unlinked copy of linked copy, overwrite attributes of unlinked copy, group inside", () => { - cy.window().then(async (win) => { - win.postMessage( - { - doenetML: ` - a - - - (1,2) - (3,4) - - - - - - - `, - }, - "*", - ); - }); - - cy.get(cesc("#\\/_text1")).should("have.text", "a"); // to wait for page to load - - cy.window().then(async (win) => { - let stateVariables = await win.returnAllStateVariables1(); - expect(stateVariables["/g"].stateValues.xmax).eq(5); - expect(stateVariables["/g"].stateValues.xmin).eq(-10); - expect(stateVariables["/g"].stateValues.ymax).eq(10); - expect(stateVariables["/g/A"].stateValues.xs).eqls([1, 2]); - expect(stateVariables["/g/A"].stateValues.styleNumber).eqls(2); - expect(stateVariables["/g/B"].stateValues.xs).eqls([3, 4]); - expect(stateVariables["/g/B"].stateValues.styleNumber).eqls(1); - - expect(stateVariables["/g2"].stateValues.xmax).eq(5); - expect(stateVariables["/g2"].stateValues.xmin).eq(-3); - expect(stateVariables["/g2"].stateValues.ymax).eq(10); - expect(stateVariables["/g2/A"].stateValues.xs).eqls([1, 2]); - expect(stateVariables["/g2/A"].stateValues.styleNumber).eqls(2); - expect(stateVariables["/g2/B"].stateValues.xs).eqls([3, 4]); - expect(stateVariables["/g2/B"].stateValues.styleNumber).eqls(1); - - expect(stateVariables["/g3"].stateValues.xmax).eq(7); - expect(stateVariables["/g3"].stateValues.xmin).eq(-5); - expect(stateVariables["/g3"].stateValues.ymax).eq(8); - expect(stateVariables["/g3/A"].stateValues.xs).eqls([1, 2]); - expect(stateVariables["/g3/A"].stateValues.styleNumber).eqls(2); - expect(stateVariables["/g3/B"].stateValues.xs).eqls([3, 4]); - expect(stateVariables["/g3/B"].stateValues.styleNumber).eqls(4); - }); - }); - - it("create snapshot of group with conditionalContent", () => { - cy.window().then(async (win) => { - win.postMessage( - { - doenetML: ` - a - - - - - - - - false - - - - - - - - $gr{link="false"} - - - `, - }, - "*", - ); - }); - - cy.get(cesc("#\\/_text1")).should("have.text", "a"); // to wait for page to load - - // move points - cy.window().then(async (win) => { - await win.callAction1({ - actionName: "movePoint", - componentName: "/P", - args: { x: -1, y: -7 }, - }); - await win.callAction1({ - actionName: "movePoint", - componentName: "/Q", - args: { x: 5, y: 3 }, - }); - }); - - cy.get(cesc("#\\/takeSnapshot_button")).click(); - cy.get(cesc2("#/copy")).should("have.text", "true"); - - cy.window().then(async (win) => { - let stateVariables = await win.returnAllStateVariables1(); - expect(stateVariables["/P"].stateValues.xs).eqls([-1, -7]); - expect(stateVariables["/Q"].stateValues.xs).eqls([5, 3]); - - let graph2Children = stateVariables["/graph2"].activeChildren; - let P2 = graph2Children[0].componentName; - let Q2 = graph2Children[1].componentName; - expect(stateVariables[P2].stateValues.xs).eqls([-1, -7]); - expect(stateVariables[Q2].stateValues.xs).eqls([5, 3]); - }); - - // move points again - cy.window().then(async (win) => { - await win.callAction1({ - actionName: "movePoint", - componentName: "/P", - args: { x: 2, y: -9 }, - }); - await win.callAction1({ - actionName: "movePoint", - componentName: "/Q", - args: { x: 1, y: 8 }, - }); - }); - - cy.window().then(async (win) => { - let stateVariables = await win.returnAllStateVariables1(); - expect(stateVariables["/P"].stateValues.xs).eqls([2, -9]); - expect(stateVariables["/Q"].stateValues.xs).eqls([1, 8]); - - let graph2Children = stateVariables["/graph2"].activeChildren; - let P2 = graph2Children[0].componentName; - let Q2 = graph2Children[1].componentName; - expect(stateVariables[P2].stateValues.xs).eqls([-1, -7]); - expect(stateVariables[Q2].stateValues.xs).eqls([5, 3]); - }); - }); - - it("create snapshot of group with callAction", () => { - cy.window().then(async (win) => { - win.postMessage( - { - doenetML: ` - a - - - - - - - - - - $gr{link="false"} - - - - - - `, - }, - "*", - ); - }); - - cy.get(cesc("#\\/_text1")).should("have.text", "a"); // to wait for page to load - - // move points - cy.window().then(async (win) => { - await win.callAction1({ - actionName: "movePoint", - componentName: "/P", - args: { x: -1, y: -7 }, - }); - await win.callAction1({ - actionName: "movePoint", - componentName: "/Q", - args: { x: 5, y: 3 }, - }); - }); - - cy.get(cesc("#\\/takeSnapshot_button")).click(); - - cy.get(cesc2("#/takeSnapshot_button")).should("be.disabled"); - - cy.window().then(async (win) => { - let stateVariables = await win.returnAllStateVariables1(); - expect(stateVariables["/P"].stateValues.xs).eqls([-1, -7]); - expect(stateVariables["/Q"].stateValues.xs).eqls([5, 3]); - - let graph2Children = stateVariables["/graph2"].activeChildren; - let P2 = graph2Children[0].componentName; - let Q2 = graph2Children[1].componentName; - expect(stateVariables[P2].stateValues.xs).eqls([-1, -7]); - expect(stateVariables[Q2].stateValues.xs).eqls([5, 3]); - }); - - // move points again - cy.window().then(async (win) => { - await win.callAction1({ - actionName: "movePoint", - componentName: "/P", - args: { x: 2, y: -9 }, - }); - await win.callAction1({ - actionName: "movePoint", - componentName: "/Q", - args: { x: 1, y: 8 }, - }); - }); - - cy.window().then(async (win) => { - let stateVariables = await win.returnAllStateVariables1(); - expect(stateVariables["/P"].stateValues.xs).eqls([2, -9]); - expect(stateVariables["/Q"].stateValues.xs).eqls([1, 8]); - - let graph2Children = stateVariables["/graph2"].activeChildren; - let P2 = graph2Children[0].componentName; - let Q2 = graph2Children[1].componentName; - expect(stateVariables[P2].stateValues.xs).eqls([-1, -7]); - expect(stateVariables[Q2].stateValues.xs).eqls([5, 3]); - }); - }); - - it("create snapshot of map with conditionalContent", () => { - cy.window().then(async (win) => { - win.postMessage( - { - doenetML: ` - a - - - - - - - - - - false - - - - - - - - $mp{link="false"} - - - `, - }, - "*", - ); - }); - - cy.get(cesc("#\\/_text1")).should("have.text", "a"); // to wait for page to load - - // move points - cy.window().then(async (win) => { - await win.callAction1({ - actionName: "movePoint", - componentName: "/P", - args: { x: -1, y: -7 }, - }); - await win.callAction1({ - actionName: "movePoint", - componentName: "/Q", - args: { x: 5, y: 3 }, - }); - }); - - cy.get(cesc("#\\/takeSnapshot_button")).click(); - cy.get(cesc2("#/copy")).should("have.text", "true"); - - cy.window().then(async (win) => { - let stateVariables = await win.returnAllStateVariables1(); - expect(stateVariables["/P"].stateValues.xs).eqls([-1, -7]); - expect(stateVariables["/Q"].stateValues.xs).eqls([5, 3]); - - let graph2Children = stateVariables["/graph2"].activeChildren; - let P2 = graph2Children[0].componentName; - let Q2 = graph2Children[1].componentName; - expect(stateVariables[P2].stateValues.xs).eqls([-1, -7]); - expect(stateVariables[Q2].stateValues.xs).eqls([5, 3]); - }); - - // move points again - cy.window().then(async (win) => { - await win.callAction1({ - actionName: "movePoint", - componentName: "/P", - args: { x: 2, y: -9 }, - }); - await win.callAction1({ - actionName: "movePoint", - componentName: "/Q", - args: { x: 1, y: 8 }, - }); - }); - - cy.window().then(async (win) => { - let stateVariables = await win.returnAllStateVariables1(); - expect(stateVariables["/P"].stateValues.xs).eqls([2, -9]); - expect(stateVariables["/Q"].stateValues.xs).eqls([1, 8]); - - let graph2Children = stateVariables["/graph2"].activeChildren; - let P2 = graph2Children[0].componentName; - let Q2 = graph2Children[1].componentName; - expect(stateVariables[P2].stateValues.xs).eqls([-1, -7]); - expect(stateVariables[Q2].stateValues.xs).eqls([5, 3]); - }); - }); - - it("create snapshot of map with callAction", () => { - cy.window().then(async (win) => { - win.postMessage( - { - doenetML: ` - a - - - - - - - - - - - - $mp{link="false"} - - - - - - `, - }, - "*", - ); - }); - - cy.get(cesc("#\\/_text1")).should("have.text", "a"); // to wait for page to load - - // move points - cy.window().then(async (win) => { - await win.callAction1({ - actionName: "movePoint", - componentName: "/P", - args: { x: -1, y: -7 }, - }); - await win.callAction1({ - actionName: "movePoint", - componentName: "/Q", - args: { x: 5, y: 3 }, - }); - }); - - cy.get(cesc("#\\/takeSnapshot_button")).click(); - - cy.get(cesc2("#/takeSnapshot_button")).should("be.disabled"); - - cy.window().then(async (win) => { - let stateVariables = await win.returnAllStateVariables1(); - expect(stateVariables["/P"].stateValues.xs).eqls([-1, -7]); - expect(stateVariables["/Q"].stateValues.xs).eqls([5, 3]); - - let graph2Children = stateVariables["/graph2"].activeChildren; - let P2 = graph2Children[0].componentName; - let Q2 = graph2Children[1].componentName; - expect(stateVariables[P2].stateValues.xs).eqls([-1, -7]); - expect(stateVariables[Q2].stateValues.xs).eqls([5, 3]); - }); - - // move points again - cy.window().then(async (win) => { - await win.callAction1({ - actionName: "movePoint", - componentName: "/P", - args: { x: 2, y: -9 }, - }); - await win.callAction1({ - actionName: "movePoint", - componentName: "/Q", - args: { x: 1, y: 8 }, - }); - }); - - cy.window().then(async (win) => { - let stateVariables = await win.returnAllStateVariables1(); - expect(stateVariables["/P"].stateValues.xs).eqls([2, -9]); - expect(stateVariables["/Q"].stateValues.xs).eqls([1, 8]); - - let graph2Children = stateVariables["/graph2"].activeChildren; - let P2 = graph2Children[0].componentName; - let Q2 = graph2Children[1].componentName; - expect(stateVariables[P2].stateValues.xs).eqls([-1, -7]); - expect(stateVariables[Q2].stateValues.xs).eqls([5, 3]); - }); - }); - - it("create snapshot of map in a group with conditionalContent", () => { - cy.window().then(async (win) => { - win.postMessage( - { - doenetML: ` - a - - - - - - - - - - - - false - - - - - - - - $gr{link="false"} - - - `, - }, - "*", - ); - }); - - cy.get(cesc("#\\/_text1")).should("have.text", "a"); // to wait for page to load - - // move points - cy.window().then(async (win) => { - await win.callAction1({ - actionName: "movePoint", - componentName: "/P", - args: { x: -1, y: -7 }, - }); - await win.callAction1({ - actionName: "movePoint", - componentName: "/Q", - args: { x: 5, y: 3 }, - }); - }); - - cy.get(cesc("#\\/takeSnapshot_button")).click(); - cy.get(cesc2("#/copy")).should("have.text", "true"); - - cy.window().then(async (win) => { - let stateVariables = await win.returnAllStateVariables1(); - expect(stateVariables["/P"].stateValues.xs).eqls([-1, -7]); - expect(stateVariables["/Q"].stateValues.xs).eqls([5, 3]); - - let graph2Children = stateVariables["/graph2"].activeChildren; - let P2 = graph2Children[0].componentName; - let Q2 = graph2Children[1].componentName; - expect(stateVariables[P2].stateValues.xs).eqls([-1, -7]); - expect(stateVariables[Q2].stateValues.xs).eqls([5, 3]); - }); - - // move points again - cy.window().then(async (win) => { - await win.callAction1({ - actionName: "movePoint", - componentName: "/P", - args: { x: 2, y: -9 }, - }); - await win.callAction1({ - actionName: "movePoint", - componentName: "/Q", - args: { x: 1, y: 8 }, - }); - }); - - cy.window().then(async (win) => { - let stateVariables = await win.returnAllStateVariables1(); - expect(stateVariables["/P"].stateValues.xs).eqls([2, -9]); - expect(stateVariables["/Q"].stateValues.xs).eqls([1, 8]); - - let graph2Children = stateVariables["/graph2"].activeChildren; - let P2 = graph2Children[0].componentName; - let Q2 = graph2Children[1].componentName; - expect(stateVariables[P2].stateValues.xs).eqls([-1, -7]); - expect(stateVariables[Q2].stateValues.xs).eqls([5, 3]); - }); - }); - - it("create snapshot of map in a group with callAction", () => { - cy.window().then(async (win) => { - win.postMessage( - { - doenetML: ` - a - - - - - - - - - - - - - - $gr{link="false"} - - - - - - `, - }, - "*", - ); - }); - - cy.get(cesc("#\\/_text1")).should("have.text", "a"); // to wait for page to load - - // move points - cy.window().then(async (win) => { - await win.callAction1({ - actionName: "movePoint", - componentName: "/P", - args: { x: -1, y: -7 }, - }); - await win.callAction1({ - actionName: "movePoint", - componentName: "/Q", - args: { x: 5, y: 3 }, - }); - }); - - cy.get(cesc("#\\/takeSnapshot_button")).click(); - - cy.get(cesc2("#/takeSnapshot_button")).should("be.disabled"); - - cy.window().then(async (win) => { - let stateVariables = await win.returnAllStateVariables1(); - expect(stateVariables["/P"].stateValues.xs).eqls([-1, -7]); - expect(stateVariables["/Q"].stateValues.xs).eqls([5, 3]); - - let graph2Children = stateVariables["/graph2"].activeChildren; - let P2 = graph2Children[0].componentName; - let Q2 = graph2Children[1].componentName; - expect(stateVariables[P2].stateValues.xs).eqls([-1, -7]); - expect(stateVariables[Q2].stateValues.xs).eqls([5, 3]); - }); - - // move points again - cy.window().then(async (win) => { - await win.callAction1({ - actionName: "movePoint", - componentName: "/P", - args: { x: 2, y: -9 }, - }); - await win.callAction1({ - actionName: "movePoint", - componentName: "/Q", - args: { x: 1, y: 8 }, - }); - }); - - cy.window().then(async (win) => { - let stateVariables = await win.returnAllStateVariables1(); - expect(stateVariables["/P"].stateValues.xs).eqls([2, -9]); - expect(stateVariables["/Q"].stateValues.xs).eqls([1, 8]); - - let graph2Children = stateVariables["/graph2"].activeChildren; - let P2 = graph2Children[0].componentName; - let Q2 = graph2Children[1].componentName; - expect(stateVariables[P2].stateValues.xs).eqls([-1, -7]); - expect(stateVariables[Q2].stateValues.xs).eqls([5, 3]); - }); - }); - - it("create snapshot of map with conditionalContent", () => { - cy.window().then(async (win) => { - win.postMessage( - { - doenetML: ` - a - - - - - - - - - - false - - - - - - - - $mp{link="false"} - - - `, - }, - "*", - ); - }); - - cy.get(cesc("#\\/_text1")).should("have.text", "a"); // to wait for page to load - - cy.log("move points and line segments"); - cy.window().then(async (win) => { - await win.callAction1({ - actionName: "movePoint", - componentName: "/P", - args: { x: -1, y: -7 }, - }); - await win.callAction1({ - actionName: "movePoint", - componentName: "/Q", - args: { x: 5, y: 3 }, - }); - - await win.callAction1({ - actionName: "moveLineSegment", - componentName: "/l", - args: { - point1coords: [3, 2], - point2coords: [-9, 4], - }, - }); - await win.callAction1({ - actionName: "moveLineSegment", - componentName: "/m", - args: { - point1coords: [-7, -5], - point2coords: [3, -8], - }, - }); - }); - - cy.get(cesc("#\\/takeSnapshot_button")).click(); - cy.get(cesc2("#/copy")).should("have.text", "true"); - - cy.window().then(async (win) => { - let stateVariables = await win.returnAllStateVariables1(); - expect(stateVariables["/P"].stateValues.xs).eqls([-1, -7]); - expect(stateVariables["/Q"].stateValues.xs).eqls([5, 3]); - expect(stateVariables["/l"].stateValues.endpoints).eqls([ - [3, 2], - [-9, 4], - ]); - expect(stateVariables["/m"].stateValues.endpoints).eqls([ - [-7, -5], - [3, -8], - ]); - - let graph2Children = stateVariables["/graph2"].activeChildren; - let P2 = graph2Children[0].componentName; - let l2 = graph2Children[1].componentName; - let Q2 = graph2Children[2].componentName; - let m2 = graph2Children[3].componentName; - expect(stateVariables[P2].stateValues.xs).eqls([-1, -7]); - expect(stateVariables[Q2].stateValues.xs).eqls([5, 3]); - expect(stateVariables[l2].stateValues.endpoints).eqls([ - [3, 2], - [-9, 4], - ]); - expect(stateVariables[m2].stateValues.endpoints).eqls([ - [-7, -5], - [3, -8], - ]); - }); - - cy.log("move points and segments again"); - cy.window().then(async (win) => { - await win.callAction1({ - actionName: "movePoint", - componentName: "/P", - args: { x: 2, y: -9 }, - }); - await win.callAction1({ - actionName: "movePoint", - componentName: "/Q", - args: { x: 1, y: 8 }, - }); - - await win.callAction1({ - actionName: "moveLineSegment", - componentName: "/l", - args: { - point1coords: [-4, 5], - point2coords: [7, 6], - }, - }); - await win.callAction1({ - actionName: "moveLineSegment", - componentName: "/m", - args: { - point1coords: [1, -2], - point2coords: [-4, 3], - }, - }); - }); - - cy.window().then(async (win) => { - let stateVariables = await win.returnAllStateVariables1(); - expect(stateVariables["/P"].stateValues.xs).eqls([2, -9]); - expect(stateVariables["/Q"].stateValues.xs).eqls([1, 8]); - expect(stateVariables["/l"].stateValues.endpoints).eqls([ - [-4, 5], - [7, 6], - ]); - expect(stateVariables["/m"].stateValues.endpoints).eqls([ - [1, -2], - [-4, 3], - ]); - - let graph2Children = stateVariables["/graph2"].activeChildren; - let P2 = graph2Children[0].componentName; - let l2 = graph2Children[1].componentName; - let Q2 = graph2Children[2].componentName; - let m2 = graph2Children[3].componentName; - expect(stateVariables[P2].stateValues.xs).eqls([-1, -7]); - expect(stateVariables[Q2].stateValues.xs).eqls([5, 3]); - expect(stateVariables[l2].stateValues.endpoints).eqls([ - [3, 2], - [-9, 4], - ]); - expect(stateVariables[m2].stateValues.endpoints).eqls([ - [-7, -5], - [3, -8], - ]); - }); - }); -});