@bubblydoo/uxp-test-framework-plugin 0.0.8 → 0.0.9

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/package.json CHANGED
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "name": "@bubblydoo/uxp-test-framework-plugin",
3
- "version": "0.0.8",
4
3
  "type": "module",
5
- "license": "MIT",
4
+ "version": "0.0.9",
6
5
  "author": "Hans Otto Wirtz <hansottowirtz@gmail.com>",
6
+ "license": "MIT",
7
+ "homepage": "https://github.com/bubblydoo/uxp-toolkit/tree/main/packages/uxp-test-framework-plugin",
7
8
  "repository": {
8
9
  "type": "git",
9
10
  "url": "https://github.com/bubblydoo/uxp-toolkit.git"
10
11
  },
11
- "homepage": "https://github.com/bubblydoo/uxp-toolkit/tree/main/packages/uxp-test-framework-plugin",
12
12
  "exports": {
13
13
  "./cli": {
14
14
  "types": "./dist-vite/cli.d.ts",
@@ -21,20 +21,19 @@
21
21
  },
22
22
  "files": [
23
23
  "dist-vite",
24
- "src-plugin",
25
- "index.html"
24
+ "index.html",
25
+ "src-plugin"
26
26
  ],
27
27
  "peerDependencies": {
28
- "@bubblydoo/uxp-toolkit": "0.0.8"
28
+ "@bubblydoo/uxp-toolkit": "0.0.9"
29
29
  },
30
30
  "dependencies": {
31
31
  "chokidar": "^5.0.0",
32
+ "tailwindcss": "^3.4.17",
32
33
  "typescript": "^5.9.3",
33
- "vite": "^6.4.1",
34
- "tailwindcss": "^3.4.17"
34
+ "vite": "^6.4.1"
35
35
  },
36
36
  "devDependencies": {
37
- "@adobe/cc-ext-uxp-types": "https://github.com/justintaylor-dev/cc-ext-uxp-types.git",
38
37
  "@babel/preset-typescript": "^7.28.5",
39
38
  "@rollup/plugin-commonjs": "^29.0.0",
40
39
  "@rollup/plugin-json": "^6.1.0",
@@ -42,7 +41,6 @@
42
41
  "@rollup/plugin-typescript": "^12.1.2",
43
42
  "@tanstack/react-query": "^5.90.20",
44
43
  "@types/node": "^20.8.7",
45
- "@types/photoshop": "^25.0.2",
46
44
  "@types/react": "^19.1.6",
47
45
  "@types/react-dom": "^19.1.6",
48
46
  "@types/ws": "^8.5.8",
@@ -64,11 +62,14 @@
64
62
  "vite-uxp-plugin": "^1.2.5",
65
63
  "ws": "^8.14.2",
66
64
  "zod": "^4.3.6",
65
+ "@adobe-uxp-types/photoshop": "0.1.0",
66
+ "@adobe-uxp-types/uxp": "0.1.0",
67
67
  "@bubblydoo/tsconfig": "0.0.3",
68
68
  "@bubblydoo/uxp-polyfills": "0.0.4",
69
- "@bubblydoo/uxp-test-framework-base": "0.0.4"
69
+ "@bubblydoo/uxp-test-framework-base": "0.0.5"
70
70
  },
71
71
  "scripts": {
72
- "build": "rm -rf dist-vite && pnpm exec rollup -c && cp cli.d.ts dist-vite/cli.d.ts"
72
+ "build": "rm -rf dist-vite && pnpm exec rollup -c && cp cli.d.ts dist-vite/cli.d.ts",
73
+ "typecheck": "tsc --noEmit"
73
74
  }
74
75
  }
@@ -1,19 +1,21 @@
1
- import { uxpEntrypointsSchema } from "@bubblydoo/uxp-toolkit";
2
- import { entrypoints } from "uxp";
1
+ /* eslint-disable no-console */
2
+ import { uxpEntrypointsSchema } from '@bubblydoo/uxp-toolkit';
3
+ import { entrypoints } from 'uxp';
3
4
 
