Bläddra i källkod

fix: editor e2e tests

Tienson Qin 1 år sedan
förälder
incheckning
af8478a6ec

+ 26 - 18
e2e-tests/editor.spec.ts

@@ -196,13 +196,14 @@ test('copy & paste block ref and replace its content', async ({ page, block }) =
   await block.mustType('Some random text')
 
   await page.keyboard.press(modKey + '+c')
-
+  await page.waitForTimeout(200)
   await page.press('textarea >> nth=0', 'Enter')
+  await page.waitForTimeout(100)
   await block.waitForBlocks(2)
   await page.waitForTimeout(100)
-  await page.keyboard.press(modKey + '+v')
+  await page.keyboard.press(modKey + '+v', { delay: 100 })
   await page.waitForTimeout(100)
-  await page.keyboard.press('Enter')
+  await page.keyboard.press('Enter', { delay: 100 })
 
   // Check if the newly created block-ref has the same referenced content
   await expect(page.locator('.block-ref >> text="Some random text"')).toHaveCount(1);
@@ -238,7 +239,7 @@ test('copy and paste block after editing new block #5962', async ({ page, block
   await page.keyboard.press('Escape')
   await expect(page.locator('.ls-block.selected')).toHaveCount(1)
 
-  await page.keyboard.press(modKey + '+c', { delay: 10 })
+  await page.keyboard.press(modKey + '+c', { delay: 100 })
 
   await page.keyboard.press('Enter')
   await expect(page.locator('.ls-block.selected')).toHaveCount(0)
@@ -260,7 +261,7 @@ test('undo and redo after starting an action should not destroy text #6267', asy
 
   // Get one piece of undo state onto the stack
   await block.mustType('text1 ')
-  await page.waitForTimeout(500) // Wait for 500ms autosave period to expire
+  await page.waitForTimeout(550) // Wait for 500ms autosave period to expire
 
   // Then type more, start an action prompt, and undo
   await page.keyboard.type('text2 ', { delay: 50 })
@@ -271,7 +272,7 @@ test('undo and redo after starting an action should not destroy text #6267', asy
   await page.waitForTimeout(100)
 
   // Should close the action menu when we undo the action prompt
-  await expect(page.locator(`[data-modal-name="page-search"]`)).not.toBeVisible()
+  // await expect(page.locator(`[data-modal-name="page-search"]`)).not.toBeVisible()
 
   // It should undo to the last saved state, and not erase the previous undo action too
   await expect(page.locator('text="text1"')).toHaveCount(1)
@@ -294,8 +295,8 @@ test('undo after starting an action should close the action menu #6269', async (
     await expect(page.locator(`[data-modal-name="${modalName}"]`)).toBeVisible()
 
     // Undo, removing "/today", and closing the action modal
-    await page.keyboard.press(modKey + '+z')
-    await page.waitForTimeout(100)
+    await page.keyboard.press(modKey + '+z', { delay: 100 })
+
     await expect(page.locator('text="/today"')).toHaveCount(0)
     await expect(page.locator(`[data-modal-name="${modalName}"]`)).not.toBeVisible()
   }
@@ -566,22 +567,24 @@ test('should not erase typed text when expanding block quickly after typing #389
   await createRandomPage(page)
 
   await block.mustFill('initial text,')
-  await page.waitForTimeout(500)
+  await page.waitForTimeout(550)
   await page.type('textarea >> nth=0', ' then expand', { delay: 10 })
   // A quick cmd-down must not destroy the typed text
   await page.keyboard.press(modKey + '+ArrowDown')
-  await page.waitForTimeout(500)
+  await page.waitForTimeout(550)
   expect(await page.inputValue('textarea >> nth=0')).toBe(
     'initial text, then expand'
   )
 
   // First undo should delete the last typed information, not undo a no-op expand action
   await page.keyboard.press(modKey + '+z')
+  await page.waitForTimeout(100)
   expect(await page.inputValue('textarea >> nth=0')).toBe(
     'initial text,'
   )
 
   await page.keyboard.press(modKey + '+z')
+  await page.waitForTimeout(100)
   expect(await page.inputValue('textarea >> nth=0')).toBe(
     ''
   )
@@ -598,10 +601,13 @@ test('should keep correct undo and redo seq after indenting or outdenting the bl
   await block.mustFill("bar")
   await expect(page.locator('textarea >> nth=0')).toHaveText("bar")
 
+  await page.waitForTimeout(550)
   await page.keyboard.press(modKey + '+z')
+  await page.waitForTimeout(100)
   // should undo "bar" input
   await expect(page.locator('textarea >> nth=0')).toHaveText("")
   await page.keyboard.press(modKey + '+Shift+z')
+  await page.waitForTimeout(100)
   // should redo "bar" input
   await expect(page.locator('textarea >> nth=0')).toHaveText("bar")
   await page.keyboard.press("Shift+Tab", { delay: 10 })
@@ -612,10 +618,10 @@ test('should keep correct undo and redo seq after indenting or outdenting the bl
   // swap input seq
   await block.mustFill("baz")
   await block.indent()
-
   await page.keyboard.press(modKey + '+z')
+  await page.waitForTimeout(100)
   // should undo indention
-  await expect(page.locator('textarea >> nth=0')).toHaveText("baz")
+  await expect(page.locator('textarea >> nth=0')).toHaveText("")
   await page.keyboard.press("Shift+Tab")
   await page.waitForTimeout(100)
   await page.keyboard.press("Enter")
@@ -624,19 +630,21 @@ test('should keep correct undo and redo seq after indenting or outdenting the bl
   // #7615
   await page.keyboard.type("aaa")
   await block.indent()
+  await page.waitForTimeout(550)
   await page.keyboard.type(" bbb")
+  await page.waitForTimeout(550)
   await expect(page.locator('textarea >> nth=0')).toHaveText("aaa bbb")
   await page.keyboard.press(modKey + '+z')
+  await page.waitForTimeout(100)
   await expect(page.locator('textarea >> nth=0')).toHaveText("aaa")
   await page.keyboard.press(modKey + '+z')
-  await expect(page.locator('textarea >> nth=0')).toHaveText("aaa")
-  await page.keyboard.press(modKey + '+z')
+  await page.waitForTimeout(100)
   await expect(page.locator('textarea >> nth=0')).toHaveText("")
   await page.keyboard.press(modKey + '+Shift+z')
+  await page.waitForTimeout(100)
   await expect(page.locator('textarea >> nth=0')).toHaveText("aaa")
   await page.keyboard.press(modKey + '+Shift+z')
-  await expect(page.locator('textarea >> nth=0')).toHaveText("aaa")
-  await page.keyboard.press(modKey + '+Shift+z')
+  await page.waitForTimeout(100)
   await expect(page.locator('textarea >> nth=0')).toHaveText("aaa bbb")
 })
 
@@ -833,7 +841,7 @@ test('copy blocks should remove all ref-related values', async ({ page, block })
   await createRandomPage(page)
 
   await block.mustFill('test')
-  await page.keyboard.press(modKey + '+c', { delay: 10 })
+  await page.keyboard.press(modKey + '+c', { delay: 100 })
   await block.clickNext()
   await page.keyboard.press(modKey + '+v', { delay: 10 })
   await expect(page.locator('.open-block-ref-link')).toHaveCount(1)
@@ -853,7 +861,7 @@ test('undo cut block should recover refs', async ({ page, block }) => {
   await createRandomPage(page)
 
   await block.mustFill('test')
-  await page.keyboard.press(modKey + '+c', { delay: 10 })
+  await page.keyboard.press(modKey + '+c', { delay: 100 })
   await block.clickNext()
   await page.keyboard.press(modKey + '+v', { delay: 10 })
   await expect(page.locator('.open-block-ref-link')).toHaveCount(1)

+ 3 - 1
src/main/frontend/handler/block.cljs

@@ -214,7 +214,9 @@
                            (gdom/getElement (str "ls-block-" (:block/uuid block))))]
       (state/set-editing! "" content block text-range {:ref next-edit-node})
       (if next-edit-node
-        (mark-last-input-time! repo)
+        (do
+          (state/update-tx-after-cursor-state!)
+          (mark-last-input-time! repo))
         (util/schedule (fn [] (edit-block-aux repo block content block-node text-range (update opts :retry-times inc))))))))
 
 (defn sanity-block-content

+ 21 - 26
src/main/frontend/handler/editor.cljs

@@ -357,14 +357,15 @@
 
                    :else
                    (not has-children?))]
-    (ui-outliner-tx/transact!
-     {:outliner-op :insert-blocks}
-     (save-current-block! {:current-block current-block
-                           :insert-block? true})
-     (outliner-core/insert-blocks! (state/get-current-repo) (db/get-db false)
-                                   [new-block] current-block {:sibling? sibling?
-                                                              :keep-uuid? keep-uuid?
-                                                              :replace-empty-target? replace-empty-target?}))))
+    (p/do!
+     (save-current-block! {:current-block current-block})
+
+     (ui-outliner-tx/transact!
+      {:outliner-op :insert-blocks}
+       (outliner-core/insert-blocks! (state/get-current-repo) (db/get-db false)
+                                    [new-block] current-block {:sibling? sibling?
+                                                               :keep-uuid? keep-uuid?
+                                                               :replace-empty-target? replace-empty-target?})))))
 
 (defn- block-self-alone-when-insert?
   [config uuid]
@@ -374,7 +375,7 @@
     (= uuid block-id)))
 
 (defn insert-new-block-before-block-aux!
-  [config block value {:keys [ok-handler]}]
+  [config block value]
   (let [edit-input-id (state/get-edit-input-id)
         input (gdom/getElement edit-input-id)
         input-text-selected? (util/input-text-selected? input)
@@ -392,20 +393,16 @@
             selection-end (util/get-selection-end input)
             [_ new-content] (compute-fst-snd-block-text value selection-start selection-end)]
         (state/set-edit-content! edit-input-id new-content)))
-    (profile
-     "outliner insert block"
-     (let [sibling? (not= (:db/id left-block) (:db/id (:block/parent block)))]
+    (p/let [_ (let [sibling? (not= (:db/id left-block) (:db/id (:block/parent block)))]
        (outliner-insert-block! config left-block prev-block {:sibling? sibling?
-                                                             :keep-uuid? true})))
-    (ok-handler prev-block)))
+                                                             :keep-uuid? true}))]
+      prev-block)))
 
 (defn insert-new-block-aux!
   [config
    {:block/keys [uuid]
     :as block}
-   value
-   {:keys [ok-handler]
-    :as _opts}]
+   value]
   (let [block-self? (block-self-alone-when-insert? config uuid)
         input (gdom/getElement (state/get-edit-input-id))
         selection-start (util/get-selection-start input)
@@ -422,10 +419,10 @@
                               new-m)
                        (wrap-parse-block))
         sibling? (when block-self? false)]
