Compare commits

...
Author SHA1 Message Date
StandardNotes CI 4ca0e89fcd chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].4
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].5
 - @standardnotes/[email protected]
2023-08-07 21:41:33 +00:00
Mo b731c73cb2 chore: fix dependency events 2023-08-07 15:58:46 -05:00
Mo 3b531ce8bb tests: vault locking (#2394) 2023-08-07 15:27:06 -05:00
StandardNotes CI a9edf671b1 chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
2023-08-07 18:52:26 +00:00
Aman Harwara 9f50ad28b0 chore: vaults improvements 2023-08-07 23:40:33 +05:30
StandardNotes CI bff2fb624d chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].9
2023-08-07 17:34:55 +00:00
Mo 0f1c8ef030 chore: update tests 2023-08-07 11:53:00 -05:00
Aman Harwara 653af36363 chore: fix empty table nodes when copying from external sources in super 2023-08-07 22:06:13 +05:30
Karol Sójko 5b88deeddb chore: safe-guard UI on shared vault limit creation (#2392) 2023-08-07 17:53:56 +02:00
StandardNotes CI 022be7d792 chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].8
2023-08-07 14:56:02 +00:00
Karol Sójko 695d37beaa chore: fix existing e2e test for shared vaults to consider limitations (#2393)
* chore: fix existing e2e test for shared vaults to consider limitations

* chore: add paid subscription on the multiple vaults test
2023-08-07 16:11:20 +02:00
StandardNotes CI b08cff4dc2 chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].7
2023-08-07 13:49:57 +00:00
Aman Harwara 1585d6b8ca chore: vaults improvements 2023-08-07 18:39:04 +05:30
StandardNotes CI 0a51034b80 chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].6
2023-08-07 12:56:50 +00:00
Aman Harwara e253da6333 chore: release [skip e2e] 2023-08-07 18:07:42 +05:30
Aman Harwara 8a029950ec chore: fix issue where tag could not be added to vault 2023-08-07 17:53:28 +05:30
StandardNotes CI f0b3d939c5 chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
2023-08-07 12:11:39 +00:00
Aman Harwara a87d3c71f3 chore: Fixed issue where app would not launch correctly when using AppImage on Linux 2023-08-07 16:54:24 +05:30
StandardNotes CI e4258a3e12 chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].4
 - @standardnotes/[email protected].5
