Skip to content

Fix saved search navigation race#2669

Open
Sakshamm-Goyal wants to merge 5 commits into
hyperdxio:mainfrom
Sakshamm-Goyal:agent/fix-saved-search-navigation-race
Open

Fix saved search navigation race#2669
Sakshamm-Goyal wants to merge 5 commits into
hyperdxio:mainfrom
Sakshamm-Goyal:agent/fix-saved-search-navigation-race

Conversation

@Sakshamm-Goyal

Copy link
Copy Markdown

Summary

Fixes the saved-search save-to-navigation race reported in #2644.

The create flow now performs a document navigation to the new saved-search
route rather than a client-side transition that can be overwritten by stale
useQueryStates URL state. The E2E helper waits for domcontentloaded so it
asserts the destination route without coupling the test to unrelated resource
loading after that document navigation.

Root cause

router.push allowed the prior search page's URL state to be restored during
the transition, intermittently replacing the new /search/<id> route.

Validation

  • yarn lint:fix
  • repository lint/type gate
  • commit hooks (Prettier, ESLint, Knip)
  • captured E2E trace reaches the new saved-search URL

The focused full-stack E2E runner is currently blocked before its test by an
existing global setup timeout waiting for page.goto('/search') to emit
load; this is unrelated to the saved-search route behavior.

@changeset-bot

changeset-bot Bot commented Jul 17, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 14aa5f6

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

This PR includes changesets to release 3 packages
Name Type
@hyperdx/app Patch
@hyperdx/api Patch
@hyperdx/otel-collector Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel

vercel Bot commented Jul 17, 2026

Copy link
Copy Markdown

@Sakshamm-Goyal is attempting to deploy a commit to the HyperDX Team on Vercel.

A member of the Team first needs to authorize it.

@Sakshamm-Goyal
Sakshamm-Goyal marked this pull request as ready for review July 17, 2026 05:36
@greptile-apps

greptile-apps Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes saved-search navigation after creating a saved search. The main changes are:

  • Uses a document navigation to the new saved-search route.
  • Builds the destination URL without stale saved-search query state.
  • Preserves explicit time bounds when they are present.
  • Adds unit and end-to-end coverage for the cleaned navigation URL and restored saved-search state.

Confidence Score: 5/5

This looks safe to merge.

  • No blocking issues found in the changed code.

Important Files Changed

Filename Overview
packages/app/src/DBSearchPage.tsx Updates saved-search creation to navigate to a cleaned saved-search URL so stored config can hydrate from the saved search.
packages/app/src/tests/DBSearchPage.test.tsx Adds unit tests for dropping saved-search query state and preserving explicit time bounds.
packages/app/tests/e2e/components/SavedSearchModalComponent.ts Updates the save helper to wait for the document navigation before checking the saved-search title.
packages/app/tests/e2e/features/search/saved-search.spec.ts Adds assertions that the saved-search URL is clean and the restored search state is visible.
.changeset/fix-saved-search-navigation-race.md Adds a patch changeset for the saved-search navigation fix.

Reviews (6): Last reviewed commit: "fix(app): preserve absolute saved search..." | Re-trigger Greptile

Comment thread packages/app/src/DBSearchPage.tsx
@github-actions

github-actions Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Deep Review

✅ No critical issues found. The change replaces a client-side router.push with a full document navigation (window.location.assign) to close the saved-search hydration race, and this pattern is already established elsewhere in the same file (lines ~1985–2002) with router.basePath prepended on the next/router singleton. Dropping non-time-range query params is intentional and safe: the newly created search stores the current config, so the destination route re-hydrates it from storage. No P0/P1 failure mode was traceable from the diff.

🟡 P2 -- recommended

  • packages/app/tests/e2e/features/search/saved-search.spec.ts:249 -- New assertions covering preserved from/to, dropped where/orderBy, and hydrated input/order-by were added, but the fix's core behavior is not demonstrated green since the focused E2E runner is currently blocked before this test.
    • Fix: Unblock the E2E global setup and run the saved-search suite to green so the navigation and param-filtering behavior is verified before merge.

🟡 P2 -- recommended (behavioral edge)

  • packages/app/src/DBSearchPage.tsx:555 -- from/to are preserved only when both are present, and the assertion expect(savedSearchParams.has('from')).toBe(true) assumes the search URL always carries an explicit from/to pair at save time; if a default relative range ever yields a URL without both, the range is silently dropped and the new test fails.
    • Fix: Confirm the search page always writes both from and to before save, or preserve each param independently and relax the test to tolerate a relative default.
