tests: fix memory leaks (#2389)
This commit is contained in:
@@ -1,5 +1,4 @@
|
||||
/* eslint-disable no-unused-expressions */
|
||||
/* eslint-disable no-undef */
|
||||
|
||||
import './vendor/chai-as-promised-built.js'
|
||||
import './vendor/buffer@5.6.0.js'
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
/* eslint-disable no-unused-expressions */
|
||||
/* eslint-disable no-undef */
|
||||
|
||||
import './vendor/chai-as-promised-built.js'
|
||||
|
||||
chai.use(chaiAsPromised)
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
/* eslint-disable no-unused-expressions */
|
||||
/* eslint-disable no-undef */
|
||||
|
||||
import './vendor/chai-as-promised-built.js'
|
||||
|
||||
chai.use(chaiAsPromised)
|
||||
|
||||
@@ -1,19 +1,17 @@
|
||||
/* eslint-disable no-unused-expressions */
|
||||
/* eslint-disable no-undef */
|
||||
import * as Factory from './lib/factory.js'
|
||||
chai.use(chaiAsPromised)
|
||||
const expect = chai.expect
|
||||
|
||||
describe('000 legacy protocol operations', () => {
|
||||
const application = Factory.createApplicationWithRealCrypto()
|
||||
const protocol004 = new SNProtocolOperator004(new SNWebCrypto())
|
||||
let protocol004
|
||||
|
||||
before(async () => {
|
||||
await Factory.initializeApplication(application)
|
||||
beforeEach(async () => {
|
||||
localStorage.clear()
|
||||
|
||||
protocol004 = new SNProtocolOperator004(new SNWebCrypto())
|
||||
})
|
||||
|
||||
after(async () => {
|
||||
await Factory.safeDeinit(application)
|
||||
afterEach(async () => {
|
||||
localStorage.clear()
|
||||
})
|
||||
|
||||
it('cannot decode 000 item', function () {
|
||||
|
||||
@@ -1,27 +1,32 @@
|
||||
/* eslint-disable no-unused-expressions */
|
||||
/* eslint-disable no-undef */
|
||||
import * as Factory from './lib/factory.js'
|
||||
|
||||
chai.use(chaiAsPromised)
|
||||
const expect = chai.expect
|
||||
|
||||
describe('001 protocol operations', () => {
|
||||
const application = Factory.createApplicationWithRealCrypto()
|
||||
const protocol001 = new SNProtocolOperator001(new SNWebCrypto())
|
||||
let application
|
||||
let protocol001
|
||||
|
||||
const _identifier = 'hello@test.com'
|
||||
const _password = 'password'
|
||||
let _identifier = 'hello@test.com'
|
||||
let _password = 'password'
|
||||
let _keyParams, _key
|
||||
|
||||
// runs once before all tests in this block
|
||||
before(async () => {
|
||||
beforeEach(async () => {
|
||||
localStorage.clear()
|
||||
|
||||
application = Factory.createApplicationWithRealCrypto()
|
||||
protocol001 = new SNProtocolOperator001(new SNWebCrypto())
|
||||
|
||||
await Factory.initializeApplication(application)
|
||||
|
||||
_key = await protocol001.createRootKey(_identifier, _password, KeyParamsOrigination.Registration)
|
||||
_keyParams = _key.keyParams
|
||||
})
|
||||
|
||||
after(async () => {
|
||||
afterEach(async () => {
|
||||
await Factory.safeDeinit(application)
|
||||
localStorage.clear()
|
||||
application = undefined
|
||||
})
|
||||
|
||||
it('generates random key', async () => {
|
||||
|
||||
@@ -1,26 +1,30 @@
|
||||
/* eslint-disable no-unused-expressions */
|
||||
/* eslint-disable no-undef */
|
||||
import * as Factory from './lib/factory.js'
|
||||
|
||||
chai.use(chaiAsPromised)
|
||||
const expect = chai.expect
|
||||
|
||||
describe('002 protocol operations', () => {
|
||||
const _identifier = 'hello@test.com'
|
||||
const _password = 'password'
|
||||
let _identifier = 'hello@test.com'
|
||||
let _password = 'password'
|
||||
let _keyParams, _key
|
||||
const application = Factory.createApplicationWithRealCrypto()
|
||||
const protocol002 = new SNProtocolOperator002(new SNWebCrypto())
|
||||
let application
|
||||
let protocol002
|
||||
|
||||
// runs once before all tests in this block
|
||||
before(async () => {
|
||||
beforeEach(async () => {
|
||||
localStorage.clear()
|
||||
|
||||
application = Factory.createApplicationWithRealCrypto()
|
||||
protocol002 = new SNProtocolOperator002(new SNWebCrypto())
|
||||
|
||||
await Factory.initializeApplication(application)
|
||||
_key = await protocol002.createRootKey(_identifier, _password, KeyParamsOrigination.Registration)
|
||||
_keyParams = _key.keyParams
|
||||
})
|
||||
|
||||
after(async () => {
|
||||
afterEach(async () => {
|
||||
await Factory.safeDeinit(application)
|
||||
localStorage.clear()
|
||||
application = undefined
|
||||
})
|
||||
|
||||
it('generates random key', async () => {
|
||||
|
||||
@@ -1,34 +1,31 @@
|
||||
/* eslint-disable no-unused-expressions */
|
||||
/* eslint-disable no-undef */
|
||||
import * as Factory from './lib/factory.js'
|
||||
|
||||
chai.use(chaiAsPromised)
|
||||
const expect = chai.expect
|
||||
|
||||
describe('003 protocol operations', () => {
|
||||
before(async () => {
|
||||
localStorage.clear()
|
||||
})
|
||||
|
||||
after(async () => {
|
||||
localStorage.clear()
|
||||
})
|
||||
|
||||
const _identifier = 'hello@test.com'
|
||||
const _password = 'password'
|
||||
let _identifier = 'hello@test.com'
|
||||
let _password = 'password'
|
||||
let _keyParams, _key
|
||||
|
||||
const sharedApplication = Factory.createApplicationWithRealCrypto()
|
||||
const protocol003 = new SNProtocolOperator003(new SNWebCrypto())
|
||||
let application
|
||||
let protocol003
|
||||
|
||||
// runs once before all tests in this block
|
||||
before(async () => {
|
||||
await Factory.initializeApplication(sharedApplication)
|
||||
beforeEach(async () => {
|
||||
localStorage.clear()
|
||||
|
||||
application = Factory.createApplicationWithRealCrypto()
|
||||
protocol003 = new SNProtocolOperator003(new SNWebCrypto())
|
||||
|
||||
await Factory.initializeApplication(application)
|
||||
_key = await protocol003.createRootKey(_identifier, _password, KeyParamsOrigination.Registration)
|
||||
_keyParams = _key.keyParams
|
||||
})
|
||||
|
||||
after(async () => {
|
||||
await Factory.safeDeinit(sharedApplication)
|
||||
afterEach(async () => {
|
||||
await Factory.safeDeinit(application)
|
||||
localStorage.clear()
|
||||
application = undefined
|
||||
})
|
||||
|
||||
it('generates random key', async () => {
|
||||
@@ -39,7 +36,7 @@ describe('003 protocol operations', () => {
|
||||
|
||||
it('cost minimum should throw', () => {
|
||||
expect(() => {
|
||||
sharedApplication.encryption.costMinimumForVersion('003')
|
||||
application.encryption.costMinimumForVersion('003')
|
||||
}).to.throw('Cost minimums only apply to versions <= 002')
|
||||
})
|
||||
|
||||
@@ -59,7 +56,7 @@ describe('003 protocol operations', () => {
|
||||
it('computes proper keys for sign in', async () => {
|
||||
const identifier = 'foo@bar.com'
|
||||
const password = 'very_secure'
|
||||
const keyParams = sharedApplication.encryption.createKeyParams({
|
||||
const keyParams = application.encryption.createKeyParams({
|
||||
pw_nonce: 'baaec0131d677cf993381367eb082fe377cefe70118c1699cb9b38f0bc850e7b',
|
||||
identifier: identifier,
|
||||
version: '003',
|
||||
@@ -73,7 +70,7 @@ describe('003 protocol operations', () => {
|
||||
it('can decrypt item generated with web version 3.3.6', async () => {
|
||||
const identifier = 'demo@standardnotes.org'
|
||||
const password = 'password'
|
||||
const keyParams = sharedApplication.encryption.createKeyParams({
|
||||
const keyParams = application.encryption.createKeyParams({
|
||||
pw_nonce: '31107837b44d86179140b7c602a55d694243e2e9ced0c4c914ac21ad90215055',
|
||||
identifier: identifier,
|
||||
version: '003',
|
||||
|
||||
@@ -1,26 +1,33 @@
|
||||
/* eslint-disable no-unused-expressions */
|
||||
/* eslint-disable no-undef */
|
||||
import * as Factory from './lib/factory.js'
|
||||
|
||||
chai.use(chaiAsPromised)
|
||||
const expect = chai.expect
|
||||
|
||||
describe('004 protocol operations', function () {
|
||||
const _identifier = 'hello@test.com'
|
||||
const _password = 'password'
|
||||
let _identifier = 'hello@test.com'
|
||||
let _password = 'password'
|
||||
let rootKeyParams
|
||||
let rootKey
|
||||
|
||||
const application = Factory.createApplicationWithRealCrypto()
|
||||
const protocol004 = new SNProtocolOperator004(new SNWebCrypto())
|
||||
let application
|
||||
let protocol004
|
||||
|
||||
beforeEach(async function () {
|
||||
localStorage.clear()
|
||||
|
||||
application = Factory.createApplicationWithRealCrypto()
|
||||
protocol004 = new SNProtocolOperator004(new SNWebCrypto())
|
||||
|
||||
before(async function () {
|
||||
await Factory.initializeApplication(application)
|
||||
|
||||
rootKey = await protocol004.createRootKey(_identifier, _password, KeyParamsOrigination.Registration)
|
||||
rootKeyParams = rootKey.keyParams
|
||||
})
|
||||
|
||||
after(async function () {
|
||||
afterEach(async function () {
|
||||
await Factory.safeDeinit(application)
|
||||
application = undefined
|
||||
localStorage.clear()
|
||||
})
|
||||
|
||||
it('cost minimum should throw', function () {
|
||||
|
||||
@@ -1,41 +1,46 @@
|
||||
/* eslint-disable no-unused-expressions */
|
||||
/* eslint-disable no-undef */
|
||||
import * as Factory from './lib/factory.js'
|
||||
import * as Utils from './lib/Utils.js'
|
||||
|
||||
chai.use(chaiAsPromised)
|
||||
const expect = chai.expect
|
||||
|
||||
describe('actions service', () => {
|
||||
const errorProcessingActionMessage = 'An issue occurred while processing this action. Please try again.'
|
||||
|
||||
before(async function () {
|
||||
let application
|
||||
let itemManager
|
||||
let actionsManager
|
||||
let email
|
||||
let password
|
||||
let authParams
|
||||
let fakeServer
|
||||
let actionsExtension
|
||||
let extensionItemUuid
|
||||
|
||||
beforeEach(async function () {
|
||||
this.timeout(20000)
|
||||
|
||||
localStorage.clear()
|
||||
|
||||
this.application = await Factory.createInitAppWithFakeCrypto()
|
||||
this.itemManager = this.application.items
|
||||
this.actionsManager = this.application.actions
|
||||
this.email = UuidGenerator.GenerateUuid()
|
||||
this.password = UuidGenerator.GenerateUuid()
|
||||
application = await Factory.createInitAppWithFakeCrypto()
|
||||
itemManager = application.items
|
||||
actionsManager = application.actions
|
||||
email = UuidGenerator.GenerateUuid()
|
||||
password = UuidGenerator.GenerateUuid()
|
||||
|
||||
await Factory.registerUserToApplication({
|
||||
application: this.application,
|
||||
email: this.email,
|
||||
password: this.password,
|
||||
application: application,
|
||||
email: email,
|
||||
password: password,
|
||||
})
|
||||
|
||||
const rootKey = await this.application.encryption.createRootKey(
|
||||
this.email,
|
||||
this.password,
|
||||
KeyParamsOrigination.Registration,
|
||||
)
|
||||
this.authParams = rootKey.keyParams.content
|
||||
const rootKey = await application.encryption.createRootKey(email, password, KeyParamsOrigination.Registration)
|
||||
authParams = rootKey.keyParams.content
|
||||
|
||||
this.fakeServer = sinon.fakeServer.create()
|
||||
this.fakeServer.respondImmediately = true
|
||||
fakeServer = sinon.fakeServer.create()
|
||||
fakeServer.respondImmediately = true
|
||||
|
||||
this.actionsExtension = {
|
||||
actionsExtension = {
|
||||
identifier: 'org.standardnotes.testing',
|
||||
name: 'Test extension',
|
||||
content_type: 'Extension',
|
||||
@@ -81,9 +86,9 @@ describe('actions service', () => {
|
||||
],
|
||||
}
|
||||
|
||||
this.fakeServer.respondWith('GET', /http:\/\/my-extension.sn.org\/get_actions\/(.*)/, (request, params) => {
|
||||
fakeServer.respondWith('GET', /http:\/\/my-extension.sn.org\/get_actions\/(.*)/, (request, params) => {
|
||||
const urlParams = new URLSearchParams(params)
|
||||
const extension = Copy(this.actionsExtension)
|
||||
const extension = Copy(actionsExtension)
|
||||
|
||||
if (urlParams.has('item_uuid')) {
|
||||
extension.actions.push({
|
||||
@@ -117,31 +122,31 @@ describe('actions service', () => {
|
||||
})
|
||||
|
||||
const encryptedPayload = CreateEncryptedServerSyncPushPayload(
|
||||
await this.application.encryption.encryptSplitSingle({
|
||||
await application.encryption.encryptSplitSingle({
|
||||
usesItemsKeyWithKeyLookup: {
|
||||
items: [payload],
|
||||
},
|
||||
}),
|
||||
)
|
||||
|
||||
this.fakeServer.respondWith('GET', /http:\/\/my-extension.sn.org\/action_[1,2]\/(.*)/, (request) => {
|
||||
fakeServer.respondWith('GET', /http:\/\/my-extension.sn.org\/action_[1,2]\/(.*)/, (request) => {
|
||||
request.respond(
|
||||
200,
|
||||
{ 'Content-Type': 'application/json' },
|
||||
JSON.stringify({
|
||||
item: encryptedPayload,
|
||||
auth_params: this.authParams,
|
||||
auth_params: authParams,
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
this.fakeServer.respondWith('GET', 'http://my-extension.sn.org/action_3/', [
|
||||
fakeServer.respondWith('GET', 'http://my-extension.sn.org/action_3/', [
|
||||
200,
|
||||
{ 'Content-Type': 'text/html; charset=utf-8' },
|
||||
'<h2>Action #3</h2>',
|
||||
])
|
||||
|
||||
this.fakeServer.respondWith('POST', /http:\/\/my-extension.sn.org\/action_[4,6]\/(.*)/, (request) => {
|
||||
fakeServer.respondWith('POST', /http:\/\/my-extension.sn.org\/action_[4,6]\/(.*)/, (request) => {
|
||||
const requestBody = JSON.parse(request.requestBody)
|
||||
|
||||
const response = {
|
||||
@@ -152,7 +157,7 @@ describe('actions service', () => {
|
||||
request.respond(200, { 'Content-Type': 'application/json' }, JSON.stringify(response))
|
||||
})
|
||||
|
||||
this.fakeServer.respondWith('GET', 'http://my-extension.sn.org/action_5/', (request) => {
|
||||
fakeServer.respondWith('GET', 'http://my-extension.sn.org/action_5/', (request) => {
|
||||
const encryptedPayloadClone = Copy(encryptedPayload)
|
||||
|
||||
encryptedPayloadClone.items_key_id = undefined
|
||||
@@ -163,53 +168,56 @@ describe('actions service', () => {
|
||||
|
||||
const payload = {
|
||||
item: encryptedPayloadClone,
|
||||
auth_params: this.authParams,
|
||||
auth_params: authParams,
|
||||
}
|
||||
|
||||
request.respond(200, { 'Content-Type': 'application/json' }, JSON.stringify(payload))
|
||||
})
|
||||
|
||||
// Extension item
|
||||
const extensionItem = await this.application.mutator.createItem(
|
||||
ContentType.TYPES.ActionsExtension,
|
||||
this.actionsExtension,
|
||||
)
|
||||
this.extensionItemUuid = extensionItem.uuid
|
||||
const extensionItem = await application.mutator.createItem(ContentType.TYPES.ActionsExtension, actionsExtension)
|
||||
extensionItemUuid = extensionItem.uuid
|
||||
})
|
||||
|
||||
after(async function () {
|
||||
this.fakeServer.restore()
|
||||
await Factory.safeDeinit(this.application)
|
||||
this.application = null
|
||||
afterEach(async function () {
|
||||
fakeServer.restore()
|
||||
|
||||
await Factory.safeDeinit(application)
|
||||
|
||||
application = undefined
|
||||
itemManager = undefined
|
||||
actionsManager = undefined
|
||||
fakeServer = undefined
|
||||
|
||||
localStorage.clear()
|
||||
})
|
||||
|
||||
it('should get extension items', async function () {
|
||||
await this.application.mutator.createItem(ContentType.TYPES.Note, {
|
||||
await application.mutator.createItem(ContentType.TYPES.Note, {
|
||||
title: 'A simple note',
|
||||
text: 'Standard Notes rocks! lml.',
|
||||
})
|
||||
const extensions = this.actionsManager.getExtensions()
|
||||
const extensions = actionsManager.getExtensions()
|
||||
expect(extensions.length).to.eq(1)
|
||||
})
|
||||
|
||||
it('should get extensions in context of item', async function () {
|
||||
const noteItem = await this.application.mutator.createItem(ContentType.TYPES.Note, {
|
||||
const noteItem = await application.mutator.createItem(ContentType.TYPES.Note, {
|
||||
title: 'Another note',
|
||||
text: 'Whiskey In The Jar',
|
||||
})
|
||||
|
||||
const noteItemExtensions = this.actionsManager.extensionsInContextOfItem(noteItem)
|
||||
const noteItemExtensions = actionsManager.extensionsInContextOfItem(noteItem)
|
||||
expect(noteItemExtensions.length).to.eq(1)
|
||||
expect(noteItemExtensions[0].supported_types).to.include(noteItem.content_type)
|
||||
})
|
||||
|
||||
it('should get actions based on item context', async function () {
|
||||
const tagItem = await this.application.mutator.createItem(ContentType.TYPES.Tag, {
|
||||
const tagItem = await application.mutator.createItem(ContentType.TYPES.Tag, {
|
||||
title: 'Music',
|
||||
})
|
||||
|
||||
const extensionItem = await this.itemManager.findItem(this.extensionItemUuid)
|
||||
const extensionItem = await itemManager.findItem(extensionItemUuid)
|
||||
const tagActions = extensionItem.actionsWithContextForItem(tagItem)
|
||||
|
||||
expect(tagActions.length).to.eq(1)
|
||||
@@ -217,15 +225,15 @@ describe('actions service', () => {
|
||||
})
|
||||
|
||||
it('should load extension in context of item', async function () {
|
||||
const noteItem = await this.application.mutator.createItem(ContentType.TYPES.Note, {
|
||||
const noteItem = await application.mutator.createItem(ContentType.TYPES.Note, {
|
||||
title: 'Yet another note',
|
||||
text: 'And all things will end ♫',
|
||||
})
|
||||
|
||||
const extensionItem = await this.itemManager.findItem(this.extensionItemUuid)
|
||||
const extensionItem = await itemManager.findItem(extensionItemUuid)
|
||||
expect(extensionItem.actions.length).to.be.eq(5)
|
||||
|
||||
const extensionWithItem = await this.actionsManager.loadExtensionInContextOfItem(extensionItem, noteItem)
|
||||
const extensionWithItem = await actionsManager.loadExtensionInContextOfItem(extensionItem, noteItem)
|
||||
expect(extensionWithItem.actions.length).to.be.eq(7)
|
||||
expect(extensionWithItem.actions.map((action) => action.label)).to.include.members([
|
||||
/**
|
||||
@@ -237,7 +245,7 @@ describe('actions service', () => {
|
||||
])
|
||||
|
||||
// Actions that are relevant for an item should not be stored.
|
||||
const updatedExtensionItem = await this.itemManager.findItem(this.extensionItemUuid)
|
||||
const updatedExtensionItem = await itemManager.findItem(extensionItemUuid)
|
||||
const expectedActions = extensionItem.actions.map((action) => {
|
||||
const { id, ...rest } = action
|
||||
return rest
|
||||
@@ -248,18 +256,22 @@ describe('actions service', () => {
|
||||
describe('render action', async function () {
|
||||
const sandbox = sinon.createSandbox()
|
||||
|
||||
before(async function () {
|
||||
this.noteItem = await this.application.mutator.createItem(ContentType.TYPES.Note, {
|
||||
let noteItem
|
||||
let renderAction
|
||||
let alertServiceAlert
|
||||
let windowAlert
|
||||
let httpServiceGetAbsolute
|
||||
|
||||
beforeEach(async function () {
|
||||
noteItem = await application.mutator.createItem(ContentType.TYPES.Note, {
|
||||
title: 'Hey',
|
||||
text: 'Welcome To Paradise',
|
||||
})
|
||||
const extensionItem = await this.itemManager.findItem(this.extensionItemUuid)
|
||||
this.renderAction = extensionItem.actions.filter((action) => action.verb === 'render')[0]
|
||||
})
|
||||
const extensionItem = await itemManager.findItem(extensionItemUuid)
|
||||
|
||||
beforeEach(async function () {
|
||||
this.alertServiceAlert = sandbox.spy(this.actionsManager.alertService, 'alert')
|
||||
this.windowAlert = sandbox.stub(window, 'alert').callsFake((message) => message)
|
||||
renderAction = extensionItem.actions.filter((action) => action.verb === 'render')[0]
|
||||
alertServiceAlert = sandbox.spy(actionsManager.alertService, 'alert')
|
||||
windowAlert = sandbox.stub(window, 'alert').callsFake((message) => message)
|
||||
})
|
||||
|
||||
afterEach(async function () {
|
||||
@@ -267,35 +279,35 @@ describe('actions service', () => {
|
||||
})
|
||||
|
||||
it('should show an alert if the request fails', async function () {
|
||||
this.httpServiceGetAbsolute = sandbox
|
||||
.stub(this.actionsManager.httpService, 'getAbsolute')
|
||||
httpServiceGetAbsolute = sandbox
|
||||
.stub(actionsManager.httpService, 'getAbsolute')
|
||||
.callsFake((url) => Promise.reject(new Error('Dummy error.')))
|
||||
|
||||
const actionResponse = await this.actionsManager.runAction(this.renderAction, this.noteItem)
|
||||
const actionResponse = await actionsManager.runAction(renderAction, noteItem)
|
||||
|
||||
sinon.assert.calledOnceWithExactly(this.httpServiceGetAbsolute, this.renderAction.url)
|
||||
sinon.assert.calledOnceWithExactly(this.alertServiceAlert, errorProcessingActionMessage)
|
||||
sinon.assert.calledOnceWithExactly(httpServiceGetAbsolute, renderAction.url)
|
||||
sinon.assert.calledOnceWithExactly(alertServiceAlert, errorProcessingActionMessage)
|
||||
expect(actionResponse.error.message).to.eq(errorProcessingActionMessage)
|
||||
})
|
||||
|
||||
it('should return a response if payload is valid', async function () {
|
||||
const actionResponse = await this.actionsManager.runAction(this.renderAction, this.noteItem)
|
||||
const actionResponse = await actionsManager.runAction(renderAction, noteItem)
|
||||
|
||||
expect(actionResponse).to.have.property('item')
|
||||
expect(actionResponse.item.payload.content.title).to.eq('Testing')
|
||||
})
|
||||
|
||||
it('should return undefined if payload is invalid', async function () {
|
||||
sandbox.stub(this.actionsManager, 'payloadByDecryptingResponse').returns(null)
|
||||
sandbox.stub(actionsManager, 'payloadByDecryptingResponse').returns(null)
|
||||
|
||||
const actionResponse = await this.actionsManager.runAction(this.renderAction, this.noteItem)
|
||||
const actionResponse = await actionsManager.runAction(renderAction, noteItem)
|
||||
expect(actionResponse).to.be.undefined
|
||||
})
|
||||
|
||||
it('should return decrypted payload if password is valid', async function () {
|
||||
const extensionItem = await this.itemManager.findItem(this.extensionItemUuid)
|
||||
this.renderAction = extensionItem.actions.filter((action) => action.verb === 'render')[0]
|
||||
const actionResponse = await this.actionsManager.runAction(this.renderAction, this.noteItem)
|
||||
const extensionItem = await itemManager.findItem(extensionItemUuid)
|
||||
renderAction = extensionItem.actions.filter((action) => action.verb === 'render')[0]
|
||||
const actionResponse = await actionsManager.runAction(renderAction, noteItem)
|
||||
|
||||
expect(actionResponse.item).to.be.ok
|
||||
expect(actionResponse.item.title).to.be.equal('Testing')
|
||||
@@ -305,24 +317,24 @@ describe('actions service', () => {
|
||||
describe('show action', async function () {
|
||||
const sandbox = sinon.createSandbox()
|
||||
|
||||
before(async function () {
|
||||
const extensionItem = await this.itemManager.findItem(this.extensionItemUuid)
|
||||
this.showAction = extensionItem.actions[2]
|
||||
})
|
||||
let showAction
|
||||
let deviceInterfaceOpenUrl
|
||||
|
||||
beforeEach(async function () {
|
||||
this.actionsManager.device.openUrl = (url) => url
|
||||
this.deviceInterfaceOpenUrl = sandbox.spy(this.actionsManager.device, 'openUrl')
|
||||
const extensionItem = await itemManager.findItem(extensionItemUuid)
|
||||
showAction = extensionItem.actions[2]
|
||||
actionsManager.device.openUrl = (url) => url
|
||||
deviceInterfaceOpenUrl = sandbox.spy(actionsManager.device, 'openUrl')
|
||||
})
|
||||
|
||||
this.afterEach(async function () {
|
||||
afterEach(async function () {
|
||||
sandbox.restore()
|
||||
})
|
||||
|
||||
it('should open the action url', async function () {
|
||||
const response = await this.actionsManager.runAction(this.showAction)
|
||||
const response = await actionsManager.runAction(showAction)
|
||||
|
||||
sandbox.assert.calledOnceWithExactly(this.deviceInterfaceOpenUrl, this.showAction.url)
|
||||
sandbox.assert.calledOnceWithExactly(deviceInterfaceOpenUrl, showAction.url)
|
||||
expect(response).to.eql({})
|
||||
})
|
||||
})
|
||||
@@ -330,28 +342,32 @@ describe('actions service', () => {
|
||||
describe('post action', async function () {
|
||||
const sandbox = sinon.createSandbox()
|
||||
|
||||
before(async function () {
|
||||
this.noteItem = await this.application.mutator.createItem(ContentType.TYPES.Note, {
|
||||
let noteItem
|
||||
let extensionItem
|
||||
let decryptedPostAction
|
||||
let encryptedPostAction
|
||||
let alertServiceAlert
|
||||
let httpServicePostAbsolute
|
||||
|
||||
beforeEach(async function () {
|
||||
noteItem = await application.mutator.createItem(ContentType.TYPES.Note, {
|
||||
title: 'Excuse Me',
|
||||
text: 'Time To Be King 8)',
|
||||
})
|
||||
this.extensionItem = await this.itemManager.findItem(this.extensionItemUuid)
|
||||
this.extensionItem = await this.actionsManager.loadExtensionInContextOfItem(this.extensionItem, this.noteItem)
|
||||
extensionItem = await itemManager.findItem(extensionItemUuid)
|
||||
extensionItem = await actionsManager.loadExtensionInContextOfItem(extensionItem, noteItem)
|
||||
|
||||
this.decryptedPostAction = this.extensionItem.actions.filter(
|
||||
decryptedPostAction = extensionItem.actions.filter(
|
||||
(action) => action.access_type === 'decrypted' && action.verb === 'post',
|
||||
)[0]
|
||||
|
||||
this.encryptedPostAction = this.extensionItem.actions.filter(
|
||||
encryptedPostAction = extensionItem.actions.filter(
|
||||
(action) => action.access_type === 'encrypted' && action.verb === 'post',
|
||||
)[0]
|
||||
})
|
||||
|
||||
beforeEach(async function () {
|
||||
this.alertServiceAlert = sandbox.spy(this.actionsManager.alertService, 'alert')
|
||||
this.windowAlert = sandbox.stub(window, 'alert').callsFake((message) => message)
|
||||
this.httpServicePostAbsolute = sandbox.stub(this.actionsManager.httpService, 'postAbsolute')
|
||||
this.httpServicePostAbsolute.callsFake((url, params) => Promise.resolve(params))
|
||||
alertServiceAlert = sandbox.spy(actionsManager.alertService, 'alert')
|
||||
sandbox.stub(window, 'alert').callsFake((message) => message)
|
||||
httpServicePostAbsolute = sandbox.stub(actionsManager.httpService, 'postAbsolute')
|
||||
httpServicePostAbsolute.callsFake((url, params) => Promise.resolve(params))
|
||||
})
|
||||
|
||||
afterEach(async function () {
|
||||
@@ -359,52 +375,50 @@ describe('actions service', () => {
|
||||
})
|
||||
|
||||
it('should include generic encrypted payload within request body', async function () {
|
||||
const response = await this.actionsManager.runAction(this.encryptedPostAction, this.noteItem)
|
||||
const response = await actionsManager.runAction(encryptedPostAction, noteItem)
|
||||
|
||||
expect(response.items[0].enc_item_key).to.satisfy((string) => {
|
||||
return string.startsWith(this.application.encryption.getLatestVersion())
|
||||
return string.startsWith(application.encryption.getLatestVersion())
|
||||
})
|
||||
expect(response.items[0].uuid).to.eq(this.noteItem.uuid)
|
||||
expect(response.items[0].uuid).to.eq(noteItem.uuid)
|
||||
expect(response.items[0].auth_hash).to.not.be.ok
|
||||
expect(response.items[0].content_type).to.be.ok
|
||||
expect(response.items[0].created_at).to.be.ok
|
||||
expect(response.items[0].content).to.satisfy((string) => {
|
||||
return string.startsWith(this.application.encryption.getLatestVersion())
|
||||
return string.startsWith(application.encryption.getLatestVersion())
|
||||
})
|
||||
})
|
||||
|
||||
it('should include generic decrypted payload within request body', async function () {
|
||||
const response = await this.actionsManager.runAction(this.decryptedPostAction, this.noteItem)
|
||||
const response = await actionsManager.runAction(decryptedPostAction, noteItem)
|
||||
|
||||
expect(response.items[0].uuid).to.eq(this.noteItem.uuid)
|
||||
expect(response.items[0].uuid).to.eq(noteItem.uuid)
|
||||
expect(response.items[0].enc_item_key).to.not.be.ok
|
||||
expect(response.items[0].auth_hash).to.not.be.ok
|
||||
expect(response.items[0].content_type).to.be.ok
|
||||
expect(response.items[0].created_at).to.be.ok
|
||||
expect(response.items[0].content.title).to.eq(this.noteItem.title)
|
||||
expect(response.items[0].content.text).to.eq(this.noteItem.text)
|
||||
expect(response.items[0].content.title).to.eq(noteItem.title)
|
||||
expect(response.items[0].content.text).to.eq(noteItem.text)
|
||||
})
|
||||
|
||||
it('should post to the action url', async function () {
|
||||
this.httpServicePostAbsolute.restore()
|
||||
const response = await this.actionsManager.runAction(this.decryptedPostAction, this.noteItem)
|
||||
httpServicePostAbsolute.restore()
|
||||
const response = await actionsManager.runAction(decryptedPostAction, noteItem)
|
||||
|
||||
expect(response).to.be.ok
|
||||
expect(response.uuid).to.eq(this.noteItem.uuid)
|
||||
expect(response.uuid).to.eq(noteItem.uuid)
|
||||
expect(response.result).to.eq('Action POSTed successfully.')
|
||||
})
|
||||
|
||||
it('should alert if an error occurred while processing the action', async function () {
|
||||
this.httpServicePostAbsolute.restore()
|
||||
httpServicePostAbsolute.restore()
|
||||
const dummyError = new Error('Dummy error.')
|
||||
|
||||
sandbox
|
||||
.stub(this.actionsManager.httpService, 'postAbsolute')
|
||||
.callsFake((url, params) => Promise.reject(dummyError))
|
||||
sandbox.stub(actionsManager.httpService, 'postAbsolute').callsFake((url, params) => Promise.reject(dummyError))
|
||||
|
||||
const response = await this.actionsManager.runAction(this.decryptedPostAction, this.noteItem)
|
||||
const response = await actionsManager.runAction(decryptedPostAction, noteItem)
|
||||
|
||||
sinon.assert.calledOnceWithExactly(this.alertServiceAlert, errorProcessingActionMessage)
|
||||
sinon.assert.calledOnceWithExactly(alertServiceAlert, errorProcessingActionMessage)
|
||||
expect(response).to.be.eq(dummyError)
|
||||
})
|
||||
})
|
||||
@@ -412,15 +426,17 @@ describe('actions service', () => {
|
||||
describe('nested action', async function () {
|
||||
const sandbox = sinon.createSandbox()
|
||||
|
||||
before(async function () {
|
||||
const extensionItem = await this.itemManager.findItem(this.extensionItemUuid)
|
||||
this.nestedAction = extensionItem.actions.filter((action) => action.verb === 'nested')[0]
|
||||
})
|
||||
let nestedAction
|
||||
let actionsManagerRunAction
|
||||
let httpServiceRunHttp
|
||||
let actionResponse
|
||||
|
||||
beforeEach(async function () {
|
||||
this.actionsManagerRunAction = sandbox.spy(this.actionsManager, 'runAction')
|
||||
this.httpServiceRunHttp = sandbox.spy(this.actionsManager.httpService, 'runHttp')
|
||||
this.actionResponse = await this.actionsManager.runAction(this.nestedAction)
|
||||
const extensionItem = await itemManager.findItem(extensionItemUuid)
|
||||
nestedAction = extensionItem.actions.filter((action) => action.verb === 'nested')[0]
|
||||
actionsManagerRunAction = sandbox.spy(actionsManager, 'runAction')
|
||||
httpServiceRunHttp = sandbox.spy(actionsManager.httpService, 'runHttp')
|
||||
actionResponse = await actionsManager.runAction(nestedAction)
|
||||
})
|
||||
|
||||
afterEach(async function () {
|
||||
@@ -428,15 +444,15 @@ describe('actions service', () => {
|
||||
})
|
||||
|
||||
it('should return undefined', async function () {
|
||||
expect(this.actionResponse).to.be.undefined
|
||||
expect(actionResponse).to.be.undefined
|
||||
})
|
||||
|
||||
it('should call runAction once', async function () {
|
||||
sandbox.assert.calledOnce(this.actionsManagerRunAction)
|
||||
sandbox.assert.calledOnce(actionsManagerRunAction)
|
||||
})
|
||||
|
||||
it('should not make any http requests', async function () {
|
||||
sandbox.assert.notCalled(this.httpServiceRunHttp)
|
||||
sandbox.assert.notCalled(httpServiceRunHttp)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,23 +1,32 @@
|
||||
/* eslint-disable no-unused-expressions */
|
||||
/* eslint-disable no-undef */
|
||||
import WebDeviceInterface from './lib/web_device_interface.js'
|
||||
import * as Factory from './lib/factory.js'
|
||||
|
||||
chai.use(chaiAsPromised)
|
||||
const expect = chai.expect
|
||||
|
||||
describe('application group', function () {
|
||||
const globalDevice = new WebDeviceInterface(setTimeout.bind(window), setInterval.bind(window))
|
||||
let device
|
||||
let group
|
||||
|
||||
beforeEach(async function () {
|
||||
localStorage.clear()
|
||||
|
||||
device = new WebDeviceInterface(setTimeout.bind(window), setInterval.bind(window))
|
||||
group = new SNApplicationGroup(device)
|
||||
})
|
||||
|
||||
afterEach(async function () {
|
||||
if (group.primaryApplication) {
|
||||
await Factory.safeDeinit(group.primaryApplication)
|
||||
}
|
||||
device.deinit()
|
||||
|
||||
localStorage.clear()
|
||||
|
||||
group = undefined
|
||||
})
|
||||
|
||||
it('initializing a group should result with primary application', async function () {
|
||||
const group = new SNApplicationGroup(globalDevice)
|
||||
await group.initialize({
|
||||
applicationCreator: (descriptor, deviceInterface) => {
|
||||
return Factory.createApplicationWithFakeCrypto(descriptor.identifier, deviceInterface)
|
||||
@@ -25,12 +34,9 @@ describe('application group', function () {
|
||||
})
|
||||
expect(group.primaryApplication).to.be.ok
|
||||
expect(group.primaryApplication.identifier).to.be.ok
|
||||
|
||||
await Factory.safeDeinit(group.primaryApplication)
|
||||
})
|
||||
|
||||
it('initializing a group should result with proper descriptor setup', async function () {
|
||||
const group = new SNApplicationGroup(globalDevice)
|
||||
await group.initialize({
|
||||
applicationCreator: (descriptor, deviceInterface) => {
|
||||
return Factory.createApplicationWithFakeCrypto(descriptor.identifier, deviceInterface)
|
||||
@@ -38,12 +44,9 @@ describe('application group', function () {
|
||||
})
|
||||
const identifier = group.primaryApplication.identifier
|
||||
expect(group.descriptorRecord[identifier].identifier).to.equal(identifier)
|
||||
|
||||
await Factory.safeDeinit(group.primaryApplication)
|
||||
})
|
||||
|
||||
it('should persist descriptor record after changes', async function () {
|
||||
const group = new SNApplicationGroup(globalDevice)
|
||||
await group.initialize({
|
||||
applicationCreator: (descriptor, device) => {
|
||||
return Factory.createInitAppWithFakeCryptoWithOptions({
|
||||
@@ -59,14 +62,13 @@ describe('application group', function () {
|
||||
expect(descriptorRecord[identifier].primary).to.equal(true)
|
||||
|
||||
await group.unloadCurrentAndCreateNewDescriptor()
|
||||
const descriptorRecord2 = await globalDevice.getJsonParsedRawStorageValue(RawStorageKey.DescriptorRecord)
|
||||
const descriptorRecord2 = await device.getJsonParsedRawStorageValue(RawStorageKey.DescriptorRecord)
|
||||
expect(Object.keys(descriptorRecord2).length).to.equal(2)
|
||||
|
||||
expect(descriptorRecord2[identifier].primary).to.equal(false)
|
||||
})
|
||||
|
||||
it('adding new application should incrememnt total descriptor count', async function () {
|
||||
const group = new SNApplicationGroup(globalDevice)
|
||||
await group.initialize({
|
||||
applicationCreator: (descriptor, device) => {
|
||||
return Factory.createInitAppWithFakeCryptoWithOptions({
|
||||
@@ -82,7 +84,6 @@ describe('application group', function () {
|
||||
})
|
||||
|
||||
it('should be notified when application changes', async function () {
|
||||
const group = new SNApplicationGroup(globalDevice)
|
||||
let notifyCount = 0
|
||||
const expectedCount = 2
|
||||
// eslint-disable-next-line no-async-promise-executor
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
/* eslint-disable no-unused-expressions */
|
||||
/* eslint-disable no-undef */
|
||||
import { BaseItemCounts } from './lib/BaseItemCounts.js'
|
||||
import * as Factory from './lib/factory.js'
|
||||
|
||||
chai.use(chaiAsPromised)
|
||||
const expect = chai.expect
|
||||
|
||||
@@ -133,6 +132,12 @@ describe('application instances', () => {
|
||||
deinit = sinon.spy(testSNApp, 'deinit')
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
await Factory.safeDeinit(testSNApp)
|
||||
localStorage.clear()
|
||||
sinon.restore()
|
||||
})
|
||||
|
||||
it('shows confirmation dialog when there are unsaved changes', async () => {
|
||||
await testSNApp.mutator.setItemDirty(testNote1)
|
||||
await testSNApp.user.signOut()
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
/* eslint-disable no-unused-expressions */
|
||||
/* eslint-disable no-undef */
|
||||
import { BaseItemCounts } from './lib/BaseItemCounts.js'
|
||||
import * as Factory from './lib/factory.js'
|
||||
|
||||
chai.use(chaiAsPromised)
|
||||
const expect = chai.expect
|
||||
|
||||
describe('auth fringe cases', () => {
|
||||
const createContext = async () => {
|
||||
let context
|
||||
|
||||
beforeEach(async function () {
|
||||
localStorage.clear()
|
||||
const application = await Factory.createInitAppWithFakeCrypto()
|
||||
return {
|
||||
context = {
|
||||
expectedItemCount: BaseItemCounts.DefaultItems,
|
||||
application: application,
|
||||
email: UuidGenerator.GenerateUuid(),
|
||||
@@ -17,10 +19,6 @@ describe('auth fringe cases', () => {
|
||||
await Factory.safeDeinit(application)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
beforeEach(async function () {
|
||||
localStorage.clear()
|
||||
})
|
||||
|
||||
afterEach(async function () {
|
||||
@@ -40,7 +38,6 @@ describe('auth fringe cases', () => {
|
||||
|
||||
describe('localStorage improperly cleared with 1 item', function () {
|
||||
it('item should be errored', async function () {
|
||||
const context = await createContext()
|
||||
await context.application.register(context.email, context.password)
|
||||
const note = await Factory.createSyncedNote(context.application)
|
||||
clearApplicationLocalStorageOfNonItems()
|
||||
@@ -55,7 +52,6 @@ describe('auth fringe cases', () => {
|
||||
})
|
||||
|
||||
it('signing in again should decrypt item', async function () {
|
||||
const context = await createContext()
|
||||
await context.application.register(context.email, context.password)
|
||||
const note = await Factory.createSyncedNote(context.application)
|
||||
clearApplicationLocalStorageOfNonItems()
|
||||
@@ -76,7 +72,6 @@ describe('auth fringe cases', () => {
|
||||
|
||||
describe('having offline item matching remote item uuid', function () {
|
||||
it('offline item should not overwrite recently updated server item and conflict should be created', async function () {
|
||||
const context = await createContext()
|
||||
await context.application.register(context.email, context.password)
|
||||
|
||||
const staleText = 'stale text'
|
||||
|
||||
@@ -13,11 +13,7 @@ describe('basic auth', function () {
|
||||
}
|
||||
|
||||
let context
|
||||
|
||||
afterEach(async function () {
|
||||
await context.deinit()
|
||||
localStorage.clear()
|
||||
})
|
||||
let expectedItemCount
|
||||
|
||||
beforeEach(async function () {
|
||||
localStorage.clear()
|
||||
@@ -26,12 +22,23 @@ describe('basic auth', function () {
|
||||
|
||||
await context.launch()
|
||||
|
||||
this.expectedItemCount = BaseItemCounts.DefaultItemsWithAccount
|
||||
expectedItemCount = BaseItemCounts.DefaultItemsWithAccount
|
||||
})
|
||||
|
||||
afterEach(async function () {
|
||||
await context.deinit()
|
||||
|
||||
localStorage.clear()
|
||||
|
||||
context = undefined
|
||||
|
||||
sinon.restore()
|
||||
})
|
||||
|
||||
it('successfully register new account', async function () {
|
||||
const response = await context.register()
|
||||
expect(response).to.be.ok
|
||||
|
||||
expect(await context.application.encryption.getRootKey()).to.be.ok
|
||||
})
|
||||
|
||||
@@ -58,7 +65,7 @@ describe('basic auth', function () {
|
||||
|
||||
expect(await context.application.encryption.getRootKey()).to.be.ok
|
||||
|
||||
context.application = await Factory.signOutApplicationAndReturnNew(context.application)
|
||||
await context.signout()
|
||||
|
||||
expect(await context.application.encryption.getRootKey()).to.not.be.ok
|
||||
expect(context.application.encryption.rootKeyManager.getKeyMode()).to.equal(KeyMode.RootKeyNone)
|
||||
@@ -233,7 +240,7 @@ describe('basic auth', function () {
|
||||
|
||||
await specContext.launch()
|
||||
await specContext.register()
|
||||
await specContext.signout()
|
||||
await specContext.deinit()
|
||||
|
||||
specContext = await Factory.createAppContextWithFakeCrypto(Math.random(), uppercase, password)
|
||||
|
||||
@@ -255,10 +262,11 @@ describe('basic auth', function () {
|
||||
* with an uppercase email
|
||||
*/
|
||||
const password = UuidGenerator.GenerateUuid()
|
||||
|
||||
let specContext = await Factory.createAppContextWithFakeCrypto(Math.random(), nospace, password)
|
||||
await specContext.launch()
|
||||
await specContext.register()
|
||||
await specContext.signout()
|
||||
await specContext.deinit()
|
||||
|
||||
specContext = await Factory.createAppContextWithFakeCrypto(Math.random(), withspace, password)
|
||||
await specContext.launch()
|
||||
@@ -272,7 +280,8 @@ describe('basic auth', function () {
|
||||
|
||||
it('fails login with wrong password', async function () {
|
||||
await context.register()
|
||||
context.application = await Factory.signOutApplicationAndReturnNew(context.application)
|
||||
await context.signout()
|
||||
|
||||
const response = await context.application.signIn(
|
||||
context.email,
|
||||
'wrongpassword',
|
||||
@@ -299,7 +308,8 @@ describe('basic auth', function () {
|
||||
expect(response.error).to.be.ok
|
||||
|
||||
/** Ensure we can still log in */
|
||||
context.application = await Factory.signOutAndBackIn(context.application, context.email, context.password)
|
||||
await context.signout()
|
||||
await context.signIn()
|
||||
}).timeout(20000)
|
||||
|
||||
it('registering for new account and completing first after download sync should not put us out of sync', async function () {
|
||||
@@ -339,23 +349,23 @@ describe('basic auth', function () {
|
||||
|
||||
const noteCount = 5
|
||||
await Factory.createManyMappedNotes(context.application, noteCount)
|
||||
this.expectedItemCount += noteCount
|
||||
expectedItemCount += noteCount
|
||||
|
||||
await context.sync()
|
||||
expect(context.application.items.items.length).to.equal(this.expectedItemCount)
|
||||
expect(context.application.items.items.length).to.equal(expectedItemCount)
|
||||
|
||||
const newPassword = 'newpassword'
|
||||
const response = await context.application.changePassword(context.password, newPassword)
|
||||
expect(response.error).to.not.be.ok
|
||||
|
||||
this.expectedItemCount += ['new items key'].length
|
||||
expect(context.application.items.items.length).to.equal(this.expectedItemCount)
|
||||
expectedItemCount += ['new items key'].length
|
||||
expect(context.application.items.items.length).to.equal(expectedItemCount)
|
||||
expect(context.application.payloads.invalidPayloads.length).to.equal(0)
|
||||
|
||||
await context.application.sync.markAllItemsAsNeedingSyncAndPersist()
|
||||
await context.sync(syncOptions)
|
||||
|
||||
expect(context.application.items.items.length).to.equal(this.expectedItemCount)
|
||||
expect(context.application.items.items.length).to.equal(expectedItemCount)
|
||||
}).timeout(40000)
|
||||
|
||||
it('should sign into account after changing password', async function () {
|
||||
@@ -365,7 +375,7 @@ describe('basic auth', function () {
|
||||
const response = await context.application.changePassword(context.password, newPassword)
|
||||
expect(response.error).to.not.be.ok
|
||||
|
||||
this.expectedItemCount += ['new items key'].length
|
||||
expectedItemCount += ['new items key'].length
|
||||
|
||||
await context.signout()
|
||||
|
||||
@@ -382,7 +392,7 @@ describe('basic auth', function () {
|
||||
expect(signinResponse.data.error).to.not.be.ok
|
||||
expect(await context.application.encryption.getRootKey()).to.be.ok
|
||||
|
||||
expect(context.application.items.items.length).to.equal(this.expectedItemCount)
|
||||
expect(context.application.items.items.length).to.equal(expectedItemCount)
|
||||
expect(context.application.payloads.invalidPayloads.length).to.equal(0)
|
||||
})
|
||||
|
||||
@@ -393,7 +403,7 @@ describe('basic auth', function () {
|
||||
|
||||
const noteCount = 3
|
||||
await Factory.createManyMappedNotes(context.application, noteCount)
|
||||
this.expectedItemCount += noteCount
|
||||
expectedItemCount += noteCount
|
||||
|
||||
await context.sync()
|
||||
|
||||
@@ -401,9 +411,9 @@ describe('basic auth', function () {
|
||||
const response = await context.application.changePassword(context.password, newPassword)
|
||||
expect(response.error).to.not.be.ok
|
||||
|
||||
this.expectedItemCount += ['new items key'].length
|
||||
expectedItemCount += ['new items key'].length
|
||||
|
||||
expect(context.application.items.items.length).to.equal(this.expectedItemCount)
|
||||
expect(context.application.items.items.length).to.equal(expectedItemCount)
|
||||
})
|
||||
|
||||
it('changes password many times', async function () {
|
||||
@@ -411,7 +421,7 @@ describe('basic auth', function () {
|
||||
|
||||
const noteCount = 10
|
||||
await Factory.createManyMappedNotes(context.application, noteCount)
|
||||
this.expectedItemCount += noteCount
|
||||
expectedItemCount += noteCount
|
||||
await context.application.sync.sync(syncOptions)
|
||||
|
||||
const numTimesToChangePw = 3
|
||||
@@ -422,12 +432,12 @@ describe('basic auth', function () {
|
||||
await context.application.changePassword(currentPassword, newPassword)
|
||||
|
||||
/** New items key */
|
||||
this.expectedItemCount++
|
||||
expectedItemCount++
|
||||
|
||||
currentPassword = newPassword
|
||||
newPassword = Factory.randomString()
|
||||
|
||||
expect(context.application.items.items.length).to.equal(this.expectedItemCount)
|
||||
expect(context.application.items.items.length).to.equal(expectedItemCount)
|
||||
expect(context.application.payloads.invalidPayloads.length).to.equal(0)
|
||||
|
||||
await context.application.sync.markAllItemsAsNeedingSyncAndPersist()
|
||||
@@ -460,7 +470,7 @@ describe('basic auth', function () {
|
||||
email: context.email,
|
||||
password: context.password,
|
||||
})
|
||||
context.application = await Factory.signOutApplicationAndReturnNew(context.application)
|
||||
await context.signout()
|
||||
const performSignIn = sinon.spy(context.application.sessions, 'performSignIn')
|
||||
await context.application.signIn(context.email, 'wrong password', undefined, undefined, undefined, true)
|
||||
expect(performSignIn.callCount).to.equal(1)
|
||||
|
||||
@@ -1,41 +1,38 @@
|
||||
/* eslint-disable no-unused-expressions */
|
||||
/* eslint-disable no-undef */
|
||||
import { BaseItemCounts } from './lib/BaseItemCounts.js'
|
||||
import * as Factory from './lib/factory.js'
|
||||
|
||||
chai.use(chaiAsPromised)
|
||||
const expect = chai.expect
|
||||
|
||||
describe('backups', function () {
|
||||
before(function () {
|
||||
localStorage.clear()
|
||||
})
|
||||
|
||||
after(function () {
|
||||
localStorage.clear()
|
||||
})
|
||||
let application
|
||||
let email
|
||||
let password
|
||||
|
||||
beforeEach(async function () {
|
||||
this.application = await Factory.createInitAppWithFakeCrypto()
|
||||
this.email = UuidGenerator.GenerateUuid()
|
||||
this.password = UuidGenerator.GenerateUuid()
|
||||
localStorage.clear()
|
||||
application = await Factory.createInitAppWithFakeCrypto()
|
||||
email = UuidGenerator.GenerateUuid()
|
||||
password = UuidGenerator.GenerateUuid()
|
||||
})
|
||||
|
||||
afterEach(async function () {
|
||||
await Factory.safeDeinit(this.application)
|
||||
this.application = null
|
||||
await Factory.safeDeinit(application)
|
||||
application = null
|
||||
localStorage.clear()
|
||||
})
|
||||
|
||||
it('backup file should have a version number', async function () {
|
||||
let data = await this.application.createDecryptedBackupFile()
|
||||
expect(data.version).to.equal(this.application.encryption.getLatestVersion())
|
||||
await this.application.addPasscode('passcode')
|
||||
data = await this.application.createEncryptedBackupFileForAutomatedDesktopBackups()
|
||||
expect(data.version).to.equal(this.application.encryption.getLatestVersion())
|
||||
let data = await application.createDecryptedBackupFile()
|
||||
expect(data.version).to.equal(application.encryption.getLatestVersion())
|
||||
await application.addPasscode('passcode')
|
||||
data = await application.createEncryptedBackupFileForAutomatedDesktopBackups()
|
||||
expect(data.version).to.equal(application.encryption.getLatestVersion())
|
||||
})
|
||||
|
||||
it('no passcode + no account backup file should have correct number of items', async function () {
|
||||
await Promise.all([Factory.createSyncedNote(this.application), Factory.createSyncedNote(this.application)])
|
||||
const data = await this.application.createDecryptedBackupFile()
|
||||
await Promise.all([Factory.createSyncedNote(application), Factory.createSyncedNote(application)])
|
||||
const data = await application.createDecryptedBackupFile()
|
||||
const offsetForNewItems = 2
|
||||
const offsetForNoItemsKey = -1
|
||||
expect(data.items.length).to.equal(BaseItemCounts.DefaultItems + offsetForNewItems + offsetForNoItemsKey)
|
||||
@@ -43,68 +40,68 @@ describe('backups', function () {
|
||||
|
||||
it('passcode + no account backup file should have correct number of items', async function () {
|
||||
const passcode = 'passcode'
|
||||
await this.application.addPasscode(passcode)
|
||||
await Promise.all([Factory.createSyncedNote(this.application), Factory.createSyncedNote(this.application)])
|
||||
await application.addPasscode(passcode)
|
||||
await Promise.all([Factory.createSyncedNote(application), Factory.createSyncedNote(application)])
|
||||
|
||||
// Encrypted backup without authorization
|
||||
const encryptedData = await this.application.createEncryptedBackupFileForAutomatedDesktopBackups()
|
||||
const encryptedData = await application.createEncryptedBackupFileForAutomatedDesktopBackups()
|
||||
expect(encryptedData.items.length).to.equal(BaseItemCounts.DefaultItems + 2)
|
||||
|
||||
// Encrypted backup with authorization
|
||||
Factory.handlePasswordChallenges(this.application, passcode)
|
||||
const authorizedEncryptedData = await this.application.createEncryptedBackupFile()
|
||||
Factory.handlePasswordChallenges(application, passcode)
|
||||
const authorizedEncryptedData = await application.createEncryptedBackupFile()
|
||||
expect(authorizedEncryptedData.items.length).to.equal(BaseItemCounts.DefaultItems + 2)
|
||||
})
|
||||
|
||||
it('no passcode + account backup file should have correct number of items', async function () {
|
||||
await Factory.registerUserToApplication({
|
||||
application: this.application,
|
||||
email: this.email,
|
||||
password: this.password,
|
||||
application: application,
|
||||
email: email,
|
||||
password: password,
|
||||
})
|
||||
|
||||
await Promise.all([Factory.createSyncedNote(this.application), Factory.createSyncedNote(this.application)])
|
||||
await Promise.all([Factory.createSyncedNote(application), Factory.createSyncedNote(application)])
|
||||
|
||||
// Encrypted backup without authorization
|
||||
const encryptedData = await this.application.createEncryptedBackupFileForAutomatedDesktopBackups()
|
||||
const encryptedData = await application.createEncryptedBackupFileForAutomatedDesktopBackups()
|
||||
expect(encryptedData.items.length).to.equal(BaseItemCounts.DefaultItemsWithAccount + 2)
|
||||
|
||||
Factory.handlePasswordChallenges(this.application, this.password)
|
||||
Factory.handlePasswordChallenges(application, password)
|
||||
|
||||
// Decrypted backup
|
||||
const decryptedData = await this.application.createDecryptedBackupFile()
|
||||
const decryptedData = await application.createDecryptedBackupFile()
|
||||
expect(decryptedData.items.length).to.equal(BaseItemCounts.DefaultItemsWithAccountWithoutItemsKey + 2)
|
||||
|
||||
// Encrypted backup with authorization
|
||||
const authorizedEncryptedData = await this.application.createEncryptedBackupFile()
|
||||
const authorizedEncryptedData = await application.createEncryptedBackupFile()
|
||||
expect(authorizedEncryptedData.items.length).to.equal(BaseItemCounts.DefaultItemsWithAccount + 2)
|
||||
})
|
||||
|
||||
it('passcode + account backup file should have correct number of items', async function () {
|
||||
const passcode = 'passcode'
|
||||
await this.application.register(this.email, this.password)
|
||||
Factory.handlePasswordChallenges(this.application, this.password)
|
||||
await this.application.addPasscode(passcode)
|
||||
await Promise.all([Factory.createSyncedNote(this.application), Factory.createSyncedNote(this.application)])
|
||||
await application.register(email, password)
|
||||
Factory.handlePasswordChallenges(application, password)
|
||||
await application.addPasscode(passcode)
|
||||
await Promise.all([Factory.createSyncedNote(application), Factory.createSyncedNote(application)])
|
||||
|
||||
// Encrypted backup without authorization
|
||||
const encryptedData = await this.application.createEncryptedBackupFileForAutomatedDesktopBackups()
|
||||
const encryptedData = await application.createEncryptedBackupFileForAutomatedDesktopBackups()
|
||||
expect(encryptedData.items.length).to.equal(BaseItemCounts.DefaultItemsWithAccount + 2)
|
||||
|
||||
Factory.handlePasswordChallenges(this.application, passcode)
|
||||
Factory.handlePasswordChallenges(application, passcode)
|
||||
|
||||
// Decrypted backup
|
||||
const decryptedData = await this.application.createDecryptedBackupFile()
|
||||
const decryptedData = await application.createDecryptedBackupFile()
|
||||
expect(decryptedData.items.length).to.equal(BaseItemCounts.DefaultItemsWithAccountWithoutItemsKey + 2)
|
||||
|
||||
// Encrypted backup with authorization
|
||||
const authorizedEncryptedData = await this.application.createEncryptedBackupFileForAutomatedDesktopBackups()
|
||||
const authorizedEncryptedData = await application.createEncryptedBackupFileForAutomatedDesktopBackups()
|
||||
expect(authorizedEncryptedData.items.length).to.equal(BaseItemCounts.DefaultItemsWithAccount + 2)
|
||||
}).timeout(10000)
|
||||
|
||||
it('backup file item should have correct fields', async function () {
|
||||
await Factory.createSyncedNote(this.application)
|
||||
let backupData = await this.application.createDecryptedBackupFile()
|
||||
await Factory.createSyncedNote(application)
|
||||
let backupData = await application.createDecryptedBackupFile()
|
||||
let rawItem = backupData.items.find((i) => i.content_type === ContentType.TYPES.Note)
|
||||
|
||||
expect(rawItem.fields).to.not.be.ok
|
||||
@@ -118,12 +115,12 @@ describe('backups', function () {
|
||||
expect(rawItem.updated_at).to.be.ok
|
||||
|
||||
await Factory.registerUserToApplication({
|
||||
application: this.application,
|
||||
email: this.email,
|
||||
password: this.password,
|
||||
application: application,
|
||||
email: email,
|
||||
password: password,
|
||||
})
|
||||
|
||||
backupData = await this.application.createEncryptedBackupFileForAutomatedDesktopBackups()
|
||||
backupData = await application.createEncryptedBackupFileForAutomatedDesktopBackups()
|
||||
rawItem = backupData.items.find((i) => i.content_type === ContentType.TYPES.Note)
|
||||
|
||||
expect(rawItem.fields).to.not.be.ok
|
||||
@@ -138,11 +135,11 @@ describe('backups', function () {
|
||||
})
|
||||
|
||||
it('downloading backup if item is error decrypting should succeed', async function () {
|
||||
await Factory.createSyncedNote(this.application)
|
||||
await Factory.createSyncedNote(application)
|
||||
|
||||
const note = await Factory.createSyncedNote(this.application)
|
||||
const note = await Factory.createSyncedNote(application)
|
||||
|
||||
const encrypted = await this.application.encryption.encryptSplitSingle({
|
||||
const encrypted = await application.encryption.encryptSplitSingle({
|
||||
usesItemsKeyWithKeyLookup: {
|
||||
items: [note.payload],
|
||||
},
|
||||
@@ -152,19 +149,19 @@ describe('backups', function () {
|
||||
errorDecrypting: true,
|
||||
})
|
||||
|
||||
await this.application.payloads.emitPayload(errored)
|
||||
await application.payloads.emitPayload(errored)
|
||||
|
||||
const erroredItem = this.application.items.findAnyItem(errored.uuid)
|
||||
const erroredItem = application.items.findAnyItem(errored.uuid)
|
||||
|
||||
expect(erroredItem.errorDecrypting).to.equal(true)
|
||||
|
||||
const backupData = await this.application.createDecryptedBackupFile()
|
||||
const backupData = await application.createDecryptedBackupFile()
|
||||
|
||||
expect(backupData.items.length).to.equal(BaseItemCounts.DefaultItemsNoAccounNoItemsKey + 2)
|
||||
})
|
||||
|
||||
it('decrypted backup file should not have keyParams', async function () {
|
||||
const backup = await this.application.createDecryptedBackupFile()
|
||||
const backup = await application.createDecryptedBackupFile()
|
||||
expect(backup).to.not.haveOwnProperty('keyParams')
|
||||
})
|
||||
|
||||
@@ -187,31 +184,31 @@ describe('backups', function () {
|
||||
})
|
||||
|
||||
it('encrypted backup file should have keyParams', async function () {
|
||||
await this.application.addPasscode('passcode')
|
||||
const backup = await this.application.createEncryptedBackupFileForAutomatedDesktopBackups()
|
||||
await application.addPasscode('passcode')
|
||||
const backup = await application.createEncryptedBackupFileForAutomatedDesktopBackups()
|
||||
expect(backup).to.haveOwnProperty('keyParams')
|
||||
})
|
||||
|
||||
it('decrypted backup file should not have itemsKeys', async function () {
|
||||
const backup = await this.application.createDecryptedBackupFile()
|
||||
const backup = await application.createDecryptedBackupFile()
|
||||
expect(backup.items.some((item) => item.content_type === ContentType.TYPES.ItemsKey)).to.be.false
|
||||
})
|
||||
|
||||
it('encrypted backup file should have itemsKeys', async function () {
|
||||
await this.application.addPasscode('passcode')
|
||||
const backup = await this.application.createEncryptedBackupFileForAutomatedDesktopBackups()
|
||||
await application.addPasscode('passcode')
|
||||
const backup = await application.createEncryptedBackupFileForAutomatedDesktopBackups()
|
||||
expect(backup.items.some((item) => item.content_type === ContentType.TYPES.ItemsKey)).to.be.true
|
||||
})
|
||||
|
||||
it('backup file with no account and no passcode should be decrypted', async function () {
|
||||
const note = await Factory.createSyncedNote(this.application)
|
||||
const backup = await this.application.createDecryptedBackupFile()
|
||||
const note = await Factory.createSyncedNote(application)
|
||||
const backup = await application.createDecryptedBackupFile()
|
||||
expect(backup).to.not.haveOwnProperty('keyParams')
|
||||
expect(backup.items.some((item) => item.content_type === ContentType.TYPES.ItemsKey)).to.be.false
|
||||
expect(backup.items.find((item) => item.content_type === ContentType.TYPES.Note).uuid).to.equal(note.uuid)
|
||||
let error
|
||||
try {
|
||||
await this.application.createEncryptedBackupFileForAutomatedDesktopBackups()
|
||||
await application.createEncryptedBackupFileForAutomatedDesktopBackups()
|
||||
} catch (e) {
|
||||
error = e
|
||||
}
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
/* eslint-disable no-unused-expressions */
|
||||
/* eslint-disable no-undef */
|
||||
import * as Factory from './lib/factory.js'
|
||||
import { createRelatedNoteTagPairPayload } from './lib/Items.js'
|
||||
|
||||
chai.use(chaiAsPromised)
|
||||
const expect = chai.expect
|
||||
|
||||
describe('payload collections', () => {
|
||||
before(async () => {
|
||||
beforeEach(async () => {
|
||||
localStorage.clear()
|
||||
})
|
||||
|
||||
after(async () => {
|
||||
afterEach(async () => {
|
||||
localStorage.clear()
|
||||
})
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
/* eslint-disable no-unused-expressions */
|
||||
/* eslint-disable no-undef */
|
||||
import * as Factory from './lib/factory.js'
|
||||
|
||||
chai.use(chaiAsPromised)
|
||||
const expect = chai.expect
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* eslint-disable no-undef */
|
||||
import * as Factory from './lib/factory.js'
|
||||
chai.use(chaiAsPromised)
|
||||
|
||||
const expect = chai.expect
|
||||
|
||||
describe('features', () => {
|
||||
@@ -9,6 +9,7 @@ describe('features', () => {
|
||||
let password
|
||||
|
||||
beforeEach(async function () {
|
||||
localStorage.clear()
|
||||
application = await Factory.createInitAppWithFakeCrypto()
|
||||
|
||||
sinon.spy(application.mutator, 'createItem')
|
||||
@@ -28,6 +29,8 @@ describe('features', () => {
|
||||
afterEach(async function () {
|
||||
Factory.safeDeinit(application)
|
||||
sinon.restore()
|
||||
localStorage.clear()
|
||||
application = undefined
|
||||
})
|
||||
|
||||
describe('new user roles received on api response meta', () => {
|
||||
|
||||
@@ -17,6 +17,14 @@ describe('files', function () {
|
||||
localStorage.clear()
|
||||
})
|
||||
|
||||
afterEach(async function () {
|
||||
await Factory.safeDeinit(application)
|
||||
localStorage.clear()
|
||||
|
||||
application = undefined
|
||||
context = undefined
|
||||
})
|
||||
|
||||
const setup = async ({ fakeCrypto, subscription = true }) => {
|
||||
if (fakeCrypto) {
|
||||
context = await Factory.createAppContextWithFakeCrypto()
|
||||
@@ -41,11 +49,6 @@ describe('files', function () {
|
||||
}
|
||||
}
|
||||
|
||||
afterEach(async function () {
|
||||
await Factory.safeDeinit(application)
|
||||
localStorage.clear()
|
||||
})
|
||||
|
||||
it('should create valet token from server', async function () {
|
||||
await setup({ fakeCrypto: true, subscription: true })
|
||||
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
/* eslint-disable no-unused-expressions */
|
||||
/* eslint-disable no-undef */
|
||||
import * as Factory from './lib/factory.js'
|
||||
import { createNoteParams } from './lib/Items.js'
|
||||
|
||||
chai.use(chaiAsPromised)
|
||||
const expect = chai.expect
|
||||
|
||||
describe('history manager', () => {
|
||||
const largeCharacterChange = 25
|
||||
|
||||
let application, history, email, password
|
||||
|
||||
const syncOptions = {
|
||||
checkIntegrity: true,
|
||||
awaitAll: true,
|
||||
@@ -23,15 +24,15 @@ describe('history manager', () => {
|
||||
|
||||
describe('session', function () {
|
||||
beforeEach(async function () {
|
||||
this.application = await Factory.createInitAppWithFakeCrypto()
|
||||
this.history = this.application.dependencies.get(TYPES.HistoryManager)
|
||||
this.payloadManager = this.application.payloads
|
||||
application = await Factory.createInitAppWithFakeCrypto()
|
||||
history = application.dependencies.get(TYPES.HistoryManager)
|
||||
|
||||
/** Automatically optimize after every revision by setting this to 0 */
|
||||
this.history.itemRevisionThreshold = 0
|
||||
history.itemRevisionThreshold = 0
|
||||
})
|
||||
|
||||
afterEach(async function () {
|
||||
await Factory.safeDeinit(this.application)
|
||||
await Factory.safeDeinit(application)
|
||||
})
|
||||
|
||||
async function setTextAndSync(application, item, text) {
|
||||
@@ -53,15 +54,15 @@ describe('history manager', () => {
|
||||
}
|
||||
|
||||
it('create basic history entries 1', async function () {
|
||||
const item = await Factory.createSyncedNote(this.application)
|
||||
expect(this.history.sessionHistoryForItem(item).length).to.equal(0)
|
||||
const item = await Factory.createSyncedNote(application)
|
||||
expect(history.sessionHistoryForItem(item).length).to.equal(0)
|
||||
|
||||
/** Sync with same contents, should not create new entry */
|
||||
await Factory.markDirtyAndSyncItem(this.application, item)
|
||||
expect(this.history.sessionHistoryForItem(item).length).to.equal(0)
|
||||
await Factory.markDirtyAndSyncItem(application, item)
|
||||
expect(history.sessionHistoryForItem(item).length).to.equal(0)
|
||||
|
||||
/** Sync with different contents, should create new entry */
|
||||
await this.application.changeAndSaveItem.execute(
|
||||
await application.changeAndSaveItem.execute(
|
||||
item,
|
||||
(mutator) => {
|
||||
mutator.title = Math.random()
|
||||
@@ -70,12 +71,12 @@ describe('history manager', () => {
|
||||
undefined,
|
||||
syncOptions,
|
||||
)
|
||||
expect(this.history.sessionHistoryForItem(item).length).to.equal(1)
|
||||
expect(history.sessionHistoryForItem(item).length).to.equal(1)
|
||||
})
|
||||
|
||||
it('first change should create revision with previous value', async function () {
|
||||
const identifier = this.application.identifier
|
||||
const item = await Factory.createSyncedNote(this.application)
|
||||
const identifier = application.identifier
|
||||
const item = await Factory.createSyncedNote(application)
|
||||
|
||||
/** Simulate loading new application session */
|
||||
const context = await Factory.createAppContext({ identifier })
|
||||
@@ -119,53 +120,41 @@ describe('history manager', () => {
|
||||
})
|
||||
|
||||
it('should optimize basic entries', async function () {
|
||||
let item = await Factory.createSyncedNote(this.application)
|
||||
let item = await Factory.createSyncedNote(application)
|
||||
/**
|
||||
* Add 1 character. This typically would be discarded as an entry, but it
|
||||
* won't here because it's the first change, which we want to keep.
|
||||
*/
|
||||
await setTextAndSync(this.application, item, item.content.text + '1')
|
||||
expect(this.history.sessionHistoryForItem(item).length).to.equal(1)
|
||||
await setTextAndSync(application, item, item.content.text + '1')
|
||||
expect(history.sessionHistoryForItem(item).length).to.equal(1)
|
||||
|
||||
/**
|
||||
* Changing it by one character should keep this entry,
|
||||
* since it's now the last (and will keep the first)
|
||||
*/
|
||||
item = await setTextAndSync(this.application, item, item.content.text + '2')
|
||||
expect(this.history.sessionHistoryForItem(item).length).to.equal(2)
|
||||
item = await setTextAndSync(application, item, item.content.text + '2')
|
||||
expect(history.sessionHistoryForItem(item).length).to.equal(2)
|
||||
/**
|
||||
* Change it over the largeCharacterChange threshold. It should keep this
|
||||
* revision, but now remove the previous revision, since it's no longer
|
||||
* the last, and is a small change.
|
||||
*/
|
||||
item = await setTextAndSync(
|
||||
this.application,
|
||||
item,
|
||||
item.content.text + Factory.randomString(largeCharacterChange + 1),
|
||||
)
|
||||
expect(this.history.sessionHistoryForItem(item).length).to.equal(2)
|
||||
item = await setTextAndSync(application, item, item.content.text + Factory.randomString(largeCharacterChange + 1))
|
||||
expect(history.sessionHistoryForItem(item).length).to.equal(2)
|
||||
|
||||
item = await setTextAndSync(
|
||||
this.application,
|
||||
item,
|
||||
item.content.text + Factory.randomString(largeCharacterChange + 1),
|
||||
)
|
||||
expect(this.history.sessionHistoryForItem(item).length).to.equal(2)
|
||||
item = await setTextAndSync(application, item, item.content.text + Factory.randomString(largeCharacterChange + 1))
|
||||
expect(history.sessionHistoryForItem(item).length).to.equal(2)
|
||||
/** Delete over threshold text. */
|
||||
item = await setTextAndSync(
|
||||
this.application,
|
||||
item,
|
||||
deleteCharsFromString(item.content.text, largeCharacterChange + 1),
|
||||
)
|
||||
expect(this.history.sessionHistoryForItem(item).length).to.equal(3)
|
||||
item = await setTextAndSync(application, item, deleteCharsFromString(item.content.text, largeCharacterChange + 1))
|
||||
expect(history.sessionHistoryForItem(item).length).to.equal(3)
|
||||
/**
|
||||
* Delete just 1 character. It should now retain the previous revision, as well as the
|
||||
* one previous to that.
|
||||
*/
|
||||
item = await setTextAndSync(this.application, item, deleteCharsFromString(item.content.text, 1))
|
||||
expect(this.history.sessionHistoryForItem(item).length).to.equal(4)
|
||||
item = await setTextAndSync(this.application, item, deleteCharsFromString(item.content.text, 1))
|
||||
expect(this.history.sessionHistoryForItem(item).length).to.equal(5)
|
||||
item = await setTextAndSync(application, item, deleteCharsFromString(item.content.text, 1))
|
||||
expect(history.sessionHistoryForItem(item).length).to.equal(4)
|
||||
item = await setTextAndSync(application, item, deleteCharsFromString(item.content.text, 1))
|
||||
expect(history.sessionHistoryForItem(item).length).to.equal(5)
|
||||
})
|
||||
|
||||
it('should keep the entry right before a large deletion, regardless of its delta', async function () {
|
||||
@@ -174,27 +163,19 @@ describe('history manager', () => {
|
||||
text: Factory.randomString(100),
|
||||
}),
|
||||
)
|
||||
let item = await this.application.mutator.emitItemFromPayload(payload, PayloadEmitSource.LocalChanged)
|
||||
await this.application.mutator.setItemDirty(item)
|
||||
await this.application.sync.sync(syncOptions)
|
||||
let item = await application.mutator.emitItemFromPayload(payload, PayloadEmitSource.LocalChanged)
|
||||
await application.mutator.setItemDirty(item)
|
||||
await application.sync.sync(syncOptions)
|
||||
/** It should keep the first and last by default */
|
||||
item = await setTextAndSync(this.application, item, item.content.text)
|
||||
item = await setTextAndSync(this.application, item, item.content.text + Factory.randomString(1))
|
||||
expect(this.history.sessionHistoryForItem(item).length).to.equal(2)
|
||||
item = await setTextAndSync(
|
||||
this.application,
|
||||
item,
|
||||
deleteCharsFromString(item.content.text, largeCharacterChange + 1),
|
||||
)
|
||||
expect(this.history.sessionHistoryForItem(item).length).to.equal(2)
|
||||
item = await setTextAndSync(this.application, item, item.content.text + Factory.randomString(1))
|
||||
expect(this.history.sessionHistoryForItem(item).length).to.equal(3)
|
||||
item = await setTextAndSync(
|
||||
this.application,
|
||||
item,
|
||||
item.content.text + Factory.randomString(largeCharacterChange + 1),
|
||||
)
|
||||
expect(this.history.sessionHistoryForItem(item).length).to.equal(4)
|
||||
item = await setTextAndSync(application, item, item.content.text)
|
||||
item = await setTextAndSync(application, item, item.content.text + Factory.randomString(1))
|
||||
expect(history.sessionHistoryForItem(item).length).to.equal(2)
|
||||
item = await setTextAndSync(application, item, deleteCharsFromString(item.content.text, largeCharacterChange + 1))
|
||||
expect(history.sessionHistoryForItem(item).length).to.equal(2)
|
||||
item = await setTextAndSync(application, item, item.content.text + Factory.randomString(1))
|
||||
expect(history.sessionHistoryForItem(item).length).to.equal(3)
|
||||
item = await setTextAndSync(application, item, item.content.text + Factory.randomString(largeCharacterChange + 1))
|
||||
expect(history.sessionHistoryForItem(item).length).to.equal(4)
|
||||
})
|
||||
|
||||
it('entries should be ordered from newest to oldest', async function () {
|
||||
@@ -204,32 +185,23 @@ describe('history manager', () => {
|
||||
}),
|
||||
)
|
||||
|
||||
let item = await this.application.mutator.emitItemFromPayload(payload, PayloadEmitSource.LocalChanged)
|
||||
let item = await application.mutator.emitItemFromPayload(payload, PayloadEmitSource.LocalChanged)
|
||||
|
||||
await this.application.mutator.setItemDirty(item)
|
||||
await this.application.sync.sync(syncOptions)
|
||||
await application.mutator.setItemDirty(item)
|
||||
await application.sync.sync(syncOptions)
|
||||
|
||||
item = await setTextAndSync(this.application, item, item.content.text + Factory.randomString(1))
|
||||
item = await setTextAndSync(application, item, item.content.text + Factory.randomString(1))
|
||||
|
||||
item = await setTextAndSync(
|
||||
this.application,
|
||||
item,
|
||||
deleteCharsFromString(item.content.text, largeCharacterChange + 1),
|
||||
)
|
||||
item = await setTextAndSync(application, item, deleteCharsFromString(item.content.text, largeCharacterChange + 1))
|
||||
|
||||
item = await setTextAndSync(this.application, item, item.content.text + Factory.randomString(1))
|
||||
item = await setTextAndSync(application, item, item.content.text + Factory.randomString(1))
|
||||
|
||||
item = await setTextAndSync(
|
||||
this.application,
|
||||
item,
|
||||
item.content.text + Factory.randomString(largeCharacterChange + 1),
|
||||
)
|
||||
item = await setTextAndSync(application, item, item.content.text + Factory.randomString(largeCharacterChange + 1))
|
||||
|
||||
/** First entry should be the latest revision. */
|
||||
const latestRevision = this.history.sessionHistoryForItem(item)[0]
|
||||
const latestRevision = history.sessionHistoryForItem(item)[0]
|
||||
/** Last entry should be the initial revision. */
|
||||
const initialRevision =
|
||||
this.history.sessionHistoryForItem(item)[this.history.sessionHistoryForItem(item).length - 1]
|
||||
const initialRevision = history.sessionHistoryForItem(item)[history.sessionHistoryForItem(item).length - 1]
|
||||
|
||||
expect(latestRevision).to.not.equal(initialRevision)
|
||||
|
||||
@@ -243,9 +215,9 @@ describe('history manager', () => {
|
||||
|
||||
it('unsynced entries should use payload created_at for preview titles', async function () {
|
||||
const payload = Factory.createNotePayload()
|
||||
await this.application.mutator.emitItemFromPayload(payload, PayloadEmitSource.LocalChanged)
|
||||
const item = this.application.items.findItem(payload.uuid)
|
||||
await this.application.changeAndSaveItem.execute(
|
||||
await application.mutator.emitItemFromPayload(payload, PayloadEmitSource.LocalChanged)
|
||||
const item = application.items.findItem(payload.uuid)
|
||||
await application.changeAndSaveItem.execute(
|
||||
item,
|
||||
(mutator) => {
|
||||
mutator.title = Math.random()
|
||||
@@ -254,7 +226,7 @@ describe('history manager', () => {
|
||||
undefined,
|
||||
syncOptions,
|
||||
)
|
||||
const historyItem = this.history.sessionHistoryForItem(item)[0]
|
||||
const historyItem = history.sessionHistoryForItem(item)[0]
|
||||
expect(historyItem.previewTitle()).to.equal(historyItem.payload.created_at.toLocaleString())
|
||||
})
|
||||
})
|
||||
@@ -263,52 +235,54 @@ describe('history manager', () => {
|
||||
this.timeout(Factory.TwentySecondTimeout)
|
||||
|
||||
beforeEach(async function () {
|
||||
this.application = await Factory.createInitAppWithFakeCrypto()
|
||||
this.history = this.application.dependencies.get(TYPES.HistoryManager)
|
||||
this.payloadManager = this.application.payloads
|
||||
this.email = UuidGenerator.GenerateUuid()
|
||||
this.password = UuidGenerator.GenerateUuid()
|
||||
localStorage.clear()
|
||||
|
||||
application = await Factory.createInitAppWithFakeCrypto()
|
||||
history = application.dependencies.get(TYPES.HistoryManager)
|
||||
email = UuidGenerator.GenerateUuid()
|
||||
password = UuidGenerator.GenerateUuid()
|
||||
await Factory.registerUserToApplication({
|
||||
application: this.application,
|
||||
email: this.email,
|
||||
password: this.password,
|
||||
application: application,
|
||||
email: email,
|
||||
password: password,
|
||||
})
|
||||
})
|
||||
|
||||
afterEach(async function () {
|
||||
await Factory.safeDeinit(this.application)
|
||||
await Factory.safeDeinit(application)
|
||||
localStorage.clear()
|
||||
})
|
||||
|
||||
it('response from server should be failed if not signed in', async function () {
|
||||
await this.application.user.signOut()
|
||||
this.application = await Factory.createInitAppWithFakeCrypto()
|
||||
this.history = this.application.dependencies.get(TYPES.HistoryManager)
|
||||
this.payloadManager = this.application.payloads
|
||||
const item = await Factory.createSyncedNote(this.application)
|
||||
await this.application.sync.sync(syncOptions)
|
||||
const itemHistoryOrError = await this.application.listRevisions.execute({ itemUuid: item.uuid })
|
||||
await application.user.signOut()
|
||||
application = await Factory.createInitAppWithFakeCrypto()
|
||||
history = application.dependencies.get(TYPES.HistoryManager)
|
||||
|
||||
const item = await Factory.createSyncedNote(application)
|
||||
await application.sync.sync(syncOptions)
|
||||
const itemHistoryOrError = await application.listRevisions.execute({ itemUuid: item.uuid })
|
||||
|
||||
expect(itemHistoryOrError.isFailed()).to.equal(true)
|
||||
})
|
||||
|
||||
it('create basic history entries 2', async function () {
|
||||
const item = await Factory.createSyncedNote(this.application)
|
||||
const item = await Factory.createSyncedNote(application)
|
||||
await Factory.sleep(Factory.ServerRevisionCreationDelay)
|
||||
|
||||
let itemHistoryOrError = await this.application.listRevisions.execute({ itemUuid: item.uuid })
|
||||
let itemHistoryOrError = await application.listRevisions.execute({ itemUuid: item.uuid })
|
||||
let itemHistory = itemHistoryOrError.getValue()
|
||||
|
||||
/** Server history should save initial revision */
|
||||
expect(itemHistory.length).to.equal(1)
|
||||
|
||||
/** Sync within 5 seconds (ENV VAR dependend on self-hosted setup), should not create a new entry */
|
||||
await Factory.markDirtyAndSyncItem(this.application, item)
|
||||
itemHistoryOrError = await this.application.listRevisions.execute({ itemUuid: item.uuid })
|
||||
await Factory.markDirtyAndSyncItem(application, item)
|
||||
itemHistoryOrError = await application.listRevisions.execute({ itemUuid: item.uuid })
|
||||
itemHistory = itemHistoryOrError.getValue()
|
||||
expect(itemHistory.length).to.equal(1)
|
||||
|
||||
/** Sync with different contents, should not create a new entry */
|
||||
await this.application.changeAndSaveItem.execute(
|
||||
await application.changeAndSaveItem.execute(
|
||||
item,
|
||||
(mutator) => {
|
||||
mutator.title = Math.random()
|
||||
@@ -318,18 +292,18 @@ describe('history manager', () => {
|
||||
syncOptions,
|
||||
)
|
||||
await Factory.sleep(Factory.ServerRevisionCreationDelay)
|
||||
itemHistoryOrError = await this.application.listRevisions.execute({ itemUuid: item.uuid })
|
||||
itemHistoryOrError = await application.listRevisions.execute({ itemUuid: item.uuid })
|
||||
itemHistory = itemHistoryOrError.getValue()
|
||||
expect(itemHistory.length).to.equal(1)
|
||||
})
|
||||
|
||||
it('returns revisions from server', async function () {
|
||||
let item = await Factory.createSyncedNote(this.application)
|
||||
let item = await Factory.createSyncedNote(application)
|
||||
|
||||
await Factory.sleep(Factory.ServerRevisionFrequency)
|
||||
/** Sync with different contents, should create new entry */
|
||||
const newTitleAfterFirstChange = `The title should be: ${Math.random()}`
|
||||
await this.application.changeAndSaveItem.execute(
|
||||
await application.changeAndSaveItem.execute(
|
||||
item,
|
||||
(mutator) => {
|
||||
mutator.title = newTitleAfterFirstChange
|
||||
@@ -340,12 +314,12 @@ describe('history manager', () => {
|
||||
)
|
||||
await Factory.sleep(Factory.ServerRevisionCreationDelay)
|
||||
|
||||
const itemHistoryOrError = await this.application.listRevisions.execute({ itemUuid: item.uuid })
|
||||
const itemHistoryOrError = await application.listRevisions.execute({ itemUuid: item.uuid })
|
||||
const itemHistory = itemHistoryOrError.getValue()
|
||||
expect(itemHistory.length).to.equal(2)
|
||||
|
||||
const oldestEntry = lastElement(itemHistory)
|
||||
let revisionFromServerOrError = await this.application.getRevision.execute({
|
||||
let revisionFromServerOrError = await application.getRevision.execute({
|
||||
itemUuid: item.uuid,
|
||||
revisionUuid: oldestEntry.uuid,
|
||||
})
|
||||
@@ -357,22 +331,22 @@ describe('history manager', () => {
|
||||
expect(payloadFromServer.uuid).to.eq(item.payload.uuid)
|
||||
expect(payloadFromServer.content).to.eql(item.payload.content)
|
||||
|
||||
item = this.application.items.findItem(item.uuid)
|
||||
item = application.items.findItem(item.uuid)
|
||||
expect(payloadFromServer.content).to.not.eql(item.payload.content)
|
||||
})
|
||||
|
||||
it('duplicate revisions should not have the originals uuid', async function () {
|
||||
const note = await Factory.createSyncedNote(this.application)
|
||||
await Factory.markDirtyAndSyncItem(this.application, note)
|
||||
const dupe = await this.application.mutator.duplicateItem(note, true)
|
||||
await Factory.markDirtyAndSyncItem(this.application, dupe)
|
||||
const note = await Factory.createSyncedNote(application)
|
||||
await Factory.markDirtyAndSyncItem(application, note)
|
||||
const dupe = await application.mutator.duplicateItem(note, true)
|
||||
await Factory.markDirtyAndSyncItem(application, dupe)
|
||||
|
||||
await Factory.sleep(Factory.ServerRevisionCreationDelay)
|
||||
|
||||
const dupeHistoryOrError = await this.application.listRevisions.execute({ itemUuid: dupe.uuid })
|
||||
const dupeHistoryOrError = await application.listRevisions.execute({ itemUuid: dupe.uuid })
|
||||
const dupeHistory = dupeHistoryOrError.getValue()
|
||||
|
||||
const dupeRevisionOrError = await this.application.getRevision.execute({
|
||||
const dupeRevisionOrError = await application.getRevision.execute({
|
||||
itemUuid: dupe.uuid,
|
||||
revisionUuid: dupeHistory[0].uuid,
|
||||
})
|
||||
@@ -381,54 +355,54 @@ describe('history manager', () => {
|
||||
})
|
||||
|
||||
it('revisions count matches original for duplicated items', async function () {
|
||||
const note = await Factory.createSyncedNote(this.application)
|
||||
const note = await Factory.createSyncedNote(application)
|
||||
|
||||
await Factory.sleep(Factory.ServerRevisionFrequency)
|
||||
await Factory.markDirtyAndSyncItem(this.application, note)
|
||||
await Factory.markDirtyAndSyncItem(application, note)
|
||||
|
||||
await Factory.sleep(Factory.ServerRevisionFrequency)
|
||||
await Factory.markDirtyAndSyncItem(this.application, note)
|
||||
await Factory.markDirtyAndSyncItem(application, note)
|
||||
|
||||
await Factory.sleep(Factory.ServerRevisionFrequency)
|
||||
await Factory.markDirtyAndSyncItem(this.application, note)
|
||||
await Factory.markDirtyAndSyncItem(application, note)
|
||||
|
||||
const dupe = await this.application.mutator.duplicateItem(note, true)
|
||||
await Factory.markDirtyAndSyncItem(this.application, dupe)
|
||||
const dupe = await application.mutator.duplicateItem(note, true)
|
||||
await Factory.markDirtyAndSyncItem(application, dupe)
|
||||
|
||||
await Factory.sleep(Factory.ServerRevisionCreationDelay)
|
||||
|
||||
const expectedRevisions = 4
|
||||
const noteHistoryOrError = await this.application.listRevisions.execute({ itemUuid: note.uuid })
|
||||
const noteHistoryOrError = await application.listRevisions.execute({ itemUuid: note.uuid })
|
||||
const noteHistory = noteHistoryOrError.getValue()
|
||||
|
||||
const dupeHistoryOrError = await this.application.listRevisions.execute({ itemUuid: dupe.uuid })
|
||||
const dupeHistoryOrError = await application.listRevisions.execute({ itemUuid: dupe.uuid })
|
||||
const dupeHistory = dupeHistoryOrError.getValue()
|
||||
|
||||
expect(noteHistory.length).to.equal(expectedRevisions)
|
||||
expect(dupeHistory.length).to.equal(expectedRevisions + 1)
|
||||
}).timeout(Factory.ThirtySecondTimeout)
|
||||
}).timeout(Factory.SixtySecondTimeout)
|
||||
|
||||
it('can decrypt revisions for duplicate_of items', async function () {
|
||||
const note = await Factory.createSyncedNote(this.application)
|
||||
const note = await Factory.createSyncedNote(application)
|
||||
await Factory.sleep(Factory.ServerRevisionFrequency)
|
||||
|
||||
const changedText = `${Math.random()}`
|
||||
await this.application.changeAndSaveItem.execute(note, (mutator) => {
|
||||
await application.changeAndSaveItem.execute(note, (mutator) => {
|
||||
mutator.title = changedText
|
||||
})
|
||||
await Factory.markDirtyAndSyncItem(this.application, note)
|
||||
await Factory.markDirtyAndSyncItem(application, note)
|
||||
|
||||
const dupe = await this.application.mutator.duplicateItem(note, true)
|
||||
await Factory.markDirtyAndSyncItem(this.application, dupe)
|
||||
const dupe = await application.mutator.duplicateItem(note, true)
|
||||
await Factory.markDirtyAndSyncItem(application, dupe)
|
||||
|
||||
await Factory.sleep(Factory.ServerRevisionCreationDelay)
|
||||
|
||||
const itemHistoryOrError = await this.application.listRevisions.execute({ itemUuid: dupe.uuid })
|
||||
const itemHistoryOrError = await application.listRevisions.execute({ itemUuid: dupe.uuid })
|
||||
const itemHistory = itemHistoryOrError.getValue()
|
||||
expect(itemHistory.length).to.be.above(1)
|
||||
const newestRevision = itemHistory[0]
|
||||
|
||||
const fetchedOrError = await this.application.getRevision.execute({
|
||||
const fetchedOrError = await application.getRevision.execute({
|
||||
itemUuid: dupe.uuid,
|
||||
revisionUuid: newestRevision.uuid,
|
||||
})
|
||||
|
||||
@@ -1,45 +1,43 @@
|
||||
/* eslint-disable no-unused-expressions */
|
||||
/* eslint-disable no-undef */
|
||||
import * as Factory from './lib/factory.js'
|
||||
|
||||
chai.use(chaiAsPromised)
|
||||
const expect = chai.expect
|
||||
|
||||
describe('item', () => {
|
||||
beforeEach(async function () {
|
||||
this.createBarePayload = () => {
|
||||
return new DecryptedPayload({
|
||||
uuid: '123',
|
||||
content_type: ContentType.TYPES.Note,
|
||||
content: {
|
||||
title: 'hello',
|
||||
},
|
||||
})
|
||||
}
|
||||
const createBarePayload = () => {
|
||||
return new DecryptedPayload({
|
||||
uuid: '123',
|
||||
content_type: ContentType.TYPES.Note,
|
||||
content: {
|
||||
title: 'hello',
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
this.createNote = () => {
|
||||
return new DecryptedItem(this.createBarePayload())
|
||||
}
|
||||
const createNote = () => {
|
||||
return new DecryptedItem(createBarePayload())
|
||||
}
|
||||
|
||||
this.createTag = (notes = []) => {
|
||||
const references = notes.map((note) => {
|
||||
return {
|
||||
uuid: note.uuid,
|
||||
content_type: note.content_type,
|
||||
}
|
||||
})
|
||||
return new SNTag(
|
||||
new DecryptedPayload({
|
||||
uuid: Factory.generateUuidish(),
|
||||
content_type: ContentType.TYPES.Tag,
|
||||
content: {
|
||||
title: 'thoughts',
|
||||
references: references,
|
||||
},
|
||||
}),
|
||||
)
|
||||
const createTag = (notes = []) => {
|
||||
const references = notes.map((note) => {
|
||||
return {
|
||||
uuid: note.uuid,
|
||||
content_type: note.content_type,
|
||||
}
|
||||
})
|
||||
|
||||
return new SNTag(
|
||||
new DecryptedPayload({
|
||||
uuid: Factory.generateUuidish(),
|
||||
content_type: ContentType.TYPES.Tag,
|
||||
content: {
|
||||
title: 'thoughts',
|
||||
references: references,
|
||||
},
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
describe('item', () => {
|
||||
it('constructing without uuid should throw', function () {
|
||||
let error
|
||||
|
||||
@@ -53,40 +51,40 @@ describe('item', () => {
|
||||
})
|
||||
|
||||
it('healthy constructor', function () {
|
||||
const item = this.createNote()
|
||||
const item = createNote()
|
||||
|
||||
expect(item).to.be.ok
|
||||
expect(item.payload).to.be.ok
|
||||
})
|
||||
|
||||
it('user modified date should be ok', function () {
|
||||
const item = this.createNote()
|
||||
const item = createNote()
|
||||
|
||||
expect(item.userModifiedDate).to.be.ok
|
||||
})
|
||||
|
||||
it('has relationship with item true', function () {
|
||||
const note = this.createNote()
|
||||
const tag = this.createTag()
|
||||
const note = createNote()
|
||||
const tag = createTag()
|
||||
|
||||
expect(tag.isReferencingItem(note)).to.equal(false)
|
||||
})
|
||||
|
||||
it('has relationship with item true', function () {
|
||||
const note = this.createNote()
|
||||
const tag = this.createTag([note])
|
||||
const note = createNote()
|
||||
const tag = createTag([note])
|
||||
|
||||
expect(tag.isReferencingItem(note)).to.equal(true)
|
||||
})
|
||||
|
||||
it('getDomainData for random domain should return undefined', function () {
|
||||
const note = this.createNote()
|
||||
const note = createNote()
|
||||
|
||||
expect(note.getDomainData('random')).to.not.be.ok
|
||||
})
|
||||
|
||||
it('getDomainData for app domain should return object', function () {
|
||||
const note = this.createNote()
|
||||
const note = createNote()
|
||||
|
||||
expect(note.getDomainData(DecryptedItem.DefaultAppDomain())).to.be.ok
|
||||
})
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
/* eslint-disable no-unused-expressions */
|
||||
/* eslint-disable no-undef */
|
||||
import * as Factory from './lib/factory.js'
|
||||
import { BaseItemCounts } from './lib/BaseItemCounts.js'
|
||||
|
||||
@@ -10,11 +8,6 @@ describe('item manager', function () {
|
||||
let context
|
||||
let application
|
||||
|
||||
afterEach(async function () {
|
||||
await context.deinit()
|
||||
localStorage.clear()
|
||||
})
|
||||
|
||||
beforeEach(async function () {
|
||||
localStorage.clear()
|
||||
|
||||
@@ -24,6 +17,14 @@ describe('item manager', function () {
|
||||
await context.launch()
|
||||
})
|
||||
|
||||
afterEach(async function () {
|
||||
await context.deinit()
|
||||
localStorage.clear()
|
||||
|
||||
context = undefined
|
||||
application = undefined
|
||||
})
|
||||
|
||||
const createNote = async () => {
|
||||
return application.mutator.createItem(ContentType.TYPES.Note, {
|
||||
title: 'hello',
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
/* eslint-disable no-unused-expressions */
|
||||
/* eslint-disable no-undef */
|
||||
|
||||
import * as Factory from './lib/factory.js'
|
||||
|
||||
chai.use(chaiAsPromised)
|
||||
const expect = chai.expect
|
||||
|
||||
describe('key params', function () {
|
||||
this.timeout(Factory.TenSecondTimeout)
|
||||
|
||||
before(async function () {
|
||||
beforeEach(async function () {
|
||||
localStorage.clear()
|
||||
})
|
||||
|
||||
after(async function () {
|
||||
afterEach(async function () {
|
||||
localStorage.clear()
|
||||
})
|
||||
|
||||
|
||||
@@ -1,23 +1,18 @@
|
||||
/* eslint-disable no-unused-expressions */
|
||||
/* eslint-disable no-undef */
|
||||
import * as Factory from './lib/factory.js'
|
||||
|
||||
chai.use(chaiAsPromised)
|
||||
const expect = chai.expect
|
||||
|
||||
describe('key recovery service', function () {
|
||||
this.timeout(Factory.TwentySecondTimeout)
|
||||
|
||||
const syncOptions = {
|
||||
checkIntegrity: true,
|
||||
awaitAll: true,
|
||||
}
|
||||
|
||||
beforeEach(function () {
|
||||
localStorage.clear()
|
||||
})
|
||||
|
||||
afterEach(function () {
|
||||
localStorage.clear()
|
||||
sinon.restore()
|
||||
})
|
||||
|
||||
it('when encountering an undecryptable items key, should recover through recovery wizard', async function () {
|
||||
@@ -433,9 +428,7 @@ describe('key recovery service', function () {
|
||||
KeyParamsOrigination.Registration,
|
||||
ProtocolVersion.V003,
|
||||
)
|
||||
const randomItemsKey = await context.operators
|
||||
.operatorForVersion(ProtocolVersion.V003)
|
||||
.createItemsKey()
|
||||
const randomItemsKey = await context.operators.operatorForVersion(ProtocolVersion.V003).createItemsKey()
|
||||
|
||||
const encrypted = await application.encryption.encryptSplitSingle({
|
||||
usesRootKey: {
|
||||
|
||||
@@ -1,35 +1,40 @@
|
||||
/* eslint-disable no-unused-expressions */
|
||||
/* eslint-disable no-undef */
|
||||
import * as Factory from './lib/factory.js'
|
||||
import * as Utils from './lib/Utils.js'
|
||||
|
||||
chai.use(chaiAsPromised)
|
||||
const expect = chai.expect
|
||||
|
||||
describe('keys', function () {
|
||||
this.timeout(Factory.TwentySecondTimeout)
|
||||
|
||||
let context
|
||||
let application
|
||||
let email
|
||||
let password
|
||||
|
||||
beforeEach(async function () {
|
||||
localStorage.clear()
|
||||
|
||||
this.context = await Factory.createAppContext()
|
||||
await this.context.launch()
|
||||
context = await Factory.createAppContext()
|
||||
await context.launch()
|
||||
|
||||
this.application = this.context.application
|
||||
this.email = UuidGenerator.GenerateUuid()
|
||||
this.password = UuidGenerator.GenerateUuid()
|
||||
application = context.application
|
||||
email = UuidGenerator.GenerateUuid()
|
||||
password = UuidGenerator.GenerateUuid()
|
||||
})
|
||||
|
||||
afterEach(async function () {
|
||||
if (!this.application.dealloced) {
|
||||
await Factory.safeDeinit(this.application)
|
||||
if (!application.dealloced) {
|
||||
await Factory.safeDeinit(application)
|
||||
}
|
||||
|
||||
this.application = undefined
|
||||
application = undefined
|
||||
context = undefined
|
||||
localStorage.clear()
|
||||
})
|
||||
|
||||
it('should not have root key by default', async function () {
|
||||
expect(await this.application.encryption.getRootKey()).to.not.be.ok
|
||||
expect(await application.encryption.getRootKey()).to.not.be.ok
|
||||
})
|
||||
|
||||
it('validates content types requiring root encryption', function () {
|
||||
@@ -43,7 +48,7 @@ describe('keys', function () {
|
||||
/** Items key available by default */
|
||||
const payload = Factory.createNotePayload()
|
||||
const processedPayload = CreateEncryptedLocalStorageContextPayload(
|
||||
await this.application.encryption.encryptSplitSingle({
|
||||
await application.encryption.encryptSplitSingle({
|
||||
usesItemsKeyWithKeyLookup: {
|
||||
items: [payload],
|
||||
},
|
||||
@@ -53,45 +58,41 @@ describe('keys', function () {
|
||||
})
|
||||
|
||||
it('has root key and one items key after registering user', async function () {
|
||||
await Factory.registerUserToApplication({ application: this.application })
|
||||
expect(this.application.encryption.getRootKey()).to.be.ok
|
||||
expect(this.application.items.getDisplayableItemsKeys().length).to.equal(1)
|
||||
await Factory.registerUserToApplication({ application: application })
|
||||
expect(application.encryption.getRootKey()).to.be.ok
|
||||
expect(application.items.getDisplayableItemsKeys().length).to.equal(1)
|
||||
})
|
||||
|
||||
it('changing root key with passcode should re-wrap root key', async function () {
|
||||
const email = 'foo'
|
||||
const password = 'bar'
|
||||
const key = await this.application.encryption.createRootKey(email, password, KeyParamsOrigination.Registration)
|
||||
await this.application.encryption.setRootKey(key)
|
||||
Factory.handlePasswordChallenges(this.application, password)
|
||||
await this.application.addPasscode(password)
|
||||
const key = await application.encryption.createRootKey(email, password, KeyParamsOrigination.Registration)
|
||||
await application.encryption.setRootKey(key)
|
||||
Factory.handlePasswordChallenges(application, password)
|
||||
await application.addPasscode(password)
|
||||
|
||||
/** We should be able to decrypt wrapped root key with passcode */
|
||||
const wrappingKeyParams = await this.application.encryption.rootKeyManager.getRootKeyWrapperKeyParams()
|
||||
const wrappingKey = await this.application.encryption.computeRootKey(password, wrappingKeyParams)
|
||||
await this.application.encryption.unwrapRootKey(wrappingKey).catch((error) => {
|
||||
const wrappingKeyParams = await application.encryption.rootKeyManager.getRootKeyWrapperKeyParams()
|
||||
const wrappingKey = await application.encryption.computeRootKey(password, wrappingKeyParams)
|
||||
await application.encryption.unwrapRootKey(wrappingKey).catch((error) => {
|
||||
expect(error).to.not.be.ok
|
||||
})
|
||||
|
||||
const newPassword = 'bar'
|
||||
const newKey = await this.application.encryption.createRootKey(
|
||||
email,
|
||||
newPassword,
|
||||
KeyParamsOrigination.Registration,
|
||||
)
|
||||
await this.application.encryption.setRootKey(newKey, wrappingKey)
|
||||
await this.application.encryption.unwrapRootKey(wrappingKey).catch((error) => {
|
||||
const newKey = await application.encryption.createRootKey(email, newPassword, KeyParamsOrigination.Registration)
|
||||
await application.encryption.setRootKey(newKey, wrappingKey)
|
||||
await application.encryption.unwrapRootKey(wrappingKey).catch((error) => {
|
||||
expect(error).to.not.be.ok
|
||||
})
|
||||
})
|
||||
|
||||
it('items key should be encrypted with root key', async function () {
|
||||
await Factory.registerUserToApplication({ application: this.application })
|
||||
const itemsKey = await this.application.encryption.getSureDefaultItemsKey()
|
||||
const rootKey = await this.application.encryption.getRootKey()
|
||||
await Factory.registerUserToApplication({ application: application })
|
||||
const itemsKey = await application.encryption.getSureDefaultItemsKey()
|
||||
const rootKey = await application.encryption.getRootKey()
|
||||
|
||||
/** Encrypt items key */
|
||||
const encryptedPayload = await this.application.encryption.encryptSplitSingle({
|
||||
const encryptedPayload = await application.encryption.encryptSplitSingle({
|
||||
usesRootKey: {
|
||||
items: [itemsKey.payloadRepresentation()],
|
||||
key: rootKey,
|
||||
@@ -102,7 +103,7 @@ describe('keys', function () {
|
||||
expect(encryptedPayload.items_key_id).to.not.be.ok
|
||||
|
||||
/** Attempt to decrypt with root key. Should succeed. */
|
||||
const decryptedPayload = await this.application.encryption.decryptSplitSingle({
|
||||
const decryptedPayload = await application.encryption.decryptSplitSingle({
|
||||
usesRootKey: {
|
||||
items: [encryptedPayload],
|
||||
key: rootKey,
|
||||
@@ -114,7 +115,7 @@ describe('keys', function () {
|
||||
})
|
||||
|
||||
it('should create random items key if no account and no passcode', async function () {
|
||||
const itemsKeys = this.application.items.getDisplayableItemsKeys()
|
||||
const itemsKeys = application.items.getDisplayableItemsKeys()
|
||||
expect(itemsKeys.length).to.equal(1)
|
||||
const notePayload = Factory.createNotePayload()
|
||||
|
||||
@@ -122,55 +123,55 @@ describe('keys', function () {
|
||||
dirty: true,
|
||||
dirtyIndex: getIncrementedDirtyIndex(),
|
||||
})
|
||||
await this.application.payloads.emitPayload(dirtied, PayloadEmitSource.LocalChanged)
|
||||
await this.application.sync.sync()
|
||||
await application.payloads.emitPayload(dirtied, PayloadEmitSource.LocalChanged)
|
||||
await application.sync.sync()
|
||||
|
||||
const rawPayloads = await this.application.storage.getAllRawPayloads()
|
||||
const rawPayloads = await application.storage.getAllRawPayloads()
|
||||
const rawNotePayload = rawPayloads.find((r) => r.content_type === ContentType.TYPES.Note)
|
||||
expect(typeof rawNotePayload.content).to.equal('string')
|
||||
})
|
||||
|
||||
it('should keep offline created items key upon registration', async function () {
|
||||
expect(this.application.items.getDisplayableItemsKeys().length).to.equal(1)
|
||||
const originalItemsKey = this.application.items.getDisplayableItemsKeys()[0]
|
||||
await this.application.register(this.email, this.password)
|
||||
expect(application.items.getDisplayableItemsKeys().length).to.equal(1)
|
||||
const originalItemsKey = application.items.getDisplayableItemsKeys()[0]
|
||||
await application.register(email, password)
|
||||
|
||||
expect(this.application.items.getDisplayableItemsKeys().length).to.equal(1)
|
||||
const newestItemsKey = this.application.items.getDisplayableItemsKeys()[0]
|
||||
expect(application.items.getDisplayableItemsKeys().length).to.equal(1)
|
||||
const newestItemsKey = application.items.getDisplayableItemsKeys()[0]
|
||||
expect(newestItemsKey.uuid).to.equal(originalItemsKey.uuid)
|
||||
})
|
||||
|
||||
it('should use items key for encryption of note', async function () {
|
||||
const notePayload = Factory.createNotePayload()
|
||||
const keyToUse = await this.application.encryption.itemsEncryption.keyToUseForItemEncryption(notePayload)
|
||||
const keyToUse = await application.encryption.itemsEncryption.keyToUseForItemEncryption(notePayload)
|
||||
expect(keyToUse.content_type).to.equal(ContentType.TYPES.ItemsKey)
|
||||
})
|
||||
|
||||
it('encrypting an item should associate an items key to it', async function () {
|
||||
const note = Factory.createNotePayload()
|
||||
const encryptedPayload = await this.application.encryption.encryptSplitSingle({
|
||||
const encryptedPayload = await application.encryption.encryptSplitSingle({
|
||||
usesItemsKeyWithKeyLookup: {
|
||||
items: [note],
|
||||
},
|
||||
})
|
||||
|
||||
const itemsKey = this.application.encryption.itemsKeyForEncryptedPayload(encryptedPayload)
|
||||
const itemsKey = application.encryption.itemsKeyForEncryptedPayload(encryptedPayload)
|
||||
expect(itemsKey).to.be.ok
|
||||
})
|
||||
|
||||
it('decrypt encrypted item with associated key', async function () {
|
||||
const note = Factory.createNotePayload()
|
||||
const title = note.content.title
|
||||
const encryptedPayload = await this.application.encryption.encryptSplitSingle({
|
||||
const encryptedPayload = await application.encryption.encryptSplitSingle({
|
||||
usesItemsKeyWithKeyLookup: {
|
||||
items: [note],
|
||||
},
|
||||
})
|
||||
|
||||
const itemsKey = this.application.encryption.itemsKeyForEncryptedPayload(encryptedPayload)
|
||||
const itemsKey = application.encryption.itemsKeyForEncryptedPayload(encryptedPayload)
|
||||
expect(itemsKey).to.be.ok
|
||||
|
||||
const decryptedPayload = await this.application.encryption.decryptSplitSingle({
|
||||
const decryptedPayload = await application.encryption.decryptSplitSingle({
|
||||
usesItemsKeyWithKeyLookup: {
|
||||
items: [encryptedPayload],
|
||||
},
|
||||
@@ -182,30 +183,30 @@ describe('keys', function () {
|
||||
it('decrypts items waiting for keys', async function () {
|
||||
const notePayload = Factory.createNotePayload()
|
||||
const title = notePayload.content.title
|
||||
const encryptedPayload = await this.application.encryption.encryptSplitSingle({
|
||||
const encryptedPayload = await application.encryption.encryptSplitSingle({
|
||||
usesItemsKeyWithKeyLookup: {
|
||||
items: [notePayload],
|
||||
},
|
||||
})
|
||||
|
||||
const itemsKey = this.application.encryption.itemsKeyForEncryptedPayload(encryptedPayload)
|
||||
const itemsKey = application.encryption.itemsKeyForEncryptedPayload(encryptedPayload)
|
||||
|
||||
await this.application.items.removeItemFromMemory(itemsKey)
|
||||
await application.items.removeItemFromMemory(itemsKey)
|
||||
|
||||
const erroredPayload = await this.application.encryption.decryptSplitSingle({
|
||||
const erroredPayload = await application.encryption.decryptSplitSingle({
|
||||
usesItemsKeyWithKeyLookup: {
|
||||
items: [encryptedPayload],
|
||||
},
|
||||
})
|
||||
|
||||
await this.application.mutator.emitItemsFromPayloads([erroredPayload], PayloadEmitSource.LocalChanged)
|
||||
await application.mutator.emitItemsFromPayloads([erroredPayload], PayloadEmitSource.LocalChanged)
|
||||
|
||||
const note = this.application.items.findAnyItem(notePayload.uuid)
|
||||
const note = application.items.findAnyItem(notePayload.uuid)
|
||||
expect(note.errorDecrypting).to.equal(true)
|
||||
expect(note.waitingForKey).to.equal(true)
|
||||
|
||||
const keyPayload = new DecryptedPayload(itemsKey.payload.ejected())
|
||||
await this.application.mutator.emitItemsFromPayloads([keyPayload], PayloadEmitSource.LocalChanged)
|
||||
await application.mutator.emitItemsFromPayloads([keyPayload], PayloadEmitSource.LocalChanged)
|
||||
|
||||
/**
|
||||
* Sleeping is required to trigger asyncronous encryptionService.decryptItemsWaitingForKeys,
|
||||
@@ -213,7 +214,7 @@ describe('keys', function () {
|
||||
*/
|
||||
await Factory.sleep(0.2)
|
||||
|
||||
const updatedNote = this.application.items.findItem(note.uuid)
|
||||
const updatedNote = application.items.findItem(note.uuid)
|
||||
|
||||
expect(updatedNote.errorDecrypting).to.not.be.ok
|
||||
expect(updatedNote.waitingForKey).to.not.be.ok
|
||||
@@ -221,9 +222,9 @@ describe('keys', function () {
|
||||
})
|
||||
|
||||
it('attempting to emit errored items key for which there exists a non errored master copy should ignore it', async function () {
|
||||
await Factory.registerUserToApplication({ application: this.application })
|
||||
await Factory.registerUserToApplication({ application: application })
|
||||
|
||||
const itemsKey = await this.application.encryption.getSureDefaultItemsKey()
|
||||
const itemsKey = await application.encryption.getSureDefaultItemsKey()
|
||||
|
||||
expect(itemsKey.errorDecrypting).to.not.be.ok
|
||||
|
||||
@@ -239,44 +240,44 @@ describe('keys', function () {
|
||||
},
|
||||
})
|
||||
|
||||
await this.application.sync.handleSuccessServerResponse({ payloadsSavedOrSaving: [], options: {} }, response)
|
||||
await application.sync.handleSuccessServerResponse({ payloadsSavedOrSaving: [], options: {} }, response)
|
||||
|
||||
const refreshedKey = this.application.payloads.findOne(itemsKey.uuid)
|
||||
const refreshedKey = application.payloads.findOne(itemsKey.uuid)
|
||||
|
||||
expect(refreshedKey.errorDecrypting).to.not.be.ok
|
||||
expect(refreshedKey.content.itemsKey).to.be.ok
|
||||
})
|
||||
|
||||
it('generating export params with logged in account should produce encrypted payload', async function () {
|
||||
await Factory.registerUserToApplication({ application: this.application })
|
||||
await Factory.registerUserToApplication({ application: application })
|
||||
const payload = Factory.createNotePayload()
|
||||
const encryptedPayload = await this.application.encryption.encryptSplitSingle({
|
||||
const encryptedPayload = await application.encryption.encryptSplitSingle({
|
||||
usesItemsKeyWithKeyLookup: {
|
||||
items: [payload],
|
||||
},
|
||||
})
|
||||
expect(typeof encryptedPayload.content).to.equal('string')
|
||||
expect(encryptedPayload.content.substring(0, 3)).to.equal(this.application.encryption.getLatestVersion())
|
||||
expect(encryptedPayload.content.substring(0, 3)).to.equal(application.encryption.getLatestVersion())
|
||||
})
|
||||
|
||||
it('When setting passcode, should encrypt items keys', async function () {
|
||||
await this.application.addPasscode('foo')
|
||||
const itemsKey = this.application.items.getDisplayableItemsKeys()[0]
|
||||
const rawPayloads = await this.application.storage.getAllRawPayloads()
|
||||
await application.addPasscode('foo')
|
||||
const itemsKey = application.items.getDisplayableItemsKeys()[0]
|
||||
const rawPayloads = await application.storage.getAllRawPayloads()
|
||||
const itemsKeyRawPayload = rawPayloads.find((p) => p.uuid === itemsKey.uuid)
|
||||
const itemsKeyPayload = new EncryptedPayload(itemsKeyRawPayload)
|
||||
expect(itemsKeyPayload.enc_item_key).to.be.ok
|
||||
})
|
||||
|
||||
it('items key encrypted payload should contain root key params', async function () {
|
||||
await this.application.addPasscode('foo')
|
||||
const itemsKey = this.application.items.getDisplayableItemsKeys()[0]
|
||||
const rawPayloads = await this.application.storage.getAllRawPayloads()
|
||||
await application.addPasscode('foo')
|
||||
const itemsKey = application.items.getDisplayableItemsKeys()[0]
|
||||
const rawPayloads = await application.storage.getAllRawPayloads()
|
||||
const itemsKeyRawPayload = rawPayloads.find((p) => p.uuid === itemsKey.uuid)
|
||||
const itemsKeyPayload = new EncryptedPayload(itemsKeyRawPayload)
|
||||
|
||||
const authenticatedData = this.context.encryption.getEmbeddedPayloadAuthenticatedData(itemsKeyPayload)
|
||||
const rootKeyParams = await this.application.encryption.getRootKeyParams()
|
||||
const authenticatedData = context.encryption.getEmbeddedPayloadAuthenticatedData(itemsKeyPayload)
|
||||
const rootKeyParams = await application.encryption.getRootKeyParams()
|
||||
|
||||
expect(authenticatedData.kp).to.be.ok
|
||||
expect(authenticatedData.kp).to.eql(rootKeyParams.getPortableValue())
|
||||
@@ -285,9 +286,9 @@ describe('keys', function () {
|
||||
|
||||
it('correctly validates local passcode', async function () {
|
||||
const passcode = 'foo'
|
||||
await this.application.addPasscode('foo')
|
||||
expect((await this.application.encryption.validatePasscode('wrong')).valid).to.equal(false)
|
||||
expect((await this.application.encryption.validatePasscode(passcode)).valid).to.equal(true)
|
||||
await application.addPasscode('foo')
|
||||
expect((await application.encryption.validatePasscode('wrong')).valid).to.equal(false)
|
||||
expect((await application.encryption.validatePasscode(passcode)).valid).to.equal(true)
|
||||
})
|
||||
|
||||
it('signing into 003 account should delete latest offline items key and create 003 items key', async function () {
|
||||
@@ -296,63 +297,63 @@ describe('keys', function () {
|
||||
* Upon signing into an 003 account, the application should delete any neverSynced items keys,
|
||||
* and create a new default items key that is the default for a given protocol version.
|
||||
*/
|
||||
const defaultItemsKey = await this.application.encryption.getSureDefaultItemsKey()
|
||||
const latestVersion = this.application.encryption.getLatestVersion()
|
||||
const defaultItemsKey = await application.encryption.getSureDefaultItemsKey()
|
||||
const latestVersion = application.encryption.getLatestVersion()
|
||||
expect(defaultItemsKey.keyVersion).to.equal(latestVersion)
|
||||
|
||||
/** Register with 003 version */
|
||||
await Factory.registerOldUser({
|
||||
application: this.application,
|
||||
email: this.email,
|
||||
password: this.password,
|
||||
application: application,
|
||||
email: email,
|
||||
password: password,
|
||||
version: ProtocolVersion.V003,
|
||||
})
|
||||
|
||||
const itemsKeys = this.application.items.getDisplayableItemsKeys()
|
||||
const itemsKeys = application.items.getDisplayableItemsKeys()
|
||||
expect(itemsKeys.length).to.equal(1)
|
||||
const newestItemsKey = itemsKeys[0]
|
||||
expect(newestItemsKey.keyVersion).to.equal(ProtocolVersion.V003)
|
||||
const rootKey = await this.application.encryption.getRootKey()
|
||||
const rootKey = await application.encryption.getRootKey()
|
||||
expect(newestItemsKey.itemsKey).to.equal(rootKey.masterKey)
|
||||
expect(newestItemsKey.dataAuthenticationKey).to.equal(rootKey.dataAuthenticationKey)
|
||||
})
|
||||
|
||||
it('reencrypts existing notes when logging into an 003 account', async function () {
|
||||
await Factory.createManyMappedNotes(this.application, 10)
|
||||
await Factory.createManyMappedNotes(application, 10)
|
||||
await Factory.registerOldUser({
|
||||
application: this.application,
|
||||
email: this.email,
|
||||
password: this.password,
|
||||
application: application,
|
||||
email: email,
|
||||
password: password,
|
||||
version: ProtocolVersion.V003,
|
||||
})
|
||||
|
||||
expect(this.application.payloads.invalidPayloads.length).to.equal(0)
|
||||
expect(this.application.items.getDisplayableItemsKeys().length).to.equal(1)
|
||||
expect(this.application.items.getDisplayableItemsKeys()[0].dirty).to.equal(false)
|
||||
expect(application.payloads.invalidPayloads.length).to.equal(0)
|
||||
expect(application.items.getDisplayableItemsKeys().length).to.equal(1)
|
||||
expect(application.items.getDisplayableItemsKeys()[0].dirty).to.equal(false)
|
||||
|
||||
/** Sign out and back in */
|
||||
this.application = await Factory.signOutApplicationAndReturnNew(this.application)
|
||||
await this.application.signIn(this.email, this.password, undefined, undefined, undefined, true)
|
||||
application = await Factory.signOutApplicationAndReturnNew(application)
|
||||
await application.signIn(email, password, undefined, undefined, undefined, true)
|
||||
|
||||
expect(this.application.items.getDisplayableItemsKeys().length).to.equal(1)
|
||||
expect(this.application.items.getDisplayableNotes().length).to.equal(10)
|
||||
expect(this.application.payloads.invalidPayloads.length).to.equal(0)
|
||||
expect(application.items.getDisplayableItemsKeys().length).to.equal(1)
|
||||
expect(application.items.getDisplayableNotes().length).to.equal(10)
|
||||
expect(application.payloads.invalidPayloads.length).to.equal(0)
|
||||
})
|
||||
|
||||
it('When root key changes, all items keys must be re-encrypted', async function () {
|
||||
const passcode = 'foo'
|
||||
await this.application.addPasscode(passcode)
|
||||
await Factory.createSyncedNote(this.application)
|
||||
const itemsKeys = this.application.items.getDisplayableItemsKeys()
|
||||
await application.addPasscode(passcode)
|
||||
await Factory.createSyncedNote(application)
|
||||
const itemsKeys = application.items.getDisplayableItemsKeys()
|
||||
expect(itemsKeys.length).to.equal(1)
|
||||
const originalItemsKey = itemsKeys[0]
|
||||
|
||||
const originalRootKey = await this.application.encryption.getRootKey()
|
||||
const originalRootKey = await application.encryption.getRootKey()
|
||||
/** Expect that we can decrypt raw payload with current root key */
|
||||
const rawPayloads = await this.application.storage.getAllRawPayloads()
|
||||
const rawPayloads = await application.storage.getAllRawPayloads()
|
||||
const itemsKeyRawPayload = rawPayloads.find((p) => p.uuid === originalItemsKey.uuid)
|
||||
const itemsKeyPayload = new EncryptedPayload(itemsKeyRawPayload)
|
||||
const decrypted = await this.application.encryption.decryptSplitSingle({
|
||||
const decrypted = await application.encryption.decryptSplitSingle({
|
||||
usesRootKey: {
|
||||
items: [itemsKeyPayload],
|
||||
key: originalRootKey,
|
||||
@@ -363,10 +364,10 @@ describe('keys', function () {
|
||||
expect(decrypted.content).to.eql(originalItemsKey.content)
|
||||
|
||||
/** Change passcode */
|
||||
Factory.handlePasswordChallenges(this.application, passcode)
|
||||
await this.application.changePasscode('bar')
|
||||
Factory.handlePasswordChallenges(application, passcode)
|
||||
await application.changePasscode('bar')
|
||||
|
||||
const newRootKey = await this.application.encryption.getRootKey()
|
||||
const newRootKey = await application.encryption.getRootKey()
|
||||
expect(newRootKey).to.not.equal(originalRootKey)
|
||||
expect(newRootKey.masterKey).to.not.equal(originalRootKey.masterKey)
|
||||
|
||||
@@ -374,12 +375,12 @@ describe('keys', function () {
|
||||
* Expect that originalRootKey can no longer decrypt originalItemsKey
|
||||
* as items key has been re-encrypted with new root key
|
||||
*/
|
||||
const rawPayloads2 = await this.application.storage.getAllRawPayloads()
|
||||
const rawPayloads2 = await application.storage.getAllRawPayloads()
|
||||
const itemsKeyRawPayload2 = rawPayloads2.find((p) => p.uuid === originalItemsKey.uuid)
|
||||
expect(itemsKeyRawPayload2.content).to.not.equal(itemsKeyRawPayload.content)
|
||||
|
||||
const itemsKeyPayload2 = new EncryptedPayload(itemsKeyRawPayload2)
|
||||
const decrypted2 = await this.application.encryption.decryptSplitSingle({
|
||||
const decrypted2 = await application.encryption.decryptSplitSingle({
|
||||
usesRootKey: {
|
||||
items: [itemsKeyPayload2],
|
||||
key: originalRootKey,
|
||||
@@ -388,7 +389,7 @@ describe('keys', function () {
|
||||
expect(decrypted2.errorDecrypting).to.equal(true)
|
||||
|
||||
/** Should be able to decrypt with new root key */
|
||||
const decrypted3 = await this.application.encryption.decryptSplitSingle({
|
||||
const decrypted3 = await application.encryption.decryptSplitSingle({
|
||||
usesRootKey: {
|
||||
items: [itemsKeyPayload2],
|
||||
key: newRootKey,
|
||||
@@ -399,23 +400,23 @@ describe('keys', function () {
|
||||
|
||||
it('changing account password should create new items key and encrypt items with that key', async function () {
|
||||
await Factory.registerUserToApplication({
|
||||
application: this.application,
|
||||
email: this.email,
|
||||
password: this.password,
|
||||
application: application,
|
||||
email: email,
|
||||
password: password,
|
||||
})
|
||||
const itemsKeys = this.application.items.getDisplayableItemsKeys()
|
||||
const itemsKeys = application.items.getDisplayableItemsKeys()
|
||||
expect(itemsKeys.length).to.equal(1)
|
||||
const defaultItemsKey = await this.application.encryption.getSureDefaultItemsKey()
|
||||
const defaultItemsKey = await application.encryption.getSureDefaultItemsKey()
|
||||
|
||||
const result = await this.application.changePassword(this.password, 'foobarfoo')
|
||||
const result = await application.changePassword(password, 'foobarfoo')
|
||||
expect(result.error).to.not.be.ok
|
||||
|
||||
expect(this.application.items.getDisplayableItemsKeys().length).to.equal(2)
|
||||
const newDefaultItemsKey = await this.application.encryption.getSureDefaultItemsKey()
|
||||
expect(application.items.getDisplayableItemsKeys().length).to.equal(2)
|
||||
const newDefaultItemsKey = await application.encryption.getSureDefaultItemsKey()
|
||||
expect(newDefaultItemsKey.uuid).to.not.equal(defaultItemsKey.uuid)
|
||||
|
||||
const note = await Factory.createSyncedNote(this.application)
|
||||
const payload = await this.application.encryption.encryptSplitSingle({
|
||||
const note = await Factory.createSyncedNote(application)
|
||||
const payload = await application.encryption.encryptSplitSingle({
|
||||
usesItemsKeyWithKeyLookup: {
|
||||
items: [note.payload],
|
||||
},
|
||||
@@ -480,29 +481,29 @@ describe('keys', function () {
|
||||
})
|
||||
|
||||
it('loading the keychain root key should also load its key params', async function () {
|
||||
await Factory.registerUserToApplication({ application: this.application })
|
||||
const rootKey = await this.application.encryption.rootKeyManager.getRootKeyFromKeychain()
|
||||
await Factory.registerUserToApplication({ application: application })
|
||||
const rootKey = await application.encryption.rootKeyManager.getRootKeyFromKeychain()
|
||||
expect(rootKey.keyParams).to.be.ok
|
||||
})
|
||||
|
||||
it('key params should be persisted separately and not as part of root key', async function () {
|
||||
await Factory.registerUserToApplication({ application: this.application })
|
||||
const rawKey = await this.application.device.getNamespacedKeychainValue(this.application.identifier)
|
||||
await Factory.registerUserToApplication({ application: application })
|
||||
const rawKey = await application.device.getNamespacedKeychainValue(application.identifier)
|
||||
expect(rawKey.keyParams).to.not.be.ok
|
||||
const rawKeyParams = await this.application.storage.getValue(StorageKey.RootKeyParams, StorageValueModes.Nonwrapped)
|
||||
const rawKeyParams = await application.storage.getValue(StorageKey.RootKeyParams, StorageValueModes.Nonwrapped)
|
||||
expect(rawKeyParams).to.be.ok
|
||||
})
|
||||
|
||||
it('persisted key params should exactly equal in memory rootKey.keyParams', async function () {
|
||||
await Factory.registerUserToApplication({ application: this.application })
|
||||
const rootKey = await this.application.encryption.getRootKey()
|
||||
const rawKeyParams = await this.application.storage.getValue(StorageKey.RootKeyParams, StorageValueModes.Nonwrapped)
|
||||
await Factory.registerUserToApplication({ application: application })
|
||||
const rootKey = await application.encryption.getRootKey()
|
||||
const rawKeyParams = await application.storage.getValue(StorageKey.RootKeyParams, StorageValueModes.Nonwrapped)
|
||||
expect(rootKey.keyParams.content).to.eql(rawKeyParams)
|
||||
})
|
||||
|
||||
it('key params should have expected values', async function () {
|
||||
await Factory.registerUserToApplication({ application: this.application })
|
||||
const keyParamsObject = await this.application.encryption.getRootKeyParams()
|
||||
await Factory.registerUserToApplication({ application: application })
|
||||
const keyParamsObject = await application.encryption.getRootKeyParams()
|
||||
const keyParams = keyParamsObject.content
|
||||
expect(keyParams.identifier).to.be.ok
|
||||
expect(keyParams.pw_nonce).to.be.ok
|
||||
@@ -515,20 +516,18 @@ describe('keys', function () {
|
||||
})
|
||||
|
||||
it('key params obtained when signing in should have created and origination', async function () {
|
||||
const email = this.email
|
||||
const password = this.password
|
||||
await Factory.registerUserToApplication({
|
||||
application: this.application,
|
||||
application: application,
|
||||
email,
|
||||
password,
|
||||
})
|
||||
this.application = await Factory.signOutApplicationAndReturnNew(this.application)
|
||||
application = await Factory.signOutApplicationAndReturnNew(application)
|
||||
await Factory.loginToApplication({
|
||||
application: this.application,
|
||||
application: application,
|
||||
email,
|
||||
password,
|
||||
})
|
||||
const keyParamsObject = await this.application.encryption.getRootKeyParams()
|
||||
const keyParamsObject = await application.encryption.getRootKeyParams()
|
||||
const keyParams = keyParamsObject.content
|
||||
|
||||
expect(keyParams.created).to.be.ok
|
||||
@@ -541,12 +540,12 @@ describe('keys', function () {
|
||||
|
||||
/** Register with 003 version */
|
||||
await Factory.registerOldUser({
|
||||
application: this.application,
|
||||
email: this.email,
|
||||
password: this.password,
|
||||
application: application,
|
||||
email: email,
|
||||
password: password,
|
||||
version: ProtocolVersion.V003,
|
||||
})
|
||||
const keyParamsObject = await this.application.encryption.getRootKeyParams()
|
||||
const keyParamsObject = await application.encryption.getRootKeyParams()
|
||||
const keyParams = keyParamsObject.content
|
||||
|
||||
expect(keyParams.created).to.be.ok
|
||||
@@ -556,17 +555,17 @@ describe('keys', function () {
|
||||
it('encryption name should be dependent on key params version', async function () {
|
||||
/** Register with 003 account */
|
||||
await Factory.registerOldUser({
|
||||
application: this.application,
|
||||
email: this.email,
|
||||
password: this.password,
|
||||
application: application,
|
||||
email: email,
|
||||
password: password,
|
||||
version: ProtocolVersion.V003,
|
||||
})
|
||||
expect(await this.application.encryption.getEncryptionDisplayName()).to.equal('AES-256')
|
||||
expect(await application.encryption.getEncryptionDisplayName()).to.equal('AES-256')
|
||||
|
||||
this.application = await Factory.signOutApplicationAndReturnNew(this.application)
|
||||
application = await Factory.signOutApplicationAndReturnNew(application)
|
||||
/** Register with 004 account */
|
||||
await this.application.register(this.email + 'new', this.password)
|
||||
expect(await this.application.encryption.getEncryptionDisplayName()).to.equal('XChaCha20-Poly1305')
|
||||
await application.register(email + 'new', password)
|
||||
expect(await application.encryption.getEncryptionDisplayName()).to.equal('XChaCha20-Poly1305')
|
||||
})
|
||||
|
||||
it('when launching app with no keychain but data, should present account recovery challenge', async function () {
|
||||
@@ -575,21 +574,21 @@ describe('keys', function () {
|
||||
* when setting up a new device from restore, the keychain is deleted, but the data persists.
|
||||
* We want to make sure we're prompting the user to re-authenticate their account.
|
||||
*/
|
||||
const id = this.application.identifier
|
||||
const id = application.identifier
|
||||
await Factory.registerUserToApplication({
|
||||
application: this.application,
|
||||
email: this.email,
|
||||
password: this.password,
|
||||
application: application,
|
||||
email: email,
|
||||
password: password,
|
||||
})
|
||||
/** Simulate empty keychain */
|
||||
await this.application.device.clearRawKeychainValue()
|
||||
await application.device.clearRawKeychainValue()
|
||||
|
||||
const recreatedApp = await Factory.createApplicationWithFakeCrypto(id)
|
||||
let totalChallenges = 0
|
||||
const expectedChallenges = 1
|
||||
const receiveChallenge = (challenge) => {
|
||||
totalChallenges++
|
||||
recreatedApp.submitValuesForChallenge(challenge, [CreateChallengeValue(challenge.prompts[0], this.password)])
|
||||
recreatedApp.submitValuesForChallenge(challenge, [CreateChallengeValue(challenge.prompts[0], password)])
|
||||
}
|
||||
await recreatedApp.prepareForLaunch({ receiveChallenge })
|
||||
await recreatedApp.launch(true)
|
||||
@@ -610,13 +609,13 @@ describe('keys', function () {
|
||||
*/
|
||||
it.skip('should add new items key', async function () {
|
||||
this.timeout(Factory.TwentySecondTimeout * 3)
|
||||
let oldClient = this.application
|
||||
let oldClient = application
|
||||
|
||||
/** Register an 003 account */
|
||||
await Factory.registerOldUser({
|
||||
application: oldClient,
|
||||
email: this.email,
|
||||
password: this.password,
|
||||
email: email,
|
||||
password: password,
|
||||
version: ProtocolVersion.V003,
|
||||
})
|
||||
|
||||
@@ -626,8 +625,8 @@ describe('keys', function () {
|
||||
receiveChallenge: (challenge) => {
|
||||
/** Reauth session challenge */
|
||||
newClient.submitValuesForChallenge(challenge, [
|
||||
CreateChallengeValue(challenge.prompts[0], this.email),
|
||||
CreateChallengeValue(challenge.prompts[1], this.password),
|
||||
CreateChallengeValue(challenge.prompts[0], email),
|
||||
CreateChallengeValue(challenge.prompts[1], password),
|
||||
])
|
||||
},
|
||||
})
|
||||
@@ -636,11 +635,11 @@ describe('keys', function () {
|
||||
/** Change password through session manager directly instead of application,
|
||||
* as not to create any items key (to simulate 003 client behavior) */
|
||||
const currentRootKey = await oldClient.encryption.computeRootKey(
|
||||
this.password,
|
||||
password,
|
||||
await oldClient.encryption.getRootKeyParams(),
|
||||
)
|
||||
const operator = this.context.operators.operatorForVersion(ProtocolVersion.V003)
|
||||
const newRootKey = await operator.createRootKey(this.email, this.password)
|
||||
const operator = context.operators.operatorForVersion(ProtocolVersion.V003)
|
||||
const newRootKey = await operator.createRootKey(email, password)
|
||||
Object.defineProperty(oldClient.legacyApi, 'apiVersion', {
|
||||
get: function () {
|
||||
return '20190520'
|
||||
@@ -650,7 +649,7 @@ describe('keys', function () {
|
||||
/**
|
||||
* Sign in as late as possible on new client to prevent session timeouts
|
||||
*/
|
||||
await newClient.signIn(this.email, this.password)
|
||||
await newClient.signIn(email, password)
|
||||
|
||||
await oldClient.sessions.changeCredentials({
|
||||
currentServerPassword: currentRootKey.serverPassword,
|
||||
@@ -669,28 +668,28 @@ describe('keys', function () {
|
||||
})
|
||||
|
||||
it('should add new items key from migration if pw change already happened', async function () {
|
||||
this.context.anticipateConsoleError('Shared vault network errors due to not accepting JWT-based token')
|
||||
this.context.anticipateConsoleError(
|
||||
context.anticipateConsoleError('Shared vault network errors due to not accepting JWT-based token')
|
||||
context.anticipateConsoleError(
|
||||
'Cannot find items key to use for encryption',
|
||||
'No items keys being created in this test',
|
||||
)
|
||||
|
||||
/** Register an 003 account */
|
||||
await Factory.registerOldUser({
|
||||
application: this.application,
|
||||
email: this.email,
|
||||
password: this.password,
|
||||
application: application,
|
||||
email: email,
|
||||
password: password,
|
||||
version: ProtocolVersion.V003,
|
||||
})
|
||||
|
||||
/** Change password through session manager directly instead of application,
|
||||
* as not to create any items key (to simulate 003 client behavior) */
|
||||
const currentRootKey = await this.application.encryption.computeRootKey(
|
||||
this.password,
|
||||
await this.application.encryption.getRootKeyParams(),
|
||||
const currentRootKey = await application.encryption.computeRootKey(
|
||||
password,
|
||||
await application.encryption.getRootKeyParams(),
|
||||
)
|
||||
const operator = this.context.operators.operatorForVersion(ProtocolVersion.V003)
|
||||
const newRootKeyTemplate = await operator.createRootKey(this.email, this.password)
|
||||
const operator = context.operators.operatorForVersion(ProtocolVersion.V003)
|
||||
const newRootKeyTemplate = await operator.createRootKey(email, password)
|
||||
const newRootKey = CreateNewRootKey({
|
||||
...newRootKeyTemplate.content,
|
||||
...{
|
||||
@@ -699,28 +698,28 @@ describe('keys', function () {
|
||||
},
|
||||
})
|
||||
|
||||
Object.defineProperty(this.application.legacyApi, 'apiVersion', {
|
||||
Object.defineProperty(application.legacyApi, 'apiVersion', {
|
||||
get: function () {
|
||||
return '20190520'
|
||||
},
|
||||
})
|
||||
|
||||
/** Renew session to prevent timeouts */
|
||||
this.application = await Factory.signOutAndBackIn(this.application, this.email, this.password)
|
||||
application = await Factory.signOutAndBackIn(application, email, password)
|
||||
|
||||
await this.application.sessions.changeCredentials({
|
||||
await application.sessions.changeCredentials({
|
||||
currentServerPassword: currentRootKey.serverPassword,
|
||||
newRootKey,
|
||||
})
|
||||
await this.application.dependencies.get(TYPES.ReencryptTypeAItems).execute()
|
||||
await application.dependencies.get(TYPES.ReencryptTypeAItems).execute()
|
||||
/** Note: this may result in a deadlock if features_service syncs and results in an error */
|
||||
await this.application.sync.sync({ awaitAll: true })
|
||||
await application.sync.sync({ awaitAll: true })
|
||||
|
||||
/** Relaunch application and expect new items key to be created */
|
||||
const identifier = this.application.identifier
|
||||
const identifier = application.identifier
|
||||
/** Set to pre 2.0.15 version so migration runs */
|
||||
await this.application.device.setRawStorageValue(`${identifier}-snjs_version`, '2.0.14')
|
||||
await Factory.safeDeinit(this.application)
|
||||
await application.device.setRawStorageValue(`${identifier}-snjs_version`, '2.0.14')
|
||||
await Factory.safeDeinit(application)
|
||||
|
||||
const refreshedApp = await Factory.createApplicationWithFakeCrypto(identifier)
|
||||
await Factory.initializeApplication(refreshedApp)
|
||||
@@ -740,16 +739,16 @@ describe('keys', function () {
|
||||
* The corrective action was to do a final check in encryptionService.handleDownloadFirstSyncCompletion
|
||||
* to ensure there exists an items key corresponding to the user's account version.
|
||||
*/
|
||||
const promise = this.context.awaitNextSucessfulSync()
|
||||
await this.context.sync()
|
||||
const promise = context.awaitNextSucessfulSync()
|
||||
await context.sync()
|
||||
await promise
|
||||
|
||||
await this.application.items.removeAllItemsFromMemory()
|
||||
expect(this.application.encryption.getSureDefaultItemsKey()).to.not.be.ok
|
||||
await application.items.removeAllItemsFromMemory()
|
||||
expect(application.encryption.getSureDefaultItemsKey()).to.not.be.ok
|
||||
|
||||
const protocol003 = new SNProtocolOperator003(new SNWebCrypto())
|
||||
const key = await protocol003.createItemsKey()
|
||||
await this.application.mutator.emitItemFromPayload(
|
||||
await application.mutator.emitItemFromPayload(
|
||||
key.payload.copy({
|
||||
content: {
|
||||
...key.payload.content,
|
||||
@@ -761,30 +760,30 @@ describe('keys', function () {
|
||||
}),
|
||||
)
|
||||
|
||||
const defaultKey = this.application.encryption.getSureDefaultItemsKey()
|
||||
const defaultKey = application.encryption.getSureDefaultItemsKey()
|
||||
expect(defaultKey.keyVersion).to.equal(ProtocolVersion.V003)
|
||||
expect(defaultKey.uuid).to.equal(key.uuid)
|
||||
|
||||
await Factory.registerUserToApplication({ application: this.application })
|
||||
await Factory.registerUserToApplication({ application: application })
|
||||
|
||||
const notePayload = Factory.createNotePayload()
|
||||
expect(await this.application.encryption.itemsEncryption.keyToUseForItemEncryption(notePayload)).to.be.ok
|
||||
expect(await application.encryption.itemsEncryption.keyToUseForItemEncryption(notePayload)).to.be.ok
|
||||
})
|
||||
|
||||
it('having unsynced items keys should resync them upon download first sync completion', async function () {
|
||||
await Factory.registerUserToApplication({ application: this.application })
|
||||
const itemsKey = this.application.items.getDisplayableItemsKeys()[0]
|
||||
await this.application.mutator.emitItemFromPayload(
|
||||
await Factory.registerUserToApplication({ application: application })
|
||||
const itemsKey = application.items.getDisplayableItemsKeys()[0]
|
||||
await application.mutator.emitItemFromPayload(
|
||||
itemsKey.payload.copy({
|
||||
dirty: false,
|
||||
updated_at: new Date(0),
|
||||
deleted: false,
|
||||
}),
|
||||
)
|
||||
await this.application.sync.sync({
|
||||
await application.sync.sync({
|
||||
mode: SyncMode.DownloadFirst,
|
||||
})
|
||||
const updatedKey = this.application.items.findItem(itemsKey.uuid)
|
||||
const updatedKey = application.items.findItem(itemsKey.uuid)
|
||||
expect(updatedKey.neverSynced).to.equal(false)
|
||||
})
|
||||
|
||||
@@ -794,18 +793,18 @@ describe('keys', function () {
|
||||
otherClient.items.itemsKeyDisplayController.setDisplayOptions({ sortBy: 'dsc' })
|
||||
/** On client A, create account and note */
|
||||
await Factory.registerUserToApplication({
|
||||
application: this.application,
|
||||
email: this.email,
|
||||
password: this.password,
|
||||
application: application,
|
||||
email: email,
|
||||
password: password,
|
||||
})
|
||||
await Factory.createSyncedNote(this.application)
|
||||
const itemsKey = this.application.items.getItems(ContentType.TYPES.ItemsKey)[0]
|
||||
await Factory.createSyncedNote(application)
|
||||
const itemsKey = application.items.getItems(ContentType.TYPES.ItemsKey)[0]
|
||||
|
||||
/** Create another client and sign into account */
|
||||
await Factory.loginToApplication({
|
||||
application: otherClient,
|
||||
email: this.email,
|
||||
password: this.password,
|
||||
email: email,
|
||||
password: password,
|
||||
})
|
||||
const defaultKeys = otherClient.encryption.itemsEncryption.getItemsKeys().filter((key) => {
|
||||
return key.isDefault
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
/* eslint-disable no-unused-expressions */
|
||||
/* eslint-disable no-undef */
|
||||
|
||||
import FakeWebCrypto from './fake_web_crypto.js'
|
||||
import { AppContext } from './AppContext.js'
|
||||
import { VaultsContext } from './VaultsContext.js'
|
||||
@@ -63,6 +62,10 @@ export async function createVaultsContextWithRealCrypto(identifier) {
|
||||
return createVaultsContext({ identifier, crypto: new SNWebCrypto() })
|
||||
}
|
||||
|
||||
export async function createVaultsContextWithFakeCrypto(identifier) {
|
||||
return createVaultsContext({ identifier, crypto: new FakeWebCrypto() })
|
||||
}
|
||||
|
||||
export async function createVaultsContext({ identifier, crypto, email, password, host } = {}) {
|
||||
const context = new VaultsContext({ identifier, crypto, email, password, host })
|
||||
await context.initialize()
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
/* eslint-disable no-unused-expressions */
|
||||
/* eslint-disable no-undef */
|
||||
import * as Factory from './lib/factory.js'
|
||||
|
||||
chai.use(chaiAsPromised)
|
||||
const expect = chai.expect
|
||||
|
||||
@@ -9,6 +8,7 @@ const expect = chai.expect
|
||||
* Then check browser Memory tool to make sure there are no leaks.
|
||||
*/
|
||||
describe('memory', function () {
|
||||
let application
|
||||
before(async function () {
|
||||
localStorage.clear()
|
||||
})
|
||||
@@ -18,12 +18,12 @@ describe('memory', function () {
|
||||
})
|
||||
|
||||
beforeEach(async function () {
|
||||
this.application = await Factory.createInitAppWithFakeCrypto()
|
||||
application = await Factory.createInitAppWithFakeCrypto()
|
||||
})
|
||||
|
||||
afterEach(async function () {
|
||||
await Factory.safeDeinit(this.application)
|
||||
this.application = null
|
||||
await Factory.safeDeinit(application)
|
||||
application = undefined
|
||||
})
|
||||
|
||||
it('passes', async function () {
|
||||
|
||||
@@ -1,68 +1,76 @@
|
||||
import * as Factory from './lib/factory.js'
|
||||
|
||||
chai.use(chaiAsPromised)
|
||||
const expect = chai.expect
|
||||
|
||||
const createApp = async () => Factory.createInitAppWithFakeCrypto(Environment.Web, Platform.MacWeb)
|
||||
|
||||
const accountPassword = 'password'
|
||||
|
||||
const registerApp = async (snApp) => {
|
||||
const registerApp = async (application) => {
|
||||
const email = UuidGenerator.GenerateUuid()
|
||||
const password = accountPassword
|
||||
const ephemeral = false
|
||||
const mergeLocal = true
|
||||
|
||||
await snApp.register(email, password, ephemeral, mergeLocal)
|
||||
return snApp
|
||||
await application.register(email, password, ephemeral, mergeLocal)
|
||||
return application
|
||||
}
|
||||
|
||||
describe('mfa service', () => {
|
||||
let application
|
||||
|
||||
beforeEach(async () => {
|
||||
localStorage.clear()
|
||||
application = await Factory.createInitAppWithFakeCrypto(Environment.Web, Platform.MacWeb)
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
Factory.safeDeinit(application)
|
||||
localStorage.clear()
|
||||
application = undefined
|
||||
sinon.restore()
|
||||
})
|
||||
|
||||
it('generates 160 bit base32-encoded mfa secret', async () => {
|
||||
const RFC4648 = /[ABCDEFGHIJKLMNOPQRSTUVWXYZ234567]/g
|
||||
|
||||
const snApp = await createApp()
|
||||
const secret = await snApp.generateMfaSecret()
|
||||
const secret = await application.generateMfaSecret()
|
||||
expect(secret).to.have.lengthOf(32)
|
||||
expect(secret.replace(RFC4648, '')).to.have.lengthOf(0)
|
||||
|
||||
Factory.safeDeinit(snApp)
|
||||
})
|
||||
|
||||
it('activates mfa, checks if enabled, deactivates mfa', async () => {
|
||||
const snApp = await createApp().then(registerApp)
|
||||
Factory.handlePasswordChallenges(snApp, accountPassword)
|
||||
await registerApp(application)
|
||||
|
||||
expect(await snApp.isMfaActivated()).to.equal(false)
|
||||
Factory.handlePasswordChallenges(application, accountPassword)
|
||||
|
||||
const secret = await snApp.generateMfaSecret()
|
||||
const token = await snApp.getOtpToken(secret)
|
||||
expect(await application.isMfaActivated()).to.equal(false)
|
||||
|
||||
await snApp.enableMfa(secret, token)
|
||||
const secret = await application.generateMfaSecret()
|
||||
const token = await application.getOtpToken(secret)
|
||||
|
||||
expect(await snApp.isMfaActivated()).to.equal(true)
|
||||
await application.enableMfa(secret, token)
|
||||
|
||||
await snApp.disableMfa()
|
||||
expect(await application.isMfaActivated()).to.equal(true)
|
||||
|
||||
expect(await snApp.isMfaActivated()).to.equal(false)
|
||||
await application.disableMfa()
|
||||
|
||||
Factory.safeDeinit(snApp)
|
||||
expect(await application.isMfaActivated()).to.equal(false)
|
||||
}).timeout(Factory.TenSecondTimeout)
|
||||
|
||||
it('prompts for account password when disabling mfa', async () => {
|
||||
const snApp = await createApp().then(registerApp)
|
||||
Factory.handlePasswordChallenges(snApp, accountPassword)
|
||||
const secret = await snApp.generateMfaSecret()
|
||||
const token = await snApp.getOtpToken(secret)
|
||||
await registerApp(application)
|
||||
|
||||
sinon.spy(snApp.challenges, 'sendChallenge')
|
||||
await snApp.enableMfa(secret, token)
|
||||
await snApp.disableMfa()
|
||||
Factory.handlePasswordChallenges(application, accountPassword)
|
||||
const secret = await application.generateMfaSecret()
|
||||
const token = await application.getOtpToken(secret)
|
||||
|
||||
const spyCall = snApp.challenges.sendChallenge.getCall(0)
|
||||
sinon.spy(application.challenges, 'sendChallenge')
|
||||
await application.enableMfa(secret, token)
|
||||
await application.disableMfa()
|
||||
|
||||
const spyCall = application.challenges.sendChallenge.getCall(0)
|
||||
const challenge = spyCall.firstArg
|
||||
expect(challenge.prompts).to.have.lengthOf(2)
|
||||
expect(challenge.prompts[0].validation).to.equal(ChallengeValidation.AccountPassword)
|
||||
|
||||
Factory.safeDeinit(snApp)
|
||||
}).timeout(Factory.TenSecondTimeout)
|
||||
})
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import * as Factory from '../lib/factory.js'
|
||||
|
||||
chai.use(chaiAsPromised)
|
||||
const expect = chai.expect
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
/* eslint-disable no-unused-expressions */
|
||||
/* eslint-disable no-undef */
|
||||
import * as Factory from '../lib/factory.js'
|
||||
|
||||
chai.use(chaiAsPromised)
|
||||
const expect = chai.expect
|
||||
|
||||
@@ -11,25 +10,29 @@ const setupRandomUuid = () => {
|
||||
}
|
||||
|
||||
describe('web native folders migration', () => {
|
||||
let application
|
||||
|
||||
beforeEach(async function () {
|
||||
this.application = await Factory.createInitAppWithFakeCrypto()
|
||||
localStorage.clear()
|
||||
application = await Factory.createInitAppWithFakeCrypto()
|
||||
setupRandomUuid()
|
||||
})
|
||||
|
||||
afterEach(async function () {
|
||||
await Factory.safeDeinit(this.application)
|
||||
// TODO: cleanup uuid behind us or we'll mess other tests.
|
||||
await Factory.safeDeinit(application)
|
||||
application = undefined
|
||||
localStorage.clear()
|
||||
})
|
||||
|
||||
it('migration with flat tag folders', async function () {
|
||||
const titles = ['a', 'b', 'c']
|
||||
await makeTags(this.application, titles)
|
||||
await makeTags(application, titles)
|
||||
|
||||
// Run the migration
|
||||
await this.application.mutator.migrateTagsToFolders()
|
||||
await application.mutator.migrateTagsToFolders()
|
||||
|
||||
// Check new tags
|
||||
const result = extractTagHierarchy(this.application)
|
||||
const result = extractTagHierarchy(application)
|
||||
|
||||
expect(result).to.deep.equal({
|
||||
a: { _uuid: 'a' },
|
||||
@@ -40,13 +43,13 @@ describe('web native folders migration', () => {
|
||||
|
||||
it('migration with simple tag folders', async function () {
|
||||
const titles = ['a.b.c', 'b', 'a.b']
|
||||
await makeTags(this.application, titles)
|
||||
await makeTags(application, titles)
|
||||
|
||||
// Run the migration
|
||||
await this.application.mutator.migrateTagsToFolders()
|
||||
await application.mutator.migrateTagsToFolders()
|
||||
|
||||
// Check new tags
|
||||
const result = extractTagHierarchy(this.application)
|
||||
const result = extractTagHierarchy(application)
|
||||
|
||||
expect(result).to.deep.equal({
|
||||
a: {
|
||||
@@ -62,13 +65,13 @@ describe('web native folders migration', () => {
|
||||
|
||||
it('migration with more complex cases', async function () {
|
||||
const titles = ['a.b.c', 'b', 'a.b']
|
||||
await makeTags(this.application, titles)
|
||||
await makeTags(application, titles)
|
||||
|
||||
// Run the migration
|
||||
await this.application.mutator.migrateTagsToFolders()
|
||||
await application.mutator.migrateTagsToFolders()
|
||||
|
||||
// Check new tags
|
||||
const result = extractTagHierarchy(this.application)
|
||||
const result = extractTagHierarchy(application)
|
||||
|
||||
expect(result).to.deep.equal({
|
||||
a: {
|
||||
@@ -84,13 +87,13 @@ describe('web native folders migration', () => {
|
||||
|
||||
it('should produce a valid hierarchy cases with missing intermediate tags or unordered', async function () {
|
||||
const titles = ['y.2', 'w.3', 'y']
|
||||
await makeTags(this.application, titles)
|
||||
await makeTags(application, titles)
|
||||
|
||||
// Run the migration
|
||||
await this.application.mutator.migrateTagsToFolders()
|
||||
await application.mutator.migrateTagsToFolders()
|
||||
|
||||
// Check new tags
|
||||
const result = extractTagHierarchy(this.application)
|
||||
const result = extractTagHierarchy(application)
|
||||
|
||||
expect(result).to.deep.equal({
|
||||
w: {
|
||||
@@ -105,13 +108,13 @@ describe('web native folders migration', () => {
|
||||
|
||||
it('skip prefixed names', async function () {
|
||||
const titles = ['.something', '.something...something', 'something.a.b.c']
|
||||
await makeTags(this.application, titles)
|
||||
await makeTags(application, titles)
|
||||
|
||||
// Run the migration
|
||||
await this.application.mutator.migrateTagsToFolders()
|
||||
await application.mutator.migrateTagsToFolders()
|
||||
|
||||
// Check new tags
|
||||
const result = extractTagHierarchy(this.application)
|
||||
const result = extractTagHierarchy(application)
|
||||
|
||||
expect(result).to.deep.equal({
|
||||
'.something': { _uuid: '.something' },
|
||||
@@ -132,13 +135,13 @@ describe('web native folders migration', () => {
|
||||
'a',
|
||||
'something..another.thing..anyway',
|
||||
]
|
||||
await makeTags(this.application, titles)
|
||||
await makeTags(application, titles)
|
||||
|
||||
// Run the migration
|
||||
await this.application.mutator.migrateTagsToFolders()
|
||||
await application.mutator.migrateTagsToFolders()
|
||||
|
||||
// Check new tags
|
||||
const result = extractTagHierarchy(this.application)
|
||||
const result = extractTagHierarchy(application)
|
||||
|
||||
expect(result).to.deep.equal({
|
||||
'something.': { _uuid: 'something.' },
|
||||
|
||||
@@ -1,37 +1,26 @@
|
||||
/* eslint-disable camelcase */
|
||||
/* eslint-disable no-unused-expressions */
|
||||
/* eslint-disable no-undef */
|
||||
import { BaseItemCounts } from '../lib/BaseItemCounts.js'
|
||||
import * as Factory from '../lib/factory.js'
|
||||
|
||||
chai.use(chaiAsPromised)
|
||||
const expect = chai.expect
|
||||
|
||||
describe('app models', () => {
|
||||
const sharedApplication = Factory.createApplicationWithFakeCrypto()
|
||||
|
||||
before(async function () {
|
||||
localStorage.clear()
|
||||
await Factory.initializeApplication(sharedApplication)
|
||||
})
|
||||
|
||||
after(async function () {
|
||||
localStorage.clear()
|
||||
await Factory.safeDeinit(sharedApplication)
|
||||
})
|
||||
let application
|
||||
let expectedItemCount
|
||||
let context
|
||||
|
||||
beforeEach(async function () {
|
||||
this.expectedItemCount = BaseItemCounts.DefaultItems
|
||||
this.context = await Factory.createAppContext()
|
||||
this.application = this.context.application
|
||||
await this.context.launch()
|
||||
localStorage.clear()
|
||||
expectedItemCount = BaseItemCounts.DefaultItems
|
||||
context = await Factory.createAppContext()
|
||||
application = context.application
|
||||
await context.launch()
|
||||
})
|
||||
|
||||
afterEach(async function () {
|
||||
await Factory.safeDeinit(this.application)
|
||||
})
|
||||
|
||||
it('payloads should be defined', () => {
|
||||
expect(sharedApplication.payloads).to.be.ok
|
||||
await context.deinit()
|
||||
localStorage.clear()
|
||||
sinon.restore()
|
||||
})
|
||||
|
||||
it('item should be defined', () => {
|
||||
@@ -72,17 +61,17 @@ describe('app models', () => {
|
||||
},
|
||||
})
|
||||
|
||||
await this.application.mutator.emitItemsFromPayloads([mutated], PayloadEmitSource.LocalChanged)
|
||||
await this.application.mutator.emitItemsFromPayloads([params2], PayloadEmitSource.LocalChanged)
|
||||
await application.mutator.emitItemsFromPayloads([mutated], PayloadEmitSource.LocalChanged)
|
||||
await application.mutator.emitItemsFromPayloads([params2], PayloadEmitSource.LocalChanged)
|
||||
|
||||
const item1 = this.application.items.findItem(params1.uuid)
|
||||
const item2 = this.application.items.findItem(params2.uuid)
|
||||
const item1 = application.items.findItem(params1.uuid)
|
||||
const item2 = application.items.findItem(params2.uuid)
|
||||
|
||||
expect(item1.content.references.length).to.equal(1)
|
||||
expect(item2.content.references.length).to.equal(0)
|
||||
|
||||
expect(this.application.items.itemsReferencingItem(item1).length).to.equal(0)
|
||||
expect(this.application.items.itemsReferencingItem(item2).length).to.equal(1)
|
||||
expect(application.items.itemsReferencingItem(item1).length).to.equal(0)
|
||||
expect(application.items.itemsReferencingItem(item2).length).to.equal(1)
|
||||
})
|
||||
|
||||
it('mapping an item twice shouldnt cause problems', async function () {
|
||||
@@ -95,45 +84,45 @@ describe('app models', () => {
|
||||
},
|
||||
})
|
||||
|
||||
let items = await this.application.mutator.emitItemsFromPayloads([mutated], PayloadEmitSource.LocalChanged)
|
||||
let items = await application.mutator.emitItemsFromPayloads([mutated], PayloadEmitSource.LocalChanged)
|
||||
let item = items[0]
|
||||
expect(item).to.be.ok
|
||||
|
||||
items = await this.application.mutator.emitItemsFromPayloads([mutated], PayloadEmitSource.LocalChanged)
|
||||
items = await application.mutator.emitItemsFromPayloads([mutated], PayloadEmitSource.LocalChanged)
|
||||
item = items[0]
|
||||
|
||||
expect(item.content.foo).to.equal('bar')
|
||||
expect(this.application.items.getDisplayableNotes().length).to.equal(1)
|
||||
expect(application.items.getDisplayableNotes().length).to.equal(1)
|
||||
})
|
||||
|
||||
it('mapping item twice should preserve references', async function () {
|
||||
const item1 = await Factory.createMappedNote(this.application)
|
||||
const item2 = await Factory.createMappedNote(this.application)
|
||||
const item1 = await Factory.createMappedNote(application)
|
||||
const item2 = await Factory.createMappedNote(application)
|
||||
|
||||
await this.application.mutator.changeItem(item1, (mutator) => {
|
||||
await application.mutator.changeItem(item1, (mutator) => {
|
||||
mutator.e2ePendingRefactor_addItemAsRelationship(item2)
|
||||
})
|
||||
await this.application.mutator.changeItem(item2, (mutator) => {
|
||||
await application.mutator.changeItem(item2, (mutator) => {
|
||||
mutator.e2ePendingRefactor_addItemAsRelationship(item1)
|
||||
})
|
||||
|
||||
const refreshedItem = this.application.items.findItem(item1.uuid)
|
||||
const refreshedItem = application.items.findItem(item1.uuid)
|
||||
expect(refreshedItem.content.references.length).to.equal(1)
|
||||
})
|
||||
|
||||
it('fixes relationship integrity', async function () {
|
||||
var item1 = await Factory.createMappedNote(this.application)
|
||||
var item2 = await Factory.createMappedNote(this.application)
|
||||
var item1 = await Factory.createMappedNote(application)
|
||||
var item2 = await Factory.createMappedNote(application)
|
||||
|
||||
await this.application.mutator.changeItem(item1, (mutator) => {
|
||||
await application.mutator.changeItem(item1, (mutator) => {
|
||||
mutator.e2ePendingRefactor_addItemAsRelationship(item2)
|
||||
})
|
||||
await this.application.mutator.changeItem(item2, (mutator) => {
|
||||
await application.mutator.changeItem(item2, (mutator) => {
|
||||
mutator.e2ePendingRefactor_addItemAsRelationship(item1)
|
||||
})
|
||||
|
||||
const refreshedItem1 = this.application.items.findItem(item1.uuid)
|
||||
const refreshedItem2 = this.application.items.findItem(item2.uuid)
|
||||
const refreshedItem1 = application.items.findItem(item1.uuid)
|
||||
const refreshedItem2 = application.items.findItem(item2.uuid)
|
||||
|
||||
expect(refreshedItem1.content.references.length).to.equal(1)
|
||||
expect(refreshedItem2.content.references.length).to.equal(1)
|
||||
@@ -145,54 +134,54 @@ describe('app models', () => {
|
||||
references: [],
|
||||
},
|
||||
})
|
||||
await this.application.mutator.emitItemsFromPayloads([damagedPayload], PayloadEmitSource.LocalChanged)
|
||||
await application.mutator.emitItemsFromPayloads([damagedPayload], PayloadEmitSource.LocalChanged)
|
||||
|
||||
const refreshedItem1_2 = this.application.items.findItem(item1.uuid)
|
||||
const refreshedItem2_2 = this.application.items.findItem(item2.uuid)
|
||||
const refreshedItem1_2 = application.items.findItem(item1.uuid)
|
||||
const refreshedItem2_2 = application.items.findItem(item2.uuid)
|
||||
|
||||
expect(refreshedItem1_2.content.references.length).to.equal(0)
|
||||
expect(refreshedItem2_2.content.references.length).to.equal(1)
|
||||
})
|
||||
|
||||
it('creating and removing relationships between two items should have valid references', async function () {
|
||||
var item1 = await Factory.createMappedNote(this.application)
|
||||
var item2 = await Factory.createMappedNote(this.application)
|
||||
await this.application.mutator.changeItem(item1, (mutator) => {
|
||||
var item1 = await Factory.createMappedNote(application)
|
||||
var item2 = await Factory.createMappedNote(application)
|
||||
await application.mutator.changeItem(item1, (mutator) => {
|
||||
mutator.e2ePendingRefactor_addItemAsRelationship(item2)
|
||||
})
|
||||
await this.application.mutator.changeItem(item2, (mutator) => {
|
||||
await application.mutator.changeItem(item2, (mutator) => {
|
||||
mutator.e2ePendingRefactor_addItemAsRelationship(item1)
|
||||
})
|
||||
|
||||
const refreshedItem1 = this.application.items.findItem(item1.uuid)
|
||||
const refreshedItem2 = this.application.items.findItem(item2.uuid)
|
||||
const refreshedItem1 = application.items.findItem(item1.uuid)
|
||||
const refreshedItem2 = application.items.findItem(item2.uuid)
|
||||
|
||||
expect(refreshedItem1.content.references.length).to.equal(1)
|
||||
expect(refreshedItem2.content.references.length).to.equal(1)
|
||||
|
||||
expect(this.application.items.itemsReferencingItem(item1)).to.include(refreshedItem2)
|
||||
expect(this.application.items.itemsReferencingItem(item2)).to.include(refreshedItem1)
|
||||
expect(application.items.itemsReferencingItem(item1)).to.include(refreshedItem2)
|
||||
expect(application.items.itemsReferencingItem(item2)).to.include(refreshedItem1)
|
||||
|
||||
await this.application.mutator.changeItem(item1, (mutator) => {
|
||||
await application.mutator.changeItem(item1, (mutator) => {
|
||||
mutator.removeItemAsRelationship(item2)
|
||||
})
|
||||
await this.application.mutator.changeItem(item2, (mutator) => {
|
||||
await application.mutator.changeItem(item2, (mutator) => {
|
||||
mutator.removeItemAsRelationship(item1)
|
||||
})
|
||||
|
||||
const refreshedItem1_2 = this.application.items.findItem(item1.uuid)
|
||||
const refreshedItem2_2 = this.application.items.findItem(item2.uuid)
|
||||
const refreshedItem1_2 = application.items.findItem(item1.uuid)
|
||||
const refreshedItem2_2 = application.items.findItem(item2.uuid)
|
||||
|
||||
expect(refreshedItem1_2.content.references.length).to.equal(0)
|
||||
expect(refreshedItem2_2.content.references.length).to.equal(0)
|
||||
|
||||
expect(this.application.items.itemsReferencingItem(item1).length).to.equal(0)
|
||||
expect(this.application.items.itemsReferencingItem(item2).length).to.equal(0)
|
||||
expect(application.items.itemsReferencingItem(item1).length).to.equal(0)
|
||||
expect(application.items.itemsReferencingItem(item2).length).to.equal(0)
|
||||
})
|
||||
|
||||
it('properly duplicates item with no relationships', async function () {
|
||||
const item = await Factory.createMappedNote(this.application)
|
||||
const duplicate = await this.application.mutator.duplicateItem(item)
|
||||
const item = await Factory.createMappedNote(application)
|
||||
const duplicate = await application.mutator.duplicateItem(item)
|
||||
expect(duplicate.uuid).to.not.equal(item.uuid)
|
||||
expect(item.isItemContentEqualWith(duplicate)).to.equal(true)
|
||||
expect(item.created_at.toISOString()).to.equal(duplicate.created_at.toISOString())
|
||||
@@ -200,36 +189,36 @@ describe('app models', () => {
|
||||
})
|
||||
|
||||
it('properly duplicates item with relationships', async function () {
|
||||
const item1 = await Factory.createMappedNote(this.application)
|
||||
const item2 = await Factory.createMappedNote(this.application)
|
||||
const item1 = await Factory.createMappedNote(application)
|
||||
const item2 = await Factory.createMappedNote(application)
|
||||
|
||||
const refreshedItem1 = await this.application.mutator.changeItem(item1, (mutator) => {
|
||||
const refreshedItem1 = await application.mutator.changeItem(item1, (mutator) => {
|
||||
mutator.e2ePendingRefactor_addItemAsRelationship(item2)
|
||||
})
|
||||
|
||||
expect(refreshedItem1.content.references.length).to.equal(1)
|
||||
|
||||
const duplicate = await this.application.mutator.duplicateItem(item1)
|
||||
const duplicate = await application.mutator.duplicateItem(item1)
|
||||
expect(duplicate.uuid).to.not.equal(item1.uuid)
|
||||
expect(duplicate.content.references.length).to.equal(1)
|
||||
|
||||
expect(this.application.items.itemsReferencingItem(item1).length).to.equal(0)
|
||||
expect(this.application.items.itemsReferencingItem(item2).length).to.equal(2)
|
||||
expect(application.items.itemsReferencingItem(item1).length).to.equal(0)
|
||||
expect(application.items.itemsReferencingItem(item2).length).to.equal(2)
|
||||
|
||||
const refreshedItem1_2 = this.application.items.findItem(item1.uuid)
|
||||
const refreshedItem1_2 = application.items.findItem(item1.uuid)
|
||||
expect(refreshedItem1_2.isItemContentEqualWith(duplicate)).to.equal(true)
|
||||
expect(refreshedItem1_2.created_at.toISOString()).to.equal(duplicate.created_at.toISOString())
|
||||
expect(refreshedItem1_2.content_type).to.equal(duplicate.content_type)
|
||||
})
|
||||
|
||||
it('removing references should update cross-refs', async function () {
|
||||
const item1 = await Factory.createMappedNote(this.application)
|
||||
const item2 = await Factory.createMappedNote(this.application)
|
||||
const refreshedItem1 = await this.application.mutator.changeItem(item1, (mutator) => {
|
||||
const item1 = await Factory.createMappedNote(application)
|
||||
const item2 = await Factory.createMappedNote(application)
|
||||
const refreshedItem1 = await application.mutator.changeItem(item1, (mutator) => {
|
||||
mutator.e2ePendingRefactor_addItemAsRelationship(item2)
|
||||
})
|
||||
|
||||
const refreshedItem1_2 = await this.application.mutator.emitItemFromPayload(
|
||||
const refreshedItem1_2 = await application.mutator.emitItemFromPayload(
|
||||
refreshedItem1.payloadRepresentation({
|
||||
deleted: true,
|
||||
content: {
|
||||
@@ -240,49 +229,49 @@ describe('app models', () => {
|
||||
PayloadEmitSource.LocalChanged,
|
||||
)
|
||||
|
||||
expect(this.application.items.itemsReferencingItem(item2).length).to.equal(0)
|
||||
expect(this.application.items.itemsReferencingItem(item1).length).to.equal(0)
|
||||
expect(application.items.itemsReferencingItem(item2).length).to.equal(0)
|
||||
expect(application.items.itemsReferencingItem(item1).length).to.equal(0)
|
||||
expect(refreshedItem1_2.content.references.length).to.equal(0)
|
||||
})
|
||||
|
||||
it('properly handles single item uuid alternation', async function () {
|
||||
const item1 = await Factory.createMappedNote(this.application)
|
||||
const item2 = await Factory.createMappedNote(this.application)
|
||||
const item1 = await Factory.createMappedNote(application)
|
||||
const item2 = await Factory.createMappedNote(application)
|
||||
|
||||
const refreshedItem1 = await this.application.mutator.changeItem(item1, (mutator) => {
|
||||
const refreshedItem1 = await application.mutator.changeItem(item1, (mutator) => {
|
||||
mutator.e2ePendingRefactor_addItemAsRelationship(item2)
|
||||
})
|
||||
|
||||
expect(refreshedItem1.content.references.length).to.equal(1)
|
||||
expect(this.application.items.itemsReferencingItem(item2).length).to.equal(1)
|
||||
expect(application.items.itemsReferencingItem(item2).length).to.equal(1)
|
||||
|
||||
const alternatedItem = await Factory.alternateUuidForItem(this.application, item1.uuid)
|
||||
const refreshedItem1_2 = this.application.items.findItem(item1.uuid)
|
||||
const alternatedItem = await Factory.alternateUuidForItem(application, item1.uuid)
|
||||
const refreshedItem1_2 = application.items.findItem(item1.uuid)
|
||||
expect(refreshedItem1_2).to.not.be.ok
|
||||
|
||||
expect(this.application.items.getDisplayableNotes().length).to.equal(2)
|
||||
expect(application.items.getDisplayableNotes().length).to.equal(2)
|
||||
|
||||
expect(alternatedItem.content.references.length).to.equal(1)
|
||||
expect(this.application.items.itemsReferencingItem(alternatedItem.uuid).length).to.equal(0)
|
||||
expect(application.items.itemsReferencingItem(alternatedItem.uuid).length).to.equal(0)
|
||||
|
||||
expect(this.application.items.itemsReferencingItem(item2).length).to.equal(1)
|
||||
expect(application.items.itemsReferencingItem(item2).length).to.equal(1)
|
||||
|
||||
expect(alternatedItem.isReferencingItem(item2)).to.equal(true)
|
||||
expect(alternatedItem.dirty).to.equal(true)
|
||||
})
|
||||
|
||||
it('alterating uuid of item should fill its duplicateOf value', async function () {
|
||||
const item1 = await Factory.createMappedNote(this.application)
|
||||
const alternatedItem = await Factory.alternateUuidForItem(this.application, item1.uuid)
|
||||
const item1 = await Factory.createMappedNote(application)
|
||||
const alternatedItem = await Factory.alternateUuidForItem(application, item1.uuid)
|
||||
expect(alternatedItem.duplicateOf).to.equal(item1.uuid)
|
||||
})
|
||||
|
||||
it('alterating itemskey uuid should update errored items encrypted with that key', async function () {
|
||||
const item1 = await Factory.createMappedNote(this.application)
|
||||
const itemsKey = this.application.items.getDisplayableItemsKeys()[0]
|
||||
const item1 = await Factory.createMappedNote(application)
|
||||
const itemsKey = application.items.getDisplayableItemsKeys()[0]
|
||||
|
||||
/** Encrypt item1 and emit as errored so it persists with items_key_id */
|
||||
const encrypted = await this.application.encryption.encryptSplitSingle({
|
||||
const encrypted = await application.encryption.encryptSplitSingle({
|
||||
usesItemsKeyWithKeyLookup: {
|
||||
items: [item1.payload],
|
||||
},
|
||||
@@ -292,46 +281,46 @@ describe('app models', () => {
|
||||
waitingForKey: true,
|
||||
})
|
||||
|
||||
await this.application.payloads.emitPayload(errored)
|
||||
await application.payloads.emitPayload(errored)
|
||||
|
||||
expect(this.application.payloads.findOne(item1.uuid).errorDecrypting).to.equal(true)
|
||||
expect(this.application.payloads.findOne(item1.uuid).items_key_id).to.equal(itemsKey.uuid)
|
||||
expect(application.payloads.findOne(item1.uuid).errorDecrypting).to.equal(true)
|
||||
expect(application.payloads.findOne(item1.uuid).items_key_id).to.equal(itemsKey.uuid)
|
||||
|
||||
sinon.stub(this.application.encryption.itemsEncryption, 'decryptErroredItemPayloads').callsFake(() => {
|
||||
sinon.stub(application.encryption.itemsEncryption, 'decryptErroredItemPayloads').callsFake(() => {
|
||||
// prevent auto decryption
|
||||
})
|
||||
|
||||
const alternatedKey = await Factory.alternateUuidForItem(this.application, itemsKey.uuid)
|
||||
const updatedPayload = this.application.payloads.findOne(item1.uuid)
|
||||
const alternatedKey = await Factory.alternateUuidForItem(application, itemsKey.uuid)
|
||||
const updatedPayload = application.payloads.findOne(item1.uuid)
|
||||
|
||||
expect(updatedPayload.items_key_id).to.equal(alternatedKey.uuid)
|
||||
})
|
||||
|
||||
it('properly handles mutli item uuid alternation', async function () {
|
||||
const item1 = await Factory.createMappedNote(this.application)
|
||||
const item2 = await Factory.createMappedNote(this.application)
|
||||
this.expectedItemCount += 2
|
||||
const item1 = await Factory.createMappedNote(application)
|
||||
const item2 = await Factory.createMappedNote(application)
|
||||
expectedItemCount += 2
|
||||
|
||||
await this.application.mutator.changeItem(item1, (mutator) => {
|
||||
await application.mutator.changeItem(item1, (mutator) => {
|
||||
mutator.e2ePendingRefactor_addItemAsRelationship(item2)
|
||||
})
|
||||
|
||||
expect(this.application.items.itemsReferencingItem(item2).length).to.equal(1)
|
||||
expect(application.items.itemsReferencingItem(item2).length).to.equal(1)
|
||||
|
||||
const alternatedItem1 = await Factory.alternateUuidForItem(this.application, item1.uuid)
|
||||
const alternatedItem2 = await Factory.alternateUuidForItem(this.application, item2.uuid)
|
||||
const alternatedItem1 = await Factory.alternateUuidForItem(application, item1.uuid)
|
||||
const alternatedItem2 = await Factory.alternateUuidForItem(application, item2.uuid)
|
||||
|
||||
expect(this.application.items.items.length).to.equal(this.expectedItemCount)
|
||||
expect(application.items.items.length).to.equal(expectedItemCount)
|
||||
|
||||
expect(item1.uuid).to.not.equal(alternatedItem1.uuid)
|
||||
expect(item2.uuid).to.not.equal(alternatedItem2.uuid)
|
||||
|
||||
const refreshedAltItem1 = this.application.items.findItem(alternatedItem1.uuid)
|
||||
const refreshedAltItem1 = application.items.findItem(alternatedItem1.uuid)
|
||||
expect(refreshedAltItem1.content.references.length).to.equal(1)
|
||||
expect(refreshedAltItem1.content.references[0].uuid).to.equal(alternatedItem2.uuid)
|
||||
expect(alternatedItem2.content.references.length).to.equal(0)
|
||||
|
||||
expect(this.application.items.itemsReferencingItem(alternatedItem2).length).to.equal(1)
|
||||
expect(application.items.itemsReferencingItem(alternatedItem2).length).to.equal(1)
|
||||
|
||||
expect(refreshedAltItem1.isReferencingItem(alternatedItem2)).to.equal(true)
|
||||
expect(alternatedItem2.isReferencingItem(refreshedAltItem1)).to.equal(false)
|
||||
@@ -339,39 +328,39 @@ describe('app models', () => {
|
||||
})
|
||||
|
||||
it('maintains referencing relationships when duplicating', async function () {
|
||||
const tag = await Factory.createMappedTag(this.application)
|
||||
const note = await Factory.createMappedNote(this.application)
|
||||
const refreshedTag = await this.application.mutator.changeItem(tag, (mutator) => {
|
||||
const tag = await Factory.createMappedTag(application)
|
||||
const note = await Factory.createMappedNote(application)
|
||||
const refreshedTag = await application.mutator.changeItem(tag, (mutator) => {
|
||||
mutator.e2ePendingRefactor_addItemAsRelationship(note)
|
||||
})
|
||||
|
||||
expect(refreshedTag.content.references.length).to.equal(1)
|
||||
|
||||
const noteCopy = await this.application.mutator.duplicateItem(note)
|
||||
const noteCopy = await application.mutator.duplicateItem(note)
|
||||
expect(note.uuid).to.not.equal(noteCopy.uuid)
|
||||
|
||||
expect(this.application.items.getDisplayableNotes().length).to.equal(2)
|
||||
expect(this.application.items.getDisplayableTags().length).to.equal(1)
|
||||
expect(application.items.getDisplayableNotes().length).to.equal(2)
|
||||
expect(application.items.getDisplayableTags().length).to.equal(1)
|
||||
|
||||
expect(note.content.references.length).to.equal(0)
|
||||
expect(noteCopy.content.references.length).to.equal(0)
|
||||
const refreshedTag_2 = this.application.items.findItem(tag.uuid)
|
||||
const refreshedTag_2 = application.items.findItem(tag.uuid)
|
||||
expect(refreshedTag_2.content.references.length).to.equal(2)
|
||||
})
|
||||
|
||||
it('maintains editor reference when duplicating note', async function () {
|
||||
const component = await this.application.mutator.createItem(
|
||||
const component = await application.mutator.createItem(
|
||||
ContentType.TYPES.Component,
|
||||
{ area: ComponentArea.Editor, package_info: { identifier: 'foo-editor' } },
|
||||
true,
|
||||
)
|
||||
const note = await Factory.insertItemWithOverride(this.application, ContentType.TYPES.Note, {
|
||||
const note = await Factory.insertItemWithOverride(application, ContentType.TYPES.Note, {
|
||||
editorIdentifier: 'foo-editor',
|
||||
})
|
||||
|
||||
expect(this.application.componentManager.editorForNote(note).uniqueIdentifier.value).to.equal(component.uuid)
|
||||
expect(application.componentManager.editorForNote(note).uniqueIdentifier.value).to.equal(component.uuid)
|
||||
|
||||
const duplicate = await this.application.mutator.duplicateItem(note, true)
|
||||
expect(this.application.componentManager.editorForNote(duplicate).uniqueIdentifier.value).to.equal(component.uuid)
|
||||
const duplicate = await application.mutator.duplicateItem(note, true)
|
||||
expect(application.componentManager.editorForNote(duplicate).uniqueIdentifier.value).to.equal(component.uuid)
|
||||
})
|
||||
})
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,6 @@
|
||||
/* eslint-disable no-unused-expressions */
|
||||
/* eslint-disable no-undef */
|
||||
import { BaseItemCounts } from '../lib/BaseItemCounts.js'
|
||||
import * as Factory from '../lib/factory.js'
|
||||
|
||||
chai.use(chaiAsPromised)
|
||||
const expect = chai.expect
|
||||
|
||||
@@ -11,47 +10,52 @@ describe('items', () => {
|
||||
awaitAll: true,
|
||||
}
|
||||
|
||||
let application
|
||||
let expectedItemCount
|
||||
|
||||
beforeEach(async function () {
|
||||
this.expectedItemCount = BaseItemCounts.DefaultItems
|
||||
this.application = await Factory.createInitAppWithFakeCrypto()
|
||||
localStorage.clear()
|
||||
expectedItemCount = BaseItemCounts.DefaultItems
|
||||
application = await Factory.createInitAppWithFakeCrypto()
|
||||
})
|
||||
|
||||
afterEach(async function () {
|
||||
await Factory.safeDeinit(this.application)
|
||||
await Factory.safeDeinit(application)
|
||||
localStorage.clear()
|
||||
})
|
||||
|
||||
it('setting an item as dirty should update its client updated at', async function () {
|
||||
const params = Factory.createNotePayload()
|
||||
await this.application.mutator.emitItemsFromPayloads([params], PayloadEmitSource.LocalChanged)
|
||||
const item = this.application.items.items[0]
|
||||
await application.mutator.emitItemsFromPayloads([params], PayloadEmitSource.LocalChanged)
|
||||
const item = application.items.items[0]
|
||||
const prevDate = item.userModifiedDate.getTime()
|
||||
await Factory.sleep(0.1)
|
||||
await this.application.mutator.setItemDirty(item, true)
|
||||
const refreshedItem = this.application.items.findItem(item.uuid)
|
||||
await application.mutator.setItemDirty(item, true)
|
||||
const refreshedItem = application.items.findItem(item.uuid)
|
||||
const newDate = refreshedItem.userModifiedDate.getTime()
|
||||
expect(prevDate).to.not.equal(newDate)
|
||||
})
|
||||
|
||||
it('setting an item as dirty with option to skip client updated at', async function () {
|
||||
const params = Factory.createNotePayload()
|
||||
await this.application.mutator.emitItemsFromPayloads([params], PayloadEmitSource.LocalChanged)
|
||||
const item = this.application.items.items[0]
|
||||
await application.mutator.emitItemsFromPayloads([params], PayloadEmitSource.LocalChanged)
|
||||
const item = application.items.items[0]
|
||||
const prevDate = item.userModifiedDate.getTime()
|
||||
await Factory.sleep(0.1)
|
||||
await this.application.mutator.setItemDirty(item)
|
||||
await application.mutator.setItemDirty(item)
|
||||
const newDate = item.userModifiedDate.getTime()
|
||||
expect(prevDate).to.equal(newDate)
|
||||
})
|
||||
|
||||
it('properly pins, archives, and locks', async function () {
|
||||
const params = Factory.createNotePayload()
|
||||
await this.application.mutator.emitItemsFromPayloads([params], PayloadEmitSource.LocalChanged)
|
||||
await application.mutator.emitItemsFromPayloads([params], PayloadEmitSource.LocalChanged)
|
||||
|
||||
const item = this.application.items.items[0]
|
||||
const item = application.items.items[0]
|
||||
expect(item.pinned).to.not.be.ok
|
||||
|
||||
const refreshedItem = (
|
||||
await this.application.changeAndSaveItem.execute(
|
||||
await application.changeAndSaveItem.execute(
|
||||
item,
|
||||
(mutator) => {
|
||||
mutator.pinned = true
|
||||
@@ -71,16 +75,16 @@ describe('items', () => {
|
||||
it('properly compares item equality', async function () {
|
||||
const params1 = Factory.createNotePayload()
|
||||
const params2 = Factory.createNotePayload()
|
||||
await this.application.mutator.emitItemsFromPayloads([params1, params2], PayloadEmitSource.LocalChanged)
|
||||
await application.mutator.emitItemsFromPayloads([params1, params2], PayloadEmitSource.LocalChanged)
|
||||
|
||||
let item1 = this.application.items.getDisplayableNotes()[0]
|
||||
let item2 = this.application.items.getDisplayableNotes()[1]
|
||||
let item1 = application.items.getDisplayableNotes()[0]
|
||||
let item2 = application.items.getDisplayableNotes()[1]
|
||||
|
||||
expect(item1.isItemContentEqualWith(item2)).to.equal(true)
|
||||
|
||||
// items should ignore this field when checking for equality
|
||||
item1 = (
|
||||
await this.application.changeAndSaveItem.execute(
|
||||
await application.changeAndSaveItem.execute(
|
||||
item1,
|
||||
(mutator) => {
|
||||
mutator.userModifiedDate = new Date()
|
||||
@@ -91,7 +95,7 @@ describe('items', () => {
|
||||
)
|
||||
).getValue()
|
||||
item2 = (
|
||||
await this.application.changeAndSaveItem.execute(
|
||||
await application.changeAndSaveItem.execute(
|
||||
item2,
|
||||
(mutator) => {
|
||||
mutator.userModifiedDate = undefined
|
||||
@@ -105,7 +109,7 @@ describe('items', () => {
|
||||
expect(item1.isItemContentEqualWith(item2)).to.equal(true)
|
||||
|
||||
item1 = (
|
||||
await this.application.changeAndSaveItem.execute(
|
||||
await application.changeAndSaveItem.execute(
|
||||
item1,
|
||||
(mutator) => {
|
||||
mutator.mutableContent.foo = 'bar'
|
||||
@@ -119,7 +123,7 @@ describe('items', () => {
|
||||
expect(item1.isItemContentEqualWith(item2)).to.equal(false)
|
||||
|
||||
item2 = (
|
||||
await this.application.changeAndSaveItem.execute(
|
||||
await application.changeAndSaveItem.execute(
|
||||
item2,
|
||||
(mutator) => {
|
||||
mutator.mutableContent.foo = 'bar'
|
||||
@@ -134,7 +138,7 @@ describe('items', () => {
|
||||
expect(item2.isItemContentEqualWith(item1)).to.equal(true)
|
||||
|
||||
item1 = (
|
||||
await this.application.changeAndSaveItem.execute(
|
||||
await application.changeAndSaveItem.execute(
|
||||
item1,
|
||||
(mutator) => {
|
||||
mutator.e2ePendingRefactor_addItemAsRelationship(item2)
|
||||
@@ -145,7 +149,7 @@ describe('items', () => {
|
||||
)
|
||||
).getValue()
|
||||
item2 = (
|
||||
await this.application.changeAndSaveItem.execute(
|
||||
await application.changeAndSaveItem.execute(
|
||||
item2,
|
||||
(mutator) => {
|
||||
mutator.e2ePendingRefactor_addItemAsRelationship(item1)
|
||||
@@ -162,7 +166,7 @@ describe('items', () => {
|
||||
expect(item1.isItemContentEqualWith(item2)).to.equal(false)
|
||||
|
||||
item1 = (
|
||||
await this.application.changeAndSaveItem.execute(
|
||||
await application.changeAndSaveItem.execute(
|
||||
item1,
|
||||
(mutator) => {
|
||||
mutator.removeItemAsRelationship(item2)
|
||||
@@ -173,7 +177,7 @@ describe('items', () => {
|
||||
)
|
||||
).getValue()
|
||||
item2 = (
|
||||
await this.application.changeAndSaveItem.execute(
|
||||
await application.changeAndSaveItem.execute(
|
||||
item2,
|
||||
(mutator) => {
|
||||
mutator.removeItemAsRelationship(item1)
|
||||
@@ -192,13 +196,13 @@ describe('items', () => {
|
||||
it('content equality should not have side effects', async function () {
|
||||
const params1 = Factory.createNotePayload()
|
||||
const params2 = Factory.createNotePayload()
|
||||
await this.application.mutator.emitItemsFromPayloads([params1, params2], PayloadEmitSource.LocalChanged)
|
||||
await application.mutator.emitItemsFromPayloads([params1, params2], PayloadEmitSource.LocalChanged)
|
||||
|
||||
let item1 = this.application.items.getDisplayableNotes()[0]
|
||||
const item2 = this.application.items.getDisplayableNotes()[1]
|
||||
let item1 = application.items.getDisplayableNotes()[0]
|
||||
const item2 = application.items.getDisplayableNotes()[1]
|
||||
|
||||
item1 = (
|
||||
await this.application.changeAndSaveItem.execute(
|
||||
await application.changeAndSaveItem.execute(
|
||||
item1,
|
||||
(mutator) => {
|
||||
mutator.mutableContent.foo = 'bar'
|
||||
@@ -223,7 +227,7 @@ describe('items', () => {
|
||||
// There was an issue where calling that function would modify values directly to omit keys
|
||||
// in contentKeysToIgnoreWhenCheckingEquality.
|
||||
|
||||
await this.application.mutator.setItemsDirty([item1, item2])
|
||||
await application.mutator.setItemsDirty([item1, item2])
|
||||
|
||||
expect(item1.userModifiedDate).to.be.ok
|
||||
expect(item2.userModifiedDate).to.be.ok
|
||||
|
||||
@@ -1,28 +1,35 @@
|
||||
/* eslint-disable no-unused-expressions */
|
||||
/* eslint-disable no-undef */
|
||||
import { BaseItemCounts } from '../lib/BaseItemCounts.js'
|
||||
import * as Factory from '../lib/factory.js'
|
||||
import { createNoteParams } from '../lib/Items.js'
|
||||
|
||||
chai.use(chaiAsPromised)
|
||||
const expect = chai.expect
|
||||
|
||||
describe('model manager mapping', () => {
|
||||
let application
|
||||
let expectedItemCount
|
||||
let context
|
||||
|
||||
beforeEach(async function () {
|
||||
this.expectedItemCount = BaseItemCounts.DefaultItems
|
||||
this.context = await Factory.createAppContext()
|
||||
await this.context.launch()
|
||||
this.application = this.context.application
|
||||
localStorage.clear()
|
||||
expectedItemCount = BaseItemCounts.DefaultItems
|
||||
context = await Factory.createAppContext()
|
||||
await context.launch()
|
||||
application = context.application
|
||||
})
|
||||
|
||||
afterEach(async function () {
|
||||
await Factory.safeDeinit(this.application)
|
||||
await Factory.safeDeinit(application)
|
||||
application = undefined
|
||||
context = undefined
|
||||
localStorage.clear()
|
||||
})
|
||||
|
||||
it('mapping nonexistent item creates it', async function () {
|
||||
const payload = Factory.createNotePayload()
|
||||
await this.application.mutator.emitItemsFromPayloads([payload], PayloadEmitSource.LocalChanged)
|
||||
this.expectedItemCount++
|
||||
expect(this.application.items.items.length).to.equal(this.expectedItemCount)
|
||||
await application.mutator.emitItemsFromPayloads([payload], PayloadEmitSource.LocalChanged)
|
||||
expectedItemCount++
|
||||
expect(application.items.items.length).to.equal(expectedItemCount)
|
||||
})
|
||||
|
||||
it('mapping nonexistent deleted item doesnt create it', async function () {
|
||||
@@ -31,17 +38,17 @@ describe('model manager mapping', () => {
|
||||
dirty: false,
|
||||
deleted: true,
|
||||
})
|
||||
await this.application.payloads.emitPayload(payload, PayloadEmitSource.LocalChanged)
|
||||
expect(this.application.items.items.length).to.equal(this.expectedItemCount)
|
||||
await application.payloads.emitPayload(payload, PayloadEmitSource.LocalChanged)
|
||||
expect(application.items.items.length).to.equal(expectedItemCount)
|
||||
})
|
||||
|
||||
it('mapping and deleting nonexistent item creates and deletes it', async function () {
|
||||
const payload = Factory.createNotePayload()
|
||||
await this.application.mutator.emitItemsFromPayloads([payload], PayloadEmitSource.LocalChanged)
|
||||
await application.mutator.emitItemsFromPayloads([payload], PayloadEmitSource.LocalChanged)
|
||||
|
||||
this.expectedItemCount++
|
||||
expectedItemCount++
|
||||
|
||||
expect(this.application.items.items.length).to.equal(this.expectedItemCount)
|
||||
expect(application.items.items.length).to.equal(expectedItemCount)
|
||||
|
||||
const changedParams = new DeletedPayload({
|
||||
...payload,
|
||||
@@ -49,32 +56,32 @@ describe('model manager mapping', () => {
|
||||
deleted: true,
|
||||
})
|
||||
|
||||
this.expectedItemCount--
|
||||
expectedItemCount--
|
||||
|
||||
await this.application.mutator.emitItemsFromPayloads([changedParams], PayloadEmitSource.LocalChanged)
|
||||
await application.mutator.emitItemsFromPayloads([changedParams], PayloadEmitSource.LocalChanged)
|
||||
|
||||
expect(this.application.items.items.length).to.equal(this.expectedItemCount)
|
||||
expect(application.items.items.length).to.equal(expectedItemCount)
|
||||
})
|
||||
|
||||
it('mapping deleted but dirty item should not delete it', async function () {
|
||||
const payload = Factory.createNotePayload()
|
||||
|
||||
const [item] = await this.application.mutator.emitItemsFromPayloads([payload], PayloadEmitSource.LocalChanged)
|
||||
const [item] = await application.mutator.emitItemsFromPayloads([payload], PayloadEmitSource.LocalChanged)
|
||||
|
||||
this.expectedItemCount++
|
||||
expectedItemCount++
|
||||
|
||||
await this.application.payloads.emitPayload(new DeleteItemMutator(item).getDeletedResult())
|
||||
await application.payloads.emitPayload(new DeleteItemMutator(item).getDeletedResult())
|
||||
|
||||
const payload2 = new DeletedPayload(this.application.payloads.findOne(payload.uuid).ejected())
|
||||
const payload2 = new DeletedPayload(application.payloads.findOne(payload.uuid).ejected())
|
||||
|
||||
await this.application.payloads.emitPayloads([payload2], PayloadEmitSource.LocalChanged)
|
||||
await application.payloads.emitPayloads([payload2], PayloadEmitSource.LocalChanged)
|
||||
|
||||
expect(this.application.payloads.collection.all().length).to.equal(this.expectedItemCount)
|
||||
expect(application.payloads.collection.all().length).to.equal(expectedItemCount)
|
||||
})
|
||||
|
||||
it('mapping existing item updates its properties', async function () {
|
||||
const payload = Factory.createNotePayload()
|
||||
await this.application.mutator.emitItemsFromPayloads([payload], PayloadEmitSource.LocalChanged)
|
||||
await application.mutator.emitItemsFromPayloads([payload], PayloadEmitSource.LocalChanged)
|
||||
|
||||
const newTitle = 'updated title'
|
||||
const mutated = new DecryptedPayload({
|
||||
@@ -84,45 +91,45 @@ describe('model manager mapping', () => {
|
||||
title: newTitle,
|
||||
},
|
||||
})
|
||||
await this.application.mutator.emitItemsFromPayloads([mutated], PayloadEmitSource.LocalChanged)
|
||||
const item = this.application.items.getDisplayableNotes()[0]
|
||||
await application.mutator.emitItemsFromPayloads([mutated], PayloadEmitSource.LocalChanged)
|
||||
const item = application.items.getDisplayableNotes()[0]
|
||||
|
||||
expect(item.content.title).to.equal(newTitle)
|
||||
})
|
||||
|
||||
it('setting an item dirty should retrieve it in dirty items', async function () {
|
||||
const payload = Factory.createNotePayload()
|
||||
await this.application.mutator.emitItemsFromPayloads([payload], PayloadEmitSource.LocalChanged)
|
||||
const note = this.application.items.getDisplayableNotes()[0]
|
||||
await this.application.mutator.setItemDirty(note)
|
||||
const dirtyItems = this.application.items.getDirtyItems()
|
||||
await application.mutator.emitItemsFromPayloads([payload], PayloadEmitSource.LocalChanged)
|
||||
const note = application.items.getDisplayableNotes()[0]
|
||||
await application.mutator.setItemDirty(note)
|
||||
const dirtyItems = application.items.getDirtyItems()
|
||||
expect(Uuids(dirtyItems).includes(note.uuid))
|
||||
})
|
||||
|
||||
it('set all items dirty', async function () {
|
||||
const count = 10
|
||||
this.expectedItemCount += count
|
||||
expectedItemCount += count
|
||||
const payloads = []
|
||||
for (let i = 0; i < count; i++) {
|
||||
payloads.push(Factory.createNotePayload())
|
||||
}
|
||||
await this.application.mutator.emitItemsFromPayloads(payloads, PayloadEmitSource.LocalChanged)
|
||||
await this.application.sync.markAllItemsAsNeedingSyncAndPersist()
|
||||
await application.mutator.emitItemsFromPayloads(payloads, PayloadEmitSource.LocalChanged)
|
||||
await application.sync.markAllItemsAsNeedingSyncAndPersist()
|
||||
|
||||
const dirtyItems = this.application.items.getDirtyItems()
|
||||
expect(dirtyItems.length).to.equal(this.expectedItemCount)
|
||||
const dirtyItems = application.items.getDirtyItems()
|
||||
expect(dirtyItems.length).to.equal(expectedItemCount)
|
||||
})
|
||||
|
||||
it('sync observers should be notified of changes', async function () {
|
||||
const payload = Factory.createNotePayload()
|
||||
await this.application.mutator.emitItemsFromPayloads([payload], PayloadEmitSource.LocalChanged)
|
||||
const item = this.application.items.items[0]
|
||||
await application.mutator.emitItemsFromPayloads([payload], PayloadEmitSource.LocalChanged)
|
||||
const item = application.items.items[0]
|
||||
return new Promise((resolve) => {
|
||||
this.application.items.addObserver(ContentType.TYPES.Any, ({ changed }) => {
|
||||
application.items.addObserver(ContentType.TYPES.Any, ({ changed }) => {
|
||||
expect(changed[0].uuid === item.uuid)
|
||||
resolve()
|
||||
})
|
||||
this.application.mutator.emitItemsFromPayloads([payload], PayloadEmitSource.LocalChanged)
|
||||
application.mutator.emitItemsFromPayloads([payload], PayloadEmitSource.LocalChanged)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
/* eslint-disable no-unused-expressions */
|
||||
/* eslint-disable no-undef */
|
||||
import * as Factory from '../lib/factory.js'
|
||||
|
||||
chai.use(chaiAsPromised)
|
||||
@@ -26,48 +24,53 @@ const titles = (items) => {
|
||||
}
|
||||
|
||||
describe('notes and smart views', () => {
|
||||
let application
|
||||
|
||||
beforeEach(async function () {
|
||||
this.application = await Factory.createInitAppWithFakeCrypto()
|
||||
localStorage.clear()
|
||||
application = await Factory.createInitAppWithFakeCrypto()
|
||||
})
|
||||
|
||||
afterEach(async function () {
|
||||
await Factory.safeDeinit(this.application)
|
||||
await Factory.safeDeinit(application)
|
||||
localStorage.clear()
|
||||
application = undefined
|
||||
})
|
||||
|
||||
it('lets me create a smart view and use it', async function () {
|
||||
// ## The user creates 3 notes
|
||||
const [note_1, note_2, note_3] = await Promise.all([
|
||||
Factory.createMappedNote(this.application, 'long & pinned', generateLongString()),
|
||||
Factory.createMappedNote(this.application, 'long & !pinned', generateLongString()),
|
||||
Factory.createMappedNote(this.application, 'pinned', 'this is a pinned note'),
|
||||
Factory.createMappedNote(application, 'long & pinned', generateLongString()),
|
||||
Factory.createMappedNote(application, 'long & !pinned', generateLongString()),
|
||||
Factory.createMappedNote(application, 'pinned', 'this is a pinned note'),
|
||||
])
|
||||
|
||||
// The user pin 2 notes
|
||||
await Promise.all([Factory.pinNote(this.application, note_1), Factory.pinNote(this.application, note_3)])
|
||||
await Promise.all([Factory.pinNote(application, note_1), Factory.pinNote(application, note_3)])
|
||||
|
||||
// ## The user creates smart views (long & pinned)
|
||||
const not_pinned = '!["Not Pinned", "pinned", "=", false]'
|
||||
const long = '!["Long", "text.length", ">", 500]'
|
||||
|
||||
const tag_not_pinned = await this.application.mutator.createTagOrSmartView(not_pinned)
|
||||
const tag_long = await this.application.mutator.createTagOrSmartView(long)
|
||||
const tag_not_pinned = await application.mutator.createTagOrSmartView(not_pinned)
|
||||
const tag_long = await application.mutator.createTagOrSmartView(long)
|
||||
|
||||
// ## The user can filter and see the pinned notes
|
||||
const notes_not_pinned = getFilteredNotes(this.application, {
|
||||
const notes_not_pinned = getFilteredNotes(application, {
|
||||
views: [tag_not_pinned],
|
||||
})
|
||||
|
||||
expect(titles(notes_not_pinned)).to.eql(['long & !pinned'])
|
||||
|
||||
// ## The user can filter and see the long notes
|
||||
const notes_long = getFilteredNotes(this.application, { views: [tag_long] })
|
||||
const notes_long = getFilteredNotes(application, { views: [tag_long] })
|
||||
expect(titles(notes_long)).to.eql(['long & !pinned', 'long & pinned'])
|
||||
|
||||
// ## The user creates a new long note
|
||||
await Factory.createMappedNote(this.application, 'new long', generateLongString())
|
||||
await Factory.createMappedNote(application, 'new long', generateLongString())
|
||||
|
||||
// ## The user can filter and see the new long note
|
||||
const notes_long2 = getFilteredNotes(this.application, {
|
||||
const notes_long2 = getFilteredNotes(application, {
|
||||
views: [tag_long],
|
||||
})
|
||||
expect(titles(notes_long2)).to.eql(['long & !pinned', 'long & pinned', 'new long'])
|
||||
|
||||
@@ -1,39 +1,44 @@
|
||||
/* eslint-disable no-undef */
|
||||
import * as Factory from '../lib/factory.js'
|
||||
import * as Utils from '../lib/Utils.js'
|
||||
import { createRelatedNoteTagPairPayload } from '../lib/Items.js'
|
||||
import { BaseItemCounts } from '../lib/BaseItemCounts.js'
|
||||
|
||||
chai.use(chaiAsPromised)
|
||||
const expect = chai.expect
|
||||
|
||||
describe('notes and tags', () => {
|
||||
let application
|
||||
let context
|
||||
|
||||
const syncOptions = {
|
||||
checkIntegrity: true,
|
||||
awaitAll: true,
|
||||
}
|
||||
|
||||
beforeEach(async function () {
|
||||
this.expectedItemCount = BaseItemCounts.DefaultItems
|
||||
this.context = await Factory.createAppContext()
|
||||
await this.context.launch()
|
||||
this.application = this.context.application
|
||||
localStorage.clear()
|
||||
context = await Factory.createAppContext()
|
||||
await context.launch()
|
||||
application = context.application
|
||||
})
|
||||
|
||||
afterEach(async function () {
|
||||
await Factory.safeDeinit(this.application)
|
||||
await Factory.safeDeinit(application)
|
||||
localStorage.clear()
|
||||
application = undefined
|
||||
context = undefined
|
||||
})
|
||||
|
||||
it('uses proper class for note', async function () {
|
||||
const payload = Factory.createNotePayload()
|
||||
await this.application.mutator.emitItemFromPayload(payload, PayloadEmitSource.LocalChanged)
|
||||
const note = this.application.items.getItems([ContentType.TYPES.Note])[0]
|
||||
await application.mutator.emitItemFromPayload(payload, PayloadEmitSource.LocalChanged)
|
||||
const note = application.items.getItems([ContentType.TYPES.Note])[0]
|
||||
expect(note.constructor === SNNote).to.equal(true)
|
||||
})
|
||||
|
||||
it('properly constructs syncing params', async function () {
|
||||
const title = 'Foo'
|
||||
const text = 'Bar'
|
||||
const note = await this.application.items.createTemplateItem(ContentType.TYPES.Note, {
|
||||
const note = await application.items.createTemplateItem(ContentType.TYPES.Note, {
|
||||
title,
|
||||
text,
|
||||
})
|
||||
@@ -41,7 +46,7 @@ describe('notes and tags', () => {
|
||||
expect(note.content.title).to.equal(title)
|
||||
expect(note.content.text).to.equal(text)
|
||||
|
||||
const tag = await this.application.items.createTemplateItem(ContentType.TYPES.Tag, {
|
||||
const tag = await application.items.createTemplateItem(ContentType.TYPES.Tag, {
|
||||
title,
|
||||
})
|
||||
|
||||
@@ -73,12 +78,12 @@ describe('notes and tags', () => {
|
||||
},
|
||||
})
|
||||
|
||||
await this.application.mutator.emitItemsFromPayloads([mutatedNote, mutatedTag], PayloadEmitSource.LocalChanged)
|
||||
const note = this.application.items.getItems([ContentType.TYPES.Note])[0]
|
||||
const tag = this.application.items.getItems([ContentType.TYPES.Tag])[0]
|
||||
await application.mutator.emitItemsFromPayloads([mutatedNote, mutatedTag], PayloadEmitSource.LocalChanged)
|
||||
const note = application.items.getItems([ContentType.TYPES.Note])[0]
|
||||
const tag = application.items.getItems([ContentType.TYPES.Tag])[0]
|
||||
|
||||
expect(note.content.references.length).to.equal(1)
|
||||
expect(this.application.items.itemsReferencingItem(tag).length).to.equal(1)
|
||||
expect(application.items.itemsReferencingItem(tag).length).to.equal(1)
|
||||
})
|
||||
|
||||
it('creates relationship between note and tag', async function () {
|
||||
@@ -89,9 +94,9 @@ describe('notes and tags', () => {
|
||||
expect(notePayload.content.references.length).to.equal(0)
|
||||
expect(tagPayload.content.references.length).to.equal(1)
|
||||
|
||||
await this.application.mutator.emitItemsFromPayloads([notePayload, tagPayload], PayloadEmitSource.LocalChanged)
|
||||
let note = this.application.items.getDisplayableNotes()[0]
|
||||
let tag = this.application.items.getDisplayableTags()[0]
|
||||
await application.mutator.emitItemsFromPayloads([notePayload, tagPayload], PayloadEmitSource.LocalChanged)
|
||||
let note = application.items.getDisplayableNotes()[0]
|
||||
let tag = application.items.getDisplayableTags()[0]
|
||||
|
||||
expect(note.dirty).to.not.be.ok
|
||||
expect(tag.dirty).to.not.be.ok
|
||||
@@ -102,26 +107,26 @@ describe('notes and tags', () => {
|
||||
expect(note.isReferencingItem(tag)).to.equal(false)
|
||||
expect(tag.isReferencingItem(note)).to.equal(true)
|
||||
|
||||
expect(this.application.items.itemsReferencingItem(note).length).to.equal(1)
|
||||
expect(application.items.itemsReferencingItem(note).length).to.equal(1)
|
||||
expect(note.payload.references.length).to.equal(0)
|
||||
expect(tag.noteCount).to.equal(1)
|
||||
|
||||
await this.application.mutator.setItemToBeDeleted(note)
|
||||
await application.mutator.setItemToBeDeleted(note)
|
||||
|
||||
tag = this.application.items.getDisplayableTags()[0]
|
||||
tag = application.items.getDisplayableTags()[0]
|
||||
|
||||
const deletedNotePayload = this.application.payloads.findOne(note.uuid)
|
||||
const deletedNotePayload = application.payloads.findOne(note.uuid)
|
||||
expect(deletedNotePayload.dirty).to.be.true
|
||||
expect(tag.dirty).to.be.true
|
||||
|
||||
await this.application.sync.sync(syncOptions)
|
||||
await application.sync.sync(syncOptions)
|
||||
|
||||
expect(tag.content.references.length).to.equal(0)
|
||||
expect(this.application.items.itemsReferencingItem(note).length).to.equal(0)
|
||||
expect(application.items.itemsReferencingItem(note).length).to.equal(0)
|
||||
expect(tag.noteCount).to.equal(0)
|
||||
|
||||
tag = this.application.items.getDisplayableTags()[0]
|
||||
expect(this.application.items.getDisplayableNotes().length).to.equal(0)
|
||||
tag = application.items.getDisplayableTags()[0]
|
||||
expect(application.items.getDisplayableNotes().length).to.equal(0)
|
||||
expect(tag.dirty).to.be.false
|
||||
})
|
||||
|
||||
@@ -130,14 +135,14 @@ describe('notes and tags', () => {
|
||||
const notePayload = pair[0]
|
||||
const tagPayload = pair[1]
|
||||
|
||||
await this.application.mutator.emitItemsFromPayloads(pair, PayloadEmitSource.LocalChanged)
|
||||
let note = this.application.items.getItems([ContentType.TYPES.Note])[0]
|
||||
let tag = this.application.items.getItems([ContentType.TYPES.Tag])[0]
|
||||
await application.mutator.emitItemsFromPayloads(pair, PayloadEmitSource.LocalChanged)
|
||||
let note = application.items.getItems([ContentType.TYPES.Note])[0]
|
||||
let tag = application.items.getItems([ContentType.TYPES.Tag])[0]
|
||||
|
||||
expect(note.content.references.length).to.equal(0)
|
||||
expect(tag.content.references.length).to.equal(1)
|
||||
|
||||
await this.application.sync.sync(syncOptions)
|
||||
await application.sync.sync(syncOptions)
|
||||
|
||||
const mutatedTag = new DecryptedPayload({
|
||||
...tagPayload,
|
||||
@@ -147,13 +152,13 @@ describe('notes and tags', () => {
|
||||
references: [],
|
||||
},
|
||||
})
|
||||
await this.application.mutator.emitItemsFromPayloads([mutatedTag], PayloadEmitSource.LocalChanged)
|
||||
await application.mutator.emitItemsFromPayloads([mutatedTag], PayloadEmitSource.LocalChanged)
|
||||
|
||||
note = this.application.items.findItem(note.uuid)
|
||||
tag = this.application.items.findItem(tag.uuid)
|
||||
note = application.items.findItem(note.uuid)
|
||||
tag = application.items.findItem(tag.uuid)
|
||||
|
||||
expect(tag.content.references.length).to.equal(0)
|
||||
expect(this.application.items.itemsReferencingItem(note).length).to.equal(0)
|
||||
expect(application.items.itemsReferencingItem(note).length).to.equal(0)
|
||||
expect(tag.noteCount).to.equal(0)
|
||||
|
||||
// expect to be false
|
||||
@@ -162,13 +167,13 @@ describe('notes and tags', () => {
|
||||
})
|
||||
|
||||
it('creating basic note should have text set', async function () {
|
||||
const note = await Factory.createMappedNote(this.application)
|
||||
const note = await Factory.createMappedNote(application)
|
||||
expect(note.title).to.be.ok
|
||||
expect(note.text).to.be.ok
|
||||
})
|
||||
|
||||
it('creating basic tag should have title', async function () {
|
||||
const tag = await Factory.createMappedTag(this.application)
|
||||
const tag = await Factory.createMappedTag(application)
|
||||
expect(tag.title).to.be.ok
|
||||
})
|
||||
|
||||
@@ -177,15 +182,15 @@ describe('notes and tags', () => {
|
||||
const notePayload = pair[0]
|
||||
const tagPayload = pair[1]
|
||||
|
||||
await this.application.mutator.emitItemsFromPayloads([notePayload, tagPayload], PayloadEmitSource.LocalChanged)
|
||||
const note = this.application.items.getItems([ContentType.TYPES.Note])[0]
|
||||
let tag = this.application.items.getItems([ContentType.TYPES.Tag])[0]
|
||||
await application.mutator.emitItemsFromPayloads([notePayload, tagPayload], PayloadEmitSource.LocalChanged)
|
||||
const note = application.items.getItems([ContentType.TYPES.Note])[0]
|
||||
let tag = application.items.getItems([ContentType.TYPES.Tag])[0]
|
||||
|
||||
expect(note.content.references.length).to.equal(0)
|
||||
expect(tag.content.references.length).to.equal(1)
|
||||
|
||||
tag = (
|
||||
await this.application.changeAndSaveItem.execute(
|
||||
await application.changeAndSaveItem.execute(
|
||||
tag,
|
||||
(mutator) => {
|
||||
mutator.removeItemAsRelationship(note)
|
||||
@@ -196,21 +201,21 @@ describe('notes and tags', () => {
|
||||
)
|
||||
).getValue()
|
||||
|
||||
expect(this.application.items.itemsReferencingItem(note).length).to.equal(0)
|
||||
expect(application.items.itemsReferencingItem(note).length).to.equal(0)
|
||||
expect(tag.noteCount).to.equal(0)
|
||||
})
|
||||
|
||||
it('properly handles tag duplication', async function () {
|
||||
const pair = createRelatedNoteTagPairPayload()
|
||||
await this.application.mutator.emitItemsFromPayloads(pair, PayloadEmitSource.LocalChanged)
|
||||
let note = this.application.items.getDisplayableNotes()[0]
|
||||
let tag = this.application.items.getDisplayableTags()[0]
|
||||
await application.mutator.emitItemsFromPayloads(pair, PayloadEmitSource.LocalChanged)
|
||||
let note = application.items.getDisplayableNotes()[0]
|
||||
let tag = application.items.getDisplayableTags()[0]
|
||||
|
||||
const duplicateTag = await this.application.mutator.duplicateItem(tag, true)
|
||||
await this.application.sync.sync(syncOptions)
|
||||
const duplicateTag = await application.mutator.duplicateItem(tag, true)
|
||||
await application.sync.sync(syncOptions)
|
||||
|
||||
note = this.application.items.findItem(note.uuid)
|
||||
tag = this.application.items.findItem(tag.uuid)
|
||||
note = application.items.findItem(note.uuid)
|
||||
tag = application.items.findItem(tag.uuid)
|
||||
|
||||
expect(tag.uuid).to.not.equal(duplicateTag.uuid)
|
||||
expect(tag.content.references.length).to.equal(1)
|
||||
@@ -218,7 +223,7 @@ describe('notes and tags', () => {
|
||||
expect(duplicateTag.content.references.length).to.equal(1)
|
||||
expect(duplicateTag.noteCount).to.equal(1)
|
||||
|
||||
const noteTags = this.application.items.itemsReferencingItem(note)
|
||||
const noteTags = application.items.itemsReferencingItem(note)
|
||||
expect(noteTags.length).to.equal(2)
|
||||
|
||||
const noteTag1 = noteTags[0]
|
||||
@@ -234,13 +239,13 @@ describe('notes and tags', () => {
|
||||
const pair = createRelatedNoteTagPairPayload()
|
||||
const notePayload = pair[0]
|
||||
const tagPayload = pair[1]
|
||||
await this.application.mutator.emitItemsFromPayloads([notePayload, tagPayload], PayloadEmitSource.LocalChanged)
|
||||
const note = this.application.items.getItems([ContentType.TYPES.Note])[0]
|
||||
const duplicateNote = await this.application.mutator.duplicateItem(note, true)
|
||||
await application.mutator.emitItemsFromPayloads([notePayload, tagPayload], PayloadEmitSource.LocalChanged)
|
||||
const note = application.items.getItems([ContentType.TYPES.Note])[0]
|
||||
const duplicateNote = await application.mutator.duplicateItem(note, true)
|
||||
expect(note.uuid).to.not.equal(duplicateNote.uuid)
|
||||
|
||||
expect(this.application.items.itemsReferencingItem(duplicateNote).length).to.equal(
|
||||
this.application.items.itemsReferencingItem(note).length,
|
||||
expect(application.items.itemsReferencingItem(duplicateNote).length).to.equal(
|
||||
application.items.itemsReferencingItem(note).length,
|
||||
)
|
||||
})
|
||||
|
||||
@@ -248,27 +253,27 @@ describe('notes and tags', () => {
|
||||
const pair = createRelatedNoteTagPairPayload()
|
||||
const notePayload = pair[0]
|
||||
const tagPayload = pair[1]
|
||||
await this.application.mutator.emitItemsFromPayloads([notePayload, tagPayload], PayloadEmitSource.LocalChanged)
|
||||
const note = this.application.items.getItems([ContentType.TYPES.Note])[0]
|
||||
let tag = this.application.items.getItems([ContentType.TYPES.Tag])[0]
|
||||
await application.mutator.emitItemsFromPayloads([notePayload, tagPayload], PayloadEmitSource.LocalChanged)
|
||||
const note = application.items.getItems([ContentType.TYPES.Note])[0]
|
||||
let tag = application.items.getItems([ContentType.TYPES.Tag])[0]
|
||||
|
||||
expect(tag.content.references.length).to.equal(1)
|
||||
expect(tag.noteCount).to.equal(1)
|
||||
|
||||
expect(note.content.references.length).to.equal(0)
|
||||
expect(this.application.items.itemsReferencingItem(note).length).to.equal(1)
|
||||
expect(application.items.itemsReferencingItem(note).length).to.equal(1)
|
||||
|
||||
await this.application.mutator.setItemToBeDeleted(tag)
|
||||
tag = this.application.items.findItem(tag.uuid)
|
||||
await application.mutator.setItemToBeDeleted(tag)
|
||||
tag = application.items.findItem(tag.uuid)
|
||||
expect(tag).to.not.be.ok
|
||||
})
|
||||
|
||||
it('modifying item content should not modify payload content', async function () {
|
||||
const notePayload = Factory.createNotePayload()
|
||||
await this.application.mutator.emitItemsFromPayloads([notePayload], PayloadEmitSource.LocalChanged)
|
||||
let note = this.application.items.getItems([ContentType.TYPES.Note])[0]
|
||||
await application.mutator.emitItemsFromPayloads([notePayload], PayloadEmitSource.LocalChanged)
|
||||
let note = application.items.getItems([ContentType.TYPES.Note])[0]
|
||||
note = (
|
||||
await this.application.changeAndSaveItem.execute(
|
||||
await application.changeAndSaveItem.execute(
|
||||
note,
|
||||
(mutator) => {
|
||||
mutator.mutableContent.title = Math.random()
|
||||
@@ -289,15 +294,15 @@ describe('notes and tags', () => {
|
||||
const notePayload = pair[0]
|
||||
const tagPayload = pair[1]
|
||||
|
||||
await this.application.mutator.emitItemsFromPayloads([notePayload, tagPayload], PayloadEmitSource.LocalChanged)
|
||||
let note = this.application.items.getItems([ContentType.TYPES.Note])[0]
|
||||
let tag = this.application.items.getItems([ContentType.TYPES.Tag])[0]
|
||||
await application.mutator.emitItemsFromPayloads([notePayload, tagPayload], PayloadEmitSource.LocalChanged)
|
||||
let note = application.items.getItems([ContentType.TYPES.Note])[0]
|
||||
let tag = application.items.getItems([ContentType.TYPES.Tag])[0]
|
||||
|
||||
await this.application.sync.sync(syncOptions)
|
||||
await this.application.mutator.setItemToBeDeleted(tag)
|
||||
await application.sync.sync(syncOptions)
|
||||
await application.mutator.setItemToBeDeleted(tag)
|
||||
|
||||
note = this.application.items.findItem(note.uuid)
|
||||
this.application.items.findItem(tag.uuid)
|
||||
note = application.items.findItem(note.uuid)
|
||||
application.items.findItem(tag.uuid)
|
||||
|
||||
expect(note.dirty).to.not.be.ok
|
||||
})
|
||||
@@ -305,26 +310,26 @@ describe('notes and tags', () => {
|
||||
it('should sort notes', async function () {
|
||||
await Promise.all(
|
||||
['Y', 'Z', 'A', 'B'].map(async (title) => {
|
||||
return this.application.mutator.insertItem(
|
||||
await this.application.items.createTemplateItem(ContentType.TYPES.Note, { title }),
|
||||
return application.mutator.insertItem(
|
||||
await application.items.createTemplateItem(ContentType.TYPES.Note, { title }),
|
||||
)
|
||||
}),
|
||||
)
|
||||
this.application.items.setPrimaryItemDisplayOptions({
|
||||
application.items.setPrimaryItemDisplayOptions({
|
||||
sortBy: 'title',
|
||||
sortDirection: 'dsc',
|
||||
})
|
||||
const titles = this.application.items.getDisplayableNotes().map((note) => note.title)
|
||||
const titles = application.items.getDisplayableNotes().map((note) => note.title)
|
||||
/** setPrimaryItemDisplayOptions inverses sort for title */
|
||||
expect(titles).to.deep.equal(['A', 'B', 'Y', 'Z'])
|
||||
})
|
||||
|
||||
it('setting a note dirty should collapse its properties into content', async function () {
|
||||
let note = await this.application.items.createTemplateItem(ContentType.TYPES.Note, {
|
||||
let note = await application.items.createTemplateItem(ContentType.TYPES.Note, {
|
||||
title: 'Foo',
|
||||
})
|
||||
await this.application.mutator.insertItem(note)
|
||||
note = this.application.items.findItem(note.uuid)
|
||||
await application.mutator.insertItem(note)
|
||||
note = application.items.findItem(note.uuid)
|
||||
expect(note.content.title).to.equal('Foo')
|
||||
})
|
||||
|
||||
@@ -332,49 +337,49 @@ describe('notes and tags', () => {
|
||||
it('should sort tags in ascending alphabetical order by default', async function () {
|
||||
const titles = ['1', 'A', 'b', '2']
|
||||
const sortedTitles = titles.sort((a, b) => a.localeCompare(b))
|
||||
await Promise.all(titles.map((title) => this.application.mutator.findOrCreateTag(title)))
|
||||
expect(this.application.items.tagDisplayController.items().map((t) => t.title)).to.deep.equal(sortedTitles)
|
||||
await Promise.all(titles.map((title) => application.mutator.findOrCreateTag(title)))
|
||||
expect(application.items.tagDisplayController.items().map((t) => t.title)).to.deep.equal(sortedTitles)
|
||||
})
|
||||
|
||||
it('should match a tag', async function () {
|
||||
const taggedNote = await Factory.createMappedNote(this.application)
|
||||
const tag = await this.application.mutator.findOrCreateTag('A')
|
||||
await this.application.mutator.changeItem(tag, (mutator) => {
|
||||
const taggedNote = await Factory.createMappedNote(application)
|
||||
const tag = await application.mutator.findOrCreateTag('A')
|
||||
await application.mutator.changeItem(tag, (mutator) => {
|
||||
mutator.e2ePendingRefactor_addItemAsRelationship(taggedNote)
|
||||
})
|
||||
await this.application.mutator.insertItem(
|
||||
await this.application.items.createTemplateItem(ContentType.TYPES.Note, {
|
||||
await application.mutator.insertItem(
|
||||
await application.items.createTemplateItem(ContentType.TYPES.Note, {
|
||||
title: 'A',
|
||||
}),
|
||||
)
|
||||
this.application.items.setPrimaryItemDisplayOptions({
|
||||
application.items.setPrimaryItemDisplayOptions({
|
||||
sortBy: 'title',
|
||||
sortDirection: 'dsc',
|
||||
tags: [tag],
|
||||
})
|
||||
const displayedNotes = this.application.items.getDisplayableNotes()
|
||||
const displayedNotes = application.items.getDisplayableNotes()
|
||||
expect(displayedNotes.length).to.equal(1)
|
||||
expect(displayedNotes[0].uuid).to.equal(taggedNote.uuid)
|
||||
})
|
||||
|
||||
it('should not show trashed notes when displaying a tag', async function () {
|
||||
const taggedNote = await Factory.createMappedNote(this.application)
|
||||
const trashedNote = await Factory.createMappedNote(this.application)
|
||||
const tag = await this.application.mutator.findOrCreateTag('A')
|
||||
await this.application.mutator.changeItem(tag, (mutator) => {
|
||||
const taggedNote = await Factory.createMappedNote(application)
|
||||
const trashedNote = await Factory.createMappedNote(application)
|
||||
const tag = await application.mutator.findOrCreateTag('A')
|
||||
await application.mutator.changeItem(tag, (mutator) => {
|
||||
mutator.e2ePendingRefactor_addItemAsRelationship(taggedNote)
|
||||
mutator.e2ePendingRefactor_addItemAsRelationship(trashedNote)
|
||||
})
|
||||
await this.application.mutator.changeItem(trashedNote, (mutator) => {
|
||||
await application.mutator.changeItem(trashedNote, (mutator) => {
|
||||
mutator.trashed = true
|
||||
})
|
||||
this.application.items.setPrimaryItemDisplayOptions({
|
||||
application.items.setPrimaryItemDisplayOptions({
|
||||
sortBy: 'title',
|
||||
sortDirection: 'dsc',
|
||||
tags: [tag],
|
||||
includeTrashed: false,
|
||||
})
|
||||
const displayedNotes = this.application.items.getDisplayableNotes()
|
||||
const displayedNotes = application.items.getDisplayableNotes()
|
||||
expect(displayedNotes.length).to.equal(1)
|
||||
expect(displayedNotes[0].uuid).to.equal(taggedNote.uuid)
|
||||
})
|
||||
@@ -382,31 +387,31 @@ describe('notes and tags', () => {
|
||||
it('should sort notes when displaying tag', async function () {
|
||||
await Promise.all(
|
||||
['Y', 'Z', 'A', 'B'].map(async (title) => {
|
||||
return this.application.mutator.insertItem(
|
||||
await this.application.items.createTemplateItem(ContentType.TYPES.Note, {
|
||||
return application.mutator.insertItem(
|
||||
await application.items.createTemplateItem(ContentType.TYPES.Note, {
|
||||
title,
|
||||
}),
|
||||
)
|
||||
}),
|
||||
)
|
||||
const pinnedNote = this.application.items.getDisplayableNotes().find((note) => note.title === 'B')
|
||||
await this.application.mutator.changeItem(pinnedNote, (mutator) => {
|
||||
const pinnedNote = application.items.getDisplayableNotes().find((note) => note.title === 'B')
|
||||
await application.mutator.changeItem(pinnedNote, (mutator) => {
|
||||
mutator.pinned = true
|
||||
})
|
||||
const tag = await this.application.mutator.findOrCreateTag('A')
|
||||
await this.application.mutator.changeItem(tag, (mutator) => {
|
||||
for (const note of this.application.items.getDisplayableNotes()) {
|
||||
const tag = await application.mutator.findOrCreateTag('A')
|
||||
await application.mutator.changeItem(tag, (mutator) => {
|
||||
for (const note of application.items.getDisplayableNotes()) {
|
||||
mutator.e2ePendingRefactor_addItemAsRelationship(note)
|
||||
}
|
||||
})
|
||||
|
||||
this.application.items.setPrimaryItemDisplayOptions({
|
||||
application.items.setPrimaryItemDisplayOptions({
|
||||
sortBy: 'title',
|
||||
sortDirection: 'dsc',
|
||||
tags: [tag],
|
||||
})
|
||||
|
||||
const displayedNotes = this.application.items.getDisplayableNotes()
|
||||
const displayedNotes = application.items.getDisplayableNotes()
|
||||
expect(displayedNotes).to.have.length(4)
|
||||
/** setPrimaryItemDisplayOptions inverses sort for title */
|
||||
expect(displayedNotes[0].title).to.equal('B')
|
||||
@@ -416,18 +421,18 @@ describe('notes and tags', () => {
|
||||
|
||||
describe('Smart views', function () {
|
||||
it('"title", "startsWith", "Foo"', async function () {
|
||||
const note = await this.application.mutator.insertItem(
|
||||
await this.application.items.createTemplateItem(ContentType.TYPES.Note, {
|
||||
const note = await application.mutator.insertItem(
|
||||
await application.items.createTemplateItem(ContentType.TYPES.Note, {
|
||||
title: 'Foo 🎲',
|
||||
}),
|
||||
)
|
||||
await this.application.mutator.insertItem(
|
||||
await this.application.items.createTemplateItem(ContentType.TYPES.Note, {
|
||||
await application.mutator.insertItem(
|
||||
await application.items.createTemplateItem(ContentType.TYPES.Note, {
|
||||
title: 'Not Foo 🎲',
|
||||
}),
|
||||
)
|
||||
const view = await this.application.mutator.insertItem(
|
||||
await this.application.items.createTemplateItem(ContentType.TYPES.SmartView, {
|
||||
const view = await application.mutator.insertItem(
|
||||
await application.items.createTemplateItem(ContentType.TYPES.SmartView, {
|
||||
title: 'Foo Notes',
|
||||
predicate: {
|
||||
keypath: 'title',
|
||||
@@ -436,36 +441,36 @@ describe('notes and tags', () => {
|
||||
},
|
||||
}),
|
||||
)
|
||||
const matches = this.application.items.notesMatchingSmartView(view)
|
||||
this.application.items.setPrimaryItemDisplayOptions({
|
||||
const matches = application.items.notesMatchingSmartView(view)
|
||||
application.items.setPrimaryItemDisplayOptions({
|
||||
sortBy: 'title',
|
||||
sortDirection: 'asc',
|
||||
views: [view],
|
||||
})
|
||||
|
||||
const displayedNotes = this.application.items.getDisplayableNotes()
|
||||
const displayedNotes = application.items.getDisplayableNotes()
|
||||
expect(displayedNotes).to.deep.equal(matches)
|
||||
expect(matches.length).to.equal(1)
|
||||
expect(matches[0].uuid).to.equal(note.uuid)
|
||||
})
|
||||
|
||||
it('"pinned", "=", true', async function () {
|
||||
const note = await this.application.mutator.insertItem(
|
||||
await this.application.items.createTemplateItem(ContentType.TYPES.Note, {
|
||||
const note = await application.mutator.insertItem(
|
||||
await application.items.createTemplateItem(ContentType.TYPES.Note, {
|
||||
title: 'A',
|
||||
}),
|
||||
)
|
||||
await this.application.mutator.changeItem(note, (mutator) => {
|
||||
await application.mutator.changeItem(note, (mutator) => {
|
||||
mutator.pinned = true
|
||||
})
|
||||
await this.application.mutator.insertItem(
|
||||
await this.application.items.createTemplateItem(ContentType.TYPES.Note, {
|
||||
await application.mutator.insertItem(
|
||||
await application.items.createTemplateItem(ContentType.TYPES.Note, {
|
||||
title: 'B',
|
||||
pinned: false,
|
||||
}),
|
||||
)
|
||||
const view = await this.application.mutator.insertItem(
|
||||
await this.application.items.createTemplateItem(ContentType.TYPES.SmartView, {
|
||||
const view = await application.mutator.insertItem(
|
||||
await application.items.createTemplateItem(ContentType.TYPES.SmartView, {
|
||||
title: 'Pinned',
|
||||
predicate: {
|
||||
keypath: 'pinned',
|
||||
@@ -474,35 +479,35 @@ describe('notes and tags', () => {
|
||||
},
|
||||
}),
|
||||
)
|
||||
const matches = this.application.items.notesMatchingSmartView(view)
|
||||
this.application.items.setPrimaryItemDisplayOptions({
|
||||
const matches = application.items.notesMatchingSmartView(view)
|
||||
application.items.setPrimaryItemDisplayOptions({
|
||||
sortBy: 'title',
|
||||
sortDirection: 'asc',
|
||||
views: [view],
|
||||
})
|
||||
|
||||
const displayedNotes = this.application.items.getDisplayableNotes()
|
||||
const displayedNotes = application.items.getDisplayableNotes()
|
||||
expect(displayedNotes).to.deep.equal(matches)
|
||||
expect(matches.length).to.equal(1)
|
||||
expect(matches[0].uuid).to.equal(note.uuid)
|
||||
})
|
||||
|
||||
it('"pinned", "=", false', async function () {
|
||||
const pinnedNote = await this.application.mutator.insertItem(
|
||||
await this.application.items.createTemplateItem(ContentType.TYPES.Note, {
|
||||
const pinnedNote = await application.mutator.insertItem(
|
||||
await application.items.createTemplateItem(ContentType.TYPES.Note, {
|
||||
title: 'A',
|
||||
}),
|
||||
)
|
||||
await this.application.mutator.changeItem(pinnedNote, (mutator) => {
|
||||
await application.mutator.changeItem(pinnedNote, (mutator) => {
|
||||
mutator.pinned = true
|
||||
})
|
||||
const unpinnedNote = await this.application.mutator.insertItem(
|
||||
await this.application.items.createTemplateItem(ContentType.TYPES.Note, {
|
||||
const unpinnedNote = await application.mutator.insertItem(
|
||||
await application.items.createTemplateItem(ContentType.TYPES.Note, {
|
||||
title: 'B',
|
||||
}),
|
||||
)
|
||||
const view = await this.application.mutator.insertItem(
|
||||
await this.application.items.createTemplateItem(ContentType.TYPES.SmartView, {
|
||||
const view = await application.mutator.insertItem(
|
||||
await application.items.createTemplateItem(ContentType.TYPES.SmartView, {
|
||||
title: 'Not pinned',
|
||||
predicate: {
|
||||
keypath: 'pinned',
|
||||
@@ -511,34 +516,34 @@ describe('notes and tags', () => {
|
||||
},
|
||||
}),
|
||||
)
|
||||
const matches = this.application.items.notesMatchingSmartView(view)
|
||||
this.application.items.setPrimaryItemDisplayOptions({
|
||||
const matches = application.items.notesMatchingSmartView(view)
|
||||
application.items.setPrimaryItemDisplayOptions({
|
||||
sortBy: 'title',
|
||||
sortDirection: 'asc',
|
||||
views: [view],
|
||||
})
|
||||
|
||||
const displayedNotes = this.application.items.getDisplayableNotes()
|
||||
const displayedNotes = application.items.getDisplayableNotes()
|
||||
expect(displayedNotes).to.deep.equal(matches)
|
||||
expect(matches.length).to.equal(1)
|
||||
expect(matches[0].uuid).to.equal(unpinnedNote.uuid)
|
||||
})
|
||||
|
||||
it('"text.length", ">", 500', async function () {
|
||||
const longNote = await this.application.mutator.insertItem(
|
||||
await this.application.items.createTemplateItem(ContentType.TYPES.Note, {
|
||||
const longNote = await application.mutator.insertItem(
|
||||
await application.items.createTemplateItem(ContentType.TYPES.Note, {
|
||||
title: 'A',
|
||||
text: Array(501).fill(0).join(''),
|
||||
}),
|
||||
)
|
||||
await this.application.mutator.insertItem(
|
||||
await this.application.items.createTemplateItem(ContentType.TYPES.Note, {
|
||||
await application.mutator.insertItem(
|
||||
await application.items.createTemplateItem(ContentType.TYPES.Note, {
|
||||
title: 'B',
|
||||
text: 'b',
|
||||
}),
|
||||
)
|
||||
const view = await this.application.mutator.insertItem(
|
||||
await this.application.items.createTemplateItem(ContentType.TYPES.SmartView, {
|
||||
const view = await application.mutator.insertItem(
|
||||
await application.items.createTemplateItem(ContentType.TYPES.SmartView, {
|
||||
title: 'Long',
|
||||
predicate: {
|
||||
keypath: 'text.length',
|
||||
@@ -547,13 +552,13 @@ describe('notes and tags', () => {
|
||||
},
|
||||
}),
|
||||
)
|
||||
const matches = this.application.items.notesMatchingSmartView(view)
|
||||
this.application.items.setPrimaryItemDisplayOptions({
|
||||
const matches = application.items.notesMatchingSmartView(view)
|
||||
application.items.setPrimaryItemDisplayOptions({
|
||||
sortBy: 'title',
|
||||
sortDirection: 'asc',
|
||||
views: [view],
|
||||
})
|
||||
const displayedNotes = this.application.items.getDisplayableNotes()
|
||||
const displayedNotes = application.items.getDisplayableNotes()
|
||||
expect(displayedNotes).to.deep.equal(matches)
|
||||
expect(matches.length).to.equal(1)
|
||||
expect(matches[0].uuid).to.equal(longNote.uuid)
|
||||
@@ -561,22 +566,22 @@ describe('notes and tags', () => {
|
||||
|
||||
it('"updated_at", ">", "1.days.ago"', async function () {
|
||||
await Factory.registerUserToApplication({
|
||||
application: this.application,
|
||||
application: application,
|
||||
email: Utils.generateUuid(),
|
||||
password: Utils.generateUuid(),
|
||||
})
|
||||
|
||||
const recentNote = await this.application.mutator.insertItem(
|
||||
await this.application.items.createTemplateItem(ContentType.TYPES.Note, {
|
||||
const recentNote = await application.mutator.insertItem(
|
||||
await application.items.createTemplateItem(ContentType.TYPES.Note, {
|
||||
title: 'A',
|
||||
}),
|
||||
true,
|
||||
)
|
||||
|
||||
await this.application.sync.sync()
|
||||
await application.sync.sync()
|
||||
|
||||
const olderNote = await this.application.mutator.insertItem(
|
||||
await this.application.items.createTemplateItem(ContentType.TYPES.Note, {
|
||||
const olderNote = await application.mutator.insertItem(
|
||||
await application.items.createTemplateItem(ContentType.TYPES.Note, {
|
||||
title: 'B',
|
||||
text: 'b',
|
||||
}),
|
||||
@@ -584,17 +589,17 @@ describe('notes and tags', () => {
|
||||
)
|
||||
|
||||
const threeDays = 3 * 24 * 60 * 60 * 1000
|
||||
await Factory.changePayloadUpdatedAt(this.application, olderNote.payload, new Date(Date.now() - threeDays))
|
||||
await Factory.changePayloadUpdatedAt(application, olderNote.payload, new Date(Date.now() - threeDays))
|
||||
|
||||
/** Create an unsynced note which shouldn't get an updated_at */
|
||||
await this.application.mutator.insertItem(
|
||||
await this.application.items.createTemplateItem(ContentType.TYPES.Note, {
|
||||
await application.mutator.insertItem(
|
||||
await application.items.createTemplateItem(ContentType.TYPES.Note, {
|
||||
title: 'B',
|
||||
text: 'b',
|
||||
}),
|
||||
)
|
||||
const view = await this.application.mutator.insertItem(
|
||||
await this.application.items.createTemplateItem(ContentType.TYPES.SmartView, {
|
||||
const view = await application.mutator.insertItem(
|
||||
await application.items.createTemplateItem(ContentType.TYPES.SmartView, {
|
||||
title: 'One day ago',
|
||||
predicate: {
|
||||
keypath: 'serverUpdatedAt',
|
||||
@@ -603,33 +608,33 @@ describe('notes and tags', () => {
|
||||
},
|
||||
}),
|
||||
)
|
||||
const matches = this.application.items.notesMatchingSmartView(view)
|
||||
const matches = application.items.notesMatchingSmartView(view)
|
||||
expect(matches.length).to.equal(1)
|
||||
expect(matches[0].uuid).to.equal(recentNote.uuid)
|
||||
|
||||
this.application.items.setPrimaryItemDisplayOptions({
|
||||
application.items.setPrimaryItemDisplayOptions({
|
||||
sortBy: 'title',
|
||||
sortDirection: 'asc',
|
||||
views: [view],
|
||||
})
|
||||
const displayedNotes = this.application.items.getDisplayableNotes()
|
||||
const displayedNotes = application.items.getDisplayableNotes()
|
||||
expect(displayedNotes).to.deep.equal(matches)
|
||||
})
|
||||
|
||||
it('"tags.length", "=", 0', async function () {
|
||||
const untaggedNote = await this.application.mutator.insertItem(
|
||||
await this.application.items.createTemplateItem(ContentType.TYPES.Note, {
|
||||
const untaggedNote = await application.mutator.insertItem(
|
||||
await application.items.createTemplateItem(ContentType.TYPES.Note, {
|
||||
title: 'A',
|
||||
}),
|
||||
)
|
||||
const taggedNote = await Factory.createMappedNote(this.application)
|
||||
const tag = await this.application.mutator.findOrCreateTag('A')
|
||||
await this.application.mutator.changeItem(tag, (mutator) => {
|
||||
const taggedNote = await Factory.createMappedNote(application)
|
||||
const tag = await application.mutator.findOrCreateTag('A')
|
||||
await application.mutator.changeItem(tag, (mutator) => {
|
||||
mutator.e2ePendingRefactor_addItemAsRelationship(taggedNote)
|
||||
})
|
||||
|
||||
const view = await this.application.mutator.insertItem(
|
||||
await this.application.items.createTemplateItem(ContentType.TYPES.SmartView, {
|
||||
const view = await application.mutator.insertItem(
|
||||
await application.items.createTemplateItem(ContentType.TYPES.SmartView, {
|
||||
title: 'Untagged',
|
||||
predicate: {
|
||||
keypath: 'tags.length',
|
||||
@@ -638,32 +643,32 @@ describe('notes and tags', () => {
|
||||
},
|
||||
}),
|
||||
)
|
||||
const matches = this.application.items.notesMatchingSmartView(view)
|
||||
this.application.items.setPrimaryItemDisplayOptions({
|
||||
const matches = application.items.notesMatchingSmartView(view)
|
||||
application.items.setPrimaryItemDisplayOptions({
|
||||
sortBy: 'title',
|
||||
sortDirection: 'asc',
|
||||
views: [view],
|
||||
})
|
||||
const displayedNotes = this.application.items.getDisplayableNotes()
|
||||
const displayedNotes = application.items.getDisplayableNotes()
|
||||
expect(displayedNotes).to.deep.equal(matches)
|
||||
expect(matches.length).to.equal(1)
|
||||
expect(matches[0].uuid).to.equal(untaggedNote.uuid)
|
||||
})
|
||||
|
||||
it('"tags", "includes", ["title", "startsWith", "b"]', async function () {
|
||||
const taggedNote = await Factory.createMappedNote(this.application)
|
||||
const tag = await this.application.mutator.findOrCreateTag('B')
|
||||
await this.application.mutator.changeItem(tag, (mutator) => {
|
||||
const taggedNote = await Factory.createMappedNote(application)
|
||||
const tag = await application.mutator.findOrCreateTag('B')
|
||||
await application.mutator.changeItem(tag, (mutator) => {
|
||||
mutator.e2ePendingRefactor_addItemAsRelationship(taggedNote)
|
||||
})
|
||||
await this.application.mutator.insertItem(
|
||||
await this.application.items.createTemplateItem(ContentType.TYPES.Note, {
|
||||
await application.mutator.insertItem(
|
||||
await application.items.createTemplateItem(ContentType.TYPES.Note, {
|
||||
title: 'A',
|
||||
}),
|
||||
)
|
||||
|
||||
const view = await this.application.mutator.insertItem(
|
||||
await this.application.items.createTemplateItem(ContentType.TYPES.SmartView, {
|
||||
const view = await application.mutator.insertItem(
|
||||
await application.items.createTemplateItem(ContentType.TYPES.SmartView, {
|
||||
title: 'B-tags',
|
||||
predicate: {
|
||||
keypath: 'tags',
|
||||
@@ -672,45 +677,45 @@ describe('notes and tags', () => {
|
||||
},
|
||||
}),
|
||||
)
|
||||
const matches = this.application.items.notesMatchingSmartView(view)
|
||||
this.application.items.setPrimaryItemDisplayOptions({
|
||||
const matches = application.items.notesMatchingSmartView(view)
|
||||
application.items.setPrimaryItemDisplayOptions({
|
||||
sortBy: 'title',
|
||||
sortDirection: 'asc',
|
||||
views: [view],
|
||||
})
|
||||
const displayedNotes = this.application.items.getDisplayableNotes()
|
||||
const displayedNotes = application.items.getDisplayableNotes()
|
||||
expect(displayedNotes).to.deep.equal(matches)
|
||||
expect(matches.length).to.equal(1)
|
||||
expect(matches[0].uuid).to.equal(taggedNote.uuid)
|
||||
})
|
||||
|
||||
it('"ignored", "and", [["pinned", "=", true], ["locked", "=", true]]', async function () {
|
||||
const pinnedAndLockedNote = await Factory.createMappedNote(this.application)
|
||||
await this.application.mutator.changeItem(pinnedAndLockedNote, (mutator) => {
|
||||
const pinnedAndLockedNote = await Factory.createMappedNote(application)
|
||||
await application.mutator.changeItem(pinnedAndLockedNote, (mutator) => {
|
||||
mutator.pinned = true
|
||||
mutator.locked = true
|
||||
})
|
||||
|
||||
const pinnedNote = await this.application.mutator.insertItem(
|
||||
await this.application.items.createTemplateItem(ContentType.TYPES.Note, {
|
||||
const pinnedNote = await application.mutator.insertItem(
|
||||
await application.items.createTemplateItem(ContentType.TYPES.Note, {
|
||||
title: 'A',
|
||||
}),
|
||||
)
|
||||
await this.application.mutator.changeItem(pinnedNote, (mutator) => {
|
||||
await application.mutator.changeItem(pinnedNote, (mutator) => {
|
||||
mutator.pinned = true
|
||||
})
|
||||
|
||||
const lockedNote = await this.application.mutator.insertItem(
|
||||
await this.application.items.createTemplateItem(ContentType.TYPES.Note, {
|
||||
const lockedNote = await application.mutator.insertItem(
|
||||
await application.items.createTemplateItem(ContentType.TYPES.Note, {
|
||||
title: 'A',
|
||||
}),
|
||||
)
|
||||
await this.application.mutator.changeItem(lockedNote, (mutator) => {
|
||||
await application.mutator.changeItem(lockedNote, (mutator) => {
|
||||
mutator.locked = true
|
||||
})
|
||||
|
||||
const view = await this.application.mutator.insertItem(
|
||||
await this.application.items.createTemplateItem(ContentType.TYPES.SmartView, {
|
||||
const view = await application.mutator.insertItem(
|
||||
await application.items.createTemplateItem(ContentType.TYPES.SmartView, {
|
||||
title: 'Pinned & Locked',
|
||||
predicate: {
|
||||
operator: 'and',
|
||||
@@ -721,51 +726,51 @@ describe('notes and tags', () => {
|
||||
},
|
||||
}),
|
||||
)
|
||||
const matches = this.application.items.notesMatchingSmartView(view)
|
||||
this.application.items.setPrimaryItemDisplayOptions({
|
||||
const matches = application.items.notesMatchingSmartView(view)
|
||||
application.items.setPrimaryItemDisplayOptions({
|
||||
sortBy: 'title',
|
||||
sortDirection: 'asc',
|
||||
views: [view],
|
||||
})
|
||||
const displayedNotes = this.application.items.getDisplayableNotes()
|
||||
const displayedNotes = application.items.getDisplayableNotes()
|
||||
expect(displayedNotes).to.deep.equal(matches)
|
||||
expect(matches.length).to.equal(1)
|
||||
expect(matches[0].uuid).to.equal(pinnedAndLockedNote.uuid)
|
||||
})
|
||||
|
||||
it('"ignored", "or", [["content.protected", "=", true], ["pinned", "=", true]]', async function () {
|
||||
const protectedNote = await Factory.createMappedNote(this.application)
|
||||
await this.application.mutator.changeItem(protectedNote, (mutator) => {
|
||||
const protectedNote = await Factory.createMappedNote(application)
|
||||
await application.mutator.changeItem(protectedNote, (mutator) => {
|
||||
mutator.protected = true
|
||||
})
|
||||
|
||||
const pinnedNote = await this.application.mutator.insertItem(
|
||||
await this.application.items.createTemplateItem(ContentType.TYPES.Note, {
|
||||
const pinnedNote = await application.mutator.insertItem(
|
||||
await application.items.createTemplateItem(ContentType.TYPES.Note, {
|
||||
title: 'A',
|
||||
}),
|
||||
)
|
||||
await this.application.mutator.changeItem(pinnedNote, (mutator) => {
|
||||
await application.mutator.changeItem(pinnedNote, (mutator) => {
|
||||
mutator.pinned = true
|
||||
})
|
||||
|
||||
const pinnedAndProtectedNote = await this.application.mutator.insertItem(
|
||||
await this.application.items.createTemplateItem(ContentType.TYPES.Note, {
|
||||
const pinnedAndProtectedNote = await application.mutator.insertItem(
|
||||
await application.items.createTemplateItem(ContentType.TYPES.Note, {
|
||||
title: 'A',
|
||||
}),
|
||||
)
|
||||
await this.application.mutator.changeItem(pinnedAndProtectedNote, (mutator) => {
|
||||
await application.mutator.changeItem(pinnedAndProtectedNote, (mutator) => {
|
||||
mutator.pinned = true
|
||||
mutator.protected = true
|
||||
})
|
||||
|
||||
await this.application.mutator.insertItem(
|
||||
await this.application.items.createTemplateItem(ContentType.TYPES.Note, {
|
||||
await application.mutator.insertItem(
|
||||
await application.items.createTemplateItem(ContentType.TYPES.Note, {
|
||||
title: 'A',
|
||||
}),
|
||||
)
|
||||
|
||||
const view = await this.application.mutator.insertItem(
|
||||
await this.application.items.createTemplateItem(ContentType.TYPES.SmartView, {
|
||||
const view = await application.mutator.insertItem(
|
||||
await application.items.createTemplateItem(ContentType.TYPES.SmartView, {
|
||||
title: 'Protected or Pinned',
|
||||
predicate: {
|
||||
operator: 'or',
|
||||
@@ -776,13 +781,13 @@ describe('notes and tags', () => {
|
||||
},
|
||||
}),
|
||||
)
|
||||
const matches = this.application.items.notesMatchingSmartView(view)
|
||||
this.application.items.setPrimaryItemDisplayOptions({
|
||||
const matches = application.items.notesMatchingSmartView(view)
|
||||
application.items.setPrimaryItemDisplayOptions({
|
||||
sortBy: 'created_at',
|
||||
sortDirection: 'asc',
|
||||
views: [view],
|
||||
})
|
||||
const displayedNotes = this.application.items.getDisplayableNotes()
|
||||
const displayedNotes = application.items.getDisplayableNotes()
|
||||
expect(displayedNotes.length).to.equal(matches.length)
|
||||
expect(matches.length).to.equal(3)
|
||||
expect(matches.find((note) => note.uuid === protectedNote.uuid)).to.exist
|
||||
@@ -801,12 +806,12 @@ describe('notes and tags', () => {
|
||||
const notePayload3 = Factory.createNotePayload('Bar')
|
||||
const notePayload4 = Factory.createNotePayload('Testing')
|
||||
|
||||
await this.application.mutator.emitItemsFromPayloads(
|
||||
await application.mutator.emitItemsFromPayloads(
|
||||
[notePayload1, notePayload2, notePayload3, notePayload4, tagPayload1],
|
||||
PayloadEmitSource.LocalChanged,
|
||||
)
|
||||
|
||||
this.application.items.setPrimaryItemDisplayOptions({
|
||||
application.items.setPrimaryItemDisplayOptions({
|
||||
sortBy: 'title',
|
||||
sortDirection: 'dsc',
|
||||
searchQuery: {
|
||||
@@ -814,7 +819,7 @@ describe('notes and tags', () => {
|
||||
},
|
||||
})
|
||||
|
||||
const displayedNotes = this.application.items.getDisplayableNotes()
|
||||
const displayedNotes = application.items.getDisplayableNotes()
|
||||
expect(displayedNotes.length).to.equal(2)
|
||||
/** setPrimaryItemDisplayOptions inverses sort for title */
|
||||
expect(displayedNotes[0].uuid).to.equal(notePayload1.uuid)
|
||||
@@ -831,12 +836,12 @@ describe('notes and tags', () => {
|
||||
const notePayload3 = Factory.createNotePayload('Testing FOO (Bar)')
|
||||
const notePayload4 = Factory.createNotePayload('This should not match')
|
||||
|
||||
await this.application.mutator.emitItemsFromPayloads(
|
||||
await application.mutator.emitItemsFromPayloads(
|
||||
[notePayload1, notePayload2, notePayload3, notePayload4, tagPayload1],
|
||||
PayloadEmitSource.LocalChanged,
|
||||
)
|
||||
|
||||
this.application.items.setPrimaryItemDisplayOptions({
|
||||
application.items.setPrimaryItemDisplayOptions({
|
||||
sortBy: 'title',
|
||||
sortDirection: 'dsc',
|
||||
searchQuery: {
|
||||
@@ -844,7 +849,7 @@ describe('notes and tags', () => {
|
||||
},
|
||||
})
|
||||
|
||||
const displayedNotes = this.application.items.getDisplayableNotes()
|
||||
const displayedNotes = application.items.getDisplayableNotes()
|
||||
expect(displayedNotes.length).to.equal(3)
|
||||
/** setPrimaryItemDisplayOptions inverses sort for title */
|
||||
expect(displayedNotes[0].uuid).to.equal(notePayload1.uuid)
|
||||
|
||||
@@ -1,86 +1,91 @@
|
||||
/* eslint-disable no-unused-expressions */
|
||||
/* eslint-disable no-undef */
|
||||
import * as Factory from '../lib/factory.js'
|
||||
|
||||
chai.use(chaiAsPromised)
|
||||
const expect = chai.expect
|
||||
|
||||
describe('tags as folders', () => {
|
||||
let context
|
||||
let application
|
||||
|
||||
beforeEach(async function () {
|
||||
this.context = await Factory.createAppContext()
|
||||
await this.context.launch()
|
||||
this.application = this.context.application
|
||||
localStorage.clear()
|
||||
context = await Factory.createAppContext()
|
||||
await context.launch()
|
||||
application = context.application
|
||||
})
|
||||
|
||||
afterEach(async function () {
|
||||
await Factory.safeDeinit(this.application)
|
||||
await Factory.safeDeinit(application)
|
||||
context = undefined
|
||||
application = undefined
|
||||
localStorage.clear()
|
||||
})
|
||||
|
||||
it('lets me create a tag, add relationships, move a note to a children, and query data all along', async function () {
|
||||
// ## The user creates four tags
|
||||
let tagChildren = await Factory.createMappedTag(this.application, {
|
||||
let tagChildren = await Factory.createMappedTag(application, {
|
||||
title: 'children',
|
||||
})
|
||||
let tagParent = await Factory.createMappedTag(this.application, {
|
||||
let tagParent = await Factory.createMappedTag(application, {
|
||||
title: 'parent',
|
||||
})
|
||||
let tagGrandParent = await Factory.createMappedTag(this.application, {
|
||||
let tagGrandParent = await Factory.createMappedTag(application, {
|
||||
title: 'grandparent',
|
||||
})
|
||||
let tagGrandParent2 = await Factory.createMappedTag(this.application, {
|
||||
let tagGrandParent2 = await Factory.createMappedTag(application, {
|
||||
title: 'grandparent2',
|
||||
})
|
||||
|
||||
// ## Now the users moves the tag children into the parent
|
||||
await this.application.mutator.setTagParent(tagParent, tagChildren)
|
||||
await application.mutator.setTagParent(tagParent, tagChildren)
|
||||
|
||||
expect(this.application.items.getTagParent(tagChildren)).to.equal(tagParent)
|
||||
expect(Uuids(this.application.items.getTagChildren(tagParent))).deep.to.equal(Uuids([tagChildren]))
|
||||
expect(application.items.getTagParent(tagChildren)).to.equal(tagParent)
|
||||
expect(Uuids(application.items.getTagChildren(tagParent))).deep.to.equal(Uuids([tagChildren]))
|
||||
|
||||
// ## Now the user moves the tag parent into the grand parent
|
||||
await this.application.mutator.setTagParent(tagGrandParent, tagParent)
|
||||
await application.mutator.setTagParent(tagGrandParent, tagParent)
|
||||
|
||||
expect(this.application.items.getTagParent(tagParent)).to.equal(tagGrandParent)
|
||||
expect(Uuids(this.application.items.getTagChildren(tagGrandParent))).deep.to.equal(Uuids([tagParent]))
|
||||
expect(application.items.getTagParent(tagParent)).to.equal(tagGrandParent)
|
||||
expect(Uuids(application.items.getTagChildren(tagGrandParent))).deep.to.equal(Uuids([tagParent]))
|
||||
|
||||
// ## Now the user moves the tag parent into another grand parent
|
||||
await this.application.mutator.setTagParent(tagGrandParent2, tagParent)
|
||||
await application.mutator.setTagParent(tagGrandParent2, tagParent)
|
||||
|
||||
expect(this.application.items.getTagParent(tagParent)).to.equal(tagGrandParent2)
|
||||
expect(this.application.items.getTagChildren(tagGrandParent)).deep.to.equal([])
|
||||
expect(Uuids(this.application.items.getTagChildren(tagGrandParent2))).deep.to.equal(Uuids([tagParent]))
|
||||
expect(application.items.getTagParent(tagParent)).to.equal(tagGrandParent2)
|
||||
expect(application.items.getTagChildren(tagGrandParent)).deep.to.equal([])
|
||||
expect(Uuids(application.items.getTagChildren(tagGrandParent2))).deep.to.equal(Uuids([tagParent]))
|
||||
|
||||
// ## Now the user tries to move the tag into one of its children
|
||||
await expect(this.application.mutator.setTagParent(tagChildren, tagParent)).to.eventually.be.rejected
|
||||
await expect(application.mutator.setTagParent(tagChildren, tagParent)).to.eventually.be.rejected
|
||||
|
||||
expect(this.application.items.getTagParent(tagParent)).to.equal(tagGrandParent2)
|
||||
expect(this.application.items.getTagChildren(tagGrandParent)).deep.to.equal([])
|
||||
expect(Uuids(this.application.items.getTagChildren(tagGrandParent2))).deep.to.equal(Uuids([tagParent]))
|
||||
expect(application.items.getTagParent(tagParent)).to.equal(tagGrandParent2)
|
||||
expect(application.items.getTagChildren(tagGrandParent)).deep.to.equal([])
|
||||
expect(Uuids(application.items.getTagChildren(tagGrandParent2))).deep.to.equal(Uuids([tagParent]))
|
||||
|
||||
// ## Now the user move the tag outside any hierarchy
|
||||
await this.application.mutator.unsetTagParent(tagParent)
|
||||
await application.mutator.unsetTagParent(tagParent)
|
||||
|
||||
expect(this.application.items.getTagParent(tagParent)).to.equal(undefined)
|
||||
expect(this.application.items.getTagChildren(tagGrandParent2)).deep.to.equals([])
|
||||
expect(application.items.getTagParent(tagParent)).to.equal(undefined)
|
||||
expect(application.items.getTagChildren(tagGrandParent2)).deep.to.equals([])
|
||||
})
|
||||
|
||||
it('lets me add a note to a tag hierarchy', async function () {
|
||||
// ## The user creates four tags hierarchy
|
||||
const tags = await Factory.createTags(this.application, {
|
||||
const tags = await Factory.createTags(application, {
|
||||
grandparent: { parent: { child: true } },
|
||||
another: true,
|
||||
})
|
||||
|
||||
const note1 = await Factory.createMappedNote(this.application, 'my first note')
|
||||
const note2 = await Factory.createMappedNote(this.application, 'my second note')
|
||||
const note1 = await Factory.createMappedNote(application, 'my first note')
|
||||
const note2 = await Factory.createMappedNote(application, 'my second note')
|
||||
|
||||
// ## The user add a note to the child tag
|
||||
await this.application.mutator.addTagToNote(note1, tags.child, true)
|
||||
await this.application.mutator.addTagToNote(note2, tags.another, true)
|
||||
await application.mutator.addTagToNote(note1, tags.child, true)
|
||||
await application.mutator.addTagToNote(note2, tags.another, true)
|
||||
|
||||
// ## The note has been added to other tags
|
||||
const note1Tags = await this.application.items.getSortedTagsForItem(note1)
|
||||
const note2Tags = await this.application.items.getSortedTagsForItem(note2)
|
||||
const note1Tags = await application.items.getSortedTagsForItem(note1)
|
||||
const note2Tags = await application.items.getSortedTagsForItem(note2)
|
||||
|
||||
expect(note1Tags.length).to.equal(3)
|
||||
expect(note2Tags.length).to.equal(1)
|
||||
|
||||
@@ -1,10 +1,17 @@
|
||||
/* eslint-disable no-unused-expressions */
|
||||
/* eslint-disable no-undef */
|
||||
import * as Factory from '../lib/factory.js'
|
||||
|
||||
chai.use(chaiAsPromised)
|
||||
const expect = chai.expect
|
||||
|
||||
describe('mapping performance', () => {
|
||||
beforeEach(function () {
|
||||
localStorage.clear()
|
||||
})
|
||||
|
||||
afterEach(async function () {
|
||||
localStorage.clear()
|
||||
})
|
||||
|
||||
it('shouldnt take a long time', async () => {
|
||||
/*
|
||||
There was an issue with mapping where we were using arrays for everything instead of hashes (like items, missedReferences),
|
||||
|
||||
@@ -1,47 +1,44 @@
|
||||
/* eslint-disable no-unused-expressions */
|
||||
/* eslint-disable no-undef */
|
||||
import * as Factory from './lib/factory.js'
|
||||
|
||||
chai.use(chaiAsPromised)
|
||||
const expect = chai.expect
|
||||
|
||||
describe('item mutator', () => {
|
||||
beforeEach(async function () {
|
||||
this.createBarePayload = () => {
|
||||
return new DecryptedPayload({
|
||||
uuid: '123',
|
||||
content_type: ContentType.TYPES.Note,
|
||||
const createBarePayload = () => {
|
||||
return new DecryptedPayload({
|
||||
uuid: '123',
|
||||
content_type: ContentType.TYPES.Note,
|
||||
content: {
|
||||
title: 'hello',
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
const createNote = () => {
|
||||
return new DecryptedItem(createBarePayload())
|
||||
}
|
||||
|
||||
const createTag = (notes = []) => {
|
||||
const references = notes.map((note) => {
|
||||
return {
|
||||
uuid: note.uuid,
|
||||
content_type: note.content_type,
|
||||
}
|
||||
})
|
||||
return new SNTag(
|
||||
new DecryptedPayload({
|
||||
uuid: Factory.generateUuidish(),
|
||||
content_type: ContentType.TYPES.Tag,
|
||||
content: {
|
||||
title: 'hello',
|
||||
title: 'thoughts',
|
||||
references: references,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
this.createNote = () => {
|
||||
return new DecryptedItem(this.createBarePayload())
|
||||
}
|
||||
|
||||
this.createTag = (notes = []) => {
|
||||
const references = notes.map((note) => {
|
||||
return {
|
||||
uuid: note.uuid,
|
||||
content_type: note.content_type,
|
||||
}
|
||||
})
|
||||
return new SNTag(
|
||||
new DecryptedPayload({
|
||||
uuid: Factory.generateUuidish(),
|
||||
content_type: ContentType.TYPES.Tag,
|
||||
content: {
|
||||
title: 'thoughts',
|
||||
references: references,
|
||||
},
|
||||
}),
|
||||
)
|
||||
}
|
||||
})
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
it('mutate set domain data key', function () {
|
||||
const item = this.createNote()
|
||||
const item = createNote()
|
||||
const mutator = new DecryptedItemMutator(item)
|
||||
mutator.setDomainDataKey('somekey', 'somevalue', 'somedomain')
|
||||
const payload = mutator.getResult()
|
||||
@@ -50,7 +47,7 @@ describe('item mutator', () => {
|
||||
})
|
||||
|
||||
it('mutate set pinned', function () {
|
||||
const item = this.createNote()
|
||||
const item = createNote()
|
||||
const mutator = new DecryptedItemMutator(item)
|
||||
mutator.pinned = true
|
||||
const payload = mutator.getResult()
|
||||
@@ -59,7 +56,7 @@ describe('item mutator', () => {
|
||||
})
|
||||
|
||||
it('mutate set archived', function () {
|
||||
const item = this.createNote()
|
||||
const item = createNote()
|
||||
const mutator = new DecryptedItemMutator(item)
|
||||
mutator.archived = true
|
||||
const payload = mutator.getResult()
|
||||
@@ -68,7 +65,7 @@ describe('item mutator', () => {
|
||||
})
|
||||
|
||||
it('mutate set locked', function () {
|
||||
const item = this.createNote()
|
||||
const item = createNote()
|
||||
const mutator = new DecryptedItemMutator(item)
|
||||
mutator.locked = true
|
||||
const payload = mutator.getResult()
|
||||
@@ -77,7 +74,7 @@ describe('item mutator', () => {
|
||||
})
|
||||
|
||||
it('mutate set protected', function () {
|
||||
const item = this.createNote()
|
||||
const item = createNote()
|
||||
const mutator = new DecryptedItemMutator(item)
|
||||
mutator.protected = true
|
||||
const payload = mutator.getResult()
|
||||
@@ -86,7 +83,7 @@ describe('item mutator', () => {
|
||||
})
|
||||
|
||||
it('mutate set trashed', function () {
|
||||
const item = this.createNote()
|
||||
const item = createNote()
|
||||
const mutator = new DecryptedItemMutator(item)
|
||||
mutator.trashed = true
|
||||
const payload = mutator.getResult()
|
||||
@@ -95,7 +92,7 @@ describe('item mutator', () => {
|
||||
})
|
||||
|
||||
it('calling get result should set us dirty', function () {
|
||||
const item = this.createNote()
|
||||
const item = createNote()
|
||||
const mutator = new DecryptedItemMutator(item)
|
||||
const payload = mutator.getResult()
|
||||
|
||||
@@ -103,7 +100,7 @@ describe('item mutator', () => {
|
||||
})
|
||||
|
||||
it('get result should always have userModifiedDate', function () {
|
||||
const item = this.createNote()
|
||||
const item = createNote()
|
||||
const mutator = new DecryptedItemMutator(item)
|
||||
const payload = mutator.getResult()
|
||||
const resultItem = CreateDecryptedItemFromPayload(payload)
|
||||
@@ -111,7 +108,7 @@ describe('item mutator', () => {
|
||||
})
|
||||
|
||||
it('mutate set deleted', function () {
|
||||
const item = this.createNote()
|
||||
const item = createNote()
|
||||
const mutator = new DeleteItemMutator(item)
|
||||
const payload = mutator.getDeletedResult()
|
||||
|
||||
@@ -121,7 +118,7 @@ describe('item mutator', () => {
|
||||
})
|
||||
|
||||
it('mutate app data', function () {
|
||||
const item = this.createNote()
|
||||
const item = createNote()
|
||||
const mutator = new DecryptedItemMutator(item, MutationType.UpdateUserTimestamps)
|
||||
mutator.setAppDataItem('foo', 'bar')
|
||||
mutator.setAppDataItem('bar', 'foo')
|
||||
@@ -131,8 +128,8 @@ describe('item mutator', () => {
|
||||
})
|
||||
|
||||
it('mutate add item as relationship', function () {
|
||||
const note = this.createNote()
|
||||
const tag = this.createTag()
|
||||
const note = createNote()
|
||||
const tag = createTag()
|
||||
const mutator = new DecryptedItemMutator(tag)
|
||||
mutator.e2ePendingRefactor_addItemAsRelationship(note)
|
||||
const payload = mutator.getResult()
|
||||
@@ -142,8 +139,8 @@ describe('item mutator', () => {
|
||||
})
|
||||
|
||||
it('mutate remove item as relationship', function () {
|
||||
const note = this.createNote()
|
||||
const tag = this.createTag([note])
|
||||
const note = createNote()
|
||||
const tag = createTag([note])
|
||||
const mutator = new DecryptedItemMutator(tag)
|
||||
mutator.removeItemAsRelationship(note)
|
||||
const payload = mutator.getResult()
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
/* eslint-disable no-unused-expressions */
|
||||
/* eslint-disable no-undef */
|
||||
import * as Factory from './lib/factory.js'
|
||||
import { BaseItemCounts } from './lib/BaseItemCounts.js'
|
||||
|
||||
chai.use(chaiAsPromised)
|
||||
const expect = chai.expect
|
||||
|
||||
@@ -12,11 +11,6 @@ describe('mutator service', function () {
|
||||
let application
|
||||
let mutator
|
||||
|
||||
afterEach(async function () {
|
||||
await context.deinit()
|
||||
localStorage.clear()
|
||||
})
|
||||
|
||||
beforeEach(async function () {
|
||||
localStorage.clear()
|
||||
|
||||
@@ -27,6 +21,12 @@ describe('mutator service', function () {
|
||||
await context.launch()
|
||||
})
|
||||
|
||||
afterEach(async function () {
|
||||
await context.deinit()
|
||||
localStorage.clear()
|
||||
context = undefined
|
||||
})
|
||||
|
||||
const createNote = async () => {
|
||||
return mutator.createItem(ContentType.TYPES.Note, {
|
||||
title: 'hello',
|
||||
@@ -154,8 +154,10 @@ describe('mutator service', function () {
|
||||
describe('duplicateItem', async function () {
|
||||
const sandbox = sinon.createSandbox()
|
||||
|
||||
let emitPayloads
|
||||
|
||||
beforeEach(async function () {
|
||||
this.emitPayloads = sandbox.spy(application.payloads, 'emitPayloads')
|
||||
emitPayloads = sandbox.spy(application.payloads, 'emitPayloads')
|
||||
})
|
||||
|
||||
afterEach(async function () {
|
||||
@@ -165,7 +167,7 @@ describe('mutator service', function () {
|
||||
it('should duplicate the item and set the duplicate_of property', async function () {
|
||||
const note = await createNote()
|
||||
await mutator.duplicateItem(note)
|
||||
sinon.assert.calledTwice(this.emitPayloads)
|
||||
sinon.assert.calledTwice(emitPayloads)
|
||||
|
||||
const originalNote = application.items.getDisplayableNotes()[0]
|
||||
const duplicatedNote = application.items.getDisplayableNotes()[1]
|
||||
@@ -182,7 +184,7 @@ describe('mutator service', function () {
|
||||
it('should duplicate the item and set the duplicate_of and conflict_of properties', async function () {
|
||||
const note = await createNote()
|
||||
await mutator.duplicateItem(note, true)
|
||||
sinon.assert.calledTwice(this.emitPayloads)
|
||||
sinon.assert.calledTwice(emitPayloads)
|
||||
|
||||
const originalNote = application.items.getDisplayableNotes()[0]
|
||||
const duplicatedNote = application.items.getDisplayableNotes()[1]
|
||||
|
||||
@@ -1,30 +1,36 @@
|
||||
/* eslint-disable no-undef */
|
||||
chai.use(chaiAsPromised)
|
||||
const expect = chai.expect
|
||||
|
||||
describe('note display criteria', function () {
|
||||
let payloadManager
|
||||
let itemManager
|
||||
let mutator
|
||||
|
||||
let createNote
|
||||
let createTag
|
||||
|
||||
beforeEach(async function () {
|
||||
const logger = new Logger('test')
|
||||
|
||||
this.payloadManager = new PayloadManager(logger)
|
||||
this.itemManager = new ItemManager(this.payloadManager)
|
||||
this.mutator = new MutatorService(this.itemManager, this.payloadManager)
|
||||
payloadManager = new PayloadManager(logger)
|
||||
itemManager = new ItemManager(payloadManager)
|
||||
mutator = new MutatorService(itemManager, payloadManager)
|
||||
|
||||
this.createNote = async (title = 'hello', text = 'world') => {
|
||||
return this.mutator.createItem(ContentType.TYPES.Note, {
|
||||
createNote = async (title = 'hello', text = 'world') => {
|
||||
return mutator.createItem(ContentType.TYPES.Note, {
|
||||
title: title,
|
||||
text: text,
|
||||
})
|
||||
}
|
||||
|
||||
this.createTag = async (notes = [], title = 'thoughts') => {
|
||||
createTag = async (notes = [], title = 'thoughts') => {
|
||||
const references = notes.map((note) => {
|
||||
return {
|
||||
uuid: note.uuid,
|
||||
content_type: note.content_type,
|
||||
}
|
||||
})
|
||||
return this.mutator.createItem(ContentType.TYPES.Tag, {
|
||||
return mutator.createItem(ContentType.TYPES.Tag, {
|
||||
title: title,
|
||||
references: references,
|
||||
})
|
||||
@@ -32,177 +38,147 @@ describe('note display criteria', function () {
|
||||
})
|
||||
|
||||
it('includePinned off', async function () {
|
||||
await this.createNote()
|
||||
const pendingPin = await this.createNote()
|
||||
await this.mutator.changeItem(pendingPin, (mutator) => {
|
||||
await createNote()
|
||||
const pendingPin = await createNote()
|
||||
await mutator.changeItem(pendingPin, (mutator) => {
|
||||
mutator.pinned = true
|
||||
})
|
||||
const criteria = {
|
||||
includePinned: false,
|
||||
}
|
||||
expect(
|
||||
notesAndFilesMatchingOptions(
|
||||
criteria,
|
||||
this.itemManager.collection.all(ContentType.TYPES.Note),
|
||||
this.itemManager.collection,
|
||||
).length,
|
||||
notesAndFilesMatchingOptions(criteria, itemManager.collection.all(ContentType.TYPES.Note), itemManager.collection)
|
||||
.length,
|
||||
).to.equal(1)
|
||||
})
|
||||
|
||||
it('includePinned on', async function () {
|
||||
await this.createNote()
|
||||
const pendingPin = await this.createNote()
|
||||
await this.mutator.changeItem(pendingPin, (mutator) => {
|
||||
await createNote()
|
||||
const pendingPin = await createNote()
|
||||
await mutator.changeItem(pendingPin, (mutator) => {
|
||||
mutator.pinned = true
|
||||
})
|
||||
const criteria = { includePinned: true }
|
||||
expect(
|
||||
notesAndFilesMatchingOptions(
|
||||
criteria,
|
||||
this.itemManager.collection.all(ContentType.TYPES.Note),
|
||||
this.itemManager.collection,
|
||||
).length,
|
||||
notesAndFilesMatchingOptions(criteria, itemManager.collection.all(ContentType.TYPES.Note), itemManager.collection)
|
||||
.length,
|
||||
).to.equal(2)
|
||||
})
|
||||
|
||||
it('includeTrashed off', async function () {
|
||||
await this.createNote()
|
||||
const pendingTrash = await this.createNote()
|
||||
await this.mutator.changeItem(pendingTrash, (mutator) => {
|
||||
await createNote()
|
||||
const pendingTrash = await createNote()
|
||||
await mutator.changeItem(pendingTrash, (mutator) => {
|
||||
mutator.trashed = true
|
||||
})
|
||||
const criteria = { includeTrashed: false }
|
||||
expect(
|
||||
notesAndFilesMatchingOptions(
|
||||
criteria,
|
||||
this.itemManager.collection.all(ContentType.TYPES.Note),
|
||||
this.itemManager.collection,
|
||||
).length,
|
||||
notesAndFilesMatchingOptions(criteria, itemManager.collection.all(ContentType.TYPES.Note), itemManager.collection)
|
||||
.length,
|
||||
).to.equal(1)
|
||||
})
|
||||
|
||||
it('includeTrashed on', async function () {
|
||||
await this.createNote()
|
||||
const pendingTrash = await this.createNote()
|
||||
await this.mutator.changeItem(pendingTrash, (mutator) => {
|
||||
await createNote()
|
||||
const pendingTrash = await createNote()
|
||||
await mutator.changeItem(pendingTrash, (mutator) => {
|
||||
mutator.trashed = true
|
||||
})
|
||||
const criteria = { includeTrashed: true }
|
||||
expect(
|
||||
notesAndFilesMatchingOptions(
|
||||
criteria,
|
||||
this.itemManager.collection.all(ContentType.TYPES.Note),
|
||||
this.itemManager.collection,
|
||||
).length,
|
||||
notesAndFilesMatchingOptions(criteria, itemManager.collection.all(ContentType.TYPES.Note), itemManager.collection)
|
||||
.length,
|
||||
).to.equal(2)
|
||||
})
|
||||
|
||||
it('includeArchived off', async function () {
|
||||
await this.createNote()
|
||||
const pendingArchive = await this.createNote()
|
||||
await this.mutator.changeItem(pendingArchive, (mutator) => {
|
||||
await createNote()
|
||||
const pendingArchive = await createNote()
|
||||
await mutator.changeItem(pendingArchive, (mutator) => {
|
||||
mutator.archived = true
|
||||
})
|
||||
const criteria = { includeArchived: false }
|
||||
expect(
|
||||
notesAndFilesMatchingOptions(
|
||||
criteria,
|
||||
this.itemManager.collection.all(ContentType.TYPES.Note),
|
||||
this.itemManager.collection,
|
||||
).length,
|
||||
notesAndFilesMatchingOptions(criteria, itemManager.collection.all(ContentType.TYPES.Note), itemManager.collection)
|
||||
.length,
|
||||
).to.equal(1)
|
||||
})
|
||||
|
||||
it('includeArchived on', async function () {
|
||||
await this.createNote()
|
||||
const pendingArchive = await this.createNote()
|
||||
await this.mutator.changeItem(pendingArchive, (mutator) => {
|
||||
await createNote()
|
||||
const pendingArchive = await createNote()
|
||||
await mutator.changeItem(pendingArchive, (mutator) => {
|
||||
mutator.archived = true
|
||||
})
|
||||
const criteria = {
|
||||
includeArchived: true,
|
||||
}
|
||||
expect(
|
||||
notesAndFilesMatchingOptions(
|
||||
criteria,
|
||||
this.itemManager.collection.all(ContentType.TYPES.Note),
|
||||
this.itemManager.collection,
|
||||
).length,
|
||||
notesAndFilesMatchingOptions(criteria, itemManager.collection.all(ContentType.TYPES.Note), itemManager.collection)
|
||||
.length,
|
||||
).to.equal(2)
|
||||
})
|
||||
|
||||
it('includeProtected off', async function () {
|
||||
await this.createNote()
|
||||
const pendingProtected = await this.createNote()
|
||||
await this.mutator.changeItem(pendingProtected, (mutator) => {
|
||||
await createNote()
|
||||
const pendingProtected = await createNote()
|
||||
await mutator.changeItem(pendingProtected, (mutator) => {
|
||||
mutator.protected = true
|
||||
})
|
||||
const criteria = { includeProtected: false }
|
||||
expect(
|
||||
notesAndFilesMatchingOptions(
|
||||
criteria,
|
||||
this.itemManager.collection.all(ContentType.TYPES.Note),
|
||||
this.itemManager.collection,
|
||||
).length,
|
||||
notesAndFilesMatchingOptions(criteria, itemManager.collection.all(ContentType.TYPES.Note), itemManager.collection)
|
||||
.length,
|
||||
).to.equal(1)
|
||||
})
|
||||
|
||||
it('includeProtected on', async function () {
|
||||
await this.createNote()
|
||||
const pendingProtected = await this.createNote()
|
||||
await this.mutator.changeItem(pendingProtected, (mutator) => {
|
||||
await createNote()
|
||||
const pendingProtected = await createNote()
|
||||
await mutator.changeItem(pendingProtected, (mutator) => {
|
||||
mutator.protected = true
|
||||
})
|
||||
const criteria = {
|
||||
includeProtected: true,
|
||||
}
|
||||
expect(
|
||||
notesAndFilesMatchingOptions(
|
||||
criteria,
|
||||
this.itemManager.collection.all(ContentType.TYPES.Note),
|
||||
this.itemManager.collection,
|
||||
).length,
|
||||
notesAndFilesMatchingOptions(criteria, itemManager.collection.all(ContentType.TYPES.Note), itemManager.collection)
|
||||
.length,
|
||||
).to.equal(2)
|
||||
})
|
||||
|
||||
it('protectedSearchEnabled false', async function () {
|
||||
const normal = await this.createNote('hello', 'world')
|
||||
await this.mutator.changeItem(normal, (mutator) => {
|
||||
const normal = await createNote('hello', 'world')
|
||||
await mutator.changeItem(normal, (mutator) => {
|
||||
mutator.protected = true
|
||||
})
|
||||
const criteria = {
|
||||
searchQuery: { query: 'world', includeProtectedNoteText: false },
|
||||
}
|
||||
expect(
|
||||
notesAndFilesMatchingOptions(
|
||||
criteria,
|
||||
this.itemManager.collection.all(ContentType.TYPES.Note),
|
||||
this.itemManager.collection,
|
||||
).length,
|
||||
notesAndFilesMatchingOptions(criteria, itemManager.collection.all(ContentType.TYPES.Note), itemManager.collection)
|
||||
.length,
|
||||
).to.equal(0)
|
||||
})
|
||||
|
||||
it('protectedSearchEnabled true', async function () {
|
||||
const normal = await this.createNote()
|
||||
await this.mutator.changeItem(normal, (mutator) => {
|
||||
const normal = await createNote()
|
||||
await mutator.changeItem(normal, (mutator) => {
|
||||
mutator.protected = true
|
||||
})
|
||||
const criteria = {
|
||||
searchQuery: { query: 'world', includeProtectedNoteText: true },
|
||||
}
|
||||
expect(
|
||||
notesAndFilesMatchingOptions(
|
||||
criteria,
|
||||
this.itemManager.collection.all(ContentType.TYPES.Note),
|
||||
this.itemManager.collection,
|
||||
).length,
|
||||
notesAndFilesMatchingOptions(criteria, itemManager.collection.all(ContentType.TYPES.Note), itemManager.collection)
|
||||
.length,
|
||||
).to.equal(1)
|
||||
})
|
||||
|
||||
it('tags', async function () {
|
||||
const note = await this.createNote()
|
||||
const tag = await this.createTag([note])
|
||||
const looseTag = await this.createTag([], 'loose')
|
||||
const note = await createNote()
|
||||
const tag = await createTag([note])
|
||||
const looseTag = await createTag([], 'loose')
|
||||
|
||||
const matchingCriteria = {
|
||||
tags: [tag],
|
||||
@@ -210,8 +186,8 @@ describe('note display criteria', function () {
|
||||
expect(
|
||||
notesAndFilesMatchingOptions(
|
||||
matchingCriteria,
|
||||
this.itemManager.collection.all(ContentType.TYPES.Note),
|
||||
this.itemManager.collection,
|
||||
itemManager.collection.all(ContentType.TYPES.Note),
|
||||
itemManager.collection,
|
||||
).length,
|
||||
).to.equal(1)
|
||||
|
||||
@@ -221,125 +197,125 @@ describe('note display criteria', function () {
|
||||
expect(
|
||||
notesAndFilesMatchingOptions(
|
||||
nonmatchingCriteria,
|
||||
this.itemManager.collection.all(ContentType.TYPES.Note),
|
||||
this.itemManager.collection,
|
||||
itemManager.collection.all(ContentType.TYPES.Note),
|
||||
itemManager.collection,
|
||||
).length,
|
||||
).to.equal(0)
|
||||
})
|
||||
|
||||
describe('smart views', function () {
|
||||
it('normal note', async function () {
|
||||
await this.createNote()
|
||||
await createNote()
|
||||
expect(
|
||||
notesAndFilesMatchingOptions(
|
||||
{
|
||||
views: [this.itemManager.allNotesSmartView],
|
||||
views: [itemManager.allNotesSmartView],
|
||||
},
|
||||
this.itemManager.collection.all(ContentType.TYPES.Note),
|
||||
this.itemManager.collection,
|
||||
itemManager.collection.all(ContentType.TYPES.Note),
|
||||
itemManager.collection,
|
||||
).length,
|
||||
).to.equal(1)
|
||||
|
||||
expect(
|
||||
notesAndFilesMatchingOptions(
|
||||
{
|
||||
views: [this.itemManager.trashSmartView],
|
||||
views: [itemManager.trashSmartView],
|
||||
},
|
||||
this.itemManager.collection.all(ContentType.TYPES.Note),
|
||||
this.itemManager.collection,
|
||||
itemManager.collection.all(ContentType.TYPES.Note),
|
||||
itemManager.collection,
|
||||
).length,
|
||||
).to.equal(0)
|
||||
|
||||
expect(
|
||||
notesAndFilesMatchingOptions(
|
||||
{
|
||||
views: [this.itemManager.archivedSmartView],
|
||||
views: [itemManager.archivedSmartView],
|
||||
},
|
||||
this.itemManager.collection.all(ContentType.TYPES.Note),
|
||||
this.itemManager.collection,
|
||||
itemManager.collection.all(ContentType.TYPES.Note),
|
||||
itemManager.collection,
|
||||
).length,
|
||||
).to.equal(0)
|
||||
})
|
||||
|
||||
it('trashed note', async function () {
|
||||
const normal = await this.createNote()
|
||||
await this.mutator.changeItem(normal, (mutator) => {
|
||||
const normal = await createNote()
|
||||
await mutator.changeItem(normal, (mutator) => {
|
||||
mutator.trashed = true
|
||||
})
|
||||
|
||||
expect(
|
||||
notesAndFilesMatchingOptions(
|
||||
{
|
||||
views: [this.itemManager.allNotesSmartView],
|
||||
views: [itemManager.allNotesSmartView],
|
||||
includeTrashed: false,
|
||||
},
|
||||
this.itemManager.collection.all(ContentType.TYPES.Note),
|
||||
this.itemManager.collection,
|
||||
itemManager.collection.all(ContentType.TYPES.Note),
|
||||
itemManager.collection,
|
||||
).length,
|
||||
).to.equal(0)
|
||||
|
||||
expect(
|
||||
notesAndFilesMatchingOptions(
|
||||
{
|
||||
views: [this.itemManager.trashSmartView],
|
||||
views: [itemManager.trashSmartView],
|
||||
},
|
||||
this.itemManager.collection.all(ContentType.TYPES.Note),
|
||||
this.itemManager.collection,
|
||||
itemManager.collection.all(ContentType.TYPES.Note),
|
||||
itemManager.collection,
|
||||
).length,
|
||||
).to.equal(1)
|
||||
|
||||
expect(
|
||||
notesAndFilesMatchingOptions(
|
||||
{
|
||||
views: [this.itemManager.archivedSmartView],
|
||||
views: [itemManager.archivedSmartView],
|
||||
},
|
||||
this.itemManager.collection.all(ContentType.TYPES.Note),
|
||||
this.itemManager.collection,
|
||||
itemManager.collection.all(ContentType.TYPES.Note),
|
||||
itemManager.collection,
|
||||
).length,
|
||||
).to.equal(0)
|
||||
})
|
||||
|
||||
it('archived note', async function () {
|
||||
const normal = await this.createNote()
|
||||
await this.mutator.changeItem(normal, (mutator) => {
|
||||
const normal = await createNote()
|
||||
await mutator.changeItem(normal, (mutator) => {
|
||||
mutator.trashed = false
|
||||
mutator.archived = true
|
||||
})
|
||||
expect(
|
||||
notesAndFilesMatchingOptions(
|
||||
{
|
||||
views: [this.itemManager.allNotesSmartView],
|
||||
views: [itemManager.allNotesSmartView],
|
||||
includeArchived: false,
|
||||
},
|
||||
this.itemManager.collection.all(ContentType.TYPES.Note),
|
||||
this.itemManager.collection,
|
||||
itemManager.collection.all(ContentType.TYPES.Note),
|
||||
itemManager.collection,
|
||||
).length,
|
||||
).to.equal(0)
|
||||
|
||||
expect(
|
||||
notesAndFilesMatchingOptions(
|
||||
{
|
||||
views: [this.itemManager.trashSmartView],
|
||||
views: [itemManager.trashSmartView],
|
||||
},
|
||||
this.itemManager.collection.all(ContentType.TYPES.Note),
|
||||
this.itemManager.collection,
|
||||
itemManager.collection.all(ContentType.TYPES.Note),
|
||||
itemManager.collection,
|
||||
).length,
|
||||
).to.equal(0)
|
||||
|
||||
expect(
|
||||
notesAndFilesMatchingOptions(
|
||||
{
|
||||
views: [this.itemManager.archivedSmartView],
|
||||
views: [itemManager.archivedSmartView],
|
||||
},
|
||||
this.itemManager.collection.all(ContentType.TYPES.Note),
|
||||
this.itemManager.collection,
|
||||
itemManager.collection.all(ContentType.TYPES.Note),
|
||||
itemManager.collection,
|
||||
).length,
|
||||
).to.equal(1)
|
||||
})
|
||||
|
||||
it('archived + trashed note', async function () {
|
||||
const normal = await this.createNote()
|
||||
await this.mutator.changeItem(normal, (mutator) => {
|
||||
const normal = await createNote()
|
||||
await mutator.changeItem(normal, (mutator) => {
|
||||
mutator.trashed = true
|
||||
mutator.archived = true
|
||||
})
|
||||
@@ -347,30 +323,30 @@ describe('note display criteria', function () {
|
||||
expect(
|
||||
notesAndFilesMatchingOptions(
|
||||
{
|
||||
views: [this.itemManager.allNotesSmartView],
|
||||
views: [itemManager.allNotesSmartView],
|
||||
},
|
||||
this.itemManager.collection.all(ContentType.TYPES.Note),
|
||||
this.itemManager.collection,
|
||||
itemManager.collection.all(ContentType.TYPES.Note),
|
||||
itemManager.collection,
|
||||
).length,
|
||||
).to.equal(1)
|
||||
|
||||
expect(
|
||||
notesAndFilesMatchingOptions(
|
||||
{
|
||||
views: [this.itemManager.trashSmartView],
|
||||
views: [itemManager.trashSmartView],
|
||||
},
|
||||
this.itemManager.collection.all(ContentType.TYPES.Note),
|
||||
this.itemManager.collection,
|
||||
itemManager.collection.all(ContentType.TYPES.Note),
|
||||
itemManager.collection,
|
||||
).length,
|
||||
).to.equal(1)
|
||||
|
||||
expect(
|
||||
notesAndFilesMatchingOptions(
|
||||
{
|
||||
views: [this.itemManager.archivedSmartView],
|
||||
views: [itemManager.archivedSmartView],
|
||||
},
|
||||
this.itemManager.collection.all(ContentType.TYPES.Note),
|
||||
this.itemManager.collection,
|
||||
itemManager.collection.all(ContentType.TYPES.Note),
|
||||
itemManager.collection,
|
||||
).length,
|
||||
).to.equal(1)
|
||||
})
|
||||
@@ -378,87 +354,87 @@ describe('note display criteria', function () {
|
||||
|
||||
describe('includeTrash', function () {
|
||||
it('normal note', async function () {
|
||||
await this.createNote()
|
||||
await createNote()
|
||||
|
||||
expect(
|
||||
notesAndFilesMatchingOptions(
|
||||
{
|
||||
views: [this.itemManager.allNotesSmartView],
|
||||
views: [itemManager.allNotesSmartView],
|
||||
includeTrashed: true,
|
||||
},
|
||||
this.itemManager.collection.all(ContentType.TYPES.Note),
|
||||
this.itemManager.collection,
|
||||
itemManager.collection.all(ContentType.TYPES.Note),
|
||||
itemManager.collection,
|
||||
).length,
|
||||
).to.equal(1)
|
||||
|
||||
expect(
|
||||
notesAndFilesMatchingOptions(
|
||||
{
|
||||
views: [this.itemManager.trashSmartView],
|
||||
views: [itemManager.trashSmartView],
|
||||
includeTrashed: true,
|
||||
},
|
||||
this.itemManager.collection.all(ContentType.TYPES.Note),
|
||||
this.itemManager.collection,
|
||||
itemManager.collection.all(ContentType.TYPES.Note),
|
||||
itemManager.collection,
|
||||
).length,
|
||||
).to.equal(0)
|
||||
})
|
||||
|
||||
it('trashed note', async function () {
|
||||
const normal = await this.createNote()
|
||||
const normal = await createNote()
|
||||
|
||||
await this.mutator.changeItem(normal, (mutator) => {
|
||||
await mutator.changeItem(normal, (mutator) => {
|
||||
mutator.trashed = true
|
||||
})
|
||||
|
||||
expect(
|
||||
notesAndFilesMatchingOptions(
|
||||
{
|
||||
views: [this.itemManager.allNotesSmartView],
|
||||
views: [itemManager.allNotesSmartView],
|
||||
includeTrashed: false,
|
||||
},
|
||||
this.itemManager.collection.all(ContentType.TYPES.Note),
|
||||
this.itemManager.collection,
|
||||
itemManager.collection.all(ContentType.TYPES.Note),
|
||||
itemManager.collection,
|
||||
).length,
|
||||
).to.equal(0)
|
||||
|
||||
expect(
|
||||
notesAndFilesMatchingOptions(
|
||||
{
|
||||
views: [this.itemManager.allNotesSmartView],
|
||||
views: [itemManager.allNotesSmartView],
|
||||
includeTrashed: true,
|
||||
},
|
||||
this.itemManager.collection.all(ContentType.TYPES.Note),
|
||||
this.itemManager.collection,
|
||||
itemManager.collection.all(ContentType.TYPES.Note),
|
||||
itemManager.collection,
|
||||
).length,
|
||||
).to.equal(1)
|
||||
|
||||
expect(
|
||||
notesAndFilesMatchingOptions(
|
||||
{
|
||||
views: [this.itemManager.trashSmartView],
|
||||
views: [itemManager.trashSmartView],
|
||||
includeTrashed: true,
|
||||
},
|
||||
this.itemManager.collection.all(ContentType.TYPES.Note),
|
||||
this.itemManager.collection,
|
||||
itemManager.collection.all(ContentType.TYPES.Note),
|
||||
itemManager.collection,
|
||||
).length,
|
||||
).to.equal(1)
|
||||
|
||||
expect(
|
||||
notesAndFilesMatchingOptions(
|
||||
{
|
||||
views: [this.itemManager.archivedSmartView],
|
||||
views: [itemManager.archivedSmartView],
|
||||
includeTrashed: true,
|
||||
},
|
||||
this.itemManager.collection.all(ContentType.TYPES.Note),
|
||||
this.itemManager.collection,
|
||||
itemManager.collection.all(ContentType.TYPES.Note),
|
||||
itemManager.collection,
|
||||
).length,
|
||||
).to.equal(0)
|
||||
})
|
||||
|
||||
it('archived + trashed note', async function () {
|
||||
const normal = await this.createNote()
|
||||
const normal = await createNote()
|
||||
|
||||
await this.mutator.changeItem(normal, (mutator) => {
|
||||
await mutator.changeItem(normal, (mutator) => {
|
||||
mutator.trashed = true
|
||||
mutator.archived = true
|
||||
})
|
||||
@@ -466,30 +442,30 @@ describe('note display criteria', function () {
|
||||
expect(
|
||||
notesAndFilesMatchingOptions(
|
||||
{
|
||||
views: [this.itemManager.allNotesSmartView],
|
||||
views: [itemManager.allNotesSmartView],
|
||||
},
|
||||
this.itemManager.collection.all(ContentType.TYPES.Note),
|
||||
this.itemManager.collection,
|
||||
itemManager.collection.all(ContentType.TYPES.Note),
|
||||
itemManager.collection,
|
||||
).length,
|
||||
).to.equal(1)
|
||||
|
||||
expect(
|
||||
notesAndFilesMatchingOptions(
|
||||
{
|
||||
views: [this.itemManager.trashSmartView],
|
||||
views: [itemManager.trashSmartView],
|
||||
},
|
||||
this.itemManager.collection.all(ContentType.TYPES.Note),
|
||||
this.itemManager.collection,
|
||||
itemManager.collection.all(ContentType.TYPES.Note),
|
||||
itemManager.collection,
|
||||
).length,
|
||||
).to.equal(1)
|
||||
|
||||
expect(
|
||||
notesAndFilesMatchingOptions(
|
||||
{
|
||||
views: [this.itemManager.archivedSmartView],
|
||||
views: [itemManager.archivedSmartView],
|
||||
},
|
||||
this.itemManager.collection.all(ContentType.TYPES.Note),
|
||||
this.itemManager.collection,
|
||||
itemManager.collection.all(ContentType.TYPES.Note),
|
||||
itemManager.collection,
|
||||
).length,
|
||||
).to.equal(1)
|
||||
})
|
||||
@@ -497,85 +473,85 @@ describe('note display criteria', function () {
|
||||
|
||||
describe('includeArchived', function () {
|
||||
it('normal note', async function () {
|
||||
await this.createNote()
|
||||
await createNote()
|
||||
|
||||
expect(
|
||||
notesAndFilesMatchingOptions(
|
||||
{
|
||||
views: [this.itemManager.allNotesSmartView],
|
||||
views: [itemManager.allNotesSmartView],
|
||||
includeArchived: true,
|
||||
},
|
||||
this.itemManager.collection.all(ContentType.TYPES.Note),
|
||||
this.itemManager.collection,
|
||||
itemManager.collection.all(ContentType.TYPES.Note),
|
||||
itemManager.collection,
|
||||
).length,
|
||||
).to.equal(1)
|
||||
|
||||
expect(
|
||||
notesAndFilesMatchingOptions(
|
||||
{
|
||||
views: [this.itemManager.trashSmartView],
|
||||
views: [itemManager.trashSmartView],
|
||||
includeArchived: true,
|
||||
},
|
||||
this.itemManager.collection.all(ContentType.TYPES.Note),
|
||||
this.itemManager.collection,
|
||||
itemManager.collection.all(ContentType.TYPES.Note),
|
||||
itemManager.collection,
|
||||
).length,
|
||||
).to.equal(0)
|
||||
})
|
||||
|
||||
it('archived note', async function () {
|
||||
const normal = await this.createNote()
|
||||
await this.mutator.changeItem(normal, (mutator) => {
|
||||
const normal = await createNote()
|
||||
await mutator.changeItem(normal, (mutator) => {
|
||||
mutator.archived = true
|
||||
})
|
||||
|
||||
expect(
|
||||
notesAndFilesMatchingOptions(
|
||||
{
|
||||
views: [this.itemManager.allNotesSmartView],
|
||||
views: [itemManager.allNotesSmartView],
|
||||
includeArchived: false,
|
||||
},
|
||||
this.itemManager.collection.all(ContentType.TYPES.Note),
|
||||
this.itemManager.collection,
|
||||
itemManager.collection.all(ContentType.TYPES.Note),
|
||||
itemManager.collection,
|
||||
).length,
|
||||
).to.equal(0)
|
||||
|
||||
expect(
|
||||
notesAndFilesMatchingOptions(
|
||||
{
|
||||
views: [this.itemManager.allNotesSmartView],
|
||||
views: [itemManager.allNotesSmartView],
|
||||
includeArchived: true,
|
||||
},
|
||||
this.itemManager.collection.all(ContentType.TYPES.Note),
|
||||
this.itemManager.collection,
|
||||
itemManager.collection.all(ContentType.TYPES.Note),
|
||||
itemManager.collection,
|
||||
).length,
|
||||
).to.equal(1)
|
||||
|
||||
expect(
|
||||
notesAndFilesMatchingOptions(
|
||||
{
|
||||
views: [this.itemManager.trashSmartView],
|
||||
views: [itemManager.trashSmartView],
|
||||
includeArchived: true,
|
||||
},
|
||||
this.itemManager.collection.all(ContentType.TYPES.Note),
|
||||
this.itemManager.collection,
|
||||
itemManager.collection.all(ContentType.TYPES.Note),
|
||||
itemManager.collection,
|
||||
).length,
|
||||
).to.equal(0)
|
||||
|
||||
expect(
|
||||
notesAndFilesMatchingOptions(
|
||||
{
|
||||
views: [this.itemManager.archivedSmartView],
|
||||
views: [itemManager.archivedSmartView],
|
||||
includeArchived: false,
|
||||
},
|
||||
this.itemManager.collection.all(ContentType.TYPES.Note),
|
||||
this.itemManager.collection,
|
||||
itemManager.collection.all(ContentType.TYPES.Note),
|
||||
itemManager.collection,
|
||||
).length,
|
||||
).to.equal(1)
|
||||
})
|
||||
|
||||
it('archived + trashed note', async function () {
|
||||
const normal = await this.createNote()
|
||||
await this.mutator.changeItem(normal, (mutator) => {
|
||||
const normal = await createNote()
|
||||
await mutator.changeItem(normal, (mutator) => {
|
||||
mutator.trashed = true
|
||||
mutator.archived = true
|
||||
})
|
||||
@@ -583,33 +559,33 @@ describe('note display criteria', function () {
|
||||
expect(
|
||||
notesAndFilesMatchingOptions(
|
||||
{
|
||||
views: [this.itemManager.allNotesSmartView],
|
||||
views: [itemManager.allNotesSmartView],
|
||||
includeArchived: true,
|
||||
},
|
||||
this.itemManager.collection.all(ContentType.TYPES.Note),
|
||||
this.itemManager.collection,
|
||||
itemManager.collection.all(ContentType.TYPES.Note),
|
||||
itemManager.collection,
|
||||
).length,
|
||||
).to.equal(1)
|
||||
|
||||
expect(
|
||||
notesAndFilesMatchingOptions(
|
||||
{
|
||||
views: [this.itemManager.trashSmartView],
|
||||
views: [itemManager.trashSmartView],
|
||||
includeArchived: true,
|
||||
},
|
||||
this.itemManager.collection.all(ContentType.TYPES.Note),
|
||||
this.itemManager.collection,
|
||||
itemManager.collection.all(ContentType.TYPES.Note),
|
||||
itemManager.collection,
|
||||
).length,
|
||||
).to.equal(1)
|
||||
|
||||
expect(
|
||||
notesAndFilesMatchingOptions(
|
||||
{
|
||||
views: [this.itemManager.archivedSmartView],
|
||||
views: [itemManager.archivedSmartView],
|
||||
includeArchived: true,
|
||||
},
|
||||
this.itemManager.collection.all(ContentType.TYPES.Note),
|
||||
this.itemManager.collection,
|
||||
itemManager.collection.all(ContentType.TYPES.Note),
|
||||
itemManager.collection,
|
||||
).length,
|
||||
).to.equal(1)
|
||||
})
|
||||
@@ -617,7 +593,7 @@ describe('note display criteria', function () {
|
||||
|
||||
describe.skip('multiple tags', function () {
|
||||
it('normal note', async function () {
|
||||
await this.createNote()
|
||||
await createNote()
|
||||
/**
|
||||
* This test presently fails because the compound predicate created
|
||||
* when using multiple views is an AND predicate instead of OR
|
||||
@@ -625,82 +601,78 @@ describe('note display criteria', function () {
|
||||
expect(
|
||||
notesAndFilesMatchingOptions(
|
||||
{
|
||||
views: [
|
||||
this.itemManager.allNotesSmartView,
|
||||
this.itemManager.archivedSmartView,
|
||||
this.itemManager.trashSmartView,
|
||||
],
|
||||
views: [itemManager.allNotesSmartView, itemManager.archivedSmartView, itemManager.trashSmartView],
|
||||
},
|
||||
this.itemManager.collection.all(ContentType.TYPES.Note),
|
||||
this.itemManager.collection,
|
||||
itemManager.collection.all(ContentType.TYPES.Note),
|
||||
itemManager.collection,
|
||||
).length,
|
||||
).to.equal(1)
|
||||
|
||||
expect(
|
||||
notesAndFilesMatchingOptions(
|
||||
{
|
||||
views: [this.itemManager.trashSmartView],
|
||||
views: [itemManager.trashSmartView],
|
||||
},
|
||||
this.itemManager.collection.all(ContentType.TYPES.Note),
|
||||
this.itemManager.collection,
|
||||
itemManager.collection.all(ContentType.TYPES.Note),
|
||||
itemManager.collection,
|
||||
).length,
|
||||
).to.equal(0)
|
||||
})
|
||||
|
||||
it('archived note', async function () {
|
||||
const normal = await this.createNote()
|
||||
await this.mutator.changeItem(normal, (mutator) => {
|
||||
const normal = await createNote()
|
||||
await mutator.changeItem(normal, (mutator) => {
|
||||
mutator.archived = true
|
||||
})
|
||||
|
||||
expect(
|
||||
notesAndFilesMatchingOptions(
|
||||
{
|
||||
views: [this.itemManager.allNotesSmartView],
|
||||
views: [itemManager.allNotesSmartView],
|
||||
includeArchived: false,
|
||||
},
|
||||
this.itemManager.collection.all(ContentType.TYPES.Note),
|
||||
this.itemManager.collection,
|
||||
itemManager.collection.all(ContentType.TYPES.Note),
|
||||
itemManager.collection,
|
||||
).length,
|
||||
).to.equal(0)
|
||||
|
||||
expect(
|
||||
notesAndFilesMatchingOptions(
|
||||
{
|
||||
views: [this.itemManager.allNotesSmartView],
|
||||
views: [itemManager.allNotesSmartView],
|
||||
includeArchived: true,
|
||||
},
|
||||
this.itemManager.collection.all(ContentType.TYPES.Note),
|
||||
this.itemManager.collection,
|
||||
itemManager.collection.all(ContentType.TYPES.Note),
|
||||
itemManager.collection,
|
||||
).length,
|
||||
).to.equal(1)
|
||||
|
||||
expect(
|
||||
notesAndFilesMatchingOptions(
|
||||
{
|
||||
views: [this.itemManager.trashSmartView],
|
||||
views: [itemManager.trashSmartView],
|
||||
includeArchived: true,
|
||||
},
|
||||
this.itemManager.collection.all(ContentType.TYPES.Note),
|
||||
this.itemManager.collection,
|
||||
itemManager.collection.all(ContentType.TYPES.Note),
|
||||
itemManager.collection,
|
||||
).length,
|
||||
).to.equal(0)
|
||||
|
||||
expect(
|
||||
notesAndFilesMatchingOptions(
|
||||
{
|
||||
views: [this.itemManager.archivedSmartView],
|
||||
views: [itemManager.archivedSmartView],
|
||||
includeArchived: false,
|
||||
},
|
||||
this.itemManager.collection.all(ContentType.TYPES.Note),
|
||||
this.itemManager.collection,
|
||||
itemManager.collection.all(ContentType.TYPES.Note),
|
||||
itemManager.collection,
|
||||
).length,
|
||||
).to.equal(1)
|
||||
})
|
||||
|
||||
it('archived + trashed note', async function () {
|
||||
const normal = await this.createNote()
|
||||
await this.mutator.changeItem(normal, (mutator) => {
|
||||
const normal = await createNote()
|
||||
await mutator.changeItem(normal, (mutator) => {
|
||||
mutator.trashed = true
|
||||
mutator.archived = true
|
||||
})
|
||||
@@ -708,33 +680,33 @@ describe('note display criteria', function () {
|
||||
expect(
|
||||
notesAndFilesMatchingOptions(
|
||||
{
|
||||
views: [this.itemManager.allNotesSmartView],
|
||||
views: [itemManager.allNotesSmartView],
|
||||
includeArchived: true,
|
||||
},
|
||||
this.itemManager.collection.all(ContentType.TYPES.Note),
|
||||
this.itemManager.collection,
|
||||
itemManager.collection.all(ContentType.TYPES.Note),
|
||||
itemManager.collection,
|
||||
).length,
|
||||
).to.equal(0)
|
||||
|
||||
expect(
|
||||
notesAndFilesMatchingOptions(
|
||||
{
|
||||
views: [this.itemManager.trashSmartView],
|
||||
views: [itemManager.trashSmartView],
|
||||
includeArchived: true,
|
||||
},
|
||||
this.itemManager.collection.all(ContentType.TYPES.Note),
|
||||
this.itemManager.collection,
|
||||
itemManager.collection.all(ContentType.TYPES.Note),
|
||||
itemManager.collection,
|
||||
).length,
|
||||
).to.equal(1)
|
||||
|
||||
expect(
|
||||
notesAndFilesMatchingOptions(
|
||||
{
|
||||
views: [this.itemManager.archivedSmartView],
|
||||
views: [itemManager.archivedSmartView],
|
||||
includeArchived: true,
|
||||
},
|
||||
this.itemManager.collection.all(ContentType.TYPES.Note),
|
||||
this.itemManager.collection,
|
||||
itemManager.collection.all(ContentType.TYPES.Note),
|
||||
itemManager.collection,
|
||||
).length,
|
||||
).to.equal(0)
|
||||
})
|
||||
|
||||
@@ -1,32 +1,29 @@
|
||||
/* eslint-disable no-unused-expressions */
|
||||
/* eslint-disable no-undef */
|
||||
chai.use(chaiAsPromised)
|
||||
const expect = chai.expect
|
||||
import * as Factory from './lib/factory.js'
|
||||
|
||||
describe('payload', () => {
|
||||
beforeEach(async function () {
|
||||
this.createBarePayload = () => {
|
||||
return new DecryptedPayload({
|
||||
uuid: '123',
|
||||
content_type: ContentType.TYPES.Note,
|
||||
content: {
|
||||
title: 'hello',
|
||||
},
|
||||
})
|
||||
}
|
||||
chai.use(chaiAsPromised)
|
||||
const expect = chai.expect
|
||||
|
||||
this.createEncryptedPayload = () => {
|
||||
return new EncryptedPayload({
|
||||
uuid: '123',
|
||||
content_type: ContentType.TYPES.Note,
|
||||
content: '004:foo:bar',
|
||||
})
|
||||
}
|
||||
})
|
||||
describe('payload', () => {
|
||||
const createBarePayload = () => {
|
||||
return new DecryptedPayload({
|
||||
uuid: '123',
|
||||
content_type: ContentType.TYPES.Note,
|
||||
content: {
|
||||
title: 'hello',
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
const createEncryptedPayload = () => {
|
||||
return new EncryptedPayload({
|
||||
uuid: '123',
|
||||
content_type: ContentType.TYPES.Note,
|
||||
content: '004:foo:bar',
|
||||
})
|
||||
}
|
||||
|
||||
it('constructor should set expected fields', function () {
|
||||
const payload = this.createBarePayload()
|
||||
const payload = createBarePayload()
|
||||
|
||||
expect(payload.uuid).to.be.ok
|
||||
expect(payload.content_type).to.be.ok
|
||||
@@ -46,25 +43,25 @@ describe('payload', () => {
|
||||
})
|
||||
|
||||
it('created at should default to present', function () {
|
||||
const payload = this.createBarePayload()
|
||||
const payload = createBarePayload()
|
||||
|
||||
expect(payload.created_at - new Date()).to.be.below(1)
|
||||
})
|
||||
|
||||
it('updated at should default to epoch', function () {
|
||||
const payload = this.createBarePayload()
|
||||
const payload = createBarePayload()
|
||||
|
||||
expect(payload.updated_at.getTime()).to.equal(0)
|
||||
})
|
||||
|
||||
it('payload format bare', function () {
|
||||
const payload = this.createBarePayload()
|
||||
const payload = createBarePayload()
|
||||
|
||||
expect(isDecryptedPayload(payload)).to.equal(true)
|
||||
})
|
||||
|
||||
it('payload format encrypted string', function () {
|
||||
const payload = this.createEncryptedPayload()
|
||||
const payload = createEncryptedPayload()
|
||||
|
||||
expect(isEncryptedPayload(payload)).to.equal(true)
|
||||
})
|
||||
@@ -92,13 +89,13 @@ describe('payload', () => {
|
||||
})
|
||||
|
||||
it('payload version 004', function () {
|
||||
const payload = this.createEncryptedPayload()
|
||||
const payload = createEncryptedPayload()
|
||||
|
||||
expect(payload.version).to.equal('004')
|
||||
})
|
||||
|
||||
it('merged with absent content', function () {
|
||||
const payload = this.createBarePayload()
|
||||
const payload = createBarePayload()
|
||||
const merged = payload.copy({
|
||||
uuid: '123',
|
||||
content_type: ContentType.TYPES.Note,
|
||||
@@ -125,7 +122,7 @@ describe('payload', () => {
|
||||
})
|
||||
|
||||
it('should be immutable', async function () {
|
||||
const payload = this.createBarePayload()
|
||||
const payload = createBarePayload()
|
||||
|
||||
await Factory.sleep(0.1)
|
||||
|
||||
|
||||
@@ -1,31 +1,33 @@
|
||||
/* eslint-disable no-unused-expressions */
|
||||
/* eslint-disable no-undef */
|
||||
import * as Factory from './lib/factory.js'
|
||||
import { createRelatedNoteTagPairPayload } from './lib/Items.js'
|
||||
|
||||
chai.use(chaiAsPromised)
|
||||
const expect = chai.expect
|
||||
|
||||
describe('payload encryption', function () {
|
||||
let application
|
||||
|
||||
beforeEach(async function () {
|
||||
this.timeout(Factory.TenSecondTimeout)
|
||||
localStorage.clear()
|
||||
this.application = await Factory.createInitAppWithFakeCrypto()
|
||||
application = await Factory.createInitAppWithFakeCrypto()
|
||||
})
|
||||
|
||||
afterEach(async function () {
|
||||
await Factory.safeDeinit(this.application)
|
||||
await Factory.safeDeinit(application)
|
||||
localStorage.clear()
|
||||
application = undefined
|
||||
})
|
||||
|
||||
it('creating payload from item should create copy not by reference', async function () {
|
||||
const item = await Factory.createMappedNote(this.application)
|
||||
const item = await Factory.createMappedNote(application)
|
||||
const payload = new DecryptedPayload(item.payload.ejected())
|
||||
expect(item.content === payload.content).to.equal(false)
|
||||
expect(item.content.references === payload.content.references).to.equal(false)
|
||||
})
|
||||
|
||||
it('creating payload from item should preserve appData', async function () {
|
||||
const item = await Factory.createMappedNote(this.application)
|
||||
const item = await Factory.createMappedNote(application)
|
||||
const payload = new DecryptedPayload(item.payload.ejected())
|
||||
expect(item.content.appData).to.be.ok
|
||||
expect(JSON.stringify(item.content)).to.equal(JSON.stringify(payload.content))
|
||||
@@ -40,7 +42,7 @@ describe('payload encryption', function () {
|
||||
lastSyncBegan: new Date(),
|
||||
})
|
||||
|
||||
const encryptedPayload = await this.application.encryption.encryptSplitSingle({
|
||||
const encryptedPayload = await application.encryption.encryptSplitSingle({
|
||||
usesItemsKeyWithKeyLookup: {
|
||||
items: [notePayload],
|
||||
},
|
||||
@@ -85,7 +87,7 @@ describe('payload encryption', function () {
|
||||
})
|
||||
|
||||
it('copying payload with override content should override completely', async function () {
|
||||
const item = await Factory.createMappedNote(this.application)
|
||||
const item = await Factory.createMappedNote(application)
|
||||
const payload = new DecryptedPayload(item.payload.ejected())
|
||||
const mutated = new DecryptedPayload({
|
||||
...payload,
|
||||
@@ -114,7 +116,7 @@ describe('payload encryption', function () {
|
||||
it('returns valid encrypted params for syncing', async function () {
|
||||
const payload = Factory.createNotePayload()
|
||||
const encryptedPayload = CreateEncryptedServerSyncPushPayload(
|
||||
await this.application.encryption.encryptSplitSingle({
|
||||
await application.encryption.encryptSplitSingle({
|
||||
usesItemsKeyWithKeyLookup: {
|
||||
items: [payload],
|
||||
},
|
||||
@@ -126,7 +128,7 @@ describe('payload encryption', function () {
|
||||
expect(encryptedPayload.content_type).to.be.ok
|
||||
expect(encryptedPayload.created_at).to.be.ok
|
||||
expect(encryptedPayload.content).to.satisfy((string) => {
|
||||
return string.startsWith(this.application.encryption.getLatestVersion())
|
||||
return string.startsWith(application.encryption.getLatestVersion())
|
||||
})
|
||||
}).timeout(5000)
|
||||
|
||||
@@ -134,7 +136,7 @@ describe('payload encryption', function () {
|
||||
const payload = Factory.createNotePayload()
|
||||
|
||||
const encryptedPayload = CreateEncryptedLocalStorageContextPayload(
|
||||
await this.application.encryption.encryptSplitSingle({
|
||||
await application.encryption.encryptSplitSingle({
|
||||
usesItemsKeyWithKeyLookup: {
|
||||
items: [payload],
|
||||
},
|
||||
@@ -150,14 +152,14 @@ describe('payload encryption', function () {
|
||||
expect(encryptedPayload.deleted).to.not.be.ok
|
||||
expect(encryptedPayload.errorDecrypting).to.not.be.ok
|
||||
expect(encryptedPayload.content).to.satisfy((string) => {
|
||||
return string.startsWith(this.application.encryption.getLatestVersion())
|
||||
return string.startsWith(application.encryption.getLatestVersion())
|
||||
})
|
||||
})
|
||||
|
||||
it('omits deleted for export file', async function () {
|
||||
const payload = Factory.createNotePayload()
|
||||
const encryptedPayload = CreateEncryptedBackupFileContextPayload(
|
||||
await this.application.encryption.encryptSplitSingle({
|
||||
await application.encryption.encryptSplitSingle({
|
||||
usesItemsKeyWithKeyLookup: {
|
||||
items: [payload],
|
||||
},
|
||||
@@ -170,7 +172,7 @@ describe('payload encryption', function () {
|
||||
expect(encryptedPayload.created_at).to.be.ok
|
||||
expect(encryptedPayload.deleted).to.not.be.ok
|
||||
expect(encryptedPayload.content).to.satisfy((string) => {
|
||||
return string.startsWith(this.application.encryption.getLatestVersion())
|
||||
return string.startsWith(application.encryption.getLatestVersion())
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
/* eslint-disable no-unused-expressions */
|
||||
/* eslint-disable no-undef */
|
||||
import * as Factory from './lib/factory.js'
|
||||
|
||||
chai.use(chaiAsPromised)
|
||||
const expect = chai.expect
|
||||
|
||||
describe('payload manager', () => {
|
||||
let payloadManager
|
||||
let createNotePayload
|
||||
|
||||
beforeEach(async function () {
|
||||
const logger = new Logger('test')
|
||||
this.payloadManager = new PayloadManager(logger)
|
||||
this.createNotePayload = async () => {
|
||||
payloadManager = new PayloadManager(logger)
|
||||
|
||||
createNotePayload = async () => {
|
||||
return new DecryptedPayload({
|
||||
uuid: Factory.generateUuidish(),
|
||||
content_type: ContentType.TYPES.Note,
|
||||
@@ -21,15 +24,15 @@ describe('payload manager', () => {
|
||||
})
|
||||
|
||||
it('emit payload should create local record', async function () {
|
||||
const payload = await this.createNotePayload()
|
||||
await this.payloadManager.emitPayload(payload)
|
||||
const payload = await createNotePayload()
|
||||
await payloadManager.emitPayload(payload)
|
||||
|
||||
expect(this.payloadManager.collection.find(payload.uuid)).to.be.ok
|
||||
expect(payloadManager.collection.find(payload.uuid)).to.be.ok
|
||||
})
|
||||
|
||||
it('merge payloads onto master', async function () {
|
||||
const payload = await this.createNotePayload()
|
||||
await this.payloadManager.emitPayload(payload)
|
||||
const payload = await createNotePayload()
|
||||
await payloadManager.emitPayload(payload)
|
||||
|
||||
const newTitle = `${Math.random()}`
|
||||
const changedPayload = payload.copy({
|
||||
@@ -38,19 +41,19 @@ describe('payload manager', () => {
|
||||
title: newTitle,
|
||||
},
|
||||
})
|
||||
const { changed, inserted } = await this.payloadManager.applyPayloads([changedPayload])
|
||||
const { changed, inserted } = await payloadManager.applyPayloads([changedPayload])
|
||||
expect(changed.length).to.equal(1)
|
||||
expect(inserted.length).to.equal(0)
|
||||
expect(this.payloadManager.collection.find(payload.uuid).content.title).to.equal(newTitle)
|
||||
expect(payloadManager.collection.find(payload.uuid).content.title).to.equal(newTitle)
|
||||
})
|
||||
|
||||
it('insertion observer', async function () {
|
||||
const observations = []
|
||||
this.payloadManager.addObserver(ContentType.TYPES.Any, ({ inserted }) => {
|
||||
payloadManager.addObserver(ContentType.TYPES.Any, ({ inserted }) => {
|
||||
observations.push({ inserted })
|
||||
})
|
||||
const payload = await this.createNotePayload()
|
||||
await this.payloadManager.emitPayload(payload)
|
||||
const payload = await createNotePayload()
|
||||
await payloadManager.emitPayload(payload)
|
||||
|
||||
expect(observations.length).equal(1)
|
||||
expect(observations[0].inserted[0]).equal(payload)
|
||||
@@ -58,14 +61,14 @@ describe('payload manager', () => {
|
||||
|
||||
it('change observer', async function () {
|
||||
const observations = []
|
||||
this.payloadManager.addObserver(ContentType.TYPES.Any, ({ changed }) => {
|
||||
payloadManager.addObserver(ContentType.TYPES.Any, ({ changed }) => {
|
||||
if (changed.length > 0) {
|
||||
observations.push({ changed })
|
||||
}
|
||||
})
|
||||
const payload = await this.createNotePayload()
|
||||
await this.payloadManager.emitPayload(payload)
|
||||
await this.payloadManager.emitPayload(
|
||||
const payload = await createNotePayload()
|
||||
await payloadManager.emitPayload(payload)
|
||||
await payloadManager.emitPayload(
|
||||
payload.copy({
|
||||
content: {
|
||||
...payload.content,
|
||||
@@ -79,12 +82,12 @@ describe('payload manager', () => {
|
||||
})
|
||||
|
||||
it('reset state', async function () {
|
||||
this.payloadManager.addObserver(ContentType.TYPES.Any, ({}) => {})
|
||||
const payload = await this.createNotePayload()
|
||||
await this.payloadManager.emitPayload(payload)
|
||||
await this.payloadManager.resetState()
|
||||
payloadManager.addObserver(ContentType.TYPES.Any, ({}) => {})
|
||||
const payload = await createNotePayload()
|
||||
await payloadManager.emitPayload(payload)
|
||||
await payloadManager.resetState()
|
||||
|
||||
expect(this.payloadManager.collection.all().length).to.equal(0)
|
||||
expect(this.payloadManager.changeObservers.length).equal(1)
|
||||
expect(payloadManager.collection.all().length).to.equal(0)
|
||||
expect(payloadManager.changeObservers.length).equal(1)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,111 +1,116 @@
|
||||
/* eslint-disable no-unused-expressions */
|
||||
/* eslint-disable no-undef */
|
||||
import * as Factory from './lib/factory.js'
|
||||
|
||||
chai.use(chaiAsPromised)
|
||||
const expect = chai.expect
|
||||
|
||||
describe('preferences', function () {
|
||||
let application
|
||||
let email
|
||||
let password
|
||||
let context
|
||||
|
||||
beforeEach(async function () {
|
||||
localStorage.clear()
|
||||
|
||||
this.context = await Factory.createAppContext()
|
||||
await this.context.launch()
|
||||
this.application = this.context.application
|
||||
context = await Factory.createAppContext()
|
||||
await context.launch()
|
||||
application = context.application
|
||||
|
||||
this.email = UuidGenerator.GenerateUuid()
|
||||
this.password = UuidGenerator.GenerateUuid()
|
||||
email = UuidGenerator.GenerateUuid()
|
||||
password = UuidGenerator.GenerateUuid()
|
||||
})
|
||||
|
||||
afterEach(async function () {
|
||||
await Factory.safeDeinit(this.application)
|
||||
await Factory.safeDeinit(application)
|
||||
localStorage.clear()
|
||||
context = undefined
|
||||
})
|
||||
|
||||
function register() {
|
||||
return Factory.registerUserToApplication({
|
||||
application: this.application,
|
||||
email: this.email,
|
||||
password: this.password,
|
||||
application: application,
|
||||
email: email,
|
||||
password: password,
|
||||
})
|
||||
}
|
||||
|
||||
it('sets preference', async function () {
|
||||
await this.application.setPreference('editorLeft', 300)
|
||||
expect(this.application.getPreference('editorLeft')).to.equal(300)
|
||||
await application.setPreference('editorLeft', 300)
|
||||
expect(application.getPreference('editorLeft')).to.equal(300)
|
||||
})
|
||||
|
||||
it('saves preference', async function () {
|
||||
await register.call(this)
|
||||
await this.application.setPreference('editorLeft', 300)
|
||||
await this.application.sync.sync()
|
||||
this.application = await Factory.signOutAndBackIn(this.application, this.email, this.password)
|
||||
const editorLeft = this.application.getPreference('editorLeft')
|
||||
await application.setPreference('editorLeft', 300)
|
||||
await application.sync.sync()
|
||||
application = await Factory.signOutAndBackIn(application, email, password)
|
||||
const editorLeft = application.getPreference('editorLeft')
|
||||
expect(editorLeft).to.equal(300)
|
||||
}).timeout(10000)
|
||||
|
||||
it('clears preferences on signout', async function () {
|
||||
await register.call(this)
|
||||
await this.application.setPreference('editorLeft', 300)
|
||||
await this.application.sync.sync()
|
||||
this.application = await Factory.signOutApplicationAndReturnNew(this.application)
|
||||
expect(this.application.getPreference('editorLeft')).to.equal(undefined)
|
||||
await application.setPreference('editorLeft', 300)
|
||||
await application.sync.sync()
|
||||
application = await Factory.signOutApplicationAndReturnNew(application)
|
||||
expect(application.getPreference('editorLeft')).to.equal(undefined)
|
||||
})
|
||||
|
||||
it('returns default value for non-existent preference', async function () {
|
||||
await register.call(this)
|
||||
const editorLeft = this.application.getPreference('editorLeft', 100)
|
||||
const editorLeft = application.getPreference('editorLeft', 100)
|
||||
expect(editorLeft).to.equal(100)
|
||||
})
|
||||
|
||||
it('emits an event when preferences change', async function () {
|
||||
const promise = new Promise((resolve) => {
|
||||
this.application.addEventObserver(() => {
|
||||
application.addEventObserver(() => {
|
||||
resolve()
|
||||
}, ApplicationEvent.PreferencesChanged)
|
||||
})
|
||||
|
||||
await this.application.setPreference('editorLeft', 300)
|
||||
await application.setPreference('editorLeft', 300)
|
||||
await promise
|
||||
expect(promise).to.be.fulfilled
|
||||
})
|
||||
|
||||
it('discards existing preferences when signing in', async function () {
|
||||
await register.call(this)
|
||||
await this.application.setPreference('editorLeft', 300)
|
||||
await this.application.sync.sync()
|
||||
await application.setPreference('editorLeft', 300)
|
||||
await application.sync.sync()
|
||||
|
||||
this.application = await this.context.signout()
|
||||
application = await context.signout()
|
||||
|
||||
await this.application.setPreference('editorLeft', 200)
|
||||
await this.application.signIn(this.email, this.password)
|
||||
await application.setPreference('editorLeft', 200)
|
||||
await application.signIn(email, password)
|
||||
|
||||
const promise = this.context.awaitUserPrefsSingletonResolution()
|
||||
await this.application.sync.sync({ awaitAll: true })
|
||||
const promise = context.awaitUserPrefsSingletonResolution()
|
||||
await application.sync.sync({ awaitAll: true })
|
||||
await promise
|
||||
|
||||
const editorLeft = this.application.getPreference('editorLeft')
|
||||
const editorLeft = application.getPreference('editorLeft')
|
||||
expect(editorLeft).to.equal(300)
|
||||
})
|
||||
|
||||
it('reads stored preferences on start without waiting for syncing to complete', async function () {
|
||||
const prefKey = 'editorLeft'
|
||||
const prefValue = 300
|
||||
const identifier = this.application.identifier
|
||||
const identifier = application.identifier
|
||||
|
||||
await register.call(this)
|
||||
await this.application.setPreference(prefKey, prefValue)
|
||||
await this.application.sync.sync()
|
||||
await application.setPreference(prefKey, prefValue)
|
||||
await application.sync.sync()
|
||||
|
||||
await Factory.safeDeinit(this.application)
|
||||
await Factory.safeDeinit(application)
|
||||
|
||||
this.application = Factory.createApplicationWithFakeCrypto(identifier)
|
||||
application = Factory.createApplicationWithFakeCrypto(identifier)
|
||||
const willSyncPromise = new Promise((resolve) => {
|
||||
this.application.addEventObserver(resolve, ApplicationEvent.WillSync)
|
||||
application.addEventObserver(resolve, ApplicationEvent.WillSync)
|
||||
})
|
||||
Factory.initializeApplication(this.application)
|
||||
Factory.initializeApplication(application)
|
||||
await willSyncPromise
|
||||
|
||||
expect(this.application.preferences.preferences).to.exist
|
||||
expect(this.application.getPreference(prefKey)).to.equal(prefValue)
|
||||
expect(application.preferences.preferences).to.exist
|
||||
expect(application.getPreference(prefKey)).to.equal(prefValue)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* eslint-disable no-unused-expressions */
|
||||
/* eslint-disable no-undef */
|
||||
|
||||
import * as Factory from './lib/factory.js'
|
||||
|
||||
chai.use(chaiAsPromised)
|
||||
const expect = chai.expect
|
||||
|
||||
|
||||
@@ -1,61 +1,60 @@
|
||||
/* eslint-disable no-unused-expressions */
|
||||
/* eslint-disable no-undef */
|
||||
import * as Factory from './lib/factory.js'
|
||||
|
||||
chai.use(chaiAsPromised)
|
||||
const expect = chai.expect
|
||||
|
||||
describe('protocol', function () {
|
||||
let application
|
||||
|
||||
beforeEach(async function () {
|
||||
localStorage.clear()
|
||||
this.application = await Factory.createInitAppWithFakeCrypto()
|
||||
this.email = UuidGenerator.GenerateUuid()
|
||||
this.password = UuidGenerator.GenerateUuid()
|
||||
application = await Factory.createInitAppWithFakeCrypto()
|
||||
})
|
||||
|
||||
afterEach(async function () {
|
||||
await Factory.safeDeinit(this.application)
|
||||
this.application = null
|
||||
await Factory.safeDeinit(application)
|
||||
application = null
|
||||
localStorage.clear()
|
||||
})
|
||||
|
||||
it('checks version to make sure its 004', function () {
|
||||
expect(this.application.encryption.getLatestVersion()).to.equal('004')
|
||||
expect(application.encryption.getLatestVersion()).to.equal('004')
|
||||
})
|
||||
|
||||
it('checks supported versions to make sure it includes 001, 002, 003, 004', function () {
|
||||
expect(this.application.encryption.supportedVersions()).to.eql(['001', '002', '003', '004'])
|
||||
expect(application.encryption.supportedVersions()).to.eql(['001', '002', '003', '004'])
|
||||
})
|
||||
|
||||
it('platform derivation support', function () {
|
||||
expect(
|
||||
this.application.encryption.platformSupportsKeyDerivation({
|
||||
application.encryption.platformSupportsKeyDerivation({
|
||||
version: '001',
|
||||
}),
|
||||
).to.equal(true)
|
||||
expect(
|
||||
this.application.encryption.platformSupportsKeyDerivation({
|
||||
application.encryption.platformSupportsKeyDerivation({
|
||||
version: '002',
|
||||
}),
|
||||
).to.equal(true)
|
||||
expect(
|
||||
this.application.encryption.platformSupportsKeyDerivation({
|
||||
application.encryption.platformSupportsKeyDerivation({
|
||||
version: '003',
|
||||
}),
|
||||
).to.equal(true)
|
||||
expect(
|
||||
this.application.encryption.platformSupportsKeyDerivation({
|
||||
application.encryption.platformSupportsKeyDerivation({
|
||||
version: '004',
|
||||
}),
|
||||
).to.equal(true)
|
||||
expect(
|
||||
this.application.encryption.platformSupportsKeyDerivation({
|
||||
application.encryption.platformSupportsKeyDerivation({
|
||||
version: '005',
|
||||
}),
|
||||
).to.equal(true)
|
||||
})
|
||||
|
||||
it('key params versions <= 002 should include pw_cost in portable value', function () {
|
||||
const keyParams002 = this.application.encryption.createKeyParams({
|
||||
const keyParams002 = application.encryption.createKeyParams({
|
||||
version: '002',
|
||||
pw_cost: 5000,
|
||||
})
|
||||
@@ -63,15 +62,15 @@ describe('protocol', function () {
|
||||
})
|
||||
|
||||
it('version comparison of 002 should be older than library version', function () {
|
||||
expect(this.application.encryption.isVersionNewerThanLibraryVersion('002')).to.equal(false)
|
||||
expect(application.encryption.isVersionNewerThanLibraryVersion('002')).to.equal(false)
|
||||
})
|
||||
|
||||
it('version comparison of 005 should be newer than library version', function () {
|
||||
expect(this.application.encryption.isVersionNewerThanLibraryVersion('005')).to.equal(true)
|
||||
expect(application.encryption.isVersionNewerThanLibraryVersion('005')).to.equal(true)
|
||||
})
|
||||
|
||||
it('library version should not be outdated', function () {
|
||||
var currentVersion = this.application.encryption.getLatestVersion()
|
||||
var currentVersion = application.encryption.getLatestVersion()
|
||||
expect(isProtocolVersionExpired(currentVersion)).to.equal(false)
|
||||
})
|
||||
|
||||
@@ -91,7 +90,7 @@ describe('protocol', function () {
|
||||
const payload = Factory.createNotePayload()
|
||||
let error
|
||||
try {
|
||||
await this.application.encryption.decryptSplitSingle({
|
||||
await application.encryption.decryptSplitSingle({
|
||||
usesItemsKeyWithKeyLookup: {
|
||||
items: [payload],
|
||||
},
|
||||
@@ -103,10 +102,10 @@ describe('protocol', function () {
|
||||
})
|
||||
|
||||
it('ejected payload should not have meta fields', async function () {
|
||||
await this.application.addPasscode('123')
|
||||
await application.addPasscode('123')
|
||||
const payload = Factory.createNotePayload()
|
||||
const result = CreateEncryptedServerSyncPushPayload(
|
||||
await this.application.encryption.encryptSplitSingle({
|
||||
await application.encryption.encryptSplitSingle({
|
||||
usesItemsKeyWithKeyLookup: {
|
||||
items: [payload],
|
||||
},
|
||||
@@ -121,7 +120,7 @@ describe('protocol', function () {
|
||||
it('encrypted payload for server should include duplicate_of field', async function () {
|
||||
const payload = Factory.createNotePayload('Test')
|
||||
const encryptedPayload = CreateEncryptedServerSyncPushPayload(
|
||||
await this.application.encryption.encryptSplitSingle({
|
||||
await application.encryption.encryptSplitSingle({
|
||||
usesItemsKeyWithKeyLookup: {
|
||||
items: [payload],
|
||||
},
|
||||
@@ -134,7 +133,7 @@ describe('protocol', function () {
|
||||
it('ejected payload for server should include duplicate_of field', async function () {
|
||||
const payload = Factory.createNotePayload('Test')
|
||||
const encryptedPayload = CreateEncryptedServerSyncPushPayload(
|
||||
await this.application.encryption.encryptSplitSingle({
|
||||
await application.encryption.encryptSplitSingle({
|
||||
usesItemsKeyWithKeyLookup: {
|
||||
items: [payload],
|
||||
},
|
||||
@@ -147,7 +146,7 @@ describe('protocol', function () {
|
||||
it('encrypted payload for storage should include duplicate_of field', async function () {
|
||||
const payload = Factory.createNotePayload('Test')
|
||||
const encryptedPayload = CreateEncryptedLocalStorageContextPayload(
|
||||
await this.application.encryption.encryptSplitSingle({
|
||||
await application.encryption.encryptSplitSingle({
|
||||
usesItemsKeyWithKeyLookup: {
|
||||
items: [payload],
|
||||
},
|
||||
@@ -160,7 +159,7 @@ describe('protocol', function () {
|
||||
it('ejected payload for storage should include duplicate_of field', async function () {
|
||||
const payload = Factory.createNotePayload('Test')
|
||||
const encryptedPayload = CreateEncryptedLocalStorageContextPayload(
|
||||
await this.application.encryption.encryptSplitSingle({
|
||||
await application.encryption.encryptSplitSingle({
|
||||
usesItemsKeyWithKeyLookup: {
|
||||
items: [payload],
|
||||
},
|
||||
@@ -173,7 +172,7 @@ describe('protocol', function () {
|
||||
it('encrypted payload for file should include duplicate_of field', async function () {
|
||||
const payload = Factory.createNotePayload('Test')
|
||||
const encryptedPayload = CreateEncryptedBackupFileContextPayload(
|
||||
await this.application.encryption.encryptSplitSingle({
|
||||
await application.encryption.encryptSplitSingle({
|
||||
usesItemsKeyWithKeyLookup: {
|
||||
items: [payload],
|
||||
},
|
||||
@@ -186,7 +185,7 @@ describe('protocol', function () {
|
||||
it('ejected payload for file should include duplicate_of field', async function () {
|
||||
const payload = Factory.createNotePayload('Test')
|
||||
const encryptedPayload = CreateEncryptedBackupFileContextPayload(
|
||||
await this.application.encryption.encryptSplitSingle({
|
||||
await application.encryption.encryptSplitSingle({
|
||||
usesItemsKeyWithKeyLookup: {
|
||||
items: [payload],
|
||||
},
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
/* eslint-disable no-unused-expressions */
|
||||
/* eslint-disable no-undef */
|
||||
import { BaseItemCounts } from './lib/BaseItemCounts.js'
|
||||
import * as Factory from './lib/factory.js'
|
||||
import WebDeviceInterface from './lib/web_device_interface.js'
|
||||
|
||||
chai.use(chaiAsPromised)
|
||||
const expect = chai.expect
|
||||
|
||||
describe('server session', function () {
|
||||
this.timeout(Factory.TenSecondTimeout)
|
||||
|
||||
let application
|
||||
let email
|
||||
let password
|
||||
let newPassword
|
||||
|
||||
const syncOptions = {
|
||||
checkIntegrity: true,
|
||||
awaitAll: true,
|
||||
@@ -16,16 +19,15 @@ describe('server session', function () {
|
||||
|
||||
beforeEach(async function () {
|
||||
localStorage.clear()
|
||||
this.expectedItemCount = BaseItemCounts.DefaultItems
|
||||
this.application = await Factory.createInitAppWithFakeCrypto()
|
||||
this.email = UuidGenerator.GenerateUuid()
|
||||
this.password = UuidGenerator.GenerateUuid()
|
||||
this.newPassword = Factory.randomString()
|
||||
application = await Factory.createInitAppWithFakeCrypto()
|
||||
email = UuidGenerator.GenerateUuid()
|
||||
password = UuidGenerator.GenerateUuid()
|
||||
newPassword = Factory.randomString()
|
||||
})
|
||||
|
||||
afterEach(async function () {
|
||||
await Factory.safeDeinit(this.application)
|
||||
this.application = null
|
||||
await Factory.safeDeinit(application)
|
||||
application = null
|
||||
localStorage.clear()
|
||||
})
|
||||
|
||||
@@ -47,26 +49,26 @@ describe('server session', function () {
|
||||
|
||||
it('should succeed when a sync request is perfomed with an expired access token', async function () {
|
||||
await Factory.registerUserToApplication({
|
||||
application: this.application,
|
||||
email: this.email,
|
||||
password: this.password,
|
||||
application: application,
|
||||
email: email,
|
||||
password: password,
|
||||
})
|
||||
|
||||
await sleepUntilSessionExpires(this.application)
|
||||
await sleepUntilSessionExpires(application)
|
||||
|
||||
const response = await this.application.legacyApi.sync([])
|
||||
const response = await application.legacyApi.sync([])
|
||||
|
||||
expect(response.status).to.equal(200)
|
||||
}).timeout(Factory.TwentySecondTimeout)
|
||||
|
||||
it('should return the new session in the response when refreshed', async function () {
|
||||
await Factory.registerUserToApplication({
|
||||
application: this.application,
|
||||
email: this.email,
|
||||
password: this.password,
|
||||
application: application,
|
||||
email: email,
|
||||
password: password,
|
||||
})
|
||||
|
||||
const response = await this.application.legacyApi.refreshSession()
|
||||
const response = await application.legacyApi.refreshSession()
|
||||
|
||||
expect(response.status).to.equal(200)
|
||||
expect(response.data.session.access_token).to.be.a('string')
|
||||
@@ -77,22 +79,22 @@ describe('server session', function () {
|
||||
|
||||
it('should be refreshed on any api call if access token is expired', async function () {
|
||||
await Factory.registerUserToApplication({
|
||||
application: this.application,
|
||||
email: this.email,
|
||||
password: this.password,
|
||||
application: application,
|
||||
email: email,
|
||||
password: password,
|
||||
})
|
||||
|
||||
// Saving the current session information for later...
|
||||
const sessionBeforeSync = this.application.legacyApi.getSession()
|
||||
const sessionBeforeSync = application.legacyApi.getSession()
|
||||
|
||||
// Waiting enough time for the access token to expire, before performing a new sync request.
|
||||
await sleepUntilSessionExpires(this.application)
|
||||
await sleepUntilSessionExpires(application)
|
||||
|
||||
// Performing a sync request with an expired access token.
|
||||
await this.application.sync.sync(syncOptions)
|
||||
await application.sync.sync(syncOptions)
|
||||
|
||||
// After the above sync request is completed, we obtain the session information.
|
||||
const sessionAfterSync = this.application.legacyApi.getSession()
|
||||
const sessionAfterSync = application.legacyApi.getSession()
|
||||
|
||||
expect(sessionBeforeSync.accessToken.value).to.not.equal(sessionAfterSync.accessToken.value)
|
||||
expect(sessionBeforeSync.refreshToken.value).to.not.equal(sessionAfterSync.refreshToken.value)
|
||||
@@ -103,59 +105,59 @@ describe('server session', function () {
|
||||
|
||||
it('should not deadlock while renewing session', async function () {
|
||||
await Factory.registerUserToApplication({
|
||||
application: this.application,
|
||||
email: this.email,
|
||||
password: this.password,
|
||||
application: application,
|
||||
email: email,
|
||||
password: password,
|
||||
})
|
||||
|
||||
await sleepUntilSessionExpires(this.application)
|
||||
await sleepUntilSessionExpires(application)
|
||||
|
||||
// Apply a latency simulation so that ` this.inProgressRefreshSessionPromise = this.refreshSession()` does
|
||||
// Apply a latency simulation so that ` inProgressRefreshSessionPromise = refreshSession()` does
|
||||
// not have the chance to complete before it is assigned to the variable. This test came along with a fix
|
||||
// where runHttp does not await a pending refreshSession promise if the request being made is itself a refreshSession request.
|
||||
this.application.http.__latencySimulatorMs = 1000
|
||||
await this.application.sync.sync(syncOptions)
|
||||
application.http.__latencySimulatorMs = 1000
|
||||
await application.sync.sync(syncOptions)
|
||||
|
||||
const sessionAfterSync = this.application.legacyApi.getSession()
|
||||
const sessionAfterSync = application.legacyApi.getSession()
|
||||
|
||||
expect(sessionAfterSync.accessToken.expiresAt).to.be.greaterThan(Date.now())
|
||||
}).timeout(Factory.TwentySecondTimeout)
|
||||
|
||||
it('should succeed when a sync request is perfomed after signing into an ephemeral session', async function () {
|
||||
await Factory.registerUserToApplication({
|
||||
application: this.application,
|
||||
email: this.email,
|
||||
password: this.password,
|
||||
application: application,
|
||||
email: email,
|
||||
password: password,
|
||||
})
|
||||
this.application = await Factory.signOutApplicationAndReturnNew(this.application)
|
||||
application = await Factory.signOutApplicationAndReturnNew(application)
|
||||
|
||||
await this.application.signIn(this.email, this.password, false, true)
|
||||
await application.signIn(email, password, false, true)
|
||||
|
||||
const response = await this.application.legacyApi.sync([])
|
||||
const response = await application.legacyApi.sync([])
|
||||
expect(response.status).to.equal(200)
|
||||
})
|
||||
|
||||
it('should succeed when a sync request is perfomed after registering into an ephemeral session', async function () {
|
||||
await Factory.registerUserToApplication({
|
||||
application: this.application,
|
||||
email: this.email,
|
||||
password: this.password,
|
||||
application: application,
|
||||
email: email,
|
||||
password: password,
|
||||
ephemeral: true,
|
||||
})
|
||||
|
||||
const response = await this.application.legacyApi.sync([])
|
||||
const response = await application.legacyApi.sync([])
|
||||
expect(response.status).to.equal(200)
|
||||
})
|
||||
|
||||
it('should be consistent between storage and apiService', async function () {
|
||||
await Factory.registerUserToApplication({
|
||||
application: this.application,
|
||||
email: this.email,
|
||||
password: this.password,
|
||||
application: application,
|
||||
email: email,
|
||||
password: password,
|
||||
})
|
||||
|
||||
const sessionFromStorage = await getSessionFromStorage(this.application)
|
||||
const sessionFromApiService = this.application.legacyApi.getSession()
|
||||
const sessionFromStorage = await getSessionFromStorage(application)
|
||||
const sessionFromApiService = application.legacyApi.getSession()
|
||||
|
||||
expect(sessionFromStorage.accessToken).to.equal(sessionFromApiService.accessToken.value)
|
||||
expect(sessionFromStorage.refreshToken).to.equal(sessionFromApiService.refreshToken.value)
|
||||
@@ -163,10 +165,10 @@ describe('server session', function () {
|
||||
expect(sessionFromStorage.refreshExpiration).to.equal(sessionFromApiService.refreshToken.expiresAt)
|
||||
expect(sessionFromStorage.readonlyAccess).to.equal(sessionFromApiService.isReadOnly())
|
||||
|
||||
await this.application.legacyApi.refreshSession()
|
||||
await application.legacyApi.refreshSession()
|
||||
|
||||
const updatedSessionFromStorage = await getSessionFromStorage(this.application)
|
||||
const updatedSessionFromApiService = this.application.legacyApi.getSession()
|
||||
const updatedSessionFromStorage = await getSessionFromStorage(application)
|
||||
const updatedSessionFromApiService = application.legacyApi.getSession()
|
||||
|
||||
expect(updatedSessionFromStorage.accessToken).to.equal(updatedSessionFromApiService.accessToken.value)
|
||||
expect(updatedSessionFromStorage.refreshToken).to.equal(updatedSessionFromApiService.refreshToken.value)
|
||||
@@ -177,16 +179,16 @@ describe('server session', function () {
|
||||
|
||||
it('should be performed successfully and terminate session with a valid access token', async function () {
|
||||
await Factory.registerUserToApplication({
|
||||
application: this.application,
|
||||
email: this.email,
|
||||
password: this.password,
|
||||
application: application,
|
||||
email: email,
|
||||
password: password,
|
||||
})
|
||||
|
||||
const signOutResponse = await this.application.legacyApi.signOut()
|
||||
const signOutResponse = await application.legacyApi.signOut()
|
||||
expect(signOutResponse.status).to.equal(204)
|
||||
|
||||
Factory.ignoreChallenges(this.application)
|
||||
const syncResponse = await this.application.legacyApi.sync([])
|
||||
Factory.ignoreChallenges(application)
|
||||
const syncResponse = await application.legacyApi.sync([])
|
||||
expect(syncResponse.status).to.equal(401)
|
||||
expect(syncResponse.data.error.tag).to.equal('invalid-auth')
|
||||
expect(syncResponse.data.error.message).to.equal('Invalid login credentials.')
|
||||
@@ -194,19 +196,19 @@ describe('server session', function () {
|
||||
|
||||
it('sign out request should be performed successfully and terminate session with expired access token', async function () {
|
||||
await Factory.registerUserToApplication({
|
||||
application: this.application,
|
||||
email: this.email,
|
||||
password: this.password,
|
||||
application: application,
|
||||
email: email,
|
||||
password: password,
|
||||
})
|
||||
|
||||
// Waiting enough time for the access token to expire, before performing a sign out request.
|
||||
await sleepUntilSessionExpires(this.application)
|
||||
await sleepUntilSessionExpires(application)
|
||||
|
||||
const signOutResponse = await this.application.legacyApi.signOut()
|
||||
const signOutResponse = await application.legacyApi.signOut()
|
||||
expect(signOutResponse.status).to.equal(204)
|
||||
|
||||
Factory.ignoreChallenges(this.application)
|
||||
const syncResponse = await this.application.legacyApi.sync([])
|
||||
Factory.ignoreChallenges(application)
|
||||
const syncResponse = await application.legacyApi.sync([])
|
||||
expect(syncResponse.status).to.equal(401)
|
||||
expect(syncResponse.data.error.tag).to.equal('invalid-auth')
|
||||
expect(syncResponse.data.error.message).to.equal('Invalid login credentials.')
|
||||
@@ -276,20 +278,20 @@ describe('server session', function () {
|
||||
|
||||
it('change password request should be successful with a valid access token', async function () {
|
||||
await Factory.registerUserToApplication({
|
||||
application: this.application,
|
||||
email: this.email,
|
||||
password: this.password,
|
||||
application: application,
|
||||
email: email,
|
||||
password: password,
|
||||
})
|
||||
|
||||
const changePasswordResponse = await this.application.changePassword(this.password, this.newPassword)
|
||||
const changePasswordResponse = await application.changePassword(password, newPassword)
|
||||
|
||||
expect(changePasswordResponse.error).to.not.be.ok
|
||||
|
||||
this.application = await Factory.signOutApplicationAndReturnNew(this.application)
|
||||
application = await Factory.signOutApplicationAndReturnNew(application)
|
||||
const loginResponse = await Factory.loginToApplication({
|
||||
application: this.application,
|
||||
email: this.email,
|
||||
password: this.newPassword,
|
||||
application: application,
|
||||
email: email,
|
||||
password: newPassword,
|
||||
})
|
||||
|
||||
expect(loginResponse).to.be.ok
|
||||
@@ -298,23 +300,23 @@ describe('server session', function () {
|
||||
|
||||
it('change password request should be successful after the expired access token is refreshed', async function () {
|
||||
await Factory.registerUserToApplication({
|
||||
application: this.application,
|
||||
email: this.email,
|
||||
password: this.password,
|
||||
application: application,
|
||||
email: email,
|
||||
password: password,
|
||||
})
|
||||
|
||||
// Waiting enough time for the access token to expire.
|
||||
await sleepUntilSessionExpires(this.application)
|
||||
await sleepUntilSessionExpires(application)
|
||||
|
||||
const changePasswordResponse = await this.application.changePassword(this.password, this.newPassword)
|
||||
const changePasswordResponse = await application.changePassword(password, newPassword)
|
||||
|
||||
expect(changePasswordResponse.error).to.not.be.ok
|
||||
|
||||
this.application = await Factory.signOutApplicationAndReturnNew(this.application)
|
||||
application = await Factory.signOutApplicationAndReturnNew(application)
|
||||
const loginResponse = await Factory.loginToApplication({
|
||||
application: this.application,
|
||||
email: this.email,
|
||||
password: this.newPassword,
|
||||
application: application,
|
||||
email: email,
|
||||
password: newPassword,
|
||||
})
|
||||
|
||||
expect(loginResponse).to.be.ok
|
||||
@@ -323,39 +325,39 @@ describe('server session', function () {
|
||||
|
||||
it('change password request should fail with an invalid access token', async function () {
|
||||
await Factory.registerUserToApplication({
|
||||
application: this.application,
|
||||
email: this.email,
|
||||
password: this.password,
|
||||
application: application,
|
||||
email: email,
|
||||
password: password,
|
||||
})
|
||||
|
||||
this.application.storage.setValue(StorageKey.Session, {
|
||||
application.storage.setValue(StorageKey.Session, {
|
||||
accessToken: 'this-is-a-fake-token-1234',
|
||||
refreshToken: 'this-is-a-fake-token-1234',
|
||||
accessExpiration: 999999999999999,
|
||||
refreshExpiration: 99999999999999,
|
||||
readonlyAccess: false,
|
||||
})
|
||||
this.application.sessions.initializeFromDisk()
|
||||
application.sessions.initializeFromDisk()
|
||||
|
||||
Factory.ignoreChallenges(this.application)
|
||||
const changePasswordResponse = await this.application.changePassword(this.password, this.newPassword)
|
||||
Factory.ignoreChallenges(application)
|
||||
const changePasswordResponse = await application.changePassword(password, newPassword)
|
||||
expect(changePasswordResponse.error.message).to.equal('Invalid login credentials.')
|
||||
})
|
||||
|
||||
it('change password request should fail with an expired refresh token', async function () {
|
||||
await Factory.registerUserToApplication({
|
||||
application: this.application,
|
||||
email: this.email,
|
||||
password: this.password,
|
||||
application: application,
|
||||
email: email,
|
||||
password: password,
|
||||
})
|
||||
|
||||
this.application.sync.lockSyncing()
|
||||
application.sync.lockSyncing()
|
||||
|
||||
/** Waiting for the refresh token to expire. */
|
||||
await sleepUntilSessionExpires(this.application, false)
|
||||
await sleepUntilSessionExpires(application, false)
|
||||
|
||||
Factory.ignoreChallenges(this.application)
|
||||
const changePasswordResponse = await this.application.changePassword(this.password, this.newPassword)
|
||||
Factory.ignoreChallenges(application)
|
||||
const changePasswordResponse = await application.changePassword(password, newPassword)
|
||||
|
||||
expect(changePasswordResponse).to.be.ok
|
||||
expect(changePasswordResponse.error.message).to.equal('Invalid login credentials.')
|
||||
@@ -363,17 +365,17 @@ describe('server session', function () {
|
||||
|
||||
it('should sign in successfully after signing out', async function () {
|
||||
await Factory.registerUserToApplication({
|
||||
application: this.application,
|
||||
email: this.email,
|
||||
password: this.password,
|
||||
application: application,
|
||||
email: email,
|
||||
password: password,
|
||||
})
|
||||
|
||||
await this.application.legacyApi.signOut()
|
||||
this.application.legacyApi.session = undefined
|
||||
await application.legacyApi.signOut()
|
||||
application.legacyApi.session = undefined
|
||||
|
||||
await this.application.sessions.signIn(this.email, this.password)
|
||||
await application.sessions.signIn(email, password)
|
||||
|
||||
const currentSession = this.application.legacyApi.getSession()
|
||||
const currentSession = application.legacyApi.getSession()
|
||||
|
||||
expect(currentSession).to.be.ok
|
||||
expect(currentSession.accessToken).to.be.ok
|
||||
@@ -383,16 +385,16 @@ describe('server session', function () {
|
||||
|
||||
it('should fail when renewing a session with an expired refresh token', async function () {
|
||||
await Factory.registerUserToApplication({
|
||||
application: this.application,
|
||||
email: this.email,
|
||||
password: this.password,
|
||||
application: application,
|
||||
email: email,
|
||||
password: password,
|
||||
})
|
||||
|
||||
this.application.sync.lockSyncing()
|
||||
application.sync.lockSyncing()
|
||||
|
||||
await sleepUntilSessionExpires(this.application, false)
|
||||
await sleepUntilSessionExpires(application, false)
|
||||
|
||||
const refreshSessionResponse = await this.application.legacyApi.refreshSession()
|
||||
const refreshSessionResponse = await application.legacyApi.refreshSession()
|
||||
|
||||
expect(refreshSessionResponse.status).to.equal(400)
|
||||
expect(refreshSessionResponse.data.error.tag).to.equal('expired-refresh-token')
|
||||
@@ -402,8 +404,8 @@ describe('server session', function () {
|
||||
The access token and refresh token should be expired up to this point.
|
||||
Here we make sure that any subsequent requests will fail.
|
||||
*/
|
||||
Factory.ignoreChallenges(this.application)
|
||||
const syncResponse = await this.application.legacyApi.sync([])
|
||||
Factory.ignoreChallenges(application)
|
||||
const syncResponse = await application.legacyApi.sync([])
|
||||
expect(syncResponse.status).to.equal(401)
|
||||
expect(syncResponse.data.error.tag).to.equal('invalid-auth')
|
||||
expect(syncResponse.data.error.message).to.equal('Invalid login credentials.')
|
||||
@@ -411,42 +413,42 @@ describe('server session', function () {
|
||||
|
||||
it('should fail when renewing a session with an invalid refresh token', async function () {
|
||||
await Factory.registerUserToApplication({
|
||||
application: this.application,
|
||||
email: this.email,
|
||||
password: this.password,
|
||||
application: application,
|
||||
email: email,
|
||||
password: password,
|
||||
})
|
||||
|
||||
const originalSession = this.application.legacyApi.getSession()
|
||||
const originalSession = application.legacyApi.getSession()
|
||||
|
||||
this.application.storage.setValue(StorageKey.Session, {
|
||||
application.storage.setValue(StorageKey.Session, {
|
||||
accessToken: originalSession.accessToken.value,
|
||||
refreshToken: 'this-is-a-fake-token-1234',
|
||||
accessExpiration: originalSession.accessToken.expiresAt,
|
||||
refreshExpiration: originalSession.refreshToken.expiresAt,
|
||||
readonlyAccess: false,
|
||||
})
|
||||
this.application.sessions.initializeFromDisk()
|
||||
application.sessions.initializeFromDisk()
|
||||
|
||||
const refreshSessionResponse = await this.application.legacyApi.refreshSession()
|
||||
const refreshSessionResponse = await application.legacyApi.refreshSession()
|
||||
|
||||
expect(refreshSessionResponse.status).to.equal(400)
|
||||
expect(refreshSessionResponse.data.error.tag).to.equal('invalid-refresh-token')
|
||||
expect(refreshSessionResponse.data.error.message).to.equal('The refresh token is not valid.')
|
||||
|
||||
// Access token should remain valid.
|
||||
const syncResponse = await this.application.legacyApi.sync([])
|
||||
const syncResponse = await application.legacyApi.sync([])
|
||||
expect(syncResponse.status).to.equal(200)
|
||||
})
|
||||
|
||||
it('should fail if syncing while a session refresh is in progress', async function () {
|
||||
await Factory.registerUserToApplication({
|
||||
application: this.application,
|
||||
email: this.email,
|
||||
password: this.password,
|
||||
application: application,
|
||||
email: email,
|
||||
password: password,
|
||||
})
|
||||
|
||||
const refreshPromise = this.application.legacyApi.refreshSession()
|
||||
const syncResponse = await this.application.legacyApi.sync([])
|
||||
const refreshPromise = application.legacyApi.refreshSession()
|
||||
const syncResponse = await application.legacyApi.sync([])
|
||||
|
||||
expect(syncResponse.data.error).to.be.ok
|
||||
|
||||
@@ -458,40 +460,40 @@ describe('server session', function () {
|
||||
|
||||
it('notes should be synced as expected after refreshing a session', async function () {
|
||||
await Factory.registerUserToApplication({
|
||||
application: this.application,
|
||||
email: this.email,
|
||||
password: this.password,
|
||||
application: application,
|
||||
email: email,
|
||||
password: password,
|
||||
})
|
||||
|
||||
const notesBeforeSync = await Factory.createManyMappedNotes(this.application, 5)
|
||||
const notesBeforeSync = await Factory.createManyMappedNotes(application, 5)
|
||||
|
||||
await sleepUntilSessionExpires(this.application)
|
||||
await this.application.sync.sync(syncOptions)
|
||||
expect(this.application.sync.isOutOfSync()).to.equal(false)
|
||||
await sleepUntilSessionExpires(application)
|
||||
await application.sync.sync(syncOptions)
|
||||
expect(application.sync.isOutOfSync()).to.equal(false)
|
||||
|
||||
this.application = await Factory.signOutApplicationAndReturnNew(this.application)
|
||||
await this.application.signIn(this.email, this.password, undefined, undefined, undefined, true)
|
||||
application = await Factory.signOutApplicationAndReturnNew(application)
|
||||
await application.signIn(email, password, undefined, undefined, undefined, true)
|
||||
|
||||
const expectedNotesUuids = notesBeforeSync.map((n) => n.uuid)
|
||||
const notesResults = await this.application.items.findItems(expectedNotesUuids)
|
||||
const notesResults = await application.items.findItems(expectedNotesUuids)
|
||||
|
||||
expect(notesResults.length).to.equal(notesBeforeSync.length)
|
||||
|
||||
for (const aNoteBeforeSync of notesBeforeSync) {
|
||||
const noteResult = await this.application.items.findItem(aNoteBeforeSync.uuid)
|
||||
const noteResult = await application.items.findItem(aNoteBeforeSync.uuid)
|
||||
expect(aNoteBeforeSync.isItemContentEqualWith(noteResult)).to.equal(true)
|
||||
}
|
||||
}).timeout(Factory.TwentySecondTimeout)
|
||||
|
||||
it('should prompt user for account password and sign back in on invalid session', async function () {
|
||||
await Factory.registerUserToApplication({
|
||||
application: this.application,
|
||||
email: this.email,
|
||||
password: this.password,
|
||||
application: application,
|
||||
email: email,
|
||||
password: password,
|
||||
})
|
||||
|
||||
const email = `${Math.random()}`
|
||||
const password = `${Math.random()}`
|
||||
email = `${Math.random()}`
|
||||
password = `${Math.random()}`
|
||||
let didPromptForSignIn = false
|
||||
const receiveChallenge = async (challenge) => {
|
||||
didPromptForSignIn = true
|
||||
@@ -541,55 +543,55 @@ describe('server session', function () {
|
||||
|
||||
it('should return current session in list of sessions', async function () {
|
||||
await Factory.registerUserToApplication({
|
||||
application: this.application,
|
||||
email: this.email,
|
||||
password: this.password,
|
||||
application: application,
|
||||
email: email,
|
||||
password: password,
|
||||
})
|
||||
|
||||
const response = await this.application.legacyApi.getSessionsList()
|
||||
const response = await application.legacyApi.getSessionsList()
|
||||
expect(response.data[0].current).to.equal(true)
|
||||
})
|
||||
|
||||
it('signing out should delete session from all list', async function () {
|
||||
await Factory.registerUserToApplication({
|
||||
application: this.application,
|
||||
email: this.email,
|
||||
password: this.password,
|
||||
application: application,
|
||||
email: email,
|
||||
password: password,
|
||||
})
|
||||
|
||||
/** Create new session aside from existing one */
|
||||
const app2 = await Factory.createAndInitializeApplication('app2')
|
||||
await app2.signIn(this.email, this.password)
|
||||
await app2.signIn(email, password)
|
||||
|
||||
const response = await this.application.legacyApi.getSessionsList()
|
||||
const response = await application.legacyApi.getSessionsList()
|
||||
expect(response.data.length).to.equal(2)
|
||||
|
||||
await app2.user.signOut()
|
||||
|
||||
const response2 = await this.application.legacyApi.getSessionsList()
|
||||
const response2 = await application.legacyApi.getSessionsList()
|
||||
expect(response2.data.length).to.equal(1)
|
||||
})
|
||||
|
||||
it('revoking a session should destroy local data', async function () {
|
||||
Factory.handlePasswordChallenges(this.application, this.password)
|
||||
Factory.handlePasswordChallenges(application, password)
|
||||
await Factory.registerUserToApplication({
|
||||
application: this.application,
|
||||
email: this.email,
|
||||
password: this.password,
|
||||
application: application,
|
||||
email: email,
|
||||
password: password,
|
||||
})
|
||||
|
||||
const app2identifier = 'app2'
|
||||
const app2 = await Factory.createAndInitializeApplication(app2identifier)
|
||||
await app2.signIn(this.email, this.password)
|
||||
await app2.signIn(email, password)
|
||||
const app2Deinit = new Promise((resolve) => {
|
||||
app2.setOnDeinit(() => {
|
||||
resolve()
|
||||
})
|
||||
})
|
||||
|
||||
const { data: sessions } = await this.application.getSessions()
|
||||
const { data: sessions } = await application.getSessions()
|
||||
const app2session = sessions.find((session) => !session.current)
|
||||
await this.application.revokeSession(app2session.uuid)
|
||||
await application.revokeSession(app2session.uuid)
|
||||
void app2.sync.sync()
|
||||
await app2Deinit
|
||||
|
||||
@@ -599,23 +601,23 @@ describe('server session', function () {
|
||||
}).timeout(Factory.TwentySecondTimeout)
|
||||
|
||||
it('revoking other sessions should destroy their local data', async function () {
|
||||
Factory.handlePasswordChallenges(this.application, this.password)
|
||||
Factory.handlePasswordChallenges(application, password)
|
||||
await Factory.registerUserToApplication({
|
||||
application: this.application,
|
||||
email: this.email,
|
||||
password: this.password,
|
||||
application: application,
|
||||
email: email,
|
||||
password: password,
|
||||
})
|
||||
|
||||
const app2identifier = 'app2'
|
||||
const app2 = await Factory.createAndInitializeApplication(app2identifier)
|
||||
await app2.signIn(this.email, this.password)
|
||||
await app2.signIn(email, password)
|
||||
const app2Deinit = new Promise((resolve) => {
|
||||
app2.setOnDeinit(() => {
|
||||
resolve()
|
||||
})
|
||||
})
|
||||
|
||||
await this.application.revokeAllOtherSessions()
|
||||
await application.revokeAllOtherSessions()
|
||||
void app2.sync.sync()
|
||||
await app2Deinit
|
||||
|
||||
@@ -626,24 +628,24 @@ describe('server session', function () {
|
||||
|
||||
it('signing out with invalid session token should still delete local data', async function () {
|
||||
await Factory.registerUserToApplication({
|
||||
application: this.application,
|
||||
email: this.email,
|
||||
password: this.password,
|
||||
application: application,
|
||||
email: email,
|
||||
password: password,
|
||||
})
|
||||
|
||||
this.application.storage.setValue(StorageKey.Session, {
|
||||
application.storage.setValue(StorageKey.Session, {
|
||||
accessToken: undefined,
|
||||
refreshToken: undefined,
|
||||
accessExpiration: 999999999999999,
|
||||
refreshExpiration: 999999999999999,
|
||||
readonlyAccess: false,
|
||||
})
|
||||
this.application.sessions.initializeFromDisk()
|
||||
application.sessions.initializeFromDisk()
|
||||
|
||||
const storageKey = this.application.storage.getPersistenceKey()
|
||||
const storageKey = application.storage.getPersistenceKey()
|
||||
expect(localStorage.getItem(storageKey)).to.be.ok
|
||||
|
||||
await this.application.user.signOut()
|
||||
await application.user.signOut()
|
||||
expect(localStorage.getItem(storageKey)).to.not.be.ok
|
||||
})
|
||||
})
|
||||
|
||||
@@ -30,6 +30,11 @@ describe('settings service', function () {
|
||||
})
|
||||
})
|
||||
|
||||
afterEach(async function () {
|
||||
await Factory.safeDeinit(application)
|
||||
localStorage.clear()
|
||||
})
|
||||
|
||||
const reInitializeApplicationWithRealCrypto = async () => {
|
||||
await Factory.safeDeinit(application)
|
||||
|
||||
@@ -46,11 +51,6 @@ describe('settings service', function () {
|
||||
})
|
||||
}
|
||||
|
||||
afterEach(async function () {
|
||||
await Factory.safeDeinit(application)
|
||||
localStorage.clear()
|
||||
})
|
||||
|
||||
it('creates and reads a setting', async function () {
|
||||
await application.settings.updateSetting(validSetting, fakePayload)
|
||||
const responseCreate = await application.settings.getSetting(validSetting)
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
/* eslint-disable no-unused-expressions */
|
||||
/* eslint-disable no-undef */
|
||||
import { BaseItemCounts } from './lib/BaseItemCounts.js'
|
||||
import * as Factory from './lib/factory.js'
|
||||
import WebDeviceInterface from './lib/web_device_interface.js'
|
||||
|
||||
chai.use(chaiAsPromised)
|
||||
const expect = chai.expect
|
||||
|
||||
@@ -28,48 +26,60 @@ describe('singletons', function () {
|
||||
return context.singletons.findOrCreateContentTypeSingleton(ContentType.TYPES.UserPrefs, FillItemContent({}))
|
||||
}
|
||||
|
||||
let expectedItemCount
|
||||
let application
|
||||
let context
|
||||
let email
|
||||
let password
|
||||
let registerUser
|
||||
let signIn
|
||||
let signOut
|
||||
let extManagerId
|
||||
let extPred
|
||||
let createExtMgr
|
||||
|
||||
beforeEach(async function () {
|
||||
localStorage.clear()
|
||||
this.expectedItemCount = BaseItemCounts.DefaultItems
|
||||
expectedItemCount = BaseItemCounts.DefaultItems
|
||||
|
||||
this.context = await Factory.createAppContext()
|
||||
await this.context.launch()
|
||||
this.application = this.context.application
|
||||
context = await Factory.createAppContext()
|
||||
await context.launch()
|
||||
application = context.application
|
||||
|
||||
this.email = UuidGenerator.GenerateUuid()
|
||||
this.password = UuidGenerator.GenerateUuid()
|
||||
email = UuidGenerator.GenerateUuid()
|
||||
password = UuidGenerator.GenerateUuid()
|
||||
|
||||
this.registerUser = async () => {
|
||||
this.expectedItemCount = BaseItemCounts.DefaultItemsWithAccount
|
||||
registerUser = async () => {
|
||||
expectedItemCount = BaseItemCounts.DefaultItemsWithAccount
|
||||
await Factory.registerUserToApplication({
|
||||
application: this.application,
|
||||
email: this.email,
|
||||
password: this.password,
|
||||
application: application,
|
||||
email: email,
|
||||
password: password,
|
||||
})
|
||||
}
|
||||
|
||||
this.signOut = async () => {
|
||||
this.application = await this.context.signout()
|
||||
signOut = async () => {
|
||||
application = await context.signout()
|
||||
}
|
||||
|
||||
this.signIn = async () => {
|
||||
await this.application.signIn(this.email, this.password, undefined, undefined, undefined, true)
|
||||
signIn = async () => {
|
||||
await application.signIn(email, password, undefined, undefined, undefined, true)
|
||||
}
|
||||
|
||||
this.extManagerId = 'org.standardnotes.extensions-manager'
|
||||
extManagerId = 'org.standardnotes.extensions-manager'
|
||||
|
||||
this.extPred = new CompoundPredicate('and', [
|
||||
extPred = new CompoundPredicate('and', [
|
||||
new Predicate('content_type', '=', ContentType.TYPES.Component),
|
||||
new Predicate('package_info.identifier', '=', this.extManagerId),
|
||||
new Predicate('package_info.identifier', '=', extManagerId),
|
||||
])
|
||||
|
||||
this.createExtMgr = () => {
|
||||
return this.application.mutator.createItem(
|
||||
createExtMgr = () => {
|
||||
return application.mutator.createItem(
|
||||
ContentType.TYPES.Component,
|
||||
{
|
||||
package_info: {
|
||||
name: 'Extensions',
|
||||
identifier: this.extManagerId,
|
||||
identifier: extManagerId,
|
||||
},
|
||||
},
|
||||
true,
|
||||
@@ -78,13 +88,13 @@ describe('singletons', function () {
|
||||
})
|
||||
|
||||
afterEach(async function () {
|
||||
expect(this.application.sync.isOutOfSync()).to.equal(false)
|
||||
expect(this.application.items.items.length).to.equal(this.expectedItemCount)
|
||||
expect(application.sync.isOutOfSync()).to.equal(false)
|
||||
expect(application.items.items.length).to.equal(expectedItemCount)
|
||||
|
||||
const rawPayloads = await this.application.storage.getAllRawPayloads()
|
||||
expect(rawPayloads.length).to.equal(this.expectedItemCount)
|
||||
const rawPayloads = await application.storage.getAllRawPayloads()
|
||||
expect(rawPayloads.length).to.equal(expectedItemCount)
|
||||
|
||||
await Factory.safeDeinit(this.application)
|
||||
await Factory.safeDeinit(application)
|
||||
|
||||
localStorage.clear()
|
||||
})
|
||||
@@ -95,99 +105,99 @@ describe('singletons', function () {
|
||||
const prefs2 = createPrefsPayload()
|
||||
const prefs3 = createPrefsPayload()
|
||||
|
||||
const items = await this.application.mutator.emitItemsFromPayloads(
|
||||
const items = await application.mutator.emitItemsFromPayloads(
|
||||
[prefs1, prefs2, prefs3],
|
||||
PayloadEmitSource.LocalChanged,
|
||||
)
|
||||
await this.application.mutator.setItemsDirty(items)
|
||||
await this.application.sync.sync(syncOptions)
|
||||
expect(this.application.items.items.length).to.equal(this.expectedItemCount)
|
||||
await application.mutator.setItemsDirty(items)
|
||||
await application.sync.sync(syncOptions)
|
||||
expect(application.items.items.length).to.equal(expectedItemCount)
|
||||
})
|
||||
|
||||
it('duplicate components should auto-resolve to 1', async function () {
|
||||
const extManager = await this.createExtMgr()
|
||||
this.expectedItemCount += 1
|
||||
const extManager = await createExtMgr()
|
||||
expectedItemCount += 1
|
||||
|
||||
/** Call needlessly */
|
||||
await this.createExtMgr()
|
||||
await this.createExtMgr()
|
||||
await this.createExtMgr()
|
||||
await createExtMgr()
|
||||
await createExtMgr()
|
||||
await createExtMgr()
|
||||
|
||||
expect(extManager).to.be.ok
|
||||
|
||||
const refreshedExtMgr = this.application.items.findItem(extManager.uuid)
|
||||
const refreshedExtMgr = application.items.findItem(extManager.uuid)
|
||||
|
||||
expect(refreshedExtMgr).to.be.ok
|
||||
|
||||
await this.application.sync.sync(syncOptions)
|
||||
await application.sync.sync(syncOptions)
|
||||
|
||||
expect(this.application.items.itemsMatchingPredicate(ContentType.TYPES.Component, this.extPred).length).to.equal(1)
|
||||
expect(application.items.itemsMatchingPredicate(ContentType.TYPES.Component, extPred).length).to.equal(1)
|
||||
})
|
||||
|
||||
it('resolves via find or create', async function () {
|
||||
/* Set to never synced as singleton manager will attempt to sync before resolving */
|
||||
this.application.sync.ut_clearLastSyncDate()
|
||||
this.application.sync.ut_setDatabaseLoaded(false)
|
||||
application.sync.ut_clearLastSyncDate()
|
||||
application.sync.ut_setDatabaseLoaded(false)
|
||||
const contentType = ContentType.TYPES.UserPrefs
|
||||
const predicate = new Predicate('content_type', '=', contentType)
|
||||
/* Start a sync right after we await singleton resolve below */
|
||||
setTimeout(() => {
|
||||
this.application.sync.ut_setDatabaseLoaded(true)
|
||||
this.application.sync.sync({
|
||||
application.sync.ut_setDatabaseLoaded(true)
|
||||
application.sync.sync({
|
||||
/* Simulate the first sync occuring as that is handled specially by sync service */
|
||||
mode: SyncMode.DownloadFirst,
|
||||
})
|
||||
})
|
||||
const userPreferences = await this.context.singletons.findOrCreateContentTypeSingleton(contentType, {})
|
||||
const userPreferences = await context.singletons.findOrCreateContentTypeSingleton(contentType, {})
|
||||
|
||||
expect(userPreferences).to.be.ok
|
||||
const refreshedUserPrefs = this.application.items.findItem(userPreferences.uuid)
|
||||
const refreshedUserPrefs = application.items.findItem(userPreferences.uuid)
|
||||
expect(refreshedUserPrefs).to.be.ok
|
||||
await this.application.sync.sync(syncOptions)
|
||||
expect(this.application.items.itemsMatchingPredicate(contentType, predicate).length).to.equal(1)
|
||||
await application.sync.sync(syncOptions)
|
||||
expect(application.items.itemsMatchingPredicate(contentType, predicate).length).to.equal(1)
|
||||
})
|
||||
|
||||
it('resolves registered predicate with signing in/out', async function () {
|
||||
await this.registerUser()
|
||||
await registerUser()
|
||||
|
||||
await this.signOut()
|
||||
await signOut()
|
||||
|
||||
this.email = UuidGenerator.GenerateUuid()
|
||||
this.password = UuidGenerator.GenerateUuid()
|
||||
email = UuidGenerator.GenerateUuid()
|
||||
password = UuidGenerator.GenerateUuid()
|
||||
|
||||
await this.createExtMgr()
|
||||
await createExtMgr()
|
||||
|
||||
this.expectedItemCount += 1
|
||||
expectedItemCount += 1
|
||||
|
||||
await Factory.registerUserToApplication({
|
||||
application: this.application,
|
||||
email: this.email,
|
||||
password: this.password,
|
||||
application: application,
|
||||
email: email,
|
||||
password: password,
|
||||
})
|
||||
|
||||
await this.signOut()
|
||||
await signOut()
|
||||
|
||||
await this.createExtMgr()
|
||||
await createExtMgr()
|
||||
|
||||
await this.application.sync.sync(syncOptions)
|
||||
await application.sync.sync(syncOptions)
|
||||
|
||||
const extraSync = this.application.sync.sync(syncOptions)
|
||||
const extraSync = application.sync.sync(syncOptions)
|
||||
|
||||
await this.signIn()
|
||||
await signIn()
|
||||
|
||||
await extraSync
|
||||
}).timeout(15000)
|
||||
|
||||
it('singletons that are deleted after download first sync should not sync to server', async function () {
|
||||
await this.registerUser()
|
||||
await this.createExtMgr()
|
||||
await this.createExtMgr()
|
||||
await this.createExtMgr()
|
||||
this.expectedItemCount++
|
||||
await registerUser()
|
||||
await createExtMgr()
|
||||
await createExtMgr()
|
||||
await createExtMgr()
|
||||
expectedItemCount++
|
||||
|
||||
let didCompleteRelevantSync = false
|
||||
let beginCheckingResponse = false
|
||||
this.application.sync.addEventObserver(async (eventName, data) => {
|
||||
application.sync.addEventObserver(async (eventName, data) => {
|
||||
if (eventName === SyncEvent.DownloadFirstSyncCompleted) {
|
||||
beginCheckingResponse = true
|
||||
}
|
||||
@@ -202,59 +212,59 @@ describe('singletons', function () {
|
||||
expect(matching).to.not.be.ok
|
||||
}
|
||||
})
|
||||
await this.application.sync.sync({ mode: SyncMode.DownloadFirst })
|
||||
await application.sync.sync({ mode: SyncMode.DownloadFirst })
|
||||
expect(didCompleteRelevantSync).to.equal(true)
|
||||
}).timeout(10000)
|
||||
|
||||
it('signing into account and retrieving singleton shouldnt put us in deadlock', async function () {
|
||||
await this.registerUser()
|
||||
await registerUser()
|
||||
|
||||
/** Create prefs */
|
||||
const ogPrefs = await findOrCreatePrefsSingleton(this.context)
|
||||
await this.application.sync.sync(syncOptions)
|
||||
const ogPrefs = await findOrCreatePrefsSingleton(context)
|
||||
await application.sync.sync(syncOptions)
|
||||
|
||||
this.application = await this.context.signout()
|
||||
application = await context.signout()
|
||||
|
||||
/** Create another instance while signed out */
|
||||
await findOrCreatePrefsSingleton(this.context)
|
||||
await findOrCreatePrefsSingleton(context)
|
||||
|
||||
await Factory.loginToApplication({
|
||||
application: this.application,
|
||||
email: this.email,
|
||||
password: this.password,
|
||||
application: application,
|
||||
email: email,
|
||||
password: password,
|
||||
})
|
||||
|
||||
/** After signing in, the instance retrieved from the server should be the one kept */
|
||||
const latestPrefs = await findOrCreatePrefsSingleton(this.context)
|
||||
const latestPrefs = await findOrCreatePrefsSingleton(context)
|
||||
expect(latestPrefs.uuid).to.equal(ogPrefs.uuid)
|
||||
|
||||
const allPrefs = this.application.items.getItems(ogPrefs.content_type)
|
||||
const allPrefs = application.items.getItems(ogPrefs.content_type)
|
||||
expect(allPrefs.length).to.equal(1)
|
||||
})
|
||||
|
||||
it('resolving singleton before first sync, then signing in, should result in correct number of instances', async function () {
|
||||
await this.registerUser()
|
||||
await registerUser()
|
||||
/** Create prefs and associate them with account */
|
||||
const ogPrefs = await findOrCreatePrefsSingleton(this.context)
|
||||
await this.application.sync.sync(syncOptions)
|
||||
this.application = await this.context.signout()
|
||||
const ogPrefs = await findOrCreatePrefsSingleton(context)
|
||||
await application.sync.sync(syncOptions)
|
||||
application = await context.signout()
|
||||
|
||||
/** Create another instance while signed out */
|
||||
await findOrCreatePrefsSingleton(this.context)
|
||||
await findOrCreatePrefsSingleton(context)
|
||||
await Factory.loginToApplication({
|
||||
application: this.application,
|
||||
email: this.email,
|
||||
password: this.password,
|
||||
application: application,
|
||||
email: email,
|
||||
password: password,
|
||||
})
|
||||
/** After signing in, the instance retrieved from the server should be the one kept */
|
||||
const latestPrefs = await findOrCreatePrefsSingleton(this.context)
|
||||
const latestPrefs = await findOrCreatePrefsSingleton(context)
|
||||
expect(latestPrefs.uuid).to.equal(ogPrefs.uuid)
|
||||
const allPrefs = this.application.items.getItems(ogPrefs.content_type)
|
||||
const allPrefs = application.items.getItems(ogPrefs.content_type)
|
||||
expect(allPrefs.length).to.equal(1)
|
||||
})
|
||||
|
||||
it('if only result is errorDecrypting, create new item', async function () {
|
||||
const item = this.application.items.items.find((item) => item.content_type === ContentType.TYPES.UserPrefs)
|
||||
const item = application.items.items.find((item) => item.content_type === ContentType.TYPES.UserPrefs)
|
||||
|
||||
const erroredPayload = new EncryptedPayload({
|
||||
...item.payload.ejected(),
|
||||
@@ -262,13 +272,13 @@ describe('singletons', function () {
|
||||
errorDecrypting: true,
|
||||
})
|
||||
|
||||
await this.application.payloads.emitPayload(erroredPayload)
|
||||
await application.payloads.emitPayload(erroredPayload)
|
||||
|
||||
const resolvedItem = await this.context.singletons.findOrCreateContentTypeSingleton(item.content_type, item.content)
|
||||
const resolvedItem = await context.singletons.findOrCreateContentTypeSingleton(item.content_type, item.content)
|
||||
|
||||
await this.application.sync.sync({ awaitAll: true })
|
||||
await application.sync.sync({ awaitAll: true })
|
||||
|
||||
expect(this.application.items.items.length).to.equal(this.expectedItemCount)
|
||||
expect(application.items.items.length).to.equal(expectedItemCount)
|
||||
expect(resolvedItem.uuid).to.not.equal(item.uuid)
|
||||
expect(resolvedItem.errorDecrypting).to.not.be.ok
|
||||
})
|
||||
@@ -284,13 +294,13 @@ describe('singletons', function () {
|
||||
const sharedContent = {
|
||||
package_info: {
|
||||
name: 'Extensions',
|
||||
identifier: this.extManagerId,
|
||||
identifier: extManagerId,
|
||||
},
|
||||
}
|
||||
|
||||
const errorDecryptingFalse = false
|
||||
await Factory.insertItemWithOverride(
|
||||
this.application,
|
||||
application,
|
||||
ContentType.TYPES.Component,
|
||||
sharedContent,
|
||||
true,
|
||||
@@ -299,16 +309,16 @@ describe('singletons', function () {
|
||||
|
||||
const errorDecryptingTrue = true
|
||||
const errored = await Factory.insertItemWithOverride(
|
||||
this.application,
|
||||
application,
|
||||
ContentType.TYPES.Component,
|
||||
sharedContent,
|
||||
true,
|
||||
errorDecryptingTrue,
|
||||
)
|
||||
|
||||
this.expectedItemCount += 1
|
||||
expectedItemCount += 1
|
||||
|
||||
await this.application.sync.sync(syncOptions)
|
||||
await application.sync.sync(syncOptions)
|
||||
|
||||
/** Now mark errored as not errorDecrypting and sync */
|
||||
const notErrored = new DecryptedPayload({
|
||||
@@ -317,34 +327,31 @@ describe('singletons', function () {
|
||||
errorDecrypting: false,
|
||||
})
|
||||
|
||||
await this.application.payloads.emitPayload(notErrored)
|
||||
await application.payloads.emitPayload(notErrored)
|
||||
|
||||
/** Item will get decrypted on current tick, so wait one before syncing */
|
||||
await Factory.sleep(0)
|
||||
await this.application.sync.sync(syncOptions)
|
||||
await application.sync.sync(syncOptions)
|
||||
|
||||
expect(this.application.items.itemsMatchingPredicate(ContentType.TYPES.Component, this.extPred).length).to.equal(1)
|
||||
expect(application.items.itemsMatchingPredicate(ContentType.TYPES.Component, extPred).length).to.equal(1)
|
||||
})
|
||||
|
||||
it('alternating the uuid of a singleton should return correct result', async function () {
|
||||
const payload = createPrefsPayload()
|
||||
const item = await this.application.mutator.emitItemFromPayload(payload, PayloadEmitSource.LocalChanged)
|
||||
await this.application.sync.sync(syncOptions)
|
||||
const item = await application.mutator.emitItemFromPayload(payload, PayloadEmitSource.LocalChanged)
|
||||
await application.sync.sync(syncOptions)
|
||||
const predicate = new Predicate('content_type', '=', item.content_type)
|
||||
let resolvedItem = await this.context.singletons.findOrCreateContentTypeSingleton(
|
||||
payload.content_type,
|
||||
payload.content,
|
||||
)
|
||||
let resolvedItem = await context.singletons.findOrCreateContentTypeSingleton(payload.content_type, payload.content)
|
||||
const originalUuid = resolvedItem.uuid
|
||||
await Factory.alternateUuidForItem(this.application, resolvedItem.uuid)
|
||||
await this.application.sync.sync(syncOptions)
|
||||
const resolvedItem2 = await this.context.singletons.findOrCreateContentTypeSingleton(
|
||||
await Factory.alternateUuidForItem(application, resolvedItem.uuid)
|
||||
await application.sync.sync(syncOptions)
|
||||
const resolvedItem2 = await context.singletons.findOrCreateContentTypeSingleton(
|
||||
payload.content_type,
|
||||
payload.content,
|
||||
)
|
||||
resolvedItem = this.application.items.findItem(resolvedItem.uuid)
|
||||
resolvedItem = application.items.findItem(resolvedItem.uuid)
|
||||
expect(resolvedItem).to.not.be.ok
|
||||
expect(resolvedItem2.uuid).to.not.equal(originalUuid)
|
||||
expect(this.application.items.items.length).to.equal(this.expectedItemCount)
|
||||
expect(application.items.items.length).to.equal(expectedItemCount)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
/* eslint-disable no-unused-expressions */
|
||||
/* eslint-disable no-undef */
|
||||
import { BaseItemCounts } from './lib/BaseItemCounts.js'
|
||||
import * as Factory from './lib/factory.js'
|
||||
|
||||
chai.use(chaiAsPromised)
|
||||
const expect = chai.expect
|
||||
|
||||
@@ -13,128 +12,137 @@ describe('storage manager', function () {
|
||||
*/
|
||||
const BASE_KEY_COUNT = ['storage', 'snjs_version', 'keychain'].length
|
||||
|
||||
let application
|
||||
let email
|
||||
let password
|
||||
let expectedKeyCount
|
||||
|
||||
beforeEach(async function () {
|
||||
localStorage.clear()
|
||||
this.expectedKeyCount = BASE_KEY_COUNT
|
||||
expectedKeyCount = BASE_KEY_COUNT
|
||||
|
||||
this.context = await Factory.createAppContext()
|
||||
await this.context.launch()
|
||||
context = await Factory.createAppContext()
|
||||
await context.launch()
|
||||
|
||||
this.application = this.context.application
|
||||
this.email = UuidGenerator.GenerateUuid()
|
||||
this.password = UuidGenerator.GenerateUuid()
|
||||
application = context.application
|
||||
email = UuidGenerator.GenerateUuid()
|
||||
password = UuidGenerator.GenerateUuid()
|
||||
})
|
||||
|
||||
afterEach(async function () {
|
||||
await Factory.safeDeinit(this.application)
|
||||
await context.deinit()
|
||||
|
||||
application = undefined
|
||||
context = undefined
|
||||
|
||||
localStorage.clear()
|
||||
})
|
||||
|
||||
it('should set and retrieve values', async function () {
|
||||
const key = 'foo'
|
||||
const value = 'bar'
|
||||
await this.application.storage.setValue(key, value)
|
||||
expect(await this.application.storage.getValue(key)).to.eql(value)
|
||||
await application.storage.setValue(key, value)
|
||||
expect(await application.storage.getValue(key)).to.eql(value)
|
||||
})
|
||||
|
||||
it('should set and retrieve items', async function () {
|
||||
const payload = Factory.createNotePayload()
|
||||
await this.application.storage.savePayload(payload)
|
||||
const payloads = await this.application.storage.getAllRawPayloads()
|
||||
await application.storage.savePayload(payload)
|
||||
const payloads = await application.storage.getAllRawPayloads()
|
||||
expect(payloads.length).to.equal(BaseItemCounts.DefaultItems + 1)
|
||||
})
|
||||
|
||||
it('should clear values', async function () {
|
||||
const key = 'foo'
|
||||
const value = 'bar'
|
||||
await this.application.storage.setValue(key, value)
|
||||
await this.application.storage.clearAllData()
|
||||
expect(await this.application.storage.getValue(key)).to.not.be.ok
|
||||
await application.storage.setValue(key, value)
|
||||
await application.storage.clearAllData()
|
||||
expect(await application.storage.getValue(key)).to.not.be.ok
|
||||
})
|
||||
|
||||
it('serverPassword should not be saved to keychain', async function () {
|
||||
await Factory.registerUserToApplication({
|
||||
application: this.application,
|
||||
email: this.email,
|
||||
password: this.password,
|
||||
application: application,
|
||||
email: email,
|
||||
password: password,
|
||||
ephemeral: false,
|
||||
})
|
||||
const keychainValue = await this.application.device.getNamespacedKeychainValue(this.application.identifier)
|
||||
const keychainValue = await application.device.getNamespacedKeychainValue(application.identifier)
|
||||
expect(keychainValue.masterKey).to.be.ok
|
||||
expect(keychainValue.serverPassword).to.not.be.ok
|
||||
})
|
||||
|
||||
it('regular session should persist data', async function () {
|
||||
await Factory.registerUserToApplication({
|
||||
application: this.application,
|
||||
email: this.email,
|
||||
password: this.password,
|
||||
application: application,
|
||||
email: email,
|
||||
password: password,
|
||||
ephemeral: false,
|
||||
})
|
||||
|
||||
const key = 'foo'
|
||||
const value = 'bar'
|
||||
await this.application.storage.setValue(key, value)
|
||||
await application.storage.setValue(key, value)
|
||||
|
||||
expect(Object.keys(localStorage).length).to.equal(this.expectedKeyCount + BaseItemCounts.DefaultItemsWithAccount)
|
||||
const retrievedValue = await this.application.storage.getValue(key)
|
||||
expect(Object.keys(localStorage).length).to.equal(expectedKeyCount + BaseItemCounts.DefaultItemsWithAccount)
|
||||
const retrievedValue = await application.storage.getValue(key)
|
||||
expect(retrievedValue).to.equal(value)
|
||||
})
|
||||
|
||||
it('ephemeral session should not persist data', async function () {
|
||||
this.retries(2)
|
||||
await Factory.registerUserToApplication({
|
||||
application: this.application,
|
||||
email: this.email,
|
||||
password: this.password,
|
||||
application: application,
|
||||
email: email,
|
||||
password: password,
|
||||
ephemeral: true,
|
||||
})
|
||||
const key = 'foo'
|
||||
const value = 'bar'
|
||||
await this.application.storage.setValueAndAwaitPersist(key, value)
|
||||
await application.storage.setValueAndAwaitPersist(key, value)
|
||||
|
||||
const expectedKeys = ['keychain']
|
||||
expect(Object.keys(localStorage).length).to.equal(expectedKeys.length)
|
||||
|
||||
const retrievedValue = await this.application.storage.getValue(key)
|
||||
const retrievedValue = await application.storage.getValue(key)
|
||||
expect(retrievedValue).to.equal(value)
|
||||
})
|
||||
|
||||
it('ephemeral session should not persist to database', async function () {
|
||||
await Factory.registerUserToApplication({
|
||||
application: this.application,
|
||||
email: this.email,
|
||||
password: this.password,
|
||||
application: application,
|
||||
email: email,
|
||||
password: password,
|
||||
ephemeral: true,
|
||||
})
|
||||
await Factory.createSyncedNote(this.application)
|
||||
const rawPayloads = await this.application.storage.getAllRawPayloads()
|
||||
await Factory.createSyncedNote(application)
|
||||
const rawPayloads = await application.storage.getAllRawPayloads()
|
||||
expect(rawPayloads.length).to.equal(0)
|
||||
})
|
||||
|
||||
it('storage with no account and no passcode should not be encrypted', async function () {
|
||||
await this.application.storage.setValueAndAwaitPersist('foo', 'bar')
|
||||
const wrappedValue = this.application.storage.values[ValueModesKeys.Wrapped]
|
||||
await application.storage.setValueAndAwaitPersist('foo', 'bar')
|
||||
const wrappedValue = application.storage.values[ValueModesKeys.Wrapped]
|
||||
const payload = new DecryptedPayload(wrappedValue)
|
||||
expect(payload.content).to.be.an.instanceof(Object)
|
||||
})
|
||||
|
||||
it('storage aftering adding passcode should be encrypted', async function () {
|
||||
await this.application.storage.setValueAndAwaitPersist('foo', 'bar')
|
||||
await this.application.addPasscode('123')
|
||||
const wrappedValue = this.application.storage.values[ValueModesKeys.Wrapped]
|
||||
await application.storage.setValueAndAwaitPersist('foo', 'bar')
|
||||
await application.addPasscode('123')
|
||||
const wrappedValue = application.storage.values[ValueModesKeys.Wrapped]
|
||||
const payload = new EncryptedPayload(wrappedValue)
|
||||
expect(payload.content).to.be.a('string')
|
||||
})
|
||||
|
||||
it('storage after adding passcode then removing passcode should not be encrypted', async function () {
|
||||
const passcode = '123'
|
||||
Factory.handlePasswordChallenges(this.application, passcode)
|
||||
await this.application.storage.setValueAndAwaitPersist('foo', 'bar')
|
||||
await this.application.addPasscode(passcode)
|
||||
await this.application.storage.setValueAndAwaitPersist('bar', 'foo')
|
||||
await this.application.removePasscode()
|
||||
const wrappedValue = this.application.storage.values[ValueModesKeys.Wrapped]
|
||||
Factory.handlePasswordChallenges(application, passcode)
|
||||
await application.storage.setValueAndAwaitPersist('foo', 'bar')
|
||||
await application.addPasscode(passcode)
|
||||
await application.storage.setValueAndAwaitPersist('bar', 'foo')
|
||||
await application.removePasscode()
|
||||
const wrappedValue = application.storage.values[ValueModesKeys.Wrapped]
|
||||
const payload = new DecryptedPayload(wrappedValue)
|
||||
expect(payload.content).to.be.an.instanceof(Object)
|
||||
})
|
||||
@@ -147,80 +155,85 @@ describe('storage manager', function () {
|
||||
* the account keys to be moved to the keychain.
|
||||
* */
|
||||
await Factory.registerUserToApplication({
|
||||
application: this.application,
|
||||
email: this.email,
|
||||
password: this.password,
|
||||
application: application,
|
||||
email: email,
|
||||
password: password,
|
||||
})
|
||||
expect(await this.application.device.getNamespacedKeychainValue(this.application.identifier)).to.be.ok
|
||||
await this.application.storage.setValueAndAwaitPersist('foo', 'bar')
|
||||
Factory.handlePasswordChallenges(this.application, this.password)
|
||||
await this.application.addPasscode(passcode)
|
||||
expect(await this.application.device.getNamespacedKeychainValue(this.application.identifier)).to.not.be.ok
|
||||
await this.application.storage.setValueAndAwaitPersist('bar', 'foo')
|
||||
Factory.handlePasswordChallenges(this.application, passcode)
|
||||
await this.application.removePasscode()
|
||||
expect(await this.application.device.getNamespacedKeychainValue(this.application.identifier)).to.be.ok
|
||||
expect(await application.device.getNamespacedKeychainValue(application.identifier)).to.be.ok
|
||||
await application.storage.setValueAndAwaitPersist('foo', 'bar')
|
||||
Factory.handlePasswordChallenges(application, password)
|
||||
await application.addPasscode(passcode)
|
||||
expect(await application.device.getNamespacedKeychainValue(application.identifier)).to.not.be.ok
|
||||
await application.storage.setValueAndAwaitPersist('bar', 'foo')
|
||||
Factory.handlePasswordChallenges(application, passcode)
|
||||
await application.removePasscode()
|
||||
expect(await application.device.getNamespacedKeychainValue(application.identifier)).to.be.ok
|
||||
|
||||
const wrappedValue = this.application.storage.values[ValueModesKeys.Wrapped]
|
||||
const wrappedValue = application.storage.values[ValueModesKeys.Wrapped]
|
||||
const payload = new EncryptedPayload(wrappedValue)
|
||||
expect(payload.content).to.be.a('string')
|
||||
})
|
||||
|
||||
it('adding account should encrypt storage with account keys', async function () {
|
||||
await this.application.storage.setValueAndAwaitPersist('foo', 'bar')
|
||||
await application.storage.setValueAndAwaitPersist('foo', 'bar')
|
||||
await Factory.registerUserToApplication({
|
||||
application: this.application,
|
||||
email: this.email,
|
||||
password: this.password,
|
||||
application: application,
|
||||
email: email,
|
||||
password: password,
|
||||
ephemeral: true,
|
||||
})
|
||||
const accountKey = await this.application.encryption.getRootKey()
|
||||
expect(await this.application.storage.canDecryptWithKey(accountKey)).to.equal(true)
|
||||
const accountKey = await application.encryption.getRootKey()
|
||||
expect(await application.storage.canDecryptWithKey(accountKey)).to.equal(true)
|
||||
})
|
||||
|
||||
it('signing out of account should decrypt storage', async function () {
|
||||
await this.application.storage.setValueAndAwaitPersist('foo', 'bar')
|
||||
await application.storage.setValueAndAwaitPersist('foo', 'bar')
|
||||
await Factory.registerUserToApplication({
|
||||
application: this.application,
|
||||
email: this.email,
|
||||
password: this.password,
|
||||
application: application,
|
||||
email: email,
|
||||
password: password,
|
||||
ephemeral: true,
|
||||
})
|
||||
this.application = await Factory.signOutApplicationAndReturnNew(this.application)
|
||||
await this.application.storage.setValueAndAwaitPersist('bar', 'foo')
|
||||
const wrappedValue = this.application.storage.values[ValueModesKeys.Wrapped]
|
||||
|
||||
application = await Factory.signOutApplicationAndReturnNew(application)
|
||||
|
||||
await application.storage.setValueAndAwaitPersist('bar', 'foo')
|
||||
const wrappedValue = application.storage.values[ValueModesKeys.Wrapped]
|
||||
const payload = new DecryptedPayload(wrappedValue)
|
||||
|
||||
expect(payload.content).to.be.an.instanceof(Object)
|
||||
|
||||
await Factory.safeDeinit(application)
|
||||
})
|
||||
|
||||
it('adding account then passcode should encrypt storage with account keys', async function () {
|
||||
/** Should encrypt storage with account keys and encrypt account keys with passcode */
|
||||
await this.application.storage.setValueAndAwaitPersist('foo', 'bar')
|
||||
await application.storage.setValueAndAwaitPersist('foo', 'bar')
|
||||
await Factory.registerUserToApplication({
|
||||
application: this.application,
|
||||
email: this.email,
|
||||
password: this.password,
|
||||
application: application,
|
||||
email: email,
|
||||
password: password,
|
||||
ephemeral: true,
|
||||
})
|
||||
|
||||
/** Should not be wrapped root key yet */
|
||||
expect(await this.application.encryption.rootKeyManager.getWrappedRootKey()).to.not.be.ok
|
||||
expect(await application.encryption.rootKeyManager.getWrappedRootKey()).to.not.be.ok
|
||||
|
||||
const passcode = '123'
|
||||
Factory.handlePasswordChallenges(this.application, this.password)
|
||||
await this.application.addPasscode(passcode)
|
||||
await this.application.storage.setValueAndAwaitPersist('bar', 'foo')
|
||||
Factory.handlePasswordChallenges(application, password)
|
||||
await application.addPasscode(passcode)
|
||||
await application.storage.setValueAndAwaitPersist('bar', 'foo')
|
||||
|
||||
/** Root key should now be wrapped */
|
||||
expect(await this.application.encryption.rootKeyManager.getWrappedRootKey()).to.be.ok
|
||||
expect(await application.encryption.rootKeyManager.getWrappedRootKey()).to.be.ok
|
||||
|
||||
const accountKey = await this.application.encryption.getRootKey()
|
||||
expect(await this.application.storage.canDecryptWithKey(accountKey)).to.equal(true)
|
||||
const passcodeKey = await this.application.encryption.computeWrappingKey(passcode)
|
||||
const wrappedRootKey = await this.application.encryption.rootKeyManager.getWrappedRootKey()
|
||||
const accountKey = await application.encryption.getRootKey()
|
||||
expect(await application.storage.canDecryptWithKey(accountKey)).to.equal(true)
|
||||
const passcodeKey = await application.encryption.computeWrappingKey(passcode)
|
||||
const wrappedRootKey = await application.encryption.rootKeyManager.getWrappedRootKey()
|
||||
/** Expect that we can decrypt wrapped root key with passcode key */
|
||||
const payload = new EncryptedPayload(wrappedRootKey)
|
||||
const decrypted = await this.application.encryption.decryptSplitSingle({
|
||||
const decrypted = await application.encryption.decryptSplitSingle({
|
||||
usesRootKey: {
|
||||
items: [payload],
|
||||
key: passcodeKey,
|
||||
@@ -230,9 +243,9 @@ describe('storage manager', function () {
|
||||
})
|
||||
|
||||
it('stored payloads should not contain metadata fields', async function () {
|
||||
await this.application.addPasscode('123')
|
||||
await Factory.createSyncedNote(this.application)
|
||||
const payloads = await this.application.storage.getAllRawPayloads()
|
||||
await application.addPasscode('123')
|
||||
await Factory.createSyncedNote(application)
|
||||
const payloads = await application.storage.getAllRawPayloads()
|
||||
const payload = payloads[0]
|
||||
expect(payload.fields).to.not.be.ok
|
||||
expect(payload.source).to.not.be.ok
|
||||
@@ -240,9 +253,9 @@ describe('storage manager', function () {
|
||||
})
|
||||
|
||||
it('storing an offline synced payload should not include dirty flag', async function () {
|
||||
await this.application.addPasscode('123')
|
||||
await Factory.createSyncedNote(this.application)
|
||||
const payloads = await this.application.storage.getAllRawPayloads()
|
||||
await application.addPasscode('123')
|
||||
await Factory.createSyncedNote(application)
|
||||
const payloads = await application.storage.getAllRawPayloads()
|
||||
const payload = payloads[0]
|
||||
|
||||
expect(payload.dirty).to.not.be.ok
|
||||
@@ -250,14 +263,14 @@ describe('storage manager', function () {
|
||||
|
||||
it('storing an online synced payload should not include dirty flag', async function () {
|
||||
await Factory.registerUserToApplication({
|
||||
application: this.application,
|
||||
email: this.email,
|
||||
password: this.password,
|
||||
application: application,
|
||||
email: email,
|
||||
password: password,
|
||||
ephemeral: false,
|
||||
})
|
||||
|
||||
await Factory.createSyncedNote(this.application)
|
||||
const payloads = await this.application.storage.getAllRawPayloads()
|
||||
await Factory.createSyncedNote(application)
|
||||
const payloads = await application.storage.getAllRawPayloads()
|
||||
const payload = payloads[0]
|
||||
|
||||
expect(payload.dirty).to.not.be.ok
|
||||
@@ -265,29 +278,36 @@ describe('storage manager', function () {
|
||||
|
||||
it('signing out should clear unwrapped value store', async function () {
|
||||
await Factory.registerUserToApplication({
|
||||
application: this.application,
|
||||
email: this.email,
|
||||
password: this.password,
|
||||
application: application,
|
||||
email: email,
|
||||
password: password,
|
||||
ephemeral: false,
|
||||
})
|
||||
|
||||
this.application = await Factory.signOutApplicationAndReturnNew(this.application)
|
||||
const values = this.application.storage.values[ValueModesKeys.Unwrapped]
|
||||
application = await Factory.signOutApplicationAndReturnNew(application)
|
||||
|
||||
const values = application.storage.values[ValueModesKeys.Unwrapped]
|
||||
expect(Object.keys(values).length).to.equal(0)
|
||||
|
||||
await Factory.safeDeinit(application)
|
||||
})
|
||||
|
||||
it('signing out should clear payloads', async function () {
|
||||
await Factory.registerUserToApplication({
|
||||
application: this.application,
|
||||
email: this.email,
|
||||
password: this.password,
|
||||
application: application,
|
||||
email: email,
|
||||
password: password,
|
||||
ephemeral: false,
|
||||
})
|
||||
|
||||
await Factory.createSyncedNote(this.application)
|
||||
expect(await Factory.storagePayloadCount(this.application)).to.equal(BaseItemCounts.DefaultItemsWithAccount + 1)
|
||||
this.application = await Factory.signOutApplicationAndReturnNew(this.application)
|
||||
await Factory.createSyncedNote(application)
|
||||
expect(await Factory.storagePayloadCount(application)).to.equal(BaseItemCounts.DefaultItemsWithAccount + 1)
|
||||
|
||||
application = await Factory.signOutApplicationAndReturnNew(application)
|
||||
|
||||
await Factory.sleep(0.1, 'Allow all untrackable singleton syncs to complete')
|
||||
expect(await Factory.storagePayloadCount(this.application)).to.equal(BaseItemCounts.DefaultItems)
|
||||
expect(await Factory.storagePayloadCount(application)).to.equal(BaseItemCounts.DefaultItems)
|
||||
|
||||
await Factory.safeDeinit(application)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -10,11 +10,6 @@ describe('subscriptions', function () {
|
||||
let context
|
||||
let subscriptionManager
|
||||
|
||||
afterEach(async function () {
|
||||
await Factory.safeDeinit(application)
|
||||
localStorage.clear()
|
||||
})
|
||||
|
||||
beforeEach(async function () {
|
||||
localStorage.clear()
|
||||
|
||||
@@ -34,6 +29,11 @@ describe('subscriptions', function () {
|
||||
await context.activatePaidSubscriptionForUser()
|
||||
})
|
||||
|
||||
afterEach(async function () {
|
||||
await Factory.safeDeinit(application)
|
||||
localStorage.clear()
|
||||
})
|
||||
|
||||
it('should invite a user by email to a shared subscription', async () => {
|
||||
await subscriptionManager.inviteToSubscription('test@test.te')
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,31 +1,37 @@
|
||||
/* eslint-disable no-unused-expressions */
|
||||
/* eslint-disable no-undef */
|
||||
import { BaseItemCounts } from '../lib/BaseItemCounts.js'
|
||||
import * as Factory from '../lib/factory.js'
|
||||
|
||||
chai.use(chaiAsPromised)
|
||||
const expect = chai.expect
|
||||
|
||||
describe('sync integrity', () => {
|
||||
before(function () {
|
||||
localStorage.clear()
|
||||
})
|
||||
|
||||
after(function () {
|
||||
localStorage.clear()
|
||||
})
|
||||
let application
|
||||
let email
|
||||
let password
|
||||
let expectedItemCount
|
||||
|
||||
beforeEach(async function () {
|
||||
this.expectedItemCount = BaseItemCounts.DefaultItemsWithAccount
|
||||
this.application = await Factory.createInitAppWithFakeCrypto()
|
||||
this.email = UuidGenerator.GenerateUuid()
|
||||
this.password = UuidGenerator.GenerateUuid()
|
||||
localStorage.clear()
|
||||
expectedItemCount = BaseItemCounts.DefaultItemsWithAccount
|
||||
application = await Factory.createInitAppWithFakeCrypto()
|
||||
email = UuidGenerator.GenerateUuid()
|
||||
password = UuidGenerator.GenerateUuid()
|
||||
await Factory.registerUserToApplication({
|
||||
application: this.application,
|
||||
email: this.email,
|
||||
password: this.password,
|
||||
application: application,
|
||||
email: email,
|
||||
password: password,
|
||||
})
|
||||
})
|
||||
|
||||
afterEach(async function () {
|
||||
expect(application.sync.isOutOfSync()).to.equal(false)
|
||||
const rawPayloads = await application.storage.getAllRawPayloads()
|
||||
expect(rawPayloads.length).to.equal(expectedItemCount)
|
||||
await Factory.safeDeinit(application)
|
||||
localStorage.clear()
|
||||
application = undefined
|
||||
})
|
||||
|
||||
const awaitSyncEventPromise = (application, targetEvent) => {
|
||||
return new Promise((resolve) => {
|
||||
application.sync.addEventObserver((event) => {
|
||||
@@ -36,44 +42,37 @@ describe('sync integrity', () => {
|
||||
})
|
||||
}
|
||||
|
||||
afterEach(async function () {
|
||||
expect(this.application.sync.isOutOfSync()).to.equal(false)
|
||||
const rawPayloads = await this.application.storage.getAllRawPayloads()
|
||||
expect(rawPayloads.length).to.equal(this.expectedItemCount)
|
||||
await Factory.safeDeinit(this.application)
|
||||
})
|
||||
|
||||
it('should detect when out of sync', async function () {
|
||||
const item = await this.application.mutator.emitItemFromPayload(
|
||||
const item = await application.mutator.emitItemFromPayload(
|
||||
Factory.createNotePayload(),
|
||||
PayloadEmitSource.LocalChanged,
|
||||
)
|
||||
this.expectedItemCount++
|
||||
expectedItemCount++
|
||||
|
||||
const didEnterOutOfSync = awaitSyncEventPromise(this.application, SyncEvent.EnterOutOfSync)
|
||||
await this.application.sync.sync({ checkIntegrity: true })
|
||||
const didEnterOutOfSync = awaitSyncEventPromise(application, SyncEvent.EnterOutOfSync)
|
||||
await application.sync.sync({ checkIntegrity: true })
|
||||
|
||||
await this.application.items.removeItemFromMemory(item)
|
||||
await this.application.sync.sync({ checkIntegrity: true, awaitAll: true })
|
||||
await application.items.removeItemFromMemory(item)
|
||||
await application.sync.sync({ checkIntegrity: true, awaitAll: true })
|
||||
|
||||
await didEnterOutOfSync
|
||||
})
|
||||
|
||||
it('should self heal after out of sync', async function () {
|
||||
const item = await this.application.mutator.emitItemFromPayload(
|
||||
const item = await application.mutator.emitItemFromPayload(
|
||||
Factory.createNotePayload(),
|
||||
PayloadEmitSource.LocalChanged,
|
||||
)
|
||||
this.expectedItemCount++
|
||||
expectedItemCount++
|
||||
|
||||
const didEnterOutOfSync = awaitSyncEventPromise(this.application, SyncEvent.EnterOutOfSync)
|
||||
const didExitOutOfSync = awaitSyncEventPromise(this.application, SyncEvent.ExitOutOfSync)
|
||||
const didEnterOutOfSync = awaitSyncEventPromise(application, SyncEvent.EnterOutOfSync)
|
||||
const didExitOutOfSync = awaitSyncEventPromise(application, SyncEvent.ExitOutOfSync)
|
||||
|
||||
await this.application.sync.sync({ checkIntegrity: true })
|
||||
await this.application.items.removeItemFromMemory(item)
|
||||
await this.application.sync.sync({ checkIntegrity: true, awaitAll: true })
|
||||
await application.sync.sync({ checkIntegrity: true })
|
||||
await application.items.removeItemFromMemory(item)
|
||||
await application.sync.sync({ checkIntegrity: true, awaitAll: true })
|
||||
|
||||
await Promise.all([didEnterOutOfSync, didExitOutOfSync])
|
||||
expect(this.application.sync.isOutOfSync()).to.equal(false)
|
||||
expect(application.sync.isOutOfSync()).to.equal(false)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
/* eslint-disable no-unused-expressions */
|
||||
/* eslint-disable no-undef */
|
||||
import * as Factory from '../lib/factory.js'
|
||||
import { createRelatedNoteTagPairPayload } from '../lib/Items.js'
|
||||
|
||||
chai.use(chaiAsPromised)
|
||||
const expect = chai.expect
|
||||
|
||||
describe('notes + tags syncing', function () {
|
||||
let application
|
||||
|
||||
const syncOptions = {
|
||||
checkIntegrity: true,
|
||||
awaitAll: true,
|
||||
@@ -16,30 +17,31 @@ describe('notes + tags syncing', function () {
|
||||
})
|
||||
|
||||
beforeEach(async function () {
|
||||
this.application = await Factory.createInitAppWithFakeCrypto()
|
||||
Factory.disableIntegrityAutoHeal(this.application)
|
||||
application = await Factory.createInitAppWithFakeCrypto()
|
||||
Factory.disableIntegrityAutoHeal(application)
|
||||
const email = UuidGenerator.GenerateUuid()
|
||||
const password = UuidGenerator.GenerateUuid()
|
||||
await Factory.registerUserToApplication({
|
||||
application: this.application,
|
||||
application: application,
|
||||
email,
|
||||
password,
|
||||
})
|
||||
})
|
||||
|
||||
afterEach(async function () {
|
||||
await Factory.safeDeinit(this.application)
|
||||
await Factory.safeDeinit(application)
|
||||
application = undefined
|
||||
})
|
||||
|
||||
it('syncing an item then downloading it should include items_key_id', async function () {
|
||||
const note = await Factory.createMappedNote(this.application)
|
||||
await this.application.mutator.setItemDirty(note)
|
||||
await this.application.sync.sync(syncOptions)
|
||||
await this.application.payloads.resetState()
|
||||
await this.application.items.resetState()
|
||||
await this.application.sync.clearSyncPositionTokens()
|
||||
await this.application.sync.sync(syncOptions)
|
||||
const downloadedNote = this.application.items.getDisplayableNotes()[0]
|
||||
const note = await Factory.createMappedNote(application)
|
||||
await application.mutator.setItemDirty(note)
|
||||
await application.sync.sync(syncOptions)
|
||||
await application.payloads.resetState()
|
||||
await application.items.resetState()
|
||||
await application.sync.clearSyncPositionTokens()
|
||||
await application.sync.sync(syncOptions)
|
||||
const downloadedNote = application.items.getDisplayableNotes()[0]
|
||||
expect(downloadedNote.items_key_id).to.not.be.ok
|
||||
// Allow time for waitingForKey
|
||||
await Factory.sleep(0.1)
|
||||
@@ -52,21 +54,21 @@ describe('notes + tags syncing', function () {
|
||||
const notePayload = pair[0]
|
||||
const tagPayload = pair[1]
|
||||
|
||||
await this.application.mutator.emitItemsFromPayloads([notePayload, tagPayload], PayloadEmitSource.LocalChanged)
|
||||
const note = this.application.items.getItems([ContentType.TYPES.Note])[0]
|
||||
const tag = this.application.items.getItems([ContentType.TYPES.Tag])[0]
|
||||
expect(this.application.items.getDisplayableNotes().length).to.equal(1)
|
||||
expect(this.application.items.getDisplayableTags().length).to.equal(1)
|
||||
await application.mutator.emitItemsFromPayloads([notePayload, tagPayload], PayloadEmitSource.LocalChanged)
|
||||
const note = application.items.getItems([ContentType.TYPES.Note])[0]
|
||||
const tag = application.items.getItems([ContentType.TYPES.Tag])[0]
|
||||
expect(application.items.getDisplayableNotes().length).to.equal(1)
|
||||
expect(application.items.getDisplayableTags().length).to.equal(1)
|
||||
|
||||
for (let i = 0; i < 9; i++) {
|
||||
await this.application.mutator.setItemsDirty([note, tag])
|
||||
await this.application.sync.sync(syncOptions)
|
||||
this.application.sync.clearSyncPositionTokens()
|
||||
await application.mutator.setItemsDirty([note, tag])
|
||||
await application.sync.sync(syncOptions)
|
||||
application.sync.clearSyncPositionTokens()
|
||||
expect(tag.content.references.length).to.equal(1)
|
||||
expect(this.application.items.itemsReferencingItem(note).length).to.equal(1)
|
||||
expect(application.items.itemsReferencingItem(note).length).to.equal(1)
|
||||
expect(tag.noteCount).to.equal(1)
|
||||
expect(this.application.items.getDisplayableNotes().length).to.equal(1)
|
||||
expect(this.application.items.getDisplayableTags().length).to.equal(1)
|
||||
expect(application.items.getDisplayableNotes().length).to.equal(1)
|
||||
expect(application.items.getDisplayableTags().length).to.equal(1)
|
||||
console.warn('Waiting 0.1s...')
|
||||
await Factory.sleep(0.1)
|
||||
}
|
||||
@@ -76,59 +78,59 @@ describe('notes + tags syncing', function () {
|
||||
const pair = createRelatedNoteTagPairPayload()
|
||||
const notePayload = pair[0]
|
||||
const tagPayload = pair[1]
|
||||
await this.application.mutator.emitItemsFromPayloads([notePayload, tagPayload], PayloadEmitSource.LocalChanged)
|
||||
const originalNote = this.application.items.getDisplayableNotes()[0]
|
||||
const originalTag = this.application.items.getDisplayableTags()[0]
|
||||
await this.application.mutator.setItemsDirty([originalNote, originalTag])
|
||||
await application.mutator.emitItemsFromPayloads([notePayload, tagPayload], PayloadEmitSource.LocalChanged)
|
||||
const originalNote = application.items.getDisplayableNotes()[0]
|
||||
const originalTag = application.items.getDisplayableTags()[0]
|
||||
await application.mutator.setItemsDirty([originalNote, originalTag])
|
||||
|
||||
await this.application.sync.sync(syncOptions)
|
||||
await application.sync.sync(syncOptions)
|
||||
|
||||
expect(originalTag.content.references.length).to.equal(1)
|
||||
expect(originalTag.noteCount).to.equal(1)
|
||||
expect(this.application.items.itemsReferencingItem(originalNote).length).to.equal(1)
|
||||
expect(application.items.itemsReferencingItem(originalNote).length).to.equal(1)
|
||||
|
||||
// when signing in, all local items are cleared from storage (but kept in memory; to clear desktop logs),
|
||||
// then resaved with alternated uuids.
|
||||
await this.application.storage.clearAllPayloads()
|
||||
await this.application.sync.markAllItemsAsNeedingSyncAndPersist()
|
||||
await application.storage.clearAllPayloads()
|
||||
await application.sync.markAllItemsAsNeedingSyncAndPersist()
|
||||
|
||||
expect(this.application.items.getDisplayableNotes().length).to.equal(1)
|
||||
expect(this.application.items.getDisplayableTags().length).to.equal(1)
|
||||
expect(application.items.getDisplayableNotes().length).to.equal(1)
|
||||
expect(application.items.getDisplayableTags().length).to.equal(1)
|
||||
|
||||
const note = this.application.items.getDisplayableNotes()[0]
|
||||
const tag = this.application.items.getDisplayableTags()[0]
|
||||
const note = application.items.getDisplayableNotes()[0]
|
||||
const tag = application.items.getDisplayableTags()[0]
|
||||
|
||||
expect(tag.content.references.length).to.equal(1)
|
||||
expect(note.content.references.length).to.equal(0)
|
||||
|
||||
expect(tag.noteCount).to.equal(1)
|
||||
expect(this.application.items.itemsReferencingItem(note).length).to.equal(1)
|
||||
expect(application.items.itemsReferencingItem(note).length).to.equal(1)
|
||||
})
|
||||
|
||||
it('duplicating a tag should maintian its relationships', async function () {
|
||||
const pair = createRelatedNoteTagPairPayload()
|
||||
const notePayload = pair[0]
|
||||
const tagPayload = pair[1]
|
||||
await this.application.mutator.emitItemsFromPayloads([notePayload, tagPayload], PayloadEmitSource.LocalChanged)
|
||||
let note = this.application.items.getDisplayableNotes()[0]
|
||||
let tag = this.application.items.getDisplayableTags()[0]
|
||||
expect(this.application.items.itemsReferencingItem(note).length).to.equal(1)
|
||||
await application.mutator.emitItemsFromPayloads([notePayload, tagPayload], PayloadEmitSource.LocalChanged)
|
||||
let note = application.items.getDisplayableNotes()[0]
|
||||
let tag = application.items.getDisplayableTags()[0]
|
||||
expect(application.items.itemsReferencingItem(note).length).to.equal(1)
|
||||
|
||||
await this.application.mutator.setItemsDirty([note, tag])
|
||||
await this.application.sync.sync(syncOptions)
|
||||
await this.application.sync.clearSyncPositionTokens()
|
||||
await application.mutator.setItemsDirty([note, tag])
|
||||
await application.sync.sync(syncOptions)
|
||||
await application.sync.clearSyncPositionTokens()
|
||||
|
||||
note = this.application.items.findItem(note.uuid)
|
||||
tag = this.application.items.findItem(tag.uuid)
|
||||
note = application.items.findItem(note.uuid)
|
||||
tag = application.items.findItem(tag.uuid)
|
||||
|
||||
expect(note.dirty).to.equal(false)
|
||||
expect(tag.dirty).to.equal(false)
|
||||
|
||||
expect(this.application.items.getDisplayableNotes().length).to.equal(1)
|
||||
expect(this.application.items.getDisplayableTags().length).to.equal(1)
|
||||
expect(application.items.getDisplayableNotes().length).to.equal(1)
|
||||
expect(application.items.getDisplayableTags().length).to.equal(1)
|
||||
|
||||
await Factory.changePayloadTimeStampAndSync(
|
||||
this.application,
|
||||
application,
|
||||
tag.payload,
|
||||
Factory.dateToMicroseconds(Factory.yesterday()),
|
||||
{
|
||||
@@ -137,13 +139,13 @@ describe('notes + tags syncing', function () {
|
||||
syncOptions,
|
||||
)
|
||||
|
||||
tag = this.application.items.findItem(tag.uuid)
|
||||
tag = application.items.findItem(tag.uuid)
|
||||
|
||||
// tag should now be conflicted and a copy created
|
||||
expect(this.application.items.getDisplayableNotes().length).to.equal(1)
|
||||
expect(this.application.items.getDisplayableTags().length).to.equal(2)
|
||||
expect(application.items.getDisplayableNotes().length).to.equal(1)
|
||||
expect(application.items.getDisplayableTags().length).to.equal(2)
|
||||
|
||||
const tags = this.application.items.getDisplayableTags()
|
||||
const tags = application.items.getDisplayableTags()
|
||||
const conflictedTag = tags.find((tag) => {
|
||||
return !!tag.content.conflict_of
|
||||
})
|
||||
@@ -157,11 +159,11 @@ describe('notes + tags syncing', function () {
|
||||
expect(conflictedTag.content.conflict_of).to.equal(originalTag.uuid)
|
||||
expect(conflictedTag.noteCount).to.equal(originalTag.noteCount)
|
||||
|
||||
expect(this.application.items.itemsReferencingItem(conflictedTag).length).to.equal(0)
|
||||
expect(this.application.items.itemsReferencingItem(originalTag).length).to.equal(0)
|
||||
expect(application.items.itemsReferencingItem(conflictedTag).length).to.equal(0)
|
||||
expect(application.items.itemsReferencingItem(originalTag).length).to.equal(0)
|
||||
|
||||
// Two tags now link to this note
|
||||
const referencingItems = this.application.items.itemsReferencingItem(note)
|
||||
const referencingItems = application.items.itemsReferencingItem(note)
|
||||
expect(referencingItems.length).to.equal(2)
|
||||
expect(referencingItems[0]).to.not.equal(referencingItems[1])
|
||||
}).timeout(10000)
|
||||
|
||||
@@ -1,71 +1,70 @@
|
||||
/* eslint-disable no-unused-expressions */
|
||||
/* eslint-disable no-undef */
|
||||
import { BaseItemCounts } from '../lib/BaseItemCounts.js'
|
||||
import * as Factory from '../lib/factory.js'
|
||||
|
||||
chai.use(chaiAsPromised)
|
||||
const expect = chai.expect
|
||||
|
||||
describe('offline syncing', () => {
|
||||
let context
|
||||
let application
|
||||
let expectedItemCount
|
||||
|
||||
const syncOptions = {
|
||||
checkIntegrity: true,
|
||||
awaitAll: true,
|
||||
}
|
||||
|
||||
beforeEach(async function () {
|
||||
this.expectedItemCount = BaseItemCounts.DefaultItems
|
||||
this.context = await Factory.createAppContext()
|
||||
await this.context.launch()
|
||||
this.application = this.context.application
|
||||
localStorage.clear()
|
||||
expectedItemCount = BaseItemCounts.DefaultItems
|
||||
context = await Factory.createAppContext()
|
||||
await context.launch()
|
||||
application = context.application
|
||||
})
|
||||
|
||||
afterEach(async function () {
|
||||
expect(this.application.sync.isOutOfSync()).to.equal(false)
|
||||
await Factory.safeDeinit(this.application)
|
||||
})
|
||||
|
||||
before(async function () {
|
||||
localStorage.clear()
|
||||
})
|
||||
|
||||
after(async function () {
|
||||
expect(application.sync.isOutOfSync()).to.equal(false)
|
||||
await Factory.safeDeinit(application)
|
||||
localStorage.clear()
|
||||
application = undefined
|
||||
context = undefined
|
||||
})
|
||||
|
||||
it('uuid alternation should delete original payload', async function () {
|
||||
const note = await Factory.createMappedNote(this.application)
|
||||
this.expectedItemCount++
|
||||
const note = await Factory.createMappedNote(application)
|
||||
expectedItemCount++
|
||||
|
||||
await Factory.alternateUuidForItem(this.application, note.uuid)
|
||||
await this.application.sync.sync(syncOptions)
|
||||
await Factory.alternateUuidForItem(application, note.uuid)
|
||||
await application.sync.sync(syncOptions)
|
||||
|
||||
const notes = this.application.items.getDisplayableNotes()
|
||||
const notes = application.items.getDisplayableNotes()
|
||||
expect(notes.length).to.equal(1)
|
||||
expect(notes[0].uuid).to.not.equal(note.uuid)
|
||||
|
||||
const items = this.application.items.allTrackedItems()
|
||||
expect(items.length).to.equal(this.expectedItemCount)
|
||||
const items = application.items.allTrackedItems()
|
||||
expect(items.length).to.equal(expectedItemCount)
|
||||
})
|
||||
|
||||
it('should sync item with no passcode', async function () {
|
||||
let note = await Factory.createMappedNote(this.application)
|
||||
expect(Uuids(this.application.items.getDirtyItems()).includes(note.uuid))
|
||||
let note = await Factory.createMappedNote(application)
|
||||
expect(Uuids(application.items.getDirtyItems()).includes(note.uuid))
|
||||
|
||||
await this.application.sync.sync(syncOptions)
|
||||
await application.sync.sync(syncOptions)
|
||||
|
||||
note = this.application.items.findItem(note.uuid)
|
||||
note = application.items.findItem(note.uuid)
|
||||
|
||||
/** In rare cases a sync can complete so fast that the dates are equal; this is ok. */
|
||||
expect(note.lastSyncEnd).to.be.at.least(note.lastSyncBegan)
|
||||
|
||||
this.expectedItemCount++
|
||||
expectedItemCount++
|
||||
|
||||
expect(this.application.items.getDirtyItems().length).to.equal(0)
|
||||
expect(application.items.getDirtyItems().length).to.equal(0)
|
||||
|
||||
const rawPayloads2 = await this.application.storage.getAllRawPayloads()
|
||||
expect(rawPayloads2.length).to.equal(this.expectedItemCount)
|
||||
const rawPayloads2 = await application.storage.getAllRawPayloads()
|
||||
expect(rawPayloads2.length).to.equal(expectedItemCount)
|
||||
|
||||
const itemsKeyRaw = (await Factory.getStoragePayloadsOfType(this.application, ContentType.TYPES.ItemsKey))[0]
|
||||
const noteRaw = (await Factory.getStoragePayloadsOfType(this.application, ContentType.TYPES.Note))[0]
|
||||
const itemsKeyRaw = (await Factory.getStoragePayloadsOfType(application, ContentType.TYPES.ItemsKey))[0]
|
||||
const noteRaw = (await Factory.getStoragePayloadsOfType(application, ContentType.TYPES.Note))[0]
|
||||
|
||||
/** Encrypts with default items key */
|
||||
expect(typeof noteRaw.content).to.equal('string')
|
||||
@@ -75,30 +74,30 @@ describe('offline syncing', () => {
|
||||
})
|
||||
|
||||
it('should sync item encrypted with passcode', async function () {
|
||||
await this.application.addPasscode('foobar')
|
||||
await Factory.createMappedNote(this.application)
|
||||
expect(this.application.items.getDirtyItems().length).to.equal(1)
|
||||
const rawPayloads1 = await this.application.storage.getAllRawPayloads()
|
||||
expect(rawPayloads1.length).to.equal(this.expectedItemCount)
|
||||
await application.addPasscode('foobar')
|
||||
await Factory.createMappedNote(application)
|
||||
expect(application.items.getDirtyItems().length).to.equal(1)
|
||||
const rawPayloads1 = await application.storage.getAllRawPayloads()
|
||||
expect(rawPayloads1.length).to.equal(expectedItemCount)
|
||||
|
||||
await this.application.sync.sync(syncOptions)
|
||||
this.expectedItemCount++
|
||||
await application.sync.sync(syncOptions)
|
||||
expectedItemCount++
|
||||
|
||||
expect(this.application.items.getDirtyItems().length).to.equal(0)
|
||||
const rawPayloads2 = await this.application.storage.getAllRawPayloads()
|
||||
expect(rawPayloads2.length).to.equal(this.expectedItemCount)
|
||||
expect(application.items.getDirtyItems().length).to.equal(0)
|
||||
const rawPayloads2 = await application.storage.getAllRawPayloads()
|
||||
expect(rawPayloads2.length).to.equal(expectedItemCount)
|
||||
|
||||
const payload = rawPayloads2[0]
|
||||
expect(typeof payload.content).to.equal('string')
|
||||
expect(payload.content.startsWith(this.application.encryption.getLatestVersion())).to.equal(true)
|
||||
expect(payload.content.startsWith(application.encryption.getLatestVersion())).to.equal(true)
|
||||
})
|
||||
|
||||
it('signing out while offline should succeed', async function () {
|
||||
await Factory.createMappedNote(this.application)
|
||||
this.expectedItemCount++
|
||||
await this.application.sync.sync(syncOptions)
|
||||
this.application = await Factory.signOutApplicationAndReturnNew(this.application)
|
||||
expect(this.application.sessions.isSignedIn()).to.equal(false)
|
||||
expect(this.application.sessions.getUser()).to.not.be.ok
|
||||
await Factory.createMappedNote(application)
|
||||
expectedItemCount++
|
||||
await application.sync.sync(syncOptions)
|
||||
application = await Factory.signOutApplicationAndReturnNew(application)
|
||||
expect(application.sessions.isSignedIn()).to.equal(false)
|
||||
expect(application.sessions.getUser()).to.not.be.ok
|
||||
})
|
||||
})
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,41 +1,49 @@
|
||||
/* eslint-disable no-unused-expressions */
|
||||
/* eslint-disable no-undef */
|
||||
import * as Factory from './lib/factory.js'
|
||||
|
||||
chai.use(chaiAsPromised)
|
||||
const expect = chai.expect
|
||||
|
||||
describe('upgrading', () => {
|
||||
let application
|
||||
let context
|
||||
let email
|
||||
let password
|
||||
let passcode
|
||||
let receiveChallenge
|
||||
let receiveChallengeWithApp
|
||||
|
||||
beforeEach(async function () {
|
||||
localStorage.clear()
|
||||
|
||||
this.context = await Factory.createAppContext()
|
||||
await this.context.launch()
|
||||
context = await Factory.createAppContext()
|
||||
await context.launch()
|
||||
|
||||
this.application = this.context.application
|
||||
this.email = UuidGenerator.GenerateUuid()
|
||||
this.password = UuidGenerator.GenerateUuid()
|
||||
this.passcode = '1234'
|
||||
application = context.application
|
||||
email = UuidGenerator.GenerateUuid()
|
||||
password = UuidGenerator.GenerateUuid()
|
||||
passcode = '1234'
|
||||
|
||||
const promptValueReply = (prompts) => {
|
||||
const values = []
|
||||
for (const prompt of prompts) {
|
||||
if (prompt.validation === ChallengeValidation.LocalPasscode) {
|
||||
values.push(CreateChallengeValue(prompt, this.passcode))
|
||||
values.push(CreateChallengeValue(prompt, passcode))
|
||||
} else {
|
||||
values.push(CreateChallengeValue(prompt, this.password))
|
||||
values.push(CreateChallengeValue(prompt, password))
|
||||
}
|
||||
}
|
||||
return values
|
||||
}
|
||||
this.receiveChallenge = (challenge) => {
|
||||
void this.receiveChallengeWithApp(this.application, challenge)
|
||||
|
||||
receiveChallenge = (challenge) => {
|
||||
void receiveChallengeWithApp(application, challenge)
|
||||
}
|
||||
this.receiveChallengeWithApp = (application, challenge) => {
|
||||
|
||||
receiveChallengeWithApp = (application, challenge) => {
|
||||
application.addChallengeObserver(challenge, {
|
||||
onInvalidValue: (value) => {
|
||||
const values = promptValueReply([value.prompt])
|
||||
application.submitValuesForChallenge(challenge, values)
|
||||
numPasscodeAttempts++
|
||||
},
|
||||
})
|
||||
const initialValues = promptValueReply(challenge.prompts)
|
||||
@@ -44,7 +52,7 @@ describe('upgrading', () => {
|
||||
})
|
||||
|
||||
afterEach(async function () {
|
||||
await Factory.safeDeinit(this.application)
|
||||
await Factory.safeDeinit(application)
|
||||
localStorage.clear()
|
||||
})
|
||||
|
||||
@@ -52,76 +60,72 @@ describe('upgrading', () => {
|
||||
const oldVersion = ProtocolVersion.V003
|
||||
/** Register with 003 version */
|
||||
await Factory.registerOldUser({
|
||||
application: this.application,
|
||||
email: this.email,
|
||||
password: this.password,
|
||||
application: application,
|
||||
email: email,
|
||||
password: password,
|
||||
version: oldVersion,
|
||||
})
|
||||
|
||||
expect(await this.application.protocolUpgradeAvailable()).to.equal(true)
|
||||
expect(await application.protocolUpgradeAvailable()).to.equal(true)
|
||||
})
|
||||
|
||||
it('upgrade should be available when passcode only', async function () {
|
||||
const oldVersion = ProtocolVersion.V003
|
||||
await Factory.setOldVersionPasscode({
|
||||
application: this.application,
|
||||
passcode: this.passcode,
|
||||
application: application,
|
||||
passcode: passcode,
|
||||
version: oldVersion,
|
||||
})
|
||||
|
||||
expect(await this.application.protocolUpgradeAvailable()).to.equal(true)
|
||||
expect(await application.protocolUpgradeAvailable()).to.equal(true)
|
||||
})
|
||||
|
||||
it('upgrades application protocol from 003 to 004', async function () {
|
||||
const oldVersion = ProtocolVersion.V003
|
||||
const newVersion = ProtocolVersion.V004
|
||||
|
||||
await Factory.createMappedNote(this.application)
|
||||
await Factory.createMappedNote(application)
|
||||
|
||||
/** Register with 003 version */
|
||||
await Factory.registerOldUser({
|
||||
application: this.application,
|
||||
email: this.email,
|
||||
password: this.password,
|
||||
application: application,
|
||||
email: email,
|
||||
password: password,
|
||||
version: oldVersion,
|
||||
})
|
||||
|
||||
await Factory.setOldVersionPasscode({
|
||||
application: this.application,
|
||||
passcode: this.passcode,
|
||||
application: application,
|
||||
passcode: passcode,
|
||||
version: oldVersion,
|
||||
})
|
||||
|
||||
expect((await this.application.encryption.rootKeyManager.getRootKeyWrapperKeyParams()).version).to.equal(
|
||||
oldVersion,
|
||||
)
|
||||
expect((await this.application.encryption.getRootKeyParams()).version).to.equal(oldVersion)
|
||||
expect((await this.application.encryption.getRootKey()).keyVersion).to.equal(oldVersion)
|
||||
expect((await application.encryption.rootKeyManager.getRootKeyWrapperKeyParams()).version).to.equal(oldVersion)
|
||||
expect((await application.encryption.getRootKeyParams()).version).to.equal(oldVersion)
|
||||
expect((await application.encryption.getRootKey()).keyVersion).to.equal(oldVersion)
|
||||
|
||||
this.application.setLaunchCallback({
|
||||
receiveChallenge: this.receiveChallenge,
|
||||
application.setLaunchCallback({
|
||||
receiveChallenge: receiveChallenge,
|
||||
})
|
||||
const result = await this.application.upgradeProtocolVersion()
|
||||
const result = await application.upgradeProtocolVersion()
|
||||
expect(result).to.deep.equal({ success: true })
|
||||
|
||||
const wrappedRootKey = await this.application.encryption.rootKeyManager.getWrappedRootKey()
|
||||
const wrappedRootKey = await application.encryption.rootKeyManager.getWrappedRootKey()
|
||||
const payload = new EncryptedPayload(wrappedRootKey)
|
||||
expect(payload.version).to.equal(newVersion)
|
||||
|
||||
expect((await this.application.encryption.rootKeyManager.getRootKeyWrapperKeyParams()).version).to.equal(
|
||||
newVersion,
|
||||
)
|
||||
expect((await this.application.encryption.getRootKeyParams()).version).to.equal(newVersion)
|
||||
expect((await this.application.encryption.getRootKey()).keyVersion).to.equal(newVersion)
|
||||
expect((await application.encryption.rootKeyManager.getRootKeyWrapperKeyParams()).version).to.equal(newVersion)
|
||||
expect((await application.encryption.getRootKeyParams()).version).to.equal(newVersion)
|
||||
expect((await application.encryption.getRootKey()).keyVersion).to.equal(newVersion)
|
||||
|
||||
/**
|
||||
* Immediately logging out ensures we don't rely on subsequent
|
||||
* sync events to complete the upgrade
|
||||
*/
|
||||
this.application = await Factory.signOutApplicationAndReturnNew(this.application)
|
||||
await this.application.signIn(this.email, this.password, undefined, undefined, undefined, true)
|
||||
expect(this.application.items.getDisplayableNotes().length).to.equal(1)
|
||||
expect(this.application.payloads.invalidPayloads).to.be.empty
|
||||
application = await Factory.signOutApplicationAndReturnNew(application)
|
||||
await application.signIn(email, password, undefined, undefined, undefined, true)
|
||||
expect(application.items.getDisplayableNotes().length).to.equal(1)
|
||||
expect(application.payloads.invalidPayloads).to.be.empty
|
||||
}).timeout(15000)
|
||||
|
||||
it('upgrading from 003 to 004 with passcode only then reiniting app should create valid state', async function () {
|
||||
@@ -133,23 +137,23 @@ describe('upgrading', () => {
|
||||
const oldVersion = ProtocolVersion.V003
|
||||
|
||||
await Factory.setOldVersionPasscode({
|
||||
application: this.application,
|
||||
passcode: this.passcode,
|
||||
application: application,
|
||||
passcode: passcode,
|
||||
version: oldVersion,
|
||||
})
|
||||
await Factory.createSyncedNote(this.application)
|
||||
await Factory.createSyncedNote(application)
|
||||
|
||||
this.application.setLaunchCallback({
|
||||
receiveChallenge: this.receiveChallenge,
|
||||
application.setLaunchCallback({
|
||||
receiveChallenge: receiveChallenge,
|
||||
})
|
||||
|
||||
const identifier = this.application.identifier
|
||||
const identifier = application.identifier
|
||||
|
||||
/** Recreate the app once */
|
||||
const appFirst = Factory.createApplicationWithFakeCrypto(identifier)
|
||||
await appFirst.prepareForLaunch({
|
||||
receiveChallenge: (challenge) => {
|
||||
this.receiveChallengeWithApp(appFirst, challenge)
|
||||
receiveChallengeWithApp(appFirst, challenge)
|
||||
},
|
||||
})
|
||||
await appFirst.launch(true)
|
||||
@@ -162,7 +166,7 @@ describe('upgrading', () => {
|
||||
const appSecond = Factory.createApplicationWithFakeCrypto(identifier)
|
||||
await appSecond.prepareForLaunch({
|
||||
receiveChallenge: (challenge) => {
|
||||
this.receiveChallengeWithApp(appSecond, challenge)
|
||||
receiveChallengeWithApp(appSecond, challenge)
|
||||
},
|
||||
})
|
||||
await appSecond.launch(true)
|
||||
@@ -172,46 +176,46 @@ describe('upgrading', () => {
|
||||
|
||||
it('protocol version should be upgraded on password change', async function () {
|
||||
/** Delete default items key that is created on launch */
|
||||
const itemsKey = await this.application.encryption.getSureDefaultItemsKey()
|
||||
await this.application.mutator.setItemToBeDeleted(itemsKey)
|
||||
expect(Uuids(this.application.items.getDisplayableItemsKeys()).includes(itemsKey.uuid)).to.equal(false)
|
||||
const itemsKey = await application.encryption.getSureDefaultItemsKey()
|
||||
await application.mutator.setItemToBeDeleted(itemsKey)
|
||||
expect(Uuids(application.items.getDisplayableItemsKeys()).includes(itemsKey.uuid)).to.equal(false)
|
||||
|
||||
Factory.createMappedNote(this.application)
|
||||
Factory.createMappedNote(application)
|
||||
|
||||
/** Register with 003 version */
|
||||
await Factory.registerOldUser({
|
||||
application: this.application,
|
||||
email: this.email,
|
||||
password: this.password,
|
||||
application: application,
|
||||
email: email,
|
||||
password: password,
|
||||
version: ProtocolVersion.V003,
|
||||
})
|
||||
|
||||
expect(this.application.items.getDisplayableItemsKeys().length).to.equal(1)
|
||||
expect(application.items.getDisplayableItemsKeys().length).to.equal(1)
|
||||
|
||||
expect((await this.application.encryption.getRootKeyParams()).version).to.equal(ProtocolVersion.V003)
|
||||
expect((await this.application.encryption.getRootKey()).keyVersion).to.equal(ProtocolVersion.V003)
|
||||
expect((await application.encryption.getRootKeyParams()).version).to.equal(ProtocolVersion.V003)
|
||||
expect((await application.encryption.getRootKey()).keyVersion).to.equal(ProtocolVersion.V003)
|
||||
|
||||
/** Ensure note is encrypted with 003 */
|
||||
const notePayloads = await Factory.getStoragePayloadsOfType(this.application, ContentType.TYPES.Note)
|
||||
const notePayloads = await Factory.getStoragePayloadsOfType(application, ContentType.TYPES.Note)
|
||||
expect(notePayloads.length).to.equal(1)
|
||||
expect(notePayloads[0].version).to.equal(ProtocolVersion.V003)
|
||||
|
||||
const { error } = await this.application.changePassword(this.password, 'foobarfoo')
|
||||
const { error } = await application.changePassword(password, 'foobarfoo')
|
||||
expect(error).to.not.exist
|
||||
|
||||
const latestVersion = this.application.encryption.getLatestVersion()
|
||||
expect((await this.application.encryption.getRootKeyParams()).version).to.equal(latestVersion)
|
||||
expect((await this.application.encryption.getRootKey()).keyVersion).to.equal(latestVersion)
|
||||
const latestVersion = application.encryption.getLatestVersion()
|
||||
expect((await application.encryption.getRootKeyParams()).version).to.equal(latestVersion)
|
||||
expect((await application.encryption.getRootKey()).keyVersion).to.equal(latestVersion)
|
||||
|
||||
const defaultItemsKey = await this.application.encryption.getSureDefaultItemsKey()
|
||||
const defaultItemsKey = await application.encryption.getSureDefaultItemsKey()
|
||||
expect(defaultItemsKey.keyVersion).to.equal(latestVersion)
|
||||
|
||||
/** After change, note should now be encrypted with latest protocol version */
|
||||
|
||||
const note = this.application.items.getDisplayableNotes()[0]
|
||||
await Factory.markDirtyAndSyncItem(this.application, note)
|
||||
const note = application.items.getDisplayableNotes()[0]
|
||||
await Factory.markDirtyAndSyncItem(application, note)
|
||||
|
||||
const refreshedNotePayloads = await Factory.getStoragePayloadsOfType(this.application, ContentType.TYPES.Note)
|
||||
const refreshedNotePayloads = await Factory.getStoragePayloadsOfType(application, ContentType.TYPES.Note)
|
||||
const refreshedNotePayload = refreshedNotePayloads[0]
|
||||
expect(refreshedNotePayload.version).to.equal(latestVersion)
|
||||
}).timeout(5000)
|
||||
@@ -221,19 +225,19 @@ describe('upgrading', () => {
|
||||
const oldVersion = ProtocolVersion.V003
|
||||
|
||||
beforeEach(async function () {
|
||||
await Factory.createMappedNote(this.application)
|
||||
await Factory.createMappedNote(application)
|
||||
|
||||
/** Register with 003 version */
|
||||
await Factory.registerOldUser({
|
||||
application: this.application,
|
||||
email: this.email,
|
||||
password: this.password,
|
||||
application: application,
|
||||
email: email,
|
||||
password: password,
|
||||
version: oldVersion,
|
||||
})
|
||||
|
||||
await Factory.setOldVersionPasscode({
|
||||
application: this.application,
|
||||
passcode: this.passcode,
|
||||
application: application,
|
||||
passcode: passcode,
|
||||
version: oldVersion,
|
||||
})
|
||||
})
|
||||
@@ -243,44 +247,36 @@ describe('upgrading', () => {
|
||||
})
|
||||
|
||||
it('rolls back the local protocol upgrade if syncing fails', async function () {
|
||||
sinon.replace(this.application.sync, 'sync', sinon.fake())
|
||||
this.application.setLaunchCallback({
|
||||
receiveChallenge: this.receiveChallenge,
|
||||
sinon.replace(application.sync, 'sync', sinon.fake())
|
||||
application.setLaunchCallback({
|
||||
receiveChallenge: receiveChallenge,
|
||||
})
|
||||
expect((await this.application.encryption.rootKeyManager.getRootKeyWrapperKeyParams()).version).to.equal(
|
||||
oldVersion,
|
||||
)
|
||||
const errors = await this.application.upgradeProtocolVersion()
|
||||
expect((await application.encryption.rootKeyManager.getRootKeyWrapperKeyParams()).version).to.equal(oldVersion)
|
||||
const errors = await application.upgradeProtocolVersion()
|
||||
expect(errors).to.not.be.empty
|
||||
|
||||
/** Ensure we're still on 003 */
|
||||
expect((await this.application.encryption.rootKeyManager.getRootKeyWrapperKeyParams()).version).to.equal(
|
||||
oldVersion,
|
||||
)
|
||||
expect((await this.application.encryption.getRootKeyParams()).version).to.equal(oldVersion)
|
||||
expect((await this.application.encryption.getRootKey()).keyVersion).to.equal(oldVersion)
|
||||
expect((await this.application.encryption.getSureDefaultItemsKey()).keyVersion).to.equal(oldVersion)
|
||||
expect((await application.encryption.rootKeyManager.getRootKeyWrapperKeyParams()).version).to.equal(oldVersion)
|
||||
expect((await application.encryption.getRootKeyParams()).version).to.equal(oldVersion)
|
||||
expect((await application.encryption.getRootKey()).keyVersion).to.equal(oldVersion)
|
||||
expect((await application.encryption.getSureDefaultItemsKey()).keyVersion).to.equal(oldVersion)
|
||||
})
|
||||
|
||||
it('rolls back the local protocol upgrade if the server responds with an error', async function () {
|
||||
sinon.replace(this.application.sessions, 'changeCredentials', () => [Error()])
|
||||
sinon.replace(application.sessions, 'changeCredentials', () => [Error()])
|
||||
|
||||
this.application.setLaunchCallback({
|
||||
receiveChallenge: this.receiveChallenge,
|
||||
application.setLaunchCallback({
|
||||
receiveChallenge: receiveChallenge,
|
||||
})
|
||||
expect((await this.application.encryption.rootKeyManager.getRootKeyWrapperKeyParams()).version).to.equal(
|
||||
oldVersion,
|
||||
)
|
||||
const errors = await this.application.upgradeProtocolVersion()
|
||||
expect((await application.encryption.rootKeyManager.getRootKeyWrapperKeyParams()).version).to.equal(oldVersion)
|
||||
const errors = await application.upgradeProtocolVersion()
|
||||
expect(errors).to.not.be.empty
|
||||
|
||||
/** Ensure we're still on 003 */
|
||||
expect((await this.application.encryption.rootKeyManager.getRootKeyWrapperKeyParams()).version).to.equal(
|
||||
oldVersion,
|
||||
)
|
||||
expect((await this.application.encryption.getRootKeyParams()).version).to.equal(oldVersion)
|
||||
expect((await this.application.encryption.getRootKey()).keyVersion).to.equal(oldVersion)
|
||||
expect((await this.application.encryption.getSureDefaultItemsKey()).keyVersion).to.equal(oldVersion)
|
||||
expect((await application.encryption.rootKeyManager.getRootKeyWrapperKeyParams()).version).to.equal(oldVersion)
|
||||
expect((await application.encryption.getRootKeyParams()).version).to.equal(oldVersion)
|
||||
expect((await application.encryption.getRootKey()).keyVersion).to.equal(oldVersion)
|
||||
expect((await application.encryption.getSureDefaultItemsKey()).keyVersion).to.equal(oldVersion)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
/* eslint-disable no-unused-expressions */
|
||||
/* eslint-disable no-undef */
|
||||
|
||||
chai.use(chaiAsPromised)
|
||||
const expect = chai.expect
|
||||
|
||||
|
||||
@@ -9,11 +9,6 @@ describe('asymmetric messages', function () {
|
||||
|
||||
let context
|
||||
|
||||
afterEach(async function () {
|
||||
await context.deinit()
|
||||
localStorage.clear()
|
||||
})
|
||||
|
||||
beforeEach(async function () {
|
||||
localStorage.clear()
|
||||
|
||||
@@ -23,6 +18,12 @@ describe('asymmetric messages', function () {
|
||||
await context.register()
|
||||
})
|
||||
|
||||
afterEach(async function () {
|
||||
await context.deinit()
|
||||
localStorage.clear()
|
||||
sinon.restore()
|
||||
})
|
||||
|
||||
it('should not trust message if the trusted payload data recipientUuid does not match the message user uuid', async () => {
|
||||
const { sharedVault, contactContext, deinitContactContext } =
|
||||
await Collaboration.createSharedVaultWithAcceptedInvite(context)
|
||||
|
||||
@@ -9,11 +9,6 @@ describe('shared vault conflicts', function () {
|
||||
|
||||
let context
|
||||
|
||||
afterEach(async function () {
|
||||
await context.deinit()
|
||||
localStorage.clear()
|
||||
})
|
||||
|
||||
beforeEach(async function () {
|
||||
localStorage.clear()
|
||||
|
||||
@@ -23,6 +18,12 @@ describe('shared vault conflicts', function () {
|
||||
await context.register()
|
||||
})
|
||||
|
||||
afterEach(async function () {
|
||||
await context.deinit()
|
||||
localStorage.clear()
|
||||
sinon.restore()
|
||||
})
|
||||
|
||||
it('after being removed from shared vault, attempting to sync previous vault item should result in SharedVaultNotMemberError. The item should be duplicated then removed.', async () => {
|
||||
const { sharedVault, note, contactContext, deinitContactContext } =
|
||||
await Collaboration.createSharedVaultWithAcceptedInviteAndNote(context)
|
||||
@@ -81,7 +82,10 @@ describe('shared vault conflicts', function () {
|
||||
|
||||
it('attempting to modify note as read user should result in SharedVaultInsufficientPermissionsError', async () => {
|
||||
const { note, contactContext, deinitContactContext } =
|
||||
await Collaboration.createSharedVaultWithAcceptedInviteAndNote(context, SharedVaultUserPermission.PERMISSIONS.Read)
|
||||
await Collaboration.createSharedVaultWithAcceptedInviteAndNote(
|
||||
context,
|
||||
SharedVaultUserPermission.PERMISSIONS.Read,
|
||||
)
|
||||
|
||||
const promise = contactContext.resolveWithConflicts()
|
||||
await contactContext.changeNoteTitleAndSync(note, 'new title')
|
||||
|
||||
@@ -9,11 +9,6 @@ describe('contacts', function () {
|
||||
|
||||
let context
|
||||
|
||||
afterEach(async function () {
|
||||
await context.deinit()
|
||||
localStorage.clear()
|
||||
})
|
||||
|
||||
beforeEach(async function () {
|
||||
localStorage.clear()
|
||||
|
||||
@@ -23,6 +18,12 @@ describe('contacts', function () {
|
||||
await context.register()
|
||||
})
|
||||
|
||||
afterEach(async function () {
|
||||
await context.deinit()
|
||||
localStorage.clear()
|
||||
sinon.restore()
|
||||
})
|
||||
|
||||
it('should create contact', async () => {
|
||||
const contact = await context.contacts.createOrEditTrustedContact({
|
||||
name: 'John Doe',
|
||||
|
||||
@@ -9,11 +9,6 @@ describe('shared vault crypto', function () {
|
||||
|
||||
let context
|
||||
|
||||
afterEach(async function () {
|
||||
await context.deinit()
|
||||
localStorage.clear()
|
||||
})
|
||||
|
||||
beforeEach(async function () {
|
||||
localStorage.clear()
|
||||
|
||||
@@ -23,6 +18,12 @@ describe('shared vault crypto', function () {
|
||||
await context.register()
|
||||
})
|
||||
|
||||
afterEach(async function () {
|
||||
await context.deinit()
|
||||
localStorage.clear()
|
||||
sinon.restore()
|
||||
})
|
||||
|
||||
describe('root key', () => {
|
||||
it('root key loaded from disk should have keypairs', async () => {
|
||||
const appIdentifier = context.identifier
|
||||
|
||||
@@ -10,11 +10,6 @@ describe('shared vault deletion', function () {
|
||||
let context
|
||||
let sharedVaults
|
||||
|
||||
afterEach(async function () {
|
||||
await context.deinit()
|
||||
localStorage.clear()
|
||||
})
|
||||
|
||||
beforeEach(async function () {
|
||||
localStorage.clear()
|
||||
|
||||
@@ -26,6 +21,12 @@ describe('shared vault deletion', function () {
|
||||
sharedVaults = context.sharedVaults
|
||||
})
|
||||
|
||||
afterEach(async function () {
|
||||
await context.deinit()
|
||||
localStorage.clear()
|
||||
sinon.restore()
|
||||
})
|
||||
|
||||
it('should remove item from all user devices when item is deleted permanently', async () => {
|
||||
const { note, contactContext, deinitContactContext } =
|
||||
await Collaboration.createSharedVaultWithAcceptedInviteAndNote(context)
|
||||
@@ -102,7 +103,10 @@ describe('shared vault deletion', function () {
|
||||
|
||||
it('leaving a shared vault should remove its items locally', async () => {
|
||||
const { sharedVault, note, contactContext, deinitContactContext } =
|
||||
await Collaboration.createSharedVaultWithAcceptedInviteAndNote(context, SharedVaultUserPermission.PERMISSIONS.Admin)
|
||||
await Collaboration.createSharedVaultWithAcceptedInviteAndNote(
|
||||
context,
|
||||
SharedVaultUserPermission.PERMISSIONS.Admin,
|
||||
)
|
||||
|
||||
const originalNote = contactContext.items.findItem(note.uuid)
|
||||
expect(originalNote).to.not.be.undefined
|
||||
|
||||
@@ -11,11 +11,6 @@ describe('shared vault files', function () {
|
||||
let context
|
||||
let vaults
|
||||
|
||||
afterEach(async function () {
|
||||
await context.deinit()
|
||||
localStorage.clear()
|
||||
})
|
||||
|
||||
beforeEach(async function () {
|
||||
localStorage.clear()
|
||||
|
||||
@@ -28,6 +23,12 @@ describe('shared vault files', function () {
|
||||
await context.activatePaidSubscriptionForUser()
|
||||
})
|
||||
|
||||
afterEach(async function () {
|
||||
await context.deinit()
|
||||
localStorage.clear()
|
||||
sinon.restore()
|
||||
})
|
||||
|
||||
describe('private vaults', () => {
|
||||
it('should be able to upload and download file to private vault as owner', async () => {
|
||||
const vault = await Collaboration.createPrivateVault(context)
|
||||
|
||||
@@ -9,11 +9,6 @@ describe.skip('vault importing', function () {
|
||||
|
||||
let context
|
||||
|
||||
afterEach(async function () {
|
||||
await context.deinit()
|
||||
localStorage.clear()
|
||||
})
|
||||
|
||||
beforeEach(async function () {
|
||||
localStorage.clear()
|
||||
|
||||
@@ -23,6 +18,12 @@ describe.skip('vault importing', function () {
|
||||
await context.register()
|
||||
})
|
||||
|
||||
afterEach(async function () {
|
||||
await context.deinit()
|
||||
localStorage.clear()
|
||||
sinon.restore()
|
||||
})
|
||||
|
||||
it('should import vaulted items with synced root key', async () => {
|
||||
console.error('TODO: implement')
|
||||
})
|
||||
|
||||
@@ -9,11 +9,6 @@ describe('shared vault invites', function () {
|
||||
|
||||
let context
|
||||
|
||||
afterEach(async function () {
|
||||
await context.deinit()
|
||||
localStorage.clear()
|
||||
})
|
||||
|
||||
beforeEach(async function () {
|
||||
localStorage.clear()
|
||||
|
||||
@@ -22,6 +17,12 @@ describe('shared vault invites', function () {
|
||||
await context.register()
|
||||
})
|
||||
|
||||
afterEach(async function () {
|
||||
await context.deinit()
|
||||
localStorage.clear()
|
||||
sinon.restore()
|
||||
})
|
||||
|
||||
it('should invite contact to vault', async () => {
|
||||
const sharedVault = await Collaboration.createSharedVault(context)
|
||||
const { contactContext, deinitContactContext } = await Collaboration.createContactContext()
|
||||
|
||||
@@ -9,11 +9,6 @@ describe('shared vault items', function () {
|
||||
|
||||
let context
|
||||
|
||||
afterEach(async function () {
|
||||
await context.deinit()
|
||||
localStorage.clear()
|
||||
})
|
||||
|
||||
beforeEach(async function () {
|
||||
localStorage.clear()
|
||||
|
||||
@@ -23,6 +18,12 @@ describe('shared vault items', function () {
|
||||
await context.register()
|
||||
})
|
||||
|
||||
afterEach(async function () {
|
||||
await context.deinit()
|
||||
localStorage.clear()
|
||||
sinon.restore()
|
||||
})
|
||||
|
||||
it('should add item to shared vault with no other members', async () => {
|
||||
const note = await context.createSyncedNote('foo', 'bar')
|
||||
|
||||
|
||||
@@ -8,11 +8,6 @@ describe('vault key management', function () {
|
||||
|
||||
let context
|
||||
|
||||
afterEach(async function () {
|
||||
await context.deinit()
|
||||
localStorage.clear()
|
||||
})
|
||||
|
||||
beforeEach(async function () {
|
||||
localStorage.clear()
|
||||
|
||||
@@ -21,6 +16,12 @@ describe('vault key management', function () {
|
||||
await context.launch()
|
||||
})
|
||||
|
||||
afterEach(async function () {
|
||||
await context.deinit()
|
||||
localStorage.clear()
|
||||
sinon.restore()
|
||||
})
|
||||
|
||||
describe('locking', () => {
|
||||
it('should throw if attempting to add item to locked vault', async () => {
|
||||
const vault = await context.vaults.createUserInputtedPasswordVault({
|
||||
|
||||
@@ -9,11 +9,6 @@ describe('vault key rotation', function () {
|
||||
|
||||
let context
|
||||
|
||||
afterEach(async function () {
|
||||
await context.deinit()
|
||||
localStorage.clear()
|
||||
})
|
||||
|
||||
beforeEach(async function () {
|
||||
localStorage.clear()
|
||||
|
||||
@@ -23,6 +18,12 @@ describe('vault key rotation', function () {
|
||||
await context.register()
|
||||
})
|
||||
|
||||
afterEach(async function () {
|
||||
await context.deinit()
|
||||
localStorage.clear()
|
||||
sinon.restore()
|
||||
})
|
||||
|
||||
it('should reencrypt all items keys belonging to key system', async () => {
|
||||
const { sharedVault, contactContext, deinitContactContext } =
|
||||
await Collaboration.createSharedVaultWithAcceptedInvite(context)
|
||||
|
||||
@@ -9,11 +9,6 @@ describe('vault key sharing', function () {
|
||||
|
||||
let context
|
||||
|
||||
afterEach(async function () {
|
||||
await context.deinit()
|
||||
localStorage.clear()
|
||||
})
|
||||
|
||||
beforeEach(async function () {
|
||||
localStorage.clear()
|
||||
|
||||
@@ -23,6 +18,12 @@ describe('vault key sharing', function () {
|
||||
await context.register()
|
||||
})
|
||||
|
||||
afterEach(async function () {
|
||||
await context.deinit()
|
||||
localStorage.clear()
|
||||
sinon.restore()
|
||||
})
|
||||
|
||||
it('sharing a vault with user inputted and ephemeral password should share the key as synced for the recipient', async () => {
|
||||
const privateVault = await context.vaults.createUserInputtedPasswordVault({
|
||||
name: 'My Private Vault',
|
||||
|
||||
@@ -9,11 +9,6 @@ describe('keypair change', function () {
|
||||
|
||||
let context
|
||||
|
||||
afterEach(async function () {
|
||||
await context.deinit()
|
||||
localStorage.clear()
|
||||
})
|
||||
|
||||
beforeEach(async function () {
|
||||
localStorage.clear()
|
||||
|
||||
@@ -23,6 +18,12 @@ describe('keypair change', function () {
|
||||
await context.register()
|
||||
})
|
||||
|
||||
afterEach(async function () {
|
||||
await context.deinit()
|
||||
localStorage.clear()
|
||||
sinon.restore()
|
||||
})
|
||||
|
||||
it('contacts should be able to handle receiving multiple keypair changed messages and trust them in order', async () => {
|
||||
const { note, contactContext, deinitContactContext } =
|
||||
await Collaboration.createSharedVaultWithAcceptedInviteAndNote(context)
|
||||
|
||||
@@ -9,11 +9,6 @@ describe('shared vault permissions', function () {
|
||||
|
||||
let context
|
||||
|
||||
afterEach(async function () {
|
||||
await context.deinit()
|
||||
localStorage.clear()
|
||||
})
|
||||
|
||||
beforeEach(async function () {
|
||||
localStorage.clear()
|
||||
|
||||
@@ -23,6 +18,12 @@ describe('shared vault permissions', function () {
|
||||
await context.register()
|
||||
})
|
||||
|
||||
afterEach(async function () {
|
||||
await context.deinit()
|
||||
localStorage.clear()
|
||||
sinon.restore()
|
||||
})
|
||||
|
||||
it('non-admin user should not be able to invite user', async () => {
|
||||
context.anticipateConsoleError('Could not create invite')
|
||||
|
||||
@@ -42,6 +43,8 @@ describe('shared vault permissions', function () {
|
||||
|
||||
expect(result.isFailed()).to.be.true
|
||||
|
||||
await thirdParty.deinitContactContext()
|
||||
|
||||
await deinitContactContext()
|
||||
})
|
||||
|
||||
|
||||
@@ -7,24 +7,23 @@ describe('public key cryptography', function () {
|
||||
this.timeout(Factory.TwentySecondTimeout)
|
||||
|
||||
let context
|
||||
let sessions
|
||||
let encryption
|
||||
|
||||
afterEach(async function () {
|
||||
await context.deinit()
|
||||
localStorage.clear()
|
||||
})
|
||||
|
||||
beforeEach(async function () {
|
||||
beforeEach(async () => {
|
||||
localStorage.clear()
|
||||
|
||||
context = await Factory.createVaultsContextWithRealCrypto()
|
||||
|
||||
await context.launch()
|
||||
await context.register()
|
||||
})
|
||||
|
||||
sessions = context.application.sessions
|
||||
encryption = context.encryption
|
||||
afterEach(async () => {
|
||||
await context.deinit()
|
||||
localStorage.clear()
|
||||
|
||||
sinon.restore()
|
||||
|
||||
context = undefined
|
||||
})
|
||||
|
||||
it('should create keypair during registration', () => {
|
||||
@@ -38,7 +37,7 @@ describe('public key cryptography', function () {
|
||||
it('should populate keypair during sign in', async () => {
|
||||
const email = context.email
|
||||
const password = context.password
|
||||
await context.signout()
|
||||
await context.deinit()
|
||||
|
||||
const recreatedContext = await Factory.createVaultsContextWithRealCrypto()
|
||||
await recreatedContext.launch()
|
||||
|
||||
@@ -24,6 +24,8 @@ describe('shared vaults', function () {
|
||||
afterEach(async function () {
|
||||
await context.deinit()
|
||||
localStorage.clear()
|
||||
sinon.restore()
|
||||
context = undefined
|
||||
})
|
||||
|
||||
it('should update vault name and description', async () => {
|
||||
|
||||
@@ -9,11 +9,6 @@ describe('signatures', function () {
|
||||
|
||||
let context
|
||||
|
||||
afterEach(async function () {
|
||||
await context.deinit()
|
||||
localStorage.clear()
|
||||
})
|
||||
|
||||
beforeEach(async function () {
|
||||
localStorage.clear()
|
||||
|
||||
@@ -23,6 +18,13 @@ describe('signatures', function () {
|
||||
await context.register()
|
||||
})
|
||||
|
||||
afterEach(async function () {
|
||||
await context.deinit()
|
||||
localStorage.clear()
|
||||
sinon.restore()
|
||||
context = undefined
|
||||
})
|
||||
|
||||
describe('item decryption signature verification', () => {
|
||||
it('should have failing signature if contact public key does not match', async () => {
|
||||
const { note, contactContext, deinitContactContext } =
|
||||
|
||||
@@ -9,21 +9,24 @@ describe('vaults', function () {
|
||||
let context
|
||||
let vaults
|
||||
|
||||
afterEach(async function () {
|
||||
await context.deinit()
|
||||
localStorage.clear()
|
||||
})
|
||||
|
||||
beforeEach(async function () {
|
||||
localStorage.clear()
|
||||
|
||||
context = await Factory.createVaultsContextWithRealCrypto()
|
||||
context = await Factory.createVaultsContextWithFakeCrypto()
|
||||
|
||||
await context.launch()
|
||||
|
||||
vaults = context.vaults
|
||||
})
|
||||
|
||||
afterEach(async function () {
|
||||
await context.deinit()
|
||||
localStorage.clear()
|
||||
sinon.restore()
|
||||
context = undefined
|
||||
vaults = undefined
|
||||
})
|
||||
|
||||
describe('offline', function () {
|
||||
it('should be able to create an offline vault', async () => {
|
||||
const vault = await vaults.createRandomizedVault({
|
||||
@@ -77,7 +80,7 @@ describe('vaults', function () {
|
||||
await vaults.moveItemToVault(vault, note)
|
||||
await context.deinit()
|
||||
|
||||
const recreatedContext = await Factory.createVaultsContextWithRealCrypto(appIdentifier)
|
||||
const recreatedContext = await Factory.createVaultsContextWithFakeCrypto(appIdentifier)
|
||||
await recreatedContext.launch()
|
||||
|
||||
const updatedNote = recreatedContext.items.findItem(note.uuid)
|
||||
@@ -101,7 +104,7 @@ describe('vaults', function () {
|
||||
|
||||
await context.deinit()
|
||||
|
||||
const recreatedContext = await Factory.createVaultsContextWithRealCrypto(appIdentifier)
|
||||
const recreatedContext = await Factory.createVaultsContextWithFakeCrypto(appIdentifier)
|
||||
await recreatedContext.launch()
|
||||
|
||||
const notes = recreatedContext.notes
|
||||
@@ -128,7 +131,7 @@ describe('vaults', function () {
|
||||
|
||||
await context.deinit()
|
||||
|
||||
const recreatedContext = await Factory.createVaultsContextWithRealCrypto(appIdentifier)
|
||||
const recreatedContext = await Factory.createVaultsContextWithFakeCrypto(appIdentifier)
|
||||
await recreatedContext.launch()
|
||||
|
||||
const updatedNote = recreatedContext.items.findItem(note.uuid)
|
||||
@@ -181,7 +184,7 @@ describe('vaults', function () {
|
||||
await vaults.moveItemToVault(vault, note)
|
||||
await context.deinit()
|
||||
|
||||
const recreatedContext = await Factory.createVaultsContextWithRealCrypto(appIdentifier)
|
||||
const recreatedContext = await Factory.createVaultsContextWithFakeCrypto(appIdentifier)
|
||||
await recreatedContext.launch()
|
||||
|
||||
const updatedNote = recreatedContext.items.findItem(note.uuid)
|
||||
|
||||
Reference in New Issue
Block a user