github.com/tilt-dev/tilt@v0.33.15-0.20240515162809-0a22ed45d8a0/web/src/ResourceGroupsContext.test.tsx (about) 1 import { act, render } from "@testing-library/react" 2 import userEvent from "@testing-library/user-event" 3 import React from "react" 4 import { AnalyticsAction, AnalyticsType } from "./analytics" 5 import { 6 cleanupMockAnalyticsCalls, 7 expectIncrs, 8 mockAnalyticsCalls, 9 } from "./analytics_test_helpers" 10 import { 11 DEFAULT_GROUP_STATE, 12 ResourceGroupsContextProvider, 13 useResourceGroups, 14 } from "./ResourceGroupsContext" 15 16 const GROUP_STATE_ID = "test-group-state" 17 const LABEL_STATE_ID = "test-label-state" 18 19 // This is a very basic test component that prints out the state 20 // from the ResourceGroups context and provides buttons to trigger 21 // methods returned by the context, so they can be tested 22 const TestConsumer = (props: { labelName?: string }) => { 23 const { groups, getGroup, toggleGroupExpanded } = useResourceGroups() 24 25 return ( 26 <> 27 <p id={GROUP_STATE_ID}>{JSON.stringify(groups)}</p> 28 {/* Display the label state if a specific label is present */} 29 {props.labelName && ( 30 <p id={LABEL_STATE_ID}>{JSON.stringify(getGroup(props.labelName))}</p> 31 )} 32 {/* Display a button to toggle the label state if a specific label is present */} 33 {props.labelName && ( 34 <button 35 onClick={() => 36 toggleGroupExpanded(props.labelName || "", AnalyticsType.Grid) 37 } 38 /> 39 )} 40 </> 41 ) 42 } 43 44 describe("ResourceGroupsContext", () => { 45 let wrapper: HTMLElement 46 47 // Helpers 48 const groupState = () => 49 wrapper.querySelector(`#${GROUP_STATE_ID}`)!.innerHTML 50 const labelState = () => 51 wrapper.querySelector(`#${LABEL_STATE_ID}`)!.innerHTML 52 const clickButton = () => { 53 userEvent.click(wrapper.querySelector("button")!) 54 } 55 56 beforeEach(() => { 57 localStorage.clear() 58 mockAnalyticsCalls() 59 }) 60 61 afterEach(() => { 62 localStorage.clear() 63 cleanupMockAnalyticsCalls() 64 }) 65 66 it("defaults to an empty state with no groups", () => { 67 wrapper = renderContainer( 68 <ResourceGroupsContextProvider> 69 <TestConsumer /> 70 </ResourceGroupsContextProvider> 71 ) 72 73 expect(groupState()).toBe(JSON.stringify({})) 74 }) 75 76 describe("toggleGroupExpanded", () => { 77 it("sets expanded to `true` when group is collapsed", () => { 78 const testValues = { test: { expanded: false } } 79 wrapper = renderContainer( 80 <ResourceGroupsContextProvider initialValuesForTesting={testValues}> 81 <TestConsumer labelName="test" /> 82 </ResourceGroupsContextProvider> 83 ) 84 clickButton() 85 86 expect(labelState()).toBe(JSON.stringify({ expanded: true })) 87 }) 88 89 it("sets expanded to `false` when group is expanded", () => { 90 const testValues = { test: { expanded: true } } 91 wrapper = renderContainer( 92 <ResourceGroupsContextProvider initialValuesForTesting={testValues}> 93 <TestConsumer labelName="test" /> 94 </ResourceGroupsContextProvider> 95 ) 96 clickButton() 97 98 expect(labelState()).toBe(JSON.stringify({ expanded: false })) 99 }) 100 101 it("sets expanded to `false` if a group isn't saved yet and is toggled", () => { 102 wrapper = renderContainer( 103 <ResourceGroupsContextProvider> 104 <TestConsumer labelName="a-non-existent-group" /> 105 </ResourceGroupsContextProvider> 106 ) 107 clickButton() 108 109 expect(labelState()).toBe(JSON.stringify({ expanded: false })) 110 }) 111 112 it("makes an analytics call with the right payload", () => { 113 const testValues = { test: { expanded: true } } 114 wrapper = renderContainer( 115 <ResourceGroupsContextProvider initialValuesForTesting={testValues}> 116 <TestConsumer labelName="test" /> 117 </ResourceGroupsContextProvider> 118 ) 119 clickButton() 120 // Expect the "collapse" action value because the test label group is expanded 121 // when it's clicked on and the "grid" type value because it's hardcoded in the 122 // test component 123 expectIncrs({ 124 name: "ui.web.resourceGroup", 125 tags: { action: AnalyticsAction.Collapse, type: AnalyticsType.Grid }, 126 }) 127 }) 128 }) 129 130 describe("getGroup", () => { 131 it("returns the correct state of a resource group", () => { 132 const testValues = { frontend: { expanded: false } } 133 wrapper = renderContainer( 134 <ResourceGroupsContextProvider initialValuesForTesting={testValues}> 135 <TestConsumer labelName="frontend" /> 136 </ResourceGroupsContextProvider> 137 ) 138 139 expect(labelState()).toBe(JSON.stringify({ expanded: false })) 140 }) 141 142 it("returns a default state of a resource group if a group isn't saved yet", () => { 143 const testValues = { frontend: { expanded: false } } 144 wrapper = renderContainer( 145 <ResourceGroupsContextProvider initialValuesForTesting={testValues}> 146 <TestConsumer labelName="backend" /> 147 </ResourceGroupsContextProvider> 148 ) 149 150 expect(labelState()).toBe(JSON.stringify(DEFAULT_GROUP_STATE)) 151 }) 152 }) 153 154 it("memoizes renders", () => { 155 let renderCount = 0 156 let toggleGroupExpanded: any 157 let FakeEl = React.memo(() => { 158 let context = useResourceGroups() 159 toggleGroupExpanded = context.toggleGroupExpanded 160 renderCount++ 161 return <div></div> 162 }) 163 164 let tree = () => { 165 const init = { frontend: { expanded: false } } 166 return ( 167 <ResourceGroupsContextProvider initialValuesForTesting={init}> 168 <FakeEl /> 169 </ResourceGroupsContextProvider> 170 ) 171 } 172 173 let { rerender } = render(tree()) 174 expect(renderCount).toEqual(1) 175 176 // Make sure we don't re-render 177 rerender(tree()) 178 expect(renderCount).toEqual(1) 179 180 act(() => toggleGroupExpanded("frontend", "")) 181 expect(renderCount).toEqual(2) 182 }) 183 }) 184 185 function renderContainer(x: any) { 186 let { container } = render(x) 187 return container 188 }