@bigbluebutton/tlschema 2.0.0-alpha.19

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.
Files changed (261) hide show
  1. package/LICENSE +190 -0
  2. package/dist-cjs/TLStore.js +133 -0
  3. package/dist-cjs/TLStore.js.map +7 -0
  4. package/dist-cjs/assets/TLBaseAsset.js +37 -0
  5. package/dist-cjs/assets/TLBaseAsset.js.map +7 -0
  6. package/dist-cjs/assets/TLBookmarkAsset.js +38 -0
  7. package/dist-cjs/assets/TLBookmarkAsset.js.map +7 -0
  8. package/dist-cjs/assets/TLImageAsset.js +76 -0
  9. package/dist-cjs/assets/TLImageAsset.js.map +7 -0
  10. package/dist-cjs/assets/TLVideoAsset.js +76 -0
  11. package/dist-cjs/assets/TLVideoAsset.js.map +7 -0
  12. package/dist-cjs/createPresenceStateDerivation.js +68 -0
  13. package/dist-cjs/createPresenceStateDerivation.js.map +7 -0
  14. package/dist-cjs/createTLSchema.js +68 -0
  15. package/dist-cjs/createTLSchema.js.map +7 -0
  16. package/dist-cjs/index.d.ts +1414 -0
  17. package/dist-cjs/index.js +142 -0
  18. package/dist-cjs/index.js.map +7 -0
  19. package/dist-cjs/misc/TLColor.js +36 -0
  20. package/dist-cjs/misc/TLColor.js.map +7 -0
  21. package/dist-cjs/misc/TLCursor.js +55 -0
  22. package/dist-cjs/misc/TLCursor.js.map +7 -0
  23. package/dist-cjs/misc/TLHandle.js +36 -0
  24. package/dist-cjs/misc/TLHandle.js.map +7 -0
  25. package/dist-cjs/misc/TLOpacity.js +30 -0
  26. package/dist-cjs/misc/TLOpacity.js.map +7 -0
  27. package/dist-cjs/misc/TLScribble.js +40 -0
  28. package/dist-cjs/misc/TLScribble.js.map +7 -0
  29. package/dist-cjs/misc/geometry-types.js +37 -0
  30. package/dist-cjs/misc/geometry-types.js.map +7 -0
  31. package/dist-cjs/misc/id-validator.js +33 -0
  32. package/dist-cjs/misc/id-validator.js.map +7 -0
  33. package/dist-cjs/records/TLAsset.js +74 -0
  34. package/dist-cjs/records/TLAsset.js.map +7 -0
  35. package/dist-cjs/records/TLCamera.js +74 -0
  36. package/dist-cjs/records/TLCamera.js.map +7 -0
  37. package/dist-cjs/records/TLDocument.js +82 -0
  38. package/dist-cjs/records/TLDocument.js.map +7 -0
  39. package/dist-cjs/records/TLInstance.js +452 -0
  40. package/dist-cjs/records/TLInstance.js.map +7 -0
  41. package/dist-cjs/records/TLPage.js +74 -0
  42. package/dist-cjs/records/TLPage.js.map +7 -0
  43. package/dist-cjs/records/TLPageState.js +209 -0
  44. package/dist-cjs/records/TLPageState.js.map +7 -0
  45. package/dist-cjs/records/TLPointer.js +76 -0
  46. package/dist-cjs/records/TLPointer.js.map +7 -0
  47. package/dist-cjs/records/TLPresence.js +170 -0
  48. package/dist-cjs/records/TLPresence.js.map +7 -0
  49. package/dist-cjs/records/TLRecord.js +17 -0
  50. package/dist-cjs/records/TLRecord.js.map +7 -0
  51. package/dist-cjs/records/TLShape.js +147 -0
  52. package/dist-cjs/records/TLShape.js.map +7 -0
  53. package/dist-cjs/shapes/TLArrowShape.js +156 -0
  54. package/dist-cjs/shapes/TLArrowShape.js.map +7 -0
  55. package/dist-cjs/shapes/TLBaseShape.js +52 -0
  56. package/dist-cjs/shapes/TLBaseShape.js.map +7 -0
  57. package/dist-cjs/shapes/TLBookmarkShape.js +57 -0
  58. package/dist-cjs/shapes/TLBookmarkShape.js.map +7 -0
  59. package/dist-cjs/shapes/TLDrawShape.js +89 -0
  60. package/dist-cjs/shapes/TLDrawShape.js.map +7 -0
  61. package/dist-cjs/shapes/TLEmbedShape.js +709 -0
  62. package/dist-cjs/shapes/TLEmbedShape.js.map +7 -0
  63. package/dist-cjs/shapes/TLFrameShape.js +33 -0
  64. package/dist-cjs/shapes/TLFrameShape.js.map +7 -0
  65. package/dist-cjs/shapes/TLGeoShape.js +230 -0
  66. package/dist-cjs/shapes/TLGeoShape.js.map +7 -0
  67. package/dist-cjs/shapes/TLGroupShape.js +28 -0
  68. package/dist-cjs/shapes/TLGroupShape.js.map +7 -0
  69. package/dist-cjs/shapes/TLHighlightShape.js +38 -0
  70. package/dist-cjs/shapes/TLHighlightShape.js.map +7 -0
  71. package/dist-cjs/shapes/TLImageShape.js +69 -0
  72. package/dist-cjs/shapes/TLImageShape.js.map +7 -0
  73. package/dist-cjs/shapes/TLLineShape.js +70 -0
  74. package/dist-cjs/shapes/TLLineShape.js.map +7 -0
  75. package/dist-cjs/shapes/TLNoteShape.js +144 -0
  76. package/dist-cjs/shapes/TLNoteShape.js.map +7 -0
  77. package/dist-cjs/shapes/TLTextShape.js +67 -0
  78. package/dist-cjs/shapes/TLTextShape.js.map +7 -0
  79. package/dist-cjs/shapes/TLVideoShape.js +53 -0
  80. package/dist-cjs/shapes/TLVideoShape.js.map +7 -0
  81. package/dist-cjs/store-migrations.js +81 -0
  82. package/dist-cjs/store-migrations.js.map +7 -0
  83. package/dist-cjs/styles/StyleProp.js +94 -0
  84. package/dist-cjs/styles/StyleProp.js.map +7 -0
  85. package/dist-cjs/styles/TLColorStyle.js +283 -0
  86. package/dist-cjs/styles/TLColorStyle.js.map +7 -0
  87. package/dist-cjs/styles/TLDashStyle.js +29 -0
  88. package/dist-cjs/styles/TLDashStyle.js.map +7 -0
  89. package/dist-cjs/styles/TLFillStyle.js +29 -0
  90. package/dist-cjs/styles/TLFillStyle.js.map +7 -0
  91. package/dist-cjs/styles/TLFontStyle.js +36 -0
  92. package/dist-cjs/styles/TLFontStyle.js.map +7 -0
  93. package/dist-cjs/styles/TLHorizontalAlignStyle.js +29 -0
  94. package/dist-cjs/styles/TLHorizontalAlignStyle.js.map +7 -0
  95. package/dist-cjs/styles/TLSizeStyle.js +29 -0
  96. package/dist-cjs/styles/TLSizeStyle.js.map +7 -0
  97. package/dist-cjs/styles/TLVerticalAlignStyle.js +29 -0
  98. package/dist-cjs/styles/TLVerticalAlignStyle.js.map +7 -0
  99. package/dist-cjs/translations/languages.js +59 -0
  100. package/dist-cjs/translations/languages.js.map +7 -0
  101. package/dist-cjs/translations/translations.js +63 -0
  102. package/dist-cjs/translations/translations.js.map +7 -0
  103. package/dist-cjs/util-types.js +17 -0
  104. package/dist-cjs/util-types.js.map +7 -0
  105. package/dist-esm/TLStore.mjs +113 -0
  106. package/dist-esm/TLStore.mjs.map +7 -0
  107. package/dist-esm/assets/TLBaseAsset.mjs +17 -0
  108. package/dist-esm/assets/TLBaseAsset.mjs.map +7 -0
  109. package/dist-esm/assets/TLBookmarkAsset.mjs +18 -0
  110. package/dist-esm/assets/TLBookmarkAsset.mjs.map +7 -0
  111. package/dist-esm/assets/TLImageAsset.mjs +56 -0
  112. package/dist-esm/assets/TLImageAsset.mjs.map +7 -0
  113. package/dist-esm/assets/TLVideoAsset.mjs +56 -0
  114. package/dist-esm/assets/TLVideoAsset.mjs.map +7 -0
  115. package/dist-esm/createPresenceStateDerivation.mjs +48 -0
  116. package/dist-esm/createPresenceStateDerivation.mjs.map +7 -0
  117. package/dist-esm/createTLSchema.mjs +48 -0
  118. package/dist-esm/createTLSchema.mjs.map +7 -0
  119. package/dist-esm/index.d.mts +1414 -0
  120. package/dist-esm/index.mjs +193 -0
  121. package/dist-esm/index.mjs.map +7 -0
  122. package/dist-esm/misc/TLColor.mjs +16 -0
  123. package/dist-esm/misc/TLColor.mjs.map +7 -0
  124. package/dist-esm/misc/TLCursor.mjs +35 -0
  125. package/dist-esm/misc/TLCursor.mjs.map +7 -0
  126. package/dist-esm/misc/TLHandle.mjs +16 -0
  127. package/dist-esm/misc/TLHandle.mjs.map +7 -0
  128. package/dist-esm/misc/TLOpacity.mjs +10 -0
  129. package/dist-esm/misc/TLOpacity.mjs.map +7 -0
  130. package/dist-esm/misc/TLScribble.mjs +20 -0
  131. package/dist-esm/misc/TLScribble.mjs.map +7 -0
  132. package/dist-esm/misc/geometry-types.mjs +17 -0
  133. package/dist-esm/misc/geometry-types.mjs.map +7 -0
  134. package/dist-esm/misc/id-validator.mjs +13 -0
  135. package/dist-esm/misc/id-validator.mjs.map +7 -0
  136. package/dist-esm/records/TLAsset.mjs +57 -0
  137. package/dist-esm/records/TLAsset.mjs.map +7 -0
  138. package/dist-esm/records/TLCamera.mjs +54 -0
  139. package/dist-esm/records/TLCamera.mjs.map +7 -0
  140. package/dist-esm/records/TLDocument.mjs +62 -0
  141. package/dist-esm/records/TLDocument.mjs.map +7 -0
  142. package/dist-esm/records/TLInstance.mjs +432 -0
  143. package/dist-esm/records/TLInstance.mjs.map +7 -0
  144. package/dist-esm/records/TLPage.mjs +54 -0
  145. package/dist-esm/records/TLPage.mjs.map +7 -0
  146. package/dist-esm/records/TLPageState.mjs +189 -0
  147. package/dist-esm/records/TLPageState.mjs.map +7 -0
  148. package/dist-esm/records/TLPointer.mjs +56 -0
  149. package/dist-esm/records/TLPointer.mjs.map +7 -0
  150. package/dist-esm/records/TLPresence.mjs +150 -0
  151. package/dist-esm/records/TLPresence.mjs.map +7 -0
  152. package/dist-esm/records/TLRecord.mjs +1 -0
  153. package/dist-esm/records/TLRecord.mjs.map +7 -0
  154. package/dist-esm/records/TLShape.mjs +127 -0
  155. package/dist-esm/records/TLShape.mjs.map +7 -0
  156. package/dist-esm/shapes/TLArrowShape.mjs +136 -0
  157. package/dist-esm/shapes/TLArrowShape.mjs.map +7 -0
  158. package/dist-esm/shapes/TLBaseShape.mjs +32 -0
  159. package/dist-esm/shapes/TLBaseShape.mjs.map +7 -0
  160. package/dist-esm/shapes/TLBookmarkShape.mjs +37 -0
  161. package/dist-esm/shapes/TLBookmarkShape.mjs.map +7 -0
  162. package/dist-esm/shapes/TLDrawShape.mjs +69 -0
  163. package/dist-esm/shapes/TLDrawShape.mjs.map +7 -0
  164. package/dist-esm/shapes/TLEmbedShape.mjs +689 -0
  165. package/dist-esm/shapes/TLEmbedShape.mjs.map +7 -0
  166. package/dist-esm/shapes/TLFrameShape.mjs +13 -0
  167. package/dist-esm/shapes/TLFrameShape.mjs.map +7 -0
  168. package/dist-esm/shapes/TLGeoShape.mjs +212 -0
  169. package/dist-esm/shapes/TLGeoShape.mjs.map +7 -0
  170. package/dist-esm/shapes/TLGroupShape.mjs +8 -0
  171. package/dist-esm/shapes/TLGroupShape.mjs.map +7 -0
  172. package/dist-esm/shapes/TLHighlightShape.mjs +18 -0
  173. package/dist-esm/shapes/TLHighlightShape.mjs.map +7 -0
  174. package/dist-esm/shapes/TLImageShape.mjs +49 -0
  175. package/dist-esm/shapes/TLImageShape.mjs.map +7 -0
  176. package/dist-esm/shapes/TLLineShape.mjs +50 -0
  177. package/dist-esm/shapes/TLLineShape.mjs.map +7 -0
  178. package/dist-esm/shapes/TLNoteShape.mjs +126 -0
  179. package/dist-esm/shapes/TLNoteShape.mjs.map +7 -0
  180. package/dist-esm/shapes/TLTextShape.mjs +47 -0
  181. package/dist-esm/shapes/TLTextShape.mjs.map +7 -0
  182. package/dist-esm/shapes/TLVideoShape.mjs +33 -0
  183. package/dist-esm/shapes/TLVideoShape.mjs.map +7 -0
  184. package/dist-esm/store-migrations.mjs +61 -0
  185. package/dist-esm/store-migrations.mjs.map +7 -0
  186. package/dist-esm/styles/StyleProp.mjs +74 -0
  187. package/dist-esm/styles/StyleProp.mjs.map +7 -0
  188. package/dist-esm/styles/TLColorStyle.mjs +263 -0
  189. package/dist-esm/styles/TLColorStyle.mjs.map +7 -0
  190. package/dist-esm/styles/TLDashStyle.mjs +9 -0
  191. package/dist-esm/styles/TLDashStyle.mjs.map +7 -0
  192. package/dist-esm/styles/TLFillStyle.mjs +9 -0
  193. package/dist-esm/styles/TLFillStyle.mjs.map +7 -0
  194. package/dist-esm/styles/TLFontStyle.mjs +16 -0
  195. package/dist-esm/styles/TLFontStyle.mjs.map +7 -0
  196. package/dist-esm/styles/TLHorizontalAlignStyle.mjs +9 -0
  197. package/dist-esm/styles/TLHorizontalAlignStyle.mjs.map +7 -0
  198. package/dist-esm/styles/TLSizeStyle.mjs +9 -0
  199. package/dist-esm/styles/TLSizeStyle.mjs.map +7 -0
  200. package/dist-esm/styles/TLVerticalAlignStyle.mjs +9 -0
  201. package/dist-esm/styles/TLVerticalAlignStyle.mjs.map +7 -0
  202. package/dist-esm/translations/languages.mjs +39 -0
  203. package/dist-esm/translations/languages.mjs.map +7 -0
  204. package/dist-esm/translations/translations.mjs +43 -0
  205. package/dist-esm/translations/translations.mjs.map +7 -0
  206. package/dist-esm/util-types.mjs +1 -0
  207. package/dist-esm/util-types.mjs.map +7 -0
  208. package/package.json +64 -0
  209. package/src/TLStore.ts +160 -0
  210. package/src/assets/TLBaseAsset.ts +44 -0
  211. package/src/assets/TLBookmarkAsset.ts +31 -0
  212. package/src/assets/TLImageAsset.ts +73 -0
  213. package/src/assets/TLVideoAsset.ts +73 -0
  214. package/src/createPresenceStateDerivation.ts +53 -0
  215. package/src/createTLSchema.ts +65 -0
  216. package/src/index.ts +168 -0
  217. package/src/migrations.test.ts +1768 -0
  218. package/src/misc/TLColor.ts +28 -0
  219. package/src/misc/TLCursor.ts +54 -0
  220. package/src/misc/TLHandle.ts +41 -0
  221. package/src/misc/TLOpacity.ts +11 -0
  222. package/src/misc/TLScribble.ts +39 -0
  223. package/src/misc/geometry-types.ts +37 -0
  224. package/src/misc/id-validator.ts +14 -0
  225. package/src/records/TLAsset.ts +80 -0
  226. package/src/records/TLCamera.ts +74 -0
  227. package/src/records/TLDocument.ts +77 -0
  228. package/src/records/TLInstance.ts +493 -0
  229. package/src/records/TLPage.ts +72 -0
  230. package/src/records/TLPageState.ts +216 -0
  231. package/src/records/TLPointer.ts +74 -0
  232. package/src/records/TLPresence.ts +179 -0
  233. package/src/records/TLRecord.ts +21 -0
  234. package/src/records/TLShape.ts +214 -0
  235. package/src/shapes/TLArrowShape.ts +160 -0
  236. package/src/shapes/TLBaseShape.ts +67 -0
  237. package/src/shapes/TLBookmarkShape.ts +44 -0
  238. package/src/shapes/TLDrawShape.ts +89 -0
  239. package/src/shapes/TLEmbedShape.ts +754 -0
  240. package/src/shapes/TLFrameShape.ts +18 -0
  241. package/src/shapes/TLGeoShape.ts +226 -0
  242. package/src/shapes/TLGroupShape.ts +14 -0
  243. package/src/shapes/TLHighlightShape.ts +24 -0
  244. package/src/shapes/TLImageShape.ts +59 -0
  245. package/src/shapes/TLLineShape.ts +61 -0
  246. package/src/shapes/TLNoteShape.ts +137 -0
  247. package/src/shapes/TLTextShape.ts +55 -0
  248. package/src/shapes/TLVideoShape.ts +40 -0
  249. package/src/store-migrations.ts +63 -0
  250. package/src/styles/StyleProp.ts +105 -0
  251. package/src/styles/TLColorStyle.ts +296 -0
  252. package/src/styles/TLDashStyle.ts +11 -0
  253. package/src/styles/TLFillStyle.ts +11 -0
  254. package/src/styles/TLFontStyle.ts +19 -0
  255. package/src/styles/TLHorizontalAlignStyle.ts +11 -0
  256. package/src/styles/TLSizeStyle.ts +11 -0
  257. package/src/styles/TLVerticalAlignStyle.ts +11 -0
  258. package/src/translations/languages.ts +39 -0
  259. package/src/translations/translations.test.ts +43 -0
  260. package/src/translations/translations.ts +73 -0
  261. package/src/util-types.ts +2 -0
