Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions apps/host/tests/functional/fixtures/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,19 @@ export const BACKENDS = [

export type Backend = (typeof BACKENDS)[number];

/**
* How each network transport is named in a user story.
*
* Test titles are read by people deciding whether a behaviour is covered, so
* they name the transport the way the settings screen does rather than by its
* stored value.
*/
export const TRANSPORT_LABELS: Record<Backend, string> = {
"smoldot-shared-worker": "shared smoldot",
"smoldot-direct": "smoldot per app",
"rpc-gateway": "trusted provider",
};

export interface CacheSeed {
skipCidCache: boolean;
skipArchiveCache: boolean;
Expand Down
25 changes: 17 additions & 8 deletions apps/host/tests/functional/host-settings.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import {
CACHE_ENABLED,
SKIP_ARCHIVE_ONLY,
SKIP_CID_ONLY,
TRANSPORT_LABELS,
updateCacheSettings,
} from "./fixtures/settings";
import { test } from "./helpers/shared-mode-reset";
Expand Down Expand Up @@ -101,7 +102,7 @@ test.describe("Settings works", () => {
});

for (const backend of BACKENDS) {
test(`As a user opening a link that selects ${backend}, my session runs in that mode and stays there`, async ({
test(`As a user opening a link that selects ${TRANSPORT_LABELS[backend]}, my session runs in that mode and stays there`, async ({
page,
}) => {
// When
Expand Down Expand Up @@ -130,7 +131,10 @@ test.describe("Settings works", () => {
// Then
const state = await readChainBackendState(page, "smoldot-direct");
expect(state.chainBackend).toBe("smoldot-direct");
expect(state.url).toContain("chainBackend=smoldot-direct");
// The link asked for the default mode, and a default axis is stripped from
// the address bar, so landing in it leaves a clean URL rather than one
// that still names it. See the contract in `packages/config/url-settings`.
expect(state.url).not.toContain("chainBackend=");
});

test("As a user who arrived through such a link, reloading without it keeps me in the mode I landed in", async ({
Expand Down Expand Up @@ -234,11 +238,13 @@ test.describe("Settings works", () => {
expect(cache.skipWorkerCache).toBe(false);
expect(state.url).toContain("skipCidCache=1");
expect(state.url).toContain("skipArchiveCache=1");
expect(state.url).toContain("skipWorkerCache=0");
// The worker cache was left at its default, so it is stripped rather than
// written back as `=0`. Only the axes I actually changed travel in the link.
expect(state.url).not.toContain("skipWorkerCache=");
});

for (const backend of BACKENDS) {
test(`As a user on ${backend} with the dotNS cache on, revisiting a site skips looking its name up again`, async ({
test(`As a user on ${TRANSPORT_LABELS[backend]} with the dotNS cache on, revisiting a site skips looking its name up again`, async ({
browser,
}) => {
// Given
Expand All @@ -263,7 +269,7 @@ test.describe("Settings works", () => {
}
});

test(`As a user on ${backend} who turns the dotNS cache off, every visit looks the name up again`, async ({
test(`As a user on ${TRANSPORT_LABELS[backend]} who turns the dotNS cache off, every visit looks the name up again`, async ({
browser,
}) => {
// Given
Expand All @@ -283,15 +289,18 @@ test.describe("Settings works", () => {
// Then
await waitForResolutionOutcome(page, TIMEOUT_MS, backend);
expect(await hostResolveStarted(page)).toBe(true);
expect(await hasCachedCid(page, DOMAIN)).toBe(true);
// Changing a cache setting wipes this origin, so the entry saved on the
// first visit is gone rather than merely ignored, and with the cache off
// nothing writes a new one. See the wipe in `applyUrlSettings`.
expect(await hasCachedCid(page, DOMAIN)).toBe(false);
} finally {
await context.close();
}
});
}

for (const backend of BACKENDS) {
test(`As a user on ${backend} with the archive cache on, revisiting a site checks my local copy first`, async ({
test(`As a user on ${TRANSPORT_LABELS[backend]} with the archive cache on, revisiting a site checks my local copy first`, async ({
browser,
}) => {
// Given
Expand All @@ -315,7 +324,7 @@ test.describe("Settings works", () => {
}
});

test(`As a user on ${backend} who turns the archive cache off, the site is fetched fresh instead of from my local copy`, async ({
test(`As a user on ${TRANSPORT_LABELS[backend]} who turns the archive cache off, the site is fetched fresh instead of from my local copy`, async ({
browser,
}) => {
// Given
Expand Down
26 changes: 13 additions & 13 deletions apps/host/tests/functional/loading.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ async function shrinkTimeout(
);
}

test("As a user using smoldot directly, when the light client panics mid-resolution, I see the appropriate error and can switch backend", async ({
test("As a user on smoldot per app, when the light client panics mid-resolution, I see the appropriate error and can switch network transport", async ({
page,
}) => {
// Given
Expand All @@ -183,7 +183,7 @@ test("As a user using smoldot directly, when the light client panics mid-resolut
);
});

test("As a user using smoldot in shared worker, when the light client panics mid-resolution, I see the appropriate error and can switch backend", async ({
test("As a user on shared smoldot, when the light client panics mid-resolution, I see the appropriate error and can switch network transport", async ({
page,
}) => {
// Given
Expand All @@ -209,7 +209,7 @@ test("As a user using smoldot in shared worker, when the light client panics mid
);
});

test("As a user using smoldot in shared worker, when the browser can't create a worker, I see the appropriate error and can switch backend", async ({
test("As a user on shared smoldot, when the browser can't create a worker, I see the appropriate error and can switch network transport", async ({
page,
}) => {
// Given
Expand Down Expand Up @@ -238,7 +238,7 @@ test("As a user using smoldot in shared worker, when the browser can't create a
);
});

test("As a user using smoldot in shared worker, when the worker dies silently, I see the appropriate error and can switch backend", async ({
test("As a user on shared smoldot, when the worker dies silently, I see the appropriate error and can switch network transport", async ({
page,
}) => {
// Given
Expand Down Expand Up @@ -267,7 +267,7 @@ test("As a user using smoldot in shared worker, when the worker dies silently, I
);
});

test("As a user using smoldot directly, when loading is slow (>10s) I see a one-click gateway escape, and if it times out (>45s) I see the appropriate error and can switch backend", async ({
test("As a user on smoldot per app, when loading is slow (>10s) I see a one-click escape to a trusted provider, and if it times out (>45s) I see the appropriate error and can switch network transport", async ({
page,
}) => {
// Given
Expand Down Expand Up @@ -304,7 +304,7 @@ test("As a user using smoldot directly, when loading is slow (>10s) I see a one-
);
});

test("As a user using smoldot in shared worker, when loading is slow (>10s) I see a one-click gateway escape, and if it times out (>45s) I see the appropriate error and can switch backend", async ({
test("As a user on shared smoldot, when loading is slow (>10s) I see a one-click escape to a trusted provider, and if it times out (>45s) I see the appropriate error and can switch network transport", async ({
page,
}) => {
// Given
Expand Down Expand Up @@ -341,7 +341,7 @@ test("As a user using smoldot in shared worker, when loading is slow (>10s) I se
);
});

test("As a user using smoldot directly, when I click the gateway escape, the backend flips to rpc-gateway and the page reloads", async ({
test("As a user on smoldot per app, when I click the escape, my network transport becomes the trusted provider and the page reloads", async ({
page,
}) => {
// Given
Expand Down Expand Up @@ -391,7 +391,7 @@ test("As a user, when the app chunks fail to load mid-session, I see the appropr
await expect(page.locator("#error-retry-btn")).toContainText("Reload");
});

test("As a user using smoldot directly, when smoldot rejects the chain spec, I see the appropriate error and can switch backend", async ({
test("As a user on smoldot per app, when smoldot rejects the chain spec, I see the appropriate error and can switch network transport", async ({
page,
}) => {
// Given
Expand Down Expand Up @@ -420,7 +420,7 @@ test("As a user using smoldot directly, when smoldot rejects the chain spec, I s
);
});

test("As a user using smoldot in shared worker, when smoldot rejects the chain spec, I see the appropriate error and can switch backend", async ({
test("As a user on shared smoldot, when smoldot rejects the chain spec, I see the appropriate error and can switch network transport", async ({
page,
}) => {
// Given
Expand Down Expand Up @@ -497,7 +497,7 @@ test("As a user, when the domain's contenthash is unsupported or malformed, I se
await expect(page.locator("#error-retry-btn")).toHaveCount(0);
});

test("As a user, after a resolution failure, clicking retry switches backend and the app loads successfully", async ({
test("As a user, after a resolution failure, clicking retry switches my network transport and the app loads successfully", async ({
page,
}) => {
// Given
Expand All @@ -524,7 +524,7 @@ test("As a user, after a resolution failure, clicking retry switches backend and
expect(backendAfter).toBe("rpc-gateway");
});

test("As a user, after a resolution failure, I can refresh instead of switching backend, and the backend stays unchanged", async ({
test("As a user, after a resolution failure, I can refresh instead of switching network transport, and my network transport stays unchanged", async ({
page,
}) => {
// Given
Expand Down Expand Up @@ -557,10 +557,10 @@ test("As a user, after a resolution failure, I can refresh instead of switching
});

for (const [label, backend] of [
["per-product smoldot", "smoldot-direct"],
["smoldot per app", "smoldot-direct"],
["shared smoldot", "smoldot-shared-worker"],
] as const) {
test(`As a user using ${label}, the host must only spawn one instance of the light client`, async ({
test(`As a user on ${label}, I only ever get one light client, never one per app`, async ({
page,
}) => {
// Given
Expand Down
24 changes: 12 additions & 12 deletions apps/host/tests/functional/navigation.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ async function seedBackend(page: Page): Promise<void> {
}

test.describe("URL parameters are forwarded into the product", () => {
test("when I open http://<label>.dot.li/foo?a=b#h, I land on /foo?a=b#h inside the product", async ({
test("As a user, when I open http://<label>.dot.li/foo?a=b#h, I land on /foo?a=b#h inside the product", async ({
page,
}) => {
// Given
Expand All @@ -52,7 +52,7 @@ test.describe("URL parameters are forwarded into the product", () => {
expect(loc.hash).toBe("#h");
});

test("when I open http://<label>.dot.li/foo%20bar, the percent-encoding survives into the product pathname", async ({
test("As a user, when I open http://<label>.dot.li/foo%20bar, the percent-encoding survives into the product pathname", async ({
page,
}) => {
// Given
Expand All @@ -67,7 +67,7 @@ test.describe("URL parameters are forwarded into the product", () => {
expect(loc.pathname).toBe("/foo%20bar");
});

test("when I open http://<label>.dot.li/?a=1&a=2, both values reach the product", async ({
test("As a user, when I open http://<label>.dot.li/?a=1&a=2, both values reach the product", async ({
page,
}) => {
// Given
Expand All @@ -82,7 +82,7 @@ test.describe("URL parameters are forwarded into the product", () => {
expect(new URLSearchParams(loc.search).getAll("a")).toEqual(["1", "2"]);
});

test("when I open http://<label>.dot.li/?a=, the empty query value reaches the product", async ({
test("As a user, when I open http://<label>.dot.li/?a=, the empty query value reaches the product", async ({
page,
}) => {
// Given
Expand All @@ -102,7 +102,7 @@ test.describe("Host URL bar preserves the entered URL after render", () => {
// `applyUrlSettings` canonicalises the URL on every load so non-default
// settings axes (rpc-gateway here) get re-inserted. Assert the user's
// own params survive, not that canonicalisation is a no-op.
test("after the product renders from http://<label>.dot.li/foo?a=b#h, the URL bar still shows /foo?a=b#h", async ({
test("As a user, after the product renders from http://<label>.dot.li/foo?a=b#h, the URL bar still shows /foo?a=b#h", async ({
page,
}) => {
// Given
Expand All @@ -121,7 +121,7 @@ test.describe("Host URL bar preserves the entered URL after render", () => {
});

test.describe("Reloading the page preserves the URL", () => {
test("when I reload http://<label>.dot.li/foo?a=b, the path and query survive the reload", async ({
test("As a user, when I reload http://<label>.dot.li/foo?a=b, the path and query survive the reload", async ({
page,
}) => {
// Given
Expand All @@ -142,7 +142,7 @@ test.describe("Reloading the page preserves the URL", () => {
});

test.describe("Sandbox URL hygiene: host contract keys never reach the product", () => {
test("with a cold cache, when the product loads, the host contract keys are not visible in the product's URL", async ({
test("As a user with a cold cache, when the product loads, the host contract keys are not visible in the product's URL", async ({
page,
}) => {
// Given
Expand All @@ -158,7 +158,7 @@ test.describe("Sandbox URL hygiene: host contract keys never reach the product",
assertNoContractKeys(loc.search);
});

test("with a warm cache, when the product loads, the host contract keys are still not visible in the product's URL", async ({
test("As a user with a warm cache, when the product loads, the host contract keys are still not visible in the product's URL", async ({
page,
}) => {
// Given
Expand All @@ -180,7 +180,7 @@ test.describe("Sandbox URL hygiene: host contract keys never reach the product",
});

test.describe("Validator regression guards", () => {
test("when the sandbox receives an unknown chainBackend value, the sandbox renders an error page instead of guessing a default", async ({
test("As a user, when the sandbox receives an unknown network transport value, the sandbox renders an error page instead of guessing a default", async ({
browser,
}) => {
// Given
Expand Down Expand Up @@ -219,7 +219,7 @@ test.describe("Validator regression guards", () => {
}
});

test("when I open http://<label>.dot.li/?ref=42, the unknown key reaches the product and does not trigger the validator", async ({
test("As a user, when I open http://<label>.dot.li/?ref=42, the unknown key reaches the product and does not trigger the validator", async ({
page,
}) => {
// Given
Expand All @@ -245,7 +245,7 @@ test.describe("Validator regression guards", () => {
assertNoContractKeys(loc.search);
});

test("when I open http://<label>.dot.li/?chainBackend=foo, the host's valid value wins and my value is dropped from the product's URL", async ({
test("As a user, when I open http://<label>.dot.li/?chainBackend=foo, the host's valid network transport wins and my value is dropped from the product's URL", async ({
page,
}) => {
// Given
Expand All @@ -269,7 +269,7 @@ test.describe("Validator regression guards", () => {
});

test.describe("Sandbox side-effects from URL contract keys", () => {
test("when I open http://<label>.dot.li/?fullReset=1, sandbox-origin IndexedDB is purged before the product loads", async ({
test("As a user, when I open http://<label>.dot.li/?fullReset=1, sandbox-origin IndexedDB is purged before the product loads", async ({
browser,
}) => {
// Given
Expand Down
6 changes: 3 additions & 3 deletions apps/host/tests/functional/resolution.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,16 @@
import { DOMAIN, DOTNS_NAME, PORT, TIMEOUT_MS } from "../env";
import { setupTest } from "./helpers/context";
import { waitForResolutionOutcome } from "../product-frame";
import { BACKENDS } from "./fixtures/settings";
import { BACKENDS, TRANSPORT_LABELS } from "./fixtures/settings";
import { test } from "./helpers/shared-mode-reset";

const BASE_URL = `http://${DOMAIN}.localhost:${PORT}/`;

test.setTimeout(BACKENDS.length * TIMEOUT_MS * 2);

test.describe("Resolution across chain backends", () => {
test.describe("Resolution across network transports", () => {
for (const backend of BACKENDS) {
test(`As a user opening ${DOTNS_NAME} via ${backend}, the shell loads the app`, async ({
test(`As a user opening ${DOTNS_NAME} on ${TRANSPORT_LABELS[backend]}, the shell loads the app`, async ({
browser,
}) => {
// Given
Expand Down
Loading
Loading