obsidian-e2e 0.3.1 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/vitest.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { _ as VaultSeedEntry, a as acquireVaultRunLock, c as readVaultRunLockMarker, d as ObsidianFixtures, f as ObsidianTest, g as VaultSeed, h as SharedVaultLockOptions, i as VaultRunLockState, l as CreateObsidianTestOptions, m as PluginTest, n as VaultRunLock, o as clearVaultRunLockMarker, p as PluginFixtures, r as VaultRunLockMetadata, s as inspectVaultRunLock, t as AcquireVaultRunLockOptions, u as CreatePluginTestOptions } from "./vault-lock-DarzOEzv.mjs";
1
+ import { a as acquireVaultRunLock, b as VaultSeedEntry, c as readVaultRunLockMarker, d as ObsidianFixtures, f as ObsidianTest, g as SharedVaultLockOptions, h as PluginTest, i as VaultRunLockState, l as CreateObsidianTestOptions, n as VaultRunLock, o as clearVaultRunLockMarker, p as PluginFixtures, r as VaultRunLockMetadata, s as inspectVaultRunLock, t as AcquireVaultRunLockOptions, u as CreatePluginTestOptions, y as VaultSeed } from "./vault-lock-CYyOdRP1.mjs";
2
2
 
3
3
  //#region src/fixtures/create-obsidian-test.d.ts
4
4
  declare function createObsidianTest(options: CreateObsidianTestOptions): ObsidianTest;
package/dist/vitest.mjs CHANGED
@@ -1,157 +1,6 @@
1
- import { a as inspectVaultRunLock, c as getClientInternals, i as clearVaultRunLockMarker, n as createVaultApi, o as readVaultRunLockMarker, r as acquireVaultRunLock, s as createObsidianClient, t as createSandboxApi } from "./sandbox-Cz3rj_Rn.mjs";
2
- import { mkdir, writeFile } from "node:fs/promises";
3
- import path from "node:path";
1
+ import { a as clearVaultRunLockMarker, d as createVaultApi, f as resolveFilesystemPath, i as acquireVaultRunLock, m as getClientInternals, o as inspectVaultRunLock, r as createBaseFixtures, s as readVaultRunLockMarker } from "./test-context-BprSx6U1.mjs";
2
+ import { t as createNoteDocument } from "./document-DunL2Moz.mjs";
4
3
  import { test } from "vite-plus/test";