2023-08-07 10:52:11 +00:00
Aman Harwara e2215f9a1f chore: minor vaults ui improvements 2023-08-07 15:36:34 +05:30
Mo ee473fe34d chore: fix uuid generation for importers 2023-08-07 05:06:11 -05:00
Karol Sójko cedd8e1006 chore: add e2e tests covering invalidation of other sessions (#2385) 2023-08-07 05:01:04 -05:00
Karol Sójko 4b278c4193 chore: fix session modal to show session creation date instead of update date (#2390) 2023-08-07 11:44:53 +02:00
StandardNotes CI 52ef2a8a70 chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].8
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].4
2023-08-06 21:04:39 +00:00
Mo 8655bdb5dd tests: fix memory leaks (#2389) 2023-08-06 15:23:31 -05:00
StandardNotes CI d59e1befff chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].3
2023-08-06 19:40:57 +00:00
Aman Harwara a295b4f2f7 chore: fix style issues [skip e2e] 2023-08-07 00:54:00 +05:30
176 changed files with 5317 additions and 4541 deletions
+4
View File
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.26.45](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-08-07)
**Note:** Version bump only for package @standardnotes/api
## [1.26.44](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-08-06)
**Note:** Version bump only for package @standardnotes/api
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/api",
"version": "1.26.44",
"version": "1.26.45",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
+36
View File
@@ -3,6 +3,42 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.1.153](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-08-07)
**Note:** Version bump only for package @standardnotes/clipper
## [1.1.152](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-08-07)
**Note:** Version bump only for package @standardnotes/clipper
## [1.1.151](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-08-07)
**Note:** Version bump only for package @standardnotes/clipper
## [1.1.150](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-08-07)
**Note:** Version bump only for package @standardnotes/clipper
## [1.1.149](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-08-07)
**Note:** Version bump only for package @standardnotes/clipper
## [1.1.148](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-08-07)
**Note:** Version bump only for package @standardnotes/clipper
## [1.1.147](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-08-07)
**Note:** Version bump only for package @standardnotes/clipper
## [1.1.146](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-08-06)
**Note:** Version bump only for package @standardnotes/clipper
## [1.1.145](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-08-06)
**Note:** Version bump only for package @standardnotes/clipper
## [1.1.144](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-08-06)
**Note:** Version bump only for package @standardnotes/clipper
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@standardnotes/clipper",
"description": "Web clipper browser extension for Standard Notes",
"version": "1.1.144",
"version": "1.1.153",
"private": true,
"scripts": {
"build-mv2": "yarn clean && webpack --config ./webpack.config.prod.js",
+40
View File
@@ -3,6 +3,46 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [3.108.85](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-08-07)
**Note:** Version bump only for package @standardnotes/desktop
## [3.108.84](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-08-07)
**Note:** Version bump only for package @standardnotes/desktop
## [3.108.83](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-08-07)
**Note:** Version bump only for package @standardnotes/desktop
## [3.108.82](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-08-07)
**Note:** Version bump only for package @standardnotes/desktop
## [3.108.81](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-08-07)
**Note:** Version bump only for package @standardnotes/desktop
## [3.108.80](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-08-07)
**Note:** Version bump only for package @standardnotes/desktop
## [3.108.79](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-08-07)
**Note:** Version bump only for package @standardnotes/desktop
## [3.108.78](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-08-07)
**Note:** Version bump only for package @standardnotes/desktop
## [3.108.77](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-08-06)
**Note:** Version bump only for package @standardnotes/desktop
## [3.108.76](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-08-06)
**Note:** Version bump only for package @standardnotes/desktop
## [3.108.75](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-08-06)
**Note:** Version bump only for package @standardnotes/desktop
@@ -6,7 +6,9 @@ export function initializeZoomManager(window: BrowserWindow, store: Store): void
window.webContents.on('dom-ready', () => {
const zoomFactor = store.get(StoreKeys.ZoomFactor)
if (zoomFactor) {
window.webContents.zoomFactor = zoomFactor
window.once('ready-to-show', () => {
window.webContents.zoomFactor = zoomFactor
})
}
})
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@standardnotes/desktop",
"main": "./app/dist/index.js",
"version": "3.108.75",
"version": "3.108.85",
"license": "CC BY-NC-SA 4.0",
"author": "Standard Notes.",
"private": true,
+4
View File
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.21.67](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-08-07)
**Note:** Version bump only for package @standardnotes/encryption
## [1.21.66](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-08-06)
**Note:** Version bump only for package @standardnotes/encryption
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/encryption",
"version": "1.21.66",
"version": "1.21.67",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
+4
View File
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.28.77](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-08-07)
**Note:** Version bump only for package @standardnotes/filepicker
## [1.28.76](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-08-06)
**Note:** Version bump only for package @standardnotes/filepicker
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/filepicker",
"version": "1.28.76",
"version": "1.28.77",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
+4
View File
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.16.23](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-08-07)
**Note:** Version bump only for package @standardnotes/files
## [1.16.22](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-08-06)
**Note:** Version bump only for package @standardnotes/files
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/files",
"version": "1.16.22",
"version": "1.16.23",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
+36
View File
@@ -3,6 +3,42 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [3.56.64](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-08-07)
**Note:** Version bump only for package @standardnotes/mobile
## [3.56.63](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-08-07)
**Note:** Version bump only for package @standardnotes/mobile
## [3.56.62](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-08-07)
**Note:** Version bump only for package @standardnotes/mobile
## [3.56.61](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-08-07)
**Note:** Version bump only for package @standardnotes/mobile
## [3.56.60](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-08-07)
**Note:** Version bump only for package @standardnotes/mobile
## [3.56.59](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-08-07)
**Note:** Version bump only for package @standardnotes/mobile
## [3.56.58](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-08-07)
**Note:** Version bump only for package @standardnotes/mobile
## [3.56.57](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-08-06)
**Note:** Version bump only for package @standardnotes/mobile
## [3.56.56](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-08-06)
**Note:** Version bump only for package @standardnotes/mobile
## [3.56.55](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-08-06)
**Note:** Version bump only for package @standardnotes/mobile
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/mobile",
"version": "3.56.55",
"version": "3.56.64",
"author": "Standard Notes.",
"private": true,
"license": "CC BY-NC-SA 4.0",
+4
View File
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.47.4](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-08-07)
**Note:** Version bump only for package @standardnotes/models
## [1.47.3](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-08-06)
**Note:** Version bump only for package @standardnotes/models
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/models",
"version": "1.47.3",
"version": "1.47.4",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
@@ -52,4 +52,8 @@ export class KeySystemRootKey extends DecryptedItem<KeySystemRootKeyContent> imp
override get shared_vault_uuid(): undefined {
return undefined
}
isEqual(other: KeySystemRootKeyInterface): boolean {
return this.itemsKey === other.itemsKey && this.token === other.token
}
}
@@ -35,4 +35,6 @@ export interface KeySystemRootKeyInterface extends DecryptedItemInterface<KeySys
*/
get key_system_identifier(): undefined
get shared_vault_uuid(): undefined
isEqual(other: KeySystemRootKeyInterface): boolean
}
+40
View File
@@ -3,6 +3,46 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.4.437](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-08-07)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.436](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-08-07)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.435](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-08-07)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.434](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-08-07)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.433](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-08-07)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.432](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-08-07)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.431](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-08-07)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.430](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-08-07)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.429](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-08-06)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.428](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-08-06)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.427](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-08-06)
**Note:** Version bump only for package @standardnotes/releases
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/releases",
"version": "1.4.427",
"version": "1.4.437",
"license": "CC BY-NC-SA 4.0",
"main": "dist/releases.json",
"types": "dist/index.d.ts",
+8
View File
@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.63.29](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-08-07)
**Note:** Version bump only for package @standardnotes/services
## [1.63.28](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-08-07)
**Note:** Version bump only for package @standardnotes/services
## [1.63.27](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-08-06)
**Note:** Version bump only for package @standardnotes/services
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/services",
"version": "1.63.27",
"version": "1.63.29",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
@@ -11,6 +11,7 @@ import {
LegacyApiServiceInterface,
StatusServiceInterface,
MfaServiceInterface,
GenerateUuid,
} from '@standardnotes/services'
import { VaultLockServiceInterface } from './../VaultLock/VaultLockServiceInterface'
import { HistoryServiceInterface } from './../History/HistoryServiceInterface'
@@ -85,6 +86,7 @@ export interface ApplicationInterface {
get files(): FilesClientInterface
get history(): HistoryServiceInterface
get homeServer(): HomeServerServiceInterface | undefined
get generateUuid(): GenerateUuid
get items(): ItemManagerInterface
get legacyApi(): LegacyApiServiceInterface
get mfa(): MfaServiceInterface
@@ -10,7 +10,6 @@ import {
SNTag,
ItemInterface,
AnyItemInterface,
KeySystemIdentifier,
ItemCollection,
SNNote,
SmartView,
@@ -107,7 +106,6 @@ export interface ItemManagerInterface extends AbstractService {
findItemsIncludingBlanks<T extends DecryptedItemInterface>(uuids: string[]): (T | undefined)[]
get trashedItems(): SNNote[]
itemsBelongingToKeySystem(systemIdentifier: KeySystemIdentifier): DecryptedItemInterface[]
hasTagsNeedingFoldersMigration(): boolean
get invalidNonVaultedItems(): EncryptedItemInterface[]
isTemplateItem(item: DecryptedItemInterface): boolean
@@ -0,0 +1,222 @@
import { ItemsEncryptionService } from './ItemsEncryption'
import { ItemManagerInterface } from '../Item/ItemManagerInterface'
import { PayloadManagerInterface } from '../Payloads/PayloadManagerInterface'
import { StorageServiceInterface } from '../Storage/StorageServiceInterface'
import {
DecryptedParameters,
EncryptionOperatorsInterface,
KeySystemItemsKey,
StandardException,
} from '@standardnotes/encryption'
import { KeySystemKeyManagerInterface } from '../KeySystem/KeySystemKeyManagerInterface'
import { FindDefaultItemsKey } from './../Encryption/UseCase/ItemsKey/FindDefaultItemsKey'
import { InternalEventBusInterface } from '../Internal/InternalEventBusInterface'
import { EncryptedOutputParameters } from '@standardnotes/encryption'
import { PkcKeyPair } from '@standardnotes/sncrypto-common'
import {
DecryptedPayloadInterface,
EncryptedPayloadInterface,
ItemsKeyInterface,
KeySystemItemsKeyInterface,
} from '@standardnotes/models'
describe('ItemsEncryptionService', () => {
let itemsEncryptionService: ItemsEncryptionService
let mockItems: jest.Mocked<ItemManagerInterface>
let mockPayloads: jest.Mocked<PayloadManagerInterface>
let mockStorage: jest.Mocked<StorageServiceInterface>
let mockOperators: jest.Mocked<EncryptionOperatorsInterface>
let mockKeys: jest.Mocked<KeySystemKeyManagerInterface>
let mockFindDefaultItemsKey: jest.Mocked<FindDefaultItemsKey>
let mockInternalEventBus: jest.Mocked<InternalEventBusInterface>
beforeEach(() => {
mockItems = {
addObserver: jest.fn(),
} as unknown as jest.Mocked<ItemManagerInterface>
mockPayloads = {} as jest.Mocked<PayloadManagerInterface>
mockStorage = {} as jest.Mocked<StorageServiceInterface>
mockOperators = {} as jest.Mocked<EncryptionOperatorsInterface>
mockKeys = {} as jest.Mocked<KeySystemKeyManagerInterface>
mockFindDefaultItemsKey = {} as jest.Mocked<FindDefaultItemsKey>
mockInternalEventBus = {} as jest.Mocked<InternalEventBusInterface>
itemsEncryptionService = new ItemsEncryptionService(
mockItems,
mockPayloads,
mockStorage,
mockOperators,
mockKeys,
mockFindDefaultItemsKey,
mockInternalEventBus,
)
})
describe('decryptPayloadWithKeyLookup', () => {
it('returns decrypted parameters when a key is found', async () => {
const mockPayload = {
uuid: 'payload-uuid',
} as EncryptedPayloadInterface
const mockKey = {
uuid: 'key-uuid',
} as KeySystemItemsKey
const mockDecryptedParameters = {
uuid: 'decrypted-uuid',
content: {},
} as DecryptedParameters
itemsEncryptionService.keyToUseForDecryptionOfPayload = jest.fn().mockReturnValue(mockKey)
itemsEncryptionService.decryptPayload = jest.fn().mockResolvedValue(mockDecryptedParameters)
const result = await itemsEncryptionService.decryptPayloadWithKeyLookup(mockPayload)
expect(itemsEncryptionService.keyToUseForDecryptionOfPayload).toHaveBeenCalledWith(mockPayload)
expect(itemsEncryptionService.decryptPayload).toHaveBeenCalledWith(mockPayload, mockKey)
expect(result).toEqual(mockDecryptedParameters)
})
it('returns error parameters when no key is found', async () => {
const mockPayload = {
uuid: 'payload-uuid',
} as EncryptedPayloadInterface
itemsEncryptionService.keyToUseForDecryptionOfPayload = jest.fn().mockReturnValue(undefined)
const result = await itemsEncryptionService.decryptPayloadWithKeyLookup(mockPayload)
expect(itemsEncryptionService.keyToUseForDecryptionOfPayload).toHaveBeenCalledWith(mockPayload)
expect(result).toEqual({
uuid: mockPayload.uuid,
errorDecrypting: true,
waitingForKey: true,
})
})
})
describe('keyToUseForDecryptionOfPayload', () => {
it('returns itemsKey when payload has items_key_id', () => {
const mockPayload: EncryptedPayloadInterface = {
items_key_id: 'test-key-id',
} as EncryptedPayloadInterface
const mockItemsKey: ItemsKeyInterface = {} as ItemsKeyInterface
itemsEncryptionService.itemsKeyForEncryptedPayload = jest.fn().mockReturnValue(mockItemsKey)
const result = itemsEncryptionService.keyToUseForDecryptionOfPayload(mockPayload)
expect(itemsEncryptionService.itemsKeyForEncryptedPayload).toHaveBeenCalledWith(mockPayload)
expect(result).toBe(mockItemsKey)
})
it('returns defaultKey when payload does not have items_key_id', () => {
const mockPayload: EncryptedPayloadInterface = {} as EncryptedPayloadInterface
const mockDefaultKey: KeySystemItemsKeyInterface = {} as KeySystemItemsKeyInterface
itemsEncryptionService.defaultItemsKeyForItemVersion = jest.fn().mockReturnValue(mockDefaultKey)
const result = itemsEncryptionService.keyToUseForDecryptionOfPayload(mockPayload)
expect(itemsEncryptionService.defaultItemsKeyForItemVersion).toHaveBeenCalledWith(mockPayload.version)
expect(result).toBe(mockDefaultKey)
})
})
describe('encryptPayloadWithKeyLookup', () => {
it('throws an error when keyToUseForItemEncryption returns an instance of StandardException', async () => {
const mockPayload: DecryptedPayloadInterface = {} as DecryptedPayloadInterface
const mockError: StandardException = new StandardException('test-error')
itemsEncryptionService.keyToUseForItemEncryption = jest.fn().mockReturnValue(mockError)
await expect(() => itemsEncryptionService.encryptPayloadWithKeyLookup(mockPayload)).rejects.toThrow('test-error')
})
it('encrypts the payload using the provided key', async () => {
const mockPayload: DecryptedPayloadInterface = {} as DecryptedPayloadInterface
const mockKey: ItemsKeyInterface = {} as ItemsKeyInterface
const encryptedOutputParameters: EncryptedOutputParameters = {
content: 'encrypted-content',
} as EncryptedOutputParameters
itemsEncryptionService.keyToUseForItemEncryption = jest.fn().mockReturnValue(mockKey)
itemsEncryptionService.encryptPayload = jest.fn().mockResolvedValue(encryptedOutputParameters)
const result = await itemsEncryptionService.encryptPayloadWithKeyLookup(mockPayload)
expect(itemsEncryptionService.encryptPayload).toHaveBeenCalledWith(mockPayload, mockKey, undefined)
expect(result).toBe(encryptedOutputParameters)
})
})
describe('encryptPayload', () => {
it('throws an error when the payload has no content', async () => {
const mockPayload: DecryptedPayloadInterface = { content: null } as unknown as DecryptedPayloadInterface
const mockKey: ItemsKeyInterface = {} as ItemsKeyInterface
await expect(() => itemsEncryptionService.encryptPayload(mockPayload, mockKey)).rejects.toThrow(
'Attempting to encrypt payload with no content.',
)
})
it('throws an error when the payload has no UUID', async () => {
const mockPayload: DecryptedPayloadInterface = { uuid: null, content: {} } as unknown as DecryptedPayloadInterface
const mockKey: ItemsKeyInterface = {} as ItemsKeyInterface
await expect(() => itemsEncryptionService.encryptPayload(mockPayload, mockKey)).rejects.toThrow(
'Attempting to encrypt payload with no UuidGenerator.',
)
})
it('returns encrypted output parameters', async () => {
const mockPayload: DecryptedPayloadInterface = {
uuid: 'test-uuid',
content: 'test-content',
} as unknown as DecryptedPayloadInterface
const mockKey: ItemsKeyInterface = {} as ItemsKeyInterface
const encryptedOutputParameters: EncryptedOutputParameters = {
content: 'encrypted-content',
} as EncryptedOutputParameters
jest.spyOn(itemsEncryptionService, 'encryptPayload').mockResolvedValue(encryptedOutputParameters)
const result = await itemsEncryptionService.encryptPayload(mockPayload, mockKey)
expect(result).toBe(encryptedOutputParameters)
})
})
it('repersistAllItems', async () => {
Object.defineProperty(mockItems, 'items', {
get: jest.fn().mockReturnValue([{ payload: { uuid: '123' } }]),
})
mockStorage.savePayloads = jest.fn().mockResolvedValue(undefined)
await itemsEncryptionService.repersistAllItems()
expect(mockStorage.savePayloads).toHaveBeenCalledWith([{ uuid: '123' }])
})
it('encryptPayloadWithKeyLookup', async () => {
const mockPayload: DecryptedPayloadInterface = {
key_system_identifier: 'test-identifier',
} as DecryptedPayloadInterface
const mockKeyPair: PkcKeyPair = { publicKey: 'publicKey', privateKey: 'privateKey' }
const mockKey = { uuid: 'key-id', keyVersion: '004' } as KeySystemItemsKey
mockKeys.getPrimaryKeySystemItemsKey = jest.fn().mockReturnValue(mockKey)
itemsEncryptionService.encryptPayload = jest
.fn()
.mockResolvedValue({ content: '004:...' } as EncryptedOutputParameters)
const result = await itemsEncryptionService.encryptPayloadWithKeyLookup(mockPayload, mockKeyPair)
expect(mockKeys.getPrimaryKeySystemItemsKey).toHaveBeenCalledWith(mockPayload.key_system_identifier)
expect(itemsEncryptionService.encryptPayload).toHaveBeenCalledWith(mockPayload, mockKey, mockKeyPair)
expect(result).toEqual({ content: '004:...' })
})
})
@@ -97,7 +97,7 @@ export class ItemsEncryptionService extends AbstractService {
return this._findDefaultItemsKey.execute(this.getItemsKeys()).getValue()
}
private keyToUseForItemEncryption(
keyToUseForItemEncryption(
payload: DecryptedPayloadInterface,
): ItemsKeyInterface | KeySystemItemsKeyInterface | KeySystemRootKeyInterface | StandardException {
if (payload.key_system_identifier) {
@@ -132,7 +132,7 @@ export class ItemsEncryptionService extends AbstractService {
return result
}
private keyToUseForDecryptionOfPayload(
keyToUseForDecryptionOfPayload(
payload: EncryptedPayloadInterface,
): ItemsKeyInterface | KeySystemItemsKeyInterface | undefined {
if (payload.items_key_id) {
@@ -1,16 +1,16 @@
import { GetVaultItems } from './../../Vault/UseCase/GetVaultItems'
import { SharedVaultListingInterface, VaultListingInterface, VaultListingMutator } from '@standardnotes/models'
import { ClientDisplayableError, isErrorResponse } from '@standardnotes/responses'
import { SharedVaultServerInterface } from '@standardnotes/api'
import { ItemManagerInterface } from '../../Item/ItemManagerInterface'
import { MoveItemsToVault } from '../../Vault/UseCase/MoveItemsToVault'
import { MutatorClientInterface } from '../../Mutator/MutatorClientInterface'
export class ConvertToSharedVault {
constructor(
private items: ItemManagerInterface,
private mutator: MutatorClientInterface,
private sharedVaultServer: SharedVaultServerInterface,
private moveItemsToVault: MoveItemsToVault,
private _moveItemsToVault: MoveItemsToVault,
private _getVaultItems: GetVaultItems,
) {}
async execute(dto: { vault: VaultListingInterface }): Promise<SharedVaultListingInterface | ClientDisplayableError> {
@@ -35,9 +35,9 @@ export class ConvertToSharedVault {
},
)
const vaultItems = this.items.itemsBelongingToKeySystem(sharedVaultListing.systemIdentifier)
const vaultItems = this._getVaultItems.execute(sharedVaultListing).getValue()
await this.moveItemsToVault.execute({ vault: sharedVaultListing, items: vaultItems })
await this._moveItemsToVault.execute({ vault: sharedVaultListing, items: vaultItems })
return sharedVaultListing as SharedVaultListingInterface
}
@@ -1,3 +1,4 @@
import { GetVaultItems } from './../../Vault/UseCase/GetVaultItems'
import {
KeySystemRootKeyStorageMode,
SharedVaultListingInterface,
@@ -6,18 +7,17 @@ import {
} from '@standardnotes/models'
import { ClientDisplayableError, isErrorResponse } from '@standardnotes/responses'
import { SharedVaultServerInterface } from '@standardnotes/api'
import { ItemManagerInterface } from '../../Item/ItemManagerInterface'
import { CreateVault } from '../../Vault/UseCase/CreateVault'
import { MoveItemsToVault } from '../../Vault/UseCase/MoveItemsToVault'
import { MutatorClientInterface } from '../../Mutator/MutatorClientInterface'
export class CreateSharedVault {
constructor(
private items: ItemManagerInterface,
private mutator: MutatorClientInterface,
private sharedVaultServer: SharedVaultServerInterface,
private createVault: CreateVault,
private moveItemsToVault: MoveItemsToVault,
private _createVault: CreateVault,
private _moveItemsToVault: MoveItemsToVault,
private _getVaultItems: GetVaultItems,
) {}
async execute(dto: {
@@ -26,7 +26,7 @@ export class CreateSharedVault {
userInputtedPassword: string | undefined
storagePreference: KeySystemRootKeyStorageMode
}): Promise<SharedVaultListingInterface | ClientDisplayableError> {
const privateVault = await this.createVault.execute({
const privateVault = await this._createVault.execute({
vaultName: dto.vaultName,
vaultDescription: dto.vaultDescription,
userInputtedPassword: dto.userInputtedPassword,
@@ -50,9 +50,9 @@ export class CreateSharedVault {
},
)
const vaultItems = this.items.itemsBelongingToKeySystem(sharedVaultListing.systemIdentifier)
const vaultItems = this._getVaultItems.execute(sharedVaultListing).getValue()
await this.moveItemsToVault.execute({ vault: sharedVaultListing, items: vaultItems })
await this._moveItemsToVault.execute({ vault: sharedVaultListing, items: vaultItems })
return sharedVaultListing as SharedVaultListingInterface
}
@@ -13,11 +13,7 @@ export class GetOwnedSharedVaults implements SyncUseCaseInterface<SharedVaultLis
const sharedVaults = this._getSharedVaults.execute().getValue()
const ownedVaults = sharedVaults.filter((vault) => {
return this._isVaultOwnwer
.execute({
sharedVault: vault,
})
.getValue()
return this._isVaultOwnwer.execute(vault).getValue()
})
return Result.ok(ownedVaults)
@@ -28,7 +28,7 @@ export class SendVaultDataChangedMessage implements UseCaseInterface<void> {
) {}
async execute(params: { vault: SharedVaultListingInterface }): Promise<Result<void>> {
const isOwner = this._isVaultOwner.execute({ sharedVault: params.vault }).getValue()
const isOwner = this._isVaultOwner.execute(params.vault).getValue()
if (!isOwner) {
return Result.ok()
}
@@ -37,10 +37,6 @@ export class SubscriptionManager
protected override internalEventBus: InternalEventBusInterface,
) {
super(internalEventBus)
internalEventBus.addEventHandler(this, ApplicationEvent.UserRolesChanged)
internalEventBus.addEventHandler(this, ApplicationEvent.Launched)
internalEventBus.addEventHandler(this, ApplicationEvent.SignedIn)
}
async handleEvent(event: InternalEventInterface): Promise<void> {
@@ -0,0 +1,12 @@
import { Result, SyncUseCaseInterface } from '@standardnotes/domain-core'
import { PureCryptoInterface } from '@standardnotes/sncrypto-common'
export class GenerateUuid implements SyncUseCaseInterface<string> {
constructor(private crypto: PureCryptoInterface) {}
execute(): Result<string> {
const uuid = this.crypto.generateUUID()
return Result.ok(uuid)
}
}
@@ -15,8 +15,6 @@ export class NotificationService
constructor(internalEventBus: InternalEventBusInterface) {
super(internalEventBus)
internalEventBus.addEventHandler(this, SyncEvent.ReceivedNotifications)
}
async handleEvent(event: InternalEventInterface): Promise<void> {
@@ -1,14 +1,14 @@
import { ClientDisplayableError } from '@standardnotes/responses'
import { ItemManagerInterface } from '../../Item/ItemManagerInterface'
import { VaultListingInterface } from '@standardnotes/models'
import { MutatorClientInterface } from '../../Mutator/MutatorClientInterface'
import { KeySystemKeyManagerInterface } from '../../KeySystem/KeySystemKeyManagerInterface'
import { GetVaultItems } from './GetVaultItems'
export class DeleteVault {
constructor(
private items: ItemManagerInterface,
private mutator: MutatorClientInterface,
private keys: KeySystemKeyManagerInterface,
private _getVaultItems: GetVaultItems,
) {}
async execute(vault: VaultListingInterface): Promise<ClientDisplayableError | void> {
@@ -24,7 +24,7 @@ export class DeleteVault {
const itemsKeys = this.keys.getKeySystemItemsKeys(vault.systemIdentifier)
await this.mutator.setItemsToBeDeleted(itemsKeys)
const vaultItems = this.items.itemsBelongingToKeySystem(vault.systemIdentifier)
const vaultItems = this._getVaultItems.execute(vault).getValue()
await this.mutator.setItemsToBeDeleted(vaultItems)
await this.mutator.setItemToBeDeleted(vault)
@@ -0,0 +1,11 @@
import { Result, SyncUseCaseInterface } from '@standardnotes/domain-core'
import { DecryptedItemInterface, ItemContent, VaultListingInterface } from '@standardnotes/models'
import { ItemManagerInterface } from '../../Item/ItemManagerInterface'
export class GetVaultItems implements SyncUseCaseInterface<DecryptedItemInterface[]> {
constructor(private items: ItemManagerInterface) {}
execute(vault: VaultListingInterface): Result<DecryptedItemInterface<ItemContent>[]> {
return Result.ok(this.items.items.filter((item) => item.key_system_identifier === vault.systemIdentifier))
}
}
@@ -114,7 +114,7 @@ export class RotateVaultKey implements UseCaseInterface<VaultListingInterface> {
return Result.ok()
}
const isOwner = this._isVaultOwner.execute({ sharedVault: params.vault }).getValue()
const isOwner = this._isVaultOwner.execute(params.vault).getValue()
if (!isOwner) {
return Result.ok()
@@ -1,3 +1,5 @@
import { ValidateVaultPassword } from './../VaultLock/UseCase/ValidateVaultPassword'
import { IsVaultOwner } from './../VaultUser/UseCase/IsVaultOwner'
import { SendVaultDataChangedMessage } from './../SharedVaults/UseCase/SendVaultDataChangedMessage'
import { isClientDisplayableError } from '@standardnotes/responses'
import {
@@ -5,6 +7,7 @@ import {
FileItem,
KeySystemIdentifier,
KeySystemRootKeyStorageMode,
SharedVaultListingInterface,
VaultListingInterface,
VaultListingMutator,
isNote,
@@ -48,6 +51,8 @@ export class VaultService
private _deleteVault: DeleteVault,
private _rotateVaultKey: RotateVaultKey,
private _sendVaultDataChangeMessage: SendVaultDataChangedMessage,
private _isVaultOwner: IsVaultOwner,
private _validateVaultPassword: ValidateVaultPassword,
eventBus: InternalEventBusInterface,
) {
super(eventBus)
@@ -238,8 +243,39 @@ export class VaultService
throw new Error('Attempting to change vault options on a locked vault')
}
if (!this._isVaultOwner.execute(dto.vault).getValue()) {
throw new Error('Third party vault options should be changed via changeThirdPartyVaultStorageOptions')
}
const result = await this._changeVaultKeyOptions.execute(dto)
return result
}
async changeThirdPartyVaultStorageOptions(dto: {
vault: SharedVaultListingInterface
newStorageMode: KeySystemRootKeyStorageMode | undefined
vaultPassword: string
}): Promise<Result<void>> {
if (this.vaultLocks.isVaultLocked(dto.vault)) {
throw new Error('Attempting to change vault options on a locked vault')
}
if (this._isVaultOwner.execute(dto.vault).getValue()) {
throw new Error('First party vault options should be changed via changeVaultKeyOptions')
}
const validPassword = this._validateVaultPassword.execute(dto.vault, dto.vaultPassword).getValue()
if (!validPassword) {
return Result.fail('Invalid vault password')
}
const result = await this._changeVaultKeyOptions.execute({
vault: dto.vault,
newStorageMode: dto.newStorageMode,
newPasswordOptions: undefined,
})
return result
}
}
@@ -2,6 +2,7 @@ import {
DecryptedItemInterface,
KeySystemIdentifier,
KeySystemRootKeyStorageMode,
SharedVaultListingInterface,
VaultListingInterface,
} from '@standardnotes/models'
import { AbstractService } from '../Service/AbstractService'
@@ -36,5 +37,11 @@ export interface VaultServiceInterface
params: { name: string; description: string },
): Promise<VaultListingInterface>
rotateVaultRootKey(vault: VaultListingInterface, vaultPassword?: string): Promise<void>
changeVaultKeyOptions(dto: ChangeVaultKeyOptionsDTO): Promise<Result<void>>
changeThirdPartyVaultStorageOptions(dto: {
vault: SharedVaultListingInterface
newStorageMode: KeySystemRootKeyStorageMode | undefined
vaultPassword: string
}): Promise<Result<void>>
}
@@ -0,0 +1,30 @@
import { EncryptionProviderInterface } from './../../Encryption/EncryptionProviderInterface'
import { Result, SyncUseCaseInterface } from '@standardnotes/domain-core'
import { KeySystemPasswordType, VaultListingInterface } from '@standardnotes/models'
import { KeySystemKeyManagerInterface } from '../../KeySystem/KeySystemKeyManagerInterface'
export class ValidateVaultPassword implements SyncUseCaseInterface<boolean> {
constructor(
private encryption: EncryptionProviderInterface,
private keys: KeySystemKeyManagerInterface,
) {}
execute(vault: VaultListingInterface, password: string): Result<boolean> {
if (vault.keyPasswordType !== KeySystemPasswordType.UserInputted) {
throw new Error('Vault uses randomized password and cannot be validated with password')
}
const rootKey = this.keys.getPrimaryKeySystemRootKey(vault.systemIdentifier)
if (!rootKey) {
return Result.ok(false)
}
const derivedRootKey = this.encryption.deriveUserInputtedKeySystemRootKey({
keyParams: vault.rootKeyParams,
userInputtedPassword: password,
})
return Result.ok(rootKey.isEqual(derivedRootKey))
}
}
@@ -1,3 +1,5 @@
import { GetVaultItems } from './../Vault/UseCase/GetVaultItems'
import { RemoveItemsFromMemory } from './../Storage/UseCase/RemoveItemsFromMemory'
import { GetVaults } from '../Vault/UseCase/GetVaults'
import { KeySystemPasswordType, KeySystemRootKeyStorageMode, VaultListingInterface } from '@standardnotes/models'
import { VaultLockServiceInterface } from './VaultLockServiceInterface'
@@ -22,6 +24,8 @@ export class VaultLockService
private keys: KeySystemKeyManagerInterface,
private _getVaults: GetVaults,
private _decryptErroredPayloads: DecryptErroredPayloads,
private _removeItemsFromMemory: RemoveItemsFromMemory,
private _getVaultItems: GetVaultItems,
eventBus: InternalEventBusInterface,
) {
super(eventBus)
@@ -40,6 +44,8 @@ export class VaultLockService
;(this.keys as unknown) = undefined
;(this._getVaults as unknown) = undefined
;(this._decryptErroredPayloads as unknown) = undefined
;(this._removeItemsFromMemory as unknown) = undefined
;(this._getVaultItems as unknown) = undefined
this.lockMap.clear()
}
@@ -68,6 +74,9 @@ export class VaultLockService
await this.keys.wipeVaultKeysFromMemory(vault)
const vaultItems = this._getVaultItems.execute(vault).getValue()
await this._removeItemsFromMemory.execute(vaultItems)
this.lockMap.set(vault.uuid, true)
void this.notifyEventSync(VaultLockServiceEvent.VaultLocked, { vault })
@@ -1,17 +1,21 @@
import { UserServiceInterface } from './../../User/UserServiceInterface'
import { Result, SyncUseCaseInterface } from '@standardnotes/domain-core'
import { SharedVaultListingInterface } from '@standardnotes/models'
import { VaultListingInterface } from '@standardnotes/models'
export class IsVaultOwner implements SyncUseCaseInterface<boolean> {
constructor(private users: UserServiceInterface) {}
execute(dto: { sharedVault: SharedVaultListingInterface }): Result<boolean> {
if (!dto.sharedVault.sharing.ownerUserUuid) {
throw new Error(`Shared vault ${dto.sharedVault.sharing.sharedVaultUuid} does not have an owner user uuid`)
execute(vault: VaultListingInterface): Result<boolean> {
if (!vault.sharing) {
return Result.ok(true)
}
if (!vault.sharing.ownerUserUuid) {
throw new Error(`Shared vault ${vault.sharing.sharedVaultUuid} does not have an owner user uuid`)
}
const user = this.users.sureUser
return Result.ok(dto.sharedVault.sharing.ownerUserUuid === user.uuid)
return Result.ok(vault.sharing.ownerUserUuid === user.uuid)
}
}
@@ -52,11 +52,7 @@ export class VaultUserService extends AbstractService<VaultUserServiceEvent> imp
}
public isCurrentUserSharedVaultAdmin(sharedVault: SharedVaultListingInterface): boolean {
return this._isVaultOwner
.execute({
sharedVault,
})
.getValue()
return this._isVaultOwner.execute(sharedVault).getValue()
}
async removeUserFromSharedVault(sharedVault: SharedVaultListingInterface, userUuid: string): Promise<Result<void>> {
+3
View File
@@ -178,6 +178,7 @@ export * from './Sync/SyncServiceInterface'
export * from './Sync/SyncSource'
export * from './UseCase/ChangeAndSaveItem'
export * from './UseCase/DiscardItemsLocally'
export * from './UseCase/GenerateUuid'
export * from './UseCase/GetHost'
export * from './UseCase/SetHost'
export * from './User/AccountEvent'
@@ -196,6 +197,7 @@ export * from './Vault/UseCase/ChangeVaultStorageMode'
export * from './Vault/UseCase/CreateVault'
export * from './Vault/UseCase/DeleteVault'
export * from './Vault/UseCase/GetVault'
export * from './Vault/UseCase/GetVaultItems'
export * from './Vault/UseCase/GetVaults'
export * from './Vault/UseCase/MoveItemsToVault'
export * from './Vault/UseCase/RemoveItemFromVault'
@@ -213,6 +215,7 @@ export * from './VaultInvite/UseCase/SendVaultInvite'
export * from './VaultInvite/VaultInviteService'
export * from './VaultInvite/VaultInviteServiceEvent'
export * from './VaultInvite/VaultInviteServiceInterface'
export * from './VaultLock/UseCase/ValidateVaultPassword'
export * from './VaultLock/VaultLockService'
export * from './VaultLock/VaultLockServiceEvent'
export * from './VaultLock/VaultLockServiceInterface'
+4
View File
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.14.8](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-08-06)
**Note:** Version bump only for package @standardnotes/sncrypto-web
## [1.14.7](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-08-01)
**Note:** Version bump only for package @standardnotes/sncrypto-web
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/sncrypto-web",
"version": "1.14.7",
"version": "1.14.8",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
+1 -2
View File
@@ -1,5 +1,4 @@
/* eslint-disable no-unused-expressions */
/* eslint-disable no-undef */
import './vendor/chai-as-promised-built.js'
import './vendor/[email protected]'
+1 -2
View File
@@ -1,5 +1,4 @@
/* eslint-disable no-unused-expressions */
/* eslint-disable no-undef */
import './vendor/chai-as-promised-built.js'
chai.use(chaiAsPromised)
+1 -2
View File
@@ -1,5 +1,4 @@
/* eslint-disable no-unused-expressions */
/* eslint-disable no-undef */
import './vendor/chai-as-promised-built.js'
chai.use(chaiAsPromised)
+20
View File
@@ -3,6 +3,26 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [2.202.39](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-08-07)
**Note:** Version bump only for package @standardnotes/snjs
## [2.202.38](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-08-07)
**Note:** Version bump only for package @standardnotes/snjs
## [2.202.37](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-08-07)
**Note:** Version bump only for package @standardnotes/snjs
## [2.202.36](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-08-07)
**Note:** Version bump only for package @standardnotes/snjs
## [2.202.35](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-08-06)
**Note:** Version bump only for package @standardnotes/snjs
## [2.202.34](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-08-06)
**Note:** Version bump only for package @standardnotes/snjs
+8 -83
View File
@@ -24,8 +24,6 @@ import {
ImportDataUseCase,
StoragePersistencePolicies,
HomeServerServiceInterface,
ApiServiceEvent,
IntegrityEvent,
DeviceInterface,
SubscriptionManagerInterface,
FeaturesClientInterface,
@@ -71,7 +69,6 @@ import {
EncryptionProviderInterface,
VaultUserServiceInterface,
VaultInviteServiceInterface,
NotificationServiceEvent,
VaultLockServiceInterface,
ApplicationConstructorOptions,
FullyResolvedApplicationOptions,
@@ -81,6 +78,7 @@ import {
GetHost,
SetHost,
MfaServiceInterface,
GenerateUuid,
} from '@standardnotes/services'
import {
SNNote,
@@ -112,7 +110,6 @@ import {
removeFromArray,
isNullOrUndefined,
sleep,
UuidGenerator,
useBoolean,
LoggerInterface,
canBlockDeinit,
@@ -135,6 +132,7 @@ import { GetAuthenticatorAuthenticationResponse } from '@Lib/Domain/UseCase/GetA
import { GetAuthenticatorAuthenticationOptions } from '@Lib/Domain/UseCase/GetAuthenticatorAuthenticationOptions/GetAuthenticatorAuthenticationOptions'
import { Dependencies } from './Dependencies/Dependencies'
import { TYPES } from './Dependencies/Types'
import { RegisterApplicationServicesEvents } from './Dependencies/DependencyEvents'
/** How often to automatically sync, in milliseconds */
const DEFAULT_AUTO_SYNC_INTERVAL = 30_000
@@ -225,8 +223,8 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli
this.dependencies = new Dependencies(this.options)
this.registerServiceObservers()
this.defineInternalEventHandlers()
this.createBackgroundDependencies()
RegisterApplicationServicesEvents(this.dependencies, this.events)
}
private registerServiceObservers() {
@@ -932,10 +930,6 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli
return this.migrations.hasPendingMigrations()
}
public generateUuid(): string {
return UuidGenerator.GenerateUuid()
}
public presentKeyRecoveryWizard(): void {
const service = this.dependencies.get<KeyRecoveryService>(TYPES.KeyRecoveryService)
return service.presentKeyRecoveryWizard()
@@ -982,79 +976,6 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli
return this.getHost.execute().getValue() === (await homeServerService.getHomeServerUrl())
}
private createBackgroundDependencies() {
this.dependencies.get(TYPES.NotificationService)
this.dependencies.get(TYPES.KeyRecoveryService)
}
private defineInternalEventHandlers(): void {
this.events.addEventHandler(this.dependencies.get(TYPES.FeaturesService), ApiServiceEvent.MetaReceived)
this.events.addEventHandler(this.dependencies.get(TYPES.IntegrityService), SyncEvent.SyncRequestsIntegrityCheck)
this.events.addEventHandler(this.dependencies.get(TYPES.SyncService), IntegrityEvent.IntegrityCheckCompleted)
this.events.addEventHandler(this.dependencies.get(TYPES.UserService), AccountEvent.SignedInOrRegistered)
this.events.addEventHandler(this.dependencies.get(TYPES.SessionManager), ApiServiceEvent.SessionRefreshed)
this.events.addEventHandler(this.dependencies.get(TYPES.SubscriptionManager), SessionEvent.Restored)
this.events.addEventHandler(this.dependencies.get(TYPES.VaultInviteService), SyncEvent.ReceivedSharedVaultInvites)
this.events.addEventHandler(this.dependencies.get(TYPES.SharedVaultService), SessionEvent.UserKeyPairChanged)
this.events.addEventHandler(
this.dependencies.get(TYPES.SharedVaultService),
NotificationServiceEvent.NotificationReceived,
)
this.events.addEventHandler(this.dependencies.get(TYPES.SharedVaultService), SyncEvent.ReceivedRemoteSharedVaults)
this.events.addEventHandler(
this.dependencies.get(TYPES.AsymmetricMessageService),
SyncEvent.ReceivedAsymmetricMessages,
)
if (this.dependencies.get(TYPES.FilesBackupService)) {
this.events.addEventHandler(
this.dependencies.get(TYPES.FilesBackupService),
ApplicationEvent.ApplicationStageChanged,
)
}
if (this.dependencies.get(TYPES.HomeServerService)) {
this.events.addEventHandler(
this.dependencies.get(TYPES.HomeServerService),
ApplicationEvent.ApplicationStageChanged,
)
}
this.events.addEventHandler(this.dependencies.get(TYPES.SessionManager), ApplicationEvent.ApplicationStageChanged)
this.events.addEventHandler(
this.dependencies.get(TYPES.SelfContactManager),
ApplicationEvent.ApplicationStageChanged,
)
this.events.addEventHandler(
this.dependencies.get(TYPES.KeySystemKeyManager),
ApplicationEvent.ApplicationStageChanged,
)
this.events.addEventHandler(
this.dependencies.get(TYPES.SubscriptionManager),
ApplicationEvent.ApplicationStageChanged,
)
this.events.addEventHandler(this.dependencies.get(TYPES.FeaturesService), ApplicationEvent.ApplicationStageChanged)
this.events.addEventHandler(
this.dependencies.get(TYPES.KeyRecoveryService),
ApplicationEvent.ApplicationStageChanged,
)
this.events.addEventHandler(this.dependencies.get(TYPES.MigrationService), ApplicationEvent.ApplicationStageChanged)
this.events.addEventHandler(
this.dependencies.get(TYPES.PreferencesService),
ApplicationEvent.ApplicationStageChanged,
)
this.events.addEventHandler(
this.dependencies.get(TYPES.ProtectionService),
ApplicationEvent.ApplicationStageChanged,
)
this.events.addEventHandler(
this.dependencies.get(TYPES.DiskStorageService),
ApplicationEvent.ApplicationStageChanged,
)
}
get device(): DeviceInterface {
return this.dependencies.get<DeviceInterface>(TYPES.DeviceInterface)
}
@@ -1243,6 +1164,10 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli
return this.dependencies.get<MfaService>(TYPES.MfaService)
}
public get generateUuid(): GenerateUuid {
return this.dependencies.get<GenerateUuid>(TYPES.GenerateUuid)
}
private get migrations(): MigrationService {
return this.dependencies.get<MigrationService>(TYPES.MigrationService)
}
@@ -130,6 +130,9 @@ import {
FullyResolvedApplicationOptions,
GetHost,
SetHost,
GenerateUuid,
GetVaultItems,
ValidateVaultPassword,
} from '@standardnotes/services'
import { ItemManager } from '../../Services/Items/ItemManager'
import { PayloadManager } from '../../Services/Payloads/PayloadManager'
@@ -214,6 +217,21 @@ export class Dependencies {
}
private registerUseCaseMakers() {
this.factory.set(TYPES.ValidateVaultPassword, () => {
return new ValidateVaultPassword(
this.get<EncryptionService>(TYPES.EncryptionService),
this.get<KeySystemKeyManager>(TYPES.KeySystemKeyManager),
)
})
this.factory.set(TYPES.GenerateUuid, () => {
return new GenerateUuid(this.get<PureCryptoInterface>(TYPES.Crypto))
})
this.factory.set(TYPES.GetVaultItems, () => {
return new GetVaultItems(this.get<ItemManager>(TYPES.ItemManager))
})
this.factory.set(TYPES.DecryptErroredPayloads, () => {
return new DecryptErroredPayloads(
this.get<ItemsEncryptionService>(TYPES.ItemsEncryptionService),
@@ -384,9 +402,9 @@ export class Dependencies {
this.factory.set(TYPES.DeleteVault, () => {
return new DeleteVault(
this.get<ItemManager>(TYPES.ItemManager),
this.get<MutatorService>(TYPES.MutatorService),
this.get<KeySystemKeyManager>(TYPES.KeySystemKeyManager),
this.get<GetVaultItems>(TYPES.GetVaultItems),
)
})
@@ -427,11 +445,11 @@ export class Dependencies {
this.factory.set(TYPES.CreateSharedVault, () => {
return new CreateSharedVault(
this.get<ItemManager>(TYPES.ItemManager),
this.get<MutatorService>(TYPES.MutatorService),
this.get<SharedVaultServer>(TYPES.SharedVaultServer),
this.get<CreateVault>(TYPES.CreateVault),
this.get<MoveItemsToVault>(TYPES.MoveItemsToVault),
this.get<GetVaultItems>(TYPES.GetVaultItems),
)
})
@@ -561,10 +579,10 @@ export class Dependencies {
this.factory.set(TYPES.ConvertToSharedVault, () => {
return new ConvertToSharedVault(
this.get<ItemManager>(TYPES.ItemManager),
this.get<MutatorService>(TYPES.MutatorService),
this.get<SharedVaultServer>(TYPES.SharedVaultServer),
this.get<MoveItemsToVault>(TYPES.MoveItemsToVault),
this.get<GetVaultItems>(TYPES.GetVaultItems),
)
})
@@ -832,6 +850,8 @@ export class Dependencies {
this.get<KeySystemKeyManager>(TYPES.KeySystemKeyManager),
this.get<GetVaults>(TYPES.GetVaults),
this.get<DecryptErroredPayloads>(TYPES.DecryptErroredPayloads),
this.get<RemoveItemsFromMemory>(TYPES.RemoveItemsFromMemory),
this.get<GetVaultItems>(TYPES.GetVaultItems),
this.get<InternalEventBus>(TYPES.InternalEventBus),
)
})
@@ -852,6 +872,8 @@ export class Dependencies {
this.get<DeleteVault>(TYPES.DeleteVault),
this.get<RotateVaultKey>(TYPES.RotateVaultKey),
this.get<SendVaultDataChangedMessage>(TYPES.SendVaultDataChangedMessage),
this.get<IsVaultOwner>(TYPES.IsVaultOwner),
this.get<ValidateVaultPassword>(TYPES.ValidateVaultPassword),
this.get<InternalEventBus>(TYPES.InternalEventBus),
)
})
@@ -0,0 +1,48 @@
import {
AccountEvent,
ApiServiceEvent,
ApplicationEvent,
IntegrityEvent,
InternalEventBusInterface,
NotificationServiceEvent,
SessionEvent,
SyncEvent,
} from '@standardnotes/services'
import { Dependencies } from './Dependencies'
import { TYPES } from './Types'
export function RegisterApplicationServicesEvents(container: Dependencies, events: InternalEventBusInterface): void {
events.addEventHandler(container.get(TYPES.AsymmetricMessageService), SyncEvent.ReceivedAsymmetricMessages)
events.addEventHandler(container.get(TYPES.DiskStorageService), ApplicationEvent.ApplicationStageChanged)
events.addEventHandler(container.get(TYPES.FeaturesService), ApiServiceEvent.MetaReceived)
events.addEventHandler(container.get(TYPES.FeaturesService), ApplicationEvent.ApplicationStageChanged)
events.addEventHandler(container.get(TYPES.IntegrityService), SyncEvent.SyncRequestsIntegrityCheck)
events.addEventHandler(container.get(TYPES.KeyRecoveryService), ApplicationEvent.ApplicationStageChanged)
events.addEventHandler(container.get(TYPES.KeySystemKeyManager), ApplicationEvent.ApplicationStageChanged)
events.addEventHandler(container.get(TYPES.MigrationService), ApplicationEvent.ApplicationStageChanged)
events.addEventHandler(container.get(TYPES.NotificationService), SyncEvent.ReceivedNotifications)
events.addEventHandler(container.get(TYPES.PreferencesService), ApplicationEvent.ApplicationStageChanged)
events.addEventHandler(container.get(TYPES.ProtectionService), ApplicationEvent.ApplicationStageChanged)
events.addEventHandler(container.get(TYPES.ProtectionService), ApplicationEvent.Started)
events.addEventHandler(container.get(TYPES.SelfContactManager), ApplicationEvent.ApplicationStageChanged)
events.addEventHandler(container.get(TYPES.SessionManager), ApiServiceEvent.SessionRefreshed)
events.addEventHandler(container.get(TYPES.SessionManager), ApplicationEvent.ApplicationStageChanged)
events.addEventHandler(container.get(TYPES.SharedVaultService), NotificationServiceEvent.NotificationReceived)
events.addEventHandler(container.get(TYPES.SharedVaultService), SessionEvent.UserKeyPairChanged)
events.addEventHandler(container.get(TYPES.SharedVaultService), SyncEvent.ReceivedRemoteSharedVaults)
events.addEventHandler(container.get(TYPES.SubscriptionManager), ApplicationEvent.ApplicationStageChanged)
events.addEventHandler(container.get(TYPES.SubscriptionManager), ApplicationEvent.Launched)
events.addEventHandler(container.get(TYPES.SubscriptionManager), ApplicationEvent.SignedIn)
events.addEventHandler(container.get(TYPES.SubscriptionManager), ApplicationEvent.UserRolesChanged)
events.addEventHandler(container.get(TYPES.SubscriptionManager), SessionEvent.Restored)
events.addEventHandler(container.get(TYPES.SyncService), IntegrityEvent.IntegrityCheckCompleted)
events.addEventHandler(container.get(TYPES.UserService), AccountEvent.SignedInOrRegistered)
events.addEventHandler(container.get(TYPES.VaultInviteService), SyncEvent.ReceivedSharedVaultInvites)
if (container.get(TYPES.FilesBackupService)) {
events.addEventHandler(container.get(TYPES.FilesBackupService), ApplicationEvent.ApplicationStageChanged)
}
if (container.get(TYPES.HomeServerService)) {
events.addEventHandler(container.get(TYPES.HomeServerService), ApplicationEvent.ApplicationStageChanged)
}
}
@@ -159,6 +159,9 @@ export const TYPES = {
ChangeAndSaveItem: Symbol.for('ChangeAndSaveItem'),
GetHost: Symbol.for('GetHost'),
SetHost: Symbol.for('SetHost'),
GenerateUuid: Symbol.for('GenerateUuid'),
GetVaultItems: Symbol.for('GetVaultItems'),
ValidateVaultPassword: Symbol.for('ValidateVaultPassword'),
// Mappers
SessionStorageMapper: Symbol.for('SessionStorageMapper'),
@@ -852,10 +852,6 @@ export class ItemManager extends Services.AbstractService implements Services.It
: ItemRelationshipDirection.NoRelationship
}
itemsBelongingToKeySystem(systemIdentifier: Models.KeySystemIdentifier): Models.DecryptedItemInterface[] {
return this.items.filter((item) => item.key_system_identifier === systemIdentifier)
}
public conflictsOf(uuid: string) {
return this.collection.conflictsOf(uuid)
}
+7 -9
View File
@@ -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 () {
+14 -9
View File
@@ -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 = '[email protected]'
const _password = 'password'
let _identifier = '[email protected]'
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 () => {
+13 -9
View File
@@ -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 = '[email protected]'
const _password = 'password'
let _identifier = '[email protected]'
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 () => {
+19 -22
View File
@@ -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 = '[email protected]'
const _password = 'password'
let _identifier = '[email protected]'
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 = '[email protected]'
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 = '[email protected]'
const password = 'password'
const keyParams = sharedApplication.encryption.createKeyParams({
const keyParams = application.encryption.createKeyParams({
pw_nonce: '31107837b44d86179140b7c602a55d694243e2e9ced0c4c914ac21ad90215055',
identifier: identifier,
version: '003',
+15 -8
View File
@@ -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 = '[email protected]'
const _password = 'password'
let _identifier = '[email protected]'
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 () {
+132 -116
View File
@@ -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)
})
})
})
+14 -13
View File
@@ -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
+7 -2
View File
@@ -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()
+6 -11
View File
@@ -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'
+35 -25
View File
@@ -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)
+60 -63
View File
@@ -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
}
+3 -4
View File
@@ -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 -2
View File
@@ -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
+4 -1
View File
@@ -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', () => {
+8 -5
View File
@@ -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 })
+107 -133
View File
@@ -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,
})
+39 -41
View File
@@ -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
})
+8 -7
View File
@@ -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',
+4 -4
View File
@@ -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: {
+195 -196
View File
@@ -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
+47 -26
View File
@@ -198,7 +198,7 @@ export class AppContext {
const responses = []
const accountPassword = this.passwordToUseForAccountPasswordChallenge || this.password
const accountPassword = this.password
for (const prompt of challenge.prompts) {
if (prompt.validation === ChallengeValidation.LocalPasscode) {
@@ -300,6 +300,17 @@ export class AppContext {
})
}
awaitNextSyncError() {
return new Promise((resolve) => {
const removeObserver = this.application.sync.addEventObserver((event, data) => {
if (event === SyncEvent.SyncError) {
removeObserver()
resolve(data)
}
})
})
}
awaitNextSyncEvent(eventName) {
return new Promise((resolve) => {
const removeObserver = this.application.sync.addEventObserver((event, data) => {
@@ -322,10 +333,30 @@ export class AppContext {
})
}
async restoreSession(latestPassword) {
const promise = this.resolveWhenSessionIsReauthenticated()
this.password = latestPassword
await this.sync()
await promise
await this.sync()
}
resolveWhenSessionIsReauthenticated() {
return new Promise((resolve) => {
const removeObserver = this.application.sessions.addEventObserver((event, data) => {
if (event === SessionEvent.Restored) {
removeObserver()
resolve()
}
})
})
}
resolveWithUploadedPayloads() {
return new Promise((resolve) => {
this.application.sync.addEventObserver((event, data) => {
const removeObserver = this.application.sync.addEventObserver((event, data) => {
if (event === SyncEvent.PaginatedSyncRequestCompleted) {
removeObserver()
resolve(data.uploadedPayloads)
}
})
@@ -334,8 +365,9 @@ export class AppContext {
resolveWithSyncRetrievedPayloads() {
return new Promise((resolve) => {
this.application.sync.addEventObserver((event, data) => {
const removeObserver = this.application.sync.addEventObserver((event, data) => {
if (event === SyncEvent.PaginatedSyncRequestCompleted) {
removeObserver()
resolve(data.retrievedPayloads)
}
})
@@ -344,8 +376,9 @@ export class AppContext {
resolveWithConflicts() {
return new Promise((resolve) => {
this.application.sync.addEventObserver((event, response) => {
const removeObserver = this.application.sync.addEventObserver((event, response) => {
if (event === SyncEvent.PaginatedSyncRequestCompleted) {
removeObserver()
resolve(response.rawConflictObjects)
}
})
@@ -354,10 +387,11 @@ export class AppContext {
resolveWhenSavedSyncPayloadsIncludesItemUuid(uuid) {
return new Promise((resolve) => {
this.application.sync.addEventObserver((event, response) => {
const removeObserver = this.application.sync.addEventObserver((event, response) => {
if (event === SyncEvent.PaginatedSyncRequestCompleted) {
const savedPayload = response.savedPayloads.find((payload) => payload.uuid === uuid)
if (savedPayload) {
removeObserver()
resolve()
}
}
@@ -367,10 +401,11 @@ export class AppContext {
resolveWhenSavedSyncPayloadsIncludesItemThatIsDuplicatedOf(uuid) {
return new Promise((resolve) => {
this.application.sync.addEventObserver((event, response) => {
const removeObserver = this.application.sync.addEventObserver((event, response) => {
if (event === SyncEvent.PaginatedSyncRequestCompleted) {
const savedPayload = response.savedPayloads.find((payload) => payload.duplicate_of === uuid)
if (savedPayload) {
removeObserver()
resolve()
}
}
@@ -513,6 +548,12 @@ export class AppContext {
this.password = newPassword
}
async changeEmail(newEmail) {
await this.application.changeEmail(newEmail, this.password, this.passcode)
this.email = newEmail
}
findItem(uuid) {
return this.application.items.findItem(uuid)
}
@@ -535,26 +576,6 @@ export class AppContext {
}
}
disableKeyRecoveryServerSignIn() {
this.keyRecovery.performServerSignIn = () => {
console.warn('application.keyRecovery.performServerSignIn has been stubbed with an empty implementation')
}
}
preventKeyRecoveryOfKeys(ids) {
const originalImpl = this.keyRecovery.handleUndecryptableItemsKeys
this.keyRecovery.handleUndecryptableItemsKeys = function (keys) {
const filtered = keys.filter((k) => !ids.includes(k.uuid))
originalImpl.apply(this, [filtered])
}
}
respondToAccountPasswordChallengeWith(password) {
this.passwordToUseForAccountPasswordChallenge = password
}
spyOnChangedItems(callback) {
this.application.items.addObserver(ContentType.TYPES.Any, ({ changed, unerrored }) => {
callback([...changed, ...unerrored])
+3 -2
View File
@@ -85,7 +85,6 @@ export class VaultsContext extends AppContext {
await this.vaults.moveItemToVault(privateVault, note)
const sharedVault = await this.sharedVaults.convertVaultToSharedVault(privateVault)
console.log('createSharedPasswordVault > sharedVault:', sharedVault)
const { thirdPartyContext, deinitThirdPartyContext } = await Collaboration.inviteNewPartyToSharedVault(
this,
@@ -94,6 +93,8 @@ export class VaultsContext extends AppContext {
await Collaboration.acceptAllInvites(thirdPartyContext)
return { sharedVault, thirdPartyContext, deinitThirdPartyContext }
const contactVault = thirdPartyContext.vaults.getVault({ keySystemIdentifier: sharedVault.systemIdentifier })
return { contactVault, sharedVault, thirdPartyContext, deinitThirdPartyContext }
}
}
+5 -2
View File
@@ -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()
+5 -5
View File
@@ -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 () {
+37 -29
View File
@@ -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.' },
+106 -117
View File
@@ -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
+35 -31
View File
@@ -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
+47 -40
View File
@@ -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'])
+213 -208
View File
@@ -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),
+45 -48
View File
@@ -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()
+12 -10
View File
@@ -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]
+208 -236
View File
@@ -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)
})
+28 -31
View File
@@ -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)
+16 -14
View File
@@ -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())
})
})
+27 -24
View File
@@ -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)
})
})
+46 -41
View File
@@ -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)
})
})
+2 -2
View File
@@ -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
+26 -27
View File
@@ -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],
},

Some files were not shown because too many files have changed in this diff Show More