Explorar o código

feat: add a secret global flag to load graph progammtically (#3531)

* feat: add a secret global flag to load graph from a user defined dir

* fix: use LOGSEQ_OVERWRITE_OPEN_DIR env var to overwrite the openning graph dir

* e2e: add a test for load & check saved graph data

* fix: should also pass in process.env for testing fixtures

* feat: new way to set open dir folder

* fix: e2e

* fix: remove playwright global flag check

Co-authored-by: Tienson Qin <[email protected]>
Peng Xiao %!s(int64=3) %!d(string=hai) anos
pai
achega
17b5cdb9e8

+ 1 - 0
.gitignore

@@ -13,6 +13,7 @@ pom.xml.asc
 
 node_modules/
 static/
+tmp
 
 .cpcache/
 /src/gen

+ 19 - 2
e2e-tests/basic.spec.ts

@@ -1,5 +1,7 @@
 import { expect } from '@playwright/test'
-import { test } from './fixtures'
+import fs from 'fs/promises'
+import path from 'path'
+import { test, graphDir } from './fixtures'
 import { randomString, createRandomPage, newBlock } from './utils'
 
 
@@ -41,7 +43,7 @@ test('search', async ({ page }) => {
 })
 
 test('create page and blocks', async ({ page }) => {
-  await createRandomPage(page)
+  const pageTitle = await createRandomPage(page)
 
   // do editing
   await page.fill(':nth-match(textarea, 1)', 'this is my first bullet')
@@ -81,6 +83,21 @@ test('create page and blocks', async ({ page }) => {
   await page.keyboard.press('Escape')
   await page.waitForTimeout(500)
   expect(await page.$$('.ls-block')).toHaveLength(5)
+
+  await page.waitForTimeout(500)
+
+  const contentOnDisk = await fs.readFile(
+    path.join(graphDir, `pages/${pageTitle}.md`),
+    'utf8'
+  )
+
+  expect(contentOnDisk.trim()).toEqual(`
+- this is my first bullet
+- this is my second bullet
+	- this is my third bullet
+	- continue editing test
+	  continue
+- test ok`.trim())
 })
 
 test('delete and backspace', async ({ page }) => {

+ 12 - 0
e2e-tests/fixtures.ts

@@ -1,9 +1,14 @@
+import fs from 'fs'
+import path from 'path'
 import { test as base, expect, ConsoleMessage } from '@playwright/test';
 import { ElectronApplication, Page, BrowserContext, _electron as electron } from 'playwright'
+import { loadLocalGraph, randomString } from './utils';
 
 let electronApp: ElectronApplication
 let context: BrowserContext
 let page: Page
+let repoName = randomString(10)
+export let graphDir = path.resolve(__dirname, '../tmp/e2e-graph', repoName)
 
 // NOTE: This is a console log watcher for error logs.
 const consoleLogWatcher = (msg: ConsoleMessage) => {
@@ -19,9 +24,14 @@ base.beforeAll(async () => {
     return
   }
 
+  fs.mkdirSync(graphDir, {
+    recursive: true,
+  });
+
   electronApp = await electron.launch({
     cwd: "./static",
     args: ["electron.js"],
+    locale: 'en',
   })
   context = electronApp.context()
   await context.tracing.start({ screenshots: true, snapshots: true });
@@ -51,6 +61,8 @@ base.beforeAll(async () => {
     console.log('Page loaded!')
     await page.screenshot({ path: 'startup.png' })
   })
+
+  await loadLocalGraph(page, graphDir);
 })
 
 base.beforeEach(async () => {

+ 44 - 0
e2e-tests/utils.ts

@@ -33,6 +33,8 @@ export async function createRandomPage(page: Page) {
     await page.click('text=/.*New page: ".*/')
     // wait for textarea of first block
     await page.waitForSelector(':nth-match(textarea, 1)', { state: 'visible' })
+
+    return randomTitle;
 }
 
 /**
@@ -101,6 +103,48 @@ export async function escapeToBlockEditor(page: Page): Promise<void> {
     await page.waitForTimeout(500)
 }
 
+export async function setMockedOpenDirPath(
+  page: Page,
+  path?: string
+): Promise<void> {
+  // set next open directory
+  await page.evaluate(
+    ([path]) => {
+      Object.assign(window, {
+        __MOCKED_OPEN_DIR_PATH__: path,
+      })
+    },
+    [path]
+  )
+}
+
+export async function loadLocalGraph(page: Page, path?: string): Promise<void> {
+  const hasOpenButton = await page.$('#head >> .button >> text=Open')
+  await setMockedOpenDirPath(page, path);
+  if (hasOpenButton) {
+    await page.click('#head >> .button >> text=Open')
+  } else {
+    if (!(await page.$('.ls-left-sidebar-open'))) {
+      await page.click('.cp__header-left-menu.button')
+    }
+    await page.click('#left-sidebar >> #repo-switch')
+    await page.click('text=Add new graph')
+    await page.waitForSelector('h1:has-text("Open a local directory")')
+    await page.click('h1:has-text("Open a local directory")')
+  }
+
+  setMockedOpenDirPath(page, ''); // reset it
+
+  await page.waitForSelector(':has-text("Parsing files")', {
+    state: 'hidden',
+    timeout: 1000 * 60 * 5,
+  })
+
+  await page.waitForFunction('window.document.title != "Loading"')
+
+  console.log('Graph loaded for ' + path)
+}
+
 export async function activateNewPage(page: Page) {
     await page.click('.ls-block >> nth=0')
     await page.waitForTimeout(500)

+ 3 - 2
resources/js/preload.js

@@ -83,13 +83,14 @@ contextBridge.exposeInMainWorld('apis', {
    *
    * @param {string} html html file with embedded state
    */
-  exportPublishAssets (html, customCSSPath, repoPath, assetFilenames) {
+  exportPublishAssets (html, customCSSPath, repoPath, assetFilenames, outputDir) {
     ipcRenderer.invoke(
       'export-publish-assets',
       html,
       customCSSPath,
       repoPath,
-      assetFilenames
+      assetFilenames,
+      outputDir
     )
   },
 

+ 2 - 4
src/electron/electron/core.cljs

@@ -68,12 +68,10 @@
      (.unregisterProtocol protocol LSP_SCHEME)
      (.unregisterProtocol protocol "assets")))
 
-(defn- handle-export-publish-assets [_event html custom-css-path repo-path asset-filenames]
+(defn- handle-export-publish-assets [_event html custom-css-path repo-path asset-filenames output-path]
   (p/let [app-path (. app getAppPath)
           asset-filenames (js->clj asset-filenames)
-          result (. dialog showOpenDialog (clj->js {:properties ["openDirectory" "createDirectory" "promptToCreate", "multiSelections"]}))
-          result (get (js->clj result) "filePaths")
-          root-dir (first result)]
+          root-dir (or output-path (handler/open-dir-dialog))]
     (when root-dir
       (let [static-dir (path/join root-dir "static")
             assets-from-dir (path/join repo-path "assets")

+ 7 - 7
src/electron/electron/handler.cljs

@@ -139,11 +139,14 @@
                  (remove nil?))]
     (vec (cons {:path (utils/fix-win-path! path)} result))))
 
-(defmethod handle :openDir [^js window _messages]
+(defn open-dir-dialog []
   (p/let [result (.showOpenDialog dialog (bean/->js
                                           {:properties ["openDirectory" "createDirectory" "promptToCreate"]}))
-          result (get (js->clj result) "filePaths")
-          path (first result)]
+          result (get (js->clj result) "filePaths")]
+    (p/resolved (first result))))
+
+(defmethod handle :openDir [^js window _messages]
+  (p/let [path (open-dir-dialog)]
     (if path
       (p/resolved (bean/->js (get-files path)))
       (p/rejected (js/Error "path empty")))))
@@ -269,10 +272,7 @@
     (watcher/watch-dir! window dir)))
 
 (defmethod handle :openDialog [^js window messages]
-  (p/let [result (.showOpenDialog dialog (bean/->js
-                                          {:properties ["openDirectory"]}))
-          result (get (js->clj result) "filePaths")]
-    (p/resolved (first result))))
+  (open-dir-dialog))
 
 (defmethod handle :getLogseqDotDirRoot []
   (utils/get-ls-dotdir-root))

+ 8 - 1
src/main/frontend/fs/node.cljs

@@ -92,6 +92,13 @@
                       (error-handler error)
                       (log/error :write-file-failed error)))))))))
 
