@rimori/playwright-testing 0.3.5-next.1 → 0.3.5-next.3

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.
@@ -79,6 +79,19 @@ export declare class RimoriTestEnvironment {
79
79
  * Removes a one-time mock from the mocks array after it's been used.
80
80
  */
81
81
  private removeOneTimeMock;
82
+ /**
83
+ * Creates a wrapper around the Playwright Request object that provides backwards compatibility
84
+ * for matchers. The new rimori-client sends `messages` array instead of `instructions`,
85
+ * so this wrapper extracts the prompts from messages and provides them as `instructions`.
86
+ *
87
+ * The old API had a single `instructions` field which typically contained the user's specific
88
+ * instruction (what the AI should do). The new API splits this into:
89
+ * - systemPrompt (messages[0] with role='system'): High-level behavior instructions
90
+ * - userPrompt (messages[1] with role='user'): Specific task instruction
91
+ *
92
+ * For backwards compatibility, we concatenate all message contents into `instructions`.
93
+ */
94
+ private createBackwardsCompatibleRequest;
82
95
  private handleRoute;
83
96
  /**
84
97
  * Adds a supabase route to the supabase routes object.
@@ -216,21 +229,45 @@ export declare class RimoriTestEnvironment {
216
229
  triggerOnMainPanelAction: (payload: MainPanelAction) => Promise<void>;
217
230
  };
218
231
  readonly ai: {
232
+ /**
233
+ * Mocks a text response from the LLM endpoint.
234
+ * Since getText now uses streamObject internally with a { result: string } schema,
235
+ * the mock value should be the full response object.
236
+ *
237
+ * @param values - The response object to return. Should include { result: string } for getText calls.
238
+ * @param options - Optional mock options.
239
+ */
219
240
  mockGetText: (values: unknown, options?: MockOptions) => void;
220
241
  /**
221
242
  * Mocks a streaming text response from the LLM endpoint.
222
- * The text will be formatted as SSE (Server-Sent Events) to simulate streaming.
243
+ * The new rimori-client's getSteamedText uses streamObject internally with { result: string } schema,
244
+ * so the text is wrapped in a result object.
223
245
  *
224
246
  * **Note**: Due to Playwright's route.fulfill() requiring a complete response body,
225
247
  * all SSE chunks are sent at once (no delays). The client will still parse it as SSE correctly.
226
248
  *
227
- * @param text - The text to stream. Will be formatted as SSE chunks.
249
+ * @param text - The text to stream. Will be wrapped as { result: text } and formatted as SSE.
228
250
  * @param options - Optional mock options.
229
251
  */
230
252
  mockGetSteamedText: (text: string, options?: MockOptions) => void;
231
253
  mockGetVoice: (values: Buffer, options?: MockOptions) => void;
232
254
  mockGetTextFromVoice: (text: string, options?: MockOptions) => void;
255
+ /**
256
+ * Mocks an object response from the LLM endpoint.
257
+ * Since getObject now uses streamObject internally, this is a streaming response.
258
+ *
259
+ * @param value - The object to return from the LLM.
260
+ * @param options - Optional mock options.
261
+ */
233
262
  mockGetObject: (value: Record<string, unknown>, options?: MockOptions) => void;
263
+ /**
264
+ * Mocks a streaming object response from the LLM endpoint.
265
+ * Returns the object via SSE format with data: prefix.
266
+ *
267
+ * @param value - The object to stream from the LLM.
268
+ * @param options - Optional mock options.
269
+ */
270
+ mockGetStreamedObject: (value: Record<string, unknown>, options?: MockOptions) => void;
234
271
  };
