mirror of
https://github.com/toeverything/AFFiNE.git
synced 2024-11-24 01:55:14 +03:00
test: sync provider (#1754)
This commit is contained in:
parent
d8d965b9b8
commit
4dd1490eef
@ -32,7 +32,7 @@ const createAffineWebSocketProvider = (
|
||||
blockSuiteWorkspace.id,
|
||||
blockSuiteWorkspace.doc,
|
||||
{
|
||||
params: { token: getLoginStorage()?.token ?? '' },
|
||||
params: { token: getLoginStorage()?.refresh ?? '' },
|
||||
// @ts-expect-error ignore the type
|
||||
awareness: blockSuiteWorkspace.awarenessStore.awareness,
|
||||
// we maintain broadcast channel by ourselves
|
||||
|
@ -23,5 +23,9 @@
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"zod": "^3.21.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/ws": "^8.5.4",
|
||||
"ws": "^8.13.0"
|
||||
}
|
||||
}
|
||||
|
166
packages/workspace/src/affine/__tests__/sync.spec.ts
Normal file
166
packages/workspace/src/affine/__tests__/sync.spec.ts
Normal file
@ -0,0 +1,166 @@
|
||||
import type { Workspace } from '@affine/workspace/affine/api';
|
||||
import {
|
||||
createWorkspaceApis,
|
||||
PermissionType,
|
||||
} from '@affine/workspace/affine/api';
|
||||
import type { LoginResponse } from '@affine/workspace/affine/login';
|
||||
import { loginResponseSchema } from '@affine/workspace/affine/login';
|
||||
import { WebsocketProvider } from '@affine/workspace/affine/sync';
|
||||
import { createEmptyBlockSuiteWorkspace } from '@affine/workspace/utils';
|
||||
import user1 from '@affine-test/fixtures/built-in-user1.json';
|
||||
import user2 from '@affine-test/fixtures/built-in-user2.json';
|
||||
import type { ParagraphBlockModel } from '@blocksuite/blocks/models';
|
||||
import type { Page, Text } from '@blocksuite/store';
|
||||
import { uuidv4, Workspace as BlockSuiteWorkspace } from '@blocksuite/store';
|
||||
import { beforeEach, describe, expect, test, vi } from 'vitest';
|
||||
import WebSocket from 'ws';
|
||||
|
||||
const currentTokenRef = {
|
||||
current: null as LoginResponse | null,
|
||||
};
|
||||
|
||||
vi.stubGlobal('localStorage', {
|
||||
getItem: () => JSON.stringify(currentTokenRef.current),
|
||||
setItem: () => null,
|
||||
});
|
||||
|
||||
let workspaceApis: ReturnType<typeof createWorkspaceApis>;
|
||||
|
||||
let user1Token: LoginResponse;
|
||||
let user2Token: LoginResponse;
|
||||
|
||||
beforeEach(() => {
|
||||
workspaceApis = createWorkspaceApis('http://127.0.0.1:3000/');
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
const data = await fetch('http://127.0.0.1:3000/api/user/token', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
type: 'DebugLoginUser',
|
||||
email: user1.email,
|
||||
password: user1.password,
|
||||
}),
|
||||
}).then(r => r.json());
|
||||
loginResponseSchema.parse(data);
|
||||
user1Token = data;
|
||||
const data2 = await fetch('http://127.0.0.1:3000/api/user/token', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
type: 'DebugLoginUser',
|
||||
email: user2.email,
|
||||
password: user2.password,
|
||||
}),
|
||||
}).then(r => r.json());
|
||||
loginResponseSchema.parse(data2);
|
||||
user2Token = data2;
|
||||
});
|
||||
|
||||
const wsUrl = `ws://127.0.0.1:3000/api/sync/`;
|
||||
|
||||
describe('ydoc sync', () => {
|
||||
test(
|
||||
'page',
|
||||
async () => {
|
||||
currentTokenRef.current = user1Token;
|
||||
const list = await workspaceApis.getWorkspaces();
|
||||
const root = list.find(
|
||||
workspace => workspace.permission === PermissionType.Owner
|
||||
) as Workspace;
|
||||
expect(root).toBeDefined();
|
||||
const binary = await workspaceApis.downloadWorkspace(root.id);
|
||||
const workspace1 = createEmptyBlockSuiteWorkspace(
|
||||
root.id,
|
||||
(k: string) => ({ api: '/api/workspace', token: user1Token.token }[k])
|
||||
);
|
||||
const workspace2 = createEmptyBlockSuiteWorkspace(
|
||||
root.id,
|
||||
(k: string) => ({ api: '/api/workspace', token: user2Token.token }[k])
|
||||
);
|
||||
BlockSuiteWorkspace.Y.applyUpdate(workspace1.doc, new Uint8Array(binary));
|
||||
BlockSuiteWorkspace.Y.applyUpdate(workspace2.doc, new Uint8Array(binary));
|
||||
const provider1 = new WebsocketProvider(
|
||||
wsUrl,
|
||||
workspace1.id,
|
||||
workspace1.doc,
|
||||
{
|
||||
// @ts-expect-error ignore the error
|
||||
WebSocketPolyfill: WebSocket,
|
||||
params: { token: user1Token.refresh },
|
||||
// @ts-expect-error ignore the type
|
||||
awareness: workspace1.awarenessStore.awareness,
|
||||
disableBc: true,
|
||||
connect: false,
|
||||
}
|
||||
);
|
||||
|
||||
const provider2 = new WebsocketProvider(
|
||||
wsUrl,
|
||||
workspace2.id,
|
||||
workspace2.doc,
|
||||
{
|
||||
// @ts-expect-error ignore the error
|
||||
WebSocketPolyfill: WebSocket,
|
||||
params: { token: user2Token.refresh },
|
||||
// @ts-expect-error ignore the type
|
||||
awareness: workspace2.awarenessStore.awareness,
|
||||
disableBc: true,
|
||||
connect: false,
|
||||
}
|
||||
);
|
||||
|
||||
provider1.connect();
|
||||
provider2.connect();
|
||||
|
||||
function waitForConnected(provider: WebsocketProvider) {
|
||||
return new Promise<void>(resolve => {
|
||||
provider.once('status', ({ status }: any) => {
|
||||
expect(status).toBe('connected');
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
await Promise.all([
|
||||
waitForConnected(provider1),
|
||||
waitForConnected(provider2),
|
||||
]);
|
||||
|
||||
const pageId = uuidv4();
|
||||
const page1 = workspace1.createPage(pageId);
|
||||
const pageBlockId = page1.addBlock('affine:page', {
|
||||
title: new page1.Text(''),
|
||||
});
|
||||
page1.addBlock('affine:surface', {}, null);
|
||||
const frameId = page1.addBlock('affine:frame', {}, pageBlockId);
|
||||
const paragraphId = page1.addBlock('affine:paragraph', {}, frameId);
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
expect(workspace2.getPage(pageId)).toBeDefined();
|
||||
expect(workspace2.doc.getMap(`space:${pageId}`).toJSON()).toEqual(
|
||||
workspace1.doc.getMap(`space:${pageId}`).toJSON()
|
||||
);
|
||||
const page2 = workspace2.getPage(pageId) as Page;
|
||||
page1.updateBlockById(paragraphId, {
|
||||
text: new page1.Text('hello world'),
|
||||
});
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
const paragraph2 = page2.getBlockById(paragraphId) as ParagraphBlockModel;
|
||||
const text = paragraph2.text as Text;
|
||||
expect(text.toString()).toEqual(
|
||||
page1.getBlockById(paragraphId)?.text?.toString()
|
||||
);
|
||||
|
||||
provider1.disconnect();
|
||||
provider2.disconnect();
|
||||
},
|
||||
{
|
||||
timeout: 30000,
|
||||
}
|
||||
);
|
||||
});
|
13
yarn.lock
13
yarn.lock
@ -234,6 +234,7 @@ __metadata:
|
||||
"@affine/env": "workspace:*"
|
||||
"@blocksuite/blocks": 0.5.0-20230324040005-14417c2
|
||||
"@blocksuite/store": 0.5.0-20230324040005-14417c2
|
||||
"@types/ws": ^8.5.4
|
||||
firebase: ^9.18.0
|
||||
jotai: ^2.0.3
|
||||
js-base64: ^3.7.5
|
||||
@ -241,6 +242,7 @@ __metadata:
|
||||
lib0: ^0.2.73
|
||||
react: ^18.2.0
|
||||
react-dom: ^18.2.0
|
||||
ws: ^8.13.0
|
||||
zod: ^3.21.4
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
@ -6742,6 +6744,15 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/ws@npm:^8.5.4":
|
||||
version: 8.5.4
|
||||
resolution: "@types/ws@npm:8.5.4"
|
||||
dependencies:
|
||||
"@types/node": "*"
|
||||
checksum: fefbad20d211929bb996285c4e6f699b12192548afedbe4930ab4384f8a94577c9cd421acaad163cacd36b88649509970a05a0b8f20615b30c501ed5269038d1
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/yargs-parser@npm:*":
|
||||
version: 21.0.0
|
||||
resolution: "@types/yargs-parser@npm:21.0.0"
|
||||
@ -19256,7 +19267,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"ws@npm:^8.2.3":
|
||||
"ws@npm:^8.13.0, ws@npm:^8.2.3":
|
||||
version: 8.13.0
|
||||
resolution: "ws@npm:8.13.0"
|
||||
peerDependencies:
|
||||
|
Loading…
Reference in New Issue
Block a user