+(defn- open-dir []
+  (p/let [dir-path (util/mocked-open-dir-path)
+          result (if dir-path
+                   (ipc/ipc "getFiles" dir-path)
+                   (ipc/ipc "openDir" {}))]
+    result))
+
 (defrecord Node []
   protocol/Fs
   (mkdir! [this dir]
@@ -124,7 +131,7 @@
     (let [path (concat-path dir path)]
       (ipc/ipc "stat" path)))
   (open-dir [this ok-handler]
-    (ipc/ipc "openDir" {}))
+    (open-dir))
   (get-files [this path-or-handle ok-handler]
     (ipc/ipc "getFiles" path-or-handle))
   (watch-dir! [this dir]

+ 6 - 1
src/main/frontend/handler/export.cljs

@@ -106,7 +106,12 @@
           html-str     (str "data:text/html;charset=UTF-8,"
                             (js/encodeURIComponent raw-html-str))]
       (if (util/electron?)
-        (js/window.apis.exportPublishAssets raw-html-str (config/get-custom-css-path) (config/get-repo-dir repo) (clj->js asset-filenames))
+        (js/window.apis.exportPublishAssets
+         raw-html-str
+         (config/get-custom-css-path)
+         (config/get-repo-dir repo)
+         (clj->js asset-filenames)
+         (util/mocked-open-dir-path))
 
         (when-let [anchor (gdom/getElement "download-as-html")]
           (.setAttribute anchor "href" html-str)

+ 6 - 0
src/main/frontend/util.cljc

@@ -71,6 +71,12 @@
        (let [ua (string/lower-case js/navigator.userAgent)]
          (string/includes? ua " electron")))))
 
+#?(:cljs
+   (defn mocked-open-dir-path
+     "Mocked open DIR path for by-passing open dir in electron during testing. Nil if not given"
+     []
+     (when (electron?) (. js/window -__MOCKED_OPEN_DIR_PATH__))))
+
 #?(:cljs
    (def nfs? (and (not (electron?))
                   (not (is-native-platform?)))))