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