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