4
5
  const manifestId = uxpEntrypointsSchema.parse(entrypoints)._pluginInfo.id;
5
6
 
6
7
  declare global {
8
+ // eslint-disable-next-line vars-on-top
7
9
  var BOLT_UXP_HOT_RELOAD_PORT: number;
8
10
  }
9
11
 
10
- const prefix = "[⚡ Bolt Hot Reload]";
12
+ const prefix = '[⚡ Bolt Hot Reload]';
11
13
 
12
14
  const log = console.log.bind(console, prefix);
13
15
 
14
- const listenForHotReload = () => {
15
- if (typeof BOLT_UXP_HOT_RELOAD_PORT === "undefined") {
16
- log("BOLT_UXP_HOT_RELOAD_PORT is not defined");
16
+ function listenForHotReload() {
17
+ if (typeof BOLT_UXP_HOT_RELOAD_PORT === 'undefined') {
18
+ log('BOLT_UXP_HOT_RELOAD_PORT is not defined');
17
19
  return;
18
20
  }
19
21
  const reconnect = (reason: string) => {
@@ -23,17 +25,17 @@ const listenForHotReload = () => {
23
25
  setTimeout(listenForHotReload, 3000);
24
26
  };
25
27
  const ws = new WebSocket(`ws://localhost:${BOLT_UXP_HOT_RELOAD_PORT}`);
26
- ws.onclose = () => reconnect("closed");
28
+ ws.onclose = () => reconnect('closed');
27
29
  ws.onmessage = (event) => {
28
30
  const data = JSON.parse(event.data);
29
- if (data.id === manifestId && data.status === "updated") {
30
- log("Hot reloading...");
31
+ if (data.id === manifestId && data.status === 'updated') {
32
+ log('Hot reloading...');
31
33
  location.reload();
32
34
  }
33
35
  };
34
36
  ws.onopen = () => {
35
- log("Connected to server");
37
+ log('Connected to server');
36
38
  };
37
- };
39
+ }
38
40
 
39
41
  listenForHotReload();
@@ -1,16 +1,17 @@
1
- import { useMutation, useQuery } from "@tanstack/react-query";
2
- import { BasicStackFrame, copyToClipboard, getBasicStackFrameAbsoluteFilePath, parseUxpErrorSourcemaps } from "@bubblydoo/uxp-toolkit";
1
+ import type { BasicStackFrame } from '@bubblydoo/uxp-toolkit';
2
+ import { copyToClipboard, getBasicStackFrameAbsoluteFilePath, parseUxpErrorSourcemaps } from '@bubblydoo/uxp-toolkit';
3
+ import { useMutation, useQuery } from '@tanstack/react-query';
3
4
 
4
5
  declare const __UNSOURCEMAPPED_HEADER_LINES__: number;
5
6
 
6
- const UNSOURCEMAPPED_HEADER_LINES =
7
- typeof __UNSOURCEMAPPED_HEADER_LINES__ === "number"
7
+ const UNSOURCEMAPPED_HEADER_LINES
8
+ = typeof __UNSOURCEMAPPED_HEADER_LINES__ === 'number'
8
9
  ? __UNSOURCEMAPPED_HEADER_LINES__
9
10
  : 0;
10
11
 
11
12
  export function ErrorView({ error }: { error: Error }) {
12
13
  const sourcemappedError = useQuery({
13
- queryKey: ["sourcemappedError", error],
14
+ queryKey: ['sourcemappedError', error],
14
15
  queryFn: async () => {
15
16
  return await parseUxpErrorSourcemaps(error, {
16
17
  unsourcemappedHeaderLines: UNSOURCEMAPPED_HEADER_LINES,
@@ -35,7 +36,10 @@ export function ErrorView({ error }: { error: Error }) {
35
36
  <div className="p-2">
36
37
  <div>Sourcemapped error:</div>
37
38
  {copyMutation.error && (
38
- <>Copy error: {errorToString(copyMutation.error)}</>
39
+ <>
40
+ Copy error:
41
+ {errorToString(copyMutation.error)}
42
+ </>
39
43
  )}
40
44
  {sourcemappedError.isPending && (
41
45
  <div>⏳ Loading sourcemapped error...</div>
@@ -45,15 +49,24 @@ export function ErrorView({ error }: { error: Error }) {
45
49
  )}
46
50
  {sourcemappedError.data && (
47
51
  <div className="text-red-500 whitespace-pre-wrap">
48
- {error.name}: {error.message}
52
+ {error.name}
53
+ :
54
+ {error.message}
49
55
  {sourcemappedError.data.map((frame, i) => (
50
56
  <div key={i}>
51
- {frame.functionName} @{" "}
57
+ {frame.functionName}
58
+ {' '}
59
+ @
60
+ {' '}
52
61
  <span
53
62
  className="underline"
54
63
  onClick={() => copyMutation.mutate(frame)}
55
64
  >
56
- {frame.fileName}:{frame.lineNumber}:{frame.columnNumber}
65
+ {frame.fileName}
66
+ :
67
+ {frame.lineNumber}
68
+ :
69
+ {frame.columnNumber}
57
70
  </span>
58
71
  </div>
59
72
  ))}
@@ -1,13 +1,13 @@
1
- import "@bubblydoo/uxp-polyfills";
2
- import "./bolt-uxp-ws-listener";
1
+ import * as React from 'react';
2
+ import ReactDOM from 'react-dom/client';
3
3
 
4
- import "./index.css";
5
- import React from "react";
6
- import ReactDOM from "react-dom/client";
4
+ import { App } from './main';
5
+ import '@bubblydoo/uxp-polyfills';
6
+ import './bolt-uxp-ws-listener';
7
7
 
8
- import { App } from "./main";
8
+ import './index.css';
9
9
 
10
- ReactDOM.createRoot(document.getElementById("app") as HTMLElement).render(
10
+ ReactDOM.createRoot(document.getElementById('app') as HTMLElement).render(
11
11
  <React.StrictMode>
12
12
  <App />
13
13
  </React.StrictMode>,
@@ -1,17 +1,19 @@
1
- import { resolve as nativeResolve } from "path";
1
+ // eslint-disable-next-line unicorn/prefer-node-protocol
2
+ import { resolve as nativeResolve } from 'path';
2
3
 
3
- /** for some reason native path.resolve in UXP returns a URL object
4
+ /**
5
+ * for some reason native path.resolve in UXP returns a URL object
4
6
  * this function converts it to a string
5
7
  */
6
8
  export function pathResolve(...pathSegments: string[]): string {
7
9
  const urlOrString = nativeResolve(...pathSegments) as URL | string;
8
- if (typeof urlOrString === "string") {
10
+ if (typeof urlOrString === 'string') {
9
11
  return urlOrString;
10
12
  }
11
13
  if (isUrl(urlOrString)) {
12
14
  return urlOrString.toString();
13
15
  }
14
- throw new Error("Unexpected URL object");
16
+ throw new Error('Unexpected URL object');
15
17
  }
16
18
 
17
19
  function isUrl(urlOrString: any): urlOrString is URL {
@@ -1,39 +1,40 @@
1
- import { tests } from "TESTS";
2
- import React, {
1
+ import type { Test } from '@bubblydoo/uxp-test-framework-base';
2
+ import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
3
+ import * as React from 'react';
4
+ import {
3
5
  createContext,
6
+ use,
4
7
  useCallback,
5
- useContext,
6
8
  useEffect,
7
9
  useMemo,
8
10
  useState,
9
- } from "react";
10
- import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
11
- import { ErrorView } from "./components/ErrorView";
12
- import { Test } from "@bubblydoo/uxp-test-framework-base";
11
+ } from 'react';
12
+ import { tests } from 'TESTS';
13
+ import { ErrorView } from './components/ErrorView';
13
14
 
14
- type TestResult = {
15
- status: "idle" | "success" | "error" | "pending";
15
+ interface TestResult {
16
+ status: 'idle' | 'success' | 'error' | 'pending';
16
17
  isIdle: boolean;
17
18
  isSuccess: boolean;
18
19
  isPending: boolean;
19
20
  error: any;
20
- };
21
+ }
21
22
 
22
23
  const TestResultsContext = createContext<{
23
- set(test: Test, result: TestResult): void;
24
- get(test: Test): TestResult | undefined;
25
- delete(test: Test): void;
24
+ set: (test: Test, result: TestResult) => void;
25
+ get: (test: Test) => TestResult | undefined;
26
+ delete: (test: Test) => void;
26
27
  }>(null!);
27
28
 
28
- export const App = () => {
29
+ export function App() {
29
30
  const [queryClient] = useState(() => new QueryClient());
30
31
  const [testResultsCache, setTestResults] = useState(
31
- () => new Map<Test, any>()
32
+ () => new Map<Test, any>(),
32
33
  );
33
34
  const testResults = useMemo(
34
35
  () => ({
35
36
  set: (test: Test, result: TestResult) =>
36
- setTestResults((prev) => new Map(prev).set(test, result)),
37
+ setTestResults(prev => new Map(prev).set(test, result)),
37
38
  get: (test: Test) => testResultsCache.get(test),
38
39
  delete: (test: Test) => {
39
40
  setTestResults((prev) => {
@@ -43,26 +44,27 @@ export const App = () => {
43
44
  });
44
45
  },
45
46
  }),
46
- [testResultsCache]
47
+ [testResultsCache],
47
48
  );
48
49
 
49
50
  return (
50
51
  <>
51
52
  <main className="text-white overflow-y-auto">
52
53
  <QueryClientProvider client={queryClient}>
53
- <TestResultsContext.Provider value={testResults}>
54
+ <TestResultsContext value={testResults}>
54
55
  <TestView tests={tests} />
55
- </TestResultsContext.Provider>
56
+ </TestResultsContext>
56
57
  </QueryClientProvider>
57
58
  </main>
58
59
  </>
59
60
  );
60
- };
61
+ }
61
62
 
62
63
  async function runTest(test: Test): Promise<void> {
63
64
  try {
64
65
  await test.run.call(undefined, { name: test.name });
65
- } catch (e) {
66
+ }
67
+ catch (e) {
66
68
  if (!(e instanceof Error)) {
67
69
  console.error(`Test ${test.name} threw a non-Error (${typeof e}): ${e}`);
68
70
  }
@@ -95,25 +97,25 @@ function usePromiseStatus<T>(promise: Promise<T> | null): TestResult {
95
97
  const result: TestResult = useMemo(
96
98
  () => ({
97
99
  status: success
98
- ? "success"
100
+ ? 'success'
99
101
  : error
100
- ? "error"
101
- : isPending
102
- ? "pending"
103
- : "idle",
102
+ ? 'error'
103
+ : isPending
104
+ ? 'pending'
105
+ : 'idle',
104
106
  isIdle: !promise,
105
107
  isSuccess: success,
106
108
  error,
107
109
  isPending,
108
110
  }),
109
- [success, error, isPending, promise]
111
+ [success, error, isPending, promise],
110
112
  );
111
113
 
112
114
  return result;
113
115
  }
114
116
 
115
117
  const stableIdleResult: TestResult = {
116
- status: "idle",
118
+ status: 'idle',
117
119
  isIdle: true,
118
120
  isSuccess: false,
119
121
  isPending: false,
@@ -122,19 +124,19 @@ const stableIdleResult: TestResult = {
122
124
 
123
125
  function useTestResult(test: Test): { mutate: () => void; result: TestResult } {
124
126
  const [promise, setPromise] = useState<Promise<void> | null>(null);
125
- const testResults = useContext(TestResultsContext);
127
+ const testResults = use(TestResultsContext);
126
128
  const mutate = useCallback(() => {
127
129
  testResults.delete(test);
128
130
  setPromise(runTest(test));
129
131
  }, [test, testResults]);
130
132
  const promiseResult = usePromiseStatus(promise);
131
133
  useEffect(() => {
132
- if (promiseResult.status !== "idle") {
134
+ if (promiseResult.status !== 'idle') {
133
135
  testResults.set(test, promiseResult);
134
136
  }
135
137
  }, [promiseResult]);
136
- const globalResult =
137
- promiseResult.status !== "idle"
138
+ const globalResult
139
+ = promiseResult.status !== 'idle'
138
140
  ? promiseResult
139
141
  : testResults.get(test) ?? stableIdleResult;
140
142
  return {
@@ -144,13 +146,13 @@ function useTestResult(test: Test): { mutate: () => void; result: TestResult } {
144
146
  }
145
147
 
146
148
  function TestView({ tests }: { tests: Test[] }) {
147
- const resultContext = useContext(TestResultsContext);
149
+ const resultContext = use(TestResultsContext);
148
150
  const runAllTests = useCallback(async () => {
149
151
  for (const test of tests) {
150
152
  console.log(`Running test ${test.name}`);
151
153
  // await runTest(test);
152
154
  resultContext.set(test, {
153
- status: "pending",
155
+ status: 'pending',
154
156
  isIdle: false,
155
157
  isSuccess: false,
156
158
  isPending: true,
@@ -159,15 +161,16 @@ function TestView({ tests }: { tests: Test[] }) {
159
161
  try {
160
162
  await runTest(test);
161
163
  resultContext.set(test, {
162
- status: "success",
164
+ status: 'success',
163
165
  isIdle: false,
164
166
  isSuccess: true,
165
167
  isPending: false,
166
168
  error: null,
167
169
  });
168
- } catch (e) {
170
+ }
171
+ catch (e) {
169
172
  resultContext.set(test, {
170
- status: "error",
173
+ status: 'error',
171
174
  isIdle: false,
172
175
  isSuccess: false,
173
176
  isPending: false,
@@ -187,7 +190,8 @@ function TestView({ tests }: { tests: Test[] }) {
187
190
  Run All Tests
188
191
  </button>
189
192
  {tests.map((test, i) => {
190
- if (!test) return;
193
+ if (!test)
194
+ return null;
191
195
  return <OneTest test={test} key={i} />;
192
196
  })}
193
197
  </div>
@@ -216,7 +220,11 @@ function OneTest(props: { test: Test }) {
216
220
  </button>
217
221
  </div>
218
222
  <div>
219
- Result: {result.status} <TestResultEmoji result={result} />
223
+ Result:
224
+ {' '}
225
+ {result.status}
226
+ {' '}
227
+ <TestResultEmoji result={result} />
220
228
  {result.error && (
221
229
  <div className="pt-2">
222
230
  <ErrorView error={result.error} />
@@ -229,10 +237,10 @@ function OneTest(props: { test: Test }) {
229
237
 
230
238
  function TestResultEmoji({ result }: { result: TestResult }) {
231
239
  return result.error
232
- ? ""
240
+ ? ''
233
241
  : result.isSuccess
234
- ? ""
235
- : result.isPending
236
- ? ""
237
- : "";
242
+ ? ''
243
+ : result.isPending
244
+ ? ''
245
+ : '';
238
246
  }
@@ -1,5 +1,5 @@
1
- declare module "TESTS" {
2
- type Test = import("@bubblydoo/uxp-test-framework-base").Test;
1
+ declare module 'TESTS' {
2
+ type Test = import('@bubblydoo/uxp-test-framework-base').Test;
3
3
 
4
4
  export const tests: Test[];
5
5
  }