fix(desktop): resolve flaky integration tests via project-level assertion timeout (#812)

This commit is contained in:
Will Pfleger
2026-06-01 18:48:37 -04:00
committed by GitHub
parent 93183cb75d
commit 6481428e2b
11 changed files with 38 additions and 57 deletions
+3
View File
@@ -48,6 +48,9 @@ export default defineConfig({
use: {
...devices["Desktop Chrome"],
},
expect: {
timeout: process.env.CI ? 15_000 : 10_000,
},
},
],
webServer: {
@@ -11,7 +11,7 @@ export function useActiveChannelHeader(
currentPubkey?: string,
) {
const activeDmParticipantPubkeys = React.useMemo(() => {
if (!activeChannel || activeChannel.channelType !== "dm") {
if (activeChannel?.channelType !== "dm") {
return [];
}
@@ -18,7 +18,7 @@ type TypingIndicatorRowProps = {
};
function resolveFallbackName(channel: Channel | null, pubkey: string) {
if (!channel || channel.channelType !== "dm") {
if (channel?.channelType !== "dm") {
return null;
}
@@ -20,9 +20,9 @@ test("move up succeeds: middle section swaps order with the one above", () => {
const result = swapSectionOrder(store, "b", "up");
assert.notEqual(result, null);
const byId = Object.fromEntries(result.sections.map((s) => [s.id, s.order]));
assert.equal(byId["b"], 0);
assert.equal(byId["a"], 1);
assert.equal(byId["c"], 2);
assert.equal(byId.b, 0);
assert.equal(byId.a, 1);
assert.equal(byId.c, 2);
});
test("move down succeeds: middle section swaps order with the one below", () => {
@@ -34,9 +34,9 @@ test("move down succeeds: middle section swaps order with the one below", () =>
const result = swapSectionOrder(store, "b", "down");
assert.notEqual(result, null);
const byId = Object.fromEntries(result.sections.map((s) => [s.id, s.order]));
assert.equal(byId["b"], 2);
assert.equal(byId["c"], 1);
assert.equal(byId["a"], 0);
assert.equal(byId.b, 2);
assert.equal(byId.c, 1);
assert.equal(byId.a, 0);
});
test("move up at top boundary returns null", () => {
@@ -73,7 +73,7 @@ test("non-contiguous orders: swap uses actual order values not indices", () => {
const result = swapSectionOrder(store, "b", "up");
assert.notEqual(result, null);
const byId = Object.fromEntries(result.sections.map((s) => [s.id, s.order]));
assert.equal(byId["b"], 0);
assert.equal(byId["a"], 5);
assert.equal(byId["c"], 10);
assert.equal(byId.b, 0);
assert.equal(byId.a, 5);
assert.equal(byId.c, 10);
});
+1 -1
View File
@@ -90,7 +90,7 @@ export function useUserStatusSubscription() {
function handleStatusEvent(event: RelayEvent) {
if (isCancelled) return;
const dTag = event.tags.find((t) => t[0] === "d");
if (!dTag || dTag[1] !== "general") return;
if (dTag?.[1] !== "general") return;
const parsed = parseUserStatusEvent(event);
const status: UserStatus | null =
parsed.text || parsed.emoji
+1 -1
View File
@@ -564,7 +564,7 @@ export class RelayClient {
return async () => {
const active = this.subscriptions.get(subId);
if (!active || active.mode !== "live") {
if (active?.mode !== "live") {
return;
}
@@ -6,7 +6,7 @@ import { resolveFileCard } from "./markdownFileCard.ts";
// A generic-file URL (non-media extension) does not match the relay-media
// proxy regex, so `rewriteRelayUrl` passes it through unchanged — assertions
// can compare hrefs directly.
const PDF_URL = "https://relay.example/media/" + "a".repeat(64) + ".pdf";
const PDF_URL = `https://relay.example/media/${"a".repeat(64)}.pdf`;
test("resolveFileCard: returns null when there is no imeta entry", () => {
assert.equal(resolveFileCard(undefined, PDF_URL, ""), null);
@@ -62,12 +62,12 @@ test("resolveFileCard: falls back to link child text when imeta has no filename"
test("resolveFileCard: falls back to URL tail when no filename or child text", () => {
const card = resolveFileCard({ m: "application/octet-stream" }, PDF_URL, "");
assert.equal(card?.filename, "a".repeat(64) + ".pdf");
assert.equal(card?.filename, `${"a".repeat(64)}.pdf`);
});
test("resolveFileCard: octet-stream (no magic bytes) is treated as a file", () => {
// Text/code/data upload with no magic signature — the Slack-like case.
const url = "https://relay.example/media/" + "b".repeat(64) + ".txt";
const url = `https://relay.example/media/${"b".repeat(64)}.txt`;
const card = resolveFileCard(
{ m: "application/octet-stream", filename: "notes.txt" },
url,
+1 -1
View File
@@ -4298,7 +4298,7 @@ function resolveMockUploadDescriptors(
if (configured !== undefined) return configured;
return [
{
url: "https://mock.relay/media/" + "a".repeat(64) + ".pdf",
url: `https://mock.relay/media/${"a".repeat(64)}.pdf`,
sha256: "a".repeat(64),
size: 12345,
type: "application/pdf",
+5 -25
View File
@@ -6,7 +6,6 @@ import { assertRelaySeeded } from "../helpers/seed";
const isCi = Boolean(process.env.CI);
const relaySeedHookTimeoutMs = isCi ? 90_000 : 30_000;
const relayDeliveryTimeoutMs = isCi ? 15_000 : 10_000;
async function createStream(
page: import("@playwright/test").Page,
@@ -276,11 +275,7 @@ test("live mentions refetch the home feed without waiting for polling", async ({
message,
);
await expect
.poll(() => getLoggedNotificationCount(targetPage), {
timeout: relayDeliveryTimeoutMs,
})
.toBe(1);
await expect.poll(() => getLoggedNotificationCount(targetPage)).toBe(1);
const notifications = await getLoggedNotifications(targetPage);
@@ -300,14 +295,9 @@ test("live mentions refetch the home feed without waiting for polling", async ({
await expect(targetPage.getByTestId("home-inbox-list")).toBeVisible();
await expect(targetPage.getByTestId("home-inbox-list")).toContainText(
message,
{ timeout: relayDeliveryTimeoutMs },
);
await expect(targetPage.getByTestId("sidebar-home-count")).toHaveCount(0);
await expect
.poll(() => getLoggedNotificationCount(targetPage), {
timeout: relayDeliveryTimeoutMs,
})
.toBe(1);
await expect.poll(() => getLoggedNotificationCount(targetPage)).toBe(1);
} finally {
await targetContext.close();
await senderContext.close();
@@ -345,15 +335,9 @@ test("live forum mentions refetch the home feed without waiting for polling", as
mentionPubkeys: [TEST_IDENTITIES.tyler.pubkey],
});
await expect(targetPage.getByTestId("sidebar-home-count")).toHaveText("1", {
timeout: relayDeliveryTimeoutMs,
});
await expect(targetPage.getByTestId("sidebar-home-count")).toHaveText("1");
await expect
.poll(() => getLoggedNotificationCount(targetPage), {
timeout: relayDeliveryTimeoutMs,
})
.toBe(1);
await expect.poll(() => getLoggedNotificationCount(targetPage)).toBe(1);
const notifications = await getLoggedNotifications(targetPage);
@@ -371,11 +355,7 @@ test("live forum mentions refetch the home feed without waiting for polling", as
message,
);
await expect(targetPage.getByTestId("sidebar-home-count")).toHaveCount(0);
await expect
.poll(() => getLoggedNotificationCount(targetPage), {
timeout: relayDeliveryTimeoutMs,
})
.toBe(1);
await expect.poll(() => getLoggedNotificationCount(targetPage)).toBe(1);
} finally {
await targetContext.close();
await senderContext.close();
+3 -10
View File
@@ -4,13 +4,10 @@ import { installRelayBridge, TEST_IDENTITIES } from "../helpers/bridge";
import { assertRelaySeeded } from "../helpers/seed";
const isCi = Boolean(process.env.CI);
const relayDeliveryTimeoutMs = isCi ? 15_000 : 5_000;
const relaySeedHookTimeoutMs = isCi ? 90_000 : 30_000;
async function expectTimelineToContain(page: Page, text: string) {
await expect(page.getByTestId("message-timeline")).toContainText(text, {
timeout: relayDeliveryTimeoutMs,
});
await expect(page.getByTestId("message-timeline")).toContainText(text);
}
async function getTimelineMetrics(page: Page) {
@@ -178,9 +175,7 @@ test("loads the home feed from the relay", async ({ browser }) => {
mentionPubkeys: [TEST_IDENTITIES.tyler.pubkey],
});
await expect(page.getByTestId("home-inbox-list")).toContainText(message, {
timeout: relayDeliveryTimeoutMs,
});
await expect(page.getByTestId("home-inbox-list")).toContainText(message);
await expect(page.getByTestId("home-inbox-detail")).toBeVisible();
} finally {
await targetContext.close();
@@ -210,9 +205,7 @@ test("shows sent inbox replies immediately in the inbox detail pane", async ({
mentionPubkeys: [TEST_IDENTITIES.tyler.pubkey],
});
await page.getByTestId("home-inbox-list").getByText(message).click({
timeout: relayDeliveryTimeoutMs,
});
await page.getByTestId("home-inbox-list").getByText(message).click();
await expect(page.getByTestId("home-inbox-detail")).toBeVisible();
await expect(page.getByTestId("message-input")).toBeEnabled();
+10 -5
View File
@@ -42,7 +42,8 @@ export function useGitTree(
return useQuery({
queryKey: ["git-tree", owner, repoName, ref, path ?? ""],
queryFn: async () => {
const { fs, dir } = cloneQuery.data!;
if (!cloneQuery.data) throw new Error("unreachable: enabled guards data");
const { fs, dir } = cloneQuery.data;
const oid = await resolveRef({ fs, dir, ref });
const entries = await readTreeEntries(fs, dir, oid, path || undefined);
@@ -65,7 +66,8 @@ export function useGitLog(owner: string, repoName: string, ref: string) {
return useQuery({
queryKey: ["git-log", owner, repoName, ref],
queryFn: async () => {
const { fs, dir } = cloneQuery.data!;
if (!cloneQuery.data) throw new Error("unreachable: enabled guards data");
const { fs, dir } = cloneQuery.data;
return getCommitLog(fs, dir, ref);
},
enabled: !!cloneQuery.data,
@@ -80,7 +82,8 @@ export function useGitReadme(owner: string, repoName: string, ref: string) {
return useQuery({
queryKey: ["git-readme", owner, repoName, ref],
queryFn: async () => {
const { fs, dir } = cloneQuery.data!;
if (!cloneQuery.data) throw new Error("unreachable: enabled guards data");
const { fs, dir } = cloneQuery.data;
return findReadme(fs, dir, ref);
},
enabled: !!cloneQuery.data,
@@ -100,7 +103,8 @@ export function useGitBlob(
return useQuery({
queryKey: ["git-blob", owner, repoName, ref, filepath],
queryFn: async () => {
const { fs, dir } = cloneQuery.data!;
if (!cloneQuery.data) throw new Error("unreachable: enabled guards data");
const { fs, dir } = cloneQuery.data;
const oid = await resolveRef({ fs, dir, ref });
return readBlobView(fs, dir, oid, filepath);
},
@@ -128,7 +132,8 @@ export function useGitHtmlDoc(
return useQuery({
queryKey: ["git-html-doc", owner, repoName, ref, filepath],
queryFn: async () => {
const { fs, dir } = cloneQuery.data!;
if (!cloneQuery.data) throw new Error("unreachable: enabled guards data");
const { fs, dir } = cloneQuery.data;
const oid = await resolveRef({ fs, dir, ref });
return resolveHtmlAssets(fs, dir, oid, filepath, html);
},