Repository: SiaFoundation/web
Pull request #1045: fix(e2e): stabilize e2e tests
Author: login: alexfreska
Created: 2025-05-02T14:42:16Z | Merged: 2025-05-02T21:30:12Z

PR with a bunch of improvements found while debugging the cluster startup issues:

- Update cluster deps.
- Improve flaky hostd volume test.
- Removed unnecessary `^build` from the explorer-e2e e2e target (should make startup _much_ faster, it was doing a production (slow) build of explorer that was not even being used cc @telestrial)
- Fixed `waitFor`. If the condition hung or errored would prevent natural test timeout on CI.
- Fixed -explored=1 startup errors in `cmd`.


Diff:
diff --git a/.changeset/wild-pianos-hammer.md b/.changeset/wild-pianos-hammer.md
new file mode 100644
index 000000000..c0c32b2f7
--- /dev/null
+++ b/.changeset/wild-pianos-hammer.md
@@ -0,0 +1,5 @@
+---
+'@siafoundation/clusterd': patch
+---
+
+waitFor now waits for the current condition to return before next iteration.
diff --git a/apps/explorer-e2e/project.json b/apps/explorer-e2e/project.json
index 9410e9101..86a04b245 100644
--- a/apps/explorer-e2e/project.json
+++ b/apps/explorer-e2e/project.json
@@ -24,7 +24,7 @@
     "e2e": {
       "executor": "@nx/playwright:playwright",
       "outputs": ["{workspaceRoot}/dist/.playwright/apps/explorer-e2e"],
-      "dependsOn": ["^build", "build-cluster"],
+      "dependsOn": ["build-cluster"],
       "options": {
         "config": "apps/explorer-e2e/playwright.config.ts"
       }
diff --git a/apps/hostd-e2e/src/fixtures/volumes.ts b/apps/hostd-e2e/src/fixtures/volumes.ts
index a5fe04a4c..ed25c581d 100644
--- a/apps/hostd-e2e/src/fixtures/volumes.ts
+++ b/apps/hostd-e2e/src/fixtures/volumes.ts
@@ -46,7 +46,16 @@ export const deleteVolume = step(
       page.getByText('Volume is now being permanently deleted')
     ).toBeVisible()
     const row = page.getByRole('row', { name: fullPath })
-    await expect(row.getByText('removing')).toBeVisible()
+    await expect
+      .poll(
+        async () =>
+          (await row.getByText('removing').isVisible()) ||
+          (await page
+            .getByTestId('volumesTable')
+            .getByText(fullPath)
+            .isHidden())
+      )
+      .toBe(true)
   }
 )
 
