From d3dcd24ef44b906b4fe271f1d2c1bd2ebfa0940e Mon Sep 17 00:00:00 2001
From: Kevin Jahns <kevin.jahns@pm.me>
Date: Wed, 8 Dec 2021 16:10:49 +0100
Subject: [PATCH] fix various tests

---
 src/structs/Item.js       | 15 +++++++++++++-
 src/utils/ListIterator.js |  6 +++---
 src/utils/YEvent.js       |  8 +++++---
 tests/y-array.tests.js    | 42 ++++++++++++++++++++++++++++++++++++++-
 4 files changed, 63 insertions(+), 8 deletions(-)

diff --git a/src/structs/Item.js b/src/structs/Item.js
index 392321e0..44e9aeef 100644
--- a/src/structs/Item.js
+++ b/src/structs/Item.js
@@ -28,6 +28,7 @@ import {
 
 import * as error from 'lib0/error'
 import * as binary from 'lib0/binary'
+import { ContentMove } from './ContentMove.js'
 
 /**
  * @todo This should return several items
@@ -381,9 +382,19 @@ export class Item extends AbstractStruct {
     if (this.parent && this.parent.constructor === ID && this.id.client !== this.parent.client && this.parent.clock >= getState(store, this.parent.client)) {
       return this.parent.client
     }
+    if (this.content.constructor === ContentMove) {
+      const c = /** @type {ContentMove} */ (this.content)
+      const start = c.start.item
+      const end = c.isCollapsed() ? null : c.end.item
+      if (start && start.clock >= getState(store, start.client)) {
+        return start.client
+      }
+      if (end && end.clock >= getState(store, end.client)) {
+        return end.client
+      }
+    }
 
     // We have all missing ids, now find the items
-
     if (this.origin) {
       this.left = getItemCleanEnd(transaction, this.origin)
       this.origin = this.left.lastId
@@ -413,6 +424,7 @@ export class Item extends AbstractStruct {
         this.parent = /** @type {ContentType} */ (parentItem.content).type
       }
     }
+
     return null
   }
 
@@ -640,6 +652,7 @@ export class Item extends AbstractStruct {
     if (!this.deleted) {
       throw error.unexpectedCase()
     }
+    this.moved = null
     this.content.gc(store)
     if (parentGCd) {
       replaceStruct(store, this, new GC(this.id, this.length))
diff --git a/src/utils/ListIterator.js b/src/utils/ListIterator.js
index 4fd830c8..4404f23c 100644
--- a/src/utils/ListIterator.js
+++ b/src/utils/ListIterator.js
@@ -286,8 +286,8 @@ export class ListIterator {
     const startLength = len
     const sm = this.type._searchMarker
     let item = this.nextItem
-    while (len > 0 && !this.reachedEnd) {
-      while (item && !item.deleted && item.countable && !this.reachedEnd && len > 0) {
+    while (len > 0) {
+      while (item && !item.deleted && item.countable && !this.reachedEnd && len > 0 && item.moved === this.currMove && item !== this.currMoveEnd) {
         if (this.rel > 0) {
           item = getItemCleanStart(tr, createID(item.id.client, item.id.clock + this.rel))
           this.rel = 0
@@ -303,7 +303,7 @@ export class ListIterator {
           this.reachedEnd = true
         }
       }
-      if (item && !this.reachedEnd && len > 0) {
+      if (len > 0) {
         this.nextItem = item
         this.forward(tr, 0)
         item = this.nextItem
diff --git a/src/utils/YEvent.js b/src/utils/YEvent.js
index f50b9d70..e583fed3 100644
--- a/src/utils/YEvent.js
+++ b/src/utils/YEvent.js
@@ -195,13 +195,15 @@ export class YEvent {
               delta.push(lastOp)
             }
           }
-          for (let item = target._start; item !== null;) {
-            if (item === currMoveEnd) {
+          for (let item = target._start; ;) {
+            if (item === currMoveEnd && currMove) {
               item = currMove
               const { end, move, isNew } = movedStack.pop() || { end: null, move: null, isNew: false }
               currMoveIsNew = isNew
               currMoveEnd = end
               currMove = move
+            } else if (item === null) {
+              break
             } else if (item.content.constructor === ContentMove) {
               if (item.moved === currMove) {
                 movedStack.push({ end: currMoveEnd, move: currMove, isNew: currMoveIsNew })
@@ -213,7 +215,7 @@ export class YEvent {
                 continue // do not move to item.right
               }
             } else if (item.moved !== currMove) {
-              if (!currMoveIsNew && item.countable && item.moved && !this.adds(item) && !this.adds(item.moved) && (this.transaction.prevMoved.get(item) || null) === currMove) {
+              if (!currMoveIsNew && item.countable && item.moved && !this.adds(item) && this.adds(item.moved) && (this.transaction.prevMoved.get(item) || null) === currMove) {
                 if (lastOp === null || lastOp.delete === undefined) {
                   packOp()
                   lastOp = { delete: 0 }
diff --git a/tests/y-array.tests.js b/tests/y-array.tests.js
index 3c5ff268..0d0e84fa 100644
--- a/tests/y-array.tests.js
+++ b/tests/y-array.tests.js
@@ -472,6 +472,46 @@ export const testMove = tc => {
   compare(users)
 }
 
+/**
+ * @param {t.TestCase} tc
+ */
+export const testMove2 = tc => {
+  {
+    // move in uninitialized type
+    const yarr = new Y.Array()
+    yarr.insert(0, [1, 2])
+    yarr.move(1, 0)
+    // @ts-ignore
+    t.compare(yarr._prelimContent, [2, 1])
+  }
+  const { array0, array1, users } = init(tc, { users: 3 })
+  /**
+   * @type {any}
+   */
+  let event0 = null
+  /**
+   * @type {any}
+   */
+  let event1 = null
+  array0.observe(event => {
+    event0 = event
+  })
+  array1.observe(event => {
+    event1 = event
+  })
+  array0.insert(0, [1, 2])
+  array0.move(1, 0)
+  t.compare(array0.toArray(), [2, 1])
+  t.compare(event0.delta, [{ insert: [2] }, { retain: 1 }, { delete: 1 }])
+  Y.applyUpdate(users[1], Y.encodeStateAsUpdate(users[0]))
+  t.compare(array1.toArray(), [2, 1])
+  t.compare(event1.delta, [{ insert: [2, 1] }])
+  array0.move(0, 2)
+  t.compare(array0.toArray(), [1, 2])
+  t.compare(event0.delta, [{ delete: 1 }, { retain: 1 }, { insert: [2] }])
+  compare(users)
+}
+
 /**
  * @param {t.TestCase} tc
  */
@@ -613,7 +653,7 @@ const compareTestobjects = cmp => {
  * @param {t.TestCase} tc
  */
 export const testRepeatGeneratingYarrayTests6 = tc => {
-  compareTestobjects(applyRandomTests(tc, arrayTransactions, 3, monitorArrayTestObject))
+  compareTestobjects(applyRandomTests(tc, arrayTransactions, 7, monitorArrayTestObject))
 }
 
 /**