@@ -0,0 +1,1768 @@
1
+ import { Migrations, Store, createRecordType } from '@bigbluebutton/store'
2
+ import fs from 'fs'
3
+ import { imageAssetMigrations } from './assets/TLImageAsset'
4
+ import { videoAssetMigrations } from './assets/TLVideoAsset'
5
+ import { assetMigrations, assetVersions } from './records/TLAsset'
6
+ import { cameraMigrations, cameraVersions } from './records/TLCamera'
7
+ import { documentMigrations, documentVersions } from './records/TLDocument'
8
+ import { instanceMigrations, instanceVersions } from './records/TLInstance'
9
+ import { pageMigrations, pageVersions } from './records/TLPage'
10
+ import { instancePageStateMigrations, instancePageStateVersions } from './records/TLPageState'
11
+ import { pointerMigrations, pointerVersions } from './records/TLPointer'
12
+ import { instancePresenceMigrations, instancePresenceVersions } from './records/TLPresence'
13
+ import { TLShape, rootShapeMigrations, rootShapeVersions } from './records/TLShape'
14
+ import { ArrowMigrationVersions, arrowShapeMigrations } from './shapes/TLArrowShape'
15
+ import { bookmarkShapeMigrations } from './shapes/TLBookmarkShape'
16
+ import { drawShapeMigrations } from './shapes/TLDrawShape'
17
+ import { embedShapeMigrations } from './shapes/TLEmbedShape'
18
+ import { GeoShapeVersions, geoShapeMigrations } from './shapes/TLGeoShape'
19
+ import { imageShapeMigrations } from './shapes/TLImageShape'
20
+ import { lineShapeMigrations, lineShapeVersions } from './shapes/TLLineShape'
21
+ import { noteShapeMigrations } from './shapes/TLNoteShape'
22
+ import { textShapeMigrations } from './shapes/TLTextShape'
23
+ import { videoShapeMigrations } from './shapes/TLVideoShape'
24
+ import { storeMigrations, storeVersions } from './store-migrations'
25
+
26
+ const assetModules = fs
27
+ .readdirSync('src/assets')
28
+ .filter((n) => n.match(/^TL.*\.ts$/))
29
+ .map((f) => [f, require(`./assets/${f.slice(0, -3)}`)])
30
+ const shapeModules = fs
31
+ .readdirSync('src/shapes')
32
+ .filter((n) => n.match(/^TL.*\.ts$/))
33
+ .map((f) => [f, require(`./shapes/${f.slice(0, -3)}`)])
34
+ const recordModules = fs
35
+ .readdirSync('src/records')
36
+ .filter((n) => n.match(/^TL.*\.ts$/))
37
+ .map((f) => [f, require(`./records/${f.slice(0, -3)}`)])
38
+
39
+ const allModules = [
40
+ ...assetModules,
41
+ ...shapeModules,
42
+ ...recordModules,
43
+ ['store-migrations.ts', require('./store-migrations')],
44
+ ]
45
+
46
+ const allMigrators: Array<{
47
+ fileName: string
48
+ version: number
49
+ up: jest.SpyInstance
50
+ down: jest.SpyInstance
51
+ }> = []
52
+
53
+ for (const [fileName, module] of allModules) {
54
+ const migrationsKey = Object.keys(module).find((k) => k.endsWith('igrations'))
55
+
56
+ if (!migrationsKey) continue
57
+
58
+ const migrations: Migrations = module[migrationsKey]
59
+
60
+ for (const version of Object.keys(migrations.migrators)) {
61
+ const originalUp = migrations.migrators[version as any].up
62
+ const originalDown = migrations.migrators[version as any].down
63
+ const up = jest
64
+ .spyOn(migrations.migrators[version as any], 'up')
65
+ .mockImplementation((initialRecord) => {
66
+ if (initialRecord instanceof Store) return originalUp(initialRecord)
67
+
68
+ const clonedRecord = structuredClone(initialRecord)
69
+ const result = originalUp(initialRecord)
70
+ // mutations should never mutate their input
71
+ expect(initialRecord).toEqual(clonedRecord)
72
+ return result
73
+ })
74
+ const down = jest
75
+ .spyOn(migrations.migrators[version as any], 'down')
76
+ .mockImplementation((initialRecord) => {
77
+ if (initialRecord instanceof Store) return originalDown(initialRecord)
78
+
79
+ const clonedRecord = structuredClone(initialRecord)
80
+ const result = originalDown(initialRecord)
81
+ // mutations should never mutate their input
82
+ expect(initialRecord).toEqual(clonedRecord)
83
+ return result
84
+ })
85
+ allMigrators.push({
86
+ fileName,
87
+ version: Number(version),
88
+ up,
89
+ down,
90
+ })
91
+ }
92
+ }
93
+
94
+ test('all modules export migrations', () => {
95
+ const modulesWithoutMigrations = allModules
96
+ .filter(([, module]) => {
97
+ return !Object.keys(module).find((k) => k.endsWith('igrations'))
98
+ })
99
+ .map(([fileName]) => fileName)
100
+ .filter((n) => !(n === 'TLBaseAsset.ts' || n === 'TLBaseShape.ts' || n === 'TLRecord.ts'))
101
+
102
+ // IF THIS LINE IS FAILING YOU NEED TO MAKE SURE THE MIGRATIONS ARE EXPORTED
103
+ expect(modulesWithoutMigrations).toHaveLength(0)
104
+ })
105
+
106
+ /* --- PUT YOUR MIGRATIONS TESTS BELOW HERE --- */
107
+
108
+ describe('TLVideoAsset AddIsAnimated', () => {
109
+ const oldAsset = {
110
+ id: '1',
111
+ type: 'video',
112
+ props: {
113
+ src: 'https://www.youtube.com/watch?v=1',
114
+ name: 'video',
115
+ width: 100,
116
+ height: 100,
117
+ mimeType: 'video/mp4',
118
+ },
119
+ }
120
+
121
+ const newAsset = {
122
+ id: '1',
123
+ type: 'video',
124
+ props: {
125
+ src: 'https://www.youtube.com/watch?v=1',
126
+ name: 'video',
127
+ width: 100,
128
+ height: 100,
129
+ mimeType: 'video/mp4',
130
+ isAnimated: false,
131
+ },
132
+ }
133
+
134
+ const { up, down } = videoAssetMigrations.migrators[1]
135
+
136
+ test('up works as expected', () => {
137
+ expect(up(oldAsset)).toEqual(newAsset)
138
+ })
139
+ test('down works as expected', () => {
140
+ expect(down(newAsset)).toEqual(oldAsset)
141
+ })
142
+ })
143
+
144
+ describe('TLImageAsset AddIsAnimated', () => {
145
+ const oldAsset = {
146
+ id: '1',
147
+ type: 'image',
148
+ props: {
149
+ src: 'https://www.youtube.com/watch?v=1',
150
+ name: 'image',
151
+ width: 100,
152
+ height: 100,
153
+ mimeType: 'image/gif',
154
+ },
155
+ }
156
+
157
+ const newAsset = {
158
+ id: '1',
159
+ type: 'image',
160
+ props: {
161
+ src: 'https://www.youtube.com/watch?v=1',
162
+ name: 'image',
163
+ width: 100,
164
+ height: 100,
165
+ mimeType: 'image/gif',
166
+ isAnimated: false,
167
+ },
168
+ }
169
+
170
+ const { up, down } = imageAssetMigrations.migrators[1]
171
+
172
+ test('up works as expected', () => {
173
+ expect(up(oldAsset)).toEqual(newAsset)
174
+ })
175
+ test('down works as expected', () => {
176
+ expect(down(newAsset)).toEqual(oldAsset)
177
+ })
178
+ })
179
+
180
+ const ShapeRecord = createRecordType('shape', {
181
+ validator: { validate: (record) => record as TLShape },
182
+ scope: 'document',
183
+ })
184
+
185
+ describe('Store removing Icon and Code shapes', () => {
186
+ test('up works as expected', () => {
187
+ const snapshot = Object.fromEntries(
188
+ [
189
+ ShapeRecord.create({
190
+ type: 'icon',
191
+ parentId: 'page:any',
192
+ index: 'a0',
193
+ props: { name: 'a' },
194
+ } as any),
195
+ ShapeRecord.create({
196
+ type: 'icon',
197
+ parentId: 'page:any',
198
+ index: 'a0',
199
+ props: { name: 'b' },
200
+ } as any),
201
+ ShapeRecord.create({
202
+ type: 'code',
203
+ parentId: 'page:any',
204
+ index: 'a0',
205
+ props: { name: 'c' },
206
+ } as any),
207
+ ShapeRecord.create({
208
+ type: 'code',
209
+ parentId: 'page:any',
210
+ index: 'a0',
211
+ props: { name: 'd' },
212
+ } as any),
213
+ ShapeRecord.create({
214
+ type: 'geo',
215
+ parentId: 'page:any',
216
+ index: 'a0',
217
+ props: { geo: 'rectangle', w: 1, h: 1, growY: 1, text: '' },
218
+ } as any),
219
+ ].map((shape) => [shape.id, shape])
220
+ )
221
+ const fixed = storeMigrations.migrators[storeVersions.RemoveCodeAndIconShapeTypes].up(snapshot)
222
+ expect(Object.entries(fixed)).toHaveLength(1)
223
+ })
224
+
225
+ test('down works as expected', () => {
226
+ const snapshot = Object.fromEntries(
227
+ [
228
+ ShapeRecord.create({
229
+ type: 'geo',
230
+ parentId: 'page:any',
231
+ index: 'a0',
232
+ props: { geo: 'rectangle', name: 'e', w: 1, h: 1, growY: 1, text: '' },
233
+ } as any),
234
+ ].map((shape) => [shape.id, shape])
235
+ )
236
+
237
+ storeMigrations.migrators[storeVersions.RemoveCodeAndIconShapeTypes].down(snapshot)
238
+ expect(Object.entries(snapshot)).toHaveLength(1)
239
+ })
240
+ })
241
+
242
+ describe('Adding export background', () => {
243
+ const { up, down } = instanceMigrations.migrators[1]
244
+ test('up works as expected', () => {
245
+ const before = {}
246
+ const after = { exportBackground: true }
247
+ expect(up(before)).toStrictEqual(after)
248
+ })
249
+
250
+ test('down works as expected', () => {
251
+ const before = { exportBackground: true }
252
+ const after = {}
253
+ expect(down(before)).toStrictEqual(after)
254
+ })
255
+ })
256
+
257
+ describe('Removing dialogs from instance', () => {
258
+ const { up, down } = instanceMigrations.migrators[2]
259
+ test('up works as expected', () => {
260
+ const before = { dialog: null }
261
+ const after = {}
262
+ expect(up(before)).toStrictEqual(after)
263
+ })
264
+
265
+ test('down works as expected', () => {
266
+ const before = {}
267
+ const after = { dialog: null }
268
+ expect(down(before)).toStrictEqual(after)
269
+ })
270
+ })
271
+
272
+ describe('Adding url props', () => {
273
+ for (const [name, { up, down }] of [
274
+ ['video shape', videoShapeMigrations.migrators[1]],
275
+ ['note shape', noteShapeMigrations.migrators[1]],
276
+ ['geo shape', geoShapeMigrations.migrators[1]],
277
+ ['image shape', imageShapeMigrations.migrators[1]],
278
+ ] as const) {
279
+ test(`${name}: up works as expected`, () => {
280
+ const before = { props: {} }
281
+ const after = { props: { url: '' } }
282
+ expect(up(before)).toStrictEqual(after)
283
+ })
284
+
285
+ test(`${name}: down works as expected`, () => {
286
+ const before = { props: { url: '' } }
287
+ const after = { props: {} }
288
+ expect(down(before)).toStrictEqual(after)
289
+ })
290
+ }
291
+ })
292
+
293
+ describe('Bookmark null asset id', () => {
294
+ const { up, down } = bookmarkShapeMigrations.migrators[1]
295
+ test('up works as expected', () => {
296
+ const before = { props: {} }
297
+ const after = { props: { assetId: null } }
298
+ expect(up(before)).toStrictEqual(after)
299
+ })
300
+
301
+ test('down works as expected', () => {
302
+ const before = { props: { assetId: null } }
303
+ const after = { props: {} }
304
+ expect(down(before)).toStrictEqual(after)
305
+ })
306
+ })
307
+
308
+ describe('Renaming asset props', () => {
309
+ for (const [name, { up, down }] of [
310
+ ['image shape', imageAssetMigrations.migrators[2]],
311
+ ['video shape', videoAssetMigrations.migrators[2]],
312
+ ] as const) {
313
+ test(`${name}: up works as expected`, () => {
314
+ const before = { props: { width: 100, height: 100 } }
315
+ const after = { props: { w: 100, h: 100 } }
316
+ expect(up(before)).toStrictEqual(after)
317
+ })
318
+
319
+ test(`${name}: down works as expected`, () => {
320
+ const before = { props: { w: 100, h: 100 } }
321
+ const after = { props: { width: 100, height: 100 } }
322
+ expect(down(before)).toStrictEqual(after)
323
+ })
324
+ }
325
+ })
326
+
327
+ describe('Adding instance.isToolLocked', () => {
328
+ const { up, down } = instanceMigrations.migrators[3]
329
+ test('up works as expected', () => {
330
+ expect(up({})).toMatchObject({ isToolLocked: false })
331
+ expect(up({ isToolLocked: true })).toMatchObject({ isToolLocked: false })
332
+ })
333
+
334
+ test('down works as expected', () => {
335
+ expect(down({ isToolLocked: true })).toStrictEqual({})
336
+ expect(down({ isToolLocked: false })).toStrictEqual({})
337
+ })
338
+ })
339
+
340
+ describe('Cleaning up junk data in instance.propsForNextShape', () => {
341
+ const { up, down } = instanceMigrations.migrators[4]
342
+ test('up works as expected', () => {
343
+ expect(up({ propsForNextShape: { color: 'red', unknown: 'gone' } })).toEqual({
344
+ propsForNextShape: {
345
+ color: 'red',
346
+ },
347
+ })
348
+ })
349
+
350
+ test('down works as expected', () => {
351
+ const instance = { propsForNextShape: { color: 'red' } }
352
+ expect(down(instance)).toBe(instance)
353
+ })
354
+ })
355
+
356
+ describe('Generating original URL from embed URL in GenOriginalUrlInEmbed', () => {
357
+ const { up, down } = embedShapeMigrations.migrators[1]
358
+ test('up works as expected', () => {
359
+ expect(up({ props: { url: 'https://codepen.io/Rplus/embed/PWZYRM' } })).toEqual({
360
+ props: {
361
+ url: 'https://codepen.io/Rplus/pen/PWZYRM',
362
+ tmpOldUrl: 'https://codepen.io/Rplus/embed/PWZYRM',
363
+ },
364
+ })
365
+ })
366
+
367
+ test('invalid up works as expected', () => {
368
+ expect(up({ props: { url: 'https://example.com' } })).toEqual({
369
+ props: {
370
+ url: '',
371
+ tmpOldUrl: 'https://example.com',
372
+ },
373
+ })
374
+ })
375
+
376
+ test('down works as expected', () => {
377
+ const instance = {
378
+ props: {
379
+ url: 'https://codepen.io/Rplus/pen/PWZYRM',
380
+ tmpOldUrl: 'https://codepen.io/Rplus/embed/PWZYRM',
381
+ },
382
+ }
383
+ expect(down(instance)).toEqual({ props: { url: 'https://codepen.io/Rplus/embed/PWZYRM' } })
384
+ })
385
+
386
+ test('invalid down works as expected', () => {
387
+ const instance = {
388
+ props: {
389
+ url: 'https://example.com',
390
+ tmpOldUrl: '',
391
+ },
392
+ }
393
+ expect(down(instance)).toEqual({ props: { url: '' } })
394
+ })
395
+ })
396
+
397
+ describe('Adding isPen prop', () => {
398
+ const { up, down } = drawShapeMigrations.migrators[1]
399
+
400
+ test('up works as expected with a shape that is not a pen shape', () => {
401
+ expect(
402
+ up({
403
+ props: {
404
+ segments: [
405
+ {
406
+ type: 'free',
407
+ points: [
408
+ { x: 0, y: 0, z: 0.5 },
409
+ { x: 1, y: 1, z: 0.5 },
410
+ ],
411
+ },
412
+ ],
413
+ },
414
+ })
415
+ ).toEqual({
416
+ props: {
417
+ isPen: false,
418
+ segments: [
419
+ {
420
+ type: 'free',
421
+ points: [
422
+ { x: 0, y: 0, z: 0.5 },
423
+ { x: 1, y: 1, z: 0.5 },
424
+ ],
425
+ },
426
+ ],
427
+ },
428
+ })
429
+ })
430
+
431
+ test('up works as expected when converting to pen', () => {
432
+ expect(
433
+ up({
434
+ props: {
435
+ segments: [
436
+ {
437
+ type: 'free',
438
+ points: [
439
+ { x: 0, y: 0, z: 0.2315 },
440
+ { x: 1, y: 1, z: 0.2421 },
441
+ ],
442
+ },
443
+ ],
444
+ },
445
+ })
446
+ ).toEqual({
447
+ props: {
448
+ isPen: true,
449
+ segments: [
450
+ {
451
+ type: 'free',
452
+ points: [
453
+ { x: 0, y: 0, z: 0.2315 },
454
+ { x: 1, y: 1, z: 0.2421 },
455
+ ],
456
+ },
457
+ ],
458
+ },
459
+ })
460
+ })
461
+
462
+ test('down works as expected', () => {
463
+ expect(down({ props: { isPen: false } })).toEqual({
464
+ props: {},
465
+ })
466
+ })
467
+ })
468
+
469
+ describe('Adding isLocked prop', () => {
470
+ const { up, down } = rootShapeMigrations.migrators[1]
471
+
472
+ test('up works as expected', () => {
473
+ expect(up({})).toEqual({ isLocked: false })
474
+ })
475
+
476
+ test('down works as expected', () => {
477
+ expect(down({ isLocked: false })).toEqual({})
478
+ })
479
+ })
480
+
481
+ describe('Adding labelColor prop to geo / arrow shapes', () => {
482
+ for (const [name, { up, down }] of [
483
+ ['arrow shape', arrowShapeMigrations.migrators[1]],
484
+ ['geo shape', geoShapeMigrations.migrators[2]],
485
+ ] as const) {
486
+ test(`${name}: up works as expected`, () => {
487
+ expect(up({ props: { color: 'red' } })).toEqual({
488
+ props: { color: 'red', labelColor: 'black' },
489
+ })
490
+ })
491
+
492
+ test(`${name}: down works as expected`, () => {
493
+ expect(down({ props: { color: 'red', labelColor: 'blue' } })).toEqual({
494
+ props: { color: 'red' },
495
+ })
496
+ })
497
+ }
498
+ })
499
+
500
+ describe('Adding labelColor prop to propsForNextShape', () => {
501
+ const { up, down } = instanceMigrations.migrators[5]
502
+ test('up works as expected', () => {
503
+ expect(up({ propsForNextShape: { color: 'red' } })).toEqual({
504
+ propsForNextShape: { color: 'red', labelColor: 'black' },
505
+ })
506
+ })
507
+
508
+ test('down works as expected', () => {
509
+ expect(down({ propsForNextShape: { color: 'red', labelColor: 'blue' } })).toEqual({
510
+ propsForNextShape: { color: 'red' },
511
+ })
512
+ })
513
+ })
514
+
515
+ describe('Adding croppingShapeId to instancePageState', () => {
516
+ const { up, down } = instancePageStateMigrations.migrators[1]
517
+ test('up works as expected', () => {
518
+ expect(up({})).toEqual({
519
+ croppingShapeId: null,
520
+ })
521
+ })
522
+
523
+ test('down works as expected', () => {
524
+ expect(down({ croppingShapeId: null })).toEqual({})
525
+ })
526
+ })
527
+
528
+ describe('Renaming properties in instancePageState', () => {
529
+ const { up, down } =
530
+ instancePageStateMigrations.migrators[instancePageStateVersions.RenameProperties]
531
+ test('up works as expected', () => {
532
+ expect(
533
+ up({
534
+ selectedShapeIds: [],
535
+ hintingShapeIds: [],
536
+ erasingShapeIds: [],
537
+ hoveredShapeId: null,
538
+ editingShapeId: null,
539
+ croppingShapeId: null,
540
+ focusedGroupId: null,
541
+ meta: {
542
+ name: 'hallo',
543
+ },
544
+ })
545
+ ).toEqual({
546
+ selectedShapeIds: [],
547
+ hintingShapeIds: [],
548
+ erasingShapeIds: [],
549
+ hoveredShapeId: null,
550
+ editingShapeId: null,
551
+ croppingShapeId: null,
552
+ focusedGroupId: null,
553
+ meta: {
554
+ name: 'hallo',
555
+ },
556
+ })
557
+ })
558
+
559
+ test('down works as expected', () => {
560
+ expect(
561
+ down({
562
+ selectedShapeIds: [],
563
+ hintingShapeIds: [],
564
+ erasingShapeIds: [],
565
+ hoveredShapeId: null,
566
+ editingShapeId: null,
567
+ croppingShapeId: null,
568
+ focusedGroupId: null,
569
+ meta: {
570
+ name: 'hallo',
571
+ },
572
+ })
573
+ ).toEqual({
574
+ selectedShapeIds: [],
575
+ hintingShapeIds: [],
576
+ erasingShapeIds: [],
577
+ hoveredShapeId: null,
578
+ editingShapeId: null,
579
+ croppingShapeId: null,
580
+ focusedGroupId: null,
581
+ meta: {
582
+ name: 'hallo',
583
+ },
584
+ })
585
+ })
586
+ })
587
+
588
+ describe('Renaming properties again in instancePageState', () => {
589
+ const { up, down } =
590
+ instancePageStateMigrations.migrators[instancePageStateVersions.RenamePropertiesAgain]
591
+ test('up works as expected', () => {
592
+ expect(
593
+ up({
594
+ selectedIds: [],
595
+ hintingIds: [],
596
+ erasingIds: [],
597
+ hoveredId: null,
598
+ editingId: null,
599
+ croppingId: null,
600
+ focusLayerId: null,
601
+ meta: {
602
+ name: 'hallo',
603
+ },
604
+ })
605
+ ).toEqual({
606
+ selectedShapeIds: [],
607
+ hintingShapeIds: [],
608
+ erasingShapeIds: [],
609
+ hoveredShapeId: null,
610
+ editingShapeId: null,
611
+ croppingShapeId: null,
612
+ focusedGroupId: null,
613
+ meta: {
614
+ name: 'hallo',
615
+ },
616
+ })
617
+ })
618
+
619
+ test('down works as expected', () => {
620
+ expect(
621
+ down({
622
+ selectedShapeIds: [],
623
+ hintingShapeIds: [],
624
+ erasingShapeIds: [],
625
+ hoveredShapeId: null,
626
+ editingShapeId: null,
627
+ croppingShapeId: null,
628
+ focusedGroupId: null,
629
+ meta: {
630
+ name: 'hallo',
631
+ },
632
+ })
633
+ ).toEqual({
634
+ selectedIds: [],
635
+ hintingIds: [],
636
+ erasingIds: [],
637
+ hoveredId: null,
638
+ editingId: null,
639
+ croppingId: null,
640
+ focusLayerId: null,
641
+ meta: {
642
+ name: 'hallo',
643
+ },
644
+ })
645
+ })
646
+ })
647
+
648
+ describe('Adding followingUserId prop to instance', () => {
649
+ const { up, down } = instanceMigrations.migrators[6]
650
+ test('up works as expected', () => {
651
+ expect(up({})).toEqual({ followingUserId: null })
652
+ })
653
+
654
+ test('down works as expected', () => {
655
+ expect(down({ followingUserId: '123' })).toEqual({})
656
+ })
657
+ })
658
+
659
+ describe('Removing align=justify from propsForNextShape', () => {
660
+ const { up, down } = instanceMigrations.migrators[7]
661
+ test('up works as expected', () => {
662
+ expect(up({ propsForNextShape: { color: 'black', align: 'justify' } })).toEqual({
663
+ propsForNextShape: { color: 'black', align: 'start' },
664
+ })
665
+ expect(up({ propsForNextShape: { color: 'black', align: 'end' } })).toEqual({
666
+ propsForNextShape: { color: 'black', align: 'end' },
667
+ })
668
+ })
669
+
670
+ test('down works as expected', () => {
671
+ expect(down({ propsForNextShape: { color: 'black', align: 'end' } })).toEqual({
672
+ propsForNextShape: { color: 'black', align: 'end' },
673
+ })
674
+ })
675
+ })
676
+
677
+ describe('Adding zoomBrush prop to instance', () => {
678
+ const { up, down } = instanceMigrations.migrators[8]
679
+ test('up works as expected', () => {
680
+ expect(up({})).toEqual({ zoomBrush: null })
681
+ })
682
+
683
+ test('down works as expected', () => {
684
+ expect(down({ zoomBrush: { x: 1, y: 2, w: 3, h: 4 } })).toEqual({})
685
+ })
686
+ })
687
+
688
+ describe('Removing align=justify from shape align props', () => {
689
+ for (const [name, { up, down }] of [
690
+ ['text', textShapeMigrations.migrators[1]],
691
+ ['note', noteShapeMigrations.migrators[2]],
692
+ ['geo', geoShapeMigrations.migrators[3]],
693
+ ] as const) {
694
+ test(`${name}: up works as expected`, () => {
695
+ expect(up({ props: { align: 'justify' } })).toEqual({
696
+ props: { align: 'start' },
697
+ })
698
+ expect(up({ props: { align: 'end' } })).toEqual({
699
+ props: { align: 'end' },
700
+ })
701
+ })
702
+
703
+ test(`${name}: down works as expected`, () => {
704
+ expect(down({ props: { align: 'start' } })).toEqual({
705
+ props: { align: 'start' },
706
+ })
707
+ })
708
+ }
709
+ })
710
+
711
+ describe('Add crop=null to image shapes', () => {
712
+ const { up, down } = imageShapeMigrations.migrators[2]
713
+ test('up works as expected', () => {
714
+ expect(up({ props: { w: 100 } })).toEqual({
715
+ props: { w: 100, crop: null },
716
+ })
717
+ })
718
+
719
+ test('down works as expected', () => {
720
+ expect(down({ props: { w: 100, crop: null } })).toEqual({
721
+ props: { w: 100 },
722
+ })
723
+ })
724
+ })
725
+
726
+ describe('Adding instance_presence to the schema', () => {
727
+ const { up, down } = storeMigrations.migrators[storeVersions.AddInstancePresenceType]
728
+
729
+ test('up works as expected', () => {
730
+ expect(up({})).toEqual({})
731
+ })
732
+ test('down works as expected', () => {
733
+ expect(
734
+ down({
735
+ 'instance_presence:123': { id: 'instance_presence:123', typeName: 'instance_presence' },
736
+ 'instance:123': { id: 'instance:123', typeName: 'instance' },
737
+ })
738
+ ).toEqual({
739
+ 'instance:123': { id: 'instance:123', typeName: 'instance' },
740
+ })
741
+ })
742
+ })
743
+
744
+ describe('Adding name to document', () => {
745
+ const { up, down } = documentMigrations.migrators[1]
746
+
747
+ test('up works as expected', () => {
748
+ expect(up({})).toEqual({ name: '' })
749
+ })
750
+
751
+ test('down works as expected', () => {
752
+ expect(down({ name: '' })).toEqual({})
753
+ })
754
+ })
755
+
756
+ describe('Adding check-box to geo shape', () => {
757
+ const { up, down } = geoShapeMigrations.migrators[4]
758
+
759
+ test('up works as expected', () => {
760
+ expect(up({ props: { geo: 'rectangle' } })).toEqual({ props: { geo: 'rectangle' } })
761
+ })
762
+ test('down works as expected', () => {
763
+ expect(down({ props: { geo: 'rectangle' } })).toEqual({ props: { geo: 'rectangle' } })
764
+ expect(down({ props: { geo: 'check-box' } })).toEqual({ props: { geo: 'rectangle' } })
765
+ })
766
+ })
767
+
768
+ describe('Add verticalAlign to geo shape', () => {
769
+ const { up, down } = geoShapeMigrations.migrators[5]
770
+
771
+ test('up works as expected', () => {
772
+ expect(up({ props: { type: 'ellipse' } })).toEqual({
773
+ props: { type: 'ellipse', verticalAlign: 'middle' },
774
+ })
775
+ })
776
+ test('down works as expected', () => {
777
+ expect(down({ props: { verticalAlign: 'middle', type: 'ellipse' } })).toEqual({
778
+ props: { type: 'ellipse' },
779
+ })
780
+ })
781
+ })
782
+
783
+ describe('Add verticalAlign to props for next shape', () => {
784
+ const { up, down } = instanceMigrations.migrators[9]
785
+ test('up works as expected', () => {
786
+ expect(up({ propsForNextShape: { color: 'red' } })).toEqual({
787
+ propsForNextShape: {
788
+ color: 'red',
789
+ verticalAlign: 'middle',
790
+ },
791
+ })
792
+ })
793
+
794
+ test('down works as expected', () => {
795
+ const instance = { propsForNextShape: { color: 'red', verticalAlign: 'middle' } }
796
+ expect(down(instance)).toEqual({
797
+ propsForNextShape: {
798
+ color: 'red',
799
+ },
800
+ })
801
+ })
802
+ })
803
+
804
+ describe('Migrate GeoShape legacy horizontal alignment', () => {
805
+ const { up, down } = geoShapeMigrations.migrators[6]
806
+
807
+ test('up works as expected', () => {
808
+ expect(up({ props: { align: 'start', type: 'ellipse' } })).toEqual({
809
+ props: { align: 'start-legacy', type: 'ellipse' },
810
+ })
811
+ expect(up({ props: { align: 'middle', type: 'ellipse' } })).toEqual({
812
+ props: { align: 'middle-legacy', type: 'ellipse' },
813
+ })
814
+ expect(up({ props: { align: 'end', type: 'ellipse' } })).toEqual({
815
+ props: { align: 'end-legacy', type: 'ellipse' },
816
+ })
817
+ })
818
+ test('down works as expected', () => {
819
+ expect(down({ props: { align: 'start-legacy', type: 'ellipse' } })).toEqual({
820
+ props: { align: 'start', type: 'ellipse' },
821
+ })
822
+ expect(down({ props: { align: 'middle-legacy', type: 'ellipse' } })).toEqual({
823
+ props: { align: 'middle', type: 'ellipse' },
824
+ })
825
+ expect(down({ props: { align: 'end-legacy', type: 'ellipse' } })).toEqual({
826
+ props: { align: 'end', type: 'ellipse' },
827
+ })
828
+ })
829
+ })
830
+
831
+ describe('adding cloud shape', () => {
832
+ const { up, down } = geoShapeMigrations.migrators[GeoShapeVersions.AddCloud]
833
+
834
+ test('up does nothing', () => {
835
+ expect(up({ props: { geo: 'rectangle' } })).toEqual({
836
+ props: { geo: 'rectangle' },
837
+ })
838
+ })
839
+
840
+ test('down converts clouds to rectangles', () => {
841
+ expect(down({ props: { geo: 'cloud' } })).toEqual({
842
+ props: { geo: 'rectangle' },
843
+ })
844
+ })
845
+ })
846
+
847
+ describe('Migrate NoteShape legacy horizontal alignment', () => {
848
+ const { up, down } = noteShapeMigrations.migrators[3]
849
+
850
+ test('up works as expected', () => {
851
+ expect(up({ props: { align: 'start', color: 'red' } })).toEqual({
852
+ props: { align: 'start-legacy', color: 'red' },
853
+ })
854
+ expect(up({ props: { align: 'middle', color: 'red' } })).toEqual({
855
+ props: { align: 'middle-legacy', color: 'red' },
856
+ })
857
+ expect(up({ props: { align: 'end', color: 'red' } })).toEqual({
858
+ props: { align: 'end-legacy', color: 'red' },
859
+ })
860
+ })
861
+ test('down works as expected', () => {
862
+ expect(down({ props: { align: 'start-legacy', color: 'red' } })).toEqual({
863
+ props: { align: 'start', color: 'red' },
864
+ })
865
+ expect(down({ props: { align: 'middle-legacy', color: 'red' } })).toEqual({
866
+ props: { align: 'middle', color: 'red' },
867
+ })
868
+ expect(down({ props: { align: 'end-legacy', color: 'red' } })).toEqual({
869
+ props: { align: 'end', color: 'red' },
870
+ })
871
+ })
872
+ })
873
+
874
+ describe('Adds delay to scribble', () => {
875
+ const { up, down } = instanceMigrations.migrators[10]
876
+
877
+ test('up has no effect when scribble is null', () => {
878
+ expect(
879
+ up({
880
+ scribble: null,
881
+ })
882
+ ).toEqual({ scribble: null })
883
+ })
884
+
885
+ test('up adds the delay property', () => {
886
+ expect(
887
+ up({
888
+ scribble: {
889
+ points: [{ x: 0, y: 0 }],
890
+ size: 4,
891
+ color: 'black',
892
+ opacity: 1,
893
+ state: 'starting',
894
+ },
895
+ })
896
+ ).toEqual({
897
+ scribble: {
898
+ points: [{ x: 0, y: 0 }],
899
+ size: 4,
900
+ color: 'black',
901
+ opacity: 1,
902
+ state: 'starting',
903
+ delay: 0,
904
+ },
905
+ })
906
+ })
907
+
908
+ test('down has no effect when scribble is null', () => {
909
+ expect(down({ scribble: null })).toEqual({ scribble: null })
910
+ })
911
+
912
+ test('removes the delay property', () => {
913
+ expect(
914
+ down({
915
+ scribble: {
916
+ points: [{ x: 0, y: 0 }],
917
+ size: 4,
918
+ color: 'black',
919
+ opacity: 1,
920
+ state: 'starting',
921
+ delay: 0,
922
+ },
923
+ })
924
+ ).toEqual({
925
+ scribble: {
926
+ points: [{ x: 0, y: 0 }],
927
+ size: 4,
928
+ color: 'black',
929
+ opacity: 1,
930
+ state: 'starting',
931
+ },
932
+ })
933
+ })
934
+ })
935
+
936
+ describe('Adds delay to scribble', () => {
937
+ const { up, down } = instancePresenceMigrations.migrators[1]
938
+
939
+ test('up has no effect when scribble is null', () => {
940
+ expect(
941
+ up({
942
+ scribble: null,
943
+ })
944
+ ).toEqual({ scribble: null })
945
+ })
946
+
947
+ test('up adds the delay property', () => {
948
+ expect(
949
+ up({
950
+ scribble: {
951
+ points: [{ x: 0, y: 0 }],
952
+ size: 4,
953
+ color: 'black',
954
+ opacity: 1,
955
+ state: 'starting',
956
+ },
957
+ })
958
+ ).toEqual({
959
+ scribble: {
960
+ points: [{ x: 0, y: 0 }],
961
+ size: 4,
962
+ color: 'black',
963
+ opacity: 1,
964
+ state: 'starting',
965
+ delay: 0,
966
+ },
967
+ })
968
+ })
969
+
970
+ test('down has no effect when scribble is null', () => {
971
+ expect(down({ scribble: null })).toEqual({ scribble: null })
972
+ })
973
+
974
+ test('removes the delay property', () => {
975
+ expect(
976
+ down({
977
+ scribble: {
978
+ points: [{ x: 0, y: 0 }],
979
+ size: 4,
980
+ color: 'black',
981
+ opacity: 1,
982
+ state: 'starting',
983
+ delay: 0,
984
+ },
985
+ })
986
+ ).toEqual({
987
+ scribble: {
988
+ points: [{ x: 0, y: 0 }],
989
+ size: 4,
990
+ color: 'black',
991
+ opacity: 1,
992
+ state: 'starting',
993
+ },
994
+ })
995
+ })
996
+ })
997
+
998
+ describe('user config refactor', () => {
999
+ test('removes user and user_presence types from snapshots', () => {
1000
+ const { up, down } =
1001
+ storeMigrations.migrators[storeVersions.RemoveTLUserAndPresenceAndAddPointer]
1002
+
1003
+ const prevSnapshot = {
1004
+ 'user:123': {
1005
+ id: 'user:123',
1006
+ typeName: 'user',
1007
+ },
1008
+ 'user_presence:123': {
1009
+ id: 'user_presence:123',
1010
+ typeName: 'user_presence',
1011
+ },
1012
+ 'instance:123': {
1013
+ id: 'instance:123',
1014
+ typeName: 'instance',
1015
+ },
1016
+ }
1017
+
1018
+ const nextSnapshot = {
1019
+ 'instance:123': {
1020
+ id: 'instance:123',
1021
+ typeName: 'instance',
1022
+ },
1023
+ }
1024
+
1025
+ // up removes the user and user_presence types
1026
+ expect(up(prevSnapshot)).toEqual(nextSnapshot)
1027
+ // down cannot add them back so it should be a no-op
1028
+ expect(
1029
+ down({
1030
+ ...nextSnapshot,
1031
+ 'pointer:134': {
1032
+ id: 'pointer:134',
1033
+ typeName: 'pointer',
1034
+ },
1035
+ })
1036
+ ).toEqual(nextSnapshot)
1037
+ })
1038
+
1039
+ test('removes userId from the instance state', () => {
1040
+ const { up, down } = instanceMigrations.migrators[instanceVersions.RemoveUserId]
1041
+
1042
+ const prev = {
1043
+ id: 'instance:123',
1044
+ typeName: 'instance',
1045
+ userId: 'user:123',
1046
+ }
1047
+
1048
+ const next = {
1049
+ id: 'instance:123',
1050
+ typeName: 'instance',
1051
+ }
1052
+
1053
+ expect(up(prev)).toEqual(next)
1054
+ // it cannot be added back so it should add some meaningless id in there
1055
+ // in practice, because we bumped the store version, this down migrator will never be used
1056
+ expect(down(next)).toMatchInlineSnapshot(`
1057
+ Object {
1058
+ "id": "instance:123",
1059
+ "typeName": "instance",
1060
+ "userId": "user:none",
1061
+ }
1062
+ `)
1063
+ })
1064
+ })
1065
+
1066
+ describe('making instance state independent', () => {
1067
+ it('adds isPenMode and isGridMode to instance state', () => {
1068
+ const { up, down } = instanceMigrations.migrators[instanceVersions.AddIsPenModeAndIsGridMode]
1069
+
1070
+ const prev = {
1071
+ id: 'instance:123',
1072
+ typeName: 'instance',
1073
+ }
1074
+ const next = {
1075
+ id: 'instance:123',
1076
+ typeName: 'instance',
1077
+ isPenMode: false,
1078
+ isGridMode: false,
1079
+ }
1080
+
1081
+ expect(up(prev)).toEqual(next)
1082
+ expect(down(next)).toEqual(prev)
1083
+ })
1084
+
1085
+ it('removes instanceId and cameraId from instancePageState', () => {
1086
+ const { up, down } =
1087
+ instancePageStateMigrations.migrators[instancePageStateVersions.RemoveInstanceIdAndCameraId]
1088
+
1089
+ const prev = {
1090
+ id: 'instance_page_state:123',
1091
+ typeName: 'instance_page_state',
1092
+ instanceId: 'instance:123',
1093
+ cameraId: 'camera:123',
1094
+ selectedShapeIds: [],
1095
+ }
1096
+
1097
+ const next = {
1098
+ id: 'instance_page_state:123',
1099
+ typeName: 'instance_page_state',
1100
+ selectedShapeIds: [],
1101
+ }
1102
+
1103
+ expect(up(prev)).toEqual(next)
1104
+ // down should never be called
1105
+ expect(down(next)).toMatchInlineSnapshot(`
1106
+ Object {
1107
+ "cameraId": "camera:void",
1108
+ "id": "instance_page_state:123",
1109
+ "instanceId": "instance:instance",
1110
+ "selectedShapeIds": Array [],
1111
+ "typeName": "instance_page_state",
1112
+ }
1113
+ `)
1114
+ })
1115
+
1116
+ it('removes instanceId from instancePresence', () => {
1117
+ const { up, down } =
1118
+ instancePresenceMigrations.migrators[instancePresenceVersions.RemoveInstanceId]
1119
+
1120
+ const prev = {
1121
+ id: 'instance_presence:123',
1122
+ typeName: 'instance_presence',
1123
+ instanceId: 'instance:123',
1124
+ selectedShapeIds: [],
1125
+ }
1126
+
1127
+ const next = {
1128
+ id: 'instance_presence:123',
1129
+ typeName: 'instance_presence',
1130
+ selectedShapeIds: [],
1131
+ }
1132
+
1133
+ expect(up(prev)).toEqual(next)
1134
+
1135
+ // down should never be called
1136
+ expect(down(next)).toMatchInlineSnapshot(`
1137
+ Object {
1138
+ "id": "instance_presence:123",
1139
+ "instanceId": "instance:instance",
1140
+ "selectedShapeIds": Array [],
1141
+ "typeName": "instance_presence",
1142
+ }
1143
+ `)
1144
+ })
1145
+
1146
+ it('removes userDocument from the schema', () => {
1147
+ const { up, down } = storeMigrations.migrators[storeVersions.RemoveUserDocument]
1148
+
1149
+ const prev = {
1150
+ 'user_document:123': {
1151
+ id: 'user_document:123',
1152
+ typeName: 'user_document',
1153
+ },
1154
+ 'instance:123': {
1155
+ id: 'instance:123',
1156
+ typeName: 'instance',
1157
+ },
1158
+ }
1159
+
1160
+ const next = {
1161
+ 'instance:123': {
1162
+ id: 'instance:123',
1163
+ typeName: 'instance',
1164
+ },
1165
+ }
1166
+
1167
+ expect(up(prev)).toEqual(next)
1168
+ expect(down(next)).toEqual(next)
1169
+ })
1170
+ })
1171
+
1172
+ describe('Adds NoteShape vertical alignment', () => {
1173
+ const { up, down } = noteShapeMigrations.migrators[4]
1174
+
1175
+ test('up works as expected', () => {
1176
+ expect(up({ props: { color: 'red' } })).toEqual({
1177
+ props: { verticalAlign: 'middle', color: 'red' },
1178
+ })
1179
+ })
1180
+ test('down works as expected', () => {
1181
+ expect(down({ props: { verticalAlign: 'top', color: 'red' } })).toEqual({
1182
+ props: { color: 'red' },
1183
+ })
1184
+ })
1185
+ })
1186
+
1187
+ describe('hoist opacity', () => {
1188
+ test('hoists opacity from a shape to another', () => {
1189
+ const { up, down } = rootShapeMigrations.migrators[rootShapeVersions.HoistOpacity]
1190
+ const before = {
1191
+ type: 'myShape',
1192
+ x: 0,
1193
+ y: 0,
1194
+ props: {
1195
+ color: 'red',
1196
+ opacity: '0.5',
1197
+ },
1198
+ }
1199
+ const after = {
1200
+ type: 'myShape',
1201
+ x: 0,
1202
+ y: 0,
1203
+ opacity: 0.5,
1204
+ props: {
1205
+ color: 'red',
1206
+ },
1207
+ }
1208
+ const afterWithNonMatchingOpacity = {
1209
+ type: 'myShape',
1210
+ x: 0,
1211
+ y: 0,
1212
+ opacity: 0.6,
1213
+ props: {
1214
+ color: 'red',
1215
+ },
1216
+ }
1217
+
1218
+ expect(up(before)).toEqual(after)
1219
+ expect(down(after)).toEqual(before)
1220
+ expect(down(afterWithNonMatchingOpacity)).toEqual(before)
1221
+ })
1222
+
1223
+ test('hoists opacity from propsForNextShape', () => {
1224
+ const { up, down } = instanceMigrations.migrators[instanceVersions.HoistOpacity]
1225
+ const before = {
1226
+ isToolLocked: true,
1227
+ propsForNextShape: {
1228
+ color: 'black',
1229
+ opacity: '0.5',
1230
+ },
1231
+ }
1232
+ const after = {
1233
+ isToolLocked: true,
1234
+ opacityForNextShape: 0.5,
1235
+ propsForNextShape: {
1236
+ color: 'black',
1237
+ },
1238
+ }
1239
+ const afterWithNonMatchingOpacity = {
1240
+ isToolLocked: true,
1241
+ opacityForNextShape: 0.6,
1242
+ propsForNextShape: {
1243
+ color: 'black',
1244
+ },
1245
+ }
1246
+
1247
+ expect(up(before)).toEqual(after)
1248
+ expect(down(after)).toEqual(before)
1249
+ expect(down(afterWithNonMatchingOpacity)).toEqual(before)
1250
+ })
1251
+ })
1252
+
1253
+ describe('Adds highlightedUserIds to instance', () => {
1254
+ const { up, down } = instanceMigrations.migrators[instanceVersions.AddHighlightedUserIds]
1255
+
1256
+ test('up works as expected', () => {
1257
+ expect(up({})).toEqual({ highlightedUserIds: [] })
1258
+ })
1259
+
1260
+ test('down works as expected', () => {
1261
+ expect(down({ highlightedUserIds: [] })).toEqual({})
1262
+ })
1263
+ })
1264
+
1265
+ describe('Adds chat message to presence', () => {
1266
+ const { up, down } = instancePresenceMigrations.migrators[3]
1267
+
1268
+ test('up adds the chatMessage property', () => {
1269
+ expect(up({})).toEqual({ chatMessage: '' })
1270
+ })
1271
+
1272
+ test('down removes the chatMessage property', () => {
1273
+ expect(down({ chatMessage: '' })).toEqual({})
1274
+ })
1275
+ })
1276
+
1277
+ describe('Adds chat properties to instance', () => {
1278
+ const { up, down } = instanceMigrations.migrators[14]
1279
+
1280
+ test('up adds the chatMessage property', () => {
1281
+ expect(up({})).toEqual({ chatMessage: '', isChatting: false })
1282
+ })
1283
+
1284
+ test('down removes the chatMessage property', () => {
1285
+ expect(down({ chatMessage: '', isChatting: true })).toEqual({})
1286
+ })
1287
+ })
1288
+
1289
+ describe('Removes does resize from embed', () => {
1290
+ const { up, down } = embedShapeMigrations.migrators[2]
1291
+ test('up works as expected', () => {
1292
+ expect(up({ props: { url: 'https://tldraw.com', doesResize: true } })).toEqual({
1293
+ props: { url: 'https://tldraw.com' },
1294
+ })
1295
+ })
1296
+ test('down works as expected', () => {
1297
+ expect(down({ props: { url: 'https://tldraw.com' } })).toEqual({
1298
+ props: { url: 'https://tldraw.com', doesResize: true },
1299
+ })
1300
+ })
1301
+ })
1302
+
1303
+ describe('Removes tmpOldUrl from embed', () => {
1304
+ const { up, down } = embedShapeMigrations.migrators[3]
1305
+ test('up works as expected', () => {
1306
+ expect(up({ props: { url: 'https://tldraw.com', tmpOldUrl: 'https://tldraw.com' } })).toEqual({
1307
+ props: { url: 'https://tldraw.com' },
1308
+ })
1309
+ })
1310
+ test('down works as expected', () => {
1311
+ expect(down({ props: { url: 'https://tldraw.com' } })).toEqual({
1312
+ props: { url: 'https://tldraw.com' },
1313
+ })
1314
+ })
1315
+ })
1316
+
1317
+ describe('Removes overridePermissions from embed', () => {
1318
+ const { up, down } = embedShapeMigrations.migrators[4]
1319
+
1320
+ test('up works as expected', () => {
1321
+ expect(
1322
+ up({ props: { url: 'https://tldraw.com', overridePermissions: { display: 'maybe' } } })
1323
+ ).toEqual({
1324
+ props: { url: 'https://tldraw.com' },
1325
+ })
1326
+ })
1327
+ test('down works as expected', () => {
1328
+ expect(down({ props: { url: 'https://tldraw.com' } })).toEqual({
1329
+ props: { url: 'https://tldraw.com' },
1330
+ })
1331
+ })
1332
+ })
1333
+
1334
+ describe('propsForNextShape -> stylesForNextShape', () => {
1335
+ test('deletes propsForNextShape and adds stylesForNextShape without trying to bring across contents', () => {
1336
+ const { up, down } =
1337
+ instanceMigrations.migrators[instanceVersions.ReplacePropsForNextShapeWithStylesForNextShape]
1338
+ const beforeUp = {
1339
+ isToolLocked: true,
1340
+ propsForNextShape: {
1341
+ color: 'red',
1342
+ size: 'm',
1343
+ },
1344
+ }
1345
+ const afterUp = {
1346
+ isToolLocked: true,
1347
+ stylesForNextShape: {},
1348
+ }
1349
+ const afterDown = {
1350
+ isToolLocked: true,
1351
+ propsForNextShape: {
1352
+ color: 'black',
1353
+ labelColor: 'black',
1354
+ dash: 'draw',
1355
+ fill: 'none',
1356
+ size: 'm',
1357
+ icon: 'file',
1358
+ font: 'draw',
1359
+ align: 'middle',
1360
+ verticalAlign: 'middle',
1361
+ geo: 'rectangle',
1362
+ arrowheadStart: 'none',
1363
+ arrowheadEnd: 'arrow',
1364
+ spline: 'line',
1365
+ },
1366
+ }
1367
+
1368
+ expect(up(beforeUp)).toEqual(afterUp)
1369
+ expect(down(afterUp)).toEqual(afterDown)
1370
+ })
1371
+ })
1372
+
1373
+ describe('adds meta ', () => {
1374
+ const metaMigrations = [
1375
+ assetMigrations.migrators[assetVersions.AddMeta],
1376
+ cameraMigrations.migrators[cameraVersions.AddMeta],
1377
+ documentMigrations.migrators[documentVersions.AddMeta],
1378
+ instanceMigrations.migrators[instanceVersions.AddMeta],
1379
+ instancePageStateMigrations.migrators[instancePageStateVersions.AddMeta],
1380
+ instancePresenceMigrations.migrators[instancePresenceVersions.AddMeta],
1381
+ pageMigrations.migrators[pageVersions.AddMeta],
1382
+ pointerMigrations.migrators[pointerVersions.AddMeta],
1383
+ rootShapeMigrations.migrators[rootShapeVersions.AddMeta],
1384
+ ]
1385
+
1386
+ for (const { up, down } of metaMigrations) {
1387
+ test('up works as expected', () => {
1388
+ expect(up({})).toStrictEqual({ meta: {} })
1389
+ })
1390
+
1391
+ test('down works as expected', () => {
1392
+ expect(down({ meta: {} })).toStrictEqual({})
1393
+ })
1394
+ }
1395
+ })
1396
+
1397
+ describe('removes cursor color', () => {
1398
+ const { up, down } = instanceMigrations.migrators[instanceVersions.RemoveCursorColor]
1399
+
1400
+ test('up works as expected', () => {
1401
+ expect(
1402
+ up({
1403
+ cursor: {
1404
+ type: 'default',
1405
+ rotation: 0.1,
1406
+ color: 'black',
1407
+ },
1408
+ })
1409
+ ).toStrictEqual({
1410
+ cursor: {
1411
+ type: 'default',
1412
+ rotation: 0.1,
1413
+ },
1414
+ })
1415
+ })
1416
+
1417
+ test('down works as expected', () => {
1418
+ expect(
1419
+ down({
1420
+ cursor: {
1421
+ type: 'default',
1422
+ rotation: 0.1,
1423
+ },
1424
+ })
1425
+ ).toStrictEqual({
1426
+ cursor: {
1427
+ type: 'default',
1428
+ rotation: 0.1,
1429
+ color: 'black',
1430
+ },
1431
+ })
1432
+ })
1433
+ })
1434
+
1435
+ describe('adds lonely properties', () => {
1436
+ const { up, down } = instanceMigrations.migrators[instanceVersions.AddLonelyProperties]
1437
+
1438
+ test('up works as expected', () => {
1439
+ expect(up({})).toStrictEqual({
1440
+ canMoveCamera: true,
1441
+ isFocused: false,
1442
+ devicePixelRatio: 1,
1443
+ isCoarsePointer: false,
1444
+ openMenus: [],
1445
+ isChangingStyle: false,
1446
+ isReadOnly: false,
1447
+ })
1448
+ })
1449
+
1450
+ test('down works as expected', () => {
1451
+ expect(
1452
+ down({
1453
+ canMoveCamera: true,
1454
+ isFocused: false,
1455
+ devicePixelRatio: 1,
1456
+ isCoarsePointer: false,
1457
+ openMenus: [],
1458
+ isChangingStyle: false,
1459
+ isReadOnly: false,
1460
+ })
1461
+ ).toStrictEqual({})
1462
+ })
1463
+ })
1464
+
1465
+ describe('rename isReadOnly to isReadonly', () => {
1466
+ const { up, down } = instanceMigrations.migrators[instanceVersions.ReadOnlyReadonly]
1467
+
1468
+ test('up works as expected', () => {
1469
+ expect(up({ isReadOnly: false })).toStrictEqual({
1470
+ isReadonly: false,
1471
+ })
1472
+ })
1473
+
1474
+ test('down works as expected', () => {
1475
+ expect(down({ isReadonly: false })).toStrictEqual({
1476
+ isReadOnly: false,
1477
+ })
1478
+ })
1479
+ })
1480
+
1481
+ describe('Renames selectedShapeIds in presence', () => {
1482
+ const { up, down } =
1483
+ instancePresenceMigrations.migrators[instancePresenceVersions.RenameSelectedShapeIds]
1484
+
1485
+ test('up adds the chatMessage property', () => {
1486
+ expect(up({ selectedShapeIds: [] })).toEqual({ selectedShapeIds: [] })
1487
+ })
1488
+
1489
+ test('down removes the chatMessage property', () => {
1490
+ expect(down({ selectedShapeIds: [] })).toEqual({ selectedShapeIds: [] })
1491
+ })
1492
+ })
1493
+
1494
+ describe('Adding canSnap to line handles', () => {
1495
+ const { up, down } = lineShapeMigrations.migrators[lineShapeVersions.AddSnapHandles]
1496
+
1497
+ test(`up works as expected`, () => {
1498
+ expect(
1499
+ up({
1500
+ props: {
1501
+ handles: {
1502
+ start: {
1503
+ id: 'start',
1504
+ type: 'vertex',
1505
+ canBind: false,
1506
+ index: 'a1',
1507
+ x: 0,
1508
+ y: 0,
1509
+ },
1510
+ end: {
1511
+ id: 'end',
1512
+ type: 'vertex',
1513
+ canBind: false,
1514
+ index: 'a2',
1515
+ x: 100.66015625,
1516
+ y: -22.07421875,
1517
+ },
1518
+ },
1519
+ },
1520
+ })
1521
+ ).toEqual({
1522
+ props: {
1523
+ handles: {
1524
+ start: {
1525
+ id: 'start',
1526
+ type: 'vertex',
1527
+ canBind: false,
1528
+ canSnap: true,
1529
+ index: 'a1',
1530
+ x: 0,
1531
+ y: 0,
1532
+ },
1533
+ end: {
1534
+ id: 'end',
1535
+ type: 'vertex',
1536
+ canBind: false,
1537
+ canSnap: true,
1538
+ index: 'a2',
1539
+ x: 100.66015625,
1540
+ y: -22.07421875,
1541
+ },
1542
+ },
1543
+ },
1544
+ })
1545
+ })
1546
+
1547
+ test(`down works as expected`, () => {
1548
+ expect(
1549
+ down({
1550
+ props: {
1551
+ handles: {
1552
+ start: {
1553
+ id: 'start',
1554
+ type: 'vertex',
1555
+ canBind: false,
1556
+ canSnap: true,
1557
+ index: 'a1',
1558
+ x: 0,
1559
+ y: 0,
1560
+ },
1561
+ end: {
1562
+ id: 'end',
1563
+ type: 'vertex',
1564
+ canBind: false,
1565
+ canSnap: true,
1566
+ index: 'a2',
1567
+ x: 100.66015625,
1568
+ y: -22.07421875,
1569
+ },
1570
+ },
1571
+ },
1572
+ })
1573
+ ).toEqual({
1574
+ props: {
1575
+ handles: {
1576
+ start: {
1577
+ id: 'start',
1578
+ type: 'vertex',
1579
+ canBind: false,
1580
+ index: 'a1',
1581
+ x: 0,
1582
+ y: 0,
1583
+ },
1584
+ end: {
1585
+ id: 'end',
1586
+ type: 'vertex',
1587
+ canBind: false,
1588
+ index: 'a2',
1589
+ x: 100.66015625,
1590
+ y: -22.07421875,
1591
+ },
1592
+ },
1593
+ },
1594
+ })
1595
+ })
1596
+ })
1597
+
1598
+ describe('add isHoveringCanvas to TLInstance', () => {
1599
+ const { up, down } = instanceMigrations.migrators[instanceVersions.AddHoveringCanvas]
1600
+
1601
+ test('up works as expected', () => {
1602
+ expect(up({})).toEqual({ isHoveringCanvas: null })
1603
+ })
1604
+
1605
+ test('down works as expected', () => {
1606
+ expect(down({ isHoveringCanvas: null })).toEqual({})
1607
+ })
1608
+ })
1609
+
1610
+ describe('add scribbles to TLInstance', () => {
1611
+ const { up, down } = instanceMigrations.migrators[instanceVersions.AddScribbles]
1612
+
1613
+ test('up works as expected', () => {
1614
+ expect(
1615
+ up({
1616
+ scribble: null,
1617
+ })
1618
+ ).toEqual({ scribbles: [] })
1619
+ })
1620
+
1621
+ test('down works as expected', () => {
1622
+ expect(down({ scribbles: [] })).toEqual({ scribble: null })
1623
+ })
1624
+ })
1625
+
1626
+ describe('add isPrecise to arrow handles', () => {
1627
+ const { up, down } = arrowShapeMigrations.migrators[ArrowMigrationVersions.AddIsPrecise]
1628
+
1629
+ test('up works as expected', () => {
1630
+ expect(
1631
+ up({
1632
+ props: {
1633
+ start: {
1634
+ type: 'point',
1635
+ },
1636
+ end: {
1637
+ type: 'binding',
1638
+ normalizedAnchor: { x: 0.5, y: 0.5 },
1639
+ },
1640
+ },
1641
+ })
1642
+ ).toEqual({
1643
+ props: {
1644
+ start: {
1645
+ type: 'point',
1646
+ },
1647
+ end: {
1648
+ type: 'binding',
1649
+ normalizedAnchor: { x: 0.5, y: 0.5 },
1650
+ isPrecise: false,
1651
+ },
1652
+ },
1653
+ })
1654
+ expect(
1655
+ up({
1656
+ props: {
1657
+ start: {
1658
+ type: 'point',
1659
+ },
1660
+ end: {
1661
+ type: 'binding',
1662
+ normalizedAnchor: { x: 0.15, y: 0.15 },
1663
+ },
1664
+ },
1665
+ })
1666
+ ).toEqual({
1667
+ props: {
1668
+ start: {
1669
+ type: 'point',
1670
+ },
1671
+ end: {
1672
+ type: 'binding',
1673
+ normalizedAnchor: { x: 0.15, y: 0.15 },
1674
+ isPrecise: true,
1675
+ },
1676
+ },
1677
+ })
1678
+ })
1679
+
1680
+ test('down works as expected', () => {
1681
+ expect(
1682
+ down({
1683
+ props: {
1684
+ start: {
1685
+ type: 'point',
1686
+ },
1687
+ end: {
1688
+ type: 'binding',
1689
+ normalizedAnchor: { x: 0.5, y: 0.5 },
1690
+ isPrecise: true,
1691
+ },
1692
+ },
1693
+ })
1694
+ ).toEqual({
1695
+ props: {
1696
+ start: {
1697
+ type: 'point',
1698
+ },
1699
+ end: {
1700
+ type: 'binding',
1701
+ normalizedAnchor: { x: 0.5, y: 0.5 },
1702
+ },
1703
+ },
1704
+ })
1705
+
1706
+ expect(
1707
+ down({
1708
+ props: {
1709
+ start: {
1710
+ type: 'point',
1711
+ },
1712
+ end: {
1713
+ type: 'binding',
1714
+ normalizedAnchor: { x: 0.25, y: 0.25 },
1715
+ isPrecise: true,
1716
+ },
1717
+ },
1718
+ })
1719
+ ).toEqual({
1720
+ props: {
1721
+ start: {
1722
+ type: 'point',
1723
+ },
1724
+ end: {
1725
+ type: 'binding',
1726
+ normalizedAnchor: { x: 0.25, y: 0.25 },
1727
+ },
1728
+ },
1729
+ })
1730
+
1731
+ expect(
1732
+ down({
1733
+ props: {
1734
+ start: {
1735
+ type: 'binding',
1736
+ normalizedAnchor: { x: 0.5, y: 0.5 },
1737
+ isPrecise: false,
1738
+ },
1739
+ end: {
1740
+ type: 'binding',
1741
+ normalizedAnchor: { x: 0.15, y: 0.15 },
1742
+ isPrecise: false,
1743
+ },
1744
+ },
1745
+ })
1746
+ ).toEqual({
1747
+ props: {
1748
+ start: {
1749
+ type: 'binding',
1750
+ normalizedAnchor: { x: 0.5, y: 0.5 },
1751
+ },
1752
+ end: {
1753
+ type: 'binding',
1754
+ normalizedAnchor: { x: 0.5, y: 0.5 },
1755
+ },
1756
+ },
1757
+ })
1758
+ })
1759
+ })
1760
+
1761
+ /* --- PUT YOUR MIGRATIONS TESTS ABOVE HERE --- */
1762
+
1763
+ for (const migrator of allMigrators) {
1764
+ test(`[${migrator.fileName} v${migrator.version}] up and down migrations have both been tested`, () => {
1765
+ expect(migrator.up).toHaveBeenCalled()
1766
+ expect(migrator.down).toHaveBeenCalled()
1767
+ })
1768
+ }