Repair the gates the rename merge left pointing at nothing
Three failures, one cause each, all of them silent in the sense that mattered: the gate went red or green for reasons that had nothing to do with the code under it. Every test/browser/*.test.mjs read its subject from internal/meet/static, which has not existed since the rename. The rename branch had fixed all six; the merge into main resolved those files to main's side and took the old paths back. That is the whole web gate — each node test dies on the open before Playwright is reached, so `make browser` could not run at all. native-auth.mjs filled input[name="address"] on whatever page the target landed on. Since guest joining, an invitation lands on guest.html — join in the app, a name form, a link to sign in — and that page has no address field, so the guest scenario spent thirty seconds waiting for one. It now follows the sign-in link when that is the page it got, and goes straight to the field when it is already there. The native gate's guest case then got further and stopped on an Admit button that was never coming: meeting access defaults ship the waiting room off, so a guest following a link is admitted at once and no queue is drawn. A scenario whose subject IS the queue has to ask for one, or it quietly stops testing the thing it is named after. rig.enableWaitingRoom turns it on, and deliberately does not use http.DefaultClient — that follows the 303 to the settings page and hands back its 200, which makes a refused save and an accepted one look identical. Gates: make ci, make native-test, make native-browser and make browser all green, the last two including both guest cases. Run make ci with GOTOOLCHAIN=go1.26.5; under any other toolchain the credits test fails on runtime.Version() and reports it as a credits problem, which it is not.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
9 files changed,
+62
−9
internal/callstest/browser_test.go+37 −0internal/callstest/native_browser_test.go+4 −0test/browser/audio.test.mjs+2 −2test/browser/chat.test.mjs+1 −1test/browser/devices.test.mjs+2 −2test/browser/native-auth.mjs+13 −1test/browser/negotiation.test.mjs+1 −1test/browser/room-lane.test.mjs+1 −1test/browser/room-share.test.mjs+1 −1
diff --git a/internal/callstest/browser_test.go b/internal/callstest/browser_test.go| index f3e6777..d139c84 100644 |
| --- a/internal/callstest/browser_test.go |
| +++ b/internal/callstest/browser_test.go |
| @@ -506,6 +506,43 @@ func (rig *browserRig) signIn(t *testing.T, address string) (name, value string) |
| var sqidOnPage = regexp.MustCompile(`/m/([23456789a-z]{10})/join`) |
| var tokenOnPage = regexp.MustCompile(`/j/([A-Za-z0-9_-]{43})`) |
| +// enableWaitingRoom turns one meeting's waiting room on, as member A. |
| +// |
| +// It exists because the waiting room is no longer the default: meeting |
| +// access defaults ship it off, so a guest following a link is admitted |
| +// straight away and no admission queue is ever drawn. A scenario whose |
| +// subject IS the queue has to ask for one, or it silently stops testing |
| +// what it is named after — which is how the native gate's guest case came |
| +// to wait thirty seconds for an Admit button that was never going to be |
| +// there. |
| +func (rig *browserRig) enableWaitingRoom(t *testing.T, sqid string) { |
| + t.Helper() |
| + req, err := http.NewRequest(http.MethodPost, rig.origin+"/meetings/"+sqid+"/settings", |
| + strings.NewReader(url.Values{"waiting_room": {"on"}}.Encode())) |
| + if err != nil { |
| + t.Fatalf("build settings request: %v", err) |
| + } |
| + req.Header.Set("Content-Type", "application/x-www-form-urlencoded") |
| + req.Header.Set("Origin", rig.origin) |
| + req.AddCookie(&http.Cookie{Name: rig.cookieName, Value: rig.cookieA}) |
| + // Not http.DefaultClient: it follows the 303 to the settings page and |
| + // hands back that page's 200, so a refused save and an accepted one |
| + // look identical. The redirect itself is the only honest evidence the |
| + // handler accepted this. |
| + client := &http.Client{CheckRedirect: func(*http.Request, []*http.Request) error { |
| + return http.ErrUseLastResponse |
| + }} |
| + res, err := client.Do(req) |
| + if err != nil { |
| + t.Fatalf("POST meeting settings: %v", err) |
| + } |
| + defer res.Body.Close() |
| + if res.StatusCode != http.StatusSeeOther { |
| + body, _ := io.ReadAll(res.Body) |
| + t.Fatalf("POST meeting settings: %d\n%s", res.StatusCode, body) |
| + } |
| +} |
| + |
| // createMeeting is member A starting a call and reading the link off the |
| // page it is shown on exactly once. |
| func (rig *browserRig) createMeeting(t *testing.T) (sqid, token string) { |
diff --git a/internal/callstest/native_browser_test.go b/internal/callstest/native_browser_test.go| index 2f4d206..51d90e9 100644 |
| --- a/internal/callstest/native_browser_test.go |
| +++ b/internal/callstest/native_browser_test.go |
| @@ -36,6 +36,10 @@ func nativeMacCallsBrowser(t *testing.T, guest, chat bool) { |
| if guest { |
| mode = "guest" |
| rig.ctl.SetAllowlist(memberA) |
| + // This scenario's subject is the admission queue: a guest joins |
| + // from the app and a member admits them from the browser. Access |
| + // defaults ship the waiting room off, so it has to be asked for. |
| + rig.enableWaitingRoom(t, room) |
| req, err := http.NewRequest("GET", rig.origin+"/j/"+token, nil) |
| if err != nil { |
| t.Fatal(err) |
diff --git a/test/browser/audio.test.mjs b/test/browser/audio.test.mjs| index fe83d1e..328a5a5 100644 |
| --- a/test/browser/audio.test.mjs |
| +++ b/test/browser/audio.test.mjs |
| @@ -21,8 +21,8 @@ import assert from 'node:assert/strict'; |
| import { readFileSync } from 'node:fs'; |
| import { fileURLToPath } from 'node:url'; |
| -const devicesJS = fileURLToPath(new URL('../../internal/meet/static/devices.js', import.meta.url)); |
| -const roomJS = fileURLToPath(new URL('../../internal/meet/static/room.js', import.meta.url)); |
| +const devicesJS = fileURLToPath(new URL('../../internal/calls/static/devices.js', import.meta.url)); |
| +const roomJS = fileURLToPath(new URL('../../internal/calls/static/room.js', import.meta.url)); |
| // lift returns the source of one `function name(...) {...}` by matching |
| // braces from its declaration — negotiation.test.mjs's helper, and it |
diff --git a/test/browser/chat.test.mjs b/test/browser/chat.test.mjs| index e0f5a0f..75d7973 100644 |
| --- a/test/browser/chat.test.mjs |
| +++ b/test/browser/chat.test.mjs |
| @@ -33,7 +33,7 @@ import assert from 'node:assert/strict'; |
| import { readFileSync } from 'node:fs'; |
| import { fileURLToPath } from 'node:url'; |
| -const chatJS = fileURLToPath(new URL('../../internal/meet/static/chat.js', import.meta.url)); |
| +const chatJS = fileURLToPath(new URL('../../internal/calls/static/chat.js', import.meta.url)); |
| const src = readFileSync(chatJS, 'utf8'); |
| // lift returns the source of one `function name(...) {...}` by matching |
diff --git a/test/browser/devices.test.mjs b/test/browser/devices.test.mjs| index 6b6c4bc..dcf8873 100644 |
| --- a/test/browser/devices.test.mjs |
| +++ b/test/browser/devices.test.mjs |
| @@ -20,8 +20,8 @@ import assert from 'node:assert/strict'; |
| import { readFileSync } from 'node:fs'; |
| import { fileURLToPath } from 'node:url'; |
| -const devicesJS = fileURLToPath(new URL('../../internal/meet/static/devices.js', import.meta.url)); |
| -const roomJS = fileURLToPath(new URL('../../internal/meet/static/room.js', import.meta.url)); |
| +const devicesJS = fileURLToPath(new URL('../../internal/calls/static/devices.js', import.meta.url)); |
| +const roomJS = fileURLToPath(new URL('../../internal/calls/static/room.js', import.meta.url)); |
| function lift(source, name, where) { |
| let start = source.indexOf('function ' + name + '('); |
diff --git a/test/browser/native-auth.mjs b/test/browser/native-auth.mjs| index f1c648c..25d2624 100644 |
| --- a/test/browser/native-auth.mjs |
| +++ b/test/browser/native-auth.mjs |
| @@ -11,7 +11,19 @@ try { |
| const page = await context.newPage(); |
| await page.goto(config.url); |
| process.stdout.write("stage:signin\n"); |
| - await page.locator('input[name="address"]').fill(config.address); |
| + // A member's target lands on the sign-in page. An invitation now lands on |
| + // the guest screen instead (guest.html, added with guest joining): join in |
| + // the app, a name form to continue in the browser, and a link to sign in. |
| + // Only the sign-in page carries the address field, so follow the link when |
| + // that is what this target produced. |
| + const address = page.locator('input[name="address"]'); |
| + if ((await address.count()) === 0) { |
| + await Promise.all([ |
| + page.waitForURL('**/signin*'), |
| + page.getByRole('link', { name: 'sign in', exact: true }).click(), |
| + ]); |
| + } |
| + await address.fill(config.address); |
| await Promise.all([ |
| page.waitForURL('**/signin?sent=1'), |
| page.locator('form[action="/signin"] button[type="submit"]').click(), |
diff --git a/test/browser/negotiation.test.mjs b/test/browser/negotiation.test.mjs| index 5097f5f..79a7aec 100644 |
| --- a/test/browser/negotiation.test.mjs |
| +++ b/test/browser/negotiation.test.mjs |
| @@ -25,7 +25,7 @@ import assert from 'node:assert/strict'; |
| import { readFileSync } from 'node:fs'; |
| import { fileURLToPath } from 'node:url'; |
| -const roomJS = fileURLToPath(new URL('../../internal/meet/static/room.js', import.meta.url)); |
| +const roomJS = fileURLToPath(new URL('../../internal/calls/static/room.js', import.meta.url)); |
| // lift returns the source of one top-level-ish `function name(...) {...}` |
| // by matching braces from its declaration. |
diff --git a/test/browser/room-lane.test.mjs b/test/browser/room-lane.test.mjs| index 68dd7fe..345b452 100644 |
| --- a/test/browser/room-lane.test.mjs |
| +++ b/test/browser/room-lane.test.mjs |
| @@ -21,7 +21,7 @@ import assert from 'node:assert/strict'; |
| import { readFileSync } from 'node:fs'; |
| import { fileURLToPath } from 'node:url'; |
| -const roomJS = fileURLToPath(new URL('../../internal/meet/static/room.js', import.meta.url)); |
| +const roomJS = fileURLToPath(new URL('../../internal/calls/static/room.js', import.meta.url)); |
| const src = readFileSync(roomJS, 'utf8'); |
| function lift(source, name) { |
diff --git a/test/browser/room-share.test.mjs b/test/browser/room-share.test.mjs| index 9b257db..aa20f7a 100644 |
| --- a/test/browser/room-share.test.mjs |
| +++ b/test/browser/room-share.test.mjs |
| @@ -30,7 +30,7 @@ import assert from 'node:assert/strict'; |
| import { readFileSync } from 'node:fs'; |
| import { fileURLToPath } from 'node:url'; |
| -const roomJS = fileURLToPath(new URL('../../internal/meet/static/room.js', import.meta.url)); |
| +const roomJS = fileURLToPath(new URL('../../internal/calls/static/room.js', import.meta.url)); |
| const src = readFileSync(roomJS, 'utf8'); |
| // lift returns the source of one `function name(...) {...}` by matching |