Fix bugs in waiting room.
This commit is contained in:
+3
-1
@@ -1201,6 +1201,7 @@ type Status struct {
|
|||||||
AuthServer string `json:"authServer,omitempty"`
|
AuthServer string `json:"authServer,omitempty"`
|
||||||
AuthPortal string `json:"authPortal,omitempty"`
|
AuthPortal string `json:"authPortal,omitempty"`
|
||||||
Locked bool `json:"locked,omitempty"`
|
Locked bool `json:"locked,omitempty"`
|
||||||
|
WaitingRoom bool `json:"waitingRoom,omitempty"`
|
||||||
Recording bool `json:"recording,omitempty"`
|
Recording bool `json:"recording,omitempty"`
|
||||||
ClientCount *int `json:"clientCount,omitempty"`
|
ClientCount *int `json:"clientCount,omitempty"`
|
||||||
CanChangePassword bool `json:"canChangePassword,omitempty"`
|
CanChangePassword bool `json:"canChangePassword,omitempty"`
|
||||||
@@ -1255,9 +1256,10 @@ func (g *Hall) Status(authentified bool, base *url.URL) Status {
|
|||||||
|
|
||||||
if authentified || desc.Public {
|
if authentified || desc.Public {
|
||||||
// these are considered private information
|
// these are considered private information
|
||||||
locked, _ := g.Locked()
|
locked, hasOperator := g.WaitingStatus()
|
||||||
count := g.ClientCount()
|
count := g.ClientCount()
|
||||||
d.Locked = locked
|
d.Locked = locked
|
||||||
|
d.WaitingRoom = locked && hasOperator
|
||||||
d.ClientCount = &count
|
d.ClientCount = &count
|
||||||
}
|
}
|
||||||
if authentified {
|
if authentified {
|
||||||
|
|||||||
@@ -296,6 +296,36 @@ func TestScheduledUnlockCompletesWhileIssuerIsOperator(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestScheduledUnlockTimerCompletesWhileIssuerIsOperator(t *testing.T) {
|
||||||
|
g, hallName := newWaitingTestHall(t, 0)
|
||||||
|
operator := addTestWebClient(t, hallName, "Operator", "op")
|
||||||
|
g.SetLocked(true, "")
|
||||||
|
waiter := joinWaitingTestClient(t, hallName, "Alice")
|
||||||
|
if err := updateWaitingPreference(waiter, true); err != nil {
|
||||||
|
t.Fatalf("enable automatic waiting-room admission: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
now := time.Now()
|
||||||
|
if err := scheduleHallUnlock(g, operator, now.Add(20*time.Millisecond), now); err != nil {
|
||||||
|
t.Fatalf("scheduleHallUnlock: %v", err)
|
||||||
|
}
|
||||||
|
deadline := time.Now().Add(time.Second)
|
||||||
|
for {
|
||||||
|
locked, _ := g.Locked()
|
||||||
|
if !locked && waitingClientHall(waiter) == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if time.Now().After(deadline) {
|
||||||
|
t.Fatal("scheduled timer left hall locked while issuing operator was present")
|
||||||
|
}
|
||||||
|
time.Sleep(time.Millisecond)
|
||||||
|
}
|
||||||
|
drainActions(t, waiter)
|
||||||
|
if waiter.hall != g || waiter.waiting != nil {
|
||||||
|
t.Fatalf("waiting user was not admitted after scheduled unlock: hall=%v waiting=%v", waiter.hall, waiter.waiting)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestScheduledUnlockRequiresOperatorPermissionAtDeadline(t *testing.T) {
|
func TestScheduledUnlockRequiresOperatorPermissionAtDeadline(t *testing.T) {
|
||||||
hallName := newChalkboardTestHall(t)
|
hallName := newChalkboardTestHall(t)
|
||||||
operator := addTestWebClient(t, hallName, "Operator", "op")
|
operator := addTestWebClient(t, hallName, "Operator", "op")
|
||||||
|
|||||||
@@ -70,6 +70,9 @@ func TestWaitingClientAuthenticatedWithoutMembership(t *testing.T) {
|
|||||||
drainActions(t, operator)
|
drainActions(t, operator)
|
||||||
|
|
||||||
waiter := joinWaitingTestClient(t, name, "Alice")
|
waiter := joinWaitingTestClient(t, name, "Alice")
|
||||||
|
if status := g.Status(true, nil); !status.WaitingRoom {
|
||||||
|
t.Fatalf("locked hall with operator did not report waiting-room availability: %#v", status)
|
||||||
|
}
|
||||||
if waiter.hall != nil || waiter.waiting == nil {
|
if waiter.hall != nil || waiter.waiting == nil {
|
||||||
t.Fatalf("waiting client membership: hall=%v waiting=%v", waiter.hall, waiter.waiting)
|
t.Fatalf("waiting client membership: hall=%v waiting=%v", waiter.hall, waiter.waiting)
|
||||||
}
|
}
|
||||||
@@ -108,6 +111,25 @@ func TestWaitingClientAuthenticatedWithoutMembership(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestWaitingClientLivenessMessagesDoNotReportAdmissionError(t *testing.T) {
|
||||||
|
g, name := newWaitingTestHall(t, 0)
|
||||||
|
operator := addTestWebClient(t, name, "Operator", "op")
|
||||||
|
g.SetLocked(true, "")
|
||||||
|
drainActions(t, operator)
|
||||||
|
|
||||||
|
waiter := joinWaitingTestClient(t, name, "Alice")
|
||||||
|
for _, messageType := range []string{"ping", "pong"} {
|
||||||
|
if err := handleClientMessage(waiter, clientMessage{Type: messageType}); err != nil {
|
||||||
|
t.Fatalf("waiting %s: %v", messageType, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, message := range drainMessages(waiter) {
|
||||||
|
if message.Type == "usermessage" && message.Kind == "error" {
|
||||||
|
t.Fatalf("waiting liveness message produced error: %#v", message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestWaitingManualAdmissionAndLastOperatorCancellation(t *testing.T) {
|
func TestWaitingManualAdmissionAndLastOperatorCancellation(t *testing.T) {
|
||||||
g, name := newWaitingTestHall(t, 0)
|
g, name := newWaitingTestHall(t, 0)
|
||||||
operator := addTestWebClient(t, name, "Operator", "op")
|
operator := addTestWebClient(t, name, "Operator", "op")
|
||||||
|
|||||||
@@ -1467,7 +1467,7 @@ func handleClientMessage(c *webClient, m clientMessage) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if waitingClientHall(c) != nil && m.Type != "waiting" && m.Type != "join" && m.Type != "pong" {
|
if waitingClientHall(c) != nil && m.Type != "waiting" && m.Type != "join" && m.Type != "ping" && m.Type != "pong" {
|
||||||
return c.error(hall.UserError("you have not been admitted to the hall"))
|
return c.error(hall.UserError("you have not been admitted to the hall"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ the following fields:
|
|||||||
- `authServer`: the URL of the authentication server, if any;
|
- `authServer`: the URL of the authentication server, if any;
|
||||||
- `authPortal`: the uRL of the authentication portal, if any;
|
- `authPortal`: the uRL of the authentication portal, if any;
|
||||||
- `locked`: true if the hall is locked;
|
- `locked`: true if the hall is locked;
|
||||||
|
- `waitingRoom`: true if the hall is locked and its waiting room is available;
|
||||||
- `clientCount`: the number of clients currently in the hall.
|
- `clientCount`: the number of clients currently in the hall.
|
||||||
|
|
||||||
All fields are optional except `name`, `location` and `endpoint`.
|
All fields are optional except `name`, `location` and `endpoint`.
|
||||||
|
|||||||
@@ -4386,6 +4386,8 @@ async function start() {
|
|||||||
if(window.location.search)
|
if(window.location.search)
|
||||||
window.history.replaceState(null, '', window.location.pathname);
|
window.history.replaceState(null, '', window.location.pathname);
|
||||||
setTitle(hallStatus.displayName || capitalise(hall));
|
setTitle(hallStatus.displayName || capitalise(hall));
|
||||||
|
getInputElement('connectbutton').value = hallStatus.waitingRoom ?
|
||||||
|
'Join waiting room' : 'Connect';
|
||||||
|
|
||||||
await setMediaChoices(false);
|
await setMediaChoices(false);
|
||||||
reflectSettings();
|
reflectSettings();
|
||||||
|
|||||||
@@ -156,6 +156,8 @@ func TestWaitingRoomUsesAccessibleNativeControlsAndLiveRegions(t *testing.T) {
|
|||||||
requireContains(t, html, `id="waiting-leave" type="button"`, "leave button")
|
requireContains(t, html, `id="waiting-leave" type="button"`, "leave button")
|
||||||
requireContains(t, html, `aria-labelledby="waiting-list-heading"`, "operator waiting list")
|
requireContains(t, html, `aria-labelledby="waiting-list-heading"`, "operator waiting list")
|
||||||
requireFunctionContains(t, js, "showWaitingRoom", "waiting-heading').focus")
|
requireFunctionContains(t, js, "showWaitingRoom", "waiting-heading').focus")
|
||||||
|
requireContains(t, js, "hallStatus.waitingRoom ?", "waiting-room submit label condition")
|
||||||
|
requireContains(t, js, "'Join waiting room' : 'Connect'", "waiting-room submit label")
|
||||||
requireFunctionContains(t, js, "updateWaitingStatus", "Math.ceil")
|
requireFunctionContains(t, js, "updateWaitingStatus", "Math.ceil")
|
||||||
requireFunctionContains(t, js, "updateWaitingStatus", "announceChat(status)")
|
requireFunctionContains(t, js, "updateWaitingStatus", "announceChat(status)")
|
||||||
requireFunctionContains(t, js, "gotWaiting", "announceUrgent")
|
requireFunctionContains(t, js, "gotWaiting", "announceUrgent")
|
||||||
|
|||||||
Reference in New Issue
Block a user