Initial import of LogDarioSeq baseline

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>
This commit is contained in:
2026-08-25 15:32:15 +02:00
commit 40631362b3
2016 changed files with 543898 additions and 0 deletions

View File

@@ -0,0 +1,507 @@
(ns logseq.e2e.block-property-basic-test
(:require
[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.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)
(defn- current-page-name
[]
(page/get-page-name))
(defn- add-property-through-ui!
[property-name property-type]
(util/input-command "Add property")
(w/click "input[placeholder]")
(util/input property-name)
(w/click (w/get-by-text "New option:"))
(w/click (loc/and "span" (util/get-by-text property-type true))))
(defn- block-value
[uuid property-name]
(let [result (ls-api-call! :editor.getBlockProperty uuid property-name)]
(if (instance? java.util.Map result)
(get result "value")
result)))
(defn- open-flashcards!
[]
(util/double-esc)
(when-not (w/visible? "#left-sidebar.is-open")
(w/click "#left-menu")
(w/wait-for "#left-sidebar.is-open"))
(w/wait-for ".flashcards-nav")
(w/click ".flashcards-nav a")
(assert/assert-is-visible "#cards-modal"))
(defn- wait-for-property-type
[property-name expected-type]
(loop [remaining-attempts 20]
(let [actual-type (get (ls-api-call! :editor.getProperty property-name)
"type")]
(if (or (= expected-type actual-type)
(zero? remaining-attempts))
actual-type
(do
(util/wait-timeout 50)
(recur (dec remaining-attempts)))))))
(deftest references-embeds-and-mounted-instance-refresh-test
(testing "page refs, embeds, linked refs and mounted copies share canonical updates"
(let [target-page "block sample target"
source-page "block sample source"]
(page/new-page target-page)
(let [target (ls-api-call! :editor.appendBlockInPage
target-page
"target parent")
target-uuid (get target "uuid")]
(ls-api-call! :editor.insertBlock
target-uuid
"target child"
{:sibling false})
(page/new-page source-page)
(b/new-block (str "[[" target-page "]]"))
(b/new-block "")
(util/input-command "Node embed")
(util/press-seq target-page)
(assert/assert-is-visible
(loc/filter ".ui__popover-content a.menu-link.chosen"
:has-text target-page))
(k/enter)
(assert/assert-is-visible
(loc/filter ".embed-block" :has-text "target child"))
(ls-api-call! :editor.openInRightSidebar target-uuid)
(w/click
(format ".cp__right-sidebar #block-content-%s" target-uuid))
(w/fill util/editor-q "target parent updated")
(util/exit-edit)
(doseq [container ["#main-content-container" ".cp__right-sidebar"]]
(assert/assert-is-visible
(loc/filter container :has-text "target parent updated")))
(page/goto-page target-page)
(assert/assert-is-visible
(loc/filter ".references" :has-text source-page))))))
(deftest unlinked-reference-filter-and-breadcrumb-test
(testing "unlinked-reference search updates results and breadcrumbs track renames"
(let [target "reference sample target"
include-page "reference include"
exclude-page "reference exclude"]
(page/new-page target)
(let [parent (ls-api-call! :editor.appendBlockInPage
target
"target nested parent")
parent-uuid (get parent "uuid")
child (ls-api-call! :editor.insertBlock
parent-uuid
"target nested child"
{:sibling false})
child-uuid (get child "uuid")]
(page/new-page include-page)
(b/new-block (str target " plain mention"))
(page/new-page exclude-page)
(b/new-block (str target " another mention"))
(page/goto-page target)
(w/click ".unlinked-references .ls-foldable-title-control")
(assert/assert-is-visible
(loc/filter ".unlinked-references" :has-text include-page))
(w/click
".unlinked-references button:has(.ls-icon-search)")
(w/fill ".unlinked-references .view-action-search input" include-page)
(assert/assert-have-count
(loc/filter ".unlinked-references .ls-view-body" :has-text exclude-page)
0)
(w/click (format "#dot-%s" child-uuid))
(assert/assert-is-visible
(loc/filter ".breadcrumb" :has-text "target nested parent"))
(ls-api-call! :editor.updateBlock parent-uuid "target renamed parent")
(assert/assert-is-visible
(loc/filter ".breadcrumb" :has-text "target renamed parent"))))))
(deftest flashcard-rating-advances-once-test
(testing "answer reveal and one rating advance to the next card and update due count"
(let [page-name (current-page-name)]
(ls-api-call! :editor.appendBlockInPage page-name "sample card one #Card")
(ls-api-call! :editor.appendBlockInPage page-name "sample card two #Card")
(open-flashcards!)
(w/click "#card-answers")
(assert/assert-is-visible
(loc/filter "#cards-modal" :has-text "Again"))
(let [before (util/get-text "#cards-modal")]
(w/click "#card-good")
(let [after (util/get-text "#cards-modal")]
(is (not= before after))))
(assert/assert-have-count "#cards-modal .card-rating-loading" 0))))
(deftest multi-target-comment-draft-edit-delete-test
(testing "multi-target comments preserve drafts and support edit/delete lifecycle"
(b/new-blocks ["comment sample a" "comment sample b"])
(b/select-blocks 2)
(util/search-and-click "Add comment")
(assert/assert-is-visible
(loc/filter ".ls-comments-area" :has-text "those blocks"))
(w/fill ".ls-comment-add textarea" "draft sample comment")
(k/esc)
(assert/assert-have-count
(loc/filter ".ls-comment-row" :has-text "draft sample comment")
0)
(let [reply-placeholder
(loc/filter ".ls-comment-reply-placeholder"
:has-text "draft sample comment")]
(assert/assert-is-visible reply-placeholder)
(w/click reply-placeholder))
(is (= "draft sample comment"
(.inputValue (w/-query ".ls-comment-add textarea"))))
(w/fill ".ls-comment-add textarea" "saved sample comment")
(w/click ".ls-comment-submit")
(assert/assert-is-visible
(loc/filter ".ls-comment-row" :has-text "saved sample comment"))
(w/click ".ls-comment-row button[aria-label='Click to edit']")
(w/fill ".ls-comment-row textarea" "edited sample comment")
(k/enter)
(assert/assert-is-visible
(loc/filter ".ls-comment-row" :has-text "edited sample comment"))
(w/click ".ls-comment-row button[title='Delete']")
(assert/assert-have-count ".ls-comment-row" 0)))
(deftest block-and-comment-reaction-toggle-test
(testing "block and comment reactions use their own targets and toggle without duplicates"
(b/new-blocks ["reaction sample a" "reaction sample b"])
(b/select-blocks 2)
(util/search-and-click "Add reaction")
(w/fill ".ls-icon-picker input" "thumbs up")
(w/click ".ls-icon-picker button:has(em-emoji[id='+1'])")
(assert/assert-have-count
".ls-page-blocks .ls-block-reactions button:has(em-emoji[id='+1'])"
2)
(w/click
".ls-page-blocks .ls-block:has-text('reaction sample a') .ls-block-reactions button:has(em-emoji[id='+1'])")
(assert/assert-have-count
".ls-page-blocks .ls-block:has-text('reaction sample a') .ls-block-reactions button:has(em-emoji[id='+1'])"
0)
(w/click (loc/filter ".block-title-wrap" :has-text "reaction sample b"))
(util/search-and-click "Add comment")
(w/fill ".ls-comment-add textarea" "reaction comment")
(w/click ".ls-comment-submit")
(w/click ".ls-comment-row button[title='Add reaction']")
(w/fill ".ls-icon-picker input" "red heart")
(w/click ".ls-icon-picker button:has(em-emoji[id='heart'])")
(assert/assert-have-count
".ls-comment-row .ls-block-reactions button:has(em-emoji[id='heart'])"
1)
(assert/assert-have-count
".ls-page-blocks .ls-block-reactions button:has(em-emoji[id='heart'])"
1)))
(deftest icon-and-structural-tag-visibility-test
(testing "icons update mounted references and internal tags stay hidden from user tags"
(let [page-name "icon sample page"]
(page/new-page page-name)
(util/exit-edit)
(w/click "button:text('Add icon')")
(w/fill ".cp__emoji-icon-picker input" "books")
(w/click ".cp__emoji-icon-picker button:has(em-emoji[id='books'])")
(assert/assert-is-visible ".ls-page-icon")
(b/new-block "icon sample block")
(util/set-tag "task")
(util/exit-edit)
(assert/assert-is-visible
(loc/filter ".block-tag" :has-text "task"))
(assert/assert-have-count
(loc/filter ".block-tag" :has-text "Page")
0)
(ls-api-call! :editor.openInRightSidebar
(get (ls-api-call! :editor.getPage page-name) "uuid"))
(assert/assert-is-visible
".cp__right-sidebar .page-title em-emoji[id='books']")
(w/click ".ls-page-icon button")
(w/click ".cp__emoji-icon-picker button[data-action='del']")
(assert/assert-have-count "em-emoji[id='books']" 0))))
(deftest property-create-and-name-validation-test
(testing "property creation is unique and invalid names cannot create ghost nodes"
(b/new-block "property validation owner")
(add-property-through-ui! "valid-property" "Text")
(assert/assert-have-count
(loc/filter ".property-k" :has-text "valid-property")
1)
(b/open-last-block)
(util/input-command "Add property")
(w/click "input[placeholder]")
(assert/assert-have-count (util/get-by-text "New option:" false) 0)
(k/esc)
(doseq [invalid-name ["[[bad" "#bad"]]
(b/open-last-block)
(util/input-command "Add property")
(w/click "input[placeholder]")
(util/input invalid-name)
(w/click (w/get-by-text "New option:"))
(w/click (loc/and "span" (util/get-by-text "Text" true)))
(assert/assert-is-visible
(loc/filter ".ui__toast.error" :has-text "invalid property name"))
(doseq [message ["Property failed to create"
"invalid property name"]]
(let [toast (loc/filter ".ui__toast.error" :has-text message)]
(when (w/visible? toast)
(w/click (.locator toast ".ui__toast-close")))))
(k/esc))
(is (nil? (ls-api-call! :editor.getProperty "[[bad")))
(is (nil? (ls-api-call! :editor.getProperty "#bad")))))
(deftest scalar-property-value-validation-test
(testing "number/date reject incompatible text and URL values preserve valid edits"
(let [page-name (current-page-name)
block (ls-api-call! :editor.appendBlockInPage page-name "scalar property owner")
uuid (get block "uuid")
journal (ls-api-call! :editor.createJournalPage
"2026-07-28T12:00:00")
journal-id (get journal "id")]
(doseq [[property-name property-type valid-value invalid-value]
[["sample-number" "number" -12.5 "not-a-number"]
["sample-date" "date" journal-id "not-a-date"]]]
(ls-api-call! :editor.upsertProperty property-name {:type property-type})
(ls-api-call! :editor.upsertBlockProperty uuid property-name valid-value)
(is (some? (block-value uuid property-name)))
(let [property-row
(loc/filter (format "#ls-block-%s .bottom-property-pill" uuid)
:has-text property-name)]
(w/click
(.locator property-row ".bottom-property-content .jtrigger")))
(k/press "ControlOrMeta+a")
(util/press-seq invalid-value)
(k/enter)
(is (not= invalid-value (block-value uuid property-name))))
(let [property-name "sample-url"
initial-value "https://logseq.com"
updated-value "https://docs.logseq.com"]
(ls-api-call! :editor.upsertProperty property-name {:type "url"})
(ls-api-call! :editor.upsertBlockProperty uuid property-name initial-value)
(is (= initial-value (block-value uuid property-name)))
(let [property-row
(loc/filter (format "#ls-block-%s .property-pair" uuid)
:has-text property-name)]
(w/click
(.locator property-row ".property-block-container.jtrigger")))
(util/input updated-value)
(k/enter)
(is (= updated-value (block-value uuid property-name))))
(util/refresh-until-graph-loaded)
(assert/assert-is-visible (format "#ls-block-%s" uuid)))))
(deftest property-type-cardinality-and-checkbox-choice-test
(testing "schema controls expose cardinality and allow unused properties to change type"
(let [property-name "sample-schema-property"
checkbox-property-name "sample-checkbox-choice"
page-name (current-page-name)
block (ls-api-call! :editor.appendBlockInPage page-name "schema property owner")
uuid (get block "uuid")]
(ls-api-call! :editor.upsertProperty property-name
{:type "default" :cardinality "many"})
(ls-api-call! :editor.upsertBlockProperty uuid property-name ["one" "two"])
(is (= 2 (count (block-value uuid property-name))))
(page/goto-page property-name)
(assert/assert-is-visible
(loc/filter ".ls-table-header-cell" :has-text property-name))
(w/click
(.first
(loc/filter ".ls-table-header-cell" :has-text property-name)))
(assert/assert-is-visible
(loc/filter ".ls-property-dropdown" :has-text "Multiple values"))
(assert/assert-is-visible
(loc/filter ".ls-property-dropdown" :has-text "Property type"))
(k/esc)
(ls-api-call! :editor.upsertProperty
checkbox-property-name
{:type "default"})
(page/goto-page checkbox-property-name)
(assert/assert-is-visible
(loc/filter ".ls-table-header-cell" :has-text checkbox-property-name))
(w/click
(.first
(loc/filter ".ls-table-header-cell" :has-text checkbox-property-name)))
(w/click
(loc/filter "[role='menuitem']" :has-text "Property type"))
(w/click
(util/get-by-text "Checkbox" true))
(is (= "checkbox"
(wait-for-property-type checkbox-property-name "checkbox"))))))
(deftest property-default-description-position-and-hidden-state-test
(testing "property presentation/default settings apply to new objects and hidden values"
(let [property-name "sample-configured-property"
tag-name "sample configured class"
property (ls-api-call! :editor.upsertProperty
property-name
{:type "default"})
property-uuid (get property "uuid")]
(ls-api-call! :editor.upsertBlockProperty
property-uuid
"logseq.property/default-value"
"sample default")
(ls-api-call! :editor.upsertBlockProperty
property-uuid
"logseq.property/description"
"sample description")
(ls-api-call! :editor.createTag
tag-name
{:tagProperties [{:name property-name}]})
(page/new-page "configured object host")
(b/new-block "configured object")
(util/set-tag tag-name)
(util/exit-edit)
(assert/assert-is-visible
(loc/filter ".property-pair" :has-text "sample default"))
(is (= "sample description"
(block-value property-uuid
"logseq.property/description")))
(ls-api-call! :editor.upsertBlockProperty
property-uuid
"logseq.property/hide-empty-value"
true)
(assert/assert-have-count
(loc/filter ".property-pair" :has-text property-name)
0)
(k/press "p")
(k/press "a")
(assert/assert-is-visible
(loc/filter ".property-pair" :has-text "sample default")))))
(deftest property-delete-and-bidirectional-refresh-test
(testing "bidirectional values refresh both sides and deleting the definition removes usages"
(let [property-name "sample bidirectional"
owner-tag "SampleOwner"
container-page "bidirectional refresh host"
owner-class (ls-api-call! :editor.createTag
owner-tag
{:tagProperties [{:name property-name
:schema {:type "node"}}]})
property (ls-api-call! :editor.getProperty property-name)]
(ls-api-call! :editor.upsertBlockProperty
(get property "id")
"logseq.property/classes"
(get owner-class "id"))
(ls-api-call! :editor.upsertBlockProperty
(get owner-class "uuid")
"logseq.property.class/enable-bidirectional?"
true)
(ls-api-call! :editor.createPage container-page)
(let [owner (ls-api-call! :editor.insertBlock
container-page
(str "sample owner object #" owner-tag))
target (ls-api-call! :editor.createPage "sample target object")]
(ls-api-call! :editor.upsertBlockProperty
(get owner "uuid") property-name (get target "id"))
(page/goto-page "sample target object")
(assert/assert-is-visible
(loc/filter ".ls-bidirectional-properties" :has-text "sample owner object"))
(ls-api-call! :editor.removeBlockProperty (get owner "uuid") property-name)
(assert/assert-have-count
(loc/filter ".ls-bidirectional-properties" :has-text "sample owner object")
0)
(ls-api-call! :editor.removeProperty property-name)
(is (nil? (ls-api-call! :editor.getProperty property-name)))))))
(deftest tag-inheritance-schema-and-object-view-test
(testing "class inheritance and schema mutations update object views without losing values"
(let [parent-tag-name "SampleParent"
child-tag-name "SampleChild"
inherited-property "sample-inherited-property"
child-property "sample-child-property"
parent-tag (ls-api-call! :editor.createTag
parent-tag-name
{:tagProperties [{:name inherited-property}]})
child-tag (ls-api-call! :editor.createTag
child-tag-name
{:tagProperties [{:name child-property}]})]
(ls-api-call! :editor.addTagExtends
(get child-tag "id")
(get parent-tag "id"))
(page/new-page "tag object host")
(let [object (ls-api-call! :editor.appendBlockInPage
(current-page-name)
(str "sample child object #" child-tag-name)
{:properties {inherited-property "kept"}})]
(page/goto-page parent-tag-name)
(assert/assert-is-visible
(loc/filter ".ls-view-body" :has-text "sample child object"))
(assert/assert-is-visible
(loc/filter ".ls-table-header-cell" :has-text inherited-property))
(ls-api-call! :editor.upsertBlockProperty
(get object "uuid") child-property "child value")
(page/goto-page child-tag-name)
(assert/assert-is-visible
(loc/filter ".ls-view-body" :has-text "child value"))
(is (= "kept"
(block-value (get object "uuid")
inherited-property)))))))
(deftest tag-template-dynamic-values-test
(testing "a tag template is applied once and resolves dynamic date references"
(let [tag-name "sample template class"
template-page "sample tag template"
template-root "sample template root"]
(page/new-page template-page)
(let [template (ls-api-call! :editor.appendBlockInPage
template-page
template-root)
template-uuid (get template "uuid")
template-tag (ls-api-call! :editor.getTag "Template")
tag (ls-api-call! :editor.createTag tag-name)]
(ls-api-call! :editor.upsertBlockProperty
template-uuid
"block/tags"
(get template-tag "id"))
(ls-api-call! :editor.insertBatchBlock
template-uuid
[{:content "template static child"}
{:content "template date <% today %>"}]
{:sibling false})
(ls-api-call! :editor.upsertBlockProperty
template-uuid
"logseq.property/template-applied-to"
(get tag "id"))
(page/new-page "sample templated object")
(let [object (ls-api-call! :editor.appendBlockInPage
(current-page-name)
"templated object")]
(ls-api-call! :editor.upsertBlockProperty
(get object "uuid")
"block/tags"
(get tag "id")))
(assert/assert-have-count
(loc/filter ".ls-page-blocks" :has-text "template static child")
1)
(assert/assert-is-visible ".page-reference")
(util/refresh-until-graph-loaded)
(assert/assert-have-count
(loc/filter ".ls-page-blocks" :has-text "template static child")
1)))))
(deftest checkbox-property-toggle-persists-test
(testing "checkbox property toggles immediately and survives refresh"
(let [property-name "e2e checkbox"
page-name (page/get-page-name)]
(ls-api-call! :editor.upsertProperty property-name {:type "checkbox"})
(let [block (ls-api-call! :editor.appendBlockInPage
page-name
"checkbox owner"
{:properties {property-name false}})
uuid (get block "uuid")
checkbox (w/-query
(format "#ls-block-%s .bottom-property-pill" uuid))]
(w/click (.locator checkbox "button[role='checkbox']"))
(assert/assert-is-visible
(.locator checkbox "button[role='checkbox'][data-checked]"))
(is (true? (block-value uuid property-name)))
(util/refresh-until-graph-loaded)
(assert/assert-is-visible
(format "#ls-block-%s [role='checkbox'][data-checked]" uuid))))))