235
272
  /**
236
273
  * Helpers for tracking browser audio playback in tests.
@@ -47,7 +47,13 @@ class RimoriTestEnvironment {
47
47
  this.addSupabaseRoute('plugin_settings', response, { ...options, method: 'POST' });
48
48
  },
49
49
  mockGetUserInfo: (userInfo, options) => {
50
- this.addSupabaseRoute('/user-info', { ...this.rimoriInfo.profile, ...userInfo }, { ...options, delay: 0 });
50
+ // Update the rimoriInfo.profile so that MessageChannelSimulator returns the correct user info
51
+ this.rimoriInfo.profile = { ...this.rimoriInfo.profile, ...userInfo };
52
+ // Also update the MessageChannelSimulator if it exists (setup() has been called)
53
+ if (this.messageChannelSimulator) {
54
+ this.messageChannelSimulator.setUserInfo(this.rimoriInfo.profile);
55
+ }
56
+ this.addSupabaseRoute('/user-info', this.rimoriInfo.profile, { ...options, delay: 0 });
51
57
  },
52
58
  mockGetPluginInfo: (pluginInfo, options) => {
53
59
  this.addSupabaseRoute('/plugin-info', pluginInfo, options);
@@ -191,23 +197,31 @@ class RimoriTestEnvironment {
191
197
  },
192
198
  };
193
199
  this.ai = {
200
+ /**
201
+ * Mocks a text response from the LLM endpoint.
202
+ * Since getText now uses streamObject internally with a { result: string } schema,
203
+ * the mock value should be the full response object.
204
+ *
205
+ * @param values - The response object to return. Should include { result: string } for getText calls.
206
+ * @param options - Optional mock options.
207
+ */
194
208
  mockGetText: (values, options) => {
195
- console.log('Mocking get text for mockGetText', values, options);
196
- console.warn('mockGetText is not tested');
197
- this.addBackendRoute('/llm-text', values, options);
209
+ this.addBackendRoute('/ai/llm', values, { ...options, isStreaming: true });
198
210
  },
199
211
  /**
200
212
  * Mocks a streaming text response from the LLM endpoint.
201
- * The text will be formatted as SSE (Server-Sent Events) to simulate streaming.
213
+ * The new rimori-client's getSteamedText uses streamObject internally with { result: string } schema,
214
+ * so the text is wrapped in a result object.
202
215
  *
203
216
  * **Note**: Due to Playwright's route.fulfill() requiring a complete response body,
204
217
  * all SSE chunks are sent at once (no delays). The client will still parse it as SSE correctly.
205
218
  *
206
- * @param text - The text to stream. Will be formatted as SSE chunks.
219
+ * @param text - The text to stream. Will be wrapped as { result: text } and formatted as SSE.
207
220
  * @param options - Optional mock options.
208
221
  */
209
222
  mockGetSteamedText: (text, options) => {
210
- this.addBackendRoute('/ai/llm', text, { ...options, isStreaming: true });
223
+ // Wrap text in result object as the new client expects { result: string }
224
+ this.addBackendRoute('/ai/llm', { result: text }, { ...options, isStreaming: true });
211
225
  },
