Handle SNJS rename of cryptoManager to protocolManager, extract test suite into SNJS
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import { isDesktopApplication } from '@/utils';
|
||||
import { PrivilegesManager } from '@/services/privilegesManager';
|
||||
import template from '%/directives/account-menu.pug';
|
||||
import { cryptoManager } from 'snjs';
|
||||
import { protocolManager } from 'snjs';
|
||||
|
||||
export class AccountMenu {
|
||||
constructor() {
|
||||
@@ -326,9 +326,9 @@ export class AccountMenu {
|
||||
}
|
||||
|
||||
if(data.auth_params) {
|
||||
cryptoManager.computeEncryptionKeysForUser(password, data.auth_params).then((keys) => {
|
||||
protocolManager.computeEncryptionKeysForUser(password, data.auth_params).then((keys) => {
|
||||
try {
|
||||
cryptoManager.decryptMultipleItems(data.items, keys, false) /* throws = false as we don't want to interrupt all decryption if just one fails */
|
||||
protocolManager.decryptMultipleItems(data.items, keys, false) /* throws = false as we don't want to interrupt all decryption if just one fails */
|
||||
.then(() => {
|
||||
// delete items enc_item_key since the user's actually key will do the encrypting once its passed off
|
||||
data.items.forEach(function(item){
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { cryptoManager } from 'snjs';
|
||||
import { protocolManager } from 'snjs';
|
||||
import template from '%/directives/password-wizard.pug';
|
||||
|
||||
export class PasswordWizard {
|
||||
@@ -213,7 +213,7 @@ export class PasswordWizard {
|
||||
// Ensure value for current password matches what's saved
|
||||
let authParams = await authManager.getAuthParams();
|
||||
let password = $scope.formData.currentPassword;
|
||||
cryptoManager.computeEncryptionKeysForUser(password, authParams).then(async (keys) => {
|
||||
protocolManager.computeEncryptionKeysForUser(password, authParams).then(async (keys) => {
|
||||
let success = keys.mk === (await authManager.keys()).mk;
|
||||
if(success) {
|
||||
this.currentServerPw = keys.pw;
|
||||
@@ -241,7 +241,7 @@ export class PasswordWizard {
|
||||
|
||||
let currentServerPw = this.currentServerPw;
|
||||
|
||||
let results = await cryptoManager.generateInitialKeysAndAuthParamsForUser(authManager.user.email, newUserPassword);
|
||||
let results = await protocolManager.generateInitialKeysAndAuthParamsForUser(authManager.user.email, newUserPassword);
|
||||
let newKeys = results.keys;
|
||||
let newAuthParams = results.authParams;
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { cryptoManager, SNComponent, SFItem, SFModelManager } from 'snjs';
|
||||
import { protocolManager, SNComponent, SFItem, SFModelManager } from 'snjs';
|
||||
import template from '%/directives/revision-preview-modal.pug';
|
||||
|
||||
export class RevisionPreviewModal {
|
||||
@@ -33,7 +33,7 @@ export class RevisionPreviewModal {
|
||||
// but then generate new uuid for note as not to save changes to original, if editor makes changes.
|
||||
$scope.note.uuid = $scope.uuid;
|
||||
let editorForNote = componentManager.editorForNote($scope.note);
|
||||
$scope.note.uuid = cryptoManager.crypto.generateUUIDSync();
|
||||
$scope.note.uuid = protocolManager.crypto.generateUUIDSync();
|
||||
|
||||
if(editorForNote) {
|
||||
// Create temporary copy, as a lot of componentManager is uuid based,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import _ from 'lodash';
|
||||
import angular from 'angular';
|
||||
import { Action, SFModelManager, SFItemParams, cryptoManager } from 'snjs';
|
||||
import { Action, SFModelManager, SFItemParams, protocolManager } from 'snjs';
|
||||
|
||||
export class ActionsManager {
|
||||
|
||||
@@ -85,7 +85,7 @@ export class ActionsManager {
|
||||
let handleResponseDecryption = async (response, keys, merge) => {
|
||||
var item = response.item;
|
||||
|
||||
await cryptoManager.decryptItem(item, keys);
|
||||
await protocolManager.decryptItem(item, keys);
|
||||
|
||||
if(!item.errorDecrypting) {
|
||||
if(merge) {
|
||||
@@ -115,7 +115,7 @@ export class ActionsManager {
|
||||
}
|
||||
triedPasswords.push(passwordCandidate);
|
||||
|
||||
var keyResults = await cryptoManager.computeEncryptionKeysForUser(passwordCandidate, response.auth_params);
|
||||
var keyResults = await protocolManager.computeEncryptionKeysForUser(passwordCandidate, response.auth_params);
|
||||
if(!keyResults) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import angular from 'angular';
|
||||
import { StorageManager } from './storageManager';
|
||||
import { cryptoManager, SFItem, SFPredicate, SFAuthManager } from 'snjs';
|
||||
import { protocolManager, SFItem, SFPredicate, SFAuthManager } from 'snjs';
|
||||
|
||||
export class AuthManager extends SFAuthManager {
|
||||
/* @ngInject */
|
||||
@@ -112,7 +112,7 @@ export class AuthManager extends SFAuthManager {
|
||||
|
||||
async verifyAccountPassword(password) {
|
||||
let authParams = await this.getAuthParams();
|
||||
let keys = await cryptoManager.computeEncryptionKeysForUser(password, authParams);
|
||||
let keys = await protocolManager.computeEncryptionKeysForUser(password, authParams);
|
||||
let success = keys.mk === (await this.keys()).mk;
|
||||
return success;
|
||||
}
|
||||
@@ -122,7 +122,7 @@ export class AuthManager extends SFAuthManager {
|
||||
return false;
|
||||
}
|
||||
|
||||
let latest = cryptoManager.version();
|
||||
let latest = protocolManager.version();
|
||||
let updateAvailable = await this.protocolVersion() !== latest;
|
||||
if(updateAvailable !== this.securityUpdateAvailable) {
|
||||
this.securityUpdateAvailable = updateAvailable;
|
||||
|
||||
@@ -1,32 +1,5 @@
|
||||
import _ from 'lodash';
|
||||
import {
|
||||
SFItem,
|
||||
SFModelManager,
|
||||
SFPrivileges,
|
||||
SFPredicate,
|
||||
SNNote,
|
||||
SNTag,
|
||||
SNSmartTag,
|
||||
SNExtension,
|
||||
SNEditor,
|
||||
SNTheme,
|
||||
SNComponent,
|
||||
SNServerExtension,
|
||||
SNMfa
|
||||
} from 'snjs';
|
||||
|
||||
SFModelManager.ContentTypeClassMapping = {
|
||||
"Note" : SNNote,
|
||||
"Tag" : SNTag,
|
||||
"SN|SmartTag" : SNSmartTag,
|
||||
"Extension" : SNExtension,
|
||||
"SN|Editor" : SNEditor,
|
||||
"SN|Theme" : SNTheme,
|
||||
"SN|Component" : SNComponent,
|
||||
"SF|Extension" : SNServerExtension,
|
||||
"SF|MFA" : SNMfa,
|
||||
"SN|Privileges" : SFPrivileges
|
||||
};
|
||||
import { SFModelManager, SNSmartTag, SFPredicate } from 'snjs';
|
||||
|
||||
export class ModelManager extends SFModelManager {
|
||||
/* @ngInject */
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import _ from 'lodash';
|
||||
import { isDesktopApplication } from '@/utils';
|
||||
import { StorageManager } from './storageManager';
|
||||
import { cryptoManager } from 'snjs';
|
||||
import { protocolManager } from 'snjs';
|
||||
|
||||
const MillisecondsPerSecond = 1000;
|
||||
|
||||
@@ -88,7 +88,7 @@ export class PasscodeManager {
|
||||
async verifyPasscode(passcode) {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
var params = this.passcodeAuthParams();
|
||||
let keys = await cryptoManager.computeEncryptionKeysForUser(passcode, params);
|
||||
let keys = await protocolManager.computeEncryptionKeysForUser(passcode, params);
|
||||
if(keys.pw !== params.hash) {
|
||||
resolve(false);
|
||||
} else {
|
||||
@@ -99,7 +99,7 @@ export class PasscodeManager {
|
||||
|
||||
unlock(passcode, callback) {
|
||||
var params = this.passcodeAuthParams();
|
||||
cryptoManager.computeEncryptionKeysForUser(passcode, params).then((keys) => {
|
||||
protocolManager.computeEncryptionKeysForUser(passcode, params).then((keys) => {
|
||||
if(keys.pw !== params.hash) {
|
||||
callback(false);
|
||||
return;
|
||||
@@ -115,9 +115,9 @@ export class PasscodeManager {
|
||||
}
|
||||
|
||||
setPasscode(passcode, callback) {
|
||||
var uuid = cryptoManager.crypto.generateUUIDSync();
|
||||
var uuid = protocolManager.crypto.generateUUIDSync();
|
||||
|
||||
cryptoManager.generateInitialKeysAndAuthParamsForUser(uuid, passcode).then((results) => {
|
||||
protocolManager.generateInitialKeysAndAuthParamsForUser(uuid, passcode).then((results) => {
|
||||
let keys = results.keys;
|
||||
let authParams = results.authParams;
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { cryptoManager, SNEncryptedStorage, SFStorageManager , SFItemParams } from 'snjs';
|
||||
import { protocolManager, SNEncryptedStorage, SFStorageManager , SFItemParams } from 'snjs';
|
||||
|
||||
export class MemoryStorage {
|
||||
constructor() {
|
||||
@@ -170,7 +170,7 @@ export class StorageManager extends SFStorageManager {
|
||||
|
||||
async decryptStorage() {
|
||||
var stored = JSON.parse(this.getItemSync("encryptedStorage", StorageManager.Fixed));
|
||||
await cryptoManager.decryptItem(stored, this.encryptedStorageKeys);
|
||||
await protocolManager.decryptItem(stored, this.encryptedStorageKeys);
|
||||
var encryptedStorage = new SNEncryptedStorage(stored);
|
||||
|
||||
for(var key of Object.keys(encryptedStorage.content.storage)) {
|
||||
|
||||
30
dist/javascripts/app.js
vendored
30
dist/javascripts/app.js
vendored
File diff suppressed because one or more lines are too long
2
dist/javascripts/app.js.map
vendored
2
dist/javascripts/app.js.map
vendored
File diff suppressed because one or more lines are too long
6
package-lock.json
generated
6
package-lock.json
generated
@@ -8687,9 +8687,9 @@
|
||||
}
|
||||
},
|
||||
"snjs": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/snjs/-/snjs-1.0.2.tgz",
|
||||
"integrity": "sha512-ciTP0YALhV7BMJ4B5m5gHnn3hDwGtpEniKd6/WUobKvgA365rCc0GboiYTCj07LY21I9mVq1seDSZT3bSkM15w==",
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/snjs/-/snjs-1.0.3.tgz",
|
||||
"integrity": "sha512-XKw0PYfZjTB4bQOdF7nxkSHdCgAsHv6CyfXLK927vQzo5PBi5pZWobx+C9l0uajt/VmSk9YdO8+fQhCK3GcFNw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"lodash": "^4.17.15"
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
"scripts": {
|
||||
"start": "webpack --mode development -w",
|
||||
"bundle": "webpack --mode production",
|
||||
"build": "bundle install && npm install && grunt",
|
||||
"build": "bundle install && npm install && npm run bundle",
|
||||
"submodules": "git submodule update --init --force --remote",
|
||||
"test": "karma start karma.conf.js --single-run"
|
||||
},
|
||||
@@ -44,7 +44,7 @@
|
||||
"mocha": "^6.2.2",
|
||||
"serve-static": "^1.14.1",
|
||||
"sn-stylekit": "2.0.20",
|
||||
"snjs": "1.0.2",
|
||||
"snjs": "1.0.3",
|
||||
"webpack": "^4.41.3",
|
||||
"webpack-cli": "^3.3.10"
|
||||
},
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import '../../../dist/javascripts/compiled.js';
|
||||
import '../../../dist/javascripts/app.js';
|
||||
import '../../../node_modules/chai/chai.js';
|
||||
import '../vendor/chai-as-promised-built.js';
|
||||
import '../../../vendor/assets/javascripts/lodash/lodash.custom.js';
|
||||
|
||||
import LocalStorageManager from './localStorageManager.js';
|
||||
const sf_default = new SNCryptoManager();
|
||||
const sf_default = new SNProtocolManager();
|
||||
SFItem.AppDomain = "org.standardnotes.sn";
|
||||
|
||||
var _globalStorageManager = null;
|
||||
@@ -48,7 +47,7 @@ export default class Factory {
|
||||
}
|
||||
|
||||
static globalCryptoManager() {
|
||||
if(_globalCryptoManager == null) { _globalCryptoManager = new SNCryptoManager(); }
|
||||
if(_globalCryptoManager == null) { _globalCryptoManager = new SNProtocolManager(); }
|
||||
return _globalCryptoManager;
|
||||
}
|
||||
|
||||
@@ -62,7 +61,7 @@ export default class Factory {
|
||||
|
||||
static createItemParams() {
|
||||
var params = {
|
||||
uuid: cryptoManager.crypto.generateUUIDSync(),
|
||||
uuid: protocolManager.crypto.generateUUIDSync(),
|
||||
content_type: "Note",
|
||||
content: {
|
||||
title: "hello",
|
||||
|
||||
@@ -1,622 +0,0 @@
|
||||
import '../../dist/javascripts/compiled.js';
|
||||
import '../../node_modules/chai/chai.js';
|
||||
import './vendor/chai-as-promised-built.js';
|
||||
import '../../vendor/assets/javascripts/lodash/lodash.custom.js';
|
||||
import Factory from './lib/factory.js';
|
||||
|
||||
chai.use(chaiAsPromised);
|
||||
var expect = chai.expect;
|
||||
|
||||
const getNoteParams = () => {
|
||||
var params = {
|
||||
uuid: cryptoManager.crypto.generateUUIDSync(),
|
||||
content_type: "Note",
|
||||
content: {
|
||||
title: "hello",
|
||||
text: "world"
|
||||
}
|
||||
};
|
||||
return params;
|
||||
}
|
||||
|
||||
const createRelatedNoteTagPair = () => {
|
||||
let noteParams = getNoteParams();
|
||||
let tagParams = {
|
||||
uuid: cryptoManager.crypto.generateUUIDSync(),
|
||||
content_type: "Tag",
|
||||
content: {
|
||||
title: "thoughts",
|
||||
}
|
||||
};
|
||||
tagParams.content.references = [
|
||||
{
|
||||
uuid: noteParams.uuid,
|
||||
content_type: noteParams.content_type
|
||||
}
|
||||
]
|
||||
|
||||
noteParams.content.references = []
|
||||
|
||||
return [noteParams, tagParams];
|
||||
}
|
||||
describe("notes and tags", () => {
|
||||
|
||||
it('uses proper class for note', () => {
|
||||
let modelManager = Factory.createModelManager();
|
||||
let noteParams = getNoteParams();
|
||||
modelManager.mapResponseItemsToLocalModels([noteParams]);
|
||||
let note = modelManager.allItemsMatchingTypes(["Note"])[0];
|
||||
expect(note).to.be.an.instanceOf(SNNote);
|
||||
});
|
||||
|
||||
it('properly constructs syncing params', () => {
|
||||
let note = new SNNote();
|
||||
let title = "Foo", text = "Bar";
|
||||
note.title = title;
|
||||
note.text = text;
|
||||
|
||||
let content = note.createContentJSONFromProperties();
|
||||
expect(content.title).to.equal(title);
|
||||
expect(content.text).to.equal(text);
|
||||
|
||||
let tag = new SNTag();
|
||||
tag.title = title;
|
||||
|
||||
expect(tag.createContentJSONFromProperties().title).to.equal(title);
|
||||
|
||||
expect(tag.structureParams().title).to.equal(tag.getContentCopy().title);
|
||||
})
|
||||
|
||||
it('properly handles legacy relationships', () => {
|
||||
// legacy relationships are when a note has a reference to a tag
|
||||
let modelManager = Factory.createModelManager();
|
||||
let pair = createRelatedNoteTagPair();
|
||||
let noteParams = pair[0];
|
||||
let tagParams = pair[1];
|
||||
tagParams.content.references = null;
|
||||
noteParams.content.references = [
|
||||
{
|
||||
uuid: tagParams.uuid,
|
||||
content_type: tagParams.content_type
|
||||
}
|
||||
];
|
||||
|
||||
modelManager.mapResponseItemsToLocalModels([noteParams, tagParams]);
|
||||
let note = modelManager.allItemsMatchingTypes(["Note"])[0];
|
||||
let tag = modelManager.allItemsMatchingTypes(["Tag"])[0];
|
||||
|
||||
expect(note.tags.length).to.equal(1);
|
||||
expect(tag.notes.length).to.equal(1);
|
||||
})
|
||||
|
||||
it('creates two-way relationship between note and tag', () => {
|
||||
let modelManager = Factory.createModelManager();
|
||||
|
||||
let pair = createRelatedNoteTagPair();
|
||||
let noteParams = pair[0];
|
||||
let tagParams = pair[1];
|
||||
|
||||
expect(noteParams.content.references.length).to.equal(0);
|
||||
expect(tagParams.content.references.length).to.equal(1);
|
||||
|
||||
modelManager.mapResponseItemsToLocalModels([noteParams, tagParams]);
|
||||
let note = modelManager.allItemsMatchingTypes(["Note"])[0];
|
||||
let tag = modelManager.allItemsMatchingTypes(["Tag"])[0];
|
||||
|
||||
// expect to be false
|
||||
expect(note.dirty).to.not.be.ok;
|
||||
expect(tag.dirty).to.not.be.ok;
|
||||
|
||||
expect(note.content.references.length).to.equal(0);
|
||||
expect(tag.content.references.length).to.equal(1);
|
||||
|
||||
expect(note.hasRelationshipWithItem(tag)).to.equal(false);
|
||||
expect(tag.hasRelationshipWithItem(note)).to.equal(true);
|
||||
|
||||
expect(note.tags.length).to.equal(1);
|
||||
expect(tag.notes.length).to.equal(1);
|
||||
|
||||
modelManager.setItemToBeDeleted(note);
|
||||
expect(note.tags.length).to.equal(0);
|
||||
expect(tag.notes.length).to.equal(0);
|
||||
|
||||
// expect to be true
|
||||
expect(note.dirty).to.be.ok;
|
||||
expect(tag.dirty).to.be.ok;
|
||||
});
|
||||
|
||||
it('handles remote deletion of relationship', () => {
|
||||
let modelManager = Factory.createModelManager();
|
||||
|
||||
let pair = createRelatedNoteTagPair();
|
||||
let noteParams = pair[0];
|
||||
let tagParams = pair[1];
|
||||
|
||||
modelManager.mapResponseItemsToLocalModels([noteParams, tagParams]);
|
||||
let note = modelManager.allItemsMatchingTypes(["Note"])[0];
|
||||
let tag = modelManager.allItemsMatchingTypes(["Tag"])[0];
|
||||
|
||||
expect(note.content.references.length).to.equal(0);
|
||||
expect(tag.content.references.length).to.equal(1);
|
||||
|
||||
tagParams.content.references = [];
|
||||
modelManager.mapResponseItemsToLocalModels([tagParams]);
|
||||
|
||||
expect(tag.content.references.length).to.equal(0);
|
||||
expect(note.tags.length).to.equal(0);
|
||||
expect(tag.notes.length).to.equal(0);
|
||||
|
||||
// expect to be false
|
||||
expect(note.dirty).to.not.be.ok;
|
||||
expect(tag.dirty).to.not.be.ok;
|
||||
});
|
||||
|
||||
it('resets cached note tags string when tag is deleted from remote source', () => {
|
||||
let modelManager = Factory.createModelManager();
|
||||
|
||||
let pair = createRelatedNoteTagPair();
|
||||
let noteParams = pair[0];
|
||||
let tagParams = pair[1];
|
||||
|
||||
modelManager.mapResponseItemsToLocalModels([noteParams, tagParams]);
|
||||
let note = modelManager.allItemsMatchingTypes(["Note"])[0];
|
||||
let tag = modelManager.allItemsMatchingTypes(["Tag"])[0];
|
||||
|
||||
expect(note.tagsString().length).to.not.equal(0);
|
||||
|
||||
tagParams.deleted = true;
|
||||
modelManager.mapResponseItemsToLocalModels([tagParams]);
|
||||
|
||||
// should be null
|
||||
expect(note.savedTagsString).to.not.be.ok;
|
||||
|
||||
expect(note.tags.length).to.equal(0);
|
||||
expect(tag.notes.length).to.equal(0);
|
||||
});
|
||||
|
||||
it('resets cached note tags string when tag reference is removed from remote source', () => {
|
||||
let modelManager = Factory.createModelManager();
|
||||
|
||||
let pair = createRelatedNoteTagPair();
|
||||
let noteParams = pair[0];
|
||||
let tagParams = pair[1];
|
||||
|
||||
modelManager.mapResponseItemsToLocalModels([noteParams, tagParams]);
|
||||
let note = modelManager.allItemsMatchingTypes(["Note"])[0];
|
||||
let tag = modelManager.allItemsMatchingTypes(["Tag"])[0];
|
||||
|
||||
expect(note.tagsString().length).to.not.equal(0);
|
||||
|
||||
tagParams.content.references = [];
|
||||
modelManager.mapResponseItemsToLocalModels([tagParams]);
|
||||
|
||||
// should be null
|
||||
expect(note.savedTagsString).to.not.be.ok;
|
||||
|
||||
expect(note.tags.length).to.equal(0);
|
||||
expect(tag.notes.length).to.equal(0);
|
||||
});
|
||||
|
||||
it('resets cached note tags string when tag is renamed', () => {
|
||||
let modelManager = Factory.createModelManager();
|
||||
|
||||
let pair = createRelatedNoteTagPair();
|
||||
let noteParams = pair[0];
|
||||
let tagParams = pair[1];
|
||||
|
||||
modelManager.mapResponseItemsToLocalModels([noteParams, tagParams]);
|
||||
let note = modelManager.allItemsMatchingTypes(["Note"])[0];
|
||||
let tag = modelManager.allItemsMatchingTypes(["Tag"])[0];
|
||||
|
||||
expect(note.tagsString()).to.equal(`#${tagParams.content.title}`);
|
||||
|
||||
var title = Math.random();
|
||||
|
||||
// Saving involves modifying local state first, then syncing with omitting content.
|
||||
tag.title = title;
|
||||
tagParams.content.title = title;
|
||||
// simulate a save, which omits `content`
|
||||
modelManager.mapResponseItemsToLocalModelsOmittingFields([tagParams], ['content']);
|
||||
|
||||
// should be null
|
||||
expect(note.savedTagsString).to.not.be.ok;
|
||||
expect(note.tagsString()).to.equal(`#${title}`);
|
||||
});
|
||||
|
||||
it('handles removing relationship between note and tag', () => {
|
||||
let modelManager = Factory.createModelManager();
|
||||
|
||||
let pair = createRelatedNoteTagPair();
|
||||
let noteParams = pair[0];
|
||||
let tagParams = pair[1];
|
||||
|
||||
modelManager.mapResponseItemsToLocalModels([noteParams, tagParams]);
|
||||
let note = modelManager.allItemsMatchingTypes(["Note"])[0];
|
||||
let tag = modelManager.allItemsMatchingTypes(["Tag"])[0];
|
||||
|
||||
expect(note.content.references.length).to.equal(0);
|
||||
expect(tag.content.references.length).to.equal(1);
|
||||
|
||||
tag.removeItemAsRelationship(note);
|
||||
modelManager.mapResponseItemsToLocalModels([tag]);
|
||||
|
||||
expect(note.tags.length).to.equal(0);
|
||||
expect(tag.notes.length).to.equal(0);
|
||||
});
|
||||
|
||||
it('properly handles tag duplication', async () => {
|
||||
let modelManager = Factory.createModelManager();
|
||||
|
||||
let pair = createRelatedNoteTagPair();
|
||||
let noteParams = pair[0];
|
||||
let tagParams = pair[1];
|
||||
|
||||
modelManager.mapResponseItemsToLocalModels([noteParams, tagParams]);
|
||||
let note = modelManager.allItemsMatchingTypes(["Note"])[0];
|
||||
let tag = modelManager.allItemsMatchingTypes(["Tag"])[0];
|
||||
|
||||
var duplicateTag = await modelManager.duplicateItemAndAddAsConflict(tag);
|
||||
|
||||
expect(tag.uuid).to.not.equal(duplicateTag.uuid);
|
||||
|
||||
expect(tag.content.references.length).to.equal(1);
|
||||
expect(tag.notes.length).to.equal(1);
|
||||
|
||||
expect(duplicateTag.content.references.length).to.equal(1);
|
||||
expect(duplicateTag.notes.length).to.equal(1);
|
||||
|
||||
expect(note.tags.length).to.equal(2);
|
||||
|
||||
var noteTag1 = note.tags[0];
|
||||
var noteTag2 = note.tags[1];
|
||||
expect(noteTag1.uuid).to.not.equal(noteTag2.uuid);
|
||||
|
||||
// expect to be false
|
||||
expect(note.dirty).to.not.be.ok;
|
||||
expect(tag.dirty).to.not.be.ok;
|
||||
});
|
||||
|
||||
it('duplicating a note should maintain its tag references', async () => {
|
||||
let modelManager = Factory.createModelManager();
|
||||
|
||||
let pair = createRelatedNoteTagPair();
|
||||
let noteParams = pair[0];
|
||||
let tagParams = pair[1];
|
||||
|
||||
modelManager.mapResponseItemsToLocalModels([noteParams, tagParams]);
|
||||
let note = modelManager.allItemsMatchingTypes(["Note"])[0];
|
||||
let tag = modelManager.allItemsMatchingTypes(["Tag"])[0];
|
||||
|
||||
var duplicateNote = await modelManager.duplicateItemAndAddAsConflict(note);
|
||||
|
||||
expect(note.uuid).to.not.equal(duplicateNote.uuid);
|
||||
expect(duplicateNote.tags.length).to.equal(note.tags.length);
|
||||
});
|
||||
|
||||
it('deleting a note should update tag references', () => {
|
||||
let modelManager = Factory.createModelManager();
|
||||
|
||||
let pair = createRelatedNoteTagPair();
|
||||
let noteParams = pair[0];
|
||||
let tagParams = pair[1];
|
||||
|
||||
modelManager.mapResponseItemsToLocalModels([noteParams, tagParams]);
|
||||
let note = modelManager.allItemsMatchingTypes(["Note"])[0];
|
||||
let tag = modelManager.allItemsMatchingTypes(["Tag"])[0];
|
||||
|
||||
expect(tag.content.references.length).to.equal(1);
|
||||
expect(tag.notes.length).to.equal(1);
|
||||
|
||||
expect(note.content.references.length).to.equal(0);
|
||||
expect(note.tags.length).to.equal(1);
|
||||
|
||||
modelManager.setItemToBeDeleted(tag);
|
||||
modelManager.mapResponseItemsToLocalModels([tag]);
|
||||
expect(tag.content.references.length).to.equal(0);
|
||||
expect(tag.notes.length).to.equal(0);
|
||||
});
|
||||
|
||||
it('importing existing data should keep relationships valid', () => {
|
||||
let modelManager = Factory.createModelManager();
|
||||
|
||||
let pair = createRelatedNoteTagPair();
|
||||
let noteParams = pair[0];
|
||||
let tagParams = pair[1];
|
||||
|
||||
modelManager.mapResponseItemsToLocalModels([noteParams, tagParams]);
|
||||
let note = modelManager.allItemsMatchingTypes(["Note"])[0];
|
||||
let tag = modelManager.allItemsMatchingTypes(["Tag"])[0];
|
||||
|
||||
expect(tag.content.references.length).to.equal(1);
|
||||
expect(tag.notes.length).to.equal(1);
|
||||
|
||||
expect(note.content.references.length).to.equal(0);
|
||||
expect(note.tags.length).to.equal(1);
|
||||
|
||||
modelManager.importItems([noteParams, tagParams]);
|
||||
|
||||
expect(modelManager.allItems.length).to.equal(2);
|
||||
|
||||
expect(tag.content.references.length).to.equal(1);
|
||||
expect(tag.notes.length).to.equal(1);
|
||||
|
||||
expect(note.content.references.length).to.equal(0);
|
||||
expect(note.referencingObjects.length).to.equal(1);
|
||||
expect(note.tags.length).to.equal(1);
|
||||
});
|
||||
|
||||
it('importing data with differing content should create duplicates', async () => {
|
||||
let modelManager = Factory.createModelManager();
|
||||
|
||||
let pair = createRelatedNoteTagPair();
|
||||
let noteParams = pair[0];
|
||||
let tagParams = pair[1];
|
||||
|
||||
modelManager.mapResponseItemsToLocalModels([noteParams, tagParams]);
|
||||
let note = modelManager.allItemsMatchingTypes(["Note"])[0];
|
||||
let tag = modelManager.allItemsMatchingTypes(["Tag"])[0];
|
||||
|
||||
noteParams.content.title = Math.random();
|
||||
tagParams.content.title = Math.random();
|
||||
await modelManager.importItems([noteParams, tagParams]);
|
||||
|
||||
expect(modelManager.allItems.length).to.equal(4);
|
||||
|
||||
var newNote = modelManager.allItemsMatchingTypes(["Note"])[1];
|
||||
var newTag = modelManager.allItemsMatchingTypes(["Tag"])[1];
|
||||
|
||||
expect(newNote.uuid).to.not.equal(note.uuid);
|
||||
expect(newTag.uuid).to.not.equal(tag.uuid);
|
||||
|
||||
expect(tag.content.references.length).to.equal(2);
|
||||
expect(tag.notes.length).to.equal(2);
|
||||
|
||||
expect(note.content.references.length).to.equal(0);
|
||||
expect(note.referencingObjects.length).to.equal(2);
|
||||
expect(note.tags.length).to.equal(2);
|
||||
|
||||
expect(newTag.content.references.length).to.equal(1);
|
||||
expect(newTag.notes.length).to.equal(1);
|
||||
|
||||
expect(newNote.content.references.length).to.equal(0);
|
||||
expect(newNote.referencingObjects.length).to.equal(1);
|
||||
expect(newNote.tags.length).to.equal(1);
|
||||
});
|
||||
|
||||
it('deleting a tag from a note with bi-directional relationship', () => {
|
||||
// Tags now reference notes, but it used to be that tags referenced notes and notes referenced tags.
|
||||
// After the change, there was an issue where removing an old tag relationship from a note would only
|
||||
// remove one way, and thus keep it intact on the visual level.
|
||||
|
||||
let modelManager = Factory.createModelManager();
|
||||
|
||||
let pair = createRelatedNoteTagPair();
|
||||
let noteParams = pair[0];
|
||||
let tagParams = pair[1];
|
||||
|
||||
noteParams.content.references = [{
|
||||
content_type: tagParams.content_type,
|
||||
uuid: tagParams.uuid
|
||||
}]
|
||||
|
||||
modelManager.mapResponseItemsToLocalModels([noteParams, tagParams]);
|
||||
let note = modelManager.allItemsMatchingTypes(["Note"])[0];
|
||||
let tag = modelManager.allItemsMatchingTypes(["Tag"])[0];
|
||||
|
||||
expect(tag.notes.length).to.equal(1);
|
||||
expect(note.tags.length).to.equal(1);
|
||||
|
||||
tag.removeItemAsRelationship(note);
|
||||
|
||||
expect(tag.notes.length).to.equal(0);
|
||||
expect(note.tags.length).to.equal(0);
|
||||
|
||||
expect(note.content.references.length).to.equal(0);
|
||||
expect(tag.content.references.length).to.equal(0);
|
||||
});
|
||||
|
||||
it('deleting a tag should not dirty notes', () => {
|
||||
// Tags now reference notes, but it used to be that tags referenced notes and notes referenced tags.
|
||||
// After the change, there was an issue where removing an old tag relationship from a note would only
|
||||
// remove one way, and thus keep it intact on the visual level.
|
||||
|
||||
let modelManager = Factory.createModelManager();
|
||||
|
||||
let pair = createRelatedNoteTagPair();
|
||||
let noteParams = pair[0];
|
||||
let tagParams = pair[1];
|
||||
|
||||
modelManager.mapResponseItemsToLocalModels([noteParams, tagParams]);
|
||||
let note = modelManager.allItemsMatchingTypes(["Note"])[0];
|
||||
let tag = modelManager.allItemsMatchingTypes(["Tag"])[0];
|
||||
|
||||
modelManager.setItemToBeDeleted(tag);
|
||||
|
||||
expect(tag.dirty).to.equal(true);
|
||||
expect(note.dirty).to.not.be.ok;
|
||||
})
|
||||
});
|
||||
|
||||
describe("syncing", () => {
|
||||
var totalItemCount = 0;
|
||||
|
||||
beforeEach((done) => {
|
||||
var email = Factory.globalCryptoManager().crypto.generateUUIDSync();
|
||||
var password = Factory.globalCryptoManager().crypto.generateUUIDSync();
|
||||
Factory.globalStorageManager().clearAllData().then(() => {
|
||||
Factory.newRegisteredUser(email, password).then((user) => {
|
||||
done();
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
let modelManager = Factory.createModelManager();
|
||||
let authManager = Factory.globalAuthManager();
|
||||
let syncManager = new SFSyncManager(modelManager, Factory.globalStorageManager(), Factory.globalHttpManager());
|
||||
|
||||
syncManager.setKeyRequestHandler(async () => {
|
||||
return {
|
||||
keys: await authManager.keys(),
|
||||
auth_params: await authManager.getAuthParams(),
|
||||
offline: false
|
||||
};
|
||||
})
|
||||
|
||||
const wait = (secs) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
setTimeout(function () {
|
||||
resolve();
|
||||
}, secs * 1000);
|
||||
})
|
||||
}
|
||||
|
||||
it('syncing a note should collapse its properties into the content object after sync', async () => {
|
||||
let note = new SNNote();
|
||||
note.title = "Foo";
|
||||
note.setDirty(true);
|
||||
modelManager.addItem(note);
|
||||
|
||||
expect(note.content.title).to.not.be.ok;
|
||||
|
||||
await syncManager.sync();
|
||||
|
||||
expect(note.content.title).to.equal("Foo");
|
||||
});
|
||||
|
||||
it('syncing a note many times does not cause duplication', async () => {
|
||||
modelManager.handleSignout();
|
||||
let pair = createRelatedNoteTagPair();
|
||||
let noteParams = pair[0];
|
||||
let tagParams = pair[1];
|
||||
|
||||
modelManager.mapResponseItemsToLocalModels([noteParams, tagParams]);
|
||||
let note = modelManager.allItemsMatchingTypes(["Note"])[0];
|
||||
let tag = modelManager.allItemsMatchingTypes(["Tag"])[0];
|
||||
|
||||
for(var i = 0; i < 9; i++) {
|
||||
note.setDirty(true);
|
||||
tag.setDirty(true);
|
||||
await syncManager.sync();
|
||||
syncManager.clearSyncToken();
|
||||
expect(tag.content.references.length).to.equal(1);
|
||||
expect(note.tags.length).to.equal(1);
|
||||
expect(tag.notes.length).to.equal(1);
|
||||
expect(modelManager.allItems.length).to.equal(2);
|
||||
console.log("Waiting 1.1s...");
|
||||
await wait(1.1);
|
||||
|
||||
}
|
||||
}).timeout(20000);
|
||||
|
||||
it("handles signing in and merging data", async () => {
|
||||
|
||||
let syncManager = new SFSyncManager(modelManager, Factory.globalStorageManager(), Factory.globalHttpManager());
|
||||
|
||||
// be offline
|
||||
syncManager.setKeyRequestHandler(async () => {
|
||||
return {
|
||||
offline: true
|
||||
};
|
||||
})
|
||||
|
||||
modelManager.handleSignout();
|
||||
let pair = createRelatedNoteTagPair();
|
||||
let noteParams = pair[0];
|
||||
let tagParams = pair[1];
|
||||
|
||||
modelManager.mapResponseItemsToLocalModels([noteParams, tagParams]);
|
||||
let originalNote = modelManager.allItemsMatchingTypes(["Note"])[0];
|
||||
let originalTag = modelManager.allItemsMatchingTypes(["Tag"])[0];
|
||||
originalNote.setDirty(true);
|
||||
originalTag.setDirty(true);
|
||||
|
||||
await syncManager.sync();
|
||||
|
||||
expect(originalTag.content.references.length).to.equal(1);
|
||||
expect(originalTag.notes.length).to.equal(1);
|
||||
expect(originalNote.tags.length).to.equal(1);
|
||||
|
||||
// go online
|
||||
syncManager.setKeyRequestHandler(async () => {
|
||||
return {
|
||||
keys: await authManager.keys(),
|
||||
auth_params: await authManager.getAuthParams(),
|
||||
offline: false
|
||||
};
|
||||
})
|
||||
|
||||
// when signing in, all local items are cleared from storage (but kept in memory; to clear desktop logs),
|
||||
// then resaved with alternated uuids.
|
||||
await Factory.globalStorageManager().clearAllModels();
|
||||
await syncManager.markAllItemsDirtyAndSaveOffline(true)
|
||||
|
||||
let note = modelManager.allItemsMatchingTypes(["Note"])[0];
|
||||
let tag = modelManager.allItemsMatchingTypes(["Tag"])[0];
|
||||
|
||||
expect(modelManager.allItems.length).to.equal(2);
|
||||
|
||||
expect(note.uuid).to.not.equal(originalNote.uuid);
|
||||
expect(tag.uuid).to.not.equal(originalTag.uuid);
|
||||
|
||||
expect(tag.content.references.length).to.equal(1);
|
||||
expect(note.content.references.length).to.equal(0);
|
||||
|
||||
expect(note.referencingObjects.length).to.equal(1);
|
||||
expect(tag.notes.length).to.equal(1);
|
||||
expect(note.tags.length).to.equal(1);
|
||||
})
|
||||
|
||||
it('duplicating a tag should maintian its relationships', async () => {
|
||||
await syncManager.loadLocalItems();
|
||||
modelManager.handleSignout();
|
||||
let pair = createRelatedNoteTagPair();
|
||||
let noteParams = pair[0];
|
||||
let tagParams = pair[1];
|
||||
|
||||
modelManager.mapResponseItemsToLocalModels([noteParams, tagParams]);
|
||||
let note = modelManager.allItemsMatchingTypes(["Note"])[0];
|
||||
let tag = modelManager.allItemsMatchingTypes(["Tag"])[0];
|
||||
|
||||
note.setDirty(true);
|
||||
tag.setDirty(true);
|
||||
|
||||
await syncManager.sync();
|
||||
await syncManager.clearSyncToken();
|
||||
|
||||
expect(modelManager.allItems.length).to.equal(2);
|
||||
|
||||
tag.title = `${Math.random()}`
|
||||
tag.updated_at = Factory.yesterday();
|
||||
tag.setDirty(true);
|
||||
|
||||
expect(note.referencingObjects.length).to.equal(1);
|
||||
|
||||
// wait about 1s, which is the value the dev server will ignore conflicting changes
|
||||
return expect(new Promise((resolve, reject) => {
|
||||
setTimeout(function () {
|
||||
resolve();
|
||||
}, 1100);
|
||||
})).to.be.fulfilled.then(async () => {
|
||||
return expect(syncManager.sync()).to.be.fulfilled.then(async (response) => {
|
||||
// tag should now be conflicted and a copy created
|
||||
let models = modelManager.allItems;
|
||||
expect(modelManager.allItems.length).to.equal(3);
|
||||
var tags = modelManager.allItemsMatchingTypes(["Tag"]);
|
||||
var tag1 = tags[0];
|
||||
var tag2 = tags[1];
|
||||
|
||||
expect(tag1.uuid).to.not.equal(tag2.uuid);
|
||||
|
||||
expect(tag1.uuid).to.equal(tag.uuid);
|
||||
expect(tag2.content.conflict_of).to.equal(tag1.uuid);
|
||||
expect(tag1.notes.length).to.equal(tag2.notes.length);
|
||||
expect(tag1.referencingObjects.length).to.equal(0);
|
||||
expect(tag2.referencingObjects.length).to.equal(0);
|
||||
|
||||
// Two tags now link to this note
|
||||
expect(note.referencingObjects.length).to.equal(2);
|
||||
expect(note.referencingObjects[0]).to.not.equal(note.referencingObjects[1]);
|
||||
})
|
||||
})
|
||||
}).timeout(10000);
|
||||
})
|
||||
@@ -13,8 +13,15 @@
|
||||
<body>
|
||||
<div id="mocha"></div>
|
||||
<script src="../../node_modules/mocha/mocha.js"></script>
|
||||
<script src="../dist/javascripts/app.js"></script>
|
||||
<script>mocha.setup('bdd')</script>
|
||||
<script type="module" src="models.test.js"></script>
|
||||
<!-- <script type="module" src="models.test.js"></script> -->
|
||||
|
||||
<script>
|
||||
// Object.assign(window, SNLibrary);
|
||||
// console.log(SNLibrary);
|
||||
</script>
|
||||
|
||||
<script type="module">
|
||||
mocha.checkLeaks();
|
||||
mocha.run();
|
||||
|
||||
@@ -33,15 +33,6 @@ module.exports = {
|
||||
loader: 'babel-loader'
|
||||
}
|
||||
},
|
||||
// Transpile only our libs since we import ES6 classes
|
||||
// {
|
||||
// test: /\.js$/,
|
||||
// include: /node_modules\/(standard-file-js|snjs)/,
|
||||
// loader: 'babel-loader',
|
||||
// options: {
|
||||
// presets: ['@babel/preset-env']
|
||||
// }
|
||||
// },
|
||||
{
|
||||
test: /\.s?css$/,
|
||||
use: [
|
||||
|
||||
Reference in New Issue
Block a user