You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
docs: check CDP E2E scripts into scripts/e2e/ (#74)
The CDP-based end-to-end workflow documented in #73 referenced helper
scripts that only existed under /tmp/ on the author's machine. Anyone
trying to reproduce the workflow had no actual scripts to start from.
Move the working scripts into the repo and rewrite AGENTS.md to point
at them by relative path:
- scripts/e2e/cdp.sh: the Runtime.evaluate helper (websocat + python3,
PORT env var, exits non-zero on CDP exception or HTTP/WS failure).
- scripts/e2e/cdp-e2e-all.js: multi-store upload smoke test.
- scripts/e2e/cdp-mermaid-e2e.js: mermaid round-trip; STORE_ID
parameterised at the top of the file.
- scripts/e2e/cdp-e2e-supplemental.js: 13-case suite; STORE_ID and
BUCKET_HOST_RE parameterised at the top of the file so the assertions
no longer hardcode 'atbug.oss-...'.
- scripts/e2e/README.md: full workflow, footguns, canonical ID table,
cleanup expectations.
AGENTS.md drops the embedded cdp.sh source, the /tmp/cdp-*.js
references, the duplicated canonical-ID table, and the 'reload plugin
via CDP' code block - all of which now live in scripts/e2e/README.md.
Instead it points readers at scripts/e2e/ with a short summary of the
workflow and footguns worth knowing without opening the README.
eslint.config.mjs adds scripts/** to globalIgnores so the helper
scripts don't trip type-aware lint rules.
Copy file name to clipboardExpand all lines: AGENTS.md
+13-85Lines changed: 13 additions & 85 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -201,99 +201,27 @@ Current test files:
201
201
202
202
### End-to-End Testing via Chrome DevTools Protocol
203
203
204
-
Unit tests can't catch Electron-specific runtime issues (e.g. Chromium rejecting an explicit `Host` header in `requestUrl`, which broke COS in 1.6.2 release candidate). We drive a **real Obsidian instance** over CDP to exercise the full publish flow against live cloud credentials.
204
+
Unit tests can't catch Electron-specific runtime issues (e.g. Chromium rejecting an explicit `Host` header in `requestUrl`, which broke COS in the 1.6.2 release candidate). The repo ships a small suite of CDP-driven scripts under [`scripts/e2e/`](scripts/e2e/) that drive a **real Obsidian instance** to exercise the full publish flow against live cloud credentials.
205
205
206
-
#### Setup
206
+
See [`scripts/e2e/README.md`](scripts/e2e/README.md) for the full workflow. Quick summary:
207
207
208
-
1. Launch Obsidian with the CDP endpoint enabled:
208
+
1. Launch Obsidian with `--remote-debugging-port=9223` (e.g. `open -a Obsidian --args --remote-debugging-port=9223`).
209
+
2. Install the built plugin into a test vault that has live credentials for the providers you want to exercise. macOS TCC blocks `~/Documents` for non-Obsidian processes; place the test vault under `~/iCloud Drive/` or another accessible location.
210
+
3. Run a script via the helper:
209
211
```bash
210
-
open -a Obsidian --args --remote-debugging-port=9223
2. Install the built plugin into a test vault that has live credentials for the providers you want to exercise:
213
-
```bash
214
-
npm run build
215
-
cp main.js manifest.json styles.css \
216
-
"$VAULT/.obsidian/plugins/image-upload-toolkit/"
217
-
```
218
-
Note: macOS TCC blocks `~/Documents` for non-Obsidian processes; place the test vault under `~/iCloud Drive/` or another accessible location.
219
-
3. Enable the plugin in the vault (or hot-reload it via CDP — see "Reload Plugin via CDP" below).
220
-
221
-
#### The `cdp.sh` Helper
222
-
223
-
Requires `jq` and `websocat` on `$PATH` (`brew install jq websocat`). A minimal shell wrapper that posts a single `Runtime.evaluate` call to the active page and prints the JSON result. The pattern is:
Anything that evaluates to a JSON-serializable value comes back in `result.result.value`. Wrap multi-statement scripts in an async IIFE so `awaitPromise: true` can wait on the result.
238
-
239
-
#### What You Have Access To Inside CDP
240
-
241
-
The evaluated expression runs in the Obsidian renderer, so the full app is in scope:
-`app.plugins.plugins["image-upload-toolkit"]` — live plugin instance
245
-
-`.settings` — current settings object (mutating it persists nothing until `saveData`)
246
-
-`.setupImageUploader()` — sync; rebuild the uploader after settings mutation
247
-
-`app.commands.executeCommandById("image-upload-toolkit:publish-page")` — invokes the publish command (it's `checkCallback`-based, so don't call `.callback` directly)
248
-
-`navigator.clipboard.readText()` — read what publish wrote to the clipboard
249
-
-`activeDocument.querySelector(".modal.upload-progress-modal")` — assert the progress modal opened
250
-
-`app.workspace.activeLeaf.view.editor.getValue()` — read the current editor buffer (do NOT use `app.vault.read(file)` after `editor.setValue()`; the latter doesn't auto-persist to disk)
251
-
252
-
Avoid `require("obsidian")` and dynamic `import()` of the bundle from inside CDP — `obsidian` is an esbuild external and resolves to nothing at runtime in this context. Use the plugin instance for everything you need from the API.
These scripts are not checked into the repo — they're working scratch under `/tmp/` during a release cycle. Treat the entries below as patterns to copy when you need to reproduce or extend coverage.
269
-
270
-
-**Multi-store upload smoke test** (`/tmp/cdp-e2e-all.js`): iterates over `["ALIYUN_OSS", "AWS_S3", "Imagekit", "TENCENTCLOUD_COS", "IMGUR"]`, mutates `settings.imageStore`, calls `setupImageUploader()`, writes a sentinel PNG to the vault, runs the publish command, reads the clipboard, asserts the URL host matches the provider.
271
-
-**Mermaid round-trip** (`/tmp/cdp-mermaid-e2e.js`): creates a note with a `mermaid` fence, runs publish, asserts the clipboard contains an uploaded image URL and the original document still contains the source fence.
When mutating settings programmatically, use the exact casing from `src/imageStore.ts`:
215
+
The helper (`scripts/e2e/cdp.sh`) requires `websocat` and `python3` on `PATH`. The expression runs in the Obsidian renderer with full access to `app`, `app.plugins.plugins["image-upload-toolkit"]`, `app.vault`, `app.commands.executeCommandById(...)`, `navigator.clipboard`, and `activeDocument`.
Footguns worth knowing without opening the README:
290
218
291
-
#### Cleanup
219
+
-`require("obsidian")` and dynamic `import()` of the bundle don't work inside CDP — `obsidian` is an esbuild external and resolves to nothing at runtime in this context. Use the plugin instance.
220
+
-`editor.setValue()` does NOT persist to disk. When asserting on `replaceOriginalDoc`, read `leaf.view.editor.getValue()`, not `app.vault.read(file)`.
221
+
- Commands are `checkCallback`-based — use `app.commands.executeCommandById(...)`, not `.callback()`.
222
+
- The progress modal opens asynchronously; poll `activeDocument.querySelector(".modal.upload-progress-modal")` for a few hundred ms.
292
223
293
-
The publish flow uploads to **real buckets**. Test scripts should:
294
-
- Prefix test filenames (e.g. `__iut-<timestamp>.png`) so they're easy to identify and prune from the bucket.
295
-
- Snapshot `plugin.settings` before mutating and restore it after the run.
296
-
- Delete any temp notes/images created in the vault via `app.vault.delete(file)`.
224
+
The scripts upload to **real buckets**. They prefix sentinel filenames (`iut-*`, `__iut-*`), snapshot/restore `plugin.settings`, and delete temp notes via `app.vault.delete(file)`.
These scripts drive a real Obsidian instance over the [Chrome DevTools Protocol](https://chromedevtools.github.io/devtools-protocol/) to validate the publish flow against live cloud credentials. Use them when:
4
+
5
+
- You changed an uploader and want to confirm it still talks to the real bucket.
6
+
- You're prepping a release and want to verify the user-visible publish flow end to end (UI modal, clipboard output, editor rewrite, mermaid conversion, etc).
7
+
- You suspect an Electron-specific runtime issue that unit tests can't catch (e.g. `requestUrl` header validation).
8
+
9
+
These are intentionally **not** wired into CI — they hit real buckets and require manual credentials.
10
+
11
+
## Prerequisites
12
+
13
+
- A test vault with the plugin installed and live credentials configured for whatever stores you want to exercise.
14
+
- Obsidian launched with the CDP endpoint enabled:
15
+
```bash
16
+
open -a Obsidian --args --remote-debugging-port=9223
17
+
```
18
+
-`websocat` and `python3` on `PATH`:
19
+
```bash
20
+
brew install websocat
21
+
```
22
+
23
+
## The helper
24
+
25
+
[`cdp.sh`](cdp.sh) wraps a single `Runtime.evaluate` call to the active page. Pass any JavaScript expression as the first argument; the expression runs in the Obsidian renderer with full access to `app`, `app.plugins`, `app.vault`, `navigator.clipboard`, `activeDocument`, etc.
Multi-statement scripts must be wrapped in an async IIFE that returns a JSON-serializable value, because `awaitPromise: true` is passed and the result is fetched via `returnByValue: true`.
-**`require("obsidian")` and dynamic `import()` of the bundle don't work** inside the CDP expression — `obsidian` is an esbuild external and resolves to nothing at runtime in this context. Use the plugin instance (`app.plugins.plugins["image-upload-toolkit"]`) for everything you need from the API.
66
+
-**`editor.setValue()` does NOT persist to disk.** When asserting on `replaceOriginalDoc` behavior, read `leaf.view.editor.getValue()`, not `app.vault.read(file)`.
67
+
-**Commands are `checkCallback`-based.** Use `app.commands.executeCommandById("image-upload-toolkit:publish-page")`. Don't call `.callback` directly.
68
+
-**Progress modal is async.** Don't query it once — poll `activeDocument.querySelector(".modal.upload-progress-modal")` for a few hundred ms after triggering publish.
69
+
-**macOS TCC blocks `~/Documents`** for non-Obsidian processes. Place test vaults under `~/iCloud Drive/` or another accessible location.
70
+
71
+
## Cleanup expectations
72
+
73
+
The scripts upload to real buckets. They:
74
+
75
+
- Prefix all sentinel filenames with `iut-` or `__iut-` so they're easy to identify and prune.
76
+
- Snapshot `plugin.settings` before mutating and restore it after the run.
77
+
- Delete temp notes/images created in the vault via `app.vault.delete(file)`.
78
+
79
+
Residual sentinel files in your bucket are harmless but accumulate over time — periodically prune them with whatever bucket-management tool you use.
80
+
81
+
## Canonical IDs and setting keys
82
+
83
+
When mutating settings programmatically, use the exact casing from [`src/imageStore.ts`](../../src/imageStore.ts):
0 commit comments