@lvce-editor/test-worker 14.14.0 → 14.16.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/api.d.ts CHANGED
@@ -101,6 +101,17 @@ export interface MockOpenAiResponseOptions {
101
101
  readonly value: any;
102
102
  }
103
103
 
104
+ export interface ToolCallItem {
105
+ readonly toolCall: {
106
+ readonly arguments: any;
107
+ readonly name: "list_files";
108
+ };
109
+ }
110
+
111
+ export interface TextItem {
112
+ readonly text: string;
113
+ }
114
+
104
115
  export interface ChatDebugEvent {
105
116
  readonly [key: string]: unknown;
106
117
  }
@@ -145,6 +156,8 @@ export interface SelectItem2Options {
145
156
  readonly label: string;
146
157
  }
147
158
 
159
+ export type MockRequestInput = ToolCallItem | TextItem;
160
+
148
161
  export type ChatDebugInputName = "closeDetails" | "eventCategoryFilter" | "filter" | "showEventStreamFinishedEvents" | "showInputEvents" | "showResponsePartEvents" | "timelineRangePreset" | "useDevtoolsLayout";
149
162
 
150
163
  export type TokenRow = readonly string[];
@@ -250,6 +263,7 @@ interface Chat {
250
263
  readonly mockOpenAiResponse: (options: MockOpenAiResponseOptions) => Promise<void>;
251
264
  readonly mockOpenApiRequestGetAll: () => Promise<readonly any[]>;
252
265
  readonly mockOpenApiRequestReset: () => Promise<void>;
266
+ readonly mockOpenApiSetResponse: (items: readonly MockRequestInput[]) => Promise<void>;
253
267
  readonly mockOpenApiStreamFinish: () => Promise<void>;
254
268
  readonly mockOpenApiStreamPushChunk: (chunk: string) => Promise<void>;
255
269
  readonly mockOpenApiStreamReset: () => Promise<void>;
@@ -315,6 +329,7 @@ interface ChatDebug {
315
329
  readonly setShowInputEvents: (enabled: boolean) => Promise<void>;
316
330
  readonly setShowResponsePartEvents: (enabled: boolean) => Promise<void>;
317
331
  readonly setTimelineRangePreset: (value: string) => Promise<void>;
332
+ readonly shouldHavePayload: (expectedPayload: any) => Promise<void>;
318
333
  readonly toggleHeadersSection: (sectionId: string) => Promise<void>;
319
334
  readonly useDevtoolsLayout: () => Promise<void>;
320
335
  }
@@ -533,7 +533,7 @@ const execute$2 = (command, ...args) => {
533
533
 
534
534
  const Two$1 = '2.0';
535
535
  const callbacks$1 = Object.create(null);
536
- const get$2 = id => {
536
+ const get$3 = id => {
537
537
  return callbacks$1[id];
538
538
  };
539
539
  const remove$2 = id => {
@@ -682,7 +682,7 @@ const warn = (...args) => {
682
682
  console.warn(...args);
683
683
  };
684
684
  const resolve$1 = (id, response) => {
685
- const fn = get$2(id);
685
+ const fn = get$3(id);
686
686
  if (!fn) {
687
687
  console.log(response);
688
688
  warn(`callback ${id} may already be disposed`);
@@ -1091,10 +1091,10 @@ const createMockRpc = ({
1091
1091
  };
1092
1092
 
1093
1093
  const rpcs = Object.create(null);
1094
- const set$4 = (id, rpc) => {
1094
+ const set$5 = (id, rpc) => {
1095
1095
  rpcs[id] = rpc;
1096
1096
  };
1097
- const get$1 = id => {
1097
+ const get$2 = id => {
1098
1098
  return rpcs[id];
1099
1099
  };
1100
1100
  const remove$1 = id => {
@@ -1105,18 +1105,18 @@ const remove$1 = id => {
1105
1105
  const create = rpcId => {
1106
1106
  return {
1107
1107
  async dispose() {
1108
- const rpc = get$1(rpcId);
1108
+ const rpc = get$2(rpcId);
1109
1109
  await rpc.dispose();
1110
1110
  },
1111
1111
  // @ts-ignore
1112
1112
  invoke(method, ...params) {
1113
- const rpc = get$1(rpcId);
1113
+ const rpc = get$2(rpcId);
1114
1114
  // @ts-ignore
1115
1115
  return rpc.invoke(method, ...params);
1116
1116
  },
1117
1117
  // @ts-ignore
1118
1118
  invokeAndTransfer(method, ...params) {
1119
- const rpc = get$1(rpcId);
1119
+ const rpc = get$2(rpcId);
1120
1120
  // @ts-ignore
1121
1121
  return rpc.invokeAndTransfer(method, ...params);
1122
1122
  },
@@ -1124,7 +1124,7 @@ const create = rpcId => {
1124
1124
  const mockRpc = createMockRpc({
1125
1125
  commandMap
1126
1126
  });
1127
- set$4(rpcId, mockRpc);
1127
+ set$5(rpcId, mockRpc);
1128
1128
  // @ts-ignore
1129
1129
  mockRpc[Symbol.dispose] = () => {
1130
1130
  remove$1(rpcId);
@@ -1133,7 +1133,7 @@ const create = rpcId => {
1133
1133
  return mockRpc;
1134
1134
  },
1135
1135
  set(rpc) {
1136
- set$4(rpcId, rpc);
1136
+ set$5(rpcId, rpc);
1137
1137
  }
1138
1138
  };
1139
1139
  };
@@ -1152,18 +1152,18 @@ const TestWorker = 9001;
1152
1152
 
1153
1153
  const {
1154
1154
  invoke: invoke$2,
1155
- set: set$3
1155
+ set: set$4
1156
1156
  } = create(EditorWorker);
1157
1157
 
1158
1158
  const {
1159
1159
  invoke: invoke$1,
1160
- set: set$2
1160
+ set: set$3
1161
1161
  } = create(OpenerWorker);
1162
1162
 
1163
1163
  const {
1164
1164
  invoke,
1165
1165
  invokeAndTransfer,
1166
- set: set$1
1166
+ set: set$2
1167
1167
  } = create(RendererWorker);
1168
1168
  const sendMessagePortToEditorWorker = async (port, rpcId) => {
1169
1169
  const command = 'HandleMessagePort.handleMessagePort';
@@ -1188,7 +1188,7 @@ const initializeEditorWorker = async () => {
1188
1188
  commandMap: {},
1189
1189
  send: send$1
1190
1190
  });
1191
- set$3(rpc);
1191
+ set$4(rpc);
1192
1192
  };
1193
1193
 
1194
1194
  const send = async port => {
@@ -1199,7 +1199,18 @@ const initializeOpenerWorker = async () => {
1199
1199
  commandMap: {},
1200
1200
  send
1201
1201
  });
1202
- set$2(rpc);
1202
+ set$3(rpc);
1203
+ };
1204
+
1205
+ let autoFixError;
1206
+ const get$1 = () => {
1207
+ return autoFixError;
1208
+ };
1209
+ const set$1 = value => {
1210
+ autoFixError = value;
1211
+ };
1212
+ const clear$3 = () => {
1213
+ autoFixError = undefined;
1203
1214
  };
1204
1215
 
1205
1216
  class AssertionError extends Error {
@@ -2060,6 +2071,9 @@ const setShowChatListTime = async showTime => {
2060
2071
  const handleAgentModeChange = async newAgentMode => {
2061
2072
  return invoke('Chat.handleAgentModeChange', newAgentMode);
2062
2073
  };
2074
+ const mockOpenApiSetResponse = async items => {
2075
+ return invoke('Chat.mockOpenApiSetResponse', items);
2076
+ };
2063
2077
 
2064
2078
  const Chat = {
2065
2079
  chatListFocusFirst,
@@ -2101,6 +2115,7 @@ const Chat = {
2101
2115
  mockOpenAiResponse,
2102
2116
  mockOpenApiRequestGetAll,
2103
2117
  mockOpenApiRequestReset,
2118
+ mockOpenApiSetResponse,
2104
2119
  mockOpenApiStreamFinish,
2105
2120
  mockOpenApiStreamPushChunk,
2106
2121
  mockOpenApiStreamReset,
@@ -2132,6 +2147,26 @@ const Chat = {
2132
2147
  useMockApi
2133
2148
  };
2134
2149
 
2150
+ const defaultMessage = 'ChatDebug.shouldHavePayload assertion failed';
2151
+ const getMessage = cause => {
2152
+ if (!cause || typeof cause !== 'object') {
2153
+ return defaultMessage;
2154
+ }
2155
+ if ('message' in cause && typeof cause.message === 'string' && cause.message) {
2156
+ return cause.message;
2157
+ }
2158
+ return defaultMessage;
2159
+ };
2160
+ class ChatDebugShouldHavePayloadError extends Error {
2161
+ constructor(expectedPayload, actualPayload, cause) {
2162
+ super(getMessage(cause));
2163
+ this.name = 'ChatDebugShouldHavePayloadError';
2164
+ this.code = 'chat-debug.should-have-payload';
2165
+ this.expectedPayload = expectedPayload;
2166
+ this.actualPayload = actualPayload;
2167
+ }
2168
+ }
2169
+
2135
2170
  const open$a = async sessionId => {
2136
2171
  await invoke('Main.openUri', `chat-debug://${sessionId}`);
2137
2172
  };
@@ -2169,6 +2204,26 @@ const resetIndexedDbSupportForTest = async () => {
2169
2204
  const selectEventRow = async index => {
2170
2205
  await invoke('ChatDebug.handleEventRowClick', String(index));
2171
2206
  };
2207
+ const getActualPayload = error => {
2208
+ if (!error || typeof error !== 'object') {
2209
+ return undefined;
2210
+ }
2211
+ if ('actualPayload' in error) {
2212
+ return error.actualPayload;
2213
+ }
2214
+ if ('actual' in error) {
2215
+ return error.actual;
2216
+ }
2217
+ return undefined;
2218
+ };
2219
+ const shouldHavePayload = async expectedPayload => {
2220
+ try {
2221
+ await invoke('ChatDebug.shouldHavePayload', expectedPayload);
2222
+ } catch (error) {
2223
+ const actualPayload = getActualPayload(error);
2224
+ throw new ChatDebugShouldHavePayloadError(expectedPayload, actualPayload, error);
2225
+ }
2226
+ };
2172
2227
  const handleInput$7 = async (name, value, checked) => {
2173
2228
  await invoke('ChatDebug.handleInput', name, value, checked);
2174
2229
  };
@@ -2282,6 +2337,7 @@ const ChatDebug = {
2282
2337
  setShowInputEvents,
2283
2338
  setShowResponsePartEvents,
2284
2339
  setTimelineRangePreset,
2340
+ shouldHavePayload,
2285
2341
  toggleHeadersSection,
2286
2342
  useDevtoolsLayout
2287
2343
  };
@@ -3482,7 +3538,7 @@ const ExtensionSearch = {
3482
3538
 
3483
3539
  const Memfs = 'memfs';
3484
3540
 
3485
- const toFileUrl = url => {
3541
+ const toFileUrl$1 = url => {
3486
3542
  const urlObject = new URL(url);
3487
3543
  const pathName = urlObject.pathname;
3488
3544
  if (!pathName.startsWith('/remote')) {
@@ -3506,7 +3562,7 @@ const getDirents = async fileUrl => {
3506
3562
  return allDirents;
3507
3563
  };
3508
3564
  const getFileMapNode = async url => {
3509
- const fileUrl = toFileUrl(url);
3565
+ const fileUrl = toFileUrl$1(url);
3510
3566
  const allFiles = await getDirents(fileUrl);
3511
3567
  const fileMap = Object.create(null);
3512
3568
  const contents = await Promise.all(allFiles.map(filePath => {
@@ -4842,6 +4898,19 @@ const executeTest2 = async (name, fn, globals, timestampGenerator) => {
4842
4898
  const duration = end - start;
4843
4899
  const formattedDuration = formatDuration(duration);
4844
4900
  if (error) {
4901
+ const isChatDebugPayloadError = error instanceof ChatDebugShouldHavePayloadError && error.actualPayload !== undefined;
4902
+ const autoFixProperties = isChatDebugPayloadError ? {
4903
+ autoFixError: {
4904
+ actualPayload: error.actualPayload,
4905
+ code: error.code,
4906
+ expectedPayload: error.expectedPayload
4907
+ },
4908
+ overlayActions: [{
4909
+ command: 'Test.tryAutoFix',
4910
+ id: 'chat-debug-should-have-payload-autofix',
4911
+ label: 'Autofix'
4912
+ }]
4913
+ } : {};
4845
4914
  return {
4846
4915
  background: 'red',
4847
4916
  duration,
@@ -4849,6 +4918,7 @@ const executeTest2 = async (name, fn, globals, timestampGenerator) => {
4849
4918
  error,
4850
4919
  formattedDuration,
4851
4920
  name,
4921
+ ...autoFixProperties,
4852
4922
  start,
4853
4923
  text: `test failed: ${error}`,
4854
4924
  type: Fail
@@ -4886,18 +4956,25 @@ const now = () => {
4886
4956
 
4887
4957
  const executeTest = async (name, fn, globals = {}) => {
4888
4958
  const {
4959
+ autoFixError,
4889
4960
  background,
4890
4961
  error,
4891
4962
  formattedDuration,
4963
+ overlayActions,
4892
4964
  text,
4893
4965
  type
4894
4966
  } = await executeTest2(name, fn, globals, now);
4967
+ set$1(autoFixError);
4895
4968
  if (error) {
4896
4969
  await printTestError(error);
4897
4970
  } else {
4898
4971
  // eslint-disable-next-line no-console
4899
4972
  console.info(`PASS ${name} in ${formattedDuration}`);
4900
4973
  }
4974
+ if (overlayActions && overlayActions.length > 0) {
4975
+ await invoke('TestFrameWork.showOverlay', type, background, text, overlayActions);
4976
+ return;
4977
+ }
4901
4978
  await invoke('TestFrameWork.showOverlay', type, background, text);
4902
4979
  };
4903
4980
 
@@ -4989,6 +5066,7 @@ const execute = async (href, platform, assetDir) => {
4989
5066
  }
4990
5067
  // TODO maybe setup file watcher earlier, to not miss events?
4991
5068
  } catch (error) {
5069
+ clear$3();
4992
5070
  await printTestError(error);
4993
5071
  await invoke('TestFrameWork.showOverlay', Fail, 'red', `test failed: ${error}`);
4994
5072
  return;
@@ -5073,17 +5151,152 @@ const handleFileWatcherEvent = async event => {
5073
5151
  await hotReloadTest(lastItem, locationHref, time);
5074
5152
  };
5075
5153
 
5154
+ const whitespaceRegex = /\s+/g;
5155
+ const shouldHavePayloadRegex = /shouldHavePayload\(([^)]*)\)/gs;
5156
+ const normalizeWhitespace = value => {
5157
+ return value.replaceAll(whitespaceRegex, '');
5158
+ };
5159
+ const trySerialize = value => {
5160
+ try {
5161
+ return JSON.stringify(value, null, 2);
5162
+ } catch {
5163
+ return undefined;
5164
+ }
5165
+ };
5166
+ const replaceMatch = (fileContent, index, length, replacement) => {
5167
+ const before = fileContent.slice(0, index);
5168
+ const after = fileContent.slice(index + length);
5169
+ return `${before}${replacement}${after}`;
5170
+ };
5171
+ const replaceShouldHavePayload = (fileContent, expectedPayload, actualPayload) => {
5172
+ const serializedActual = trySerialize(actualPayload);
5173
+ if (!serializedActual) {
5174
+ return undefined;
5175
+ }
5176
+ const matches = [...fileContent.matchAll(shouldHavePayloadRegex)];
5177
+ if (matches.length === 0) {
5178
+ return undefined;
5179
+ }
5180
+ if (matches.length === 1) {
5181
+ const [match] = matches;
5182
+ if (typeof match.index !== 'number') {
5183
+ return undefined;
5184
+ }
5185
+ return replaceMatch(fileContent, match.index, match[0].length, `shouldHavePayload(${serializedActual})`);
5186
+ }
5187
+ const serializedExpected = trySerialize(expectedPayload);
5188
+ if (!serializedExpected) {
5189
+ return undefined;
5190
+ }
5191
+ const normalizedExpected = normalizeWhitespace(serializedExpected);
5192
+ const matchingCandidates = [];
5193
+ for (const match of matches) {
5194
+ const currentArgument = match[1];
5195
+ if (normalizeWhitespace(currentArgument) === normalizedExpected) {
5196
+ matchingCandidates.push(match);
5197
+ }
5198
+ }
5199
+ if (matchingCandidates.length !== 1) {
5200
+ return undefined;
5201
+ }
5202
+ const [candidate] = matchingCandidates;
5203
+ if (typeof candidate.index !== 'number') {
5204
+ return undefined;
5205
+ }
5206
+ return replaceMatch(fileContent, candidate.index, candidate[0].length, `shouldHavePayload(${serializedActual})`);
5207
+ };
5208
+ const getFileUrlFromRemotePath = rawPathName => {
5209
+ if (!rawPathName.startsWith('/remote')) {
5210
+ return undefined;
5211
+ }
5212
+ const rest = rawPathName.slice('/remote'.length);
5213
+ return `file://${rest}`;
5214
+ };
5215
+ const toFileUrl = (url, locationHref) => {
5216
+ const parsedUrl = new URL(url, locationHref);
5217
+ if (parsedUrl.protocol === 'file:') {
5218
+ parsedUrl.hash = '';
5219
+ parsedUrl.search = '';
5220
+ return parsedUrl.toString();
5221
+ }
5222
+ return getFileUrlFromRemotePath(parsedUrl.pathname);
5223
+ };
5224
+ const isAutoFixError = value => {
5225
+ if (!value) {
5226
+ return false;
5227
+ }
5228
+ return value.code === 'chat-debug.should-have-payload';
5229
+ };
5230
+ const defaultDependencies = {
5231
+ getAutoFixError() {
5232
+ return get$1();
5233
+ },
5234
+ getLatestTestInfo() {
5235
+ return maybeLast();
5236
+ },
5237
+ getLocationHref() {
5238
+ return location.href;
5239
+ },
5240
+ now: Date.now,
5241
+ readFile(uri) {
5242
+ return readFile(uri);
5243
+ },
5244
+ rerun(url, platform, assetDir) {
5245
+ return execute(url, platform, assetDir);
5246
+ },
5247
+ setAutoFixError(value) {
5248
+ set$1(value);
5249
+ },
5250
+ writeFile(uri, content) {
5251
+ return writeFile(uri, content);
5252
+ }
5253
+ };
5254
+ const tryAutoFixWith = async dependencies => {
5255
+ const latestTestInfo = dependencies.getLatestTestInfo();
5256
+ if (!latestTestInfo) {
5257
+ return;
5258
+ }
5259
+ if (latestTestInfo.inProgress) {
5260
+ return;
5261
+ }
5262
+ const autoFixError = dependencies.getAutoFixError();
5263
+ if (!isAutoFixError(autoFixError)) {
5264
+ return;
5265
+ }
5266
+ if (autoFixError.actualPayload === undefined) {
5267
+ return;
5268
+ }
5269
+ const locationHref = dependencies.getLocationHref();
5270
+ const fileUrl = toFileUrl(latestTestInfo.url, locationHref);
5271
+ if (!fileUrl) {
5272
+ return;
5273
+ }
5274
+ const fileContent = await dependencies.readFile(fileUrl);
5275
+ const updatedFileContent = replaceShouldHavePayload(fileContent, autoFixError.expectedPayload, autoFixError.actualPayload);
5276
+ if (!updatedFileContent || updatedFileContent === fileContent) {
5277
+ return;
5278
+ }
5279
+ await dependencies.writeFile(fileUrl, updatedFileContent);
5280
+ dependencies.setAutoFixError(undefined);
5281
+ const rerunUrl = createUrlWithQueryParameter(latestTestInfo.url, locationHref, dependencies.now());
5282
+ await dependencies.rerun(rerunUrl, latestTestInfo.platform, latestTestInfo.assetDir);
5283
+ };
5284
+ const tryAutoFix = async () => {
5285
+ await tryAutoFixWith(defaultDependencies);
5286
+ };
5287
+
5076
5288
  const commandMap = {
5077
5289
  'FileWatcher.handleEvent': handleFileWatcherEvent,
5078
5290
  'Test.execute': execute,
5079
- 'Test.executeMock': executeMock
5291
+ 'Test.executeMock': executeMock,
5292
+ 'Test.tryAutoFix': tryAutoFix
5080
5293
  };
5081
5294
 
5082
5295
  const initializeRendererWorker = async () => {
5083
5296
  const rpc = await create$1({
5084
5297
  commandMap: commandMap
5085
5298
  });
5086
- set$1(rpc);
5299
+ set$2(rpc);
5087
5300
  };
5088
5301
 
5089
5302
  const listen = async () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lvce-editor/test-worker",
3
- "version": "14.14.0",
3
+ "version": "14.16.0",
4
4
  "description": "Test Worker",
5
5
  "repository": {
6
6
  "type": "git",