github.com/tilt-dev/tilt@v0.33.15-0.20240515162809-0a22ed45d8a0/web/src/ResourceNav.test.tsx (about) 1 import { render, RenderOptions, screen } from "@testing-library/react" 2 import userEvent from "@testing-library/user-event" 3 import { createMemoryHistory, MemoryHistory } from "history" 4 import React, { ChangeEvent, useState } from "react" 5 import { act } from "react-dom/test-utils" 6 import { Router } from "react-router" 7 import { ResourceNavProvider, useResourceNav } from "./ResourceNav" 8 import { ResourceName } from "./types" 9 10 const INVALID_RESOURCE = "res3" 11 12 function TestResourceNavConsumer() { 13 const { selectedResource, invalidResource, openResource } = useResourceNav() 14 const [resourceToSelect, setResourceToSelect] = useState("") 15 16 return ( 17 <> 18 <p aria-label="selectedResource">{selectedResource}</p> 19 <p aria-label="invalidResource">{invalidResource}</p> 20 <input 21 aria-label="Resource to select" 22 type="text" 23 value={resourceToSelect} 24 onChange={(e: ChangeEvent<HTMLInputElement>) => 25 setResourceToSelect(e.target.value) 26 } 27 /> 28 <button onClick={() => openResource(resourceToSelect)}> 29 openResource 30 </button> 31 </> 32 ) 33 } 34 35 // history 36 function customRender( 37 wrapperOptions: { 38 history: MemoryHistory 39 validateOverride?: (name: string) => boolean 40 }, 41 options?: RenderOptions 42 ) { 43 const validateResource = 44 wrapperOptions.validateOverride ?? 45 function (name: string) { 46 return name !== INVALID_RESOURCE 47 } 48 return render(<TestResourceNavConsumer />, { 49 wrapper: ({ children }) => ( 50 <Router history={wrapperOptions.history}> 51 <ResourceNavProvider validateResource={validateResource}> 52 {children} 53 </ResourceNavProvider> 54 </Router> 55 ), 56 ...options, 57 }) 58 } 59 60 describe("ResourceNavContext", () => { 61 it("navigates to resource on click", () => { 62 const history = createMemoryHistory() 63 customRender({ history }) 64 65 expect(screen.getByLabelText("selectedResource")).toHaveTextContent("") 66 67 userEvent.type(screen.getByRole("textbox"), "res1") 68 userEvent.click(screen.getByRole("button", { name: "openResource" })) 69 70 expect(screen.getByLabelText("selectedResource")).toHaveTextContent("res1") 71 expect(history.location.pathname).toEqual("/r/res1/overview") 72 }) 73 74 it("filters resources that don't validate", () => { 75 const history = createMemoryHistory() 76 // Set location to invalid resource 77 history.location.pathname = `/r/${INVALID_RESOURCE}/overview` 78 customRender({ history }) 79 80 expect(screen.getByLabelText("selectedResource")).toHaveTextContent("") 81 expect(screen.getByLabelText("invalidResource")).toHaveTextContent( 82 INVALID_RESOURCE 83 ) 84 }) 85 86 it("always validates the 'all' resource", () => { 87 const history = createMemoryHistory() 88 // Set location to 'all' resource 89 history.location.pathname = `/r/${ResourceName.all}/overview` 90 customRender({ history, validateOverride: (_name: string) => false }) 91 92 expect(screen.getByLabelText("selectedResource")).toHaveTextContent( 93 ResourceName.all 94 ) 95 expect(screen.getByLabelText("invalidResource")).toHaveTextContent("") 96 }) 97 98 it("encodes resource names", () => { 99 const history = createMemoryHistory() 100 customRender({ history }) 101 102 userEvent.type(screen.getByRole("textbox"), "foo/bar") 103 userEvent.click(screen.getByRole("button", { name: "openResource" })) 104 105 expect(screen.getByLabelText("selectedResource")).toHaveTextContent( 106 "foo/bar" 107 ) 108 expect(history.location.pathname).toEqual("/r/foo%2Fbar/overview") 109 }) 110 111 it("preserves filters by resource", () => { 112 const history = createMemoryHistory() 113 customRender({ history }) 114 115 let nav = (res: string) => { 116 userEvent.clear(screen.getByRole("textbox")) 117 userEvent.type(screen.getByRole("textbox"), res) 118 userEvent.click(screen.getByRole("button", { name: "openResource" })) 119 } 120 121 let url = () => { 122 return history.location.pathname + history.location.search 123 } 124 125 nav("foo") 126 expect(url()).toEqual("/r/foo/overview") 127 history.push("/r/foo/overview?term=hi") 128 nav("bar") 129 expect(url()).toEqual("/r/bar/overview") 130 nav("foo") 131 expect(url()).toEqual("/r/foo/overview?term=hi") 132 }) 133 134 // Make sure that useResourceNav() doesn't break memoization. 135 it("memoizes renders", () => { 136 let renderCount = 0 137 let FakeEl = React.memo(() => { 138 useResourceNav() 139 renderCount++ 140 return <div></div> 141 }) 142 143 let history = createMemoryHistory() 144 let validateResource = () => true 145 let { rerender } = render( 146 <Router history={history}> 147 <ResourceNavProvider validateResource={validateResource}> 148 <FakeEl /> 149 </ResourceNavProvider> 150 </Router> 151 ) 152 153 expect(renderCount).toEqual(1) 154 155 // Make sure we don't re-render on a no-op history update. 156 rerender( 157 <Router history={history}> 158 <ResourceNavProvider validateResource={validateResource}> 159 <FakeEl /> 160 </ResourceNavProvider> 161 </Router> 162 ) 163 expect(renderCount).toEqual(1) 164 165 // Make sure we do re-render on a real location update. 166 act(() => history.push("/r/foo")) 167 expect(renderCount).toEqual(2) 168 }) 169 })