@bubblydoo/vitest-pool-uxp 0.0.2
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/README.md +169 -0
- package/dist/index.d.ts +74 -0
- package/dist/index.js +64 -0
- package/dist/index.js.map +1 -0
- package/fake-plugin/index.html +10 -0
- package/fake-plugin/manifest.json +121 -0
- package/package.json +36 -0
package/README.md
ADDED
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
# @bubblydoo/vitest-pool-uxp
|
|
2
|
+
|
|
3
|
+
Vitest pool for running tests inside Adobe UXP environments (Photoshop, etc.).
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
This package provides a custom Vitest pool that executes tests inside Adobe Photoshop via the UXP (Unified Extensibility Platform) runtime. It wraps `@bubblydoo/vitest-pool-cdp` and `@bubblydoo/uxp-devtools-common` to handle the complexity of connecting to Photoshop's debugging interface.
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
pnpm add -D @bubblydoo/vitest-pool-uxp vitest
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Usage
|
|
16
|
+
|
|
17
|
+
Create a vitest config file (e.g., `vitest.uxp.config.ts`):
|
|
18
|
+
|
|
19
|
+
```typescript
|
|
20
|
+
import { defineConfig } from "vitest/config";
|
|
21
|
+
import { uxpPool } from "@bubblydoo/vitest-pool-uxp";
|
|
22
|
+
|
|
23
|
+
export default defineConfig({
|
|
24
|
+
test: {
|
|
25
|
+
include: ["src/**/*.uxp-test.ts"],
|
|
26
|
+
pool: uxpPool(),
|
|
27
|
+
// Required settings - see "Vulcan System Limitations" below
|
|
28
|
+
isolate: false,
|
|
29
|
+
fileParallelism: false,
|
|
30
|
+
maxWorkers: 1,
|
|
31
|
+
// Recommended settings
|
|
32
|
+
testTimeout: 30000,
|
|
33
|
+
watch: false,
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Run your tests:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
vitest run --config vitest.uxp.config.ts
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Configuration Options
|
|
45
|
+
|
|
46
|
+
| Option | Type | Default | Description |
|
|
47
|
+
|--------|------|---------|-------------|
|
|
48
|
+
| `pluginPath` | `string` | Built-in fake plugin | Path to a UXP plugin directory. The plugin will be loaded into Photoshop to establish the debugging connection. |
|
|
49
|
+
| `debug` | `boolean` | `false` | Enable debug logging |
|
|
50
|
+
| `connectionTimeout` | `number` | `60000` | Timeout in milliseconds for establishing the CDP connection |
|
|
51
|
+
| `rpcTimeout` | `number` | `10000` | Timeout in milliseconds for RPC calls |
|
|
52
|
+
|
|
53
|
+
### Using a Custom Plugin
|
|
54
|
+
|
|
55
|
+
By default, vitest-pool-uxp uses a built-in "Vitest UXP Test Runner" plugin. You can use your own plugin if needed:
|
|
56
|
+
|
|
57
|
+
```typescript
|
|
58
|
+
pool: uxpPool({
|
|
59
|
+
pluginPath: "./my-uxp-plugin",
|
|
60
|
+
debug: true,
|
|
61
|
+
}),
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Requirements
|
|
65
|
+
|
|
66
|
+
- **Photoshop must be running** before starting the tests
|
|
67
|
+
- **Adobe Creative Cloud** must be installed (provides the Vulcan communication system)
|
|
68
|
+
- Vitest v4 or later
|
|
69
|
+
|
|
70
|
+
## Vulcan System Limitations
|
|
71
|
+
|
|
72
|
+
Adobe's Vulcan system is the inter-process communication (IPC) mechanism that allows external tools to communicate with Adobe applications. Understanding its limitations is crucial for reliable test execution.
|
|
73
|
+
|
|
74
|
+
### Single Connection Per Process
|
|
75
|
+
|
|
76
|
+
**The Vulcan system only supports a single connection per Node.js process.** Attempting to create multiple connections will cause a native assertion failure:
|
|
77
|
+
|
|
78
|
+
```
|
|
79
|
+
Assertion failed: (vulcan_), function initSelf, file vulcanadapter.cc, line 504.
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
This happens because:
|
|
83
|
+
1. The `VulcanAdapter` native module maintains a singleton connection to the Vulcan service
|
|
84
|
+
2. When a second instance tries to initialize, it finds the global state already in use
|
|
85
|
+
3. The native code asserts and crashes the process
|
|
86
|
+
|
|
87
|
+
### Required Vitest Configuration
|
|
88
|
+
|
|
89
|
+
To prevent multiple Vulcan connections, you **must** configure vitest to run tests sequentially with a single worker:
|
|
90
|
+
|
|
91
|
+
```typescript
|
|
92
|
+
{
|
|
93
|
+
test: {
|
|
94
|
+
// Disable VM isolation - prevents vitest from creating
|
|
95
|
+
// separate V8 contexts that don't share module state
|
|
96
|
+
isolate: false,
|
|
97
|
+
|
|
98
|
+
// Run test files sequentially, not in parallel
|
|
99
|
+
fileParallelism: false,
|
|
100
|
+
|
|
101
|
+
// Use only one worker
|
|
102
|
+
maxWorkers: 1,
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
**Why `isolate: false` is critical:**
|
|
108
|
+
|
|
109
|
+
Without this setting, vitest creates isolated V8 contexts for each test file. Even though they run in the same Node.js process, module-level variables (including singleton state) are not shared between these contexts. This means each context tries to create its own Vulcan connection, causing the crash.
|
|
110
|
+
|
|
111
|
+
### Technical Details
|
|
112
|
+
|
|
113
|
+
The Vulcan system works through these components:
|
|
114
|
+
|
|
115
|
+
1. **VulcanControl.dylib** - Native library that interfaces with Adobe's Vulcan IPC
|
|
116
|
+
2. **node-napi.node** - Node.js native addon that wraps VulcanControl
|
|
117
|
+
3. **DevToolsHelper** - JavaScript wrapper that manages the connection
|
|
118
|
+
|
|
119
|
+
When `DevToolsHelper` is instantiated, it creates a `VulcanAdapter` which:
|
|
120
|
+
1. Calls `IVulcanMessageDispatcher::GetInstance()` to get the singleton dispatcher
|
|
121
|
+
2. Calls `SetConfig()` to configure the endpoint
|
|
122
|
+
3. Waits for the dispatcher to become ready (up to 100 retries with 50ms delays)
|
|
123
|
+
4. If the dispatcher is still null after setup, the native code asserts
|
|
124
|
+
|
|
125
|
+
The assertion at line 504 specifically checks that `vulcan_` (the dispatcher instance) is not null after initialization. Multiple concurrent initializations can race and corrupt this state.
|
|
126
|
+
|
|
127
|
+
### Debugging Connection Issues
|
|
128
|
+
|
|
129
|
+
If you encounter Vulcan-related errors:
|
|
130
|
+
|
|
131
|
+
1. **Kill any running Adobe UXP Developer Tools** - they also use Vulcan and can conflict
|
|
132
|
+
2. **Ensure Photoshop is running** - the Vulcan connection requires an active Adobe app
|
|
133
|
+
3. **Check for zombie processes** - previous test runs may have left Vulcan in a bad state
|
|
134
|
+
4. **Use `debug: true`** - enables logging to see connection progress
|
|
135
|
+
|
|
136
|
+
```typescript
|
|
137
|
+
pool: uxpPool({ debug: true })
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## How It Works
|
|
141
|
+
|
|
142
|
+
```
|
|
143
|
+
┌─────────────────────┐ ┌─────────────────────┐
|
|
144
|
+
│ Vitest (Node.js) │ │ Photoshop │
|
|
145
|
+
│ │ │ │
|
|
146
|
+
│ ┌───────────────┐ │ │ ┌───────────────┐ │
|
|
147
|
+
│ │ vitest-pool- │ │ │ │ UXP Plugin │ │
|
|
148
|
+
│ │ uxp │ │ │ │ (injected) │ │
|
|
149
|
+
│ └───────┬───────┘ │ │ └───────┬───────┘ │
|
|
150
|
+
│ │ │ │ │ │
|
|
151
|
+
│ ┌───────▼───────┐ │ Vulcan IPC │ │ │
|
|
152
|
+
│ │ DevTools │──┼────────────────────┼──────────┘ │
|
|
153
|
+
│ │ Helper │ │ │ │
|
|
154
|
+
│ └───────┬───────┘ │ │ │
|
|
155
|
+
│ │ │ │ │
|
|
156
|
+
│ ┌───────▼───────┐ │ CDP WebSocket │ ┌───────────────┐ │
|
|
157
|
+
│ │ vitest-pool- │──┼────────────────────┼──│ CDP Server │ │
|
|
158
|
+
│ │ cdp │ │ │ └───────────────┘ │
|
|
159
|
+
│ └───────────────┘ │ │ │
|
|
160
|
+
└─────────────────────┘ └─────────────────────┘
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
1. **Vulcan IPC** - Used to discover Photoshop and load the test plugin
|
|
164
|
+
2. **CDP (Chrome DevTools Protocol)** - Used to execute JavaScript in the UXP context
|
|
165
|
+
3. **Test execution** - Vitest tests run inside Photoshop's UXP runtime with full access to the Photoshop API
|
|
166
|
+
|
|
167
|
+
## License
|
|
168
|
+
|
|
169
|
+
MIT
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { PoolRunnerInitializer } from 'vitest/node';
|
|
2
|
+
export { PoolRunnerInitializer } from 'vitest/node';
|
|
3
|
+
import { cdpPool } from '@bubblydoo/vitest-pool-cdp';
|
|
4
|
+
|
|
5
|
+
type CdpPoolOptions = Parameters<typeof cdpPool>[0];
|
|
6
|
+
/**
|
|
7
|
+
* Get the path to the built-in fake plugin for vitest-pool-uxp.
|
|
8
|
+
* This plugin displays "Vitest UXP Test Runner" in the panel.
|
|
9
|
+
*/
|
|
10
|
+
declare function getDefaultPluginPath(): string;
|
|
11
|
+
/**
|
|
12
|
+
* Options for the UXP pool.
|
|
13
|
+
*/
|
|
14
|
+
interface UxpPoolOptions extends Omit<CdpPoolOptions, 'cdp'> {
|
|
15
|
+
/**
|
|
16
|
+
* Path to the UXP plugin directory.
|
|
17
|
+
* Defaults to the built-in "Vitest UXP Test Runner" plugin.
|
|
18
|
+
*/
|
|
19
|
+
pluginPath?: string;
|
|
20
|
+
/**
|
|
21
|
+
* This string gets passed as "UXP_MAIN_DIRECTORY" to the test file.
|
|
22
|
+
*
|
|
23
|
+
* @default process.cwd()
|
|
24
|
+
*/
|
|
25
|
+
mainDirectory?: string;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Create a Vitest pool that runs tests in Adobe UXP environments (Photoshop, etc.).
|
|
29
|
+
*
|
|
30
|
+
* This pool wraps `@bubblydoo/vitest-pool-cdp` and `@bubblydoo/uxp-devtools-common`
|
|
31
|
+
* to provide a simple way to run Vitest tests inside UXP.
|
|
32
|
+
*
|
|
33
|
+
* IMPORTANT: Adobe's Vulcan system only supports a single connection per process.
|
|
34
|
+
* You MUST use `isolate: false` and `fileParallelism: false` in your vitest config
|
|
35
|
+
* to prevent multiple workers from trying to initialize Vulcan simultaneously.
|
|
36
|
+
*
|
|
37
|
+
* @param options - Configuration options for the UXP pool
|
|
38
|
+
* @returns A PoolRunnerInitializer for Vitest
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* // vitest.config.ts
|
|
42
|
+
* import { defineConfig } from "vitest/config";
|
|
43
|
+
* import { uxpPool } from "@bubblydoo/vitest-pool-uxp";
|
|
44
|
+
*
|
|
45
|
+
* export default defineConfig({
|
|
46
|
+
* test: {
|
|
47
|
+
* include: ['src/**\/*.uxp-test.ts'],
|
|
48
|
+
* pool: uxpPool(),
|
|
49
|
+
* // Required: UXP/Vulcan only supports a single connection
|
|
50
|
+
* isolate: false,
|
|
51
|
+
* fileParallelism: false,
|
|
52
|
+
* testTimeout: 30000,
|
|
53
|
+
* watch: false,
|
|
54
|
+
* },
|
|
55
|
+
* });
|
|
56
|
+
*
|
|
57
|
+
* @example
|
|
58
|
+
* // With custom plugin
|
|
59
|
+
* import { uxpPool } from "@bubblydoo/vitest-pool-uxp";
|
|
60
|
+
*
|
|
61
|
+
* export default defineConfig({
|
|
62
|
+
* test: {
|
|
63
|
+
* pool: uxpPool({
|
|
64
|
+
* pluginPath: "./my-uxp-plugin",
|
|
65
|
+
* debug: true,
|
|
66
|
+
* }),
|
|
67
|
+
* isolate: false,
|
|
68
|
+
* fileParallelism: false,
|
|
69
|
+
* },
|
|
70
|
+
* });
|
|
71
|
+
*/
|
|
72
|
+
declare function uxpPool(options?: UxpPoolOptions): PoolRunnerInitializer;
|
|
73
|
+
|
|
74
|
+
export { type UxpPoolOptions, getDefaultPluginPath, getDefaultPluginPath as getFakePluginPath, uxpPool };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import process from 'process';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
import { waitForExecutionContextCreated, setupCdpSessionWithUxpDefaults, setupDevtoolsConnection } from '@bubblydoo/uxp-devtools-common';
|
|
5
|
+
import { cdpPool } from '@bubblydoo/vitest-pool-cdp';
|
|
6
|
+
|
|
7
|
+
// src/index.ts
|
|
8
|
+
var __dirname$1 = path.dirname(fileURLToPath(import.meta.url));
|
|
9
|
+
function getDefaultPluginPath() {
|
|
10
|
+
return path.resolve(__dirname$1, "../fake-plugin");
|
|
11
|
+
}
|
|
12
|
+
var cachedConnection = null;
|
|
13
|
+
function uxpPool(options = {}) {
|
|
14
|
+
const {
|
|
15
|
+
pluginPath = getDefaultPluginPath(),
|
|
16
|
+
debug = false,
|
|
17
|
+
mainDirectory = process.cwd(),
|
|
18
|
+
embedSourcemap = true
|
|
19
|
+
} = options;
|
|
20
|
+
const resolvedPluginPath = path.isAbsolute(pluginPath) ? pluginPath : path.resolve(process.cwd(), pluginPath);
|
|
21
|
+
const log = debug ? (...args) => console.log("[vitest-pool-uxp]", ...args) : () => {
|
|
22
|
+
};
|
|
23
|
+
return cdpPool({
|
|
24
|
+
cdp: async () => {
|
|
25
|
+
if (cachedConnection) {
|
|
26
|
+
log("Reusing existing UXP connection");
|
|
27
|
+
return cachedConnection;
|
|
28
|
+
}
|
|
29
|
+
log("Creating new UXP connection...");
|
|
30
|
+
const connection = await setupDevtoolsConnection(resolvedPluginPath);
|
|
31
|
+
log(`UXP connection established: ${connection.url}`);
|
|
32
|
+
const originalTeardown = connection.teardown;
|
|
33
|
+
cachedConnection = {
|
|
34
|
+
url: connection.url,
|
|
35
|
+
teardown: async () => {
|
|
36
|
+
log("Tearing down UXP connection and clearing cache...");
|
|
37
|
+
cachedConnection = null;
|
|
38
|
+
await originalTeardown();
|
|
39
|
+
log("UXP connection teardown complete");
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
return cachedConnection;
|
|
43
|
+
},
|
|
44
|
+
executionContextOrSession: async (cdp) => {
|
|
45
|
+
const desc = await waitForExecutionContextCreated(cdp, async () => {
|
|
46
|
+
await setupCdpSessionWithUxpDefaults(cdp);
|
|
47
|
+
});
|
|
48
|
+
return { uniqueId: desc.uniqueId };
|
|
49
|
+
},
|
|
50
|
+
embedSourcemap,
|
|
51
|
+
...options,
|
|
52
|
+
esbuildOptions: {
|
|
53
|
+
...options.esbuildOptions,
|
|
54
|
+
define: {
|
|
55
|
+
...options.esbuildOptions?.define,
|
|
56
|
+
UXP_MAIN_DIRECTORY: JSON.stringify(mainDirectory)
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export { getDefaultPluginPath, getDefaultPluginPath as getFakePluginPath, uxpPool };
|
|
63
|
+
//# sourceMappingURL=index.js.map
|
|
64
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"names":["__dirname"],"mappings":";;;;;;;AAcA,IAAMA,cAAY,IAAA,CAAK,OAAA,CAAQ,aAAA,CAAc,MAAA,CAAA,IAAA,CAAY,GAAG,CAAC,CAAA;AAMtD,SAAS,oBAAA,GAA+B;AAG7C,EAAA,OAAO,IAAA,CAAK,OAAA,CAAQA,WAAA,EAAW,gBAAgB,CAAA;AACjD;AAwBA,IAAI,gBAAA,GAA0E,IAAA;AA+CvE,SAAS,OAAA,CAAQ,OAAA,GAA0B,EAAC,EAA0B;AAC3E,EAAA,MAAM;AAAA,IACJ,aAAa,oBAAA,EAAqB;AAAA,IAClC,KAAA,GAAQ,KAAA;AAAA,IACR,aAAA,GAAgB,QAAQ,GAAA,EAAI;AAAA,IAC5B,cAAA,GAAiB;AAAA,GACnB,GAAI,OAAA;AAEJ,EAAA,MAAM,kBAAA,GAAqB,IAAA,CAAK,UAAA,CAAW,UAAU,CAAA,GACjD,UAAA,GACA,IAAA,CAAK,OAAA,CAAQ,OAAA,CAAQ,GAAA,EAAI,EAAG,UAAU,CAAA;AAE1C,EAAA,MAAM,GAAA,GAAM,KAAA,GACR,CAAA,GAAI,IAAA,KAAoB,OAAA,CAAQ,IAAI,mBAAA,EAAqB,GAAG,IAAI,CAAA,GAChE,MAAM;AAAA,EAAC,CAAA;AAEX,EAAA,OAAO,OAAA,CAAQ;AAAA,IACb,KAAK,YAAY;AAEf,MAAA,IAAI,gBAAA,EAAkB;AACpB,QAAA,GAAA,CAAI,iCAAiC,CAAA;AACrC,QAAA,OAAO,gBAAA;AAAA,MACT;AAEA,MAAA,GAAA,CAAI,gCAAgC,CAAA;AACpC,MAAA,MAAM,UAAA,GAAa,MAAM,uBAAA,CAAwB,kBAAkB,CAAA;AACnE,MAAA,GAAA,CAAI,CAAA,4BAAA,EAA+B,UAAA,CAAW,GAAG,CAAA,CAAE,CAAA;AAGnD,MAAA,MAAM,mBAAmB,UAAA,CAAW,QAAA;AACpC,MAAA,gBAAA,GAAmB;AAAA,QACjB,KAAK,UAAA,CAAW,GAAA;AAAA,QAChB,UAAU,YAAY;AACpB,UAAA,GAAA,CAAI,mDAAmD,CAAA;AACvD,UAAA,gBAAA,GAAmB,IAAA;AACnB,UAAA,MAAM,gBAAA,EAAiB;AACvB,UAAA,GAAA,CAAI,kCAAkC,CAAA;AAAA,QACxC;AAAA,OACF;AAEA,MAAA,OAAO,gBAAA;AAAA,IACT,CAAA;AAAA,IACA,yBAAA,EAA2B,OAAO,GAAA,KAAQ;AACxC,MAAA,MAAM,IAAA,GAAO,MAAM,8BAAA,CAA+B,GAAA,EAAK,YAAY;AACjE,QAAA,MAAM,+BAA+B,GAAG,CAAA;AAAA,MAC1C,CAAC,CAAA;AACD,MAAA,OAAO,EAAE,QAAA,EAAU,IAAA,CAAK,QAAA,EAAS;AAAA,IACnC,CAAA;AAAA,IACA,cAAA;AAAA,IACA,GAAG,OAAA;AAAA,IACH,cAAA,EAAgB;AAAA,MACd,GAAG,OAAA,CAAQ,cAAA;AAAA,MACX,MAAA,EAAQ;AAAA,QACN,GAAG,QAAQ,cAAA,EAAgB,MAAA;AAAA,QAC3B,kBAAA,EAAoB,IAAA,CAAK,SAAA,CAAU,aAAa;AAAA;AAClD;AACF,GACD,CAAA;AACH","file":"index.js","sourcesContent":["/* eslint-disable no-console */\nimport type { PoolRunnerInitializer } from 'vitest/node';\nimport path from 'node:path';\nimport process from 'node:process';\nimport { fileURLToPath } from 'node:url';\nimport {\n setupCdpSessionWithUxpDefaults,\n setupDevtoolsConnection,\n waitForExecutionContextCreated,\n} from '@bubblydoo/uxp-devtools-common';\nimport { cdpPool } from '@bubblydoo/vitest-pool-cdp';\n\ntype CdpPoolOptions = Parameters<typeof cdpPool>[0];\n\nconst __dirname = path.dirname(fileURLToPath(import.meta.url));\n\n/**\n * Get the path to the built-in fake plugin for vitest-pool-uxp.\n * This plugin displays \"Vitest UXP Test Runner\" in the panel.\n */\nexport function getDefaultPluginPath(): string {\n // In development: src/index.ts -> ../fake-plugin\n // In dist: dist/index.js -> ../fake-plugin\n return path.resolve(__dirname, '../fake-plugin');\n}\n\n/**\n * Options for the UXP pool.\n */\nexport interface UxpPoolOptions extends Omit<CdpPoolOptions, 'cdp'> {\n /**\n * Path to the UXP plugin directory.\n * Defaults to the built-in \"Vitest UXP Test Runner\" plugin.\n */\n pluginPath?: string;\n\n /**\n * This string gets passed as \"UXP_MAIN_DIRECTORY\" to the test file.\n *\n * @default process.cwd()\n */\n mainDirectory?: string;\n}\n\n// Cached connection - with isolate: false and fileParallelism: false,\n// there's only one worker so we just need simple caching.\n// The cache is cleared when teardown runs to ensure subsequent pool\n// initializations don't reuse a stale connection.\nlet cachedConnection: { url: string; teardown: () => Promise<void> } | null = null;\n\n/**\n * Create a Vitest pool that runs tests in Adobe UXP environments (Photoshop, etc.).\n *\n * This pool wraps `@bubblydoo/vitest-pool-cdp` and `@bubblydoo/uxp-devtools-common`\n * to provide a simple way to run Vitest tests inside UXP.\n *\n * IMPORTANT: Adobe's Vulcan system only supports a single connection per process.\n * You MUST use `isolate: false` and `fileParallelism: false` in your vitest config\n * to prevent multiple workers from trying to initialize Vulcan simultaneously.\n *\n * @param options - Configuration options for the UXP pool\n * @returns A PoolRunnerInitializer for Vitest\n *\n * @example\n * // vitest.config.ts\n * import { defineConfig } from \"vitest/config\";\n * import { uxpPool } from \"@bubblydoo/vitest-pool-uxp\";\n *\n * export default defineConfig({\n * test: {\n * include: ['src/**\\/*.uxp-test.ts'],\n * pool: uxpPool(),\n * // Required: UXP/Vulcan only supports a single connection\n * isolate: false,\n * fileParallelism: false,\n * testTimeout: 30000,\n * watch: false,\n * },\n * });\n *\n * @example\n * // With custom plugin\n * import { uxpPool } from \"@bubblydoo/vitest-pool-uxp\";\n *\n * export default defineConfig({\n * test: {\n * pool: uxpPool({\n * pluginPath: \"./my-uxp-plugin\",\n * debug: true,\n * }),\n * isolate: false,\n * fileParallelism: false,\n * },\n * });\n */\nexport function uxpPool(options: UxpPoolOptions = {}): PoolRunnerInitializer {\n const {\n pluginPath = getDefaultPluginPath(),\n debug = false,\n mainDirectory = process.cwd(),\n embedSourcemap = true,\n } = options;\n\n const resolvedPluginPath = path.isAbsolute(pluginPath)\n ? pluginPath\n : path.resolve(process.cwd(), pluginPath);\n\n const log = debug\n ? (...args: unknown[]) => console.log('[vitest-pool-uxp]', ...args)\n : () => {};\n\n return cdpPool({\n cdp: async () => {\n // Reuse existing connection if available\n if (cachedConnection) {\n log('Reusing existing UXP connection');\n return cachedConnection;\n }\n\n log('Creating new UXP connection...');\n const connection = await setupDevtoolsConnection(resolvedPluginPath);\n log(`UXP connection established: ${connection.url}`);\n\n // Wrap teardown to clear the cache when the connection is closed\n const originalTeardown = connection.teardown;\n cachedConnection = {\n url: connection.url,\n teardown: async () => {\n log('Tearing down UXP connection and clearing cache...');\n cachedConnection = null;\n await originalTeardown();\n log('UXP connection teardown complete');\n },\n };\n\n return cachedConnection;\n },\n executionContextOrSession: async (cdp) => {\n const desc = await waitForExecutionContextCreated(cdp, async () => {\n await setupCdpSessionWithUxpDefaults(cdp);\n });\n return { uniqueId: desc.uniqueId };\n },\n embedSourcemap,\n ...options,\n esbuildOptions: {\n ...options.esbuildOptions,\n define: {\n ...options.esbuildOptions?.define,\n UXP_MAIN_DIRECTORY: JSON.stringify(mainDirectory),\n },\n },\n });\n}\n\n// Re-export types and utilities that users might need\nexport type { PoolRunnerInitializer };\nexport { getDefaultPluginPath as getFakePluginPath };\n"]}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
</head>
|
|
7
|
+
<body style="color: white; display: flex; justify-content: center; align-items: center; height: 100vh">
|
|
8
|
+
Vitest UXP Test Runner
|
|
9
|
+
</body>
|
|
10
|
+
</html>
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "co.bubblydoo.vitest-uxp-test-runner",
|
|
3
|
+
"name": "Vitest UXP Test Runner",
|
|
4
|
+
"version": "0.0.1",
|
|
5
|
+
"main": "index.html",
|
|
6
|
+
"manifestVersion": 6,
|
|
7
|
+
"host": [
|
|
8
|
+
{
|
|
9
|
+
"app": "PS",
|
|
10
|
+
"minVersion": "24.2.0"
|
|
11
|
+
}
|
|
12
|
+
],
|
|
13
|
+
"entrypoints": [
|
|
14
|
+
{
|
|
15
|
+
"type": "panel",
|
|
16
|
+
"id": "co.bubblydoo.vitest-uxp-test-runner.main",
|
|
17
|
+
"label": {
|
|
18
|
+
"default": "Vitest UXP Test Runner"
|
|
19
|
+
},
|
|
20
|
+
"minimumSize": {
|
|
21
|
+
"width": 230,
|
|
22
|
+
"height": 200
|
|
23
|
+
},
|
|
24
|
+
"maximumSize": {
|
|
25
|
+
"width": 2000,
|
|
26
|
+
"height": 2000
|
|
27
|
+
},
|
|
28
|
+
"preferredDockedSize": {
|
|
29
|
+
"width": 230,
|
|
30
|
+
"height": 300
|
|
31
|
+
},
|
|
32
|
+
"preferredFloatingSize": {
|
|
33
|
+
"width": 450,
|
|
34
|
+
"height": 400
|
|
35
|
+
},
|
|
36
|
+
"icons": [
|
|
37
|
+
{
|
|
38
|
+
"width": 23,
|
|
39
|
+
"height": 23,
|
|
40
|
+
"path": "icons/dark.png",
|
|
41
|
+
"scale": [
|
|
42
|
+
1,
|
|
43
|
+
2
|
|
44
|
+
],
|
|
45
|
+
"theme": [
|
|
46
|
+
"darkest",
|
|
47
|
+
"dark",
|
|
48
|
+
"medium"
|
|
49
|
+
]
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
"width": 23,
|
|
53
|
+
"height": 23,
|
|
54
|
+
"path": "icons/light.png",
|
|
55
|
+
"scale": [
|
|
56
|
+
1,
|
|
57
|
+
2
|
|
58
|
+
],
|
|
59
|
+
"theme": [
|
|
60
|
+
"lightest",
|
|
61
|
+
"light"
|
|
62
|
+
]
|
|
63
|
+
}
|
|
64
|
+
]
|
|
65
|
+
}
|
|
66
|
+
],
|
|
67
|
+
"featureFlags": {
|
|
68
|
+
"enableAlerts": true
|
|
69
|
+
},
|
|
70
|
+
"requiredPermissions": {
|
|
71
|
+
"localFileSystem": "fullAccess",
|
|
72
|
+
"launchProcess": {
|
|
73
|
+
"schemes": [
|
|
74
|
+
"https",
|
|
75
|
+
"slack",
|
|
76
|
+
"file",
|
|
77
|
+
"ws"
|
|
78
|
+
],
|
|
79
|
+
"extensions": [
|
|
80
|
+
".xd",
|
|
81
|
+
".psd",
|
|
82
|
+
".bat",
|
|
83
|
+
".cmd",
|
|
84
|
+
""
|
|
85
|
+
]
|
|
86
|
+
},
|
|
87
|
+
"clipboard": "readAndWrite",
|
|
88
|
+
"webview": {
|
|
89
|
+
"allow": "yes",
|
|
90
|
+
"allowLocalRendering": "yes",
|
|
91
|
+
"domains": "all",
|
|
92
|
+
"enableMessageBridge": "localAndRemote"
|
|
93
|
+
},
|
|
94
|
+
"ipc": {
|
|
95
|
+
"enablePluginCommunication": true
|
|
96
|
+
},
|
|
97
|
+
"allowCodeGenerationFromStrings": true
|
|
98
|
+
},
|
|
99
|
+
"icons": [
|
|
100
|
+
{
|
|
101
|
+
"width": 48,
|
|
102
|
+
"height": 48,
|
|
103
|
+
"path": "icons/plugin-icon.png",
|
|
104
|
+
"scale": [
|
|
105
|
+
1,
|
|
106
|
+
2
|
|
107
|
+
],
|
|
108
|
+
"theme": [
|
|
109
|
+
"darkest",
|
|
110
|
+
"dark",
|
|
111
|
+
"medium",
|
|
112
|
+
"lightest",
|
|
113
|
+
"light",
|
|
114
|
+
"all"
|
|
115
|
+
],
|
|
116
|
+
"species": [
|
|
117
|
+
"pluginList"
|
|
118
|
+
]
|
|
119
|
+
}
|
|
120
|
+
]
|
|
121
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@bubblydoo/vitest-pool-uxp",
|
|
3
|
+
"type": "module",
|
|
4
|
+
"version": "0.0.2",
|
|
5
|
+
"description": "Vitest pool for running tests in Adobe UXP environments (Photoshop, etc.)",
|
|
6
|
+
"exports": {
|
|
7
|
+
".": {
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"import": "./dist/index.js"
|
|
10
|
+
},
|
|
11
|
+
"./package.json": "./package.json"
|
|
12
|
+
},
|
|
13
|
+
"publishConfig": {
|
|
14
|
+
"access": "public"
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist",
|
|
18
|
+
"fake-plugin"
|
|
19
|
+
],
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"@bubblydoo/uxp-devtools-common": "0.0.3",
|
|
22
|
+
"@bubblydoo/vitest-pool-cdp": "0.0.2"
|
|
23
|
+
},
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"@types/node": "^20.8.7",
|
|
26
|
+
"tsup": "^8.3.5",
|
|
27
|
+
"typescript": "^5.8.3",
|
|
28
|
+
"vitest": "^4.0.0",
|
|
29
|
+
"@bubblydoo/tsconfig": "0.0.3"
|
|
30
|
+
},
|
|
31
|
+
"scripts": {
|
|
32
|
+
"build": "tsup",
|
|
33
|
+
"dev": "tsup --watch",
|
|
34
|
+
"typecheck": "tsc --noEmit"
|
|
35
|
+
}
|
|
36
|
+
}
|