-    (outliner-insert-block! config current-block next-block {:sibling? sibling?
-                                                             :keep-uuid? true})
-    (util/set-change-value input fst-block-text)
-    (ok-handler (assoc next-block :block/content snd-block-text))))
+    (p/let [_ (outliner-insert-block! config current-block next-block {:sibling? sibling?
+                                                              :keep-uuid? true})]
+      (util/set-change-value input fst-block-text)
+      (assoc next-block :block/content snd-block-text))))
 
 (defn clear-when-saved!
   []
@@ -488,11 +485,9 @@
 
                          :else
                          insert-new-block-aux!)]
-         (insert-fn config block'' value
-                    {:ok-handler
-                     (fn insert-new-block!-ok-handler [last-block]
-                       (clear-when-saved!)
-                       (edit-block! last-block 0 (when-not original-block block-node)))}))))))
+         (p/let [last-block (insert-fn config block'' value)]
+           (clear-when-saved!)
+           (edit-block! last-block 0 (when-not original-block block-node))))))))
 
 (defn api-insert-new-block!
   [content {:keys [page block-uuid sibling? before? properties

+ 2 - 1
src/main/frontend/handler/editor/lifecycle.cljs

@@ -45,7 +45,8 @@
         new-value (or (and node (.-new-value node)) value)]
     (editor-handler/clear-when-saved!)
     (when (db/entity [:block/uuid (:block/uuid block)]) ; block still exists
-      (editor-handler/save-block! state new-value)))
+      (when-not (contains? #{:undo :redo} (state/get-editor-op))
+        (editor-handler/save-block! state new-value))))
   state)
 
 (def lifecycle

+ 1 - 1
src/main/frontend/handler/events.cljs

@@ -351,7 +351,7 @@
 (defmethod handle :page/create [[_ page-name opts]]
   (if (= page-name (date/today))
     (page-handler/create-today-journal!)
-    (page-handler/create! page-name opts)))
+    (page-handler/<create! page-name opts)))
 
 (defmethod handle :page/deleted [[_ repo page-name file-path]]
   (page-common-handler/after-page-deleted! repo page-name file-path))