🔵 P3 nitpicks (2)
  • packages/app/src/DBSearchPage.tsx:560 -- The window.location.assign(\${router.basePath}/...`)` hard-navigation idiom now appears at four call sites (~560, 1985, 1987, 2002), each re-deriving the basePath prepend.
    • Fix: Extract a small hardNavigate(path) helper that centralizes router.basePath prepending and the location.assign call; keep the per-site comments and leave the time-range filtering inline.
    • maintainability
  • packages/app/tests/e2e/components/SavedSearchModalComponent.ts:106 -- The await expect(this.container).toBeHidden() assertion was removed, dropping explicit coverage that the modal/page is actually torn down; the remaining title assertions cover navigation only indirectly.
    • Fix: Re-add a lightweight assertion that the modal container is gone after navigation, or document why the destination-title check is sufficient.

Reviewers (2 of 8 returned before synthesis was forced): maintainability, agent-native. Correctness, testing, project-standards, kieran-typescript, julik-frontend-races, and learnings-researcher were dispatched but had not returned when structured output was enforced; findings above are grounded in the orchestrator's direct read of the diff and the two completed reviewers. Agent-native review found no parity gap (the MCP saveSavedSearch tool reaches the same server-side create path and needs no navigation).

Testing gaps:

  • New E2E assertions for param preservation and hydration are unverified because the focused runner is blocked upstream.
  • No coverage for the single-from-or-single-to and relative-default time-range cases against the both-or-neither preservation guard.

@Sakshamm-Goyal
Sakshamm-Goyal force-pushed the agent/fix-saved-search-navigation-race branch from eb218eb to f2c86f1 Compare July 20, 2026 05:24
@wrn14897
wrn14897 requested review from Copilot and knudtty July 20, 2026 15:40

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR addresses a flaky “save saved-search → navigate to /search/<id>” race in the Next.js app by switching the create flow to a hard document navigation, preventing useQueryStates from restoring stale /search?... URL state during the transition. It also updates Playwright E2E helpers/assertions to match the new navigation behavior and adds a changeset for the app patch release.

Changes:

  • Switch saved-search create success handling from router.push(...) to window.location.assign(...), preserving only select URL state.
  • Update the E2E saved-search modal helper to wait for URL navigation at domcontentloaded.
  • Extend an E2E test to validate the saved-search route loads stored config (and not config via URL params), and add an app changeset.

Reviewed changes

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

File Description
packages/app/src/DBSearchPage.tsx Replace client-side transition with hard navigation to prevent stale useQueryStates URL restoration during saved-search creation.
packages/app/tests/e2e/components/SavedSearchModalComponent.ts Adjust waitForURL to stop waiting on full load for document navigations.
packages/app/tests/e2e/features/search/saved-search.spec.ts Add assertions about query params and restored saved-search config after navigation.
.changeset/fix-saved-search-navigation-race.md Patch changeset for @hyperdx/app.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/app/src/DBSearchPage.tsx Outdated
Comment on lines +551 to +558
const currentParams = new URLSearchParams(window.location.search);
const timeRangeParams = new URLSearchParams();
const from = currentParams.get('from');
const to = currentParams.get('to');
if (from != null && to != null) {
timeRangeParams.set('from', from);
timeRangeParams.set('to', to);
}
Comment on lines 93 to 108
// Start waiting for URL change BEFORE clicking submit to avoid race condition
const urlPromise = this.page.waitForURL(/\/search\/[a-f0-9]+/, {
timeout: 15000,
// Saving now uses a document navigation to avoid useQueryStates restoring
// stale search URL. Do not wait for unrelated resources to finish loading.
waitUntil: 'domcontentloaded',
});

await this.submit();

// Wait for navigation to complete
await urlPromise;

// Wait for modal to fully close
await expect(this.container).toBeHidden();

await expect(this.savedSearchNameTitle).toBeVisible({ timeout: 5000 });
await expect(this.savedSearchNameTitle).toHaveText(name, { timeout: 5000 });
}
Comment on lines +248 to +252
const savedSearchParams = new URL(page.url()).searchParams;
expect(savedSearchParams.has('from')).toBe(true);
expect(savedSearchParams.has('to')).toBe(true);
expect(savedSearchParams.has('where')).toBe(false);
expect(savedSearchParams.has('orderBy')).toBe(false);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants