widgetFrame.spec.tsx 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  1. import {render, screen, userEvent} from 'sentry-test/reactTestingLibrary';
  2. import {WidgetFrame} from 'sentry/views/dashboards/widgets/common/widgetFrame';
  3. describe('WidgetFrame', () => {
  4. describe('Layout', () => {
  5. it('Renders the title and description', async () => {
  6. render(<WidgetFrame title="EPS" description="Number of events per second" />);
  7. expect(screen.getByText('EPS')).toBeInTheDocument();
  8. await userEvent.hover(screen.getByRole('button', {name: 'Widget description'}));
  9. expect(await screen.findByText('Number of events per second')).toBeInTheDocument();
  10. });
  11. it('Catches errors in the visualization', async () => {
  12. jest.spyOn(console, 'error').mockImplementation();
  13. render(
  14. <WidgetFrame title="Uh Oh">
  15. <UhOh />
  16. </WidgetFrame>
  17. );
  18. expect(screen.getByText('Uh Oh')).toBeInTheDocument();
  19. expect(
  20. await screen.findByText('Sorry, something went wrong when rendering this widget.')
  21. ).toBeInTheDocument();
  22. jest.resetAllMocks();
  23. });
  24. });
  25. describe('Warnings', () => {
  26. it('Shows the warnings in a tooltip', async () => {
  27. render(<WidgetFrame title="count()" warnings={['This widget has stale data']} />);
  28. expect(screen.queryByText('This widget has stale data')).not.toBeInTheDocument();
  29. await userEvent.hover(screen.getByRole('button', {name: 'Widget warnings'}));
  30. expect(await screen.findByText('This widget has stale data')).toBeInTheDocument();
  31. });
  32. });
  33. describe('Badge', () => {
  34. it('Shows a single badge', () => {
  35. const {rerender} = render(<WidgetFrame title="count()" />);
  36. expect(screen.queryByText('Sampled')).not.toBeInTheDocument();
  37. rerender(
  38. <WidgetFrame
  39. title="count()"
  40. badgeProps={{
  41. text: 'Sampled',
  42. }}
  43. />
  44. );
  45. expect(screen.getByText('Sampled')).toBeInTheDocument();
  46. });
  47. it('Shows multiple badges', () => {
  48. const {rerender} = render(<WidgetFrame title="count()" />);
  49. expect(screen.queryByText('Sampled')).not.toBeInTheDocument();
  50. rerender(
  51. <WidgetFrame
  52. title="count()"
  53. badgeProps={[
  54. {
  55. text: 'Sampled',
  56. },
  57. {
  58. text: 'Extracted',
  59. },
  60. ]}
  61. />
  62. );
  63. expect(screen.getByText('Sampled')).toBeInTheDocument();
  64. expect(screen.getByText('Extracted')).toBeInTheDocument();
  65. });
  66. });
  67. describe('Action Menu', () => {
  68. it('Renders a single action as a button', async () => {
  69. const onAction = jest.fn();
  70. render(
  71. <WidgetFrame
  72. title="EPS"
  73. description="Number of events per second"
  74. actions={[
  75. {
  76. key: 'hello',
  77. label: 'Make Go',
  78. onAction,
  79. },
  80. ]}
  81. />
  82. );
  83. const $button = screen.getByRole('button', {name: 'Make Go'});
  84. expect($button).toBeInTheDocument();
  85. await userEvent.click($button);
  86. expect(onAction).toHaveBeenCalledTimes(1);
  87. });
  88. it('Allows disabling a single action', async () => {
  89. const onAction = jest.fn();
  90. render(
  91. <WidgetFrame
  92. title="EPS"
  93. description="Number of events per second"
  94. actionsDisabled
  95. actionsMessage="Actions are not supported"
  96. actions={[
  97. {
  98. key: 'hello',
  99. label: 'Make Go',
  100. onAction,
  101. },
  102. ]}
  103. />
  104. );
  105. const $button = screen.getByRole('button', {name: 'Make Go'});
  106. expect($button).toBeInTheDocument();
  107. expect($button).toBeDisabled();
  108. await userEvent.click($button);
  109. expect(onAction).not.toHaveBeenCalled();
  110. await userEvent.hover($button);
  111. expect(await screen.findByText('Actions are not supported')).toBeInTheDocument();
  112. });
  113. it('Renders multiple actions in a dropdown menu', async () => {
  114. const onAction1 = jest.fn();
  115. const onAction2 = jest.fn();
  116. render(
  117. <WidgetFrame
  118. title="EPS"
  119. description="Number of events per second"
  120. actions={[
  121. {
  122. key: 'one',
  123. label: 'One',
  124. onAction: onAction1,
  125. },
  126. {
  127. key: 'two',
  128. label: 'Two',
  129. onAction: onAction2,
  130. },
  131. ]}
  132. />
  133. );
  134. await userEvent.click(screen.getByRole('button', {name: 'Widget actions'}));
  135. await userEvent.click(screen.getByRole('menuitemradio', {name: 'One'}));
  136. expect(onAction1).toHaveBeenCalledTimes(1);
  137. await userEvent.click(screen.getByRole('button', {name: 'Widget actions'}));
  138. await userEvent.click(screen.getByRole('menuitemradio', {name: 'Two'}));
  139. expect(onAction2).toHaveBeenCalledTimes(1);
  140. });
  141. it('Allows disabling multiple actions', async () => {
  142. render(
  143. <WidgetFrame
  144. title="EPS"
  145. description="Number of events per second"
  146. actionsDisabled
  147. actionsMessage="Actions are not supported"
  148. actions={[
  149. {
  150. key: 'one',
  151. label: 'One',
  152. },
  153. {
  154. key: 'two',
  155. label: 'Two',
  156. },
  157. ]}
  158. />
  159. );
  160. const $trigger = screen.getByRole('button', {name: 'Widget actions'});
  161. await userEvent.click($trigger);
  162. expect(screen.queryByRole('menuitemradio', {name: 'One'})).not.toBeInTheDocument();
  163. expect(screen.queryByRole('menuitemradio', {name: 'Two'})).not.toBeInTheDocument();
  164. await userEvent.hover($trigger);
  165. expect(await screen.findByText('Actions are not supported')).toBeInTheDocument();
  166. });
  167. it('Shows actions even in error state', async () => {
  168. const onAction = jest.fn();
  169. const error = new Error('Something is wrong');
  170. render(
  171. <WidgetFrame
  172. title="EPS"
  173. description="Number of events per second"
  174. error={error}
  175. actions={[
  176. {
  177. key: 'hello',
  178. label: 'Make Go',
  179. onAction,
  180. },
  181. ]}
  182. />
  183. );
  184. const $button = screen.getByRole('button', {name: 'Make Go'});
  185. expect($button).toBeInTheDocument();
  186. await userEvent.click($button);
  187. expect(onAction).toHaveBeenCalledTimes(1);
  188. });
  189. it('Shows a "Retry" action if a retry callback is provided', () => {
  190. const onRetry = jest.fn();
  191. const error = new Error('Something is wrong');
  192. render(<WidgetFrame title="EPS" error={error} onRetry={onRetry} />);
  193. expect(screen.getByRole('button', {name: 'Retry'})).toBeInTheDocument();
  194. });
  195. });
  196. describe('Full Screen View Button', () => {
  197. it('Renders a full screen view button', async () => {
  198. const onFullScreenViewClick = jest.fn();
  199. const {rerender} = render(<WidgetFrame title="count()" />);
  200. expect(
  201. screen.queryByRole('button', {name: 'Open Full-Screen View'})
  202. ).not.toBeInTheDocument();
  203. rerender(
  204. <WidgetFrame title="count()" onFullScreenViewClick={onFullScreenViewClick} />
  205. );
  206. const $button = screen.getByRole('button', {name: 'Open Full-Screen View'});
  207. expect($button).toBeInTheDocument();
  208. await userEvent.click($button);
  209. expect(onFullScreenViewClick).toHaveBeenCalledTimes(1);
  210. });
  211. it('Hides full screen button if the widget has an error', () => {
  212. const onFullScreenViewClick = jest.fn();
  213. render(
  214. <WidgetFrame
  215. title="count()"
  216. onFullScreenViewClick={onFullScreenViewClick}
  217. error={new Error('Something went wrong')}
  218. />
  219. );
  220. const $button = screen.queryByRole('button', {name: 'Open Full-Screen View'});
  221. expect($button).not.toBeInTheDocument();
  222. });
  223. });
  224. });
  225. function UhOh() {
  226. const items: string[] = [];
  227. return <div>{items[0]!.toUpperCase()}</div>;
  228. }