Establishes version control for the personal Logseq DB fork before starting the server-client architecture migration and domain features described in docs/agent-guide and the approved implementation plan. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
260 lines
11 KiB
Clojure
260 lines
11 KiB
Clojure
(ns logseq.e2e.graph-navigation-basic-test
|
|
(:require
|
|
[clojure.string :as string]
|
|
[clojure.test :refer [deftest is testing use-fixtures]]
|
|
[logseq.e2e.api :refer [ls-api-call!]]
|
|
[logseq.e2e.assert :as assert]
|
|
[logseq.e2e.block :as b]
|
|
[logseq.e2e.fixtures :as fixtures]
|
|
[logseq.e2e.graph :as graph]
|
|
[logseq.e2e.keyboard :as k]
|
|
[logseq.e2e.locator :as loc]
|
|
[logseq.e2e.page :as page]
|
|
[logseq.e2e.util :as util]
|
|
[wally.main :as w]))
|
|
|
|
(use-fixtures :once fixtures/open-page)
|
|
(use-fixtures :each fixtures/new-logseq-page fixtures/validate-graph)
|
|
|
|
(deftest graph-empty-state-and-name-validation-test
|
|
(testing "graph creation exposes an actionable empty state and rejects invalid/duplicate names"
|
|
(let [graph-name (str "sample-valid-graph-" (random-uuid))]
|
|
(graph/goto-all-graphs)
|
|
(assert/assert-is-visible
|
|
(loc/filter "#main-content-container" :has-text "Create a new graph"))
|
|
(util/search-and-click "Add a DB graph")
|
|
(let [input (w/-query "input[placeholder='your graph name']")]
|
|
(w/fill input "")
|
|
(w/click "button:text('Submit')")
|
|
(assert/assert-is-visible ".new-graph")
|
|
(doseq [invalid-name ["bad/name" "bad\\name"]]
|
|
(w/fill input invalid-name)
|
|
(w/click "button:text('Submit')")
|
|
(assert/assert-is-visible
|
|
".ui__toast.warning:has-text(\"Graph name can't contain\")")
|
|
(w/click (.last (w/-query ".ui__toast.warning button"))))
|
|
(w/fill input graph-name)
|
|
(w/click "button:text('Submit')")
|
|
(assert/assert-graph-loaded?)
|
|
(graph/goto-all-graphs)
|
|
(util/search-and-click "Add a DB graph")
|
|
(w/fill "input[placeholder='your graph name']" graph-name)
|
|
(w/click "button:text('Submit')")
|
|
(assert/assert-is-visible
|
|
".ui__toast.error:has-text('already exists')")
|
|
(k/esc)
|
|
(graph/switch-graph graph-name false false)))))
|
|
|
|
(deftest graph-refresh-and-browser-history-test
|
|
(testing "refresh and back/forward restore current graph/page without duplicate history"
|
|
(let [page-a "graph history a"
|
|
page-b "graph history b"]
|
|
(page/new-page page-a)
|
|
(b/new-block "history a content")
|
|
(util/exit-edit)
|
|
(page/new-page page-b)
|
|
(b/new-block "history b content")
|
|
(util/exit-edit)
|
|
(.goBack (w/get-page))
|
|
(assert/assert-is-visible
|
|
(loc/filter "#main-content-container" :has-text "history a content"))
|
|
(.goForward (w/get-page))
|
|
(assert/assert-is-visible
|
|
(loc/filter "#main-content-container" :has-text "history b content"))
|
|
(util/refresh-until-graph-loaded)
|
|
(is (= page-b (page/get-page-name)))
|
|
(assert/assert-is-visible
|
|
(loc/filter "#main-content-container" :has-text "history b content")))))
|
|
|
|
(deftest direct-page-and-block-route-test
|
|
(testing "direct page/block URLs load exact worker entities and missing routes recover"
|
|
(let [suffix (str (random-uuid))
|
|
page-name (str "direct-route-page-" suffix)
|
|
block-content (str "direct route block " suffix)]
|
|
(page/new-page page-name)
|
|
(b/new-block block-content)
|
|
(let [block-uuid (.getAttribute
|
|
(util/get-edit-block-container)
|
|
"blockid")]
|
|
(util/exit-edit)
|
|
(util/refresh-until-graph-loaded)
|
|
(let [page-uuid (get (ls-api-call! :editor.getPage page-name) "uuid")
|
|
block (ls-api-call! :editor.getBlock block-uuid)
|
|
current-url (.url (w/get-page))
|
|
base (-> current-url
|
|
(string/replace #"[?#].*$" "")
|
|
(string/replace #"/$" ""))
|
|
graph-id (some-> (re-find #"[?&]graph-id=([^&]+)" current-url)
|
|
second)
|
|
direct-url (fn [kind uuid]
|
|
(str base
|
|
"/#/"
|
|
kind
|
|
"/"
|
|
uuid
|
|
"?graph-id="
|
|
graph-id))
|
|
navigate-direct! (fn [url]
|
|
(w/navigate "about:blank")
|
|
(w/navigate url))]
|
|
(is (= block-content (get block "content")))
|
|
(is (some? block-uuid))
|
|
(is (some? graph-id))
|
|
(navigate-direct! (direct-url "page" page-uuid))
|
|
(assert/assert-is-visible
|
|
(loc/filter "[data-testid='page title']" :has-text page-name))
|
|
(is (= page-name (page/get-page-name)))
|
|
(assert/assert-is-visible
|
|
(loc/filter "#main-content-container" :has-text block-content))
|
|
(is (= block-content
|
|
(get (ls-api-call! :editor.getBlock block-uuid) "content")))
|
|
(navigate-direct! (direct-url "block" block-uuid))
|
|
(assert/assert-is-visible
|
|
(loc/filter "#main-content-container" :has-text block-content))
|
|
(navigate-direct! (direct-url "page" (random-uuid)))
|
|
(assert/assert-is-visible
|
|
(loc/filter "#main-content-container" :has-text "Page not found"))
|
|
(util/goto-journals)
|
|
(assert/assert-is-visible "#journals"))))))
|
|
|
|
(deftest local-graph-delete-and-list-metadata-test
|
|
(testing "graph list metadata is current and local deletion cannot leak its route/data"
|
|
(let [graph-name (str "graph-delete-" (random-uuid))]
|
|
(graph/new-graph graph-name false)
|
|
(page/new-page "local graph deletion page")
|
|
(b/new-block "local graph deletion content")
|
|
(graph/goto-all-graphs)
|
|
(let [card (w/-query (format "div[data-testid='logseq_db_%s']" graph-name))]
|
|
(assert/assert-is-visible card)
|
|
(assert/assert-is-visible
|
|
(loc/filter card :has-text "Last opened at:"))
|
|
(w/click (.locator card ".graph-action-btn"))
|
|
(w/click ".delete-local-graph-menu-item")
|
|
(w/click "div[role='alertdialog'] button:text('Confirm')")
|
|
(assert/assert-have-count card 0))
|
|
(assert/assert-have-count
|
|
(loc/filter "#main-content-container" :has-text "local graph deletion content")
|
|
0)
|
|
(graph/switch-graph "Demo" false false)
|
|
(page/new-page "post deletion validation")
|
|
(util/exit-edit)
|
|
(util/refresh-until-graph-loaded)
|
|
(assert/assert-is-visible "#search-button"))))
|
|
|
|
(deftest default-home-route-test
|
|
(testing "configured default home opens on startup and invalid home falls back explicitly"
|
|
(let [set-default-home!
|
|
(fn [page-name]
|
|
(ls-api-call! :app.setCurrentGraphConfigs
|
|
{"default-home" (cond-> {}
|
|
page-name
|
|
(assoc "page" page-name))})
|
|
(loop [remaining 40]
|
|
(let [stored-page
|
|
(ls-api-call! :app.getCurrentGraphConfigs
|
|
"default-home"
|
|
"page")]
|
|
(when-not (= page-name stored-page)
|
|
(when (zero? remaining)
|
|
(throw (ex-info "Default home config did not persist"
|
|
{:expected page-name
|
|
:actual stored-page})))
|
|
(util/wait-timeout 250)
|
|
(recur (dec remaining))))))]
|
|
(page/new-page "sample default home")
|
|
(b/new-block "default home content")
|
|
(util/exit-edit)
|
|
(let [current-url (.url (w/get-page))
|
|
base (-> current-url
|
|
(string/replace #"[?#].*$" "")
|
|
(string/replace #"/$" ""))
|
|
graph-id (some-> (re-find #"[?&]graph-id=([^&]+)" current-url)
|
|
second)
|
|
home-url (str base "/#/?graph-id=" graph-id)
|
|
navigate-home! (fn []
|
|
(w/navigate "about:blank")
|
|
(w/navigate home-url))]
|
|
(is (some? graph-id))
|
|
(set-default-home! "sample default home")
|
|
(navigate-home!)
|
|
(assert/assert-is-visible
|
|
(loc/filter "[data-testid='page title']"
|
|
:has-text "sample default home"))
|
|
(is (= "sample default home" (page/get-page-name)))
|
|
(set-default-home! "missing sample home")
|
|
(navigate-home!)
|
|
(assert/assert-is-visible "#journals")
|
|
(assert/assert-is-hidden ".loading-graph")
|
|
(set-default-home! nil)
|
|
(page/new-page "post default home validation")
|
|
(util/exit-edit)
|
|
(util/refresh-until-graph-loaded)
|
|
(assert/assert-is-visible "#search-button")))))
|
|
|
|
(deftest graph-view-mode-settings-test
|
|
(testing "Graph view modes and settings rebuild the canvas"
|
|
(doseq [title ["graph view alpha" "graph view beta" "graph view gamma"]]
|
|
(page/new-page title)
|
|
(b/new-block "[[graph view alpha]]")
|
|
(util/set-tag title)
|
|
(util/exit-edit))
|
|
(util/search-and-click "Go to graph view")
|
|
(assert/assert-is-visible "#global-graph.graph-root")
|
|
(assert/assert-is-visible
|
|
"[role='application'][aria-label='Graph canvas']")
|
|
(w/click ".graph-settings-toggle")
|
|
(w/click (loc/filter ".graph-mode-tab" :has-text "Tags"))
|
|
(assert/assert-is-visible
|
|
(loc/filter ".graph-mode-tab[aria-selected='true']" :has-text "Tags"))
|
|
(w/click (loc/filter ".graph-mode-tab" :has-text "All pages"))
|
|
(assert/assert-is-visible
|
|
(loc/filter ".graph-mode-tab[aria-selected='true']" :has-text "All pages"))
|
|
(w/refresh)
|
|
(assert/assert-is-visible "#global-graph.graph-root")
|
|
(assert/assert-is-visible
|
|
"[role='application'][aria-label='Graph canvas']")
|
|
(assert/assert-is-hidden ".graph-error")
|
|
(page/goto-page "graph view beta")
|
|
(assert/assert-graph-loaded?)))
|
|
|
|
(deftest graph-time-travel-playback-test
|
|
(testing "time travel limits nodes and returning to Now restores the complete graph"
|
|
(page/new-page "time travel old")
|
|
(util/wait-timeout 20)
|
|
(page/new-page "time travel new")
|
|
(util/search-and-click "Go to graph view")
|
|
(w/click "button[title*='Time'], button:has-text('Time travel')")
|
|
(let [slider (w/-query "input[type='range']")]
|
|
(assert/assert-is-visible slider)
|
|
(let [full-count (util/count-elements ".graph-node, [data-node-id]")]
|
|
(w/click slider)
|
|
(k/press "Home")
|
|
(is (<= (util/count-elements ".graph-node, [data-node-id]")
|
|
full-count))
|
|
(w/click ".graph-time-travel-reset[title='Now']")
|
|
(assert/assert-is-visible
|
|
(loc/filter ".graph-time-travel-label" :has-text "Now"))
|
|
(is (= full-count
|
|
(util/count-elements ".graph-node, [data-node-id]")))))))
|
|
|
|
(deftest restoring-graph-gates-and-recovers-interaction-test
|
|
(testing "reload/restoring never accepts half-mounted edits and restores all controls"
|
|
(b/new-block "restore interaction target")
|
|
(util/exit-edit)
|
|
(w/refresh)
|
|
(assert/assert-is-visible
|
|
(loc/or ".loading-graph, .ui__loading"
|
|
"[data-testid='page title']"))
|
|
(when (w/visible? ".loading-graph, .ui__loading")
|
|
(is (or (not (w/visible? ".block-add-button"))
|
|
(.isDisabled (w/-query ".block-add-button")))))
|
|
(assert/assert-graph-loaded?)
|
|
(assert/assert-is-visible ".toolbar-dots-btn")
|
|
(w/click ".toolbar-dots-btn")
|
|
(assert/assert-is-visible "[role='menuitem']")
|
|
(k/esc)
|
|
(b/jump-to-block "restore interaction target")
|
|
(util/move-cursor-to-end)
|
|
(util/press-seq " after restore")
|
|
(is (= "restore interaction target after restore" (util/get-edit-content)))))
|