5
- //#region src/fixtures/failure-artifacts.ts
6
- const DEFAULT_ARTIFACTS_DIR = ".obsidian-e2e-artifacts";
7
- function getFailureArtifactConfig(options) {
8
- if (!options.captureOnFailure) return {
9
- artifactsDir: path.resolve(options.artifactsDir ?? DEFAULT_ARTIFACTS_DIR),
10
- capture: {
11
- activeFile: true,
12
- dom: true,
13
- editorText: true,
14
- screenshot: true,
15
- tabs: true,
16
- workspace: true
17
- },
18
- enabled: false
19
- };
20
- const overrides = options.captureOnFailure === true ? {} : options.captureOnFailure;
21
- return {
22
- artifactsDir: path.resolve(options.artifactsDir ?? DEFAULT_ARTIFACTS_DIR),
23
- capture: {
24
- activeFile: overrides.activeFile ?? true,
25
- dom: overrides.dom ?? true,
26
- editorText: overrides.editorText ?? true,
27
- screenshot: overrides.screenshot ?? true,
28
- tabs: overrides.tabs ?? true,
29
- workspace: overrides.workspace ?? true
30
- },
31
- enabled: true
32
- };
33
- }
34
- function getFailureArtifactDirectory(artifactsDir, task) {
35
- const suffix = task.id.split("_").at(-1) ?? "test";
36
- return path.join(artifactsDir, `${sanitizeForPath(task.name)}-${suffix}`);
37
- }
38
- function registerFailureArtifacts(context, obsidian, options) {
39
- const config = getFailureArtifactConfig(options);
40
- if (!config.enabled) return;
41
- context.onTestFailed(async () => {
42
- const artifactDirectory = getFailureArtifactDirectory(config.artifactsDir, context.task);
43
- await mkdir(artifactDirectory, { recursive: true });
44
- await Promise.all([
45
- captureJsonArtifact(artifactDirectory, "active-file.json", config.capture.activeFile, async () => ({ activeFile: await obsidian.dev.eval("app.workspace.getActiveFile()?.path ?? null") })),
46
- captureTextArtifact(artifactDirectory, "dom.txt", config.capture.dom, async () => String(await obsidian.dev.dom({
47
- inner: true,
48
- selector: ".workspace"
49
- }))),
50
- captureJsonArtifact(artifactDirectory, "editor.json", config.capture.editorText, async () => ({ text: await obsidian.dev.eval("app.workspace.activeLeaf?.view?.editor?.getValue?.() ?? null") })),
51
- captureScreenshotArtifact(artifactDirectory, config.capture.screenshot, obsidian),
52
- captureJsonArtifact(artifactDirectory, "tabs.json", config.capture.tabs, () => obsidian.tabs()),
53
- captureJsonArtifact(artifactDirectory, "workspace.json", config.capture.workspace, () => obsidian.workspace())
54
- ]);
55
- });
56
- }
57
- function registerPluginFailureArtifacts(context, plugin, options) {
58
- const config = getFailureArtifactConfig(options);
59
- if (!config.enabled) return;
60
- context.onTestFailed(async () => {
61
- const artifactDirectory = getFailureArtifactDirectory(config.artifactsDir, context.task);
62
- await mkdir(artifactDirectory, { recursive: true });
63
- await captureJsonArtifact(artifactDirectory, `${plugin.id}-data.json`, true, () => plugin.data().read());
64
- });
65
- }
66
- async function captureJsonArtifact(artifactDirectory, filename, enabled, readValue) {
67
- if (!enabled) return;
68
- try {
69
- const value = await readValue();
70
- await writeFile(path.join(artifactDirectory, filename), `${JSON.stringify(value, null, 2)}\n`, "utf8");
71
- } catch (error) {
72
- await writeFile(path.join(artifactDirectory, `${filename}.error.txt`), formatArtifactError(error), "utf8");
73
- }
74
- }
75
- async function captureScreenshotArtifact(artifactDirectory, enabled, obsidian) {
76
- if (!enabled) return;
77
- const screenshotPath = path.join(artifactDirectory, "screenshot.png");
78
- try {
79
- await obsidian.dev.screenshot(screenshotPath);
80
- } catch (error) {
81
- await writeFile(path.join(artifactDirectory, "screenshot.error.txt"), formatArtifactError(error), "utf8");
82
- }
83
- }
84
- async function captureTextArtifact(artifactDirectory, filename, enabled, readValue) {
85
- if (!enabled) return;
86
- try {
87
- await writeFile(path.join(artifactDirectory, filename), await readValue(), "utf8");
88
- } catch (error) {
89
- await writeFile(path.join(artifactDirectory, `${filename}.error.txt`), formatArtifactError(error), "utf8");
90
- }
91
- }
92
- function formatArtifactError(error) {
93
- return error instanceof Error ? `${error.name}: ${error.message}\n` : `${String(error)}\n`;
94
- }
95
- function sanitizeForPath(value) {
96
- return value.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 60) || "test";
97
- }
98
- function createBaseFixtures(options, fixtureOptions = {}) {
99
- const createVault = fixtureOptions.createVault ?? ((obsidian) => createVaultApi({ obsidian }));
100
- return {
101
- _vaultLock: [async ({}, use) => {
102
- if (!options.sharedVaultLock) {
103
- await use(null);
104
- return;
105
- }
106
- const lockClient = createObsidianClient(options);
107
- await lockClient.verify();
108
- const vaultLock = await acquireVaultRunLock({
109
- ...options.sharedVaultLock === true ? {} : options.sharedVaultLock,
110
- vaultName: options.vault,
111
- vaultPath: await lockClient.vaultPath()
112
- });
113
- await vaultLock.publishMarker(lockClient);
114
- try {
115
- await use(vaultLock);
116
- } finally {
117
- try {
118
- await clearVaultRunLockMarker(lockClient);
119
- } catch {}
120
- await vaultLock.release();
121
- }
122
- }, { scope: "worker" }],
123
- obsidian: async ({ _vaultLock, onTestFailed, task }, use) => {
124
- const obsidian = createObsidianClient(options);
125
- await obsidian.verify();
126
- if (_vaultLock) await _vaultLock.publishMarker(obsidian);
127
- registerFailureArtifacts({
128
- onTestFailed,
129
- task
130
- }, obsidian, options);
131
- try {
132
- await use(obsidian);
133
- } finally {
134
- await getClientInternals(obsidian).restoreAll();
135
- }
136
- },
137
- sandbox: async ({ obsidian }, use) => {
138
- const sandbox = await createSandboxApi({
139
- obsidian,
140
- sandboxRoot: options.sandboxRoot ?? "__obsidian_e2e__",
141
- testName: "test"
142
- });
143
- try {
144
- await use(sandbox);
145
- } finally {
146
- await sandbox.cleanup();
147
- }
148
- },
149
- vault: async ({ obsidian }, use) => {
150
- await use(await createVault(obsidian));
151
- }
152
- };
153
- }
154
- //#endregion
155
4
  //#region src/fixtures/create-obsidian-test.ts
