harness: Soul's s2 scenarios (unresolved root, slow DM and room joins, tap-target inventory)
This commit is contained in:
parent
a058d2a2c6
commit
0091e5e90d
3 changed files with 123 additions and 116 deletions
|
|
@ -1,79 +1,82 @@
|
|||
// Drives the built client against fakehs.js on a phone viewport and asserts the estate navigator.
|
||||
const { chromium } = require('playwright');
|
||||
const fs = require('fs');
|
||||
const out = (n) => `/out/${n}`;
|
||||
const TAG = process.env.TAG;
|
||||
const out = (n) => `/out/${TAG}-${n}`;
|
||||
const tl = [];
|
||||
const fails = [];
|
||||
const check = (ok, what) => { tl.push(`${ok ? 'PASS' : 'FAIL'} ${what}`); if (!ok) fails.push(what); };
|
||||
const T0 = Date.now();
|
||||
const t = () => `+${Date.now() - T0}ms`;
|
||||
const HEADERS = ['GameCult', 'Projects', 'nibu', 'eureka', 'Nibu', 'Eureka', 'Elsewhere', 'Direct Messages'];
|
||||
(async () => {
|
||||
const b = await chromium.launch();
|
||||
const ctx = await b.newContext({ viewport: { width: 390, height: 844 }, isMobile: true, hasTouch: true, deviceScaleFactor: 1 });
|
||||
const pg = await ctx.newPage();
|
||||
pg.on('console', (m) => { if (m.type() === 'error' && !/OIDC|issuer|endpoint|supported|ERR_NAME|404/.test(m.text())) tl.push('console.error ' + m.text().slice(0, 200)); });
|
||||
await pg.goto('http://127.0.0.1:8080/');
|
||||
await pg.evaluate(() => { localStorage.setItem('cinny_access_token', 'tok'); localStorage.setItem('cinny_device_id', 'DEV'); localStorage.setItem('cinny_user_id', '@t:test'); localStorage.setItem('cinny_hs_base_url', 'http://127.0.0.1:8080'); });
|
||||
await pg.goto('http://127.0.0.1:8080/#/home/');
|
||||
await pg.getByText('Create Room').first().waitFor({ timeout: 30000 });
|
||||
await pg.waitForTimeout(2000);
|
||||
await pg.waitForTimeout(12000);
|
||||
const text = () => pg.evaluate(() => document.body.innerText);
|
||||
const snap = async (n) => { await pg.screenshot({ path: out(n + '.png') }); };
|
||||
const open = async (name) => { const h = pg.getByText(name, { exact: true }); if (await h.count()) { await h.first().click(); await pg.waitForTimeout(300); } };
|
||||
|
||||
// Entry points
|
||||
const t0 = await text();
|
||||
check(/Create Room/.test(t0) && /Join with Address/.test(t0), 'Create Room and Join with Address are navigator actions');
|
||||
await pg.getByText('Join with Address').first().click();
|
||||
await pg.waitForTimeout(400);
|
||||
check((await pg.locator('input[name="addressInput"]').count()) === 1, 'Join with Address opens its prompt');
|
||||
await pg.reload();
|
||||
await pg.getByText('Create Room').first().waitFor({ timeout: 30000 });
|
||||
await pg.waitForTimeout(2000);
|
||||
|
||||
// Header search is the room finder, on a phone
|
||||
await pg.locator('[aria-label="Find a room"]').first().click();
|
||||
await pg.waitForTimeout(500);
|
||||
check((await pg.locator('input[placeholder="Search"]').count()) === 1 && !/#\/home\/search/.test(pg.url()), 'header search opens the room finder, not message search');
|
||||
await snap('finder');
|
||||
await pg.keyboard.press('Escape');
|
||||
await pg.waitForTimeout(300);
|
||||
|
||||
// Groups for an invitee, from the hierarchy alone; Elsewhere; invited DM
|
||||
for (const n of ['Projects', 'nibu', 'eureka', 'Nibu', 'Eureka', 'Elsewhere', 'Direct Messages']) await open(n);
|
||||
// A tier that was not rendered yet when its header was tapped is still collapsed.
|
||||
if (!(await text()).toLowerCase().includes('meeting')) await open('Elsewhere');
|
||||
await snap('expanded');
|
||||
const t1 = (await text()).toLowerCase();
|
||||
check(t1.includes('nibu') && t1.includes('eureka'), 'steward groups render for an invitee who cannot read Space state');
|
||||
check(t1.includes('elsewhere') && t1.includes('meeting'), 'a joined room outside the Space is listed under Elsewhere');
|
||||
check(t1.includes('direct messages') && (await pg.locator('[aria-label="Join Void"]').count()) === 1, 'an invited DM is listed with Join');
|
||||
|
||||
// Join: no invite card at any frame, then the room
|
||||
await pg.locator('[aria-label="Join Aetheria"]').first().click();
|
||||
const t = Date.now();
|
||||
let card = 0, room = null;
|
||||
while (Date.now() - t < 6000) {
|
||||
const body = await text();
|
||||
if (/Summary card/.test(body)) card++;
|
||||
if (room === null && /welcome to Aetheria/.test(body)) room = Date.now() - t;
|
||||
await pg.waitForTimeout(40);
|
||||
}
|
||||
await snap('joined');
|
||||
check(card === 0, `no invite card at any frame after the tap (saw ${card})`);
|
||||
check(room !== null, `the room opened (after ${room} ms)`);
|
||||
|
||||
// Join the DM: lands in the room too
|
||||
await pg.goto('http://127.0.0.1:8080/#/home/');
|
||||
await pg.waitForTimeout(3000);
|
||||
if (!(await pg.locator('[aria-label="Join Void"]').count())) await open('Direct Messages');
|
||||
check((await pg.locator('[aria-label="Join Void"]').count()) === 1, 'the DM invite is still listed after the other join');
|
||||
if (await pg.locator('[aria-label="Join Void"]').count()) {
|
||||
await pg.locator('[aria-label="Join Void"]').first().click();
|
||||
let dmCard = 0; const t2 = Date.now();
|
||||
while (Date.now() - t2 < 5000) { if (/Summary card/.test(await text())) dmCard++; await pg.waitForTimeout(40); }
|
||||
check(dmCard === 0 && /\/direct\//.test(pg.url()), 'the invited DM opens in Direct with no invite card');
|
||||
const snap = async (n) => { await pg.screenshot({ path: out(n + '.png') }); tl.push(`--- ${n} ${t()} url=${pg.url()}\n${await text()}`); };
|
||||
const buttons = async () => tl.push('buttons ' + JSON.stringify(await pg.evaluate(() => [...document.querySelectorAll('button')].map((e) => { const r = e.getBoundingClientRect(); return [e.getAttribute('aria-label') || e.innerText.slice(0, 30), Math.round(r.x), Math.round(r.y), Math.round(r.width), Math.round(r.height)]; }))));
|
||||
const small = async () => tl.push('SMALL ' + JSON.stringify(await pg.evaluate(() => [...document.querySelectorAll('button, a, [role="button"]')].filter((e) => e.getClientRects().length).map((e) => [e.getAttribute('aria-label') || e.innerText.slice(0, 30), Math.round(e.getBoundingClientRect().width), Math.round(e.getBoundingClientRect().height)]).filter((r) => r[2] < 44))));
|
||||
const layout = async () => tl.push('layout ' + JSON.stringify(await pg.evaluate(() => ({ vw: innerWidth, scrollW: document.documentElement.scrollWidth, bodyScrollW: document.body.scrollWidth }))));
|
||||
await snap('1-landing'); await buttons(); await layout(); await small();
|
||||
if (process.env.NOROOT === '1') {
|
||||
tl.push('has Create Room: ' + (await pg.getByText('Create Room', { exact: true }).count()) + ' Join with Address: ' + (await pg.getByText('Join with Address', { exact: true }).count()) + ' finder: ' + (await pg.locator('[aria-label="Find a room"]').count()));
|
||||
await b.close(); return;
|
||||
}
|
||||
const reveal = async (sel) => {
|
||||
const loc = pg.locator(sel);
|
||||
if (await loc.count()) return loc;
|
||||
for (let round = 0; round < 2; round++) for (const h of HEADERS) {
|
||||
const hl = pg.getByText(h, { exact: true });
|
||||
if (await hl.count()) { await hl.first().click(); await pg.waitForTimeout(250); if (await loc.count()) { tl.push(`revealed ${sel} via ${h}`); return loc; } }
|
||||
}
|
||||
tl.push(`could not reveal ${sel}`); return loc;
|
||||
};
|
||||
// expand everything once for the inventory
|
||||
for (const h of HEADERS) { const hl = pg.getByText(h, { exact: true }); if (await hl.count()) { await hl.first().click(); await pg.waitForTimeout(250); tl.push('clicked header ' + h); } }
|
||||
await snap('2-expanded'); await buttons(); await layout(); await small();
|
||||
// room finder
|
||||
const finder = pg.locator('[aria-label="Find a room"], [aria-label="Search"]').first();
|
||||
await finder.click(); await pg.waitForTimeout(800);
|
||||
tl.push('after finder url=' + pg.url() + ' dialogs=' + (await pg.locator('[role="dialog"]').count()) + ' inputs=' + JSON.stringify(await pg.evaluate(() => [...document.querySelectorAll('input')].map((i) => i.placeholder))));
|
||||
await snap('3-finder');
|
||||
await pg.keyboard.press('Escape'); await pg.goto('http://127.0.0.1:8080/#/home/'); await pg.waitForTimeout(3000);
|
||||
// join with frame poll
|
||||
const joinPoll = async (name, ms) => {
|
||||
const loc = await reveal(`[aria-label="Join ${name}"]`);
|
||||
if (!(await loc.count())) { tl.push(`no Join ${name}`); return; }
|
||||
const u0 = pg.url(); await loc.first().click(); const c = Date.now(); tl.push(`CLICK Join ${name} ${t()}`);
|
||||
let card = 0, frames = 0, urlAt = null, firstCardAt = null;
|
||||
while (Date.now() - c < ms) {
|
||||
const s = await text(); frames++;
|
||||
if (s.includes('Summary card')) { card++; if (firstCardAt === null) firstCardAt = Date.now() - c; }
|
||||
if (urlAt === null && pg.url() !== u0) urlAt = Date.now() - c;
|
||||
await pg.waitForTimeout(40);
|
||||
}
|
||||
tl.push(`POLL ${name}: frames=${frames} cardFrames=${card} firstCardAt=${firstCardAt} urlChangedAt=${urlAt} url=${pg.url()}`);
|
||||
await snap(`4-join-${name.replace(/\W/g, '')}`);
|
||||
await pg.goto('http://127.0.0.1:8080/#/home/'); await pg.waitForTimeout(2500);
|
||||
};
|
||||
await joinPoll('Commons', 6000);
|
||||
await joinPoll('Aetheria', 6000);
|
||||
await joinPoll('Friend DM', 6000);
|
||||
// timeout path, room
|
||||
const slow = async (name) => {
|
||||
const loc = await reveal(`[aria-label="Join ${name}"]`);
|
||||
if (!(await loc.count())) { tl.push(`no Join ${name}`); return; }
|
||||
await loc.first().click(); tl.push(`CLICK Join ${name} ${t()}`);
|
||||
await pg.waitForTimeout(21500); await snap(`5-slow-${name.replace(/\W/g, '')}-21s`);
|
||||
tl.push(`timeout msg shown: ${(await text()).includes('has not arrived yet')}`);
|
||||
await pg.waitForTimeout(5500); await snap(`5-slow-${name.replace(/\W/g, '')}-27s`);
|
||||
tl.push(`after arrival: Join ${name} still present=${await pg.locator(`[aria-label="Join ${name}"]`).count()} url=${pg.url()}`);
|
||||
};
|
||||
await slow('Odin');
|
||||
await pg.goto('http://127.0.0.1:8080/#/home/'); await pg.waitForTimeout(2500);
|
||||
await slow('Slow DM');
|
||||
// wide: tier placement (wide tiers start open)
|
||||
await pg.setViewportSize({ width: 1280, height: 800 });
|
||||
await pg.goto('http://127.0.0.1:8080/#/home/'); await pg.waitForTimeout(6000); await snap('6-wide');
|
||||
await b.close();
|
||||
})().catch((e) => { tl.push('ERROR ' + String(e.stack || e)); fails.push('error'); }).finally(() => {
|
||||
fs.writeFileSync(out('timeline.txt'), tl.join('\n'));
|
||||
console.log(tl.join('\n'));
|
||||
process.exitCode = fails.length ? 1 : 0;
|
||||
});
|
||||
})().catch((e) => { tl.push('ERROR ' + String(e.stack || e)); process.exitCode = 1; }).finally(() => fs.writeFileSync(out('timeline.txt'), tl.join('\n')));
|
||||
|
|
|
|||
|
|
@ -1,26 +1,26 @@
|
|||
// Fake Matrix homeserver + static server for the built client (harness/estate/run.sh).
|
||||
// Scenario: an invitee to the GameCult Space and 6 project rooms (never a member of the
|
||||
// Space, so the client cannot read its state), joined to #general and to #meeting, a room
|
||||
// outside the Space, and invited to a DM. The steward of each project room is carried only
|
||||
// in the m.space.child content of the hierarchy, as the accounts script writes it.
|
||||
// Soul s2 fake homeserver. Invitee with no root state; stewards only in m.space.child content.
|
||||
// Rooms: GEN joined; OP invited; 6 project rooms invited (odin slow: join arrives at +24 s);
|
||||
// PUB public joinable, not in store; STRAY joined outside hierarchy; DM1 invited direct (fast);
|
||||
// DM2 invited direct (slow, +24 s). NOROOT=1: the root alias does not resolve.
|
||||
const http = require('http'), fs = require('fs'), path = require('path');
|
||||
const DIST = process.argv[2], PORT = 8080, ME = '@t:test';
|
||||
const NOROOT = process.env.NOROOT === '1';
|
||||
const log = [];
|
||||
const P = ['aetheria', 'ghostlight', 'huginn', 'idunn', 'bifrost', 'odin'];
|
||||
const STEWARD = { aetheria: 'nibu', ghostlight: 'nibu', huginn: 'eureka', idunn: 'eureka' };
|
||||
const DM = '!dm:test', MEETING = '!meeting:test';
|
||||
const PROJ = P.map((p, i) => ({ id: `!p${i}:test`, alias: `#${p}:test`, name: p[0].toUpperCase() + p.slice(1) }));
|
||||
const ROOT = '!root:test', PSP = '!proj:test', GEN = '!gen:test', OP = '!op:test';
|
||||
const pendingJoin = [];
|
||||
const pendingAccountData = [];
|
||||
let ev = 0;
|
||||
const PROJ = P.map((p, i) => ({ id: `!p${i}:test`, alias: `#${p}:test`, name: p[0].toUpperCase() + p.slice(1), key: p }));
|
||||
const ROOT = '!root:test', PSP = '!proj:test', GEN = '!gen:test', OP = '!op:test', PUB = '!pub:test', STRAY = '!stray:test', DM1 = '!dm1:test', DM2 = '!dm2:test';
|
||||
const SLOW = new Set([PROJ[5].id, DM2]);
|
||||
const NAMES = { [PUB]: 'Commons', [DM1]: 'Friend DM', [DM2]: 'Slow DM', [STRAY]: 'Adhoc' };
|
||||
PROJ.forEach((r) => (NAMES[r.id] = r.name));
|
||||
const pending = []; const pendingAD = []; let ev = 0;
|
||||
const se = (type, content, sk = '', sender = '@admin:test') => ({ type, content, state_key: sk, sender, event_id: `$e${++ev}`, origin_server_ts: Date.now() });
|
||||
const strip = (id, name, space, dm) => [
|
||||
{ type: 'm.room.create', state_key: '', sender: '@admin:test', content: space ? { type: 'm.space', room_version: '12' } : { room_version: '12' } },
|
||||
...(name ? [{ type: 'm.room.name', state_key: '', sender: '@admin:test', content: { name } }] : []),
|
||||
...(dm ? [{ type: 'm.room.member', state_key: '@void:test', sender: '@void:test', content: { membership: 'join', displayname: 'Void' } }] : []),
|
||||
const strip = (id, name, space, direct) => [
|
||||
{ type: 'm.room.create', state_key: '', sender: direct ? '@friend:test' : '@admin:test', content: space ? { type: 'm.space', room_version: '12' } : { room_version: '12' } },
|
||||
{ type: 'm.room.name', state_key: '', sender: '@admin:test', content: { name } },
|
||||
{ type: 'm.room.join_rules', state_key: '', sender: '@admin:test', content: { join_rule: 'invite' } },
|
||||
{ type: 'm.room.member', state_key: ME, sender: dm ? '@void:test' : '@admin:test', content: dm ? { membership: 'invite', is_direct: true } : { membership: 'invite' } },
|
||||
...(direct ? [{ type: 'm.room.member', state_key: '@friend:test', sender: '@friend:test', content: { membership: 'join', displayname: 'Friend' } }] : []),
|
||||
{ type: 'm.room.member', state_key: ME, sender: direct ? '@friend:test' : '@admin:test', content: direct ? { membership: 'invite', is_direct: true } : { membership: 'invite' } },
|
||||
];
|
||||
const joinedRoom = (id, name, extra = [], space = false) => ({
|
||||
state: { events: [se('m.room.create', space ? { type: 'm.space', room_version: '12' } : { room_version: '12' }), se('m.room.name', { name }), se('m.room.join_rules', { join_rule: 'invite' }), se('m.room.member', { membership: 'join' }, ME, ME), ...extra] },
|
||||
|
|
@ -33,16 +33,18 @@ const invites = () => {
|
|||
o[PSP] = { invite_state: { events: strip(PSP, 'Projects', true) } };
|
||||
o[OP] = { invite_state: { events: strip(OP, 'Operator') } };
|
||||
PROJ.forEach((r) => (o[r.id] = { invite_state: { events: strip(r.id, r.name) } }));
|
||||
o[DM] = { invite_state: { events: strip(DM, null, false, true) } };
|
||||
o[DM1] = { invite_state: { events: strip(DM1, NAMES[DM1], false, true) } };
|
||||
o[DM2] = { invite_state: { events: strip(DM2, NAMES[DM2], false, true) } };
|
||||
return o;
|
||||
};
|
||||
const child = (id, alias) => ({ type: 'm.space.child', state_key: id, content: { via: ['test'], ...(STEWARD[alias] ? { 'org.gamecult.steward': { repo: 'GameCult/' + alias, steward: '@' + STEWARD[alias] + ':test', mind: STEWARD[alias], source: 'x' } } : {}) }, sender: '@admin:test', origin_server_ts: 1 });
|
||||
const child = (id, steward) => ({ type: 'm.space.child', state_key: id, content: { via: ['test'], ...(steward ? { 'org.gamecult.steward': { mind: steward, repo: 'x', source: 'y', steward: 'z' } } : {}) }, sender: '@admin:test', origin_server_ts: 1 });
|
||||
const hroom = (id, name, alias, extra = {}) => ({ room_id: id, name, canonical_alias: alias, num_joined_members: 1, world_readable: false, guest_can_join: false, join_rule: 'invite', children_state: [], ...extra });
|
||||
const hierarchy = () => [
|
||||
hroom(ROOT, 'GameCult', '#gamecult:test', { room_type: 'm.space', children_state: [child(GEN), child(OP), child(PSP)] }),
|
||||
hroom(ROOT, 'GameCult', '#gamecult:test', { room_type: 'm.space', children_state: [child(GEN), child(OP), child(PUB), child(PSP)] }),
|
||||
hroom(GEN, 'General', '#general:test'),
|
||||
hroom(OP, 'Operator', '#operator:test'),
|
||||
hroom(PSP, 'Projects', '#projects:test', { room_type: 'm.space', children_state: PROJ.map((r) => child(r.id, r.alias.slice(1).split(':')[0])) }),
|
||||
hroom(PUB, 'Commons', '#commons:test', { join_rule: 'public' }),
|
||||
hroom(PSP, 'Projects', '#projects:test', { room_type: 'm.space', children_state: PROJ.map((r) => child(r.id, STEWARD[r.key])) }),
|
||||
...PROJ.map((r) => hroom(r.id, r.name, r.alias)),
|
||||
];
|
||||
let syncN = 1;
|
||||
|
|
@ -59,50 +61,47 @@ http.createServer((req, res) => {
|
|||
return fs.createReadStream(f).pipe(res);
|
||||
}
|
||||
let body = ''; req.on('data', (c) => (body += c)); req.on('end', () => {
|
||||
log.push(`${Date.now()} ${req.method} ${p}`);
|
||||
log.push(`${Date.now()} ${req.method} ${p}${req.method === 'PUT' && p.includes('account_data') ? ' BODY ' + body : ''}`);
|
||||
if (p === '/_matrix/client/versions') return json(res, 200, { versions: ['v1.1', 'v1.5', 'v1.11'], unstable_features: {} });
|
||||
if (p.endsWith('/sync')) {
|
||||
if (!u.searchParams.get('since')) log.push(`${Date.now()} INITIAL SYNC`);
|
||||
const since = u.searchParams.get('since');
|
||||
if (!since) {
|
||||
const join = {}; join[GEN] = joinedRoom(GEN, 'General'); join[MEETING] = joinedRoom(MEETING, 'Meeting');
|
||||
const inv = invites();
|
||||
return json(res, 200, { next_batch: 's1', rooms: { join, invite: inv, leave: {} }, account_data: { events: [] }, presence: { events: [] }, to_device: { events: [] }, device_one_time_keys_count: { signed_curve25519: 50 } });
|
||||
const join = {}; join[GEN] = joinedRoom(GEN, 'General'); join[STRAY] = joinedRoom(STRAY, 'Adhoc');
|
||||
return json(res, 200, { next_batch: 's1', rooms: { join, invite: invites(), leave: {} }, account_data: { events: [] }, presence: { events: [] }, to_device: { events: [] }, device_one_time_keys_count: { signed_curve25519: 50 } });
|
||||
}
|
||||
const wait = () => {
|
||||
if (pendingJoin.length) {
|
||||
const id = pendingJoin.shift(); const r = PROJ.find((x) => x.id === id) || { name: id === DM ? 'Void' : id };
|
||||
const join = {}; join[id] = joinedRoom(id, r.name);
|
||||
log.push(`${Date.now()} SYNC delivers join ${id}`);
|
||||
return json(res, 200, { next_batch: `s${++syncN}`, rooms: { join, invite: {}, leave: {} } });
|
||||
const respond = () => {
|
||||
const now = Date.now(); const join = {};
|
||||
for (let i = pending.length - 1; i >= 0; i--) if (pending[i].at <= now) {
|
||||
const id = pending[i].id; pending.splice(i, 1);
|
||||
const extra = id === DM1 || id === DM2 ? [se('m.room.member', { membership: 'join', displayname: 'Friend' }, '@friend:test', '@friend:test')] : [];
|
||||
join[id] = joinedRoom(id, NAMES[id] || id, extra); log.push(`${now} SYNC delivers join ${id}`);
|
||||
}
|
||||
if (pendingAccountData.length) return json(res, 200, { next_batch: `s${++syncN}`, rooms: {}, account_data: { events: pendingAccountData.splice(0) } });
|
||||
const ad = pendingAD.splice(0);
|
||||
if (Object.keys(join).length || ad.length) return json(res, 200, { next_batch: `s${++syncN}`, rooms: { join, invite: {}, leave: {} }, account_data: { events: ad } });
|
||||
return json(res, 200, { next_batch: since, rooms: {} });
|
||||
};
|
||||
return setTimeout(wait, pendingJoin.length ? 2500 : 1500);
|
||||
return setTimeout(respond, 300);
|
||||
}
|
||||
const jm = p.match(/\/rooms\/([^/]+)\/join$/) || p.match(/\/join\/([^/]+)$/);
|
||||
if (req.method === 'POST' && jm) {
|
||||
const id = jm[1];
|
||||
log.push(`${Date.now()} JOIN ${id}`);
|
||||
const id = jm[1]; log.push(`${Date.now()} JOIN ${id}`);
|
||||
if (id === PROJ[1].id) return json(res, 403, { errcode: 'M_FORBIDDEN', error: 'You are not invited to this room.' });
|
||||
pendingJoin.push(id);
|
||||
pending.push({ id, at: Date.now() + (SLOW.has(id) ? 24000 : 1000) });
|
||||
return json(res, 200, { room_id: id });
|
||||
}
|
||||
if (p.includes('/account_data/') && req.method === 'PUT') {
|
||||
const type = decodeURIComponent(p.split('/account_data/')[1]);
|
||||
try { pendingAD.push({ type, content: JSON.parse(body) }); } catch (e) {}
|
||||
return json(res, 200, {});
|
||||
}
|
||||
if (p.includes('/hierarchy')) return json(res, 200, { rooms: hierarchy() });
|
||||
if (p.includes('/directory/room/')) return json(res, 200, { room_id: ROOT, servers: ['test'] });
|
||||
if (p.includes('/directory/room/')) return NOROOT ? json(res, 404, { errcode: 'M_NOT_FOUND', error: 'Room alias not found' }) : json(res, 200, { room_id: ROOT, servers: ['test'] });
|
||||
if (p.endsWith('/keys/upload')) return json(res, 200, { one_time_key_counts: { signed_curve25519: 50 } });
|
||||
if (p.endsWith('/keys/query')) return json(res, 200, { device_keys: {}, failures: {} });
|
||||
if (p.includes('/room_keys/version')) return json(res, 404, { errcode: 'M_NOT_FOUND', error: 'none' });
|
||||
if (p.includes('/pushrules')) return json(res, 200, { global: { override: [], underride: [], room: [], sender: [], content: [] } });
|
||||
if (p.includes('/filter')) return json(res, 200, { filter_id: '1' });
|
||||
if (p.includes('/account_data/') && req.method === 'PUT') {
|
||||
const type = decodeURIComponent(p.split('/account_data/')[1]);
|
||||
try { pendingAccountData.push({ type, content: JSON.parse(body) }); } catch (e) { /* ignore */ }
|
||||
log.push(`${Date.now()} PUT account_data ${type}`);
|
||||
return json(res, 200, {});
|
||||
}
|
||||
if (p.includes('/account_data/')) return req.method === 'GET' ? json(res, 404, { errcode: 'M_NOT_FOUND', error: 'none' }) : json(res, 200, {});
|
||||
if (p.includes('/account_data/')) return json(res, 404, { errcode: 'M_NOT_FOUND', error: 'none' });
|
||||
if (p.includes('/messages')) return json(res, 200, { chunk: [], start: 't0', end: 't0' });
|
||||
if (p.includes('/members')) return json(res, 200, { chunk: [] });
|
||||
if (p.includes('/profile/')) return json(res, 200, { displayname: 'Tess' });
|
||||
|
|
@ -113,4 +112,4 @@ http.createServer((req, res) => {
|
|||
return json(res, 200, {});
|
||||
});
|
||||
}).listen(PORT, () => console.log('fakehs up'));
|
||||
setInterval(flush, 500);
|
||||
setInterval(flush, 300);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
# Run inside playwright:v1.48.2-jammy with the repo at /src and /out writable:
|
||||
# bash /src/harness/estate/run.sh
|
||||
# Scenario A: root resolves. Scenario B (NOROOT=1): the root alias 404s.
|
||||
set -e
|
||||
cd /src
|
||||
mkdir -p /out
|
||||
|
|
@ -8,8 +9,12 @@ npm run build > /out/build.log 2>&1; echo BUILD_RC=$? >> /out/build.log
|
|||
printf '{"defaultHomeserver":0,"homeserverList":["127.0.0.1:8080"],"allowCustomHomeservers":true,"featuredCommunities":{"spaces":["#gamecult:test"],"rooms":[],"servers":["test"]},"hashRouter":{"enabled":true,"basename":"/"}}' > dist/config.json
|
||||
mkdir -p /tmp/pw && cd /tmp/pw && npm init -y >/dev/null && npm i --no-audit --no-fund playwright@1.48.2 >/out/pw.log 2>&1
|
||||
cp /src/harness/estate/drive.cjs /tmp/pw/
|
||||
HSLOG=/out/hslog.txt node /src/harness/estate/fakehs.cjs /src/dist > /out/hs.out 2>&1 &
|
||||
HS=$!
|
||||
sleep 2
|
||||
node /tmp/pw/drive.cjs; echo DRIVE_RC=$?
|
||||
kill $HS
|
||||
runone () { # tag noroot
|
||||
HSLOG=/out/hslog-$1.txt NOROOT=$2 node /src/harness/estate/fakehs.cjs /src/dist > /out/hs-$1.out 2>&1 &
|
||||
HS=$!
|
||||
sleep 2
|
||||
TAG=$1 NOROOT=$2 node /tmp/pw/drive.cjs; echo "DRIVE $1 RC=$?"
|
||||
kill $HS
|
||||
}
|
||||
runone root 0
|
||||
runone noroot 1
|
||||
|
|
|
|||
Loading…
Reference in a new issue