@epam/uui-db 5.3.0 → 5.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +5 -5
- package/{build/stats.html → stats.html} +1 -1
- package/benchmark/results/filter.json +0 -39
- package/benchmark/results/pkLookup.json +0 -39
- package/benchmark/results/pkUpdate-10.json +0 -51
- package/benchmark/results/pkUpdate-100.json +0 -51
- package/benchmark/results/pkUpdate-1000.json +0 -51
- package/benchmark/results/pkUpdate-10000.json +0 -51
- package/benchmark/results/pkUpdate-100000.json +0 -51
- package/benchmark/results/pkUpdate-1000000.json +0 -51
- package/benchmark/results/query-10.json +0 -63
- package/benchmark/results/query-100.json +0 -63
- package/benchmark/results/query-1000.json +0 -63
- package/benchmark/results/query-10000.json +0 -63
- package/benchmark/results/query-100000.json +0 -63
- package/benchmark/results/sorting.json +0 -33
- package/build/package.json +0 -69
- package/build/readme.md +0 -17
- package/globals.d.ts +0 -9
- package/index.tsx +0 -1
- package/src/Db.ts +0 -89
- package/src/DbRef.ts +0 -254
- package/src/DbTable.ts +0 -264
- package/src/IxSet.ts +0 -251
- package/src/ListLoadingTracker.ts +0 -131
- package/src/Loader.ts +0 -96
- package/src/Scheduler.ts +0 -72
- package/src/SimpleLoadingTracker.ts +0 -17
- package/src/__benchmarks__/filter.ts +0 -50
- package/src/__benchmarks__/index.ts +0 -5
- package/src/__benchmarks__/pkLookup.ts +0 -33
- package/src/__benchmarks__/pkUpdate.ts +0 -75
- package/src/__benchmarks__/query.ts +0 -101
- package/src/__benchmarks__/sorting.ts +0 -33
- package/src/__benchmarks__/testData.ts +0 -15
- package/src/__tests__/ListLoadingTracker.test.ts +0 -83
- package/src/__tests__/Scheduler.test.ts +0 -24
- package/src/__tests__/TaskDb.ts +0 -187
- package/src/__tests__/db.test.ts +0 -293
- package/src/__tests__/dbRef.test.ts +0 -100
- package/src/__tests__/dbTable.test.ts +0 -15
- package/src/__tests__/dbViews.test.ts +0 -76
- package/src/__tests__/ixSet.perf.test.ts +0 -129
- package/src/__tests__/ixSet.test.ts +0 -73
- package/src/__tests__/listLoader.test.ts +0 -85
- package/src/__tests__/loader.test.ts +0 -92
- package/src/__tests__/patchHelpers.test.ts +0 -202
- package/src/__tests__/tempIds.test.ts +0 -98
- package/src/index.ts +0 -8
- package/src/patchHelpers.ts +0 -254
- package/src/reactHooks.ts +0 -28
- package/src/tempIds.tsx +0 -236
- package/src/types.ts +0 -126
- package/src/utils.ts +0 -34
- package/tsconfig.json +0 -12
- /package/{build/index.d.ts → index.d.ts} +0 -0
- /package/{build/index.d.ts.map → index.d.ts.map} +0 -0
- /package/{build/index.esm.js → index.esm.js} +0 -0
- /package/{build/index.esm.js.map → index.esm.js.map} +0 -0
- /package/{build/index.js → index.js} +0 -0
- /package/{build/index.js.map → index.js.map} +0 -0
- /package/{build/src → src}/Db.d.ts +0 -0
- /package/{build/src → src}/Db.d.ts.map +0 -0
- /package/{build/src → src}/DbRef.d.ts +0 -0
- /package/{build/src → src}/DbRef.d.ts.map +0 -0
- /package/{build/src → src}/DbTable.d.ts +0 -0
- /package/{build/src → src}/DbTable.d.ts.map +0 -0
- /package/{build/src → src}/IxSet.d.ts +0 -0
- /package/{build/src → src}/IxSet.d.ts.map +0 -0
- /package/{build/src → src}/ListLoadingTracker.d.ts +0 -0
- /package/{build/src → src}/ListLoadingTracker.d.ts.map +0 -0
- /package/{build/src → src}/Loader.d.ts +0 -0
- /package/{build/src → src}/Loader.d.ts.map +0 -0
- /package/{build/src → src}/Scheduler.d.ts +0 -0
- /package/{build/src → src}/Scheduler.d.ts.map +0 -0
- /package/{build/src → src}/SimpleLoadingTracker.d.ts +0 -0
- /package/{build/src → src}/SimpleLoadingTracker.d.ts.map +0 -0
- /package/{build/src → src}/__benchmarks__/filter.d.ts +0 -0
- /package/{build/src → src}/__benchmarks__/filter.d.ts.map +0 -0
- /package/{build/src → src}/__benchmarks__/index.d.ts +0 -0
- /package/{build/src → src}/__benchmarks__/index.d.ts.map +0 -0
- /package/{build/src → src}/__benchmarks__/pkLookup.d.ts +0 -0
- /package/{build/src → src}/__benchmarks__/pkLookup.d.ts.map +0 -0
- /package/{build/src → src}/__benchmarks__/pkUpdate.d.ts +0 -0
- /package/{build/src → src}/__benchmarks__/pkUpdate.d.ts.map +0 -0
- /package/{build/src → src}/__benchmarks__/query.d.ts +0 -0
- /package/{build/src → src}/__benchmarks__/query.d.ts.map +0 -0
- /package/{build/src → src}/__benchmarks__/sorting.d.ts +0 -0
- /package/{build/src → src}/__benchmarks__/sorting.d.ts.map +0 -0
- /package/{build/src → src}/__benchmarks__/testData.d.ts +0 -0
- /package/{build/src → src}/__benchmarks__/testData.d.ts.map +0 -0
- /package/{build/src → src}/index.d.ts +0 -0
- /package/{build/src → src}/index.d.ts.map +0 -0
- /package/{build/src → src}/patchHelpers.d.ts +0 -0
- /package/{build/src → src}/patchHelpers.d.ts.map +0 -0
- /package/{build/src → src}/reactHooks.d.ts +0 -0
- /package/{build/src → src}/reactHooks.d.ts.map +0 -0
- /package/{build/src → src}/tempIds.d.ts +0 -0
- /package/{build/src → src}/tempIds.d.ts.map +0 -0
- /package/{build/src → src}/types.d.ts +0 -0
- /package/{build/src → src}/types.d.ts.map +0 -0
- /package/{build/src → src}/utils.d.ts +0 -0
- /package/{build/src → src}/utils.d.ts.map +0 -0
package/src/__tests__/TaskDb.ts
DELETED
|
@@ -1,187 +0,0 @@
|
|
|
1
|
-
import { DbRef } from '../DbRef';
|
|
2
|
-
import {
|
|
3
|
-
Db, DbTable, DbRelationType, DbView,
|
|
4
|
-
} from '../index';
|
|
5
|
-
import { DbPatch, DbSaveResponse } from '../types';
|
|
6
|
-
|
|
7
|
-
export interface Task {
|
|
8
|
-
id: number;
|
|
9
|
-
name: string;
|
|
10
|
-
isDone: boolean;
|
|
11
|
-
createdBy: string;
|
|
12
|
-
assignedTo: string;
|
|
13
|
-
isDraft: boolean;
|
|
14
|
-
tags?: string[];
|
|
15
|
-
details?: any;
|
|
16
|
-
estimate?: number;
|
|
17
|
-
isDeleted?: boolean;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export interface User {
|
|
21
|
-
id: string;
|
|
22
|
-
name: string;
|
|
23
|
-
title: string;
|
|
24
|
-
sex: 'm' | 'f';
|
|
25
|
-
orderStr: string;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export interface Manager {
|
|
29
|
-
subordinateId: string;
|
|
30
|
-
managerId: string;
|
|
31
|
-
role: string;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export interface View {
|
|
35
|
-
id: number;
|
|
36
|
-
title: string;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
const tasks = new DbTable<Task, number, any>({
|
|
40
|
-
tableName: 'tasks',
|
|
41
|
-
typeName: 'Task',
|
|
42
|
-
primaryKey: 'id',
|
|
43
|
-
fields: {
|
|
44
|
-
id: { isGenerated: true },
|
|
45
|
-
createdBy: {
|
|
46
|
-
isReadOnly: true,
|
|
47
|
-
fk: {
|
|
48
|
-
tableName: 'users',
|
|
49
|
-
relationType: DbRelationType.Aggregation,
|
|
50
|
-
},
|
|
51
|
-
},
|
|
52
|
-
assignedTo: {
|
|
53
|
-
fk: {
|
|
54
|
-
tableName: 'users',
|
|
55
|
-
relationType: DbRelationType.Association,
|
|
56
|
-
},
|
|
57
|
-
},
|
|
58
|
-
estimate: {
|
|
59
|
-
toClient: (value: any) => value && parseInt(value),
|
|
60
|
-
toServer: (value: number) => value && ((value + '') as any),
|
|
61
|
-
},
|
|
62
|
-
isDone: { default: false },
|
|
63
|
-
isDraft: { isClientOnly: true },
|
|
64
|
-
},
|
|
65
|
-
indexes: ['createdBy', 'assignedTo'],
|
|
66
|
-
deleteFlag: 'isDeleted',
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
const users = new DbTable<User, string, any>({
|
|
70
|
-
tableName: 'users',
|
|
71
|
-
typeName: 'User',
|
|
72
|
-
primaryKey: 'id',
|
|
73
|
-
fields: {
|
|
74
|
-
id: { isGenerated: true },
|
|
75
|
-
},
|
|
76
|
-
indexes: ['title'],
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
const managers = new DbTable<Manager, [string, string], any>({
|
|
80
|
-
tableName: 'managers',
|
|
81
|
-
typeName: 'Manager',
|
|
82
|
-
primaryKey: ['subordinateId', 'managerId'],
|
|
83
|
-
fields: {
|
|
84
|
-
subordinateId: { isGenerated: true },
|
|
85
|
-
managerId: { isGenerated: true },
|
|
86
|
-
},
|
|
87
|
-
indexes: ['subordinateId', 'managerId'],
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
const views = new DbTable<View, number, any>({
|
|
91
|
-
tableName: 'views',
|
|
92
|
-
typeName: 'View',
|
|
93
|
-
primaryKey: 'id',
|
|
94
|
-
fields: {
|
|
95
|
-
id: { isGenerated: true },
|
|
96
|
-
},
|
|
97
|
-
isClientOnly: true,
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
const viewsFn = new DbTable<View, number, any>({
|
|
101
|
-
tableName: 'viewsFn',
|
|
102
|
-
typeName: 'ViewFn',
|
|
103
|
-
primaryKey: 'id',
|
|
104
|
-
fields: {
|
|
105
|
-
id: { isGenerated: true },
|
|
106
|
-
},
|
|
107
|
-
isClientOnly: (view) => view.id < 0,
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
const taskDbTables = {
|
|
111
|
-
tasks, users, managers, views, viewsFn,
|
|
112
|
-
};
|
|
113
|
-
export type TaskDbTables = typeof taskDbTables;
|
|
114
|
-
|
|
115
|
-
export class TaskDb extends Db<TaskDbTables> {
|
|
116
|
-
public get tasks() {
|
|
117
|
-
return this.tables.tasks;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
public get users() {
|
|
121
|
-
return this.tables.users;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
public get managers() {
|
|
125
|
-
return this.tables.managers;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
public get views() {
|
|
129
|
-
return this.tables.views;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
public get viewsFn() {
|
|
133
|
-
return this.tables.viewsFn;
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
export const emptyDb = new TaskDb(taskDbTables);
|
|
138
|
-
|
|
139
|
-
export const sampleUsers = {
|
|
140
|
-
daenerys: {
|
|
141
|
-
id: 'DT', name: 'Daenerys Targaryen', sex: 'f', orderStr: 'b',
|
|
142
|
-
} as User,
|
|
143
|
-
john: {
|
|
144
|
-
id: 'JS', name: 'John Snow', sex: 'm', orderStr: 'a',
|
|
145
|
-
} as User,
|
|
146
|
-
arya: {
|
|
147
|
-
id: 'AS', name: 'Arya Stark', sex: 'f', orderStr: 'c',
|
|
148
|
-
} as User,
|
|
149
|
-
};
|
|
150
|
-
|
|
151
|
-
export const sampleData = {
|
|
152
|
-
tasks: [
|
|
153
|
-
{
|
|
154
|
-
id: 1, name: 'Implement DB', isDone: true, createdBy: 'DT', assignedTo: 'JS',
|
|
155
|
-
}, {
|
|
156
|
-
id: 2, name: 'Write Some Tests', isDone: true, createdBy: 'DT', assignedTo: null,
|
|
157
|
-
}, {
|
|
158
|
-
id: 3, name: 'Basic Indexes', isDone: true, createdBy: 'DT', assignedTo: 'JS',
|
|
159
|
-
}, {
|
|
160
|
-
id: 4, name: 'Views', isDone: false, createdBy: 'DT', assignedTo: 'JS',
|
|
161
|
-
}, {
|
|
162
|
-
id: 5, name: 'IO Scheduling', isDone: false, createdBy: 'DT', assignedTo: 'AS',
|
|
163
|
-
}, {
|
|
164
|
-
id: 6, name: 'DataSources', isDone: false, createdBy: 'JS', assignedTo: 'JS',
|
|
165
|
-
}, {
|
|
166
|
-
id: 7, name: 'Loaders', isDone: false, createdBy: 'AS', assignedTo: 'AS',
|
|
167
|
-
},
|
|
168
|
-
] as Task[],
|
|
169
|
-
users: [
|
|
170
|
-
sampleUsers.daenerys, sampleUsers.john, sampleUsers.arya,
|
|
171
|
-
] as User[],
|
|
172
|
-
};
|
|
173
|
-
|
|
174
|
-
export const sampleDb = emptyDb.with(sampleData);
|
|
175
|
-
|
|
176
|
-
export type TaskDbView<TResult, TParams, TDependencies = void> = DbView<TaskDb, TResult, TParams, TDependencies>;
|
|
177
|
-
|
|
178
|
-
export class TasksDbRef extends DbRef<TaskDbTables, TaskDb> {
|
|
179
|
-
constructor(private savePatchHandler: (patch: DbPatch<TaskDbTables>) => Promise<DbSaveResponse<TaskDbTables>>) {
|
|
180
|
-
super(emptyDb);
|
|
181
|
-
this.throttleSaveMs = 1;
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
override savePatch(patch: DbPatch<TaskDbTables>) {
|
|
185
|
-
return this.savePatchHandler?.(patch);
|
|
186
|
-
}
|
|
187
|
-
}
|
package/src/__tests__/db.test.ts
DELETED
|
@@ -1,293 +0,0 @@
|
|
|
1
|
-
import { emptyDb, sampleDb } from './TaskDb';
|
|
2
|
-
|
|
3
|
-
describe('db - DB queries and updates', () => {
|
|
4
|
-
describe('Queries', () => {
|
|
5
|
-
it('user by id with query/one', () => expect(sampleDb.users.find({ id: 'AS' }).one().name).toEqual('Arya Stark'));
|
|
6
|
-
it("user by id with 'byId'", () => expect(sampleDb.users.byId('AS').name).toEqual('Arya Stark'));
|
|
7
|
-
it("users by multiple id with 'in'", () =>
|
|
8
|
-
expect(
|
|
9
|
-
sampleDb.users
|
|
10
|
-
.find({ id: { in: ['AS', 'JS'] } })
|
|
11
|
-
.orderBy('id')
|
|
12
|
-
.toArray()
|
|
13
|
-
.map((u) => u.id),
|
|
14
|
-
).toEqual(['AS', 'JS']));
|
|
15
|
-
it('user by name', () => expect(sampleDb.users.find({ name: 'Arya Stark' }).one().id).toEqual('AS'));
|
|
16
|
-
it('user by [null] name', () => expect(sampleDb.users.find({ name: null }).one()).toBeNull());
|
|
17
|
-
it('sort by name', () =>
|
|
18
|
-
expect(
|
|
19
|
-
sampleDb.users
|
|
20
|
-
.orderBy('name')
|
|
21
|
-
.toArray()
|
|
22
|
-
.map((e) => e.name),
|
|
23
|
-
).toEqual([
|
|
24
|
-
'Arya Stark', 'Daenerys Targaryen', 'John Snow',
|
|
25
|
-
]));
|
|
26
|
-
it('sort by name and sex', () =>
|
|
27
|
-
expect(
|
|
28
|
-
sampleDb.users
|
|
29
|
-
.orderBy('sex', 'desc')
|
|
30
|
-
.thenBy('name')
|
|
31
|
-
.map((e) => e.name),
|
|
32
|
-
).toEqual([
|
|
33
|
-
'John Snow', 'Arya Stark', 'Daenerys Targaryen',
|
|
34
|
-
]));
|
|
35
|
-
it('sort by name and sex via order', () =>
|
|
36
|
-
expect(sampleDb.users.order([{ field: 'sex' }, { field: 'name', direction: 'desc' }]).map((e) => e.name)).toEqual([
|
|
37
|
-
'Daenerys Targaryen', 'Arya Stark', 'John Snow',
|
|
38
|
-
]));
|
|
39
|
-
it("find tasks by multiple values with 'in'", () =>
|
|
40
|
-
expect(
|
|
41
|
-
sampleDb.tasks
|
|
42
|
-
.find({ createdBy: { in: ['AS', 'JS'] } })
|
|
43
|
-
.orderBy('id')
|
|
44
|
-
.map((t) => t.id),
|
|
45
|
-
).toEqual([6, 7]));
|
|
46
|
-
|
|
47
|
-
it('find tasks by both indexed and non-indexed fields', () =>
|
|
48
|
-
expect(
|
|
49
|
-
sampleDb.tasks
|
|
50
|
-
.find({ createdBy: { in: ['DT'] }, assignedTo: 'JS', isDone: false })
|
|
51
|
-
.orderBy('id')
|
|
52
|
-
.map((t) => t.id),
|
|
53
|
-
).toEqual([4]));
|
|
54
|
-
|
|
55
|
-
it('non-indexable filter criteria should work', () =>
|
|
56
|
-
expect(
|
|
57
|
-
sampleDb.tasks
|
|
58
|
-
.find({ id: { gte: 4 }, assignedTo: 'AS', isDone: false })
|
|
59
|
-
.orderBy('id')
|
|
60
|
-
.map((t) => t.id),
|
|
61
|
-
).toEqual([5, 7]));
|
|
62
|
-
|
|
63
|
-
it('non-indexable and indexable criteria for the same field', () =>
|
|
64
|
-
expect(sampleDb.tasks.find({ createdBy: { in: ['AS', 'JS'], gt: 'B' }, isDone: false }).map((t) => t.id)).toEqual([6]));
|
|
65
|
-
|
|
66
|
-
it('count should work with filter', () => expect(sampleDb.tasks.find({ assignedTo: 'AS' }).count()).toEqual(2));
|
|
67
|
-
|
|
68
|
-
it('range should work with filter', () =>
|
|
69
|
-
expect(
|
|
70
|
-
sampleDb.tasks
|
|
71
|
-
.find({ createdBy: 'DT', assignedTo: { isNull: false } })
|
|
72
|
-
.orderBy('assignedTo')
|
|
73
|
-
.thenBy('id')
|
|
74
|
-
.range(1, 3)
|
|
75
|
-
.map((i) => i.id),
|
|
76
|
-
).toEqual([
|
|
77
|
-
1, 3, 4,
|
|
78
|
-
]));
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
describe('Indexes and updates', () => {
|
|
82
|
-
it('query after insert', () => {
|
|
83
|
-
const db = sampleDb.with({
|
|
84
|
-
tasks: [
|
|
85
|
-
{
|
|
86
|
-
id: 100, name: 'Test', assignedTo: 'QQ', createdBy: 'WW', isDone: false, isDraft: false,
|
|
87
|
-
},
|
|
88
|
-
],
|
|
89
|
-
});
|
|
90
|
-
expect(db.tasks.find({ assignedTo: 'QQ', createdBy: 'WW' }).one().id).toBe(100);
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
it('query after update', () => {
|
|
94
|
-
const db = sampleDb.with({
|
|
95
|
-
tasks: [
|
|
96
|
-
{
|
|
97
|
-
id: 1, name: 'Test', createdBy: 'QQ', isDone: false, isDraft: false,
|
|
98
|
-
},
|
|
99
|
-
],
|
|
100
|
-
});
|
|
101
|
-
expect(db.tasks.find({ createdBy: 'QQ' }).one().id).toBe(1);
|
|
102
|
-
expect(
|
|
103
|
-
db.tasks
|
|
104
|
-
.find({ createdBy: 'DT' })
|
|
105
|
-
.orderBy('id')
|
|
106
|
-
.map((u) => u.id),
|
|
107
|
-
).toEqual([
|
|
108
|
-
2, 3, 4, 5,
|
|
109
|
-
]);
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
it('query after delete', () => {
|
|
113
|
-
const db = sampleDb.with({ tasks: [{ id: 1, isDeleted: true }] });
|
|
114
|
-
expect(db.tasks.byId(1)).toBeFalsy();
|
|
115
|
-
expect(db.tasks.find({ createdBy: 'AS' }).map((i) => i.id)).not.toContain(1);
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
it('user name update', () => {
|
|
119
|
-
const newDb = sampleDb.with({ users: [{ id: 'AS', name: 'Ivan Ivanov' }] });
|
|
120
|
-
expect(newDb.users.byId('AS').name).toEqual('Ivan Ivanov');
|
|
121
|
-
// DB is immutable, so shallow copy should be created at all levels.
|
|
122
|
-
expect(newDb.users).not.toBe(sampleDb.users);
|
|
123
|
-
expect(newDb.tables.users).not.toBe(sampleDb.tables.users);
|
|
124
|
-
expect(newDb.users.byId('AS')).not.toBe(sampleDb.users.byId('AS'));
|
|
125
|
-
expect(sampleDb.users.byId('AS').name).toEqual('Arya Stark');
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
it('Should return updated record if index is not touched', () => {
|
|
129
|
-
const db = sampleDb.with({
|
|
130
|
-
tasks: [
|
|
131
|
-
{
|
|
132
|
-
id: 7, name: 'Test', isDone: true, isDraft: false,
|
|
133
|
-
},
|
|
134
|
-
],
|
|
135
|
-
});
|
|
136
|
-
expect(db.tasks.find({ createdBy: 'AS' }).one().id).toBe(7);
|
|
137
|
-
expect(db.tasks.find({ createdBy: 'AS' }).one().name).toBe('Test');
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
it('should handle null and undefined values', () => {
|
|
141
|
-
const testDb = emptyDb.with({
|
|
142
|
-
tasks: [{ id: 1, assignedTo: null, isDone: null }, { id: 2 }],
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
expect(
|
|
146
|
-
testDb.tasks
|
|
147
|
-
.find({ isDone: null })
|
|
148
|
-
.toArray()
|
|
149
|
-
.map((i) => i.id),
|
|
150
|
-
).toEqual([1]);
|
|
151
|
-
expect(
|
|
152
|
-
testDb.tasks
|
|
153
|
-
.find({ isDone: undefined })
|
|
154
|
-
.toArray()
|
|
155
|
-
.map((i) => i.id),
|
|
156
|
-
).toEqual([2]);
|
|
157
|
-
expect(
|
|
158
|
-
testDb.tasks
|
|
159
|
-
.find({ isDone: { isNull: true } })
|
|
160
|
-
.toArray()
|
|
161
|
-
.map((i) => i.id),
|
|
162
|
-
).toEqual([1, 2]);
|
|
163
|
-
|
|
164
|
-
expect(
|
|
165
|
-
testDb.tasks
|
|
166
|
-
.find({ assignedTo: null })
|
|
167
|
-
.toArray()
|
|
168
|
-
.map((i) => i.id),
|
|
169
|
-
).toEqual([1]);
|
|
170
|
-
expect(
|
|
171
|
-
testDb.tasks
|
|
172
|
-
.find({ assignedTo: undefined })
|
|
173
|
-
.toArray()
|
|
174
|
-
.map((i) => i.id),
|
|
175
|
-
).toEqual([2]);
|
|
176
|
-
expect(
|
|
177
|
-
testDb.tasks
|
|
178
|
-
.find({ assignedTo: { isNull: true } })
|
|
179
|
-
.toArray()
|
|
180
|
-
.map((i) => i.id),
|
|
181
|
-
).toEqual([1, 2]);
|
|
182
|
-
});
|
|
183
|
-
|
|
184
|
-
it('Should correctly handle changes from undefined to non-undefined and reverse (non-indexed)', () => {
|
|
185
|
-
const db1 = sampleDb.with({ tasks: [{ id: 100 }] }); // initial - undefined
|
|
186
|
-
expect(db1.tasks.find({ name: { isNull: true } }).one().id).toBe(100);
|
|
187
|
-
expect(db1.tasks.find({ name: undefined }).one().id).toBe(100);
|
|
188
|
-
expect(db1.tasks.find({ name: { in: [undefined] } }).one().id).toBe(100);
|
|
189
|
-
const db2 = db1.with({ tasks: [{ id: 100, name: 'Test' }] }); // set value
|
|
190
|
-
expect(db2.tasks.find({ name: 'Test' }).one().id).toBe(100);
|
|
191
|
-
const db3 = db2.with({ tasks: [{ id: 100, isDone: true }] }); // keep value
|
|
192
|
-
expect(db2.tasks.find({ name: 'Test' }).one().id).toBe(100);
|
|
193
|
-
const db4 = db3.with({ tasks: [{ id: 100, name: undefined }] }); // set to undefined
|
|
194
|
-
expect(db4.tasks.find({ name: { isNull: true } }).one().id).toBe(100);
|
|
195
|
-
expect(db4.tasks.find({ name: undefined }).one().id).toBe(100);
|
|
196
|
-
expect(db4.tasks.find({ name: { in: [undefined] } }).one().id).toBe(100);
|
|
197
|
-
});
|
|
198
|
-
|
|
199
|
-
it('Should correctly handle changes from null to non-null and reverse (non-indexed)', () => {
|
|
200
|
-
const db1 = sampleDb.with({ tasks: [{ id: 100, name: null }] }); // initial - null
|
|
201
|
-
expect(db1.tasks.find({ name: { isNull: true } }).one().id).toBe(100);
|
|
202
|
-
expect(db1.tasks.find({ name: null }).one().id).toBe(100);
|
|
203
|
-
expect(db1.tasks.find({ name: { in: [null] } }).one().id).toBe(100);
|
|
204
|
-
const db2 = db1.with({ tasks: [{ id: 100, name: 'Test' }] }); // set value
|
|
205
|
-
expect(db2.tasks.find({ name: 'Test' }).one().id).toBe(100);
|
|
206
|
-
const db3 = db2.with({ tasks: [{ id: 100, isDone: true }] }); // keep value
|
|
207
|
-
expect(db2.tasks.find({ name: 'Test' }).one().id).toBe(100);
|
|
208
|
-
const db4 = db3.with({ tasks: [{ id: 100, name: null }] }); // set to null
|
|
209
|
-
expect(db4.tasks.find({ name: { isNull: true } }).one().id).toBe(100);
|
|
210
|
-
expect(db4.tasks.find({ name: null }).one().id).toBe(100);
|
|
211
|
-
expect(db4.tasks.find({ name: { in: [null] } }).one().id).toBe(100);
|
|
212
|
-
});
|
|
213
|
-
|
|
214
|
-
it('Should correctly handle changes from null to non-null and reverse (indexed)', () => {
|
|
215
|
-
const db1 = sampleDb.with({ tasks: [{ id: 100, name: 'Test', createdBy: null }] }); // initial - null
|
|
216
|
-
expect(db1.tasks.find({ createdBy: { isNull: true } }).one().id).toBe(100);
|
|
217
|
-
expect(db1.tasks.find({ createdBy: null }).one().id).toBe(100);
|
|
218
|
-
expect(db1.tasks.find({ createdBy: { in: [null] } }).one().id).toBe(100);
|
|
219
|
-
const db2 = db1.with({ tasks: [{ id: 100, createdBy: 'QQ' }] }); // set value
|
|
220
|
-
expect(db2.tasks.find({ createdBy: 'QQ' }).one().id).toBe(100);
|
|
221
|
-
const db3 = db2.with({ tasks: [{ id: 100, isDone: true }] }); // keep value
|
|
222
|
-
expect(db2.tasks.find({ createdBy: 'QQ' }).one().id).toBe(100);
|
|
223
|
-
const db4 = db3.with({ tasks: [{ id: 100, createdBy: null }] }); // set to null
|
|
224
|
-
expect(db4.tasks.find({ createdBy: { isNull: true } }).one().id).toBe(100);
|
|
225
|
-
expect(db4.tasks.find({ createdBy: null }).one().id).toBe(100);
|
|
226
|
-
expect(db4.tasks.find({ createdBy: { in: [null] } }).one().id).toBe(100);
|
|
227
|
-
});
|
|
228
|
-
|
|
229
|
-
it('Should correctly handle changes from undefined to non-undefined and reverse (indexed)', () => {
|
|
230
|
-
const db1 = sampleDb.with({ tasks: [{ id: 100, name: 'Test' }] }); // initial - undefined
|
|
231
|
-
expect(db1.tasks.find({ createdBy: { isNull: true } }).one().id).toBe(100);
|
|
232
|
-
expect(db1.tasks.find({ createdBy: undefined }).one().id).toBe(100);
|
|
233
|
-
expect(db1.tasks.find({ createdBy: { in: [undefined] } }).one().id).toBe(100);
|
|
234
|
-
const db2 = db1.with({ tasks: [{ id: 100, createdBy: 'QQ' }] }); // set value
|
|
235
|
-
expect(db2.tasks.find({ createdBy: 'QQ' }).one().id).toBe(100);
|
|
236
|
-
const db3 = db2.with({ tasks: [{ id: 100, isDone: true }] }); // keep value
|
|
237
|
-
expect(db2.tasks.find({ createdBy: 'QQ' }).one().id).toBe(100);
|
|
238
|
-
const db4 = db3.with({ tasks: [{ id: 100, createdBy: undefined }] }); // set to undefined
|
|
239
|
-
expect(db4.tasks.find({ createdBy: { isNull: true } }).one().id).toBe(100);
|
|
240
|
-
expect(db4.tasks.find({ createdBy: undefined }).one().id).toBe(100);
|
|
241
|
-
expect(db4.tasks.find({ createdBy: { in: [undefined] } }).one().id).toBe(100);
|
|
242
|
-
});
|
|
243
|
-
|
|
244
|
-
it("Should work with update which doesn't touch indexed field", () => {
|
|
245
|
-
const db = sampleDb.with({
|
|
246
|
-
tasks: [
|
|
247
|
-
// existing, index field exists, should update index
|
|
248
|
-
{
|
|
249
|
-
id: 6, name: 'Test', isDone: true, isDraft: false, createdBy: 'XX',
|
|
250
|
-
},
|
|
251
|
-
// existing, index field omitted, should skip index update
|
|
252
|
-
{
|
|
253
|
-
id: 7, name: 'Test', isDone: true, isDraft: false,
|
|
254
|
-
},
|
|
255
|
-
// new, index field omitted, should be indexed as undefined
|
|
256
|
-
{
|
|
257
|
-
id: 123, name: 'Test', isDone: true, isDraft: false,
|
|
258
|
-
},
|
|
259
|
-
],
|
|
260
|
-
});
|
|
261
|
-
expect(db.tasks.find({ createdBy: 'XX' }).one().id).toBe(6);
|
|
262
|
-
expect(db.tasks.find({ createdBy: 'AS' }).one().id).toBe(7);
|
|
263
|
-
expect(db.tasks.find({ createdBy: undefined }).one().id).toBe(123);
|
|
264
|
-
const db2 = sampleDb.with({ tasks: [{ id: 123, createdBy: 'ZZ' }] });
|
|
265
|
-
expect(db2.tasks.find({ createdBy: 'ZZ' }).one().id).toBe(123);
|
|
266
|
-
});
|
|
267
|
-
});
|
|
268
|
-
|
|
269
|
-
describe('Composite key updates', () => {
|
|
270
|
-
it('Can merge new managers correctly', () => {
|
|
271
|
-
expect(sampleDb.managers.count()).toEqual(0);
|
|
272
|
-
const role1 = { subordinateId: 'JS', managerId: 'DT', role: 'RM' };
|
|
273
|
-
const role2 = { subordinateId: 'AS', managerId: 'DT', role: 'PM' };
|
|
274
|
-
|
|
275
|
-
// Add one role
|
|
276
|
-
const db1 = sampleDb.with({ managers: [role1] });
|
|
277
|
-
expect(db1.managers.one()).toEqual(role1);
|
|
278
|
-
expect(db1.managers).not.toBe(sampleDb.managers);
|
|
279
|
-
|
|
280
|
-
// Add second role
|
|
281
|
-
const db2 = db1.with({ managers: [role2] });
|
|
282
|
-
expect(db2.managers.find({ managerId: 'DT' }).count()).toEqual(2);
|
|
283
|
-
expect(db2.managers.byId(['JS', 'DT'])).toEqual(role1);
|
|
284
|
-
expect(db2.managers.find({ subordinateId: 'JS', managerId: 'DT' }).one()).toEqual(role1);
|
|
285
|
-
expect(db2.managers.find({ subordinateId: 'AS' }).one()).toEqual(role2);
|
|
286
|
-
|
|
287
|
-
// Update first role
|
|
288
|
-
const role1New = { ...role1, role: 'DM' };
|
|
289
|
-
const db3 = db2.with({ managers: [role1New] });
|
|
290
|
-
expect(db3.managers.find({ subordinateId: 'JS', managerId: 'DT' }).one()).toEqual(role1New);
|
|
291
|
-
});
|
|
292
|
-
});
|
|
293
|
-
});
|
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
sampleDb, sampleData, TasksDbRef,
|
|
3
|
-
} from './TaskDb';
|
|
4
|
-
import { DbRef } from '../index';
|
|
5
|
-
import { delay } from '@epam/uui-test-utils';
|
|
6
|
-
|
|
7
|
-
describe('db - DbRef query and updates', () => {
|
|
8
|
-
it('Get user by id', () => {
|
|
9
|
-
const db = new DbRef(sampleDb);
|
|
10
|
-
db.commitFetch(sampleData);
|
|
11
|
-
expect(db.db.users.byId('DT').name).toEqual('Daenerys Targaryen');
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
it('Basic update', () => {
|
|
15
|
-
const db = new DbRef(sampleDb);
|
|
16
|
-
db.commitFetch(sampleData);
|
|
17
|
-
db.commit({ users: [{ id: 'DT', name: 'Daenerys Snow' }] });
|
|
18
|
-
expect(db.db.users.byId('DT').name).toEqual('Daenerys Snow');
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
it('Revert changes', () => {
|
|
22
|
-
const db = new DbRef(sampleDb);
|
|
23
|
-
db.commitFetch(sampleData);
|
|
24
|
-
expect(db.db.users.byId('DT').name).toEqual('Daenerys Targaryen');
|
|
25
|
-
db.commit({ users: [{ id: 'DT', name: 'Daenerys Snow' }] });
|
|
26
|
-
expect(db.db.users.byId('DT').name).toEqual('Daenerys Snow');
|
|
27
|
-
db.revert();
|
|
28
|
-
expect(db.db.users.byId('DT').name).toEqual('Daenerys Targaryen');
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
it('Auto-saves changes', async () => {
|
|
32
|
-
const handleSave = jest.fn(async () => ({ submit: {} }));
|
|
33
|
-
const db = new TasksDbRef(handleSave);
|
|
34
|
-
db.commitFetch(sampleData);
|
|
35
|
-
const patch = { users: [{ id: 'DT', name: 'Daenerys Snow' }] };
|
|
36
|
-
db.commit(patch);
|
|
37
|
-
await delay(2);
|
|
38
|
-
expect(handleSave).toHaveBeenCalledTimes(1);
|
|
39
|
-
expect(handleSave).toHaveBeenCalledWith(patch);
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
it('Saves are throttled in auto-save mode', async () => {
|
|
43
|
-
const handleSave = jest.fn(async () => ({ submit: {} }));
|
|
44
|
-
const db = new TasksDbRef(handleSave);
|
|
45
|
-
db.commitFetch(sampleData);
|
|
46
|
-
const patch1 = { users: [{ id: 'DT', name: 'Daenerys Snow' }] };
|
|
47
|
-
db.commit(patch1);
|
|
48
|
-
const patch2 = { users: [{ id: 'DT', name: 'Daenerys' }] };
|
|
49
|
-
db.commit(patch2);
|
|
50
|
-
expect(handleSave).toHaveBeenCalledTimes(0);
|
|
51
|
-
await delay(2);
|
|
52
|
-
expect(handleSave).toHaveBeenCalledTimes(1);
|
|
53
|
-
expect(handleSave).toHaveBeenCalledWith(patch2);
|
|
54
|
-
|
|
55
|
-
// 2nd change
|
|
56
|
-
handleSave.mockClear();
|
|
57
|
-
db.commit(patch1);
|
|
58
|
-
expect(handleSave).toHaveBeenCalledTimes(0);
|
|
59
|
-
await delay(2);
|
|
60
|
-
expect(handleSave).toHaveBeenCalledTimes(1);
|
|
61
|
-
expect(handleSave).toHaveBeenCalledWith(patch1);
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
it('Manually saves changes', async () => {
|
|
65
|
-
const handleSave = jest.fn(async () => ({ submit: {} }));
|
|
66
|
-
const db = new TasksDbRef(handleSave);
|
|
67
|
-
db.setAutoSave(false);
|
|
68
|
-
db.commitFetch(sampleData);
|
|
69
|
-
const patch = { users: [{ id: 'DT', name: 'Daenerys Snow' }] };
|
|
70
|
-
db.commit(patch);
|
|
71
|
-
await delay(2);
|
|
72
|
-
expect(handleSave).not.toHaveBeenCalled();
|
|
73
|
-
await db.save();
|
|
74
|
-
expect(handleSave).toHaveBeenCalledWith(patch);
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
it('Pass-thru save errors', async () => {
|
|
78
|
-
const handleSave = jest.fn(async () => {
|
|
79
|
-
throw new Error();
|
|
80
|
-
});
|
|
81
|
-
const db = new TasksDbRef(handleSave);
|
|
82
|
-
db.setAutoSave(false);
|
|
83
|
-
const patch = { users: [{ id: 'DT', name: 'Daenerys Snow' }] };
|
|
84
|
-
db.commit(patch);
|
|
85
|
-
await expect(db.save()).rejects.toBeInstanceOf(Error);
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
describe('Basic lenses', () => {
|
|
89
|
-
it('update with entity lens', () => {
|
|
90
|
-
const db = new DbRef(sampleDb);
|
|
91
|
-
db.commitFetch(sampleData);
|
|
92
|
-
// const lens = db.entityLens('users', { id: 'DT' });
|
|
93
|
-
// expect(lens.get().name).eq("Daenerys Targaryen");
|
|
94
|
-
// lens.update({ name: "Daenerys Snow" });
|
|
95
|
-
// expect(lens.get().name).eq("Daenerys Snow");
|
|
96
|
-
// lens.prop('name').onValueChange("Ivan");
|
|
97
|
-
// expect(lens.get().name).eq("Ivan");
|
|
98
|
-
});
|
|
99
|
-
});
|
|
100
|
-
});
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { emptyDb, sampleDb } from './TaskDb';
|
|
2
|
-
|
|
3
|
-
describe('db - DbTable', () => {
|
|
4
|
-
describe('Basic updates', () => {
|
|
5
|
-
it('user name update', () => {
|
|
6
|
-
const users = sampleDb.tables.users;
|
|
7
|
-
const newUsers = users.with([{ id: 'AS', name: 'Ivan Ivanov' }]);
|
|
8
|
-
expect(newUsers.byId('AS').name).toEqual('Ivan Ivanov');
|
|
9
|
-
expect(users.byId('AS').name).toEqual('Arya Stark');
|
|
10
|
-
// DbTable is immutable, so shallow copy should be created at all levels.
|
|
11
|
-
expect(newUsers).not.toBe(users);
|
|
12
|
-
expect(newUsers.byId('AS')).not.toBe(users.byId('AS'));
|
|
13
|
-
});
|
|
14
|
-
});
|
|
15
|
-
});
|
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
emptyDb, sampleDb, TaskDbView, TaskDb, Task,
|
|
3
|
-
} from './TaskDb';
|
|
4
|
-
|
|
5
|
-
describe('db - Views', () => {
|
|
6
|
-
it('Should work with whole db as a view', () => {
|
|
7
|
-
const view: TaskDbView<TaskDb, void> = {
|
|
8
|
-
compute: (db) => db,
|
|
9
|
-
};
|
|
10
|
-
|
|
11
|
-
const baseResult = sampleDb.runView(view);
|
|
12
|
-
const updatedResult = sampleDb.with({ tasks: [{ id: 123, name: '123' }] }).runView(view);
|
|
13
|
-
const baseResult2 = sampleDb.runView(view);
|
|
14
|
-
|
|
15
|
-
expect(baseResult).toBe(baseResult2);
|
|
16
|
-
expect(baseResult).not.toBe(updatedResult);
|
|
17
|
-
expect(updatedResult.tables.tasks.byId(123).name).toBe('123');
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
it('Should correctly cache result', () => {
|
|
21
|
-
const view: TaskDbView<Task, number> = {
|
|
22
|
-
compute: jest.fn((db, id) => db.tasks.byId(id)),
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
sampleDb.runView(view, 1);
|
|
26
|
-
|
|
27
|
-
const baseResult = sampleDb.runView(view, 1);
|
|
28
|
-
|
|
29
|
-
const sampleDb2 = sampleDb.with({ tasks: [{ id: 1, name: 'updated' }] });
|
|
30
|
-
|
|
31
|
-
const result1 = sampleDb2.runView(view, 1);
|
|
32
|
-
const result2 = sampleDb2.runView(view, 1);
|
|
33
|
-
expect(baseResult).toBe(sampleDb.tasks.byId(1));
|
|
34
|
-
expect(result1).toBe(sampleDb2.tasks.byId(1));
|
|
35
|
-
expect(result1).not.toBe(baseResult);
|
|
36
|
-
expect(result1).toBe(result2);
|
|
37
|
-
|
|
38
|
-
expect(view.compute).toBeCalledTimes(2);
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
it('Should correctly reuse results when forked', () => {
|
|
42
|
-
const view: TaskDbView<Task, number> = {
|
|
43
|
-
compute: jest.fn((db, id) => db.tasks.byId(id)),
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
const db = emptyDb.with({ tasks: [{ id: 1, name: 'base' }] });
|
|
47
|
-
const baseResult = db.runView(view, 1);
|
|
48
|
-
|
|
49
|
-
expect(baseResult).toEqual({ id: 1, name: 'base' });
|
|
50
|
-
|
|
51
|
-
// Make three db versions:
|
|
52
|
-
// first updating a different entity
|
|
53
|
-
const sampleDb1 = db.with({ tasks: [{ id: 2, name: 'e2 - updated' }] });
|
|
54
|
-
// second - updating the same entity with the same value
|
|
55
|
-
const sampleDb2 = db.with({ tasks: [{ id: 1, name: 'base' }] });
|
|
56
|
-
// third - updating our entity with new value
|
|
57
|
-
const sampleDb3 = db.with({ tasks: [{ id: 1, name: 'updated' }] });
|
|
58
|
-
|
|
59
|
-
// In first db, the view should keep exactly same (===) existing value in the first fork
|
|
60
|
-
const result1 = sampleDb1.runView(view, 1);
|
|
61
|
-
expect(result1).toBe(baseResult);
|
|
62
|
-
expect(result1).toEqual({ id: 1, name: 'base' });
|
|
63
|
-
|
|
64
|
-
// In the second db, view should still return the same (===) object.
|
|
65
|
-
// It should re-compute the view, but find results the same as previous with deepEQ compare, and re-use previous results
|
|
66
|
-
const result2 = sampleDb2.runView(view, 1);
|
|
67
|
-
expect(result2).toBe(result1);
|
|
68
|
-
expect(result2).toEqual({ id: 1, name: 'base' });
|
|
69
|
-
|
|
70
|
-
// In the third db, view should return a new value
|
|
71
|
-
const result3 = sampleDb3.runView(view, 1);
|
|
72
|
-
expect(result3).toEqual({ id: 1, name: 'updated' });
|
|
73
|
-
|
|
74
|
-
expect(view.compute).toBeCalledTimes(4);
|
|
75
|
-
});
|
|
76
|
-
});
|