156
5
  function createObsidianTest(options) {
157
6
  return test.extend(createBaseFixtures(options));
@@ -164,41 +13,33 @@ function createPluginTest(options) {
164
13
  if (options.seedVault) await applyVaultSeed(obsidian, options.seedVault);
165
14
  return createVaultApi({ obsidian });
166
15
  } }),
167
- plugin: async ({ obsidian, onTestFailed, task }, use) => {
168
- const plugin = obsidian.plugin(options.pluginId);
169
- const wasEnabled = await plugin.isEnabled();
170
- if (!wasEnabled) await plugin.enable({ filter: options.pluginFilter });
171
- if (options.seedPluginData !== void 0) await plugin.data().write(options.seedPluginData);
172
- registerPluginFailureArtifacts({
173
- onTestFailed,
174
- task
175
- }, plugin, options);
176
- try {
177
- await use(plugin);
178
- } finally {
179
- if (!wasEnabled) await plugin.disable({ filter: options.pluginFilter });
180
- }
16
+ plugin: async ({ _testContext }, use) => {
17
+ await use(await _testContext.plugin(options.pluginId, {
18
+ filter: options.pluginFilter,
19
+ seedData: options.seedPluginData
20
+ }));
181
21
  }
182
22
  };
183
23
  return test.extend(fixtures);
184
24
  }
185
25
  async function applyVaultSeed(obsidian, seedVault) {
186
- const vaultRoot = await obsidian.vaultPath();
26
+ const vault = createVaultApi({ obsidian });
187
27
  for (const [targetPath, value] of Object.entries(seedVault)) {
188
- const resolvedPath = path.resolve(vaultRoot, ...targetPath.split("/").filter(Boolean));
189
- const normalizedVaultRoot = path.resolve(vaultRoot);
190
- if (resolvedPath !== normalizedVaultRoot && !resolvedPath.startsWith(`${normalizedVaultRoot}${path.sep}`)) throw new Error(`Seed path escapes the vault root: ${targetPath}`);
28
+ const resolvedPath = await resolveFilesystemPath(obsidian, "", targetPath);
191
29
  await getClientInternals(obsidian).snapshotFileOnce(resolvedPath);
192
- await mkdir(path.dirname(resolvedPath), { recursive: true });
193
- await writeSeedValue(resolvedPath, value);
30
+ await writeSeedValue(vault, targetPath, value);
194
31
  }
195
32
  }