212
226
  mockGetVoice: (values, options) => {
213
227
  this.addBackendRoute('/voice/tts', values, options);
@@ -215,8 +229,25 @@ class RimoriTestEnvironment {
215
229
  mockGetTextFromVoice: (text, options) => {
216
230
  this.addBackendRoute('/voice/stt', text, options);
217
231
  },
232
+ /**
233
+ * Mocks an object response from the LLM endpoint.
234
+ * Since getObject now uses streamObject internally, this is a streaming response.
235
+ *
236
+ * @param value - The object to return from the LLM.
237
+ * @param options - Optional mock options.
238
+ */
218
239
  mockGetObject: (value, options) => {
219
- this.addBackendRoute('/ai/llm-object', value, { ...options, method: 'POST' });
240
+ this.addBackendRoute('/ai/llm', value, { ...options, isStreaming: true });
241
+ },
242
+ /**
243
+ * Mocks a streaming object response from the LLM endpoint.
244
+ * Returns the object via SSE format with data: prefix.
245
+ *
246
+ * @param value - The object to stream from the LLM.
247
+ * @param options - Optional mock options.
248
+ */
249
+ mockGetStreamedObject: (value, options) => {
250
+ this.addBackendRoute('/ai/llm', value, { ...options, isStreaming: true });
220
251
  },
221
252
  };
222
253
  /**
@@ -767,6 +798,53 @@ class RimoriTestEnvironment {
767
798
  mocks.splice(index, 1);
768
799
  }
769
800
  }
801
+ /**
802
+ * Creates a wrapper around the Playwright Request object that provides backwards compatibility
803
+ * for matchers. The new rimori-client sends `messages` array instead of `instructions`,
804
+ * so this wrapper extracts the prompts from messages and provides them as `instructions`.
805
+ *
806
+ * The old API had a single `instructions` field which typically contained the user's specific
807
+ * instruction (what the AI should do). The new API splits this into:
808
+ * - systemPrompt (messages[0] with role='system'): High-level behavior instructions
809
+ * - userPrompt (messages[1] with role='user'): Specific task instruction
810
+ *
811
+ * For backwards compatibility, we concatenate all message contents into `instructions`.
812
+ */
813
+ createBackwardsCompatibleRequest(originalRequest) {
814
+ // Create a proxy that intercepts postDataJSON calls
815
+ return new Proxy(originalRequest, {
816
+ get(target, prop) {
817
+ if (prop === 'postDataJSON') {
818
+ return () => {
819
+ try {
820
+ const body = target.postDataJSON();
821
+ if (body && body.messages && Array.isArray(body.messages) && !body.instructions) {
822
+ // Concatenate all message contents for backwards compatibility
823
+ // This allows matchers to check for text that might be in either system or user prompts
824
+ const allContent = body.messages
825
+ .map((m) => m.content || '')
826
+ .filter((content) => content.length > 0)
827
+ .join('\n');
828
+ if (allContent) {
829
+ return { ...body, instructions: allContent };
830
+ }
831
+ }
832
+ return body;
833
+ }
834
+ catch {
835
+ return null;
836
+ }
837
+ };
838
+ }
839
+ // For all other properties, return the original value bound to the target
840
+ const value = target[prop];
841
+ if (typeof value === 'function') {
842
+ return value.bind(target);
843
+ }
844
+ return value;
845
+ },
846
+ });
847
+ }
770
848
  async handleRoute(route, routes) {
771
849
  const request = route.request();
772
850
  const requestUrl = request.url();
@@ -780,6 +858,8 @@ class RimoriTestEnvironment {
780
858
  route.abort('not_found');
781
859
  return;
782
860
  }
861
+ // Create backwards-compatible request wrapper for matchers
862
+ const compatRequest = this.createBackwardsCompatibleRequest(request);
783
863
  // Find the first matching mock based on matcher function
784
864
  // Priority: mocks with matchers that match > mocks without matchers (as fallback)
785
865
  let matchingMock;
@@ -787,7 +867,8 @@ class RimoriTestEnvironment {
787
867
  for (const mock of mocks) {
788
868
  if (mock.options?.matcher) {
789
869
  try {
790
- if (mock.options.matcher(request)) {
870
+ // Use the backwards-compatible request wrapper for matchers
871
+ if (mock.options.matcher(compatRequest)) {
791
872
  matchingMock = mock;
792
873
  break;
793
874
  }
@@ -821,10 +902,19 @@ class RimoriTestEnvironment {
821
902
  if (typeof matchingMock.value === 'function') {
822
903
  responseValue = await matchingMock.value(request);
823
904
  }
824
- // Handle streaming responses (for mockGetSteamedText)
905
+ // Handle streaming responses (for mockGetSteamedText and mockGetStreamedObject)
825
906
  // Since Playwright requires complete body, we format as SSE without delays
826
- if (matchingMock.isStreaming && typeof responseValue === 'string') {
827
- const body = this.formatAsSSE(responseValue);
907
+ if (matchingMock.isStreaming) {
908
+ let body;
909
+ if (typeof responseValue === 'string') {
910
+ // Text streaming (mockGetSteamedText)
911
+ body = this.formatAsSSE(responseValue);
912
+ }
913
+ else {
914
+ // Object streaming (mockGetStreamedObject)
915
+ // Format as SSE with JSON payload, followed by [DONE] marker
916
+ body = `data: ${JSON.stringify(responseValue)}\n\ndata: [DONE]\n\n`;
917
+ }
828
918
  return await route.fulfill({
829
919
  status: 200,
830
920
  headers: { 'Content-Type': 'text/event-stream' },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rimori/playwright-testing",
3
- "version": "0.3.5-next.1",
3
+ "version": "0.3.5-next.3",
4
4
  "description": "Playwright testing utilities for Rimori plugins and workers",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -23,11 +23,11 @@
23
23
  },
24
24
  "peerDependencies": {
25
25
  "@playwright/test": "^1.40.0",
26
- "@rimori/client": "2.5.10-next.0"
26
+ "@rimori/client": "2.5.11-next.1"
27
27
  },
28
28
  "devDependencies": {
29
29
  "@playwright/test": "^1.40.0",
30
- "@rimori/client": "2.5.10-next.0",
30
+ "@rimori/client": "2.5.11-next.1",
31
31
  "@types/node": "^20.12.7",
32
32
  "rimraf": "^5.0.7",
33
33
  "typescript": "^5.7.2"