Add tests for various user profile setups to sidebar (#212)
* Add tests for various user profile setups to sidebar * Don't show empty user image if it isn't there * @adamchalmer review
This commit is contained in:
		| @ -20,6 +20,7 @@ export const ActionButton = ({ | ||||
|   to = '/', | ||||
|   Element = 'button', | ||||
|   children, | ||||
|   ...props | ||||
| }: ActionButtonProps) => { | ||||
|   const classNames = `group mono text-base flex items-center gap-2 rounded-sm border border-chalkboard-40 dark:border-chalkboard-60 hover:border-liquid-40 dark:hover:bg-chalkboard-90 p-[3px] ${ | ||||
|     icon ? 'pr-2' : 'px-2' | ||||
| @ -27,21 +28,21 @@ export const ActionButton = ({ | ||||
|  | ||||
|   if (Element === 'button') { | ||||
|     return ( | ||||
|       <button onClick={onClick} className={classNames}> | ||||
|       <button onClick={onClick} className={classNames} {...props}> | ||||
|         {icon && <ActionIcon {...icon} />} | ||||
|         {children} | ||||
|       </button> | ||||
|     ) | ||||
|   } else if (Element === 'link') { | ||||
|     return ( | ||||
|       <Link to={to} className={classNames}> | ||||
|       <Link to={to} className={classNames} {...props}> | ||||
|         {icon && <ActionIcon {...icon} />} | ||||
|         {children} | ||||
|       </Link> | ||||
|     ) | ||||
|   } else { | ||||
|     return ( | ||||
|       <Element onClick={onClick} className={classNames}> | ||||
|       <Element onClick={onClick} className={classNames} {...props}> | ||||
|         {icon && <ActionIcon {...icon} />} | ||||
|         {children} | ||||
|       </Element> | ||||
|  | ||||
							
								
								
									
										66
									
								
								src/components/UserSidebarMenu.test.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								src/components/UserSidebarMenu.test.tsx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,66 @@ | ||||
| import { fireEvent, render, screen } from '@testing-library/react' | ||||
| import { User } from '../useStore' | ||||
| import UserSidebarMenu from './UserSidebarMenu' | ||||
| import { BrowserRouter } from 'react-router-dom' | ||||
|  | ||||
| it("Renders user's name and email if available", () => { | ||||
|   const userWellFormed: User = { | ||||
|     id: '8675309', | ||||
|     name: 'Test User', | ||||
|     email: 'kittycad.sidebar.test@example.com', | ||||
|     image: 'https://placekitten.com/200/200', | ||||
|     created_at: 'yesteryear', | ||||
|     updated_at: 'today', | ||||
|   } | ||||
|  | ||||
|   render( | ||||
|     <BrowserRouter> | ||||
|       <UserSidebarMenu user={userWellFormed} /> | ||||
|     </BrowserRouter> | ||||
|   ) | ||||
|  | ||||
|   fireEvent.click(screen.getByTestId('user-sidebar-toggle')) | ||||
|  | ||||
|   expect(screen.getByTestId('username')).toHaveTextContent( | ||||
|     userWellFormed.name || '' | ||||
|   ) | ||||
|   expect(screen.getByTestId('email')).toHaveTextContent(userWellFormed.email) | ||||
| }) | ||||
|  | ||||
| it("Renders just the user's email if no name is available", () => { | ||||
|   const userNoName: User = { | ||||
|     id: '8675309', | ||||
|     email: 'kittycad.sidebar.test@example.com', | ||||
|     image: 'https://placekitten.com/200/200', | ||||
|     created_at: 'yesteryear', | ||||
|     updated_at: 'today', | ||||
|   } | ||||
|  | ||||
|   render( | ||||
|     <BrowserRouter> | ||||
|       <UserSidebarMenu user={userNoName} /> | ||||
|     </BrowserRouter> | ||||
|   ) | ||||
|  | ||||
|   fireEvent.click(screen.getByTestId('user-sidebar-toggle')) | ||||
|  | ||||
|   expect(screen.getByTestId('username')).toHaveTextContent(userNoName.email) | ||||
| }) | ||||
|  | ||||
| it('Renders a menu button if no user avatar is available', () => { | ||||
|   const userNoAvatar: User = { | ||||
|     id: '8675309', | ||||
|     name: 'Test User', | ||||
|     email: 'kittycad.sidebar.test@example.com', | ||||
|     created_at: 'yesteryear', | ||||
|     updated_at: 'today', | ||||
|   } | ||||
|  | ||||
|   render( | ||||
|     <BrowserRouter> | ||||
|       <UserSidebarMenu user={userNoAvatar} /> | ||||
|     </BrowserRouter> | ||||
|   ) | ||||
|  | ||||
|   expect(screen.getByTestId('user-sidebar-toggle')).toHaveTextContent('Menu') | ||||
| }) | ||||
| @ -3,24 +3,45 @@ import { User, useStore } from '../useStore' | ||||
| import { ActionButton } from './ActionButton' | ||||
| import { faBars, faGear, faSignOutAlt } from '@fortawesome/free-solid-svg-icons' | ||||
| import { useNavigate } from 'react-router-dom' | ||||
| import { A } from '@tauri-apps/api/path-c062430b' | ||||
| import { useState } from 'react' | ||||
|  | ||||
| const UserSidebarMenu = ({ user }: { user?: User }) => { | ||||
|   const displayedName = getDisplayName(user) | ||||
|   const [imageLoadFailed, setImageLoadFailed] = useState(false) | ||||
|   const navigate = useNavigate() | ||||
|   const { setToken } = useStore((s) => ({ | ||||
|     setToken: s.setToken, | ||||
|   })) | ||||
|  | ||||
|   // Fallback logic for displaying user's "name": | ||||
|   // 1. user.name | ||||
|   // 2. user.first_name + ' ' + user.last_name | ||||
|   // 3. user.first_name | ||||
|   // 4. user.email | ||||
|   function getDisplayName(user?: User) { | ||||
|     if (!user) return null | ||||
|     if (user.name) return user.name | ||||
|     if (user.first_name) { | ||||
|       if (user.last_name) return user.first_name + ' ' + user.last_name | ||||
|       return user.first_name | ||||
|     } | ||||
|     return user.email | ||||
|   } | ||||
|  | ||||
|   return ( | ||||
|     <Popover className="relative"> | ||||
|       {user?.image ? ( | ||||
|         <Popover.Button className="border-0 rounded-full w-fit p-0"> | ||||
|       {user?.image && !imageLoadFailed ? ( | ||||
|         <Popover.Button | ||||
|           className="border-0 rounded-full w-fit p-0" | ||||
|           data-testid="user-sidebar-toggle" | ||||
|         > | ||||
|           <div className="rounded-full border border-chalkboard-70/50 hover:border-liquid-50 overflow-hidden"> | ||||
|             <img | ||||
|               src={user?.image || ''} | ||||
|               alt={user?.name || ''} | ||||
|               className="h-8 w-8" | ||||
|               referrerPolicy="no-referrer" | ||||
|               onError={() => setImageLoadFailed(true)} | ||||
|             /> | ||||
|           </div> | ||||
|         </Popover.Button> | ||||
| @ -29,6 +50,7 @@ const UserSidebarMenu = ({ user }: { user?: User }) => { | ||||
|           Element={Popover.Button} | ||||
|           icon={{ icon: faBars }} | ||||
|           className="border-transparent" | ||||
|           data-testid="user-sidebar-toggle" | ||||
|         > | ||||
|           Menu | ||||
|         </ActionButton> | ||||
| @ -38,23 +60,29 @@ const UserSidebarMenu = ({ user }: { user?: User }) => { | ||||
|       <Popover.Panel className="fixed inset-0 left-auto z-30 w-64 bg-chalkboard-10 dark:bg-chalkboard-100 border border-liquid-100 shadow-md rounded-l-lg"> | ||||
|         {user && ( | ||||
|           <div className="flex items-center gap-4 px-4 py-3 bg-liquid-100"> | ||||
|             <div className="rounded-full shadow-inner overflow-hidden"> | ||||
|               <img | ||||
|                 src={user?.image || ''} | ||||
|                 alt={user?.name || ''} | ||||
|                 className="h-8 w-8" | ||||
|                 referrerPolicy="no-referrer" | ||||
|               /> | ||||
|             </div> | ||||
|             {user.image && !imageLoadFailed && ( | ||||
|               <div className="rounded-full shadow-inner overflow-hidden"> | ||||
|                 <img | ||||
|                   src={user.image} | ||||
|                   alt={user.name || ''} | ||||
|                   className="h-8 w-8" | ||||
|                   referrerPolicy="no-referrer" | ||||
|                   onError={() => setImageLoadFailed(true)} | ||||
|                 /> | ||||
|               </div> | ||||
|             )} | ||||
|  | ||||
|             <div> | ||||
|               <p className="m-0 text-liquid-10 text-mono"> | ||||
|                 {user.name || | ||||
|                   user.first_name + ' ' + user.last_name || | ||||
|                   user.email} | ||||
|               <p | ||||
|                 className="m-0 text-liquid-10 text-mono" | ||||
|                 data-testid="username" | ||||
|               > | ||||
|                 {displayedName || ''} | ||||
|               </p> | ||||
|               {(user.name || user.first_name) && ( | ||||
|                 <p className="m-0 text-liquid-40 text-xs">{user.email}</p> | ||||
|               {displayedName !== user.email && ( | ||||
|                 <p className="m-0 text-liquid-40 text-xs" data-testid="email"> | ||||
|                   {user.email} | ||||
|                 </p> | ||||
|               )} | ||||
|             </div> | ||||
|           </div> | ||||
|  | ||||
| @ -1,4 +1,4 @@ | ||||
| import create from 'zustand' | ||||
| import { create } from 'zustand' | ||||
| import { persist } from 'zustand/middleware' | ||||
| import { addLineHighlight, EditorView } from './editor/highlightextension' | ||||
| import { abstractSyntaxTree } from './lang/abstractSyntaxTree' | ||||
|  | ||||
		Reference in New Issue
	
	Block a user