@tldraw/tlschema 4.2.1 → 4.2.2
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/dist-cjs/bindings/TLBaseBinding.js.map +2 -2
- package/dist-cjs/createTLSchema.js.map +2 -2
- package/dist-cjs/index.d.ts +242 -71
- package/dist-cjs/index.js +4 -1
- package/dist-cjs/index.js.map +2 -2
- package/dist-cjs/misc/TLOpacity.js +1 -5
- package/dist-cjs/misc/TLOpacity.js.map +2 -2
- package/dist-cjs/misc/TLRichText.js +5 -1
- package/dist-cjs/misc/TLRichText.js.map +2 -2
- package/dist-cjs/misc/b64Vecs.js +224 -0
- package/dist-cjs/misc/b64Vecs.js.map +7 -0
- package/dist-cjs/records/TLAsset.js.map +1 -1
- package/dist-cjs/records/TLBinding.js.map +2 -2
- package/dist-cjs/records/TLShape.js.map +2 -2
- package/dist-cjs/shapes/ShapeWithCrop.js.map +1 -1
- package/dist-cjs/shapes/TLArrowShape.js +26 -13
- package/dist-cjs/shapes/TLArrowShape.js.map +2 -2
- package/dist-cjs/shapes/TLBaseShape.js.map +2 -2
- package/dist-cjs/shapes/TLDrawShape.js +37 -4
- package/dist-cjs/shapes/TLDrawShape.js.map +2 -2
- package/dist-cjs/shapes/TLEmbedShape.js +17 -0
- package/dist-cjs/shapes/TLEmbedShape.js.map +2 -2
- package/dist-cjs/shapes/TLGeoShape.js +12 -1
- package/dist-cjs/shapes/TLGeoShape.js.map +2 -2
- package/dist-cjs/shapes/TLHighlightShape.js +29 -2
- package/dist-cjs/shapes/TLHighlightShape.js.map +2 -2
- package/dist-cjs/shapes/TLNoteShape.js +12 -1
- package/dist-cjs/shapes/TLNoteShape.js.map +2 -2
- package/dist-cjs/shapes/TLTextShape.js +12 -1
- package/dist-cjs/shapes/TLTextShape.js.map +2 -2
- package/dist-cjs/store-migrations.js +15 -15
- package/dist-cjs/store-migrations.js.map +2 -2
- package/dist-esm/bindings/TLBaseBinding.mjs.map +2 -2
- package/dist-esm/createTLSchema.mjs.map +2 -2
- package/dist-esm/index.d.mts +242 -71
- package/dist-esm/index.mjs +5 -1
- package/dist-esm/index.mjs.map +2 -2
- package/dist-esm/misc/TLOpacity.mjs +1 -5
- package/dist-esm/misc/TLOpacity.mjs.map +2 -2
- package/dist-esm/misc/TLRichText.mjs +5 -1
- package/dist-esm/misc/TLRichText.mjs.map +2 -2
- package/dist-esm/misc/b64Vecs.mjs +204 -0
- package/dist-esm/misc/b64Vecs.mjs.map +7 -0
- package/dist-esm/records/TLAsset.mjs.map +1 -1
- package/dist-esm/records/TLBinding.mjs.map +2 -2
- package/dist-esm/records/TLShape.mjs.map +2 -2
- package/dist-esm/shapes/TLArrowShape.mjs +26 -13
- package/dist-esm/shapes/TLArrowShape.mjs.map +2 -2
- package/dist-esm/shapes/TLBaseShape.mjs.map +2 -2
- package/dist-esm/shapes/TLDrawShape.mjs +37 -4
- package/dist-esm/shapes/TLDrawShape.mjs.map +2 -2
- package/dist-esm/shapes/TLEmbedShape.mjs +17 -0
- package/dist-esm/shapes/TLEmbedShape.mjs.map +2 -2
- package/dist-esm/shapes/TLGeoShape.mjs +12 -1
- package/dist-esm/shapes/TLGeoShape.mjs.map +2 -2
- package/dist-esm/shapes/TLHighlightShape.mjs +29 -2
- package/dist-esm/shapes/TLHighlightShape.mjs.map +2 -2
- package/dist-esm/shapes/TLNoteShape.mjs +12 -1
- package/dist-esm/shapes/TLNoteShape.mjs.map +2 -2
- package/dist-esm/shapes/TLTextShape.mjs +12 -1
- package/dist-esm/shapes/TLTextShape.mjs.map +2 -2
- package/dist-esm/store-migrations.mjs +15 -15
- package/dist-esm/store-migrations.mjs.map +2 -2
- package/package.json +8 -8
- package/src/__tests__/migrationTestUtils.ts +9 -3
- package/src/bindings/TLBaseBinding.ts +25 -14
- package/src/createTLSchema.ts +8 -2
- package/src/index.ts +9 -0
- package/src/migrations.test.ts +149 -1
- package/src/misc/TLOpacity.ts +1 -5
- package/src/misc/TLRichText.ts +6 -1
- package/src/misc/b64Vecs.ts +308 -0
- package/src/records/TLAsset.ts +2 -2
- package/src/records/TLBinding.ts +65 -23
- package/src/records/TLShape.ts +100 -5
- package/src/shapes/ShapeWithCrop.ts +2 -2
- package/src/shapes/TLArrowShape.ts +28 -14
- package/src/shapes/TLBaseShape.ts +34 -10
- package/src/shapes/TLDrawShape.ts +59 -12
- package/src/shapes/TLEmbedShape.ts +17 -0
- package/src/shapes/TLGeoShape.ts +14 -1
- package/src/shapes/TLHighlightShape.ts +37 -0
- package/src/shapes/TLNoteShape.ts +15 -1
- package/src/shapes/TLTextShape.ts +16 -2
- package/src/store-migrations.ts +17 -16
- package/src/assets/TLBookmarkAsset.test.ts +0 -96
- package/src/assets/TLImageAsset.test.ts +0 -213
- package/src/assets/TLVideoAsset.test.ts +0 -105
- package/src/bindings/TLArrowBinding.test.ts +0 -55
- package/src/misc/id-validator.test.ts +0 -50
- package/src/records/TLAsset.test.ts +0 -234
- package/src/records/TLBinding.test.ts +0 -22
- package/src/records/TLCamera.test.ts +0 -19
- package/src/records/TLDocument.test.ts +0 -35
- package/src/records/TLInstance.test.ts +0 -201
- package/src/records/TLPage.test.ts +0 -110
- package/src/records/TLPageState.test.ts +0 -228
- package/src/records/TLPointer.test.ts +0 -63
- package/src/records/TLPresence.test.ts +0 -190
- package/src/records/TLRecord.test.ts +0 -70
- package/src/records/TLShape.test.ts +0 -232
- package/src/shapes/ShapeWithCrop.test.ts +0 -18
- package/src/shapes/TLArrowShape.test.ts +0 -505
- package/src/shapes/TLBaseShape.test.ts +0 -142
- package/src/shapes/TLBookmarkShape.test.ts +0 -122
- package/src/shapes/TLDrawShape.test.ts +0 -177
- package/src/shapes/TLEmbedShape.test.ts +0 -286
- package/src/shapes/TLFrameShape.test.ts +0 -71
- package/src/shapes/TLGeoShape.test.ts +0 -247
- package/src/shapes/TLGroupShape.test.ts +0 -59
- package/src/shapes/TLHighlightShape.test.ts +0 -325
- package/src/shapes/TLImageShape.test.ts +0 -534
- package/src/shapes/TLLineShape.test.ts +0 -269
- package/src/shapes/TLNoteShape.test.ts +0 -1568
- package/src/shapes/TLTextShape.test.ts +0 -407
- package/src/shapes/TLVideoShape.test.ts +0 -112
- package/src/styles/TLColorStyle.test.ts +0 -439
|
@@ -1,234 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest'
|
|
2
|
-
import { getTestMigration } from '../__tests__/migrationTestUtils'
|
|
3
|
-
import {
|
|
4
|
-
assetMigrations,
|
|
5
|
-
AssetRecordType,
|
|
6
|
-
assetValidator,
|
|
7
|
-
assetVersions,
|
|
8
|
-
TLAssetId,
|
|
9
|
-
} from './TLAsset'
|
|
10
|
-
|
|
11
|
-
describe('TLAsset', () => {
|
|
12
|
-
describe('assetValidator', () => {
|
|
13
|
-
it('should validate different asset types using discriminated union', () => {
|
|
14
|
-
const imageAsset = {
|
|
15
|
-
id: 'asset:test_image',
|
|
16
|
-
typeName: 'asset' as const,
|
|
17
|
-
type: 'image' as const,
|
|
18
|
-
props: {
|
|
19
|
-
w: 100,
|
|
20
|
-
h: 100,
|
|
21
|
-
name: 'test.jpg',
|
|
22
|
-
isAnimated: false,
|
|
23
|
-
mimeType: 'image/jpeg',
|
|
24
|
-
src: 'https://example.com/test.jpg',
|
|
25
|
-
},
|
|
26
|
-
meta: {},
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
const videoAsset = {
|
|
30
|
-
id: 'asset:test_video',
|
|
31
|
-
typeName: 'asset' as const,
|
|
32
|
-
type: 'video' as const,
|
|
33
|
-
props: {
|
|
34
|
-
w: 640,
|
|
35
|
-
h: 480,
|
|
36
|
-
name: 'test.mp4',
|
|
37
|
-
isAnimated: true,
|
|
38
|
-
mimeType: 'video/mp4',
|
|
39
|
-
src: 'https://example.com/test.mp4',
|
|
40
|
-
},
|
|
41
|
-
meta: {},
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
const bookmarkAsset = {
|
|
45
|
-
id: 'asset:test_bookmark',
|
|
46
|
-
typeName: 'asset' as const,
|
|
47
|
-
type: 'bookmark' as const,
|
|
48
|
-
props: {
|
|
49
|
-
title: 'Test Site',
|
|
50
|
-
description: 'A test bookmark',
|
|
51
|
-
image: 'https://example.com/image.png',
|
|
52
|
-
favicon: 'https://example.com/favicon.ico',
|
|
53
|
-
src: 'https://example.com',
|
|
54
|
-
},
|
|
55
|
-
meta: {},
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
expect(() => assetValidator.validate(imageAsset)).not.toThrow()
|
|
59
|
-
expect(() => assetValidator.validate(videoAsset)).not.toThrow()
|
|
60
|
-
expect(() => assetValidator.validate(bookmarkAsset)).not.toThrow()
|
|
61
|
-
|
|
62
|
-
expect(assetValidator.validate(imageAsset).type).toBe('image')
|
|
63
|
-
expect(assetValidator.validate(videoAsset).type).toBe('video')
|
|
64
|
-
expect(assetValidator.validate(bookmarkAsset).type).toBe('bookmark')
|
|
65
|
-
})
|
|
66
|
-
|
|
67
|
-
it('should reject invalid asset types', () => {
|
|
68
|
-
const invalidAsset = {
|
|
69
|
-
id: 'asset:invalid',
|
|
70
|
-
typeName: 'asset' as const,
|
|
71
|
-
type: 'invalid_type' as any,
|
|
72
|
-
props: {
|
|
73
|
-
w: 100,
|
|
74
|
-
h: 100,
|
|
75
|
-
name: 'test.jpg',
|
|
76
|
-
},
|
|
77
|
-
meta: {},
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
expect(() => assetValidator.validate(invalidAsset)).toThrow()
|
|
81
|
-
})
|
|
82
|
-
|
|
83
|
-
it('should require valid asset structure', () => {
|
|
84
|
-
// Wrong typeName
|
|
85
|
-
const wrongTypeName = {
|
|
86
|
-
id: 'asset:wrong_typename',
|
|
87
|
-
typeName: 'shape' as const,
|
|
88
|
-
type: 'image' as const,
|
|
89
|
-
props: {
|
|
90
|
-
w: 100,
|
|
91
|
-
h: 100,
|
|
92
|
-
name: 'test.jpg',
|
|
93
|
-
isAnimated: false,
|
|
94
|
-
mimeType: 'image/jpeg',
|
|
95
|
-
src: 'https://example.com/test.jpg',
|
|
96
|
-
},
|
|
97
|
-
meta: {},
|
|
98
|
-
}
|
|
99
|
-
expect(() => assetValidator.validate(wrongTypeName)).toThrow()
|
|
100
|
-
|
|
101
|
-
// Missing meta
|
|
102
|
-
const noMeta = {
|
|
103
|
-
id: 'asset:no_meta',
|
|
104
|
-
typeName: 'asset' as const,
|
|
105
|
-
type: 'image' as const,
|
|
106
|
-
props: {
|
|
107
|
-
w: 100,
|
|
108
|
-
h: 100,
|
|
109
|
-
name: 'test.jpg',
|
|
110
|
-
isAnimated: false,
|
|
111
|
-
mimeType: 'image/jpeg',
|
|
112
|
-
src: 'https://example.com/test.jpg',
|
|
113
|
-
},
|
|
114
|
-
}
|
|
115
|
-
expect(() => assetValidator.validate(noMeta)).toThrow()
|
|
116
|
-
|
|
117
|
-
// Wrong ID prefix
|
|
118
|
-
const wrongId = {
|
|
119
|
-
id: 'shape:wrong_prefix',
|
|
120
|
-
typeName: 'asset' as const,
|
|
121
|
-
type: 'image' as const,
|
|
122
|
-
props: {
|
|
123
|
-
w: 100,
|
|
124
|
-
h: 100,
|
|
125
|
-
name: 'test.jpg',
|
|
126
|
-
isAnimated: false,
|
|
127
|
-
mimeType: 'image/jpeg',
|
|
128
|
-
src: 'https://example.com/test.jpg',
|
|
129
|
-
},
|
|
130
|
-
meta: {},
|
|
131
|
-
}
|
|
132
|
-
expect(() => assetValidator.validate(wrongId)).toThrow()
|
|
133
|
-
})
|
|
134
|
-
})
|
|
135
|
-
|
|
136
|
-
describe('assetMigrations', () => {
|
|
137
|
-
it('should have correct migration structure', () => {
|
|
138
|
-
expect(assetMigrations.sequenceId).toBe('com.tldraw.asset')
|
|
139
|
-
expect(assetMigrations.sequence).toHaveLength(1)
|
|
140
|
-
expect(assetMigrations.sequence[0].id).toBe(assetVersions.AddMeta)
|
|
141
|
-
})
|
|
142
|
-
})
|
|
143
|
-
|
|
144
|
-
describe('AddMeta migration', () => {
|
|
145
|
-
const { up } = getTestMigration(assetVersions.AddMeta)
|
|
146
|
-
|
|
147
|
-
it('should add empty meta property', () => {
|
|
148
|
-
const assetWithoutMeta = {
|
|
149
|
-
id: 'asset:test',
|
|
150
|
-
typeName: 'asset',
|
|
151
|
-
type: 'image',
|
|
152
|
-
props: {
|
|
153
|
-
w: 100,
|
|
154
|
-
h: 100,
|
|
155
|
-
name: 'test.jpg',
|
|
156
|
-
isAnimated: false,
|
|
157
|
-
mimeType: 'image/jpeg',
|
|
158
|
-
src: 'https://example.com/test.jpg',
|
|
159
|
-
},
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
const result = up(assetWithoutMeta)
|
|
163
|
-
expect(result.meta).toEqual({})
|
|
164
|
-
expect(result.props).toEqual(assetWithoutMeta.props)
|
|
165
|
-
})
|
|
166
|
-
|
|
167
|
-
it('should overwrite existing meta property', () => {
|
|
168
|
-
const assetWithMeta = {
|
|
169
|
-
id: 'asset:test',
|
|
170
|
-
typeName: 'asset',
|
|
171
|
-
type: 'image',
|
|
172
|
-
props: {
|
|
173
|
-
w: 100,
|
|
174
|
-
h: 100,
|
|
175
|
-
name: 'test.jpg',
|
|
176
|
-
isAnimated: false,
|
|
177
|
-
mimeType: 'image/jpeg',
|
|
178
|
-
src: 'https://example.com/test.jpg',
|
|
179
|
-
},
|
|
180
|
-
meta: { existing: 'data' },
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
const result = up(assetWithMeta)
|
|
184
|
-
expect(result.meta).toEqual({}) // Migration always sets to empty object
|
|
185
|
-
})
|
|
186
|
-
|
|
187
|
-
it('should preserve other properties during migration', () => {
|
|
188
|
-
const assetWithExtraProps = {
|
|
189
|
-
id: 'asset:extra_props',
|
|
190
|
-
typeName: 'asset',
|
|
191
|
-
type: 'image',
|
|
192
|
-
props: {
|
|
193
|
-
w: 200,
|
|
194
|
-
h: 150,
|
|
195
|
-
name: 'extra.png',
|
|
196
|
-
isAnimated: false,
|
|
197
|
-
mimeType: 'image/png',
|
|
198
|
-
src: 'https://example.com/extra.png',
|
|
199
|
-
},
|
|
200
|
-
customProperty: 'should be preserved',
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
const result = up(assetWithExtraProps)
|
|
204
|
-
expect(result.meta).toEqual({})
|
|
205
|
-
expect(result.customProperty).toBe('should be preserved')
|
|
206
|
-
})
|
|
207
|
-
})
|
|
208
|
-
|
|
209
|
-
describe('AssetRecordType', () => {
|
|
210
|
-
it('should have correct configuration', () => {
|
|
211
|
-
expect(AssetRecordType.typeName).toBe('asset')
|
|
212
|
-
expect(AssetRecordType.scope).toBe('document')
|
|
213
|
-
expect(AssetRecordType.validator).toBe(assetValidator)
|
|
214
|
-
})
|
|
215
|
-
|
|
216
|
-
it('should create records with default meta property', () => {
|
|
217
|
-
const assetRecord = AssetRecordType.create({
|
|
218
|
-
id: 'asset:test' as TLAssetId,
|
|
219
|
-
type: 'image',
|
|
220
|
-
props: {
|
|
221
|
-
w: 100,
|
|
222
|
-
h: 100,
|
|
223
|
-
name: 'test.jpg',
|
|
224
|
-
isAnimated: false,
|
|
225
|
-
mimeType: 'image/jpeg',
|
|
226
|
-
src: 'https://example.com/test.jpg',
|
|
227
|
-
},
|
|
228
|
-
})
|
|
229
|
-
|
|
230
|
-
expect(assetRecord.meta).toEqual({})
|
|
231
|
-
expect(assetRecord.typeName).toBe('asset')
|
|
232
|
-
})
|
|
233
|
-
})
|
|
234
|
-
})
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest'
|
|
2
|
-
import { createBindingId, rootBindingMigrations } from './TLBinding'
|
|
3
|
-
|
|
4
|
-
describe('TLBinding', () => {
|
|
5
|
-
describe('createBindingId function', () => {
|
|
6
|
-
it('should generate IDs starting with binding:', () => {
|
|
7
|
-
const id = createBindingId()
|
|
8
|
-
expect(id.startsWith('binding:')).toBe(true)
|
|
9
|
-
})
|
|
10
|
-
|
|
11
|
-
it('should use custom ID when provided', () => {
|
|
12
|
-
expect(createBindingId('test')).toBe('binding:test')
|
|
13
|
-
})
|
|
14
|
-
})
|
|
15
|
-
|
|
16
|
-
describe('rootBindingMigrations', () => {
|
|
17
|
-
it('should have correct structure', () => {
|
|
18
|
-
expect(rootBindingMigrations.sequenceId).toBe('com.tldraw.binding')
|
|
19
|
-
expect(Array.isArray(rootBindingMigrations.sequence)).toBe(true)
|
|
20
|
-
})
|
|
21
|
-
})
|
|
22
|
-
})
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest'
|
|
2
|
-
import { cameraMigrations, cameraVersions } from './TLCamera'
|
|
3
|
-
|
|
4
|
-
describe('cameraMigrations', () => {
|
|
5
|
-
it('should apply AddMeta migration correctly', () => {
|
|
6
|
-
const addMetaMigration = cameraMigrations.sequence.find((m) => m.id === cameraVersions.AddMeta)!
|
|
7
|
-
|
|
8
|
-
const oldRecord: any = {
|
|
9
|
-
typeName: 'camera',
|
|
10
|
-
id: 'camera:test',
|
|
11
|
-
x: 100,
|
|
12
|
-
y: 200,
|
|
13
|
-
z: 0.5,
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
addMetaMigration.up(oldRecord)
|
|
17
|
-
expect(oldRecord.meta).toEqual({})
|
|
18
|
-
})
|
|
19
|
-
})
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest'
|
|
2
|
-
import { documentMigrations, documentVersions, TLDOCUMENT_ID } from './TLDocument'
|
|
3
|
-
|
|
4
|
-
describe('documentMigrations', () => {
|
|
5
|
-
it('should apply AddName migration correctly', () => {
|
|
6
|
-
const addNameMigration = documentMigrations.sequence.find(
|
|
7
|
-
(m) => m.id === documentVersions.AddName
|
|
8
|
-
)!
|
|
9
|
-
|
|
10
|
-
const oldRecord: any = {
|
|
11
|
-
typeName: 'document',
|
|
12
|
-
id: TLDOCUMENT_ID,
|
|
13
|
-
gridSize: 10,
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
addNameMigration.up(oldRecord)
|
|
17
|
-
expect(oldRecord.name).toBe('')
|
|
18
|
-
})
|
|
19
|
-
|
|
20
|
-
it('should apply AddMeta migration correctly', () => {
|
|
21
|
-
const addMetaMigration = documentMigrations.sequence.find(
|
|
22
|
-
(m) => m.id === documentVersions.AddMeta
|
|
23
|
-
)!
|
|
24
|
-
|
|
25
|
-
const oldRecord: any = {
|
|
26
|
-
typeName: 'document',
|
|
27
|
-
id: TLDOCUMENT_ID,
|
|
28
|
-
gridSize: 10,
|
|
29
|
-
name: 'Test',
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
addMetaMigration.up(oldRecord)
|
|
33
|
-
expect(oldRecord.meta).toEqual({})
|
|
34
|
-
})
|
|
35
|
-
})
|
|
@@ -1,201 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest'
|
|
2
|
-
import { StyleProp } from '../styles/StyleProp'
|
|
3
|
-
import {
|
|
4
|
-
createInstanceRecordType,
|
|
5
|
-
instanceIdValidator,
|
|
6
|
-
instanceMigrations,
|
|
7
|
-
instanceVersions,
|
|
8
|
-
pluckPreservingValues,
|
|
9
|
-
shouldKeyBePreservedBetweenSessions,
|
|
10
|
-
TLInstance,
|
|
11
|
-
TLINSTANCE_ID,
|
|
12
|
-
} from './TLInstance'
|
|
13
|
-
|
|
14
|
-
// Mock style prop for testing
|
|
15
|
-
const mockColorStyle = {
|
|
16
|
-
type: 'color',
|
|
17
|
-
defaultValue: 'black',
|
|
18
|
-
getDefaultValue: () => 'black',
|
|
19
|
-
} as unknown as StyleProp<string>
|
|
20
|
-
|
|
21
|
-
const mockStylesMap = new Map([['color', mockColorStyle]])
|
|
22
|
-
createInstanceRecordType(mockStylesMap)
|
|
23
|
-
|
|
24
|
-
describe('shouldKeyBePreservedBetweenSessions', () => {
|
|
25
|
-
it('should preserve user preferences', () => {
|
|
26
|
-
const userPreferences = [
|
|
27
|
-
'isFocusMode',
|
|
28
|
-
'isDebugMode',
|
|
29
|
-
'isToolLocked',
|
|
30
|
-
'exportBackground',
|
|
31
|
-
'isGridMode',
|
|
32
|
-
'isReadonly',
|
|
33
|
-
]
|
|
34
|
-
|
|
35
|
-
userPreferences.forEach((key) => {
|
|
36
|
-
expect(
|
|
37
|
-
shouldKeyBePreservedBetweenSessions[key as keyof typeof shouldKeyBePreservedBetweenSessions]
|
|
38
|
-
).toBe(true)
|
|
39
|
-
})
|
|
40
|
-
})
|
|
41
|
-
|
|
42
|
-
it('should not preserve temporary state', () => {
|
|
43
|
-
const temporaryState = [
|
|
44
|
-
'currentPageId',
|
|
45
|
-
'opacityForNextShape',
|
|
46
|
-
'stylesForNextShape',
|
|
47
|
-
'followingUserId',
|
|
48
|
-
'brush',
|
|
49
|
-
'cursor',
|
|
50
|
-
'scribbles',
|
|
51
|
-
'zoomBrush',
|
|
52
|
-
'chatMessage',
|
|
53
|
-
'isChatting',
|
|
54
|
-
'isPenMode',
|
|
55
|
-
'isHoveringCanvas',
|
|
56
|
-
'openMenus',
|
|
57
|
-
'isChangingStyle',
|
|
58
|
-
'duplicateProps',
|
|
59
|
-
]
|
|
60
|
-
|
|
61
|
-
temporaryState.forEach((key) => {
|
|
62
|
-
expect(
|
|
63
|
-
shouldKeyBePreservedBetweenSessions[key as keyof typeof shouldKeyBePreservedBetweenSessions]
|
|
64
|
-
).toBe(false)
|
|
65
|
-
})
|
|
66
|
-
})
|
|
67
|
-
})
|
|
68
|
-
|
|
69
|
-
describe('pluckPreservingValues', () => {
|
|
70
|
-
it('should return null for null or undefined input', () => {
|
|
71
|
-
expect(pluckPreservingValues(null)).toBe(null)
|
|
72
|
-
expect(pluckPreservingValues(undefined)).toBe(null)
|
|
73
|
-
})
|
|
74
|
-
|
|
75
|
-
it('should filter properties according to preservation rules', () => {
|
|
76
|
-
const fullInstance: TLInstance = {
|
|
77
|
-
id: TLINSTANCE_ID,
|
|
78
|
-
typeName: 'instance',
|
|
79
|
-
currentPageId: 'page:page1' as any,
|
|
80
|
-
opacityForNextShape: 0.5,
|
|
81
|
-
stylesForNextShape: {},
|
|
82
|
-
followingUserId: null,
|
|
83
|
-
highlightedUserIds: [],
|
|
84
|
-
brush: null,
|
|
85
|
-
cursor: { type: 'default', rotation: 0 },
|
|
86
|
-
scribbles: [],
|
|
87
|
-
isFocusMode: true,
|
|
88
|
-
isDebugMode: false,
|
|
89
|
-
isToolLocked: true,
|
|
90
|
-
exportBackground: true,
|
|
91
|
-
screenBounds: { x: 0, y: 0, w: 1920, h: 1080 },
|
|
92
|
-
insets: [false, false, false, false],
|
|
93
|
-
zoomBrush: null,
|
|
94
|
-
chatMessage: '',
|
|
95
|
-
isChatting: false,
|
|
96
|
-
isPenMode: false,
|
|
97
|
-
isGridMode: true,
|
|
98
|
-
isFocused: true,
|
|
99
|
-
devicePixelRatio: 2,
|
|
100
|
-
isCoarsePointer: false,
|
|
101
|
-
isHoveringCanvas: null,
|
|
102
|
-
openMenus: [],
|
|
103
|
-
isChangingStyle: false,
|
|
104
|
-
isReadonly: false,
|
|
105
|
-
meta: {},
|
|
106
|
-
duplicateProps: null,
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
const preserved = pluckPreservingValues(fullInstance)
|
|
110
|
-
|
|
111
|
-
// Should preserve user preferences
|
|
112
|
-
expect(preserved?.isFocusMode).toBe(true)
|
|
113
|
-
expect(preserved?.isDebugMode).toBe(false)
|
|
114
|
-
expect(preserved?.isToolLocked).toBe(true)
|
|
115
|
-
expect(preserved?.exportBackground).toBe(true)
|
|
116
|
-
expect(preserved?.isGridMode).toBe(true)
|
|
117
|
-
|
|
118
|
-
// Should not preserve temporary state
|
|
119
|
-
expect(preserved?.currentPageId).toBeUndefined()
|
|
120
|
-
expect(preserved?.opacityForNextShape).toBeUndefined()
|
|
121
|
-
expect(preserved?.brush).toBeUndefined()
|
|
122
|
-
expect(preserved?.cursor).toBeUndefined()
|
|
123
|
-
expect(preserved?.chatMessage).toBeUndefined()
|
|
124
|
-
expect(preserved?.openMenus).toBeUndefined()
|
|
125
|
-
})
|
|
126
|
-
})
|
|
127
|
-
|
|
128
|
-
describe('instanceIdValidator', () => {
|
|
129
|
-
it('should validate correct instance IDs and reject invalid ones', () => {
|
|
130
|
-
expect(() => instanceIdValidator.validate('instance:instance')).not.toThrow()
|
|
131
|
-
expect(() => instanceIdValidator.validate('instance:test')).not.toThrow()
|
|
132
|
-
expect(() => instanceIdValidator.validate(TLINSTANCE_ID)).not.toThrow()
|
|
133
|
-
|
|
134
|
-
expect(() => instanceIdValidator.validate('invalid')).toThrow()
|
|
135
|
-
expect(() => instanceIdValidator.validate('page:instance')).toThrow()
|
|
136
|
-
expect(() => instanceIdValidator.validate('')).toThrow()
|
|
137
|
-
})
|
|
138
|
-
})
|
|
139
|
-
|
|
140
|
-
describe('createInstanceRecordType', () => {
|
|
141
|
-
it('should create a valid record type with correct configuration', () => {
|
|
142
|
-
const recordType = createInstanceRecordType(mockStylesMap)
|
|
143
|
-
expect(recordType.typeName).toBe('instance')
|
|
144
|
-
expect(recordType.scope).toBe('session')
|
|
145
|
-
})
|
|
146
|
-
})
|
|
147
|
-
|
|
148
|
-
describe('instanceMigrations', () => {
|
|
149
|
-
it('should have correct migration configuration', () => {
|
|
150
|
-
expect(instanceMigrations.sequenceId).toBe('com.tldraw.instance')
|
|
151
|
-
expect(Array.isArray(instanceMigrations.sequence)).toBe(true)
|
|
152
|
-
expect(instanceMigrations.sequence.length).toBe(25)
|
|
153
|
-
})
|
|
154
|
-
|
|
155
|
-
it('should migrate HoistOpacity correctly', () => {
|
|
156
|
-
const migration = instanceMigrations.sequence.find(
|
|
157
|
-
(m) => m.id === instanceVersions.HoistOpacity
|
|
158
|
-
)!
|
|
159
|
-
const oldRecord: any = {
|
|
160
|
-
id: TLINSTANCE_ID,
|
|
161
|
-
typeName: 'instance',
|
|
162
|
-
propsForNextShape: {
|
|
163
|
-
opacity: '0.5',
|
|
164
|
-
color: 'red',
|
|
165
|
-
},
|
|
166
|
-
}
|
|
167
|
-
const result = migration.up(oldRecord)
|
|
168
|
-
|
|
169
|
-
expect((result as any).opacityForNextShape).toBe(0.5)
|
|
170
|
-
expect((result as any).propsForNextShape.opacity).toBeUndefined()
|
|
171
|
-
expect((result as any).propsForNextShape.color).toBe('red')
|
|
172
|
-
})
|
|
173
|
-
|
|
174
|
-
it('should migrate RemoveDialog correctly', () => {
|
|
175
|
-
const migration = instanceMigrations.sequence.find(
|
|
176
|
-
(m) => m.id === instanceVersions.RemoveDialog
|
|
177
|
-
)!
|
|
178
|
-
const oldRecord: any = {
|
|
179
|
-
id: TLINSTANCE_ID,
|
|
180
|
-
typeName: 'instance',
|
|
181
|
-
dialog: 'some-dialog',
|
|
182
|
-
otherProp: 'keep-me',
|
|
183
|
-
}
|
|
184
|
-
const result = migration.up(oldRecord)
|
|
185
|
-
|
|
186
|
-
expect((result as any).dialog).toBeUndefined()
|
|
187
|
-
expect((result as any).otherProp).toBe('keep-me')
|
|
188
|
-
})
|
|
189
|
-
|
|
190
|
-
it('should have bidirectional migrations where applicable', () => {
|
|
191
|
-
const addInsetMigration = instanceMigrations.sequence.find(
|
|
192
|
-
(m) => m.id === instanceVersions.AddInset
|
|
193
|
-
)!
|
|
194
|
-
expect(addInsetMigration.down).toBeDefined()
|
|
195
|
-
|
|
196
|
-
const removeCameraMigration = instanceMigrations.sequence.find(
|
|
197
|
-
(m) => m.id === instanceVersions.RemoveCanMoveCamera
|
|
198
|
-
)!
|
|
199
|
-
expect(removeCameraMigration.down).toBeDefined()
|
|
200
|
-
})
|
|
201
|
-
})
|
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest'
|
|
2
|
-
import {
|
|
3
|
-
isPageId,
|
|
4
|
-
pageIdValidator,
|
|
5
|
-
pageMigrations,
|
|
6
|
-
PageRecordType,
|
|
7
|
-
pageValidator,
|
|
8
|
-
pageVersions,
|
|
9
|
-
TLPageId,
|
|
10
|
-
} from './TLPage'
|
|
11
|
-
|
|
12
|
-
describe('pageIdValidator', () => {
|
|
13
|
-
it('should validate correct page IDs', () => {
|
|
14
|
-
expect(() => pageIdValidator.validate('page:main')).not.toThrow()
|
|
15
|
-
expect(() => pageIdValidator.validate('page:page1')).not.toThrow()
|
|
16
|
-
})
|
|
17
|
-
|
|
18
|
-
it('should reject invalid page IDs', () => {
|
|
19
|
-
expect(() => pageIdValidator.validate('invalid')).toThrow()
|
|
20
|
-
expect(() => pageIdValidator.validate('shape:page1')).toThrow()
|
|
21
|
-
expect(() => pageIdValidator.validate('')).toThrow()
|
|
22
|
-
})
|
|
23
|
-
})
|
|
24
|
-
|
|
25
|
-
describe('pageValidator', () => {
|
|
26
|
-
it('should validate valid page records', () => {
|
|
27
|
-
const validPage = {
|
|
28
|
-
typeName: 'page',
|
|
29
|
-
id: 'page:test' as TLPageId,
|
|
30
|
-
name: 'Test Page',
|
|
31
|
-
index: 'a1' as any,
|
|
32
|
-
meta: {},
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
expect(() => pageValidator.validate(validPage)).not.toThrow()
|
|
36
|
-
})
|
|
37
|
-
|
|
38
|
-
it('should reject pages with invalid typeName', () => {
|
|
39
|
-
const invalidPage = {
|
|
40
|
-
typeName: 'not-page',
|
|
41
|
-
id: 'page:test' as TLPageId,
|
|
42
|
-
name: 'Test',
|
|
43
|
-
index: 'a1' as any,
|
|
44
|
-
meta: {},
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
expect(() => pageValidator.validate(invalidPage)).toThrow()
|
|
48
|
-
})
|
|
49
|
-
|
|
50
|
-
it('should reject pages with missing required fields', () => {
|
|
51
|
-
const incompletePages = [
|
|
52
|
-
{
|
|
53
|
-
typeName: 'page',
|
|
54
|
-
id: 'page:test' as TLPageId,
|
|
55
|
-
// missing name, index, meta
|
|
56
|
-
},
|
|
57
|
-
{
|
|
58
|
-
typeName: 'page',
|
|
59
|
-
id: 'page:test' as TLPageId,
|
|
60
|
-
name: 'Test',
|
|
61
|
-
// missing index, meta
|
|
62
|
-
},
|
|
63
|
-
]
|
|
64
|
-
|
|
65
|
-
incompletePages.forEach((page) => {
|
|
66
|
-
expect(() => pageValidator.validate(page)).toThrow()
|
|
67
|
-
})
|
|
68
|
-
})
|
|
69
|
-
})
|
|
70
|
-
|
|
71
|
-
describe('pageMigrations', () => {
|
|
72
|
-
it('should apply AddMeta migration correctly', () => {
|
|
73
|
-
const addMetaMigration = pageMigrations.sequence.find((m) => m.id === pageVersions.AddMeta)!
|
|
74
|
-
|
|
75
|
-
const oldRecord: any = {
|
|
76
|
-
typeName: 'page',
|
|
77
|
-
id: 'page:test',
|
|
78
|
-
name: 'Test Page',
|
|
79
|
-
index: 'a1',
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
addMetaMigration.up(oldRecord)
|
|
83
|
-
expect(oldRecord.meta).toEqual({})
|
|
84
|
-
})
|
|
85
|
-
})
|
|
86
|
-
|
|
87
|
-
describe('PageRecordType', () => {
|
|
88
|
-
it('should create page records with defaults', () => {
|
|
89
|
-
const page = PageRecordType.create({
|
|
90
|
-
id: 'page:test' as TLPageId,
|
|
91
|
-
name: 'Test Page',
|
|
92
|
-
index: 'a1' as any,
|
|
93
|
-
})
|
|
94
|
-
|
|
95
|
-
expect(page.meta).toEqual({})
|
|
96
|
-
})
|
|
97
|
-
})
|
|
98
|
-
|
|
99
|
-
describe('isPageId', () => {
|
|
100
|
-
it('should return true for valid page IDs', () => {
|
|
101
|
-
expect(isPageId('page:main')).toBe(true)
|
|
102
|
-
expect(isPageId('page:page1')).toBe(true)
|
|
103
|
-
})
|
|
104
|
-
|
|
105
|
-
it('should return false for invalid page IDs', () => {
|
|
106
|
-
expect(isPageId('shape:main')).toBe(false)
|
|
107
|
-
expect(isPageId('invalid')).toBe(false)
|
|
108
|
-
expect(isPageId('')).toBe(false)
|
|
109
|
-
})
|
|
110
|
-
})
|