Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle async Remix navigation properly #4839

Closed
wants to merge 5 commits into from
Closed
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
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,7 @@ const CommentIndicator = React.memo(({ thread }: CommentIndicatorProps) => {
return
}
if (isOnAnotherRoute && remixLocationRoute != null && remixState != null) {
remixState.navigate(remixLocationRoute)
void remixState.navigate(remixLocationRoute)
}
cancelHover()
dispatch([
Expand Down
2 changes: 1 addition & 1 deletion editor/src/components/canvas/multiplayer-presence.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -395,7 +395,7 @@ const FollowingOverlay = React.memo(() => {
const sceneState = remixNavigationState[presence.remix.scene]
if (sceneState != null && presence.remix.locationRoute != null) {
setActiveRemixScene(EP.fromString(presence.remix.scene))
sceneState.navigate(presence.remix.locationRoute)
void sceneState.navigate(presence.remix.locationRoute)
actions.push(
showToast(
notice(
Expand Down
176 changes: 115 additions & 61 deletions editor/src/components/canvas/remix/remix-rendering.spec.browser2.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { within } from '@testing-library/react'
import { getByText, waitFor, within } from '@testing-library/react'
import * as EP from '../../../core/shared/element-path'
import type { WindowPoint } from '../../../core/shared/math-utils'
import { windowPoint } from '../../../core/shared/math-utils'
Expand All @@ -10,7 +10,7 @@ import {
} from '../../../sample-projects/sample-project-utils.test-utils'
import type { Modifiers } from '../../../utils/modifiers'
import { emptyModifiers, cmdModifier } from '../../../utils/modifiers'
import { selectComponentsForTest } from '../../../utils/utils.test-utils'
import { selectComponentsForTest, wait } from '../../../utils/utils.test-utils'
import {
runDOMWalker,
selectComponents,
Expand Down Expand Up @@ -1320,10 +1320,14 @@ describe('Remix navigation', () => {
)

for (let i = 1; i < 7; i++) {
// eslint-disable-next-line no-await-in-loop
await clickRemixLink(renderResult)
expect(
renderResult.renderedDOM.queryAllByText(`post id: ${i}`).filter(filterOutMenuLabels),
).toHaveLength(1)
// eslint-disable-next-line no-await-in-loop
await waitFor(() => {
expect(
renderResult.renderedDOM.queryAllByText(`post id: ${i}`).filter(filterOutMenuLabels),
).toHaveLength(1)
})
}
})

Expand All @@ -1337,26 +1341,36 @@ describe('Remix navigation', () => {
expect(getPathInRemixSceneLabel(renderResult, pathToRemixScene)).toEqual(RemixIndexPathLabel)

await clickRemixLink(renderResult)

expect(
renderResult.renderedDOM.queryAllByText(AboutTextContent).filter(filterOutMenuLabels),
).toHaveLength(1)
expect(getPathInRemixSceneLabel(renderResult, pathToRemixScene)).toEqual('/about')
await waitFor(() => {
expect(
renderResult.renderedDOM.queryAllByText(AboutTextContent).filter(filterOutMenuLabels),
).toHaveLength(1)
expect(getPathInRemixSceneLabel(renderResult, pathToRemixScene)).toEqual('/about')
})

await navigateWithRemixSceneLabelButton(renderResult, pathToRemixScene, 'back')

expect(renderResult.renderedDOM.queryAllByText(RootTextContent)).toHaveLength(1)
expect(getPathInRemixSceneLabel(renderResult, pathToRemixScene)).toEqual(RemixIndexPathLabel)
await waitFor(() => {
expect(renderResult.renderedDOM.queryAllByText(RootTextContent)).toHaveLength(1)
expect(getPathInRemixSceneLabel(renderResult, pathToRemixScene)).toEqual(
RemixIndexPathLabel,
)
})

await navigateWithRemixSceneLabelButton(renderResult, pathToRemixScene, 'forward')
expect(
renderResult.renderedDOM.queryAllByText(AboutTextContent).filter(filterOutMenuLabels),
).toHaveLength(1)
expect(getPathInRemixSceneLabel(renderResult, pathToRemixScene)).toEqual('/about')
await waitFor(() => {
expect(
renderResult.renderedDOM.queryAllByText(AboutTextContent).filter(filterOutMenuLabels),
).toHaveLength(1)
expect(getPathInRemixSceneLabel(renderResult, pathToRemixScene)).toEqual('/about')
})

await navigateWithRemixSceneLabelButton(renderResult, pathToRemixScene, 'home')
expect(renderResult.renderedDOM.queryAllByText(RootTextContent)).toHaveLength(1)
expect(getPathInRemixSceneLabel(renderResult, pathToRemixScene)).toEqual(RemixIndexPathLabel)
await waitFor(() => {
expect(renderResult.renderedDOM.queryAllByText(RootTextContent)).toHaveLength(1)
expect(getPathInRemixSceneLabel(renderResult, pathToRemixScene)).toEqual(
RemixIndexPathLabel,
)
})
})

it('can navigate with the scene label nav buttons, in edit mode', async () => {
Expand All @@ -1369,33 +1383,50 @@ describe('Remix navigation', () => {

await clickRemixLink(renderResult)

expect(
renderResult.renderedDOM.queryAllByText(AboutTextContent).filter(filterOutMenuLabels),
).toHaveLength(1)
expect(getPathInRemixSceneLabel(renderResult, pathToRemixScene)).toEqual('/about')

await waitFor(() => {
expect(
renderResult.renderedDOM.queryAllByText(AboutTextContent).filter(filterOutMenuLabels),
).toHaveLength(1)
expect(getPathInRemixSceneLabel(renderResult, pathToRemixScene)).toEqual('/about')
})
await switchToEditMode(renderResult)

await waitFor(() => {
expect(getPathInRemixSceneLabel(renderResult, pathToRemixScene)).toEqual('/about')
})

// check that switching modes doesn't change the navigation state
expect(getPathInRemixSceneLabel(renderResult, pathToRemixScene)).toEqual('/about')
// expect(getPathInRemixSceneLabel(renderResult, pathToRemixScene)).toEqual('/about')
expect(
renderResult.renderedDOM.queryAllByText(AboutTextContent).filter(filterOutMenuLabels),
).toHaveLength(1)

await navigateWithRemixSceneLabelButton(renderResult, pathToRemixScene, 'back')

expect(renderResult.renderedDOM.queryAllByText(RootTextContent)).toHaveLength(1)
expect(getPathInRemixSceneLabel(renderResult, pathToRemixScene)).toEqual(RemixIndexPathLabel)
await waitFor(() => {
expect(renderResult.renderedDOM.queryAllByText(RootTextContent)).toHaveLength(1)
expect(getPathInRemixSceneLabel(renderResult, pathToRemixScene)).toEqual(
RemixIndexPathLabel,
)
})

await navigateWithRemixSceneLabelButton(renderResult, pathToRemixScene, 'forward')
expect(
renderResult.renderedDOM.queryAllByText(AboutTextContent).filter(filterOutMenuLabels),
).toHaveLength(1)
expect(getPathInRemixSceneLabel(renderResult, pathToRemixScene)).toEqual('/about')

await waitFor(() => {
expect(
renderResult.renderedDOM.queryAllByText(AboutTextContent).filter(filterOutMenuLabels),
).toHaveLength(1)
expect(getPathInRemixSceneLabel(renderResult, pathToRemixScene)).toEqual('/about')
})

await navigateWithRemixSceneLabelButton(renderResult, pathToRemixScene, 'home')
expect(renderResult.renderedDOM.queryAllByText(RootTextContent)).toHaveLength(1)
expect(getPathInRemixSceneLabel(renderResult, pathToRemixScene)).toEqual(RemixIndexPathLabel)

await waitFor(() => {
expect(renderResult.renderedDOM.queryAllByText(RootTextContent)).toHaveLength(1)
expect(getPathInRemixSceneLabel(renderResult, pathToRemixScene)).toEqual(
RemixIndexPathLabel,
)
})
})

it('navigating in one Remix scene does not affect the navigation state in the other', async () => {
Expand All @@ -1409,16 +1440,21 @@ describe('Remix navigation', () => {
expect(getPathInRemixSceneLabel(renderResult, pathToRemixScene2)).toEqual(RemixIndexPathLabel)

await clickRemixLink(renderResult)
const remixScene1 = renderResult.renderedDOM.getByTestId(Remix1TestId)
expect(
within(remixScene1).queryAllByText(AboutTextContent).filter(filterOutMenuLabels),
).toHaveLength(1)

const remixScene2 = renderResult.renderedDOM.getByTestId(Remix2TestId)
expect(within(remixScene2).queryAllByText(RootTextContent)).toHaveLength(1)
await waitFor(() => {
const remixScene1 = renderResult.renderedDOM.getByTestId(Remix1TestId)
expect(
within(remixScene1).queryAllByText(AboutTextContent).filter(filterOutMenuLabels),
).toHaveLength(1)

expect(getPathInRemixSceneLabel(renderResult, pathToRemixScene1)).toEqual('/about')
expect(getPathInRemixSceneLabel(renderResult, pathToRemixScene2)).toEqual(RemixIndexPathLabel)
const remixScene2 = renderResult.renderedDOM.getByTestId(Remix2TestId)
expect(within(remixScene2).queryAllByText(RootTextContent)).toHaveLength(1)

expect(getPathInRemixSceneLabel(renderResult, pathToRemixScene1)).toEqual('/about')
expect(getPathInRemixSceneLabel(renderResult, pathToRemixScene2)).toEqual(
RemixIndexPathLabel,
)
})
})
})

Expand All @@ -1431,25 +1467,33 @@ describe('Remix navigation', () => {

await clickRemixLink(renderResult)

expect(
renderResult.renderedDOM.queryAllByText(AboutTextContent).filter(filterOutMenuLabels),
).toHaveLength(1)
expect(getPathInRemixNavigationBar(renderResult)).toEqual('/about')
await waitFor(() => {
expect(
renderResult.renderedDOM.queryAllByText(AboutTextContent).filter(filterOutMenuLabels),
).toHaveLength(1)
expect(getPathInRemixNavigationBar(renderResult)).toEqual('/about')
})

await navigateWithRemixNavigationBarButton(renderResult, 'back')

expect(renderResult.renderedDOM.queryAllByText(RootTextContent)).toHaveLength(1)
expect(getPathInRemixNavigationBar(renderResult)).toEqual(RemixIndexPathLabel)
await waitFor(() => {
expect(renderResult.renderedDOM.queryAllByText(RootTextContent)).toHaveLength(1)
expect(getPathInRemixNavigationBar(renderResult)).toEqual(RemixIndexPathLabel)
})

await navigateWithRemixNavigationBarButton(renderResult, 'forward')
expect(
renderResult.renderedDOM.queryAllByText(AboutTextContent).filter(filterOutMenuLabels),
).toHaveLength(1)
expect(getPathInRemixNavigationBar(renderResult)).toEqual('/about')
await waitFor(() => {
expect(
renderResult.renderedDOM.queryAllByText(AboutTextContent).filter(filterOutMenuLabels),
).toHaveLength(1)
expect(getPathInRemixNavigationBar(renderResult)).toEqual('/about')
})

await navigateWithRemixNavigationBarButton(renderResult, 'home')
expect(renderResult.renderedDOM.queryAllByText(RootTextContent)).toHaveLength(1)
expect(getPathInRemixNavigationBar(renderResult)).toEqual(RemixIndexPathLabel)
await waitFor(() => {
expect(renderResult.renderedDOM.queryAllByText(RootTextContent)).toHaveLength(1)
expect(getPathInRemixNavigationBar(renderResult)).toEqual(RemixIndexPathLabel)
})
})

it('can navigate inside multiple Remix scenes, using the nav bar in the canvas toolbar', async () => {
Expand All @@ -1459,16 +1503,18 @@ describe('Remix navigation', () => {
expect(getPathInRemixNavigationBar(renderResult)).toEqual(RemixIndexPathLabel)

await clickRemixLink(renderResult)

expect(
renderResult.renderedDOM.queryAllByText(AboutTextContent).filter(filterOutMenuLabels),
).toHaveLength(1)
expect(getPathInRemixNavigationBar(renderResult)).toEqual('/about')

await waitFor(() => {
expect(
renderResult.renderedDOM.queryAllByText(AboutTextContent).filter(filterOutMenuLabels),
).toHaveLength(1)
expect(getPathInRemixNavigationBar(renderResult)).toEqual('/about')
})
const remixScene2 = renderResult.renderedDOM.getByTestId(Remix2TestId)
await mouseClickAtPoint(remixScene2, { x: 10, y: 10 })

expect(getPathInRemixNavigationBar(renderResult)).toEqual(RemixIndexPathLabel)
await mouseClickAtPoint(remixScene2, { x: 10, y: 10 })
await waitFor(() => {
expect(getPathInRemixNavigationBar(renderResult)).toEqual(RemixIndexPathLabel)
})
})
})
})
Expand Down Expand Up @@ -2331,20 +2377,25 @@ async function clickLinkWithTestId(editor: EditorRenderResult, testId: string) {
}

async function clickRemixLink(editor: EditorRenderResult) {
await wait(10)
await clickLinkWithTestId(editor, 'remix-link')
await editor.getDispatchFollowUpActionsFinished()
await wait(10)
}

async function clickPostLink(editor: EditorRenderResult) {
await wait(10)
await clickLinkWithTestId(editor, 'post-link')
await editor.getDispatchFollowUpActionsFinished()
await wait(10)
}

async function navigateWithRemixSceneLabelButton(
renderResult: EditorRenderResult,
pathToRemixScene: ElementPath,
button: RemixSceneLabelButtonType,
) {
await wait(10)
await mouseClickAtPoint(
renderResult.renderedDOM.getByTestId(RemixSceneLabelButtonTestId(pathToRemixScene, button)),
{
Expand All @@ -2353,6 +2404,7 @@ async function navigateWithRemixSceneLabelButton(
},
)
await renderResult.getDispatchFollowUpActionsFinished()
await wait(10)
}

const getPathInRemixSceneLabel = (
Expand All @@ -2364,6 +2416,7 @@ async function navigateWithRemixNavigationBarButton(
renderResult: EditorRenderResult,
button: RemixSceneLabelButtonType,
) {
await wait(10)
await mouseClickAtPoint(
renderResult.renderedDOM.getByTestId(RemixNavigationBarButtonTestId(button)),
{
Expand All @@ -2372,6 +2425,7 @@ async function navigateWithRemixNavigationBarButton(
},
)
await renderResult.getDispatchFollowUpActionsFinished()
await wait(10)
}

const getPathInRemixNavigationBar = (renderResult: EditorRenderResult) =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,16 @@ import { AlwaysFalse, usePubSubAtomReadOnly } from '../../../core/shared/atom-wi
import { CreateRemixDerivedDataRefsGLOBAL } from '../../editor/store/remix-derived-data'
import { patchRoutesWithContext } from '../../../third-party/remix/create-remix-stub'
import type { AppLoadContext } from '@remix-run/server-runtime'
import { wait } from '../../../utils/utils.test-utils'

type RouteModule = RouteModules[keyof RouteModules]
type RouterType = ReturnType<typeof createMemoryRouter>

interface RemixNavigationContext {
forward: () => void
back: () => void
home: () => void
navigate: (loc: string) => void
forward: () => Promise<void>
back: () => Promise<void>
home: () => Promise<void>
navigate: (loc: string) => Promise<void>
location: Location
entries: Array<Location>
}
Expand Down Expand Up @@ -258,10 +259,10 @@ export const UtopiaRemixRootComponent = (props: UtopiaRemixRootComponentProps) =
return {
...current,
[EP.toString(basePath)]: {
forward: () => void innerRouter.navigate(1),
back: () => void innerRouter.navigate(-1),
home: () => void innerRouter.navigate('/'),
navigate: (loc: string) => void innerRouter.navigate(loc),
forward: () => innerRouter.navigate(1),
back: () => innerRouter.navigate(-1),
home: () => innerRouter.navigate('/'),
navigate: (loc: string) => innerRouter.navigate(loc),
location: location,
entries: updatedEntries,
},
Expand Down
6 changes: 3 additions & 3 deletions editor/src/components/editor/remix-navigation-bar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,15 @@ export const RemixNavigationBar = React.memo(() => {
)

const forward = React.useCallback(
() => navigationControls[EP.toString(activeRemixScene)]?.forward(),
() => void navigationControls[EP.toString(activeRemixScene)]?.forward(),
[activeRemixScene, navigationControls],
)
const back = React.useCallback(
() => navigationControls[EP.toString(activeRemixScene)]?.back(),
() => void navigationControls[EP.toString(activeRemixScene)]?.back(),
[activeRemixScene, navigationControls],
)
const home = React.useCallback(
() => navigationControls[EP.toString(activeRemixScene)]?.home(),
() => void navigationControls[EP.toString(activeRemixScene)]?.home(),
[activeRemixScene, navigationControls],
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ const ThreadPreview = React.memo(({ thread }: ThreadPreviewProps) => {
if (remixState == null) {
return
}
remixState.navigate(remixLocationRoute)
void remixState.navigate(remixLocationRoute)
}
let actions: EditorAction[] = [...openCommentThreadActions(thread.id, commentScene)]

Expand Down
Loading