From 6f659f605a2da08f33ed36e28dca66fe483b47c9 Mon Sep 17 00:00:00 2001 From: Duane Nykamp Date: Sat, 28 Sep 2024 16:03:03 -0500 Subject: [PATCH] update format for page state saved in indexed db (#238) * add a data_format_version entry to track versioning * cypress test for reloading state of subsetOfRealsInput --- packages/doenetml-worker/src/Core.js | 43 ++++++------- packages/doenetml/src/Viewer/PageViewer.jsx | 62 +++++++++---------- .../e2e/tagSpecific/subsetofrealsinput.cy.js | 55 ++++++++++++++++ packages/utils/src/index.ts | 2 + 4 files changed, 108 insertions(+), 54 deletions(-) create mode 100644 packages/test-cypress/cypress/e2e/tagSpecific/subsetofrealsinput.cy.js diff --git a/packages/doenetml-worker/src/Core.js b/packages/doenetml-worker/src/Core.js index 96d685a2b..d4eb0f1fb 100644 --- a/packages/doenetml-worker/src/Core.js +++ b/packages/doenetml-worker/src/Core.js @@ -18,6 +18,7 @@ import { extractComponentNamesAndIndices, extractRangeIndexPieces, convertToErrorComponent, + data_format_version, } from "@doenet/utils"; import { gatherVariantComponents, getNumVariants } from "./utils/variants"; import { deprecatedPropertySubstitutions } from "./utils/expandDoenetML"; @@ -12820,13 +12821,23 @@ export default class Core { let saveId = nanoid(); + let coreStateString = JSON.stringify( + this.cumulativeStateVariableChanges, + serializedComponentsReplacer, + ); + let rendererStateString = JSON.stringify( + this.rendererState, + serializedComponentsReplacer, + ); + if (this.flags.allowLocalState) { await idb_set( `${this.activityId}|${this.pageNumber}|${this.attemptNumber}|${this.cid}`, { - coreState: this.cumulativeStateVariableChanges, - rendererState: this.rendererState, - coreInfo: this.coreInfo, + data_format_version, + coreState: coreStateString, + rendererState: rendererStateString, + coreInfo: this.coreInfoString, saveId, }, ); @@ -12844,14 +12855,8 @@ export default class Core { this.pageStateToBeSavedToDatabase = { cid: this.cid, coreInfo: this.coreInfoString, - coreState: JSON.stringify( - this.cumulativeStateVariableChanges, - serializedComponentsReplacer, - ), - rendererState: JSON.stringify( - this.rendererState, - serializedComponentsReplacer, - ), + coreState: coreStateString, + rendererState: rendererStateString, pageNumber: this.pageNumber, attemptNumber: this.attemptNumber, activityId: this.activityId, @@ -13003,18 +13008,10 @@ export default class Core { await idb_set( `${this.activityId}|${this.pageNumber}|${data.attemptNumber}|${data.cid}`, { - coreState: JSON.parse( - data.coreState, - serializedComponentsReviver, - ), - rendererState: JSON.parse( - data.rendererState, - serializedComponentsReviver, - ), - coreInfo: JSON.parse( - data.coreInfo, - serializedComponentsReviver, - ), + data_format_version, + coreState: data.coreState, + rendererState: data.rendererState, + coreInfo: data.coreInfo, saveId: data.saveId, }, ); diff --git a/packages/doenetml/src/Viewer/PageViewer.jsx b/packages/doenetml/src/Viewer/PageViewer.jsx index 3b22822de..1b748c942 100644 --- a/packages/doenetml/src/Viewer/PageViewer.jsx +++ b/packages/doenetml/src/Viewer/PageViewer.jsx @@ -10,6 +10,7 @@ import { serializedComponentsReplacer, serializedComponentsReviver, cesc, + data_format_version, } from "@doenet/utils"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faExclamationCircle } from "@fortawesome/free-solid-svg-icons"; @@ -880,6 +881,10 @@ export function PageViewer({ localInfo = await idb_get( `${activityId}|${pageNumber}|${attemptNumber}|${cid}`, ); + if (localInfo.data_format_version !== data_format_version) { + // the data saved does not match the current version, so we ignore it + localInfo = undefined; + } } catch (e) { // ignore error } @@ -920,28 +925,40 @@ export function PageViewer({ } } - if (localInfo.rendererState.__componentNeedingUpdateValue) { + const coreState = JSON.parse( + localInfo.coreState, + serializedComponentsReviver, + ); + const rendererState = JSON.parse( + localInfo.rendererState, + serializedComponentsReviver, + ); + const coreInfo = JSON.parse( + localInfo.coreInfo, + serializedComponentsReviver, + ); + + if (rendererState.__componentNeedingUpdateValue) { callAction({ action: { actionName: "updateValue", componentName: - localInfo.rendererState - .__componentNeedingUpdateValue, + rendererState.__componentNeedingUpdateValue, }, args: { doNotIgnore: true }, }); } initializeRenderers({ - rendererState: localInfo.rendererState, - coreInfo: localInfo.coreInfo, + rendererState, + coreInfo, }); initialCoreData.current = { - coreState: localInfo.coreState, + coreState, serverSaveId: localInfo.saveId, requestedVariant: JSON.parse( - localInfo.coreInfo.generatedVariantString, + coreInfo.generatedVariantString, serializedComponentsReviver, ), }; @@ -1117,18 +1134,9 @@ export function PageViewer({ let pageStateToBeSavedToDatabase = { cid, - coreInfo: JSON.stringify( - localInfo.coreInfo, - serializedComponentsReplacer, - ), - coreState: JSON.stringify( - localInfo.coreState, - serializedComponentsReplacer, - ), - rendererState: JSON.stringify( - localInfo.rendererState, - serializedComponentsReplacer, - ), + coreInfo: localInfo.coreInfo, + coreState: localInfo.coreState, + rendererState: localInfo.rendererState, pageNumber, attemptNumber, activityId, @@ -1163,18 +1171,10 @@ export function PageViewer({ if (data.stateOverwritten) { let newLocalInfo = { - coreState: JSON.parse( - data.coreState, - serializedComponentsReviver, - ), - rendererState: JSON.parse( - data.rendererState, - serializedComponentsReviver, - ), - coreInfo: JSON.parse( - data.coreInfo, - serializedComponentsReviver, - ), + data_format_version, + coreState: data.coreState, + rendererState: data.rendererState, + coreInfo: data.coreInfo, saveId: data.saveId, }; diff --git a/packages/test-cypress/cypress/e2e/tagSpecific/subsetofrealsinput.cy.js b/packages/test-cypress/cypress/e2e/tagSpecific/subsetofrealsinput.cy.js new file mode 100644 index 000000000..c5b4d8ea7 --- /dev/null +++ b/packages/test-cypress/cypress/e2e/tagSpecific/subsetofrealsinput.cy.js @@ -0,0 +1,55 @@ +import { cesc2 } from "@doenet/utils"; + +describe("SubsetOfRealsInput Tag Tests", function () { + beforeEach(() => { + cy.clearIndexedDB(); + cy.visit("/"); + }); + + it("subsetOfRealsInput state can be reloaded from local state", () => { + const doenetML = ` + a +

+

Value:

+

Value:

+ `; + cy.get("#testRunner_toggleControls").click(); + cy.get("#testRunner_allowLocalState").click(); + cy.wait(100); + cy.get("#testRunner_toggleControls").click(); + + cy.window().then(async (win) => { + win.postMessage( + { + doenetML, + }, + "*", + ); + }); + cy.get(cesc2("#/_text1")).should("have.text", "a"); // to wait for page to load + + cy.get(cesc2("#/sor")).should("contain.text", "∅"); + + cy.get(cesc2("#/sormi") + " textarea").type( + "{end}{backspace}{{}3}{enter}", + { + force: true, + }, + ); + + cy.get(cesc2("#/sor")).should("contain.text", "{3}"); + cy.wait(2000); // wait for 1 second debounce + + cy.reload(); + cy.window().then(async (win) => { + win.postMessage( + { + doenetML, + }, + "*", + ); + }); + + cy.get(cesc2("#/sor")).should("contain.text", "{3}"); + }); +}); diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts index 7e7fd4e09..a7bae5550 100644 --- a/packages/utils/src/index.ts +++ b/packages/utils/src/index.ts @@ -19,3 +19,5 @@ export * from "./math/subset-of-reals"; export * from "./style/style"; export * from "./url/url"; export * from "./version/doenetMLversion"; + +export const data_format_version = "0.7.0";