diff --git a/packages/core/__tests__/__fixtures__/backup.v5.2.copy.json b/packages/core/__tests__/__fixtures__/backup.v5.2.copy.json new file mode 100644 index 000000000..60631c5db --- /dev/null +++ b/packages/core/__tests__/__fixtures__/backup.v5.2.copy.json @@ -0,0 +1 @@ +{"version":5.2,"type":"web","date":1640684511432,"data":{"069b671dd48576dff9157120":{"id":"069b671dd48576dff9157120","contentId":"8454da84dfa427c2f75f70da","type":"note","title":"Table test note","pinned":false,"locked":false,"tags":[],"favorite":false,"dateCreated":1640684417435,"dateEdited":1640684432529,"localOnly":false,"conflicted":false},"8454da84dfa427c2f75f70da":{"noteId":"069b671dd48576dff9157120","id":"8454da84dfa427c2f75f70da","type":"tiny","data":"
Hello
World
My
Name
","dateEdited":1640684432525,"dateCreated":1640684426583,"localOnly":false,"persistDateEdited":false},"_uk_@ammarahmed6506+5@gmail.com":{},"_uk_@ammarahmed6506+5@gmail.com@_k":{"iv":{"0":17,"1":27,"2":18,"3":72,"4":98,"5":156,"6":43,"7":0,"8":63,"9":234,"10":35,"11":22},"cipher":{}},"c5f6fa4fef9be04c0f07f80e":{"noteId":"d3a7a9fbd4bb29ca41570c5e","id":"c5f6fa4fef9be04c0f07f80e","type":"tiny","data":"

test completed.

","dateEdited":1640684464785,"dateCreated":1640684448878,"localOnly":false,"persistDateEdited":false},"content":["8454da84dfa427c2f75f70da","c5f6fa4fef9be04c0f07f80e"],"d3a7a9fbd4bb29ca41570c5e":{"id":"d3a7a9fbd4bb29ca41570c5e","contentId":"c5f6fa4fef9be04c0f07f80e","type":"note","title":"Checklist test","headline":"test completed.","pinned":false,"locked":false,"tags":[],"favorite":false,"dateCreated":1640684443699,"dateEdited":1640684464786,"localOnly":false,"conflicted":false},"hasConflicts":false,"monographs":[],"notes":["069b671dd48576dff9157120","d3a7a9fbd4bb29ca41570c5e"],"settings":{"type":"settings","id":"87a858b33cd6427364d176d4","pins":[],"groupOptions":{},"aliases":{},"dateEdited":0,"dateCreated":0},"token":{"access_token":"9E5F461613022DB4AC77BE5EA46D8AD67069E7361B8F3FC9F9E9C74409E4CA46","expires_in":3600,"token_type":"Bearer","refresh_token":"4489400BF30C61DCA8B84960620F5B01B165A8FE5C537AE4709E5492758F49ED","scope":"IdentityServerApi notesnook.sync offline_access openid","t":1640684404249}},"hash":"7b6bc37e3338f10535486f03ca71e42d","hash_type":"md5"} \ No newline at end of file diff --git a/packages/core/__tests__/backup.test.js b/packages/core/__tests__/backup.test.js index 0fd1770a2..bbbcdf5b0 100644 --- a/packages/core/__tests__/backup.test.js +++ b/packages/core/__tests__/backup.test.js @@ -5,6 +5,7 @@ import { notebookTest, } from "./utils"; import v52Backup from "./__fixtures__/backup.v5.2.json"; +import v52BackupCopy from "./__fixtures__/backup.v5.2.copy.json"; beforeEach(() => { StorageInterface.clear(); @@ -64,65 +65,81 @@ test("import tempered backup", () => }) )); -describe.each([["v5.2", v52Backup]])( - "testing backup version: %s", - (version, data) => { - test(`import ${version} backup`, () => { - return databaseTest().then(async (db) => { - await db.backup.import(JSON.stringify(data)); +describe.each([ + ["v5.2", v52Backup], + ["v5.2 copy", v52BackupCopy], +])("testing backup version: %s", (version, data) => { + test(`import ${version} backup`, () => { + return databaseTest().then(async (db) => { + await db.backup.import(JSON.stringify(data)); - expect(db.settings.raw.id).toBeDefined(); - expect(db.settings.raw.dateModified).toBeDefined(); - expect(db.settings.raw.dateEdited).toBeUndefined(); + expect(db.settings.raw.id).toBeDefined(); + expect(db.settings.raw.dateModified).toBeDefined(); + expect(db.settings.raw.dateEdited).toBeUndefined(); + expect( + db.notes.all.every((v) => { + const doesNotHaveContent = v.contentId && !v.content; + const doesNotHaveColors = !v.colors && (!v.color || v.color.length); + const hasTopicsInAllNotebooks = + !v.notebooks || + v.notebooks.every((nb) => !!nb.id && !!nb.topics && !nb.topic); + const hasDateModified = v.dateModified > 0; + return ( + doesNotHaveContent && + !v.notebook && + hasTopicsInAllNotebooks && + doesNotHaveColors && + hasDateModified + ); + }) + ).toBeTruthy(); + + expect( + db.notebooks.all.every((v) => v.title != null && v.dateModified > 0) + ).toBeTruthy(); + + expect( + db.attachments.all.every((v) => v.dateModified > 0 && !v.dateEdited) + ).toBeTruthy(); + + const allContent = await db.content.all(); + expect( + allContent.every((v) => v.type === "tiny" || v.deleted) + ).toBeTruthy(); + expect(allContent.every((v) => !v.persistDateEdited)).toBeTruthy(); + expect(allContent.every((v) => v.dateModified > 0)).toBeTruthy(); + + expect( + allContent.every( + (v) => + !v.data.includes("tox-checklist") && + !v.data.includes("tox-checklist--checked") + ) + ).toBeTruthy(); + + const tableContent = allContent.find((a) => a.data.includes(" { - const doesNotHaveContent = v.contentId && !v.content; - const doesNotHaveColors = !v.colors && (!v.color || v.color.length); - const hasTopicsInAllNotebooks = - !v.notebooks || - v.notebooks.every((nb) => !!nb.id && !!nb.topics && !nb.topic); - const hasDateModified = v.dateModified > 0; - return ( - doesNotHaveContent && - !v.notebook && - hasTopicsInAllNotebooks && - doesNotHaveColors && - hasDateModified - ); - }) - ).toBeTruthy(); - - expect( - db.notebooks.all.every((v) => v.title != null && v.dateModified > 0) - ).toBeTruthy(); - - expect( - db.attachments.all.every((v) => v.dateModified > 0 && !v.dateEdited) - ).toBeTruthy(); - - const allContent = await db.content.all(); - expect( - allContent.every((v) => v.type === "tiny" || v.deleted) - ).toBeTruthy(); - expect(allContent.every((v) => !v.persistDateEdited)).toBeTruthy(); - expect(allContent.every((v) => v.dateModified > 0)).toBeTruthy(); - }); + tableContent.data.includes( + `
` + ) + ); }); + }); - test(`verify indices of ${version} backup`, () => { - return databaseTest().then(async (db) => { - await db.backup.import(JSON.stringify(data)); + test(`verify indices of ${version} backup`, () => { + return databaseTest().then(async (db) => { + await db.backup.import(JSON.stringify(data)); - verifyIndex(data, db, "notes", "notes"); - verifyIndex(data, db, "notebooks", "notebooks"); - verifyIndex(data, db, "content", "content"); - verifyIndex(data, db, "attachments", "attachments"); - // verifyIndex(data, db, "trash", "trash"); - }); + verifyIndex(data, db, "notes", "notes"); + verifyIndex(data, db, "notebooks", "notebooks"); + verifyIndex(data, db, "content", "content"); + verifyIndex(data, db, "attachments", "attachments"); + // verifyIndex(data, db, "trash", "trash"); }); - } -); + }); +}); function verifyIndex(backup, db, backupCollection, collection) { if (!backup.data[backupCollection]) return; diff --git a/packages/core/migrations.js b/packages/core/migrations.js index 053102056..18b0ee324 100644 --- a/packages/core/migrations.js +++ b/packages/core/migrations.js @@ -1,3 +1,5 @@ +import { parseHTML } from "./utils/html-parser"; + export const migrations = { 5.0: {}, 5.1: {}, @@ -7,7 +9,14 @@ export const migrations = { tag: replaceDateEditedWithDateModified(true), attachment: replaceDateEditedWithDateModified(true), trash: replaceDateEditedWithDateModified(), - tiny: replaceDateEditedWithDateModified(), + tiny: (item) => { + item = replaceDateEditedWithDateModified()(item); + + if (!item.data || item.data.iv) return item; + + item.data = removeToxClassFromChecklist(wrapTablesWithDiv(item.data)); + return item; + }, settings: replaceDateEditedWithDateModified(true), }, 5.3: { @@ -29,3 +38,30 @@ function replaceDateEditedWithDateModified(removeDateEditedProperty = false) { return item; }; } + +function wrapTablesWithDiv(html) { + const document = parseHTML(html); + const tables = document.getElementsByTagName("table"); + for (let table of tables) { + table.setAttribute("contenteditable", "true"); + table.replaceWith( + `
${table.outerHTML}
` + ); + } + return document.outerHTML || document.body.innerHTML; +} + +function removeToxClassFromChecklist(html) { + const document = parseHTML(html); + const checklists = document.querySelectorAll( + ".tox-checklist,.tox-checklist--checked" + ); + + for (let item of checklists) { + if (item.classList.contains("tox-checklist--checked")) + item.classList.replace("tox-checklist--checked", "checked"); + else if (item.classList.contains("tox-checklist")) + item.classList.replace("tox-checklist", "checklist"); + } + return document.outerHTML || document.body.innerHTML; +}