electron/home-window.node-test.mjs
Rama main · réplica de lectura del repositorio original.
import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import test from "node:test";
import vm from "node:vm";
import { cloudPlanSnapshot } from "./cloud-account.mjs";
import { installCloudPlanIpc } from "./cloud-plan-ipc.mjs";
import { cloudPageSenderAllowed, myCloudOrigin } from "./cloud-home.mjs";
import { otherOwnClouds } from "./cloud-list.mjs";
import environments from "./environments.cjs";
import localOrigin from "./local-origin.cjs";
import { HOME_SCREEN_PATH, homeOf, installHomeWindowIpc, LOOK_MS, OPEN_MS, placeOf, QUIET_MS, routeOf, SPLASH_TEXT, splashText, TEAM_RENEW_EARLY_MS, TEAM_RENEW_MS, TEAM_RENEW_RETRY_MS, UPDATING_MS } from "./home-window.mjs";
import { ACCOUNT, ACME, CLIENT, CLIENT_A, CLOUD, connected, done, LOCAL, TEAM, world } from "./fixtures/home-window-world.mjs";
/** Everything already queued runs before this comes back: what the fake clock's callbacks left to flush. */
const tick = () => new Promise((resolve) => setImmediate(resolve));
test("home is My Cloud only once the move switched and the same account is signed in; anything else is this computer or the sign-in screen", () => {
assert.deepEqual(homeOf({}), { kind: "computer" }, "no Cloud: nothing changes");
assert.deepEqual(homeOf({ state: connected() }), { kind: "computer" }, "a Cloud the move hasn't reached is still not home");
for (const phase of ["copy", "wait", "freeze", "final", "check", "home-again", "back"]) {
assert.deepEqual(homeOf({ journal: { ...done(), phase }, state: connected() }), { kind: "computer" }, phase);
}
for (const phase of ["switch", "done"]) assert.deepEqual(homeOf({ journal: { ...done(), phase }, state: connected() }), { kind: "cloud", origin: CLOUD }, phase);
assert.deepEqual(homeOf({ journal: done(), state: { ...connected(), account: { id: "acct-2" } } }), { kind: "signin", other: true }, "another account never sees this computer's copy");
for (const state of [null, { status: "signed-out" }, { status: "connecting" }, { status: "signed-out", account: ACCOUNT }]) {
assert.deepEqual(homeOf({ journal: done(), state }), { kind: "signin" }, JSON.stringify(state));
}
assert.deepEqual(homeOf({ journal: done(), state: { status: "reauth-required", account: ACCOUNT } }), { kind: "cloud", origin: CLOUD }, "an ended grant still opens My Cloud on the window's own session");
assert.deepEqual(homeOf({ journal: done(), state: { status: "unavailable", account: ACCOUNT } }), { kind: "cloud", origin: CLOUD }, "the Admin not answering is not a sign-out");
assert.deepEqual(homeOf({ journal: done(), state: connected({ hold: { until: 9 } }) }), { kind: "computer" }, "My Cloud ended: everything comes home");
});
test("a frozen copy with its journal lost counts as moved, to the Cloud it names or the account's own", () => {
assert.deepEqual(homeOf({ frozenCloud: CLOUD, state: connected() }), { kind: "cloud", origin: CLOUD });
assert.deepEqual(homeOf({ frozenCloud: true, state: connected() }), { kind: "cloud", origin: CLOUD });
assert.deepEqual(homeOf({ frozenCloud: "https://omb-t-2.fly.dev", state: connected() }), { kind: "signin", other: true });
assert.deepEqual(homeOf({ frozenCloud: true, state: { status: "unavailable", account: ACCOUNT } }), { kind: "signin" }, "no Cloud to name: the sign-in screen, never the frozen copy");
assert.deepEqual(homeOf({ frozenCloud: CLOUD, state: null }), { kind: "signin" });
assert.deepEqual(homeOf({ journal: { ...done(), phase: "freeze" }, frozenCloud: CLOUD, state: connected() }), { kind: "computer" }, "a move under way is not a move done");
});
test("a server of the person's own their bots moved to is no Cloud: no sign-in, the window shows it as any saved server, and the frozen screen names it", async () => {
const garage = "https://garage.example.test";
const w = world({ journal: null, marker: { frozen: true, cloud: garage, place: "Garage server" }, state: { status: "signed-out" }, url: `${garage}/` });
await w.home.show();
assert.deepEqual([w.local, w.went, w.fetched, w.pairs], [[false], [], [], 0], "never the sign-in screen, nor any Cloud or the Admin asked");
assert.equal(w.home.cloudOrigin(), null);
const moving = world({ journal: null, marker: { frozen: true, cloud: garage, place: "Garage server" }, state: { status: "signed-out" }, ownMove: { phase: "final", name: "Garage server" } });
assert.deepEqual(moving.home.view(), { screen: "moving", name: "Garage server" });
moving.ownMove = { phase: "copy", name: "Garage server" };
assert.deepEqual(moving.home.view(), { screen: "opening" }, "still home here: nothing is stopped yet");
});
test("a frozen copy left on this computer's own page goes back to the server it names, or says where the bots are", async () => {
const garage = "https://garage.example.test";
const marker = { frozen: true, cloud: garage, place: "Garage server" };
// This computer's page has nothing to show for a frozen copy: the window goes back to the server, as any saved server does.
const parked = world({ journal: null, marker, state: { status: "signed-out" }, url: `${LOCAL}${HOME_SCREEN_PATH}` });
await parked.home.show();
assert.deepEqual(parked.reopened, [{ origin: garage, place: "Garage server" }], "the server is asked for again, never the frozen page");
assert.equal(parked.url, `${garage}/`, "the window is on the server");
assert.deepEqual(parked.local, [], "this computer's frozen page is never shown");
assert.deepEqual(parked.calls, ["ensure"], "a window is there to open it in");
assert.deepEqual(parked.home.view(), { screen: "opening" }, "no screen of its own while the server opens");
// A server forgotten here, or one that doesn't answer: the screen says where the bots are, and looks again by itself.
const forgotten = world({ journal: null, marker, state: { status: "signed-out" }, url: `${LOCAL}${HOME_SCREEN_PATH}` });
forgotten.reopenAnswer = false;
await forgotten.home.show();
assert.deepEqual(forgotten.home.view(), { screen: "offline", reason: "Your bots are on Garage server." });
assert.equal(forgotten.timers.length, 1, "it looks again by itself");
// The second try lands on the server, and the next show() leaves the window there, never back on the frozen page.
forgotten.reopenAnswer = true;
await forgotten.home.show();
assert.equal(forgotten.url, `${garage}/`, "the retry opened the server");
await forgotten.home.show();
assert.deepEqual(forgotten.local, [false], "already on the server: nothing moves the window back");
// A show() that overlaps the recovery, while its page is still loading, leaves the window where it is going.
const racing = world({ journal: null, marker, state: { status: "signed-out" }, url: `${LOCAL}${HOME_SCREEN_PATH}` });
racing.reopenAnswer = false;
await racing.home.show();
const before = racing.reopened.length;
racing.reopenAnswer = true;
let release = () => {};
racing.reopenGate = new Promise((done) => { release = done; });
const recovering = racing.home.show();
for (let i = 0; i < 200 && racing.reopened.length === before; i++) await tick();
racing.url = `${garage}/`;
await racing.home.show();
release();
await recovering;
assert.equal(racing.url, `${garage}/`, "an overlapping show() never pulls the window back to the frozen page");
assert.deepEqual(racing.local, [], "it leaves the window, and the deadline, to the recovery");
// The recovery's own page failing to load is this app's to answer, not main's generic "not reachable" dialog.
const loading = world({ journal: null, marker, state: { status: "signed-out" }, url: `${LOCAL}${HOME_SCREEN_PATH}` });
await loading.home.show();
assert.equal(loading.home.failed(`${garage}/chats`, -105), true, "the way back's load failure is answered here");
assert.deepEqual(loading.home.view(), { screen: "offline", reason: "Your bots are on Garage server." });
// A recovery whose page never commits is bounded: the screen says where the bots are, with a retry, within OPEN_MS.
const stuck = world({ journal: null, marker, state: { status: "signed-out" }, url: `${LOCAL}${HOME_SCREEN_PATH}` });
stuck.reopenGate = new Promise(() => {});
let back = false;
const waiting = stuck.home.show().then(() => { back = true; });
for (let i = 0; i < 200 && stuck.timers.length === 0; i++) await tick();
await stuck.later(OPEN_MS);
await tick();
assert.equal(back, true, "the deadline hands show() back");
await waiting;
assert.deepEqual(stuck.home.view(), { screen: "offline", reason: "Your bots are on Garage server." }, "the screen says where they are");
assert.equal(stuck.timers.length, 1, "and looks again by itself");
// A move still under way is that screen's to say, not this one's.
const moving = world({ journal: null, marker, state: { status: "signed-out" }, ownMove: { phase: "final", name: "Garage server" }, url: `${LOCAL}${HOME_SCREEN_PATH}` });
await moving.home.show();
assert.deepEqual(moving.reopened, [], "an unfinished move is not opened");
assert.deepEqual(moving.home.view(), { screen: "moving", name: "Garage server" });
assert.equal(moving.home.failed(`${garage}/chats`, -105), true, "the move's own load failure is answered here too");
assert.deepEqual(moving.home.view(), { screen: "moving", name: "Garage server" }, "never the offline screen in the middle of a move");
assert.equal(moving.timers.length, 1, "and it looks again by itself");
const keptBack = world({ journal: { ...done(), phase: "back" }, marker, state: { status: "signed-out" }, url: `${LOCAL}${HOME_SCREEN_PATH}` });
await keptBack.home.show();
assert.deepEqual(keptBack.reopened, [{ origin: garage, place: "Garage server" }], "a journal that keeps this computer does not hide the marker");
const elsewhere = world({ journal: null, marker, state: { status: "signed-out" }, url: "https://studio.example.test/" });
await elsewhere.home.show();
assert.deepEqual(elsewhere.reopened, [], "a server already open is not left for the frozen one");
assert.equal(elsewhere.url, "https://studio.example.test/", "the window stays where it is");
});
test("a sign-in keeps only a path of My Cloud's own", () => {
assert.equal(routeOf(`${CLOUD}/chats/b1?view=2#x`, CLOUD), "/chats/b1?view=2");
assert.equal(routeOf(`${CLOUD}/`, CLOUD), null);
assert.equal(routeOf(`${CLOUD}/pair#code=AAAA-BBBB-CCCC`, CLOUD), null);
assert.equal(routeOf("https://evil.example.test/chats", CLOUD), null);
assert.equal(routeOf("not a url", CLOUD), null);
});
test("no Cloud: the window is this computer's as before, and nothing asks any Cloud or the Admin", async () => {
const w = world({ journal: null, state: { status: "signed-out" }, url: `${LOCAL}/` });
await w.home.show();
assert.deepEqual(w.local, [false]);
assert.deepEqual([w.went, w.fetched, w.pairs, w.wakes, w.splash], [[], [], 0, 0, []]);
assert.equal(w.home.cloudOrigin(), null);
const paid = world({ journal: null, state: connected(), url: "https://bots.example.test/" });
await paid.home.show();
assert.deepEqual([paid.local, paid.went, paid.fetched], [[false], [], []], "a Cloud the move hasn't reached changes nothing either, nor the saved server the window shows");
});
test("launching with My Cloud awake: a quick look, then My Cloud's page on the window's own session", async () => {
const w = world();
await w.home.show();
assert.deepEqual(w.fetched, [["/api/health", "1"], ["/api/auth/session", null]], "the look never counts as someone using My Cloud");
assert.deepEqual(w.went, [[CLOUD, CLOUD]]);
assert.equal(w.pairs, 0);
assert.deepEqual(w.splash, [SPLASH_TEXT.opening]);
assert.equal(w.home.cloudOrigin(), CLOUD);
w.fetched.length = 0;
await w.home.show();
assert.deepEqual([w.went.length, w.fetched], [1, []], "a window already on My Cloud is only brought forward");
});
test("a window that showed My Cloud as a saved server before the move is opened again as home, and goes home when My Cloud ends", async () => {
// Seen in the real app on Oct 22: with moves off, the person opened My Cloud from the switcher; after the move the
// window kept "Hosted: My Cloud", the switcher and "This computer", and when My Cloud ended it stayed on the ended Cloud.
const w = world({ url: `${CLOUD}/` });
await w.home.show({ place: "cloud" });
assert.equal(w.went.length, 1, `opened as home, not left as the saved server it was: ${JSON.stringify(w.went)}`);
w.state = connected({ hold: { until: 9 } });
w.journal = { ...done(), phase: "home-again" };
await w.home.show();
assert.equal(w.local.at(-1), true, "My Cloud ended: the window leaves it for this computer");
});
test("launching with My Cloud asleep says Waking My Cloud… and wakes it through the Admin, never by a look, then shows the page", async () => {
const w = world({ power: { state: "asleep", since: 1, sleeps: true } });
await w.home.show();
assert.deepEqual(w.splash, [SPLASH_TEXT.waking]);
assert.equal(w.wakes, 1);
assert.equal(w.fetched.some(([route]) => route === "/api/health"), false, "a sleeping Cloud is never looked at");
assert.deepEqual(w.went, [[CLOUD, CLOUD]]);
assert.ok(w.calls.includes("woke"));
});
test("a lapsed cookie: a new session from the Admin's one-time code, so the page never asks for one", async () => {
const w = world({ cloud: { signedIn: false } });
await w.home.show();
assert.equal(w.pairs, 1);
assert.deepEqual(w.went, [[`${CLOUD}/pair#code=AAAA-BBBB-CCCC`, CLOUD]]);
});
test("a session that ends while the page is open is renewed by itself, back on the same page", async () => {
const w = world();
await w.home.show();
w.url = `${CLOUD}/chats/b1`;
w.cloud.signedIn = false;
w.home.responded({ url: `${CLOUD}/api/bots`, statusCode: 401, webContentsId: 7 });
await w.home.repair();
assert.deepEqual(w.went.at(-1), [`${CLOUD}/pair?next=%2Fchats%2Fb1#code=AAAA-BBBB-CCCC`, CLOUD]);
const pairs = w.pairs;
for (const details of [{ url: `${CLOUD}/api/bots`, statusCode: 401, webContentsId: 8 }, { url: "https://other.example.test/api/bots", statusCode: 401, webContentsId: 7 },
{ url: `${CLOUD}/api/bots`, statusCode: 403, webContentsId: 7 }, { url: `${CLOUD}/assets/x.js`, statusCode: 401, webContentsId: 7 }]) {
w.home.responded(details);
await w.later(0);
}
assert.equal(w.pairs, pairs, "another window, another server, another status or a file is not this session ending");
w.cloud.signedIn = true;
w.home.responded({ url: `${CLOUD}/api/bots`, statusCode: 401, webContentsId: 7 });
await w.later(0);
assert.equal(w.pairs, pairs, "still signed in: a 401 of some other kind changes nothing");
w.cloud.signedIn = false;
w.home.navigated(`${CLOUD}/pair`);
await w.later(0);
assert.equal(w.pairs, pairs + 1, "landing on My Cloud's own sign-in page signs in again by itself");
w.state = { status: "reauth-required", message: "access-ended", account: ACCOUNT };
await w.home.repair();
assert.deepEqual(w.home.view(), { screen: "signin" }, "no code because this device's sign-in ended: the one Sign in, not the offline screen");
w.state = connected(); w.cloud.down = true;
await w.home.refresh();
await w.home.repair();
assert.equal(w.home.view().screen, "offline", "no code because nothing answered: the offline screen");
});
test("an ended Admin grant opens My Cloud while its session lasts, and shows the one Sign in when it doesn't", async () => {
const w = world({ state: { status: "reauth-required", account: ACCOUNT, lastPlan: { tier: "pro", active: true } } });
await w.home.show();
assert.deepEqual(w.went, [[CLOUD, CLOUD]], "the window's own session still works");
const lapsed = world({ state: { status: "reauth-required", account: ACCOUNT }, cloud: { signedIn: false } });
await lapsed.home.show();
assert.deepEqual(lapsed.went, [[`${LOCAL}${HOME_SCREEN_PATH}`, null]]);
assert.deepEqual(lapsed.home.view(), { screen: "signin" });
await lapsed.home.signIn();
assert.deepEqual(lapsed.signIns, ["again"]);
const out = world({ state: { status: "signed-out" } });
await out.home.show();
assert.deepEqual([out.went, out.home.view(), out.fetched], [[[`${LOCAL}${HOME_SCREEN_PATH}`, null]], { screen: "signin" }, []], "signed out: the sign-in screen, never the frozen copy");
await out.home.signIn();
assert.deepEqual(out.signIns, ["begin"]);
out.state = connected();
await out.home.changed();
assert.deepEqual(out.went.at(-1), [CLOUD, CLOUD], "signed in again: My Cloud opens by itself");
});
test("another account: the sign-in screen says so, and My Cloud is never opened for it", async () => {
const w = world({ state: { ...connected(), account: { id: "acct-2", email: "else@example.test" } } });
await w.home.show();
assert.deepEqual(w.home.view(), { screen: "signin", other: true });
assert.deepEqual([w.fetched, w.pairs], [[], 0]);
});
test("with the network down the offline screen shows at once, with the Admin's reason, and goes away by itself", async () => {
const w = world({ state: connected({ unreachable: { cause: "host", since: 1 } }), cloud: { down: true } });
const started = Date.now();
await w.home.show();
assert.ok(Date.now() - started < LOOK_MS);
assert.deepEqual(w.went, [[`${LOCAL}${HOME_SCREEN_PATH}`, null]]);
const view = w.home.view();
assert.equal(view.screen, "offline");
assert.match(view.reason, /being restarted by our hosting provider/);
assert.deepEqual([w.fetched, w.pairs], [[["/api/health", "1"]], 0], "a My Cloud that fails the look is asked nothing else, and never paired");
assert.deepEqual(w.published.at(-1), view, "the screen is told");
w.url = `${LOCAL}${HOME_SCREEN_PATH}`;
await w.later(2_000);
assert.equal(w.went.length, 1, "still down: it stays, and looks again later");
w.cloud.down = false;
await w.later(4_000);
assert.deepEqual(w.went.at(-1), [CLOUD, CLOUD], "back: My Cloud's page, with nothing pressed");
assert.equal(w.home.view().screen, "opening");
w.url = `${CLOUD}/`;
const moves = w.went.length;
w.home.failed("https://other.example.test/");
assert.deepEqual([w.home.view().screen, w.went.length], ["opening", moves], "another server failing to load is not My Cloud failing");
w.home.failed(`${CLOUD}/chats`);
assert.equal(w.home.view().screen, "offline", "My Cloud's page failing to load is the same screen");
assert.deepEqual(w.went.at(-1), [`${LOCAL}${HOME_SCREEN_PATH}`, null]);
});
test("a plan problem shows the Admin's state and never knocks on My Cloud", async () => {
for (const status of ["stopped", "payment-problem", "failed"]) {
const w = world({ state: connected({ status }) });
await w.home.show();
assert.deepEqual(w.home.view(), { screen: "plan", machine: status }, status);
assert.deepEqual(w.fetched, [], status);
}
});
test("while home is My Cloud the startup screen says Opening My Cloud…, whichever screen comes next; otherwise it keeps its own words", async () => {
for (const [state, cloud] of [[connected({ status: "stopped" }), {}], [{ status: "signed-out" }, {}], [connected(), { down: true }], [connected(), {}]]) {
const w = world({ state, cloud });
await w.home.show();
assert.deepEqual(w.splash, [SPLASH_TEXT.opening], `${JSON.stringify(state)} ${JSON.stringify(cloud)}`);
}
for (const w of [world({ journal: null }), world({ state: connected({ hold: { until: 9 } }) }), world({ journal: { ...done(), phase: "copy" } })]) {
await w.home.show();
assert.deepEqual(w.splash, [], "home is this computer");
}
});
test("a slow wake never moves the window after a newer request; a long one hands the startup screen to this computer's", async () => {
let woken;
const w = world({ power: { state: "asleep", since: 1, sleeps: true }, cloud: { wakeGate: new Promise((resolve) => { woken = resolve; }) } });
const waking = w.home.show();
await w.later(0);
assert.deepEqual(w.went, [], "the startup screen says it first");
await w.later(6_000);
assert.deepEqual(w.went, [[`${LOCAL}${HOME_SCREEN_PATH}`, null]], "the screen says Waking My Cloud… once the startup screen has been up long enough");
assert.equal(w.home.view().screen, "waking");
woken();
await waking;
assert.deepEqual(w.went.at(-1), [CLOUD, CLOUD], "then the page");
let release;
const settled = world({ power: { state: "asleep", since: 1, sleeps: true }, cloud: { wakeGate: new Promise((resolve) => { release = resolve; }) } });
const first = settled.home.show();
await new Promise((resolve) => setImmediate(resolve));
settled.journal = { ...done(), phase: "home-again" };
const second = settled.home.show();
release();
await Promise.all([first, second]);
assert.deepEqual(settled.went.filter(([, origin]) => origin === CLOUD), [], "the newer request (this computer, home again) wins");
assert.deepEqual(settled.local, [true]);
});
test("a newer My Cloud page reloads at a quiet moment; an asleep My Cloud is never asked", async () => {
const w = world();
await w.home.show();
w.home.navigated(`${CLOUD}/`);
await w.tick();
w.cloud.version = "0.1.131";
w.typed = w.clock;
await w.tick();
assert.equal(w.reloads, 0, "the person is typing");
w.clock += QUIET_MS;
await w.tick();
assert.equal(w.reloads, 1);
await w.tick();
assert.equal(w.reloads, 1, "once");
w.power = { state: "asleep", since: 2 };
const asked = w.fetched.length;
w.cloud.version = "0.1.132";
w.clock += QUIET_MS;
await w.tick();
assert.deepEqual([w.fetched.length, w.reloads], [asked, 1]);
});
test("My Cloud's page that the window reloaded for its newer version learns it once, as it loads, to say Updating My Cloud…", async () => {
const w = world();
await w.home.show();
const listeners = new Map();
installHomeWindowIpc({ ipcMain: { handle() {}, on: (channel, listener) => listeners.set(channel, listener) }, homeWindow: w.home, cloudPage: () => null });
const asks = (event = { sender: w.contents, senderFrame: w.contents.mainFrame }) => { listeners.get("home:updating")(event); return event.returnValue; };
w.url = `${CLOUD}/`;
w.home.navigated(w.url);
await w.tick();
assert.equal(asks(), false, "a page that loads for any other reason");
w.cloud.version = "0.1.131";
w.clock += QUIET_MS;
await w.tick();
assert.equal(w.reloads, 1);
assert.equal(asks({ sender: {}, senderFrame: w.contents.mainFrame }), false, "another window is never told");
assert.equal(asks(), true, "the reloaded page");
assert.equal(asks(), false, "once");
w.cloud.version = "0.1.132";
w.clock += QUIET_MS;
await w.tick();
assert.equal(w.reloads, 2);
w.clock += UPDATING_MS;
assert.equal(asks(), false, "a page that loads long after is not that version");
});
test("My Cloud ending brings the window back to this computer's page", async () => {
const w = world();
await w.home.show();
w.state = connected({ hold: { until: 9 } });
await w.home.changed();
assert.deepEqual(w.local, [true]);
assert.equal(w.home.cloudOrigin(), null);
});
test("a change of home is told once as it happens: My Cloud after the move, this computer when My Cloud ends", async () => {
const w = world();
w.rehomeThrows = true;
await w.home.show();
await w.home.show();
assert.deepEqual(w.rehomes, [{ kind: "cloud", origin: CLOUD }], "a listener that throws never stops the window");
assert.equal(w.home.cloudOrigin(), CLOUD);
w.state = connected({ hold: { until: 9 } });
await w.home.changed();
assert.deepEqual(w.rehomes, [{ kind: "cloud", origin: CLOUD }, { kind: "computer" }]);
const never = world({ journal: null, state: { status: "signed-out" } });
await never.home.show();
await never.home.refresh();
assert.deepEqual(never.rehomes, [], "no move: home stays this computer");
});
test("only the main window's main frame counts, at My Cloud's origin for its page and this computer's for the screen", async () => {
const w = world();
await w.home.show();
const frame = w.contents.mainFrame;
w.url = `${CLOUD}/`;
assert.equal(w.home.homePage({ sender: w.contents, senderFrame: frame }), true);
assert.equal(w.home.homePage({ sender: w.contents, senderFrame: { url: `${CLOUD}/` } }), false, "a subframe");
assert.equal(w.home.homePage({ sender: {}, senderFrame: frame }), false, "another window");
assert.equal(w.home.screenPage({ sender: w.contents, senderFrame: frame }), false);
w.url = `${LOCAL}${HOME_SCREEN_PATH}`;
assert.equal(w.home.homePage({ sender: w.contents, senderFrame: frame }), false);
assert.equal(w.home.screenPage({ sender: w.contents, senderFrame: frame }), true);
w.state = { status: "signed-out" };
w.url = `${CLOUD}/`;
await w.home.refresh();
assert.equal(w.home.homePage({ sender: w.contents, senderFrame: frame }), false, "signed out, My Cloud's page is no longer this app's");
});
test("the window's IPC: the screen reads and retries, My Cloud's page renews its session, either signs in again", async () => {
const w = world();
await w.home.show();
const handlers = new Map(), listeners = new Map();
installHomeWindowIpc({ ipcMain: { handle: (channel, handler) => handlers.set(channel, handler), on: (channel, listener) => listeners.set(channel, listener) },
homeWindow: w.home, cloudPage: (event) => event.cloudPage ?? null });
const frame = w.contents.mainFrame, page = { sender: w.contents, senderFrame: frame };
w.url = `${CLOUD}/`;
const sync = (event) => { listeners.get("home:page")(event); return event.returnValue; };
assert.equal(sync({ ...page }), true);
assert.equal(sync({ sender: {}, senderFrame: frame }), false);
assert.throws(() => handlers.get("home:state")(page), /only available/);
assert.throws(() => handlers.get("home:retry")(page), /only available/);
await handlers.get("home:repair")(page);
w.url = `${LOCAL}${HOME_SCREEN_PATH}`;
assert.deepEqual(handlers.get("home:state")(page), { screen: "opening" });
assert.throws(() => handlers.get("home:repair")(page), /only available/);
w.state = { status: "signed-out" };
await handlers.get("home:sign-in")(page);
w.url = "https://other.example.test/";
assert.throws(() => handlers.get("home:sign-in")(page), /only available/);
await handlers.get("home:sign-in")({ ...page, cloudPage: "cloud" });
assert.deepEqual(w.signIns, ["begin", "begin"]);
});
const PRELOAD = readFileSync(new URL("./preload.cjs", import.meta.url), "utf8");
function preload(origin, { home = false, activation = false, pairedElsewhere = false } = {}) {
let bridge; const invoked = [], synced = [], listeners = new Map();
vm.runInNewContext(PRELOAD, {
process: { platform: "darwin", argv: [`--omb-local-origin=${LOCAL}`, ...(pairedElsewhere ? ["--openmausbot-remote-client"] : ["--omb-company-desktop=1"])] },
location: { origin }, navigator: { userActivation: { isActive: activation } }, TextEncoder, localStorage: { getItem: () => null },
queueMicrotask,
require: () => ({ webUtils: {}, contextBridge: { exposeInMainWorld: (_name, value) => { bridge = value; } },
ipcRenderer: { on: (channel, listener) => listeners.set(channel, listener), removeListener() {}, send() {}, sendSync: (channel) => { synced.push(channel); return channel === "home:page" ? home : null; },
invoke: (...args) => { invoked.push(args); return Promise.resolve(); } } }),
});
const tell = (channel, ...args) => listeners.get(channel)?.({}, ...args);
return { bridge, invoked, synced, tell };
}
test("My Cloud's page in the app is the Cloud's own page, never a remote client: only a desktop paired to another computer is one", () => {
assert.equal(preload(CLOUD, { home: true }).bridge.remoteClient, undefined, "a Cloud's own page isn't told it is remote at all");
assert.equal(preload(LOCAL).bridge.remoteClient?.active, false);
assert.equal(preload(LOCAL, { pairedElsewhere: true }).bridge.remoteClient?.active, true);
});
test("My Cloud's own page, once it is home, gets this app's chat links, diagnostics and its session renewal; any other server's page doesn't", async () => {
const keys = ["home", "onChatLink", "onPackageInstall", "exportDiagnostics", "openLogsFolder"];
const home = preload(CLOUD, { home: true });
for (const key of keys) assert.ok(home.bridge[key], key);
assert.ok(home.synced.includes("home:page"));
assert.equal(home.bridge.home.updating, false);
assert.ok(home.synced.includes("home:updating"), "My Cloud's page asks once whether it was reloaded for its newer version");
for (const key of ["cloudAccount", "companion", "environments", "credential", "setCredential", "saveFile", "revealInFolder", "pickFolder"]) assert.equal(home.bridge[key], undefined, key);
await home.bridge.home.repair();
await assert.rejects(home.bridge.home.signIn(), /Choose Sign in/, "only from the person's click");
await assert.rejects(home.bridge.cloudPlan.signIn(), /Choose Sign in/);
const clicked = preload(CLOUD, { home: true, activation: true });
await clicked.bridge.home.signIn(); await clicked.bridge.cloudPlan.signIn();
assert.deepEqual([...home.invoked, ...clicked.invoked].map(([channel]) => channel), ["home:repair", "home:sign-in", "home:sign-in"]);
const other = preload("https://bots.example.test", { home: false });
for (const key of keys) assert.equal(other.bridge[key], undefined, key);
for (const page of [home, other]) {
const opened = [];
page.bridge.onOpenAppSettings(section => opened.push(section ?? "settings"));
page.tell("app:open-settings");
assert.deepEqual(opened, ["settings"], "the app menu's Settings reaches every server's page");
}
const local = preload(LOCAL);
for (const key of keys) assert.ok(local.bridge[key], key);
assert.equal(local.synced.includes("home:page"), false, "this computer's page never needs to ask");
assert.equal(local.synced.includes("home:updating"), false);
assert.equal(local.bridge.home.updating, false);
await local.bridge.home.retry(); await local.bridge.home.state(); await local.bridge.home.signIn();
assert.deepEqual(local.invoked.map(([channel]) => channel), ["home:retry", "home:state", "home:sign-in"]);
});
test("only the verified Cloud, open as this window's active server, counts as the Cloud page", () => {
const frame = { url: `${CLOUD}/` }, contents = { mainFrame: frame };
const context = { contents, homeOrigin: CLOUD, activeOrigin: CLOUD };
assert.equal(cloudPageSenderAllowed({ sender: contents, senderFrame: frame }, context), true);
assert.equal(cloudPageSenderAllowed({ sender: contents, senderFrame: { url: `${CLOUD}/` } }, context), false, "a subframe");
assert.equal(cloudPageSenderAllowed({ sender: {}, senderFrame: frame }, context), false, "another window");
assert.equal(cloudPageSenderAllowed({ sender: contents, senderFrame: frame }, { ...context, activeOrigin: "https://other.example.test" }), false);
assert.equal(cloudPageSenderAllowed({ sender: contents, senderFrame: frame }, { ...context, homeOrigin: null }), false, "signed out of Cloud");
const evil = { url: "https://evil.example.test/" }, evilContents = { mainFrame: evil };
assert.equal(cloudPageSenderAllowed({ sender: evilContents, senderFrame: evil }, { contents: evilContents, homeOrigin: CLOUD, activeOrigin: CLOUD }), false);
});
const MAIN = readFileSync(new URL("./main.mjs", import.meta.url), "utf8").replace(/\r\n/g, "\n");
const PAGE_IPC = MAIN.slice(MAIN.indexOf("function myCloud() {"), MAIN.indexOf("const savedWorkspace = id =>"));
const plain = (value) => JSON.parse(JSON.stringify(value));
function pageIpc(overrides = {}) {
const handlers = new Map(), calls = [];
const localFrame = { url: `${LOCAL}/` }, localContents = { mainFrame: localFrame };
const cloudFrame = { url: `${CLOUD}/` }, cloudContents = { mainFrame: cloudFrame };
localOrigin.setLocalOrigin(LOCAL);
const account = { status: "connected", account: { id: "a1", email: "person@example.test" }, deviceId: "d1",
entitlement: { plan: "pro", tier: "max", status: "active", expiresAt: 1, version: 1 }, machine: { status: "ready", origin: CLOUD } };
const context = vm.createContext({
ipcMain: { handle: (channel, handler) => handlers.set(channel, handler), on: () => {} },
senderIsLocal: localOrigin.isLocalSender, workspaceSenderAllowed: environments.workspaceSenderAllowed, cloudPageSenderAllowed, otherOwnClouds,
activeEnvironment: environments.activeEnvironment, rendererOrigin: () => LOCAL, desktopRemoteAccess: false,
mainWindow: { isDestroyed: () => false, webContents: cloudContents },
environmentsState: { environments: [{ id: "cloud", name: "My Cloud", origin: CLOUD }], activeId: "cloud" },
cloudAccount: { homeTarget: () => ({ origin: CLOUD }), state: () => account, isOwnCloud: (origin) => origin === CLOUD }, cloudPlanSnapshot, installCloudPlanIpc, myCloudOrigin, rememberedHome: null,
cloudTrialNotices: () => ({ due: () => null, seen: (...args) => { calls.push(["notice-seen", ...args]); } }),
ensureCloudAccount: () => ({ openDashboard: async () => { calls.push(["dashboard"]); return account; }, state: () => account,
upgradePreview: async (...args) => { calls.push(["upgrade-preview", ...args]); return { outcome: "failed" }; },
upgrade: async (...args) => { calls.push(["upgrade", ...args]); return { outcome: "failed" }; },
cancelPlan: async (...args) => { calls.push(["cancel", ...args]); return { outcome: "failed" }; },
keepPlan: async (...args) => { calls.push(["keep", ...args]); return { outcome: "failed" }; },
roomNotNow: async (...args) => { calls.push(["room-not-now", ...args]); return true; },
restartNow: async (...args) => { calls.push(["restart-now", ...args]); return "restarting"; } }),
homeWindow: { homePage: () => false },
...overrides,
});
vm.runInContext(PAGE_IPC, context);
return { handlers, calls, context, account, localContents, localFrame, cloudContents, cloudFrame };
}
test("no page gets a control over lending this computer: not My Cloud's, another Cloud's or this computer's own, and main handles none", async () => {
for (const origin of [CLOUD, "https://omb-c-a.fly.dev", LOCAL]) {
const page = preload(origin, { activation: true });
assert.equal(page.bridge.cloudLending, undefined, origin);
assert.equal(page.bridge.cloudAccount?.lending, undefined, origin);
}
assert.deepEqual(MAIN.match(/ipcMain\.handle\("(?:cloud-)?lending:[^"]*"/g), null, "no lending channel at all");
});
test("Settings on the person's own Cloud shows the plan read only, says when it is home, and can open the Plan page or sign in again; never switch away", async () => {
const page = preload(CLOUD);
assert.deepEqual(Object.keys(page.bridge.cloudPlan), ["state", "manage", "signIn", "noticeSeen", "upgradePreview", "upgrade", "cancel", "keep", "roomNotNow", "restartNow"]);
assert.equal(page.bridge.cloudAccount, undefined, "no account, credential or address reaches the Cloud's page");
await page.bridge.cloudPlan.state({ origin: "https://evil.example.test" });
await assert.rejects(page.bridge.cloudPlan.manage(), /Choose Manage/);
const ipc = pageIpc();
const event = { sender: ipc.cloudContents, senderFrame: ipc.cloudFrame };
assert.deepEqual(Object.keys(Object.fromEntries(ipc.handlers)), ["cloud-plan:state", "cloud-plan:notice-seen", "cloud-plan:manage",
"cloud-plan:upgrade-preview", "cloud-plan:upgrade", "cloud-plan:cancel", "cloud-plan:keep", "cloud-plan:room-not-now", "cloud-plan:restart-now"]);
assert.deepEqual(plain(await ipc.handlers.get("cloud-plan:state")(event, { tier: "personal" })), { status: "paid", tier: "max" });
ipc.context.homeWindow = { homePage: (asked) => asked === event };
assert.deepEqual(plain(await ipc.handlers.get("cloud-plan:state")(event)), { status: "paid", tier: "max", home: true });
await ipc.handlers.get("cloud-plan:manage")(event, "https://evil.example.test");
assert.deepEqual(ipc.calls, [["dashboard"]]);
for (const sender of [{ sender: ipc.cloudContents, senderFrame: { url: "https://other.example.test/" } }, { sender: {}, senderFrame: ipc.cloudFrame }]) {
for (const channel of ["cloud-plan:state", "cloud-plan:manage", "cloud-plan:notice-seen"]) assert.throws(() => ipc.handlers.get(channel)(sender), /only available/);
}
let current = { status: "reauth-required", message: "expired", account: ipc.account.account, lastPlan: { tier: "max", active: true } };
ipc.context.cloudAccount = { homeTarget: () => null, state: () => current, isOwnCloud: () => false };
assert.throws(() => ipc.handlers.get("cloud-plan:state")(event), /only available/);
ipc.context.rememberedHome = { accountId: "a1", origin: CLOUD };
assert.deepEqual(plain(await ipc.handlers.get("cloud-plan:state")(event)), { status: "signin", tier: "max", home: true });
current = { status: "unavailable", account: ipc.account.account, lastPlan: { tier: "max", active: true } };
assert.deepEqual(plain(await ipc.handlers.get("cloud-plan:state")(event)), { status: "checking", tier: "max", home: true });
ipc.context.rememberedHome = { accountId: "someone-else", origin: CLOUD };
assert.throws(() => ipc.handlers.get("cloud-plan:state")(event), /only available/);
});
test("main shows the switcher at home only with somewhere else to go: My Cloud alone has none", () => {
const start = MAIN.indexOf("const homeAlone = "), end = MAIN.indexOf("\n", start);
assert.ok(start > 0 && end > start);
const cloud = { id: "c", kind: "remote", name: "My Cloud", origin: CLOUD }, office = { id: "o", kind: "remote", name: "Office", origin: "https://office.example.test" };
const workspaces = [];
const context = vm.createContext({ homeWindow: { cloudOrigin: () => CLOUD }, otherPlaces: environments.otherPlaces, environmentsState: { environments: [cloud], activeId: "c" },
localProfiles: { places: () => workspaces } });
vm.runInContext(`${MAIN.slice(start, end)}; this.homeAlone = homeAlone;`, context);
assert.equal(context.homeAlone(), true, "My Cloud alone: no switcher");
context.environmentsState = { environments: [cloud, office], activeId: "c" };
assert.equal(context.homeAlone(), false, "a saved server besides: the switcher lists both");
context.environmentsState = { environments: [cloud, { id: "l", kind: "local", name: "Lab", dataDir: "/lab" }], activeId: "c" };
assert.equal(context.homeAlone(), true, "a named folder of this computer is not a place once home is My Cloud");
workspaces.push({ id: "default" });
assert.equal(context.homeAlone(), false, "a workspace of this computer's own is: the Server menu lists it, for when the rail is hidden");
workspaces.length = 0;
context.homeWindow = { cloudOrigin: () => null };
assert.equal(context.homeAlone(), false, "no home: the switcher as before");
});
test("the place asked for: My Cloud unless a team is asked or remembered; a team is the session's, one the connected session no longer lists is gone, and one the Admin can't vouch for opens as saved", () => {
const state = { ...connected(), teams: [ACME] };
assert.deepEqual(placeOf({ state }), { kind: "cloud" });
assert.deepEqual(placeOf({ ask: "cloud", remembered: { team: "team-acme" }, state }), { kind: "cloud" }, "asked for My Cloud: My Cloud");
assert.deepEqual(placeOf({ ask: { team: "team-acme" }, state }), { kind: "team", id: "team-acme", name: "Acme", role: "member", origin: TEAM, machine: ACME.machine });
assert.deepEqual(placeOf({ remembered: { team: "team-acme", name: "Old name", origin: "https://old.fly.dev" }, state }).origin, TEAM, "the session's address wins over the saved one");
assert.deepEqual(placeOf({ ask: { team: "team-acme" }, state: { ...state, teams: [{ ...ACME, machine: null }] } }), { kind: "team", id: "team-acme", name: "Acme", role: "member", origin: null, machine: null });
assert.deepEqual(placeOf({ remembered: { team: "team-acme", name: "Acme", origin: TEAM }, state: connected() }), { kind: "cloud", left: "Acme" }, "connected, not listed: gone, by name");
assert.deepEqual(placeOf({ ask: { team: "team-acme", name: "Acme" }, state: connected() }), { kind: "cloud", left: "Acme" });
assert.deepEqual(placeOf({ remembered: { team: "team-acme" }, state: connected() }), { kind: "cloud", left: null }, "gone, with no name to say");
for (const state of [{ status: "unavailable", account: ACCOUNT }, { status: "reauth-required", account: ACCOUNT }, null]) {
assert.deepEqual(placeOf({ remembered: { team: "team-acme", name: "Acme", origin: TEAM }, state }), { kind: "team", id: "team-acme", name: "Acme", role: null, origin: TEAM, machine: null }, JSON.stringify(state));
assert.deepEqual(placeOf({ remembered: { team: "team-acme" }, state }), { kind: "cloud" }, "no saved address: My Cloud");
}
assert.equal(splashText("opening", "Acme"), "Opening Acme…");
assert.equal(splashText("waking"), SPLASH_TEXT.waking);
});
test("choosing a team opens its Cloud through the Admin's team pairing, at the team's address, remembered for next time; My Cloud on request lets it go", async () => {
const w = world({ state: { ...connected(), teams: [ACME] }, team: { signedIn: false } });
await w.home.show({ place: { team: "team-acme" } });
assert.deepEqual(w.teamPairs, ["team-acme"]);
assert.equal(w.pairs, 0, "never My Cloud's pairing");
assert.deepEqual(w.went, [[`${TEAM}/pair#code=TTTT-EEEE-AAAA`, TEAM, "team-acme"]]);
assert.deepEqual(w.fetched, [["team:/api/health", "1"], ["team:/api/auth/session", null]], "a look at the team's Cloud, never My Cloud");
assert.deepEqual(w.splash, ["Opening Acme…"]);
assert.deepEqual(w.remembers, [{ team: "team-acme" }]);
assert.deepEqual(w.home.shown(), { kind: "team", id: "team-acme", name: "Acme", role: "member", origin: TEAM, machine: ACME.machine });
assert.equal(w.home.cloudOrigin(), CLOUD, "My Cloud stays the person's home");
assert.equal(w.home.placeOrigin(), TEAM, "the window's page is the team's");
assert.equal(w.wakes, 0, "a team's Cloud is never woken through the Admin");
// Already there: only brought forward.
w.fetched.length = 0;
await w.home.show();
assert.deepEqual([w.went.length, w.fetched], [1, []]);
await w.home.show({ place: "cloud" });
assert.deepEqual(w.went.at(-1), [CLOUD, CLOUD]);
assert.deepEqual(w.remembers.at(-1), null);
assert.equal(w.home.placeOrigin(), CLOUD);
});
test("launching opens the team remembered from last time; one the connected session no longer lists is gone: My Cloud, with its name said once", async () => {
const w = world({ state: { ...connected(), teams: [ACME] }, remembered: { team: "team-acme", name: "Acme", origin: TEAM } });
await w.home.show();
assert.deepEqual(w.went, [[TEAM, TEAM, "team-acme"]], "the remembered team, on the window's own session");
assert.deepEqual(w.left, []);
// Removed from the team: the next sign-in check says so.
w.state = connected();
await w.home.changed();
assert.deepEqual(w.went.at(-1), [CLOUD, CLOUD]);
assert.deepEqual(w.left, ["Acme"]);
assert.deepEqual(w.remembers.at(-1), null);
assert.equal(w.home.placeOrigin(), CLOUD);
assert.deepEqual(w.teamPairs, [], "a team gone is never paired");
});
test("a team's Cloud that can't be reached shows this computer's screen with the team's name and looks again by itself; My Cloud is untouched", async () => {
const w = world({ state: { ...connected(), teams: [ACME] }, team: { down: true }, url: `${CLOUD}/` });
await w.home.show({ place: { team: "team-acme" } });
assert.deepEqual(w.went, [[`${LOCAL}${HOME_SCREEN_PATH}`, null]]);
assert.deepEqual(w.home.view(), { screen: "offline", name: "Acme" }, "named, with no reason of My Cloud's");
assert.equal(w.fetched.some(([route]) => !route.startsWith("team:")), false, "nothing asks My Cloud");
w.team.down = false;
await w.later(2_000);
assert.deepEqual(w.went.at(-1), [TEAM, TEAM, "team-acme"], "back by itself");
assert.deepEqual(w.home.view(), { screen: "opening", name: "Acme" });
// The team's Cloud answering asleep is simply not reachable yet: no wake through the Admin.
w.team.health = 503; w.url = `${LOCAL}/`;
await w.home.show({ place: { team: "team-acme" } });
assert.deepEqual(w.home.view(), { screen: "offline", name: "Acme" });
assert.equal(w.wakes, 0);
});
test("signed out, a team shows the sign-in screen with its name; the Admin refusing the team pairing (no longer a member) falls back to My Cloud and says so", async () => {
const out = world({ state: { status: "signed-out" }, remembered: { team: "team-acme", name: "Acme", origin: TEAM } });
await out.home.show();
assert.deepEqual(out.home.view(), { screen: "signin", name: "Acme" });
assert.deepEqual(out.teamPairs, []);
const w = world({ state: { ...connected(), teams: [ACME] }, team: { signedIn: false, gone: true } });
await w.home.show({ place: { team: "team-acme" } });
assert.deepEqual(w.teamPairs, ["team-acme"]);
assert.deepEqual(w.left, ["Acme"]);
assert.deepEqual(w.went.at(-1), [CLOUD, CLOUD], "My Cloud instead");
assert.deepEqual(w.remembers.at(-1), null);
const problem = world({ state: { ...connected(), teams: [{ ...ACME, machine: { status: "stopped", hold: { until: 9 } } }] } });
await problem.home.show({ place: { team: "team-acme" } });
assert.deepEqual(problem.home.view(), { screen: "plan", machine: "stopped", name: "Acme", member: true }, "a team's Cloud that ended: its plan screen, by name");
assert.deepEqual(problem.teamPairs, []);
const stoppedFor = async (role, machine) => {
const seen = world({ state: { ...connected(), teams: [{ ...ACME, role, machine }] } });
await seen.home.show({ place: { team: "team-acme" } });
return seen.home.view();
};
for (const machine of [{ status: "stopped", hold: { until: 9 } }, { status: "payment-problem" }, { status: "stopped", origin: TEAM }, { status: "payment-problem", origin: TEAM }]) {
assert.deepEqual(await stoppedFor("member", machine), { screen: "plan", machine: machine.status, name: "Acme", member: true }, "a team's User is sent to its owner");
for (const role of ["owner", "admin"]) assert.equal((await stoppedFor(role, machine)).member, undefined, role);
}
assert.equal((await stoppedFor("member", null)).member, true, "no machine at all: stopped, for a User too");
});
test("on the team's page, a session that ends is renewed through the team pairing, the page counts as this app's, and its new version reloads at a quiet moment", async () => {
const w = world({ state: { ...connected(), teams: [ACME] } });
await w.home.show({ place: { team: "team-acme" } });
w.url = `${TEAM}/chats/b1`;
const frame = w.contents.mainFrame;
assert.equal(w.home.homePage({ sender: w.contents, senderFrame: frame }), true, "the team's page is this app's");
w.team.signedIn = false;
w.home.responded({ url: `${TEAM}/api/bots`, statusCode: 401, webContentsId: 7 });
await w.home.repair();
assert.deepEqual(w.went.at(-1), [`${TEAM}/pair?next=%2Fchats%2Fb1#code=TTTT-EEEE-AAAA`, TEAM, "team-acme"]);
assert.deepEqual(w.teamPairs, ["team-acme"]);
assert.equal(w.pairs, 0);
w.team.signedIn = true;
w.url = `${TEAM}/`;
w.home.navigated(w.url);
await w.tick();
w.team.version = "0.1.131"; w.typed = w.clock - QUIET_MS - 1;
await w.tick();
assert.equal(w.reloads, 1, "the team's page reloads for its own new version");
w.state = { status: "signed-out" };
await w.home.refresh();
assert.equal(w.home.homePage({ sender: w.contents, senderFrame: frame }), false, "signed out, the team's page is no longer this app's");
});
test("going from a team to another server lets the team go: a later session check never pulls the window back", async () => {
const w = world({ state: { ...connected(), teams: [ACME] } });
await w.home.show({ place: { team: "team-acme" } });
assert.equal(w.home.placeOrigin(), TEAM);
// The Server menu moved the window to a saved server (main's switchEnvironment), not through the home window.
w.remembered = null;
w.url = "https://office.example.test/";
w.home.navigated(w.url);
assert.equal(w.home.shown(), null);
assert.equal(w.home.placeOrigin(), CLOUD, "My Cloud is home again for the window's rules");
const went = w.went.length;
await w.home.changed();
assert.equal(w.went.length, went, "the session check left the window where the person put it");
// The team's own sign-in page or this computer's screen keep the team as the place.
await w.home.show({ place: { team: "team-acme" } });
w.home.navigated(`${LOCAL}${HOME_SCREEN_PATH}`);
assert.equal(w.home.placeOrigin(), TEAM);
});
test("a team on screen signs in again before its hour ends, without moving the page: through the Admin's team pairing, into the window's own cookie", async () => {
const w = world({ state: { ...connected(), teams: [ACME] }, team: { signedIn: false } });
await w.home.show({ place: { team: "team-acme" } });
w.url = `${TEAM}/chats/b1`;
const went = w.went.length;
await w.later(TEAM_RENEW_MS - 1);
assert.deepEqual(w.teamPairs, ["team-acme"], "not before its time");
await w.later(1);
assert.deepEqual(w.teamPairs, ["team-acme", "team-acme"]);
assert.deepEqual(w.redeemed, [["team", { code: "TTTT-EEEE-AAAA", cookie: true, label: "OpenMausBot app" }]]);
assert.equal(w.went.length, went, "the page never moved");
assert.equal(w.pairs, 0, "never My Cloud's pairing");
await w.later(TEAM_RENEW_MS);
assert.equal(w.teamPairs.length, 3, "and again each time");
// My Cloud is never renewed this way, and leaving the team stops it.
await w.home.show({ place: "cloud" });
await w.later(TEAM_RENEW_MS * 2);
assert.equal(w.teamPairs.length, 3);
assert.equal(w.redeemed.length, 2);
});
test("opening a team on the window's own session renews before the end that session reports; a failed renewal tries again soon", async () => {
const w = world({ state: { ...connected(), teams: [ACME] } });
w.team.expiresAt = w.clock + 20 * 60_000;
await w.home.show({ place: { team: "team-acme" } });
assert.deepEqual(w.teamPairs, [], "its own session: no pairing to open it");
await w.later(20 * 60_000 - TEAM_RENEW_EARLY_MS - 1);
assert.deepEqual(w.teamPairs, []);
await w.later(1);
assert.deepEqual(w.teamPairs, ["team-acme"], "ten minutes before the end it reports");
w.team.redeems = false;
await w.later(TEAM_RENEW_MS);
assert.equal(w.teamPairs.length, 2);
await w.later(TEAM_RENEW_RETRY_MS);
assert.equal(w.teamPairs.length, 3, "a code that didn't take is asked again in five minutes");
});
test("the Admin refusing the team pairing (404) means the person was removed: the team is dropped at once and My Cloud opens, on a renewal or a session that ended", async () => {
const w = world({ state: { ...connected(), teams: [ACME] }, team: { signedIn: false } });
await w.home.show({ place: { team: "team-acme" } });
w.team.gone = true;
await w.home.renew();
assert.deepEqual(w.dropped, ["team-acme"], "dropped now, not at the next session check");
assert.deepEqual(w.left, ["Acme"]);
assert.deepEqual(w.went.at(-1), [CLOUD, CLOUD]);
assert.equal(w.home.placeOrigin(), CLOUD);
assert.equal(w.redeemed.length, 0);
await w.later(TEAM_RENEW_MS * 2);
assert.equal(w.teamPairs.length, 2, "no renewal of a team that is gone");
// The same on the 401 path: the team's session ended and the re-pair is refused.
const ended = world({ state: { ...connected(), teams: [ACME] } });
await ended.home.show({ place: { team: "team-acme" } });
ended.url = `${TEAM}/`;
ended.team.signedIn = false; ended.team.gone = true;
ended.home.responded({ url: `${TEAM}/api/bots`, statusCode: 401, webContentsId: 7 });
await ended.home.repair();
assert.deepEqual(ended.dropped, ["team-acme"]);
assert.deepEqual(ended.went.at(-1), [CLOUD, CLOUD]);
});
test("while the window is on the place's own sign-in page, its 401s start no second sign-in (a team's and My Cloud's alike)", async () => {
const w = world({ state: { ...connected(), teams: [ACME] }, team: { signedIn: false } });
await w.home.show({ place: { team: "team-acme" } });
assert.deepEqual(w.teamPairs, ["team-acme"]);
assert.match(w.url, /^https:\/\/omb-u-acme\.fly\.dev\/pair#code=/);
w.home.responded({ url: `${TEAM}/api/auth/session`, statusCode: 401, webContentsId: 7 });
await w.later(0);
assert.deepEqual(w.teamPairs, ["team-acme"], "the code on its way is not replaced");
w.url = `${TEAM}/chats/b1`;
w.home.responded({ url: `${TEAM}/api/bots`, statusCode: 401, webContentsId: 7 });
await w.home.repair();
assert.deepEqual(w.teamPairs, ["team-acme", "team-acme"], "once on its page, a 401 signs in again as before");
const mine = world({ cloud: { signedIn: false } });
await mine.home.show();
mine.home.responded({ url: `${CLOUD}/api/auth/session`, statusCode: 401, webContentsId: 7 });
await mine.later(0);
assert.equal(mine.pairs, 1);
});
test("the usage meter's Upgrade: only from the person's click, only to a plan the verified session offers, only from this app's own pages", async () => {
for (const origin of [LOCAL, CLOUD]) {
const page = preload(origin);
await assert.rejects(page.bridge.cloudPlan.upgrade("max", "app_usage"), /Choose Upgrade/, "the change needs the confirm's own click on every page");
await assert.rejects(page.bridge.cloudPlan.upgradePreview("max", "app_usage"), /Choose Upgrade/, "so does the price");
const clicked = preload(origin, { activation: true });
await clicked.bridge.cloudPlan.upgradePreview("max", "app_usage");
await clicked.bridge.cloudPlan.upgrade("max", "app_usage");
assert.deepEqual(clicked.invoked, [["cloud-plan:upgrade-preview", "max", "app_usage"], ["cloud-plan:upgrade", "max", "app_usage"]]);
}
const ipc = pageIpc();
const event = { sender: ipc.cloudContents, senderFrame: ipc.cloudFrame };
ipc.account.usage = { tier: "pro", resetsAt: 1, meters: [], upgrades: [{ tier: "max", amount: 9900 }] };
await ipc.handlers.get("cloud-plan:upgrade-preview")(event, "max", "app_usage");
await ipc.handlers.get("cloud-plan:upgrade")(event, "max", "app_settings");
assert.deepEqual(ipc.calls, [["upgrade-preview", "max", "app_usage"], ["upgrade", "max", "app_settings"]]);
for (const [plan, source] of [["pro", "app_usage"], ["business", "app_usage"], ["max", "somewhere"], [{ tier: "max" }, "app_usage"]]) {
for (const channel of ["cloud-plan:upgrade-preview", "cloud-plan:upgrade"]) assert.throws(() => ipc.handlers.get(channel)(event, plan, source), /Choose a plan/);
}
for (const sender of [{ sender: ipc.cloudContents, senderFrame: { url: "https://other.example.test/" } }, { sender: {}, senderFrame: ipc.cloudFrame }]) {
for (const channel of ["cloud-plan:upgrade-preview", "cloud-plan:upgrade"]) assert.throws(() => ipc.handlers.get(channel)(sender, "max", "app_usage"), /only available/);
}
assert.equal(ipc.calls.length, 2);
});
test("Cancel and Keep my plan on the Cloud's own page: only from the person's click there, the survey's words only, and the plan's renewal shown read only", async () => {
const page = preload(CLOUD);
await assert.rejects(page.bridge.cloudPlan.cancel({ answer: "price" }), /Choose Cancel/, "the Cloud's page cancels only from the person's click");
await assert.rejects(page.bridge.cloudPlan.keep(), /Choose Keep my plan/);
const clicked = preload(CLOUD, { activation: true });
await clicked.bridge.cloudPlan.cancel({ answer: "price", improve: "words" }, { cloudId: "someone-elses" });
await clicked.bridge.cloudPlan.cancel();
await clicked.bridge.cloudPlan.keep({ cloudId: "someone-elses" });
assert.deepEqual(clicked.invoked, [["cloud-plan:cancel", { answer: "price", improve: "words" }], ["cloud-plan:cancel", null], ["cloud-plan:keep"]]);
const local = preload(LOCAL);
await local.bridge.cloudPlan.cancel({ answer: "later" });
await local.bridge.cloudPlan.keep();
assert.deepEqual(local.invoked, [["cloud-plan:cancel", { answer: "later" }], ["cloud-plan:keep"]]);
const ipc = pageIpc();
const event = { sender: ipc.cloudContents, senderFrame: ipc.cloudFrame };
ipc.account.renewal = { trial: true, cancelled: false, renewsAt: 1_900_000_000_000, endsAt: null, keptDays: 3 };
assert.deepEqual(plain(await ipc.handlers.get("cloud-plan:state")(event)).renewal, ipc.account.renewal);
await ipc.handlers.get("cloud-plan:cancel")(event, { answer: "setup" }, "https://evil.example.test");
await ipc.handlers.get("cloud-plan:keep")(event, { cloudId: "someone-elses" });
assert.deepEqual(ipc.calls, [["cancel", { answer: "setup" }], ["keep"]]);
for (const sender of [{ sender: ipc.cloudContents, senderFrame: { url: "https://other.example.test/" } }, { sender: {}, senderFrame: ipc.cloudFrame }]) {
for (const channel of ["cloud-plan:cancel", "cloud-plan:keep"]) assert.throws(() => ipc.handlers.get(channel)(sender, { answer: "price" }), /only available/);
}
assert.equal(ipc.calls.length, 2);
});
test("the room card on the person's own Cloud: the offer read only, Not now and Restart now only from their click there, for the session's own Cloud", async () => {
const page = preload(CLOUD);
await assert.rejects(page.bridge.cloudPlan.roomNotNow("offer-1"), /Choose Not now/);
await assert.rejects(page.bridge.cloudPlan.restartNow(), /Choose Restart now/);
const clicked = preload(CLOUD, { activation: true });
await clicked.bridge.cloudPlan.roomNotNow("offer-1", { cloudId: "someone-elses" });
await clicked.bridge.cloudPlan.restartNow({ cloudId: "someone-elses" });
await clicked.bridge.cloudPlan.upgradePreview("max", "app_room");
assert.deepEqual(clicked.invoked, [["cloud-plan:room-not-now", "offer-1"], ["cloud-plan:restart-now"], ["cloud-plan:upgrade-preview", "max", "app_room"]]);
const ipc = pageIpc();
const event = { sender: ipc.cloudContents, senderFrame: ipc.cloudFrame };
ipc.account.usage = { tier: "pro", resetsAt: 1, meters: [], upgrades: [{ tier: "max", amount: 9900 }] };
ipc.account.room = { id: "offer-1", cause: { kind: "memory", waits: 3, waitMinutes: 6, stopped: 0 },
plan: { tier: "max", amount: 9900, memoryMb: 8192, fromMemoryMb: 4096, diskGb: 100, fromDiskGb: 100 } };
assert.deepEqual(plain(await ipc.handlers.get("cloud-plan:state")(event)).room, ipc.account.room);
await ipc.handlers.get("cloud-plan:room-not-now")(event, "offer-1", { cloudId: "someone-elses" });
await ipc.handlers.get("cloud-plan:restart-now")(event, { cloudId: "someone-elses" });
await ipc.handlers.get("cloud-plan:upgrade-preview")(event, "max", "app_room");
assert.deepEqual(ipc.calls, [["room-not-now", "offer-1"], ["restart-now"], ["upgrade-preview", "max", "app_room"]]);
for (const sender of [{ sender: ipc.cloudContents, senderFrame: { url: "https://other.example.test/" } }, { sender: {}, senderFrame: ipc.cloudFrame }]) {
for (const channel of ["cloud-plan:room-not-now", "cloud-plan:restart-now"]) assert.throws(() => ipc.handlers.get(channel)(sender, "offer-1"), /only available/);
}
assert.equal(ipc.calls.length, 3);
});
test("another Cloud of the person's own is the place asked for by its id, from the session's list; one it no longer lists is simply My Cloud again", () => {
const state = { ...connected(), clouds: [{ ...CLIENT_A, id: "c-home", name: "My Cloud", origin: CLOUD, primary: true }, CLIENT_A] };
assert.deepEqual(placeOf({ ask: { cloud: "c-a" }, state }), { kind: "own", id: "c-a", name: "Client A", role: "owner", origin: CLIENT, machine: { status: "ready", origin: CLIENT } });
assert.deepEqual(placeOf({ remembered: { cloud: "c-a", name: "Client A", origin: CLIENT }, state }).kind, "own");
assert.deepEqual(placeOf({ ask: { cloud: "c-home" }, state }), { kind: "cloud" }, "My Cloud is never another Cloud");
assert.deepEqual(placeOf({ ask: { cloud: "c-a" }, state: connected() }), { kind: "cloud" }, "not listed (or an older Admin): My Cloud, nothing said");
assert.deepEqual(placeOf({ remembered: { cloud: "c-a", name: "Client A", origin: CLIENT }, state: { status: "unavailable" } }),
{ kind: "own", id: "c-a", name: "Client A", role: null, origin: CLIENT, machine: null }, "while the Admin can't be asked, as it was saved");
assert.deepEqual(placeOf({ ask: { cloud: "c-a" }, state: { ...state, clouds: [{ ...CLIENT_A, status: "setting_up", origin: null, setup: { step: "storage" } }] } }).machine,
{ status: "provisioning", setup: { step: "storage" } });
});
test("choosing another Cloud of the person's own opens it through the Admin's pairing for that Cloud, at its own address, remembered for next time; no team renewal", async () => {
const w = world({ state: { ...connected(), clouds: [CLIENT_A] }, client: { signedIn: false } });
await w.home.show({ place: { cloud: "c-a" } });
assert.deepEqual(w.ownPairs, ["c-a"]);
assert.equal(w.pairs, 0, "never My Cloud's pairing");
assert.deepEqual(w.went, [[`${CLIENT}/pair#code=CCCC-LLLL-AAAA`, CLIENT, "c-a"]]);
assert.deepEqual(w.fetched, [["client:/api/health", "1"], ["client:/api/auth/session", null]], "a look at that Cloud only");
assert.deepEqual(w.remembers, [{ cloud: "c-a" }]);
assert.equal(w.home.placeOrigin(), CLIENT);
assert.equal(w.timers.length, 0, "its session lasts as My Cloud's does: nothing to renew");
assert.deepEqual(w.home.view(), { screen: "opening", name: "Client A" });
});
test("another Cloud of the person's own that the Admin says is asleep is never asked for its version; once the Admin says it is awake, it is", async () => {
const w = world({ state: { ...connected(), clouds: [CLIENT_A] } });
await w.home.show({ place: { cloud: "c-a" } });
w.url = `${CLIENT}/`;
w.home.navigated(w.url);
await w.tick();
const versions = () => w.fetched.filter(([route]) => route === "client:/.well-known/openmausbot/environment");
const read = versions().length;
assert.ok(read > 0, "the shown Cloud's version is read while it is awake");
// The Clouds list says it sleeps (`power`, contract v1): any request reaching it would wake it.
w.state = { ...w.state, clouds: [{ ...CLIENT_A, power: { state: "asleep", since: 5, sleeps: true } }] };
await w.tick();
await w.tick();
assert.equal(versions().length, read, "a Cloud the Admin says is asleep is never asked");
assert.deepEqual([w.home.asleepAt(CLIENT), w.home.asleepAt(CLOUD)], [true, false], "and the quiet updater asks the same: only that Cloud sleeps");
w.state = { ...w.state, clouds: [{ ...CLIENT_A, power: { state: "awake", since: 6, sleeps: true } }] };
await w.tick();
assert.equal(versions().length, read + 1, "awake again, it is asked");
assert.deepEqual(versions().at(-1), ["client:/.well-known/openmausbot/environment", "1"], "as a background read");
});
test("another Cloud the Admin says is no longer theirs is dropped at once and My Cloud opens, with no line; one being set up shows its setup with its name", async () => {
const w = world({ state: { ...connected(), clouds: [CLIENT_A] }, client: { signedIn: false, gone: true } });
await w.home.show({ place: { cloud: "c-a" } });
assert.deepEqual(w.dropped, ["c-a"]);
assert.deepEqual(w.left, [], "a Cloud of their own leaving is no team they were removed from");
assert.deepEqual(w.went.at(-1), [CLOUD, CLOUD], "My Cloud");
const setup = world({ state: { ...connected(), clouds: [{ ...CLIENT_A, status: "setting_up", setup: { step: "starting" } }] } });
await setup.home.show({ place: { cloud: "c-a" } });
assert.deepEqual(setup.home.view(), { screen: "setup", step: "starting", name: "Client A" });
assert.deepEqual(setup.ownPairs, [], "never paired while it is set up");
});
test("an account leaving: nothing under way for it goes on, and the next show takes the window off its place", async () => {
const w = world({ journal: null, state: { status: "signed-out" }, url: `${CLOUD}/` });
w.home.accountLeaving();
assert.equal(w.home.shown(), null);
await w.home.show();
assert.deepEqual(w.local, [true], "the window showed that account's Cloud: this computer's page now");
const here = world({ journal: null, state: { status: "signed-out" }, url: `${LOCAL}/` });
here.home.accountLeaving();
await here.home.show();
assert.deepEqual(here.local, [false], "already on this computer's page: nothing moves");
});
const B_CLOUD = "https://omb-b.fly.dev";
const accountB = (machine) => ({ status: "connected", account: { id: "acct-2", email: "b@example.test" }, ...(machine ? { machine } : {}) });
test("moved to A's Cloud, B signed in with a Cloud of its own: B's Cloud opens, and A's frozen copy is never shown", () => {
assert.deepEqual(homeOf({ journal: done(), state: accountB({ status: "ready", origin: B_CLOUD }) }), { kind: "cloud", origin: B_CLOUD, otherAccount: true });
assert.deepEqual(homeOf({ journal: done(), state: accountB(null) }), { kind: "signin", other: true }, "B has no Cloud: the screen says where the bots are");
assert.deepEqual(homeOf({ journal: done(), state: accountB({ status: "stopped", hold: { until: 9 } }) }), { kind: "signin", other: true }, "B's Cloud ended: never this computer's frozen copy");
assert.deepEqual(homeOf({ journal: done(), state: { status: "unavailable", account: { id: "acct-2" } } }), { kind: "signin", other: true }, "before the Admin answers");
assert.deepEqual(homeOf({ journal: done(), state: { status: "reauth-required", account: { id: "acct-2" } } }), { kind: "signin", other: true });
});