diff --git a/docs/dev-practices.md b/docs/dev-practices.md index 1be7496a6e..281f23fb51 100644 --- a/docs/dev-practices.md +++ b/docs/dev-practices.md @@ -139,7 +139,17 @@ By convention, a namespace's tests are found at a corresponding namespace of the same name with an added `-test` suffix. For example, tests for `frontend.db.model` are found in `frontend.db.model-test`. -There are a couple different ways to develop with tests: +There are a couple different ways to run tests: + +* [Focus tests](#focus-tests) - Run one or more tests from the CLI +* [Autorun tests](#autorun-tests) - Autorun tests from the CLI +* [Repl tests](#repl-tests) - Run tests from REPL + +There a couple types of tests and they can overlap with each other: + +* [Database tests](#database-tests) - Tests that involve a datascript DB. +* [Performance tests](#performance-tests) - Tests that aim to measure and enforce a performance characteristic. +* [Async tests](#async-tests) - Tests that run async code and require some helpers. #### Focus Tests @@ -166,6 +176,15 @@ To run tests automatically on file save, run `clojure -M:test watch test the `:ns-regexp` option e.g. `clojure -M:test watch test --config-merge '{:autorun true :ns-regexp "frontend.util.page-property-test"}'`. +#### REPL tests + +Most unit tests e.g. ones that are browser compatible and don't require node libraries, can be run from the REPL. To do so: + +* Start a REPL for your editor. See [here for an example](https://github.com/logseq/logseq/blob/master/docs/develop-logseq.md#repl-setup). +* Load a test namespace. +* Run `(cljs.test/run-tests)` to run tests for the current test namespace. + + #### Database tests To write a test that uses a datascript db: @@ -188,7 +207,7 @@ To write a performance test: For examples of these tests, see `frontend.db.query-dsl-test` and `frontend.db.model-test`. -### Async Unit Testing +#### Async Tests Async unit testing is well supported in ClojureScript. https://clojurescript.org/tools/testing#async-testing is a good guide for how to diff --git a/e2e-tests/whiteboards.spec.ts b/e2e-tests/whiteboards.spec.ts index c012363c11..36158ff680 100644 --- a/e2e-tests/whiteboards.spec.ts +++ b/e2e-tests/whiteboards.spec.ts @@ -159,6 +159,38 @@ test('undo the delete action', async ({ page }) => { await expect(page.locator('.logseq-tldraw .tl-line-container')).toHaveCount(1) }) +test('convert the first rectangle to ellipse', async ({ page }) => { + await page.keyboard.press('Escape') + await page.waitForTimeout(1000) + await page.click('.logseq-tldraw .tl-box-container:first-of-type') + await page.mouse.move(0, 0) // move mouse to trigger a rerender of the context bar + await page.click('.tl-context-bar .tl-geometry-tools-pane-anchor') + await page.click('.tl-context-bar .tl-geometry-toolbar [data-tool=ellipse]') + + await expect(page.locator('.logseq-tldraw .tl-ellipse-container')).toHaveCount(1) + await expect(page.locator('.logseq-tldraw .tl-box-container')).toHaveCount(1) +}) + +test('change the color of the ellipse', async ({ page }) => { + await page.click('.tl-context-bar .tl-color-bg') + await page.click('.tl-context-bar .tl-color-palette .bg-red-500') + + await expect(page.locator('.logseq-tldraw .tl-ellipse-container ellipse:last-of-type')).toHaveAttribute('fill', 'var(--ls-wb-background-color-red)') +}) + +test('undo the color switch', async ({ page }) => { + await page.keyboard.press(modKey + '+z') + + await expect(page.locator('.logseq-tldraw .tl-ellipse-container ellipse:last-of-type')).toHaveAttribute('fill', 'var(--ls-wb-background-color-default)') +}) + +test('undo the shape conversion', async ({ page }) => { + await page.keyboard.press(modKey + '+z') + + await expect(page.locator('.logseq-tldraw .tl-box-container')).toHaveCount(2) + await expect(page.locator('.logseq-tldraw .tl-ellipse-container')).toHaveCount(0) +}) + test('locked elements should not be removed', async ({ page }) => { await page.keyboard.press('Escape') await page.waitForTimeout(1000) diff --git a/src/main/frontend/components/settings.cljs b/src/main/frontend/components/settings.cljs index 294947e25f..fa22c986df 100644 --- a/src/main/frontend/components/settings.cljs +++ b/src/main/frontend/components/settings.cljs @@ -667,14 +667,20 @@ [] [:div.panel-wrap [:div.text-sm.my-4 + (ui/admonition + :tip + [:p "If you have Logseq Sync enabled, you can view a page's edit history directly. This section is for tech-savvy only."]) + [:span.text-sm.opacity-50.my-4 + "To view page's edit history, click the three horizontal dots in the top-right corner and select \"View page history\"."] + [:br][:br] [:span.text-sm.opacity-50.my-4 - "You can view a page's edit history by clicking the three horizontal dots " - "in the top-right corner and selecting \"View page history\". " - "Logseq uses "] + "For professional users, Logseq also supports using "] [:a {:href "https://git-scm.com/" :target "_blank"} "Git"] [:span.text-sm.opacity-50.my-4 - " for version control."]] + " for version control."] + [:span.text-sm.opacity-50.my-4 + "Use Git at your own risk as general Git issues are not supported by the Logseq team"]] [:br] (switch-git-auto-commit-row t) (git-auto-commit-seconds t) @@ -827,9 +833,7 @@ [[:general "general" (t :settings-page/tab-general) (ui/icon "adjustments")] [:editor "editor" (t :settings-page/tab-editor) (ui/icon "writing")] - (when (and - (util/electron?) - (not (file-sync-handler/synced-file-graph? current-repo))) + (when (util/electron?) [:git "git" (t :settings-page/tab-version-control) (ui/icon "history")]) ;; (when (util/electron?) diff --git a/src/main/frontend/extensions/tldraw.cljs b/src/main/frontend/extensions/tldraw.cljs index 3b6ae9df5e..ff96171fde 100644 --- a/src/main/frontend/extensions/tldraw.cljs +++ b/src/main/frontend/extensions/tldraw.cljs @@ -100,7 +100,7 @@ :getBlockPageName #(:block/name (model/get-block-page (state/get-current-repo) (parse-uuid %))) :exportToImage (fn [page-name options] (state/set-modal! #(export/export-blocks page-name (merge (js->clj options :keywordize-keys true) {:whiteboard? true})))) :isWhiteboardPage model/whiteboard-page? - :isMobile (util/mobile?) + :isMobile util/mobile? :saveAsset save-asset-handler :makeAssetUrl editor-handler/make-asset-url :copyToClipboard (fn [text, html] (util/copy-to-clipboard! text :html html)) diff --git a/tldraw/apps/tldraw-logseq/src/components/ContextBar/contextBarActionFactory.tsx b/tldraw/apps/tldraw-logseq/src/components/ContextBar/contextBarActionFactory.tsx index 5136393259..ce6f2197f1 100644 --- a/tldraw/apps/tldraw-logseq/src/components/ContextBar/contextBarActionFactory.tsx +++ b/tldraw/apps/tldraw-logseq/src/components/ContextBar/contextBarActionFactory.tsx @@ -133,9 +133,7 @@ const LogseqPortalViewModeAction = observer(() => {
{collapsed ? 'Expand' : 'Collapse'}
) @@ -253,7 +251,7 @@ const NoFillAction = observer(() => { const app = useApp() const shapes = filterShapeByAction('NoFill') const handleChange = React.useCallback((v: boolean) => { - shapes.forEach(s => s.update({ noFill: v })) + app.selectedShapesArray.forEach(s => s.update({ noFill: v })) app.persist() }, []) @@ -279,14 +277,14 @@ const SwatchAction = observer(() => { >('Swatch') const handleSetColor = React.useCallback((color: string) => { - shapes.forEach(s => { + app.selectedShapesArray.forEach(s => { s.update({ fill: color, stroke: color }) }) app.persist() }, []) const handleSetOpacity = React.useCallback((opacity: number) => { - shapes.forEach(s => { + app.selectedShapesArray.forEach(s => { s.update({ opacity: opacity }) }) app.persist() diff --git a/tldraw/apps/tldraw-logseq/src/lib/shapes/EllipseShape.tsx b/tldraw/apps/tldraw-logseq/src/lib/shapes/EllipseShape.tsx index 3fb4a3c512..9930befbf7 100644 --- a/tldraw/apps/tldraw-logseq/src/lib/shapes/EllipseShape.tsx +++ b/tldraw/apps/tldraw-logseq/src/lib/shapes/EllipseShape.tsx @@ -98,7 +98,11 @@ export class EllipseShape extends TLEllipseShape { ) return ( -
+
) => { const { selectedIds, - selectedShapes, inputs: { shiftKey }, } = this.app - if (info.type === TLTargetType.Shape && !selectedShapes.has(info.shape)) { + if (info.type === TLTargetType.Shape && !selectedIds.has(info.shape.id)) { const shape = this.app.getParentGroup(info.shape) ?? info.shape if (shiftKey) { this.app.setSelectedShapes([...Array.from(selectedIds.values()), shape.id])