oficina / meet Public

Clone
git clone https://amadan.net/oficina/meet

Plain git — no account needed to clone.

Download

Download this file

1// room-space.test.mjs — Space toggles mute in the room, gated without a
2// browser.
3//
4// node --test test/browser/room-space.test.mjs
5//
6// Same trick as room-mute.test.mjs: the functions are lifted out of the
7// shipped room.js by name, so a rename fails here loudly instead of
8// passing against a copy.
9//
10// spaceKeyAction 'toggle', 'swallow' for a repeat, or '' when the key
11// is not ours
12// spaceBelongsTo whether Space already means something to the
13// focused element (typing, activation, opening)
14import { test } from 'node:test';
15import assert from 'node:assert/strict';
16import { readFileSync } from 'node:fs';
17import { fileURLToPath } from 'node:url';
18
19const roomJS = fileURLToPath(new URL('../../internal/calls/static/room.js', import.meta.url));
20const src = readFileSync(roomJS, 'utf8');
21
22function lift(source, name) {
23 const start = source.indexOf('function ' + name + '(');
24 assert.notEqual(start, -1, name + ' is not in room.js any more — this test is stale, not passing');
25 let depth = 0;
26 for (let i = source.indexOf('{', start); i < source.length; i++) {
27 if (source[i] === '{') depth += 1;
28 else if (source[i] === '}') {
29 depth -= 1;
30 if (depth === 0) return source.slice(start, i + 1);
31 }
32 }
33 assert.fail('unbalanced braces reading ' + name + ' out of room.js');
34}
35
36// eslint-disable-next-line no-new-func
37const spaceKeyAction = new Function(
38 lift(src, 'spaceBelongsTo') + '\n' + lift(src, 'spaceKeyAction') + '; return spaceKeyAction;',
39)();
40
41// A stand-in for a focused element: a tag, attributes, and whether it is
42// editable, which is all the rules read.
43function el(tagName, attrs = {}, isContentEditable = false) {
44 return {
45 tagName,
46 isContentEditable,
47 hasAttribute: (name) => Object.prototype.hasOwnProperty.call(attrs, name),
48 getAttribute: (name) => (Object.prototype.hasOwnProperty.call(attrs, name) ? attrs[name] : null),
49 };
50}
51
52const space = (extra = {}) => ({ key: ' ', code: 'Space', repeat: false, ...extra });
53const body = el('BODY');
54
55test('Space on the page toggles mute', () => {
56 assert.equal(spaceKeyAction(space(), body), 'toggle');
57 assert.equal(spaceKeyAction(space(), el('DIV')), 'toggle');
58 assert.equal(spaceKeyAction(space(), el('VIDEO')), 'toggle', 'a tile is not a control');
59 assert.equal(spaceKeyAction(space(), null), 'toggle');
60 // Either half of the key's identity is enough: some layouts report a
61 // different key for the physical Space bar, and some synthetic events
62 // carry no code.
63 assert.equal(spaceKeyAction({ key: ' ', repeat: false }, body), 'toggle');
64 assert.equal(spaceKeyAction({ key: 'Unidentified', code: 'Space', repeat: false }, body), 'toggle');
65});
66
67test('a held Space is handled once, and its repeats are swallowed', () => {
68 assert.equal(spaceKeyAction(space({ repeat: true }), body), 'swallow');
69});
70
71test('other keys and modified Space are not ours', () => {
72 assert.equal(spaceKeyAction({ key: 'm', code: 'KeyM', repeat: false }, body), '');
73 assert.equal(spaceKeyAction({ key: 'Enter', code: 'Enter', repeat: false }, body), '');
74 for (const mod of ['ctrlKey', 'metaKey', 'altKey', 'shiftKey']) {
75 assert.equal(spaceKeyAction(space({ [mod]: true }), body), '', mod + '+Space is a shortcut of its own');
76 }
77 assert.equal(spaceKeyAction(space({ isComposing: true }), body), '', 'an input method composing text owns it');
78 assert.equal(spaceKeyAction(undefined, body), '');
79});
80
81test('Space is typing in every text field, including the chat composer', () => {
82 for (const target of [
83 el('INPUT', { type: 'text' }),
84 el('INPUT'),
85 el('TEXTAREA', { id: 'chat-text' }),
86 el('textarea'),
87 el('DIV', {}, true), // contenteditable, or inside one
88 el('DIV', { role: 'textbox' }),
89 el('INPUT', { type: 'search' }),
90 ]) {
91 assert.equal(spaceKeyAction(space(), target), '', JSON.stringify(target.tagName));
92 assert.equal(spaceKeyAction(space({ repeat: true }), target), '', 'repeats in a field are typing too');
93 }
94});
95
96test('Space is activation on buttons, links and other controls', () => {
97 for (const target of [
98 el('BUTTON', { id: 'mute' }),
99 el('BUTTON', { id: 'leave' }),
100 el('INPUT', { type: 'checkbox' }),
101 el('INPUT', { type: 'button' }),
102 el('SELECT'),
103 el('SUMMARY'),
104 el('A', { href: '/m/abc' }),
105 el('VIDEO', { controls: '' }),
106 el('DIV', { role: 'button' }),
107 el('SPAN', { role: ' Switch ' }),
108 el('LI', { role: 'menuitem' }),
109 el('DIV', { role: 'slider' }),
110 ]) {
111 assert.equal(spaceKeyAction(space(), target), '', target.tagName + ' ' + JSON.stringify(target.getAttribute('role')));
112 }
113 // An anchor with no href is not a link and does nothing with Space.
114 assert.equal(spaceKeyAction(space(), el('A')), 'toggle');
115 // A role that is not a control is not claimed.
116 assert.equal(spaceKeyAction(space(), el('DIV', { role: 'region' })), 'toggle');
117});
118