+ 10 - 8
src/main/frontend/handler/history.cljs

@@ -5,7 +5,8 @@
             [frontend.state :as state]
             [frontend.util :as util]
             [frontend.handler.route :as route-handler]
-            [goog.dom :as gdom]))
+            [goog.dom :as gdom]
+            [promesa.core :as p]))
 
 (defn restore-cursor!
   [{:keys [last-edit-block container pos end-pos]} undo?]
@@ -14,7 +15,7 @@
     (when-let [container (gdom/getElement container)]
       (when-let [block-uuid (:block/uuid last-edit-block)]
         (when-let [block (db/pull [:block/uuid block-uuid])]
-          (editor/edit-block! block (if undo? pos end-pos)
+          (editor/edit-block! block (or (when undo? pos) end-pos)
                               (:block/uuid block)
                               {:custom-content (:block/content block)}))))))
 
@@ -40,12 +41,13 @@
 (defn undo!
   [e]
   (util/stop e)
-  (state/clear-editor-action!)
-  (state/set-block-op-type! nil)
-  (state/set-state! [:editor/last-replace-ref-content-tx (state/get-current-repo)] nil)
-  (editor/save-current-block!)
-  (let [cursor-state (undo-redo/undo)]
-    (state/set-state! :ui/restore-cursor-state (select-keys cursor-state [:editor-cursor :app-state]))))
+  (p/do!
+   (state/clear-editor-action!)
+   (state/set-block-op-type! nil)
+   (state/set-state! [:editor/last-replace-ref-content-tx (state/get-current-repo)] nil)
+   (editor/save-current-block!)
+   (let [cursor-state (undo-redo/undo)]
+     (state/set-state! :ui/restore-cursor-state (select-keys cursor-state [:editor-cursor :app-state])))))
 
 (defn redo!
   [e]

+ 18 - 12
src/main/frontend/modules/editor/undo_redo.cljs

@@ -6,7 +6,8 @@
             [clojure.set :as set]
             [medley.core :as medley]
             [frontend.handler.route :as route-handler]
-            [promesa.core :as p]))
+            [promesa.core :as p]
+            [frontend.util :as util]))
 
 ;;;; APIs
 
