tests first batch
This commit is contained in:
@@ -43,9 +43,6 @@ import { LivekitRoomAudioRenderer } from "../livekit/MatrixAudioRenderer";
|
|||||||
import { MediaDevicesContext } from "../MediaDevicesContext";
|
import { MediaDevicesContext } from "../MediaDevicesContext";
|
||||||
import { HeaderStyle } from "../UrlParams";
|
import { HeaderStyle } from "../UrlParams";
|
||||||
|
|
||||||
// vi.hoisted(() => {
|
|
||||||
// localStorage = {} as unknown as Storage;
|
|
||||||
// });
|
|
||||||
vi.hoisted(
|
vi.hoisted(
|
||||||
() =>
|
() =>
|
||||||
(global.ImageData = class MockImageData {
|
(global.ImageData = class MockImageData {
|
||||||
@@ -109,6 +106,7 @@ function createInCallView(): RenderResult & {
|
|||||||
getUserId: () => localRtcMember.userId,
|
getUserId: () => localRtcMember.userId,
|
||||||
getDeviceId: () => localRtcMember.deviceId,
|
getDeviceId: () => localRtcMember.deviceId,
|
||||||
getRoom: (rId) => (rId === roomId ? room : null),
|
getRoom: (rId) => (rId === roomId ? room : null),
|
||||||
|
getDomain: () => "example.com",
|
||||||
} as Partial<MatrixClient> as MatrixClient;
|
} as Partial<MatrixClient> as MatrixClient;
|
||||||
const room = mockMatrixRoom({
|
const room = mockMatrixRoom({
|
||||||
relations: {
|
relations: {
|
||||||
@@ -119,7 +117,8 @@ function createInCallView(): RenderResult & {
|
|||||||
} as unknown as RelationsContainer,
|
} as unknown as RelationsContainer,
|
||||||
client,
|
client,
|
||||||
roomId,
|
roomId,
|
||||||
getMember: (userId) => roomMembers.get(userId) ?? null,
|
// getMember: (userId) => roomMembers.get(userId) ?? null,
|
||||||
|
getMembers: () => Array.from(roomMembers.values()),
|
||||||
getMxcAvatarUrl: () => null,
|
getMxcAvatarUrl: () => null,
|
||||||
hasEncryptionStateEvent: vi.fn().mockReturnValue(true),
|
hasEncryptionStateEvent: vi.fn().mockReturnValue(true),
|
||||||
getCanonicalAlias: () => null,
|
getCanonicalAlias: () => null,
|
||||||
|
|||||||
@@ -118,16 +118,16 @@ export interface ActiveCallProps
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const ActiveCall: FC<ActiveCallProps> = (props) => {
|
export const ActiveCall: FC<ActiveCallProps> = (props) => {
|
||||||
const mediaDevices = useMediaDevices();
|
|
||||||
const [vm, setVm] = useState<CallViewModel | null>(null);
|
const [vm, setVm] = useState<CallViewModel | null>(null);
|
||||||
|
|
||||||
const { autoLeaveWhenOthersLeft, waitForCallPickup, sendNotificationType } =
|
const urlParams = useUrlParams();
|
||||||
useUrlParams();
|
const mediaDevices = useMediaDevices();
|
||||||
|
|
||||||
const trackProcessorState$ = useTrackProcessorObservable$();
|
const trackProcessorState$ = useTrackProcessorObservable$();
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const scope = new ObservableScope();
|
const scope = new ObservableScope();
|
||||||
const reactionsReader = new ReactionsReader(scope, props.rtcSession);
|
const reactionsReader = new ReactionsReader(scope, props.rtcSession);
|
||||||
|
const { autoLeaveWhenOthersLeft, waitForCallPickup, sendNotificationType } =
|
||||||
|
urlParams;
|
||||||
const vm = new CallViewModel(
|
const vm = new CallViewModel(
|
||||||
scope,
|
scope,
|
||||||
props.rtcSession,
|
props.rtcSession,
|
||||||
@@ -152,13 +152,11 @@ export const ActiveCall: FC<ActiveCallProps> = (props) => {
|
|||||||
}, [
|
}, [
|
||||||
props.rtcSession,
|
props.rtcSession,
|
||||||
props.matrixRoom,
|
props.matrixRoom,
|
||||||
mediaDevices,
|
|
||||||
props.muteStates,
|
props.muteStates,
|
||||||
props.e2eeSystem,
|
props.e2eeSystem,
|
||||||
autoLeaveWhenOthersLeft,
|
|
||||||
sendNotificationType,
|
|
||||||
waitForCallPickup,
|
|
||||||
props.onLeft,
|
props.onLeft,
|
||||||
|
urlParams,
|
||||||
|
mediaDevices,
|
||||||
trackProcessorState$,
|
trackProcessorState$,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|||||||
@@ -49,66 +49,26 @@ exports[`InCallView > rendering > renders 1`] = `
|
|||||||
</svg>
|
</svg>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div
|
|
||||||
class="participantsLine"
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
aria-label="Participants"
|
|
||||||
fill="currentColor"
|
|
||||||
height="20"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
width="20"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
d="M9.175 13.825Q10.35 15 12 15t2.825-1.175T16 11t-1.175-2.825T12 7 9.175 8.175 8 11t1.175 2.825m4.237-1.412A1.93 1.93 0 0 1 12 13q-.825 0-1.412-.588A1.93 1.93 0 0 1 10 11q0-.825.588-1.412A1.93 1.93 0 0 1 12 9q.825 0 1.412.588Q14 10.175 14 11t-.588 1.412"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
d="M22 12c0 5.523-4.477 10-10 10S2 17.523 2 12 6.477 2 12 2s10 4.477 10 10m-2 0a8 8 0 1 0-16 0 8 8 0 0 0 16 0"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
d="M16.23 18.792a13 13 0 0 0-1.455-.455 11.6 11.6 0 0 0-5.55 0q-.73.18-1.455.455a8 8 0 0 1-1.729-1.454q1.336-.618 2.709-.95A13.8 13.8 0 0 1 12 16q1.65 0 3.25.387 1.373.333 2.709.95a8 8 0 0 1-1.73 1.455"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
<span
|
|
||||||
class="_typography_6v6n8_153 _font-body-sm-medium_6v6n8_41"
|
|
||||||
data-testid="roomHeader_participants_count"
|
|
||||||
>
|
|
||||||
2
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="nav rightNav"
|
class="nav rightNav"
|
||||||
/>
|
/>
|
||||||
</header>
|
</header>
|
||||||
<div>
|
|
||||||
mocked: MatrixAudioRenderer
|
|
||||||
</div>
|
|
||||||
<div
|
<div
|
||||||
class="scrollingGrid grid"
|
class="scrollingGrid grid"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="layer"
|
class="scrolling"
|
||||||
>
|
style="--gap: 20px; --width: NaNpx; --height: NaNpx;"
|
||||||
<div
|
/>
|
||||||
class="container slot"
|
|
||||||
data-id="1"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="slot local slot"
|
|
||||||
data-block-alignment="start"
|
|
||||||
data-id="0"
|
|
||||||
data-inline-alignment="end"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="fixedGrid grid"
|
class="fixedGrid grid"
|
||||||
>
|
>
|
||||||
<div />
|
<div
|
||||||
|
class="fixed"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="bg animate"
|
class="bg animate"
|
||||||
|
|||||||
@@ -311,11 +311,14 @@ export function withCallViewModel(
|
|||||||
public getDeviceId(): string {
|
public getDeviceId(): string {
|
||||||
return localRtcMember.deviceId;
|
return localRtcMember.deviceId;
|
||||||
}
|
}
|
||||||
|
public getDomain(): string {
|
||||||
|
return "example.com";
|
||||||
|
}
|
||||||
public getSyncState(): SyncState {
|
public getSyncState(): SyncState {
|
||||||
return syncState;
|
return syncState;
|
||||||
}
|
}
|
||||||
})() as Partial<MatrixClient> as MatrixClient,
|
})() as Partial<MatrixClient> as MatrixClient,
|
||||||
getMember: (userId) => roomMembers.get(userId) ?? null,
|
getMembers: () => Array.from(roomMembers.values()),
|
||||||
});
|
});
|
||||||
const rtcSession = new MockRTCSession(room, []).withMemberships(rtcMembers$);
|
const rtcSession = new MockRTCSession(room, []).withMemberships(rtcMembers$);
|
||||||
const participantsSpy = vi
|
const participantsSpy = vi
|
||||||
|
|||||||
@@ -367,21 +367,18 @@ describe("Start connection states", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
function fakeRemoteLivekitParticipant(id: string): RemoteParticipant {
|
function fakeRemoteLivekitParticipant(
|
||||||
|
id: string,
|
||||||
|
publications: number = 1,
|
||||||
|
): RemoteParticipant {
|
||||||
return {
|
return {
|
||||||
identity: id,
|
identity: id,
|
||||||
|
getTrackPublications: () => Array(publications),
|
||||||
} as unknown as RemoteParticipant;
|
} as unknown as RemoteParticipant;
|
||||||
}
|
}
|
||||||
|
|
||||||
function fakeRtcMemberShip(userId: string, deviceId: string): CallMembership {
|
|
||||||
return {
|
|
||||||
userId,
|
|
||||||
deviceId,
|
|
||||||
} as unknown as CallMembership;
|
|
||||||
}
|
|
||||||
|
|
||||||
describe("Publishing participants observations", () => {
|
describe("Publishing participants observations", () => {
|
||||||
it("should emit the list of publishing participants", async () => {
|
it("should emit the list of publishing participants", () => {
|
||||||
setupTest();
|
setupTest();
|
||||||
|
|
||||||
const connection = setupRemoteConnection();
|
const connection = setupRemoteConnection();
|
||||||
@@ -406,107 +403,36 @@ describe("Publishing participants observations", () => {
|
|||||||
// on this connection.
|
// on this connection.
|
||||||
|
|
||||||
let participants: RemoteParticipant[] = [
|
let participants: RemoteParticipant[] = [
|
||||||
fakeRemoteLivekitParticipant("@alice:example.org:DEV000"),
|
fakeRemoteLivekitParticipant("@alice:example.org:DEV000", 0),
|
||||||
fakeRemoteLivekitParticipant("@bob:example.org:DEV111"),
|
fakeRemoteLivekitParticipant("@bob:example.org:DEV111", 0),
|
||||||
fakeRemoteLivekitParticipant("@carol:example.org:DEV222"),
|
fakeRemoteLivekitParticipant("@carol:example.org:DEV222", 0),
|
||||||
fakeRemoteLivekitParticipant("@dan:example.org:DEV333"),
|
fakeRemoteLivekitParticipant("@dan:example.org:DEV333", 0),
|
||||||
];
|
];
|
||||||
|
|
||||||
// Let's simulate 3 members on the livekitRoom
|
// Let's simulate 3 members on the livekitRoom
|
||||||
vi.spyOn(fakeLivekitRoom, "remoteParticipants", "get").mockReturnValue(
|
vi.spyOn(fakeLivekitRoom, "remoteParticipants", "get").mockImplementation(
|
||||||
new Map(participants.map((p) => [p.identity, p])),
|
() => new Map(participants.map((p) => [p.identity, p])),
|
||||||
);
|
);
|
||||||
|
|
||||||
for (const participant of participants) {
|
participants.forEach((p) =>
|
||||||
fakeRoomEventEmiter.emit(RoomEvent.ParticipantConnected, participant);
|
fakeRoomEventEmiter.emit(RoomEvent.ParticipantConnected, p),
|
||||||
}
|
);
|
||||||
|
|
||||||
// At this point there should be no publishers
|
// At this point there should be no publishers
|
||||||
expect(observedPublishers.pop()!.length).toEqual(0);
|
expect(observedPublishers.pop()!.length).toEqual(0);
|
||||||
|
|
||||||
const otherFocus: LivekitTransport = {
|
participants = [
|
||||||
livekit_alias: "!roomID:example.org",
|
fakeRemoteLivekitParticipant("@alice:example.org:DEV000", 1),
|
||||||
livekit_service_url: "https://other-matrix-rtc.example.org/livekit/jwt",
|
fakeRemoteLivekitParticipant("@bob:example.org:DEV111", 1),
|
||||||
type: "livekit",
|
fakeRemoteLivekitParticipant("@carol:example.org:DEV222", 1),
|
||||||
};
|
fakeRemoteLivekitParticipant("@dan:example.org:DEV333", 2),
|
||||||
|
|
||||||
const rtcMemberships = [
|
|
||||||
// Say bob is on the same focus
|
|
||||||
{
|
|
||||||
membership: fakeRtcMemberShip("@bob:example.org", "DEV111"),
|
|
||||||
transport: livekitFocus,
|
|
||||||
},
|
|
||||||
// Alice and carol is on a different focus
|
|
||||||
{
|
|
||||||
membership: fakeRtcMemberShip("@alice:example.org", "DEV000"),
|
|
||||||
transport: otherFocus,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
membership: fakeRtcMemberShip("@carol:example.org", "DEV222"),
|
|
||||||
transport: otherFocus,
|
|
||||||
},
|
|
||||||
// NO DAVE YET
|
|
||||||
];
|
];
|
||||||
// signal this change in rtc memberships
|
participants.forEach((p) =>
|
||||||
fakeMembershipsFocusMap$.next(rtcMemberships);
|
fakeRoomEventEmiter.emit(RoomEvent.ParticipantConnected, p),
|
||||||
|
|
||||||
// We should have bob has a publisher now
|
|
||||||
await bobIsAPublisher.promise;
|
|
||||||
const publishers = observedPublishers.pop();
|
|
||||||
expect(publishers?.length).toEqual(1);
|
|
||||||
expect(publishers?.[0].identity).toEqual("@bob:example.org:DEV111");
|
|
||||||
|
|
||||||
// Now let's make dan join the rtc memberships
|
|
||||||
rtcMemberships.push({
|
|
||||||
membership: fakeRtcMemberShip("@dan:example.org", "DEV333"),
|
|
||||||
transport: livekitFocus,
|
|
||||||
});
|
|
||||||
fakeMembershipsFocusMap$.next(rtcMemberships);
|
|
||||||
|
|
||||||
// We should have bob and dan has publishers now
|
|
||||||
await danIsAPublisher.promise;
|
|
||||||
const twoPublishers = observedPublishers.pop();
|
|
||||||
expect(twoPublishers?.length).toEqual(2);
|
|
||||||
expect(
|
|
||||||
twoPublishers?.some((p) => p.identity === "@bob:example.org:DEV111"),
|
|
||||||
).toBeTruthy();
|
|
||||||
expect(
|
|
||||||
twoPublishers?.some((p) => p.identity === "@dan:example.org:DEV333"),
|
|
||||||
).toBeTruthy();
|
|
||||||
|
|
||||||
// Now let's make bob leave the livekit room
|
|
||||||
participants = participants.filter(
|
|
||||||
(p) => p.identity !== "@bob:example.org:DEV111",
|
|
||||||
);
|
|
||||||
vi.spyOn(fakeLivekitRoom, "remoteParticipants", "get").mockReturnValue(
|
|
||||||
new Map(participants.map((p) => [p.identity, p])),
|
|
||||||
);
|
|
||||||
fakeRoomEventEmiter.emit(
|
|
||||||
RoomEvent.ParticipantDisconnected,
|
|
||||||
fakeRemoteLivekitParticipant("@bob:example.org:DEV111"),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// TODO: evaluate this test. It looks like this is not the task of the Connection anymore. Valere?
|
// At this point there should be no publishers
|
||||||
// const updatedPublishers = observedPublishers.pop();
|
expect(observedPublishers.pop()!.length).toEqual(4);
|
||||||
// // Bob is not connected to the room but he is still in the rtc memberships declaring that
|
|
||||||
// // he is using that focus to publish, so he should still appear as a publisher
|
|
||||||
// expect(updatedPublishers?.length).toEqual(2);
|
|
||||||
// const pp = updatedPublishers?.find((p) =>
|
|
||||||
// p.identity.startsWith("@bob:example.org"),
|
|
||||||
// );
|
|
||||||
// expect(pp).toBeDefined();
|
|
||||||
// expect(pp!).not.toBeDefined();
|
|
||||||
// expect(
|
|
||||||
// updatedPublishers?.some(
|
|
||||||
// (p) => p.participant?.identity === "@dan:example.org:DEV333",
|
|
||||||
// ),
|
|
||||||
// ).toBeTruthy();
|
|
||||||
// // Now if bob is not in the rtc memberships, he should disappear
|
|
||||||
// const noBob = rtcMemberships.filter(
|
|
||||||
// ({ membership }) => membership.userId !== "@bob:example.org",
|
|
||||||
// );
|
|
||||||
// fakeMembershipsFocusMap$.next(noBob);
|
|
||||||
// expect(observedPublishers.pop()?.length).toEqual(1);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should be scoped to parent scope", (): void => {
|
it("should be scoped to parent scope", (): void => {
|
||||||
@@ -523,12 +449,12 @@ describe("Publishing participants observations", () => {
|
|||||||
onTestFinished(() => s.unsubscribe());
|
onTestFinished(() => s.unsubscribe());
|
||||||
|
|
||||||
let participants: RemoteParticipant[] = [
|
let participants: RemoteParticipant[] = [
|
||||||
fakeRemoteLivekitParticipant("@bob:example.org:DEV111"),
|
fakeRemoteLivekitParticipant("@bob:example.org:DEV111", 0),
|
||||||
];
|
];
|
||||||
|
|
||||||
// Let's simulate 3 members on the livekitRoom
|
// Let's simulate 3 members on the livekitRoom
|
||||||
vi.spyOn(fakeLivekitRoom, "remoteParticipants", "get").mockReturnValue(
|
vi.spyOn(fakeLivekitRoom, "remoteParticipants", "get").mockImplementation(
|
||||||
new Map(participants.map((p) => [p.identity, p])),
|
() => new Map(participants.map((p) => [p.identity, p])),
|
||||||
);
|
);
|
||||||
|
|
||||||
for (const participant of participants) {
|
for (const participant of participants) {
|
||||||
@@ -538,15 +464,11 @@ describe("Publishing participants observations", () => {
|
|||||||
// At this point there should be no publishers
|
// At this point there should be no publishers
|
||||||
expect(observedPublishers.pop()!.length).toEqual(0);
|
expect(observedPublishers.pop()!.length).toEqual(0);
|
||||||
|
|
||||||
const rtcMemberships = [
|
participants = [fakeRemoteLivekitParticipant("@bob:example.org:DEV111", 1)];
|
||||||
// Say bob is on the same focus
|
|
||||||
{
|
for (const participant of participants) {
|
||||||
membership: fakeRtcMemberShip("@bob:example.org", "DEV111"),
|
fakeRoomEventEmiter.emit(RoomEvent.ParticipantConnected, participant);
|
||||||
transport: livekitFocus,
|
}
|
||||||
},
|
|
||||||
];
|
|
||||||
// signal this change in rtc memberships
|
|
||||||
fakeMembershipsFocusMap$.next(rtcMemberships);
|
|
||||||
|
|
||||||
// We should have bob has a publisher now
|
// We should have bob has a publisher now
|
||||||
const publishers = observedPublishers.pop();
|
const publishers = observedPublishers.pop();
|
||||||
@@ -561,9 +483,7 @@ describe("Publishing participants observations", () => {
|
|||||||
participants = participants.filter(
|
participants = participants.filter(
|
||||||
(p) => p.identity !== "@bob:example.org:DEV111",
|
(p) => p.identity !== "@bob:example.org:DEV111",
|
||||||
);
|
);
|
||||||
vi.spyOn(fakeLivekitRoom, "remoteParticipants", "get").mockReturnValue(
|
|
||||||
new Map(participants.map((p) => [p.identity, p])),
|
|
||||||
);
|
|
||||||
fakeRoomEventEmiter.emit(
|
fakeRoomEventEmiter.emit(
|
||||||
RoomEvent.ParticipantDisconnected,
|
RoomEvent.ParticipantDisconnected,
|
||||||
fakeRemoteLivekitParticipant("@bob:example.org:DEV111"),
|
fakeRemoteLivekitParticipant("@bob:example.org:DEV111"),
|
||||||
|
|||||||
@@ -223,9 +223,10 @@ export class Connection {
|
|||||||
],
|
],
|
||||||
}).pipe(
|
}).pipe(
|
||||||
map((participants) => {
|
map((participants) => {
|
||||||
return participants.filter(
|
const partsFiltered = participants.filter(
|
||||||
(participant) => participant.getTrackPublications().length > 0,
|
(participant) => participant.getTrackPublications().length > 0,
|
||||||
);
|
);
|
||||||
|
return partsFiltered;
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
[],
|
[],
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ beforeEach(() => {
|
|||||||
(transport: LivekitTransport, scope: ObservableScope) => {
|
(transport: LivekitTransport, scope: ObservableScope) => {
|
||||||
const mockConnection = {
|
const mockConnection = {
|
||||||
transport,
|
transport,
|
||||||
participantsWithTrack$: new BehaviorSubject([]),
|
remoteParticipantsWithTracks$: new BehaviorSubject([]),
|
||||||
} as unknown as Connection;
|
} as unknown as Connection;
|
||||||
vi.mocked(mockConnection).start = vi.fn();
|
vi.mocked(mockConnection).start = vi.fn();
|
||||||
vi.mocked(mockConnection).stop = vi.fn();
|
vi.mocked(mockConnection).stop = vi.fn();
|
||||||
@@ -190,7 +190,7 @@ describe("connections$ stream", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe("connectionManagerData$ stream", () => {
|
describe("connectionManagerData$ stream", () => {
|
||||||
// Used in test to control fake connections' participantsWithTrack$ streams
|
// Used in test to control fake connections' remoteParticipantsWithTracks$ streams
|
||||||
let fakePublishingParticipantsStreams: Map<
|
let fakePublishingParticipantsStreams: Map<
|
||||||
string,
|
string,
|
||||||
Behavior<LivekitParticipant[]>
|
Behavior<LivekitParticipant[]>
|
||||||
@@ -222,7 +222,8 @@ describe("connectionManagerData$ stream", () => {
|
|||||||
>([]);
|
>([]);
|
||||||
const mockConnection = {
|
const mockConnection = {
|
||||||
transport,
|
transport,
|
||||||
participantsWithTrack$: getPublishingParticipantsFor(transport),
|
remoteParticipantsWithTracks$:
|
||||||
|
getPublishingParticipantsFor(transport),
|
||||||
} as unknown as Connection;
|
} as unknown as Connection;
|
||||||
vi.mocked(mockConnection).start = vi.fn();
|
vi.mocked(mockConnection).start = vi.fn();
|
||||||
vi.mocked(mockConnection).stop = vi.fn();
|
vi.mocked(mockConnection).stop = vi.fn();
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ Please see LICENSE in the repository root for full details.
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { describe, expect, it } from "vitest";
|
import { describe, expect, it } from "vitest";
|
||||||
import { BehaviorSubject, combineLatest, timer } from "rxjs";
|
import { BehaviorSubject, combineLatest, of, Subject } from "rxjs";
|
||||||
import { logger } from "matrix-js-sdk/lib/logger";
|
import { logger } from "matrix-js-sdk/lib/logger";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -60,19 +60,6 @@ describe("Epoch", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
it("obs", () => {
|
|
||||||
const nothing = Symbol("nothing");
|
|
||||||
const scope = new ObservableScope();
|
|
||||||
const sb$ = new BehaviorSubject("initial");
|
|
||||||
const su$ = new BehaviorSubject(undefined);
|
|
||||||
expect(sb$.value).toBe("initial");
|
|
||||||
expect(su$.value).toBe(undefined);
|
|
||||||
expect(su$.value === nothing).toBe(false);
|
|
||||||
|
|
||||||
const a$ = timer(10);
|
|
||||||
|
|
||||||
scope.behavior(a$, undefined);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("diamonds emits in a predictable order", () => {
|
it("diamonds emits in a predictable order", () => {
|
||||||
const sb$ = new BehaviorSubject("initial");
|
const sb$ = new BehaviorSubject("initial");
|
||||||
@@ -97,4 +84,21 @@ describe("Epoch", () => {
|
|||||||
sb$.next("updated");
|
sb$.next("updated");
|
||||||
sb$.next("ANOTERUPDATE");
|
sb$.next("ANOTERUPDATE");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("behavior test", () => {
|
||||||
|
const scope = new ObservableScope();
|
||||||
|
const s$ = new Subject();
|
||||||
|
const behavior$ = scope.behavior(s$, 0);
|
||||||
|
behavior$.subscribe((value) => {
|
||||||
|
logger.log(`Received value: ${value}`);
|
||||||
|
});
|
||||||
|
s$.next(1);
|
||||||
|
s$.next(2);
|
||||||
|
s$.next(3);
|
||||||
|
s$.next(3);
|
||||||
|
s$.next(3);
|
||||||
|
s$.next(3);
|
||||||
|
s$.next(3);
|
||||||
|
s$.complete();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import {
|
|||||||
} from "rxjs";
|
} from "rxjs";
|
||||||
|
|
||||||
import { type Behavior } from "./Behavior";
|
import { type Behavior } from "./Behavior";
|
||||||
|
import { logger } from "matrix-js-sdk/lib/logger";
|
||||||
|
|
||||||
type MonoTypeOperator = <T>(o: Observable<T>) => Observable<T>;
|
type MonoTypeOperator = <T>(o: Observable<T>) => Observable<T>;
|
||||||
|
|
||||||
@@ -73,14 +74,22 @@ export class ObservableScope {
|
|||||||
// they will no longer re-emit their current value upon subscription. We want
|
// they will no longer re-emit their current value upon subscription. We want
|
||||||
// to support Observables that complete (for example `of({})`), so we have to
|
// to support Observables that complete (for example `of({})`), so we have to
|
||||||
// take care to not propagate the completion event.
|
// take care to not propagate the completion event.
|
||||||
setValue$.pipe(this.bind(), distinctUntilChanged()).subscribe({
|
setValue$
|
||||||
next(value) {
|
.pipe(
|
||||||
subject$.next(value);
|
this.bind(),
|
||||||
},
|
distinctUntilChanged((a, b) => {
|
||||||
error(err: unknown) {
|
logger.log("distinctUntilChanged", a, b);
|
||||||
subject$.error(err);
|
return a === b;
|
||||||
},
|
}),
|
||||||
});
|
)
|
||||||
|
.subscribe({
|
||||||
|
next(value) {
|
||||||
|
subject$.next(value);
|
||||||
|
},
|
||||||
|
error(err: unknown) {
|
||||||
|
subject$.error(err);
|
||||||
|
},
|
||||||
|
});
|
||||||
if (subject$.value === nothing)
|
if (subject$.value === nothing)
|
||||||
throw new Error("Behavior failed to synchronously emit an initial value");
|
throw new Error("Behavior failed to synchronously emit an initial value");
|
||||||
return subject$ as Behavior<T>;
|
return subject$ as Behavior<T>;
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import {
|
|||||||
type CallViewModelOptions,
|
type CallViewModelOptions,
|
||||||
} from "../state/CallViewModel/CallViewModel";
|
} from "../state/CallViewModel/CallViewModel";
|
||||||
import {
|
import {
|
||||||
|
mockConfig,
|
||||||
mockLivekitRoom,
|
mockLivekitRoom,
|
||||||
mockLocalParticipant,
|
mockLocalParticipant,
|
||||||
mockMatrixRoom,
|
mockMatrixRoom,
|
||||||
@@ -36,6 +37,8 @@ import { aliceRtcMember, localRtcMember } from "./test-fixtures";
|
|||||||
import { type RaisedHandInfo, type ReactionInfo } from "../reactions";
|
import { type RaisedHandInfo, type ReactionInfo } from "../reactions";
|
||||||
import { constant } from "../state/Behavior";
|
import { constant } from "../state/Behavior";
|
||||||
|
|
||||||
|
mockConfig({ livekit: { livekit_service_url: "https://example.com" } });
|
||||||
|
|
||||||
export function getBasicRTCSession(
|
export function getBasicRTCSession(
|
||||||
members: RoomMember[],
|
members: RoomMember[],
|
||||||
initialRtcMemberships: CallMembership[] = [localRtcMember, aliceRtcMember],
|
initialRtcMemberships: CallMembership[] = [localRtcMember, aliceRtcMember],
|
||||||
@@ -57,6 +60,7 @@ export function getBasicRTCSession(
|
|||||||
getUserId: () => localRtcMember.userId,
|
getUserId: () => localRtcMember.userId,
|
||||||
getDeviceId: () => localRtcMember.deviceId,
|
getDeviceId: () => localRtcMember.deviceId,
|
||||||
getSyncState: () => SyncState.Syncing,
|
getSyncState: () => SyncState.Syncing,
|
||||||
|
getDomain: () => null,
|
||||||
sendEvent: vitest.fn().mockResolvedValue({ event_id: "$fake:event" }),
|
sendEvent: vitest.fn().mockResolvedValue({ event_id: "$fake:event" }),
|
||||||
redactEvent: vitest.fn().mockResolvedValue({ event_id: "$fake:event" }),
|
redactEvent: vitest.fn().mockResolvedValue({ event_id: "$fake:event" }),
|
||||||
decryptEventIfNeeded: vitest.fn().mockResolvedValue(undefined),
|
decryptEventIfNeeded: vitest.fn().mockResolvedValue(undefined),
|
||||||
@@ -78,6 +82,7 @@ export function getBasicRTCSession(
|
|||||||
),
|
),
|
||||||
} as Partial<MatrixClient> as MatrixClient,
|
} as Partial<MatrixClient> as MatrixClient,
|
||||||
getMember: (userId) => matrixRoomMembers.get(userId) ?? null,
|
getMember: (userId) => matrixRoomMembers.get(userId) ?? null,
|
||||||
|
getMembers: () => Array.from(matrixRoomMembers.values()),
|
||||||
roomId: matrixRoomId,
|
roomId: matrixRoomId,
|
||||||
on: vitest
|
on: vitest
|
||||||
.fn()
|
.fn()
|
||||||
|
|||||||
Reference in New Issue
Block a user