Vitest pool for running tests inside Adobe UXP environments (Photoshop, etc.).
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.
pnpm add -D @bubblydoo/vitest-pool-uxp vitest
Create a vitest config file (e.g., vitest.uxp.config.ts):
import { defineConfig } from "vitest/config";
import { uxpPool } from "@bubblydoo/vitest-pool-uxp";
export default defineConfig({
test: {
include: ["src/**/*.uxp-test.ts"],
pool: uxpPool(),
// Required settings - see "Vulcan System Limitations" below
isolate: false,
fileParallelism: false,
maxWorkers: 1,
// Recommended settings
testTimeout: 30000,
watch: false,
},
});
Run your tests:
vitest run --config vitest.uxp.config.ts
| Option | Type | Default | Description |
|---|---|---|---|
pluginPath |
string |
Built-in plugin | Path to a UXP plugin directory. The plugin will be loaded into Photoshop to establish the debugging connection. |
debug |
boolean |
false |
Enable debug logging |
connectionTimeout |
number |
10000 |
Timeout in milliseconds for establishing the CDP connection |
rpcTimeout |
number |
10000 |
Timeout in milliseconds for RPC calls |
By default, vitest-pool-uxp uses a built-in "Vitest UXP Test Runner" plugin. You can use your own plugin if needed:
pool: uxpPool({
pluginPath: "./my-uxp-plugin",
debug: true,
}),
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.
The Vulcan system only supports a single connection per Node.js process. Attempting to create multiple connections will cause a native assertion failure:
Assertion failed: (vulcan_), function initSelf, file vulcanadapter.cc, line 504.
This happens because:
VulcanAdapter native module maintains a singleton connection to the Vulcan serviceTo prevent multiple Vulcan connections, you must configure vitest to run tests sequentially with a single worker:
{
test: {
// Disable VM isolation - prevents vitest from creating
// separate V8 contexts that don't share module state
isolate: false,
// Run test files sequentially, not in parallel
fileParallelism: false,
// Use only one worker
maxWorkers: 1,
}
}
Why isolate: false is critical:
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.
The Vulcan system works through these components:
When DevToolsHelper is instantiated, it creates a VulcanAdapter which:
IVulcanMessageDispatcher::GetInstance() to get the singleton dispatcherSetConfig() to configure the endpointThe 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.
If you encounter Vulcan-related errors:
debug: true - enables logging to see connection progresspool: uxpPool({ debug: true })
┌─────────────────────┐ ┌─────────────────────┐
│ Vitest (Node.js) │ │ Photoshop │
│ │ │ │
│ ┌───────────────┐ │ │ ┌───────────────┐ │
│ │ vitest-pool- │ │ │ │ UXP Plugin │ │
│ │ uxp │ │ │ │ (injected) │ │
│ └───────┬───────┘ │ │ └───────┬───────┘ │
│ │ │ │ │ │
│ ┌───────▼───────┐ │ Vulcan IPC │ │ │
│ │ DevTools │──┼────────────────────┼──────────┘ │
│ │ Helper │ │ │ │
│ └───────┬───────┘ │ │ │
│ │ │ │ │
│ ┌───────▼───────┐ │ CDP WebSocket │ ┌───────────────┐ │
│ │ vitest-pool- │──┼────────────────────┼──│ CDP Server │ │
│ │ cdp │ │ │ └───────────────┘ │
│ └───────────────┘ │ │ │
└─────────────────────┘ └─────────────────────┘
MIT