diff --git a/internal/cluster/cmd/clusterd/main.go b/internal/cluster/cmd/clusterd/main.go
index aa9f9c21f..dae103765 100644
--- a/internal/cluster/cmd/clusterd/main.go
+++ b/internal/cluster/cmd/clusterd/main.go
@@ -76,7 +76,7 @@ func main() {
 
 	zap.RedirectStdLog(log)
 
-	if hostdCount == 0 && renterdCount == 0 && walletdCount == 0 {
+	if hostdCount == 0 && renterdCount == 0 && walletdCount == 0 && exploredCount == 0 {
 		log.Panic("no nodes to run")
 	}
 
diff --git a/internal/cluster/go.mod b/internal/cluster/go.mod
index db6c61cd8..4f373f45b 100644
--- a/internal/cluster/go.mod
+++ b/internal/cluster/go.mod
@@ -5,7 +5,7 @@ go 1.23.2
 toolchain go1.23.4
 
 require (
-	go.sia.tech/cluster v0.1.3-0.20250429184218-965bc9400872
+	go.sia.tech/cluster v0.1.3-0.20250502195935-d28b7bd4e6de
 	go.sia.tech/core v0.12.0
 	go.sia.tech/coreutils v0.13.2
 	go.sia.tech/explored v0.0.0-20250430142716-e1daab1b0823
diff --git a/internal/cluster/go.sum b/internal/cluster/go.sum
index 21e8110fa..a60f97e37 100644
--- a/internal/cluster/go.sum
+++ b/internal/cluster/go.sum
@@ -80,8 +80,8 @@ github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOf
 github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
 go.etcd.io/bbolt v1.4.0 h1:TU77id3TnN/zKr7CO/uk+fBCwF2jGcMuw2B/FMAzYIk=
 go.etcd.io/bbolt v1.4.0/go.mod h1:AsD+OCi/qPN1giOX1aiLAha3o1U8rAz65bvN4j0sRuk=
-go.sia.tech/cluster v0.1.3-0.20250429184218-965bc9400872 h1:FbopCeklP8KdEjRdINo7oPIaqQEEEphHtOvC8K8P1b4=
-go.sia.tech/cluster v0.1.3-0.20250429184218-965bc9400872/go.mod h1:5t5ttkrhFeb1YT4mB1s8sptTpLoBBw5JXuEQl/Mv4ic=
+go.sia.tech/cluster v0.1.3-0.20250502195935-d28b7bd4e6de h1:WA7055dBXpy+qUxeyq+HOWj3HdwU/q140epg9mq/tnI=
+go.sia.tech/cluster v0.1.3-0.20250502195935-d28b7bd4e6de/go.mod h1:SrD1eLrQr2ocxqiFjJ2dFdLEPsWFczEcqGqqKslv/Gw=
 go.sia.tech/core v0.12.0 h1:nuHjUE3MnYPQ+BKo44DD64332jBGVZlH657c5RIomXw=
 go.sia.tech/core v0.12.0/go.mod h1:ycpNTb9Y7Vtnq6HQ3iqOxeywLnjDs0udD3ZVq6KE13Y=
 go.sia.tech/coreutils v0.13.2 h1:SlxQ6onhdjGrTB8I/TxFwIbFTM0mzH5amDoAQMkjmOI=
diff --git a/libs/clusterd/src/index.ts b/libs/clusterd/src/index.ts
index dc4a43d0f..4bb86dd98 100644
--- a/libs/clusterd/src/index.ts
+++ b/libs/clusterd/src/index.ts
@@ -251,15 +251,30 @@ function waitFor(
   }: { timeout?: number; interval?: number } = {}
 ) {
   return new Promise<void>((resolve, reject) => {
-    const intervalRef = setInterval(async () => {
-      if (await condition()) {
-        clearInterval(intervalRef)
-        resolve()
+    const startTime = Date.now()
+
+    const checkCondition = async () => {
+      try {
+        // Check if condition is met.
+        if (await condition()) {
+          resolve()
+          return
+        }
+
+        // Check if we've exceeded the timeout.
+        if (Date.now() - startTime >= timeout) {
+          reject(new Error('Timeout'))
+          return
+        }
+
+        // Schedule next check after this one is complete.
+        setTimeout(checkCondition, interval)
+      } catch (error) {
+        reject(error)
       }
-    }, interval)
-    setTimeout(() => {
-      clearInterval(intervalRef)
-      reject(new Error('Timeout'))
-    }, timeout)
+    }
+
+    // Start the first check.
+    checkCondition()
   })
 }
diff --git a/libs/design-system/README.md b/libs/design-system/README.md
index 72118055b..3050dec10 100644
--- a/libs/design-system/README.md
+++ b/libs/design-system/README.md
@@ -2,6 +2,6 @@
 
 Welcome to the Sia Design System. The Design System is build with React, Radix, and Tailwind.
 
-## Running unit tests
+## Testing
 
 Run `nx test design-system` to execute the unit tests via [Jest](https://jestjs.io).


Reviews:
user: login: copilot-pull-request-reviewer[bot]
body: ## Pull Request Overview

This PR aims to stabilize e2e tests by adjusting both production logging and test polling logic.  
- Added a console log in the WalletSendSeedDialog component for tracking transaction identifiers on confirmation.  
- Revised the volume deletion test to use polling for more resilient wait conditions.

### Reviewed Changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

| File | Description |
| ---- | ----------- |
| apps/walletd/dialogs/WalletSendSeedDialog/WalletSendSeedDialogV1/index.tsx | Added debug logging in the onConfirm callback. |
| apps/hostd-e2e/src/fixtures/volumes.ts | Updated polling logic to better capture volume deletion status. |




state: COMMENTED
author_association: NONE
submitted_at: 2025-05-02T14:42:56Z
user: login: telestrial
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-02T19:28:23Z

Review comments:
diff_hunk: @@ -68,6 +68,7 @@ export function WalletSendSeedDialogV1({
     walletId,
     params: sendParams,
     onConfirm: ({ transactionId }) => {
+      console.log('v1 onConfirm...', { transactionId })
path: apps/walletd/dialogs/WalletSendSeedDialog/WalletSendSeedDialogV1/index.tsx
user: login: Copilot
body: [nitpick] Consider gating or removing the debug log from production code if it is not meant for long-term use to avoid unnecessary log clutter or potential information exposure.
```suggestion
      if (process.env.NODE_ENV === 'development') {
        console.log('v1 onConfirm...', { transactionId })
      }
```
created_at: 2025-05-02T14:42:55Z
author_association: NONE
diff_hunk: @@ -46,7 +46,16 @@ export const deleteVolume = step(
       page.getByText('Volume is now being permanently deleted')
     ).toBeVisible()
     const row = page.getByRole('row', { name: fullPath })
path: apps/hostd-e2e/src/fixtures/volumes.ts
user: login: Copilot
body: [nitpick] Add an inline comment explaining why the polling combines visibility and hidden checks to clarify the intended stabilization mechanism for future maintainers.
```suggestion
    const row = page.getByRole('row', { name: fullPath })
    // Polling ensures the row transitions from a "removing" state to being completely removed.
    // The combined checks stabilize the UI by waiting until either the "removing" text is visible
    // or the row is no longer present in the DOM.
```
created_at: 2025-05-02T14:42:56Z
author_association: NONE

PR comments:
user: login: vercel[bot]
created_at: 2025-05-02T14:42:18Z
body: [vc]: #0Gl1AUNJlIxzt8OoLrk7wyg6xnNKeUulrazeJbPBClA=:eyJpc01vbm9yZXBvIjp0cnVlLCJ0eXBlIjoiZ2l0aHViIiwicHJvamVjdHMiOlt7Im5hbWUiOiJleHBsb3Jlci16ZW4iLCJyb290RGlyZWN0b3J5IjpudWxsLCJpbnNwZWN0b3JVcmwiOiJodHRwczovL3ZlcmNlbC5jb20vc2lhLWZvdW5kYXRpb24vZXhwbG9yZXItemVuLzlLc05MYzRKTmtqVm42cmVKVTRteHpFYzVtcGkiLCJwcmV2aWV3VXJsIjoiZXhwbG9yZXItemVuLWdpdC1maXhlMmVzdGFiaWxpemVlMmV0ZXN0cy1zaWEtZm91bmRhdGlvbi52ZXJjZWwuYXBwIiwibmV4dENvbW1pdFN0YXR1cyI6IklHTk9SRUQiLCJsaXZlRmVlZGJhY2siOnsicmVzb2x2ZWQiOjAsInVucmVzb2x2ZWQiOjAsInRvdGFsIjowLCJsaW5rIjoiIn19LHsibmFtZSI6IndlYnNpdGUiLCJyb290RGlyZWN0b3J5IjpudWxsLCJpbnNwZWN0b3JVcmwiOiJodHRwczovL3ZlcmNlbC5jb20vc2lhLWZvdW5kYXRpb24vd2Vic2l0ZS9HcG85bzFzUnZLNVNVZVdra1BSeUJZWFY1TjRDIiwicHJldmlld1VybCI6IndlYnNpdGUtZ2l0LWZpeGUyZXN0YWJpbGl6ZWUyZXRlc3RzLXNpYS1mb3VuZGF0aW9uLnZlcmNlbC5hcHAiLCJuZXh0Q29tbWl0U3RhdHVzIjoiSUdOT1JFRCIsImxpdmVGZWVkYmFjayI6eyJyZXNvbHZlZCI6MCwidW5yZXNvbHZlZCI6MCwidG90YWwiOjAsImxpbmsiOiIifX0seyJuYW1lIjoiaG9zdGQiLCJyb290RGlyZWN0b3J5IjpudWxsLCJpbnNwZWN0b3JVcmwiOiJodHRwczovL3ZlcmNlbC5jb20vc2lhLWZvdW5kYXRpb24vaG9zdGQvNllBRmZ0Nmc3Q0dRb0o3c2E3MXF4OFNkU241ZCIsInByZXZpZXdVcmwiOiJob3N0ZC1naXQtZml4ZTJlc3RhYmlsaXplZTJldGVzdHMtc2lhLWZvdW5kYXRpb24udmVyY2VsLmFwcCIsIm5leHRDb21taXRTdGF0dXMiOiJJR05PUkVEIiwibGl2ZUZlZWRiYWNrIjp7InJlc29sdmVkIjowLCJ1bnJlc29sdmVkIjowLCJ0b3RhbCI6MCwibGluayI6IiJ9fSx7Im5hbWUiOiJyZW50ZXJkIiwicm9vdERpcmVjdG9yeSI6bnVsbCwiaW5zcGVjdG9yVXJsIjoiaHR0cHM6Ly92ZXJjZWwuY29tL3NpYS1mb3VuZGF0aW9uL3JlbnRlcmQvQUpNU1dudzJLbkhqakhLdEw3Z2dDdU1Wamt6USIsInByZXZpZXdVcmwiOiJyZW50ZXJkLWdpdC1maXhlMmVzdGFiaWxpemVlMmV0ZXN0cy1zaWEtZm91bmRhdGlvbi52ZXJjZWwuYXBwIiwibmV4dENvbW1pdFN0YXR1cyI6IklHTk9SRUQiLCJsaXZlRmVlZGJhY2siOnsicmVzb2x2ZWQiOjAsInVucmVzb2x2ZWQiOjAsInRvdGFsIjowLCJsaW5rIjoiIn19LHsibmFtZSI6ImV4cGxvcmVyIiwicm9vdERpcmVjdG9yeSI6bnVsbCwiaW5zcGVjdG9yVXJsIjoiaHR0cHM6Ly92ZXJjZWwuY29tL3NpYS1mb3VuZGF0aW9uL2V4cGxvcmVyLzZTcDk5alUzcDc5aUZzSmk0cmtDd2Y3am8xTGgiLCJwcmV2aWV3VXJsIjoiZXhwbG9yZXItZ2l0LWZpeGUyZXN0YWJpbGl6ZWUyZXRlc3RzLXNpYS1mb3VuZGF0aW9uLnZlcmNlbC5hcHAiLCJuZXh0Q29tbWl0U3RhdHVzIjoiSUdOT1JFRCIsImxpdmVGZWVkYmFjayI6eyJyZXNvbHZlZCI6MCwidW5yZXNvbHZlZCI6MCwidG90YWwiOjAsImxpbmsiOiIifX1dfQ==
**The latest updates on your projects**. Learn more about [Vercel for Git ↗︎](https://vercel.link/github-learn-more)


<details><summary>5 Skipped Deployments</summary>

| Name | Status | Preview | Comments | Updated (UTC) |
| :--- | :----- | :------ | :------- | :------ |
| **explorer** | ⬜️ Ignored ([Inspect](https://vercel.com/sia-foundation/explorer/6Sp99jU3p79iFsJi4rkCwf7jo1Lh)) | [Visit Preview](https://explorer-git-fixe2estabilizee2etests-sia-foundation.vercel.app) |  | May 2, 2025 8:44pm |
| **explorer-zen** | ⬜️ Ignored ([Inspect](https://vercel.com/sia-foundation/explorer-zen/9KsNLc4JNkjVn6reJU4mxzEc5mpi)) | [Visit Preview](https://explorer-zen-git-fixe2estabilizee2etests-sia-foundation.vercel.app) |  | May 2, 2025 8:44pm |
| **hostd** | ⬜️ Ignored ([Inspect](https://vercel.com/sia-foundation/hostd/6YAFft6g7CGQoJ7sa71qx8SdSn5d)) | [Visit Preview](https://hostd-git-fixe2estabilizee2etests-sia-foundation.vercel.app) |  | May 2, 2025 8:44pm |
| **renterd** | ⬜️ Ignored ([Inspect](https://vercel.com/sia-foundation/renterd/AJMSWnw2KnHjjHKtL7ggCuMVjkzQ)) | [Visit Preview](https://renterd-git-fixe2estabilizee2etests-sia-foundation.vercel.app) |  | May 2, 2025 8:44pm |
| **website** | ⬜️ Ignored ([Inspect](https://vercel.com/sia-foundation/website/Gpo9o1sRvK5SUeWkkPRyBYXV5N4C)) | [Visit Preview](https://website-git-fixe2estabilizee2etests-sia-foundation.vercel.app) |  | May 2, 2025 8:44pm |
</details>






author_association: NONE
user: login: alexfreska
created_at: 2025-05-02T14:42:33Z
body: * **#1045** <a href="https://app.graphite.dev/github/pr/SiaFoundation/web/1045?utm_source=stack-comment-icon" target="_blank"><img src="https://static.graphite.dev/graphite-32x32-black.png" alt="Graphite" width="10px" height="10px"/></a> 👈 <a href="https://app.graphite.dev/github/pr/SiaFoundation/web/1045?utm_source=stack-comment-view-in-graphite" target="_blank">(View in Graphite)</a>
* `main`


<h2></h2>

This stack of pull requests is managed by <a href="https://graphite.dev?utm-source=stack-comment"><b>Graphite</b></a>. Learn more about <a href="https://stacking.dev/?utm_source=stack-comment">stacking</a>.
<!-- Current dependencies on/for this PR: -->
author_association: MEMBER
user: login: changeset-bot[bot]
created_at: 2025-05-02T14:43:22Z
body: ###  🦋  Changeset detected

Latest commit: d5b05f53f21ed7904d4da6a43b58eb3d82829734

**The changes in this PR will be included in the next version bump.**

<details><summary>This PR includes changesets to release 1 package</summary>

  | Name                    | Type  |
| ----------------------- | ----- |
| @siafoundation/clusterd | Patch |

</details>

Not sure what this means? [Click here  to learn what changesets are](https://github.com/changesets/changesets/blob/main/docs/adding-a-changeset.md).

[Click here if you're a maintainer who wants to add another changeset to this PR](https://github.com/SiaFoundation/web/new/fix_e2e_stabilize_e2e_tests?filename=.changeset/smooth-swans-sleep.md&value=---%0A%22%40siafoundation%2Fclusterd%22%3A%20patch%0A%22%40siafoundation%2Fdesign-system%22%3A%20patch%0A---%0A%0Afix(e2e)%3A%20stabilize%20e2e%20tests%0A)


author_association: NONE