@@ -26,6 +26,7 @@ import { test } from 'integration-test/base'
2626import { uuidv4 } from 'lib0/random.js'
2727import { join } from 'node:path'
2828import type { Page , WebSocketRoute } from 'playwright'
29+ import { YjsChannel } from 'ydoc-channel'
2930import { WSSharedDoc , YjsConnection , type YjsSocket } from 'ydoc-server'
3031import { makeVisUpdates , mockDataHandler , mockLSHandler , mockYdocProvider } from './lsHandler'
3132
@@ -37,6 +38,8 @@ const ROOT_PARENT_PATH = Path('/home/user/enso')
3738const ROOT_PATH = Path ( '/home/user/enso/enso-projects' )
3839const DOWNLOAD_PATH = Path ( '/home/user/enso/Downloads' )
3940
41+ // These addresses are kept for backward compatibility in OpenProject response
42+ // but the actual communication now flows through YjsChannels
4043const languageServerJsonAddress = { host : '127.0.0.1' , port : 1235 }
4144const languageServerBinaryAddress = { host : '127.0.0.1' , port : 1234 }
4245const languageServerYdocAddress = { host : '127.0.0.1' , port : 1233 }
@@ -241,7 +244,8 @@ export async function mockLocalApi(page: Page) {
241244 addDirectory ( { path : ROOT_PATH } )
242245 addDirectory ( { path : DOWNLOAD_PATH } )
243246
244- let languageServerBinaryWs : WebSocketRoute | null = null
247+ // Data channel for visualization updates, set up when client connects
248+ let dataChannel : YjsChannel < Uint8Array > | null = null
245249
246250 await test . step ( 'Mock Local API' , async ( ) => {
247251 const toJSONRPCResult = ( result : unknown ) : JSONRPCResponse < unknown > => ( {
@@ -340,40 +344,6 @@ export async function mockLocalApi(page: Page) {
340344 } )
341345 } )
342346
343- await page . routeWebSocket (
344- `ws://${ languageServerBinaryAddress . host } :${ languageServerBinaryAddress . port } /` ,
345- ( ws ) => {
346- languageServerBinaryWs = ws
347- ws . onMessage ( async ( messageRaw ) => {
348- const response = await mockDataHandler ( new Uint8Array ( Buffer . from ( messageRaw ) ) . buffer )
349- if ( response ) {
350- ws . send ( Buffer . from ( response ) )
351- }
352- } )
353- } ,
354- )
355- await page . routeWebSocket (
356- `ws://${ languageServerJsonAddress . host } :${ languageServerJsonAddress . port } /` ,
357- ( ws ) => {
358- ws . onMessage ( async ( messageRaw ) => {
359- const { method, params, jsonrpc, id } = JSON . parse ( messageRaw . toString ( ) )
360- try {
361- const result =
362- ( await mockLSHandler (
363- method ,
364- params ,
365- ( message ) => ws . send ( JSON . stringify ( { jsonrpc, ...message } ) ) ,
366- ( binaryData ?: ArrayBuffer ) => {
367- if ( binaryData ) languageServerBinaryWs ?. send ( Buffer . from ( binaryData ) )
368- } ,
369- ) ) ?? null
370- ws . send ( JSON . stringify ( { jsonrpc, id, result } ) )
371- } catch ( error ) {
372- ws . send ( JSON . stringify ( { jsonrpc, id, error } ) )
373- }
374- } )
375- } ,
376- )
377347 const ydocAddressBase = `ws://${ languageServerYdocAddress . host } :${ languageServerYdocAddress . port } `
378348
379349 class MockWs implements YjsSocket {
@@ -413,14 +383,76 @@ export async function mockLocalApi(page: Page) {
413383 }
414384 }
415385
386+ /** Set up the mock JSON-RPC channel handler with its associated data channel */
387+ function setupMockLsChannel (
388+ channel : YjsChannel < string > ,
389+ associatedDataChannel : YjsChannel < Uint8Array > | null ,
390+ ) {
391+ channel . subscribe ( async ( messageRaw ) => {
392+ const { method, params, jsonrpc, id } = JSON . parse ( messageRaw )
393+ try {
394+ const result =
395+ ( await mockLSHandler (
396+ method ,
397+ params ,
398+ ( message ) => channel . send ( JSON . stringify ( { jsonrpc, ...message } ) ) ,
399+ ( binaryData ?: ArrayBuffer ) => {
400+ // Use the associated data channel for this connection
401+ if ( binaryData && associatedDataChannel ) {
402+ associatedDataChannel . send ( new Uint8Array ( binaryData ) )
403+ }
404+ } ,
405+ ) ) ?? null
406+ channel . send ( JSON . stringify ( { jsonrpc, id, result } ) )
407+ } catch ( error ) {
408+ channel . send ( JSON . stringify ( { jsonrpc, id, error } ) )
409+ }
410+ } )
411+ }
412+
413+ /** Set up the mock binary data channel handler */
414+ function setupMockDataChannel ( channel : YjsChannel < Uint8Array > ) {
415+ // Also set the global dataChannel for updateVisualization API
416+ dataChannel = channel
417+ channel . subscribe ( async ( messageRaw ) => {
418+ // Important: Use slice to get a copy of just the relevant portion
419+ // because messageRaw.buffer may include data beyond the Uint8Array's view
420+ const data = messageRaw . buffer . slice (
421+ messageRaw . byteOffset ,
422+ messageRaw . byteOffset + messageRaw . byteLength ,
423+ ) as ArrayBuffer
424+ const response = await mockDataHandler ( data )
425+ if ( response ) {
426+ channel . send ( new Uint8Array ( response ) )
427+ }
428+ } )
429+ }
430+
416431 await page . routeWebSocket ( `${ ydocAddressBase } /**` , ( wsRoute ) => {
417432 const parsedUrl = new URL ( wsRoute . url ( ) )
418433 const room = parsedUrl . pathname . substring ( '/project/' . length )
434+ const lsChannelName = parsedUrl . searchParams . get ( 'ls' )
435+ const dataChannelName = parsedUrl . searchParams . get ( 'data' )
419436
420437 const mockWs = new MockWs ( wsRoute )
421438 const wsDoc = new WSSharedDoc ( )
422439 const _connection = new YjsConnection ( mockWs , wsDoc )
423440 mockYdocProvider ( room , wsDoc . doc )
441+
442+ let binaryChannel : YjsChannel < Uint8Array > | null = null
443+ if ( dataChannelName ) {
444+ binaryChannel = new YjsChannel < Uint8Array > ( wsDoc . doc , dataChannelName )
445+ // Only set the global dataChannel for the main 'index' room connection.
446+ // Subdoc connections should not overwrite it, as the client's DataServer
447+ // only listens on the main document's channel.
448+ if ( room === 'index' ) {
449+ setupMockDataChannel ( binaryChannel )
450+ }
451+ }
452+ if ( lsChannelName ) {
453+ const lsChannel = new YjsChannel < string > ( wsDoc . doc , lsChannelName )
454+ setupMockLsChannel ( lsChannel , binaryChannel )
455+ }
424456 } )
425457
426458 await page . route ( '/api/root-directory-path' , async ( route , request ) => {
@@ -638,7 +670,7 @@ export async function mockLocalApi(page: Page) {
638670
639671 async function updateVisualization ( preprocessor : string , data : unknown ) {
640672 for ( const update of makeVisUpdates ( preprocessor , data ) ) {
641- languageServerBinaryWs ?. send ( Buffer . from ( update ) )
673+ dataChannel ?. send ( new Uint8Array ( update ) )
642674 }
643675 }
644676
0 commit comments