@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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
827
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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"
|