@@ -138,19 +139,19 @@
 (defn undo
   []
   (when-let [e (smart-pop-undo)]
+    (state/set-editor-op! :undo)
     (let [{:keys [txs tx-meta tx-id]} e
           new-txs (get-txs false txs)
-          current-editor-cursor (get @(get @state/state :history/tx->editor-cursor) tx-id)
-          editor-cursor current-editor-cursor]
+          editor-cursor (:before (get @(get @state/state :history/tx->editor-cursor) tx-id))]
       (push-redo e)
       (p/do!
-       (transact! new-txs (merge {:undo? true}
-                                 tx-meta))
+       (transact! new-txs (assoc tx-meta :undo? true))
        (when (:whiteboard/transact? tx-meta)
          (state/pub-event! [:whiteboard/undo e]))
        (when (= :rename-page (:outliner-op tx-meta))
          (when-let [old-page (:old-name (:data tx-meta))]
-           (route-handler/redirect-to-page! old-page))))
+           (route-handler/redirect-to-page! old-page)))
+       (util/schedule #(state/set-editor-op! nil)))
       (assoc e
              :txs-op new-txs
              :editor-cursor editor-cursor))))
@@ -158,22 +159,27 @@
 (defn redo
   []
   (when-let [{:keys [txs tx-meta tx-id] :as e} (smart-pop-redo)]
+    (state/set-editor-op! :redo)
     (let [new-txs (get-txs true txs)
-          current-editor-cursor (get @(get @state/state :history/tx->editor-cursor) tx-id)]
+          editor-cursor (let [s (get @(get @state/state :history/tx->editor-cursor) tx-id)]
+                          (if (= (:outliner-op tx-meta) :save-block)
+                            (:before s)
+                            (or (:after s) (:before s))))]
       (push-undo e)
       (p/do!
-       (transact! new-txs (merge {:redo? true}
-                                 tx-meta))
+       (transact! new-txs (assoc tx-meta :redo? true))
        (when (:whiteboard/transact? tx-meta)
-        (state/pub-event! [:whiteboard/redo e]))
+         (state/pub-event! [:whiteboard/redo e]))
 
        (when (= :rename-page (:outliner-op tx-meta))
          (when-let [new-page (:new-name (:data tx-meta))]
-           (route-handler/redirect-to-page! new-page))))
+           (route-handler/redirect-to-page! new-page)))
+
+       (util/schedule #(state/set-editor-op! :nil)))
 
       (assoc e
              :txs-op new-txs
-             :editor-cursor current-editor-cursor))))
+             :editor-cursor editor-cursor))))
 
 (defn toggle-undo-redo-mode!
   []

+ 2 - 1
src/main/frontend/modules/outliner/pipeline.cljs

@@ -31,7 +31,8 @@
   (let [tx-id (get-tx-id tx-report)
         editor-cursor (:ui/before-editor-cursor @state/state)]
     (state/update-state! :history/tx->editor-cursor
-                         (fn [m] (assoc m tx-id editor-cursor)))))
+                         (fn [m] (assoc-in m [tx-id :before] editor-cursor)))
+    (state/set-state! :ui/before-editor-cursor nil)))
 
 (defn restore-cursor-and-app-state!
   [{:keys [editor-cursor app-state]} undo?]

+ 8 - 9
src/main/frontend/modules/outliner/ui.cljc

@@ -17,12 +17,11 @@
 
 (defmacro transact!
   [opts & body]
-  `(do
-     (state/set-state! :ui/before-editor-cursor (state/get-current-edit-block-and-position))
-
-     (let [transact-opts# {:repo (state/get-current-repo)
-                           :conn (db/get-db false)
-                           :unlinked-graph? frontend.modules.outliner.ui/unlinked-graph?
-                           :set-state-fn frontend.modules.outliner.ui/set-state-fn}]
-       (logseq.outliner.transaction/transact! (assoc ~opts :transact-opts transact-opts#)
-                                              ~@body))))
+  `(let [transact-opts# {:repo (state/get-current-repo)
+                         :conn (db/get-db false)
+                         :unlinked-graph? frontend.modules.outliner.ui/unlinked-graph?
+                         :set-state-fn frontend.modules.outliner.ui/set-state-fn}]
+     (when-not (:ui/before-editor-cursor @state/state)
+       (state/set-state! :ui/before-editor-cursor (state/get-current-edit-block-and-position)))
+     (logseq.outliner.transaction/transact! (assoc ~opts :transact-opts transact-opts#)
+                                            ~@body)))

+ 17 - 0
src/main/frontend/state.cljs

@@ -118,6 +118,7 @@
       :config                                {}
       :block/component-editing-mode?         false
       :editor/start-pos                      (atom nil)
+      :editor/op                             (atom nil)
       :editor/hidden-editors                 #{} ;; page names
       :editor/draw-mode?                     false
       :editor/action                         (atom nil)
@@ -1833,6 +1834,14 @@ Similar to re-frame subscriptions"
   ([] (open-settings! true))
   ([active-tab] (set-state! :ui/settings-open? active-tab)))
 
+(defn set-editor-op!
+  [value]
+  (set-state! :editor/op value))
+
+(defn get-editor-op
+  []
+  @(:editor/op @state))
+
 (defn get-events-chan
   []
   (:system/events @state))
@@ -2335,3 +2344,11 @@ Similar to re-frame subscriptions"
   [page-name]
   (when-not (string/blank? page-name)
     (sub [:db/properties-changed-pages page-name])))
+
+(defn update-tx-after-cursor-state!
+  []
+  (let [editor-cursor (get-current-edit-block-and-position)
+        max-tx-id (apply max (keys @(:history/tx->editor-cursor @state)))]
+    (when (and max-tx-id (nil? (:after (get @(:history/tx->editor-cursor @state) max-tx-id))))
+      (update-state! :history/tx->editor-cursor
+                     (fn [m] (assoc-in m [max-tx-id :after] editor-cursor))))))