196
- async function writeSeedValue(resolvedPath, value) {
33
+ async function writeSeedValue(vault, targetPath, value) {
197
34
  if (typeof value === "string") {
198
- await writeFile(resolvedPath, value, "utf8");
35
+ await vault.write(targetPath, value, { waitForContent: true });
36
+ return;
37
+ }
38
+ if ("note" in value) {
39
+ await vault.write(targetPath, createNoteDocument(value.note).raw, { waitForContent: true });
199
40
  return;
200
41
  }
201
- await writeFile(resolvedPath, `${JSON.stringify(value.json, null, 2)}\n`, "utf8");
42
+ await vault.write(targetPath, `${JSON.stringify(value.json, null, 2)}\n`, { waitForContent: true });
202
43
  }
203
44
  //#endregion
204
45
  export { acquireVaultRunLock, clearVaultRunLockMarker, createObsidianTest, createPluginTest, inspectVaultRunLock, readVaultRunLockMarker };
@@ -1 +1 @@
1
- {"version":3,"file":"vitest.mjs","names":["base","base"],"sources":["../src/fixtures/failure-artifacts.ts","../src/fixtures/base-fixtures.ts","../src/fixtures/create-obsidian-test.ts","../src/fixtures/create-plugin-test.ts"],"sourcesContent":["import { mkdir, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\n\nimport type { TestContext } from \"vite-plus/test\";\n\nimport type { ObsidianClient, PluginHandle } from \"../core/types\";\nimport type { CreateObsidianTestOptions, FailureArtifactOptions } from \"./types\";\n\nconst DEFAULT_ARTIFACTS_DIR = \".obsidian-e2e-artifacts\";\n\ninterface FailureArtifactConfig {\n artifactsDir: string;\n capture: Required<FailureArtifactOptions>;\n enabled: boolean;\n}\n\nexport function getFailureArtifactConfig(\n options: Pick<CreateObsidianTestOptions, \"artifactsDir\" | \"captureOnFailure\">,\n): FailureArtifactConfig {\n if (!options.captureOnFailure) {\n return {\n artifactsDir: path.resolve(options.artifactsDir ?? DEFAULT_ARTIFACTS_DIR),\n capture: {\n activeFile: true,\n dom: true,\n editorText: true,\n screenshot: true,\n tabs: true,\n workspace: true,\n },\n enabled: false,\n };\n }\n\n const overrides = options.captureOnFailure === true ? {} : options.captureOnFailure;\n\n return {\n artifactsDir: path.resolve(options.artifactsDir ?? DEFAULT_ARTIFACTS_DIR),\n capture: {\n activeFile: overrides.activeFile ?? true,\n dom: overrides.dom ?? true,\n editorText: overrides.editorText ?? true,\n screenshot: overrides.screenshot ?? true,\n tabs: overrides.tabs ?? true,\n workspace: overrides.workspace ?? true,\n },\n enabled: true,\n };\n}\n\nexport function getFailureArtifactDirectory(\n artifactsDir: string,\n task: Pick<TestContext[\"task\"], \"id\" | \"name\">,\n): string {\n const suffix = task.id.split(\"_\").at(-1) ?? \"test\";\n return path.join(artifactsDir, `${sanitizeForPath(task.name)}-${suffix}`);\n}\n\nexport function registerFailureArtifacts(\n context: Pick<TestContext, \"onTestFailed\" | \"task\">,\n obsidian: ObsidianClient,\n options: Pick<CreateObsidianTestOptions, \"artifactsDir\" | \"captureOnFailure\">,\n): void {\n const config = getFailureArtifactConfig(options);\n\n if (!config.enabled) {\n return;\n }\n\n context.onTestFailed(async () => {\n const artifactDirectory = getFailureArtifactDirectory(config.artifactsDir, context.task);\n await mkdir(artifactDirectory, { recursive: true });\n\n await Promise.all([\n captureJsonArtifact(\n artifactDirectory,\n \"active-file.json\",\n config.capture.activeFile,\n async () => ({\n activeFile: await obsidian.dev.eval<string | null>(\n \"app.workspace.getActiveFile()?.path ?? null\",\n ),\n }),\n ),\n captureTextArtifact(artifactDirectory, \"dom.txt\", config.capture.dom, async () =>\n String(\n await obsidian.dev.dom({\n inner: true,\n selector: \".workspace\",\n }),\n ),\n ),\n captureJsonArtifact(\n artifactDirectory,\n \"editor.json\",\n config.capture.editorText,\n async () => ({\n text: await obsidian.dev.eval<string | null>(\n \"app.workspace.activeLeaf?.view?.editor?.getValue?.() ?? null\",\n ),\n }),\n ),\n captureScreenshotArtifact(artifactDirectory, config.capture.screenshot, obsidian),\n captureJsonArtifact(artifactDirectory, \"tabs.json\", config.capture.tabs, () =>\n obsidian.tabs(),\n ),\n captureJsonArtifact(artifactDirectory, \"workspace.json\", config.capture.workspace, () =>\n obsidian.workspace(),\n ),\n ]);\n });\n}\n\nexport function registerPluginFailureArtifacts(\n context: Pick<TestContext, \"onTestFailed\" | \"task\">,\n plugin: PluginHandle,\n options: Pick<CreateObsidianTestOptions, \"artifactsDir\" | \"captureOnFailure\">,\n): void {\n const config = getFailureArtifactConfig(options);\n\n if (!config.enabled) {\n return;\n }\n\n context.onTestFailed(async () => {\n const artifactDirectory = getFailureArtifactDirectory(config.artifactsDir, context.task);\n await mkdir(artifactDirectory, { recursive: true });\n await captureJsonArtifact(artifactDirectory, `${plugin.id}-data.json`, true, () =>\n plugin.data().read(),\n );\n });\n}\n\nasync function captureJsonArtifact(\n artifactDirectory: string,\n filename: string,\n enabled: boolean,\n readValue: () => Promise<unknown>,\n): Promise<void> {\n if (!enabled) {\n return;\n }\n\n try {\n const value = await readValue();\n await writeFile(\n path.join(artifactDirectory, filename),\n `${JSON.stringify(value, null, 2)}\\n`,\n \"utf8\",\n );\n } catch (error) {\n await writeFile(\n path.join(artifactDirectory, `${filename}.error.txt`),\n formatArtifactError(error),\n \"utf8\",\n );\n }\n}\n\nasync function captureScreenshotArtifact(\n artifactDirectory: string,\n enabled: boolean,\n obsidian: ObsidianClient,\n): Promise<void> {\n if (!enabled) {\n return;\n }\n\n const screenshotPath = path.join(artifactDirectory, \"screenshot.png\");\n\n try {\n await obsidian.dev.screenshot(screenshotPath);\n } catch (error) {\n await writeFile(\n path.join(artifactDirectory, \"screenshot.error.txt\"),\n formatArtifactError(error),\n \"utf8\",\n );\n }\n}\n\nasync function captureTextArtifact(\n artifactDirectory: string,\n filename: string,\n enabled: boolean,\n readValue: () => Promise<string>,\n): Promise<void> {\n if (!enabled) {\n return;\n }\n\n try {\n await writeFile(path.join(artifactDirectory, filename), await readValue(), \"utf8\");\n } catch (error) {\n await writeFile(\n path.join(artifactDirectory, `${filename}.error.txt`),\n formatArtifactError(error),\n \"utf8\",\n );\n }\n}\n\nfunction formatArtifactError(error: unknown): string {\n return error instanceof Error ? `${error.name}: ${error.message}\\n` : `${String(error)}\\n`;\n}\n\nfunction sanitizeForPath(value: string): string {\n return (\n value\n .trim()\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\")\n .slice(0, 60) || \"test\"\n );\n}\n","import type { TestContext } from \"vite-plus/test\";\n\nimport { createObsidianClient } from \"../core/client\";\nimport { getClientInternals } from \"../core/internals\";\nimport type { ObsidianClient, VaultApi } from \"../core/types\";\nimport { createSandboxApi } from \"../vault/sandbox\";\nimport { createVaultApi } from \"../vault/vault\";\nimport { registerFailureArtifacts } from \"./failure-artifacts\";\nimport type { CreateObsidianTestOptions } from \"./types\";\nimport { acquireVaultRunLock, clearVaultRunLockMarker, type VaultRunLock } from \"./vault-lock\";\n\nexport const DEFAULT_SANDBOX_ROOT = \"__obsidian_e2e__\";\n\nexport interface BaseFixtureState {\n _vaultLock: VaultRunLock | null;\n}\n\ninterface BaseFixtureOptions {\n createVault?: (obsidian: ObsidianClient) => Promise<VaultApi> | VaultApi;\n}\n\nexport function createBaseFixtures(\n options: CreateObsidianTestOptions,\n fixtureOptions: BaseFixtureOptions = {},\n) {\n const createVault =\n fixtureOptions.createVault ?? ((obsidian: ObsidianClient) => createVaultApi({ obsidian }));\n\n return {\n _vaultLock: [\n // eslint-disable-next-line no-empty-pattern\n async ({}, use: (vaultLock: VaultRunLock | null) => Promise<void>) => {\n if (!options.sharedVaultLock) {\n await use(null);\n return;\n }\n\n const lockClient = createObsidianClient(options);\n await lockClient.verify();\n\n const lockOptions = options.sharedVaultLock === true ? {} : options.sharedVaultLock;\n const vaultLock = await acquireVaultRunLock({\n ...lockOptions,\n vaultName: options.vault,\n vaultPath: await lockClient.vaultPath(),\n });\n\n await vaultLock.publishMarker(lockClient);\n try {\n await use(vaultLock);\n } finally {\n try {\n await clearVaultRunLockMarker(lockClient);\n } catch {}\n\n await vaultLock.release();\n }\n },\n { scope: \"worker\" },\n ],\n // oxlint-disable-next-line no-empty-pattern\n obsidian: async (\n {\n _vaultLock,\n onTestFailed,\n task,\n }: Pick<BaseFixtureState & TestContext, \"_vaultLock\" | \"onTestFailed\" | \"task\">,\n use: (obsidian: ObsidianClient) => Promise<void>,\n ) => {\n const obsidian = createObsidianClient(options);\n\n await obsidian.verify();\n if (_vaultLock) {\n await _vaultLock.publishMarker(obsidian);\n }\n registerFailureArtifacts({ onTestFailed, task }, obsidian, options);\n\n try {\n await use(obsidian);\n } finally {\n await getClientInternals(obsidian).restoreAll();\n }\n },\n sandbox: async (\n { obsidian }: { obsidian: ObsidianClient },\n use: (sandbox: Awaited<ReturnType<typeof createSandboxApi>>) => Promise<void>,\n ) => {\n const sandbox = await createSandboxApi({\n obsidian,\n sandboxRoot: options.sandboxRoot ?? DEFAULT_SANDBOX_ROOT,\n testName: \"test\",\n });\n\n try {\n await use(sandbox);\n } finally {\n await sandbox.cleanup();\n }\n },\n vault: async (\n { obsidian }: { obsidian: ObsidianClient },\n use: (vault: VaultApi) => Promise<void>,\n ) => {\n await use(await createVault(obsidian));\n },\n };\n}\n","import { test as base } from \"vite-plus/test\";\n\nimport { createBaseFixtures, type BaseFixtureState } from \"./base-fixtures\";\nimport type { CreateObsidianTestOptions, ObsidianFixtures, ObsidianTest } from \"./types\";\n\nexport function createObsidianTest(options: CreateObsidianTestOptions): ObsidianTest {\n return base.extend<ObsidianFixtures & BaseFixtureState>(\n createBaseFixtures(options) as never,\n ) as ObsidianTest;\n}\n","import { mkdir, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\n\nimport { test as base } from \"vite-plus/test\";\nimport type { TestContext } from \"vite-plus/test\";\n\nimport { getClientInternals } from \"../core/internals\";\nimport type { ObsidianClient } from \"../core/types\";\nimport { createVaultApi } from \"../vault/vault\";\nimport { createBaseFixtures, type BaseFixtureState } from \"./base-fixtures\";\nimport { registerPluginFailureArtifacts } from \"./failure-artifacts\";\nimport type {\n CreatePluginTestOptions,\n PluginFixtures,\n PluginTest,\n VaultSeed,\n VaultSeedEntry,\n} from \"./types\";\n\nexport function createPluginTest(options: CreatePluginTestOptions): PluginTest {\n const fixtures = {\n ...createBaseFixtures(options, {\n async createVault(obsidian) {\n if (options.seedVault) {\n await applyVaultSeed(obsidian, options.seedVault);\n }\n\n return createVaultApi({ obsidian });\n },\n }),\n plugin: async (\n {\n obsidian,\n onTestFailed,\n task,\n }: Pick<PluginFixtures & TestContext, \"obsidian\" | \"onTestFailed\" | \"task\">,\n use: (plugin: PluginFixtures[\"plugin\"]) => Promise<void>,\n ) => {\n const plugin = obsidian.plugin(options.pluginId);\n const wasEnabled = await plugin.isEnabled();\n\n if (!wasEnabled) {\n await plugin.enable({ filter: options.pluginFilter });\n }\n\n if (options.seedPluginData !== undefined) {\n await plugin.data().write(options.seedPluginData);\n }\n\n registerPluginFailureArtifacts({ onTestFailed, task }, plugin, options);\n\n try {\n await use(plugin);\n } finally {\n if (!wasEnabled) {\n await plugin.disable({ filter: options.pluginFilter });\n }\n }\n },\n };\n\n return base.extend<PluginFixtures & BaseFixtureState>(fixtures as never) as PluginTest;\n}\n\nasync function applyVaultSeed(obsidian: ObsidianClient, seedVault: VaultSeed): Promise<void> {\n const vaultRoot = await obsidian.vaultPath();\n\n for (const [targetPath, value] of Object.entries(seedVault)) {\n const resolvedPath = path.resolve(vaultRoot, ...targetPath.split(\"/\").filter(Boolean));\n const normalizedVaultRoot = path.resolve(vaultRoot);\n\n if (\n resolvedPath !== normalizedVaultRoot &&\n !resolvedPath.startsWith(`${normalizedVaultRoot}${path.sep}`)\n ) {\n throw new Error(`Seed path escapes the vault root: ${targetPath}`);\n }\n\n await getClientInternals(obsidian).snapshotFileOnce(resolvedPath);\n await mkdir(path.dirname(resolvedPath), { recursive: true });\n await writeSeedValue(resolvedPath, value);\n }\n}\n\nasync function writeSeedValue(resolvedPath: string, value: VaultSeedEntry): Promise<void> {\n if (typeof value === \"string\") {\n await writeFile(resolvedPath, value, \"utf8\");\n return;\n }\n\n await writeFile(resolvedPath, `${JSON.stringify(value.json, null, 2)}\\n`, \"utf8\");\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"vitest.mjs","names":["base","base"],"sources":["../src/fixtures/create-obsidian-test.ts","../src/fixtures/create-plugin-test.ts"],"sourcesContent":["import { test as base } from \"vite-plus/test\";\n\nimport { createBaseFixtures, type BaseFixtureState } from \"./base-fixtures\";\nimport type { CreateObsidianTestOptions, ObsidianFixtures, ObsidianTest } from \"./types\";\n\nexport function createObsidianTest(options: CreateObsidianTestOptions): ObsidianTest {\n return base.extend<ObsidianFixtures & BaseFixtureState>(\n createBaseFixtures(options) as never,\n ) as ObsidianTest;\n}\n","import { test as base } from \"vite-plus/test\";\n\nimport { getClientInternals } from \"../core/internals\";\nimport type { ObsidianClient, VaultApi } from \"../core/types\";\nimport { createNoteDocument } from \"../note/document\";\nimport { createVaultApi } from \"../vault/vault\";\nimport { resolveFilesystemPath } from \"../vault/paths\";\nimport { createBaseFixtures, type BaseFixtureState } from \"./base-fixtures\";\nimport type {\n CreatePluginTestOptions,\n PluginFixtures,\n PluginTest,\n VaultSeed,\n VaultSeedEntry,\n} from \"./types\";\n\nexport function createPluginTest(options: CreatePluginTestOptions): PluginTest {\n const fixtures = {\n ...createBaseFixtures(options, {\n async createVault(obsidian) {\n if (options.seedVault) {\n await applyVaultSeed(obsidian, options.seedVault);\n }\n\n return createVaultApi({ obsidian });\n },\n }),\n plugin: async (\n { _testContext }: Pick<BaseFixtureState, \"_testContext\">,\n use: (plugin: PluginFixtures[\"plugin\"]) => Promise<void>,\n ) => {\n await use(\n await _testContext.plugin(options.pluginId, {\n filter: options.pluginFilter,\n seedData: options.seedPluginData,\n }),\n );\n },\n };\n\n return base.extend<PluginFixtures & BaseFixtureState>(fixtures as never) as PluginTest;\n}\n\nasync function applyVaultSeed(obsidian: ObsidianClient, seedVault: VaultSeed): Promise<void> {\n const vault = createVaultApi({ obsidian });\n\n for (const [targetPath, value] of Object.entries(seedVault)) {\n const resolvedPath = await resolveFilesystemPath(obsidian, \"\", targetPath);\n await getClientInternals(obsidian).snapshotFileOnce(resolvedPath);\n await writeSeedValue(vault, targetPath, value);\n }\n}\n\nasync function writeSeedValue(\n vault: VaultApi,\n targetPath: string,\n value: VaultSeedEntry,\n): Promise<void> {\n if (typeof value === \"string\") {\n await vault.write(targetPath, value, {\n waitForContent: true,\n });\n return;\n }\n\n if (\"note\" in value) {\n await vault.write(targetPath, createNoteDocument(value.note).raw, {\n waitForContent: true,\n });\n return;\n }\n\n await vault.write(targetPath, `${JSON.stringify(value.json, null, 2)}\\n`, {\n waitForContent: true,\n });\n}\n"],"mappings":";;;;AAKA,SAAgB,mBAAmB,SAAkD;AACnF,QAAOA,KAAK,OACV,mBAAmB,QAAQ,CAC5B;;;;ACQH,SAAgB,iBAAiB,SAA8C;CAC7E,MAAM,WAAW;EACf,GAAG,mBAAmB,SAAS,EAC7B,MAAM,YAAY,UAAU;AAC1B,OAAI,QAAQ,UACV,OAAM,eAAe,UAAU,QAAQ,UAAU;AAGnD,UAAO,eAAe,EAAE,UAAU,CAAC;KAEtC,CAAC;EACF,QAAQ,OACN,EAAE,gBACF,QACG;AACH,SAAM,IACJ,MAAM,aAAa,OAAO,QAAQ,UAAU;IAC1C,QAAQ,QAAQ;IAChB,UAAU,QAAQ;IACnB,CAAC,CACH;;EAEJ;AAED,QAAOC,KAAK,OAA0C,SAAkB;;AAG1E,eAAe,eAAe,UAA0B,WAAqC;CAC3F,MAAM,QAAQ,eAAe,EAAE,UAAU,CAAC;AAE1C,MAAK,MAAM,CAAC,YAAY,UAAU,OAAO,QAAQ,UAAU,EAAE;EAC3D,MAAM,eAAe,MAAM,sBAAsB,UAAU,IAAI,WAAW;AAC1E,QAAM,mBAAmB,SAAS,CAAC,iBAAiB,aAAa;AACjE,QAAM,eAAe,OAAO,YAAY,MAAM;;;AAIlD,eAAe,eACb,OACA,YACA,OACe;AACf,KAAI,OAAO,UAAU,UAAU;AAC7B,QAAM,MAAM,MAAM,YAAY,OAAO,EACnC,gBAAgB,MACjB,CAAC;AACF;;AAGF,KAAI,UAAU,OAAO;AACnB,QAAM,MAAM,MAAM,YAAY,mBAAmB,MAAM,KAAK,CAAC,KAAK,EAChE,gBAAgB,MACjB,CAAC;AACF;;AAGF,OAAM,MAAM,MAAM,YAAY,GAAG,KAAK,UAAU,MAAM,MAAM,MAAM,EAAE,CAAC,KAAK,EACxE,gBAAgB,MACjB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "obsidian-e2e",
3
- "version": "0.3.1",
3
+ "version": "0.5.0",
4
4
  "description": "Vitest-first end-to-end test utilities for Obsidian plugins.",
5
5
  "homepage": "https://github.com/chhoumann/obsidian-e2e#readme",
6
6
  "bugs": {
@@ -35,6 +35,9 @@
35
35
  },
36
36
  "./package.json": "./package.json"
37
37
  },
38
+ "dependencies": {
39
+ "yaml": "^2.8.2"
40
+ },
38
41
  "devDependencies": {
39
42
  "@changesets/cli": "^2.30.0",
40
43
  "@types/node": "^25.3.5",