Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Bug]: TypeError: Cannot read properties of null (reading 'focus') when testing shadcn's Select component #1374

Open
FaitAccompli opened this issue Oct 14, 2024 · 0 comments
Labels
bug Something isn't working

Comments

@FaitAccompli
Copy link

FaitAccompli commented Oct 14, 2024

Environment

Developement/Production OS: Windows 10 19045
Node version: 22.6.0
Package manager: [email protected]
Radix Vue version: 1.9.7
Vue version: 3.4.38
TypeScript version: 5.4.5
CSS framework: [email protected]
Vitest version: 2.1.2

Link to minimal reproduction

N/A

Steps to reproduce

  • Run vitest on the test file

Describe the bug

While running vitest to perform component testing for a custom component I have containing shadcn's Select component, it displays a Vue Warning

⎯⎯⎯⎯ Unhandled Rejection ⎯⎯⎯⎯⎯
TypeError: Cannot read properties of null (reading 'focus')
 ❯ f node_modules/.pnpm/[email protected][email protected][email protected]_/node_modules/radix-vue/dist/index.js:14515:112
    14513|     async function f(m) {
    14514|       var w;
    14515|       await oe(), !m.defaultPrevented && (e.value ? (w = l.onItemLeave…
       |                                                                                                                ^
    14516|     }
    14517|     async function v(m) {

Here's the stack trace
 Unhandled error during execution of native event handler 
  at <Primitive ref=fn<s> role="option" data-radix-vue-collection-item=""  ... > 
  at <SelectItem value="3" class="relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50" > 
  at <SelectItem key=3 value="3" > 
  at <Primitive role="group" as=undefined asChild=false  ... > 
  at <SelectGroup class="p-1 w-full" asChild=false as=undefined > 
  at <SelectGroup> 
  at <Primitive ref=fn<s> data-radix-select-viewport="" role="presentation"  ... > 
  at <SelectViewport class="p-1 h-[--radix-select-trigger-height] w-full min-w-[--radix-select-trigger-width]" > 
  at <Primitive ref=fn<s> style= {
  boxSizing: 'border-box',
  '--radix-select-content-transform-origin': 'var(--radix-popper-transform-origin)',
  '--radix-select-content-available-width': 'var(--radix-popper-available-width)',
  '--radix-select-content-available-height': 'var(--radix-popper-available-height)',
  '--radix-select-trigger-width': 'var(--radix-popper-anchor-width)',
  '--radix-select-trigger-height': 'var(--radix-popper-anchor-height)',
  display: 'flex',
  flexDirection: 'column',
  outline: 'none',
  pointerEvents: 'auto',
  animation: undefined
} position="popper"  ... > 
  at <PopperContent align="start" collisionPadding=10 style= {
  boxSizing: 'border-box',
  '--radix-select-content-transform-origin': 'var(--radix-popper-transform-origin)',
  '--radix-select-content-available-width': 'var(--radix-popper-available-width)',
  '--radix-select-content-available-height': 'var(--radix-popper-available-height)',
  '--radix-select-trigger-width': 'var(--radix-popper-anchor-width)',
  '--radix-select-trigger-height': 'var(--radix-popper-anchor-height)',
  display: 'flex',
  flexDirection: 'column',
  outline: 'none',
  pointerEvents: 'auto'
}  ... > 
  at <SelectPopperPosition position="popper" bodyLock=true align="start"  ... > 
  at <PrimitiveSlot data-dismissable-layer="" style= { pointerEvents: 'auto' } onFocusCapture=fn<onFocusCapture>  ... > 
  at <Primitive ref=fn<s> as-child=true as=undefined  ... > 
  at <DismissableLayer as-child="" disable-outside-pointer-events="" onFocusOutside=fn  ... > 
  at <PrimitiveSlot tabindex="-1" onKeydown=fn<d> class="relative z-50 min-w-32 overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1 max-h-80 overflow-y-scroll" > 
  at <Primitive ref_key="currentRef" ref=Ref< { asChild: [Getter/Setter], as: [Getter/Setter] } > tabindex="-1"  ... > 
  at <FocusScope as-child="" onMountAutoFocus=fn onUnmountAutoFocus=fn  ... > 
  at <SelectContentImpl position="popper" onCloseAutoFocus=fn onEscapeKeyDown=fn  ... > 
  at <Presence key=0 ref_key="presenceRef" ref=Ref< {
  present: <ref *1> ComputedRefImpl {
    getter: [Function (anonymous)],
    _setter: [Function: setter],
    dep: undefined,
    __v_isRef: true,
    __v_isReadonly: true,
    effect: ReactiveEffect {
      fn: [Function (anonymous)],
      trigger: [Function (anonymous)],
      scheduler: undefined,
      active: true,
      deps: [],
      _dirtyLevel: 4,
      _trackId: 0,
      _runnings: 0,
      _shouldSchedule: false,
      _depsLength: 0,
      computed: [Circular *1]
    },
    _cacheable: true
  }
} >  ... > 
  at <SelectContent position="popper" onCloseAutoFocus=fn onEscapeKeyDown=fn  ... > 
  at <Teleport disabled=false forceMount=false to=undefined > 
  at <SelectPortal> 
  at <SelectContent class="max-h-80 overflow-y-scroll" > 
  at <PopperRoot> 
  at <SelectRoot modelValue="-1" onUpdate:modelValue=fn onUpdate:open=fn > 
  at <Select modelValue="-1" onUpdate:modelValue=fn > 

In order to properly test shadcn's Select component we need to mock PointerEvent this was mentioned in radix-ui/primitives#1822

import { render, screen, within } from '@testing-library/vue';
import userEvent from '@testing-library/user-event';
import {
  Select,
  SelectContent,
  SelectGroup,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from '@/components/ui/select'

class MockPointerEvent extends Event {
  button: number;
  ctrlKey: boolean;
  pointerType: string;

  constructor(type: string, props: PointerEventInit) {
    super(type, props);
    this.button = props.button || 0;
    this.ctrlKey = props.ctrlKey || false;
    this.pointerType = props.pointerType || 'mouse';
  }
}
window.PointerEvent = MockPointerEvent as any;
window.HTMLElement.prototype.scrollIntoView = vi.fn();
window.HTMLElement.prototype.releasePointerCapture = vi.fn();
window.HTMLElement.prototype.hasPointerCapture = vi.fn();

it('should pass', async () => {
  const user = userEvent.setup();
  render(
    <Select>
      <SelectTrigger aria-label="Food">
        <SelectValue placeholder="Select a fruit..." />
      </SelectTrigger>
        <SelectContent>
          <SelectGroup>
            <SelectItem value="apple">Apple</SelectItem>
            <SelectItem value="banana">Banana</SelectItem>
          </SelectGroup>
        </SelectContent>
    </Select>
  );

   //look for the select trigger
  const trigger = screen.getByRole('combobox', {
    name: 'Food',
  });

  //click on the trigger
  await user.click(trigger)

   //look for the apple option 
  const option = screen.getByRole('option', { name: 'Apple' })
  
  //click on the apple option
  await user.click(option)
});

Expected behavior

  • Should not display the Vue Warning

Context & Screenshots (if applicable)

  • For added context, this also happens with @testing-library/react but with a different set of warnings. Here's the codesandbox link
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

1 participant