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

Can't access to elements using by role React native testing library due to a lack of accesibility attribute #2613

Open
mfrfinbox opened this issue May 1, 2024 · 3 comments

Comments

@mfrfinbox
Copy link

mfrfinbox commented May 1, 2024

Current Behavior

When trying to test my components using RNTL and getByRole "button" I get an error since Tamagui implementation it's just a View under the hood (As far as I could figure out).

Expected Behavior

It will be nice to have accesibility=true for Button and elements that provide accesibilityRole for having a good and more standarized test creation experience.

Tamagui Version

^1.89.29

Platform (Web, iOS, Android)

iOS and Android, not sure about Web

Reproduction

import { render, screen, userEvent } from "@testing-library/react-native";
import { useState } from "react";
import { Button, Checkbox, SizableText, TamaguiProvider, View } from "tamagui";

import { config } from "../tamagui.config";

const MyComponent = () => {
  const [allAgreed, setAllAgreed] = useState(false);
  const handleAgree = (agreed: boolean) => {
    agreed && setAllAgreed(agreed);
  };

  return (
    <View>
      <Checkbox
        id="checkbox-1"
        testID="checkbox-1"
        size="$3"
        onCheckedChange={handleAgree}
      >
        <Checkbox.Indicator>
          <SizableText>X</SizableText>
        </Checkbox.Indicator>
      </Checkbox>
      <Button accessible disabled={!allAgreed}>
        Agree
      </Button>
    </View>
  );
};

describe("Reproducible Tamagui issue #2613", () => {
  it("Enables button after checking the checkbox", () => {
    render(
      <TamaguiProvider config={config}>
        <MyComponent />
      </TamaguiProvider>,
    );

    const checkbox1 = screen.getByTestId("checkbox-1");

    const user = userEvent.setup();
    user.press(checkbox1);

    // This will fail, and we shouldn't check the button's pointerEvents prop anyway
    expect(screen.getByRole("button")).not.toHaveProp("pointerEvents", "none");

    // This should work but IS NOT WORKING because of 2 issues:
    // 1. Can't select by role see: https://github.com/tamagui/tamagui/issues/2613
    // 2. User event is not triggered
    expect(screen.getByRole("button")).toBeEnabled();
  });
});

System Info

System:
    OS: macOS 14.4.1
    CPU: (12) arm64 Apple M2 Pro
    Memory: 71.98 MB / 16.00 GB
    Shell: 5.9 - /bin/zsh
  Binaries:
    Node: 20.11.0 - ~/.nvm/versions/node/v20.11.0/bin/node
    Yarn: 4.0.1 - ~/.nvm/versions/node/v20.11.0/bin/yarn
    npm: 10.5.0 - ~/WebstormProjects/ppa-mobile/node_modules/.bin/npm
    pnpm: 8.15.4 - ~/Library/pnpm/pnpm
    Watchman: 2024.03.18.00 - /opt/homebrew/bin/watchman
  Browsers:
    Chrome: 124.0.6367.118
    Edge: 124.0.2478.67
    Safari: 17.4.1
  npmPackages:
    @aptabase/react-native: ^0.3.9 => 0.3.9 
    @babel/core: ^7.20.0 => 7.24.3 
    @babel/preset-typescript: ^7.24.1 => 7.24.1 
    @clerk/clerk-expo: ^0.20.5 => 0.20.10 
    @date-fns/utc: ^1.2.0 => 1.2.0 
    @dev-plugins/react-query: ^0.0.5 => 0.0.5 
    @expo-google-fonts/sofia-sans-extra-condensed: ^0.2.3 => 0.2.3 
    @expo-google-fonts/work-sans: ^0.2.3 => 0.2.3 
    @gorhom/bottom-sheet: ^4.6.1 => 4.6.1 
    @hookform/resolvers: ^3.3.4 => 3.3.4 
    @react-native-async-storage/async-storage: 1.21.0 => 1.21.0 
    @sentry/react-native: 5.19.1 => 5.19.1 
    @shopify/react-native-skia: 0.1.221 => 0.1.221 
    @supabase/supabase-js: ^2.39.6 => 2.39.8 
    @tamagui/animations-css: ^1.90.2 => 1.92.1 
    @tamagui/config: ^1.89.29 => 1.92.1 
    @tamagui/core: ^1.90.2 => 1.92.1 
    @tamagui/create-theme: ^1.90.2 => 1.92.1 
    @tamagui/list-item: ^1.90.2 => 1.92.1 
    @tamagui/lucide-icons: ^1.90.2 => 1.92.1 
    @tamagui/metro-plugin: ^1.89.29 => 1.92.1 
    @tamagui/react-native-svg: ^1.90.2 => 1.92.1 
    @tamagui/toast: ^1.90.2 => 1.92.1 
    @tanstack/eslint-plugin-query: ^5.20.1 => 5.28.6 
    @tanstack/react-query: ^5.21.7 => 5.28.6 
    @tanstack/react-query-devtools: ^5.21.7 => 5.28.6 
    @testing-library/jest-native: ^5.4.3 => 5.4.3 
    @testing-library/react-native: ^12.4.5 => 12.4.5 
    @types/jest: ^29.5.12 => 29.5.12 
    @types/jsonwebtoken: ^9.0.5 => 9.0.6 
    @types/react: ~18.2.45 => 18.2.69 
    @types/react-test-renderer: ^18.0.7 => 18.0.7 
    @wuba/react-native-echarts: ^1.2.5 => 1.3.0 
    ably: ^1.2.49 => 1.2.50 
    axios: ^1.6.8 => 1.6.8 
    burnt: ^0.12.2 => 0.12.2 
    change-case: ^5.4.3 => 5.4.3 
    date-fns: ^3.3.1 => 3.6.0 
    echarts: ^5.5.0 => 5.5.0 
    env-cmd: ^10.1.0 => 10.1.0 
    eslint: ^8.56.0 => 8.57.0 
    eslint-config-universe: ^12.0.0 => 12.0.0 
    eslint-plugin-testing-library: ^6.2.2 => 6.2.2 
    expo: ~50.0.13 => 50.0.14 
    expo-application: ~5.8.3 => 5.8.3 
    expo-av: ~13.10.5 => 13.10.5 
    expo-build-properties: ~0.11.1 => 0.11.1 
    expo-constants: ~15.4.5 => 15.4.5 
    expo-dev-client: ~3.3.11 => 3.3.11 
    expo-device: ~5.9.3 => 5.9.3 
    expo-document-picker: ~11.10.1 => 11.10.1 
    expo-font: ~11.10.3 => 11.10.3 
    expo-image-picker: ~14.7.1 => 14.7.1 
    expo-jwt: ^1.7.0 => 1.7.1 
    expo-linear-gradient: ~12.7.2 => 12.7.2 
    expo-linking: ~6.2.2 => 6.2.2 
    expo-notifications: ~0.27.6 => 0.27.6 
    expo-router: ~3.4.8 => 3.4.8 
    expo-secure-store: ^12.8.1 => 12.8.1 
    expo-status-bar: ~1.11.1 => 1.11.1 
    expo-updates: ~0.24.12 => 0.24.12 
    i: ^0.3.7 => 0.3.7 
    jest: ^29.3.1 => 29.7.0 
    jest-expo: ~50.0.4 => 50.0.4 
    jotai: ^2.7.1 => 2.7.1 
    jsonwebtoken: ^9.0.2 => 9.0.2 
    mime: ^4.0.1 => 4.0.1 
    moti: ^0.28.1 => 0.28.1 
    native-notify: ^4.0.0 => 4.0.0 
    npm: ^10.5.0 => 10.5.0 
    postgres: ^3.4.3 => 3.4.4 
    prettier: ^3.2.5 => 3.2.5 
    react: 18.2.0 => 18.2.0 
    react-dom: 18.2.0 => 18.2.0 
    react-hook-form: ^7.51.0 => 7.51.1 
    react-native: 0.73.6 => 0.73.6 
    react-native-circular-progress: ^1.3.9 => 1.3.9 
    react-native-dialog: ^9.3.0 => 9.3.0 
    react-native-dotenv: ^3.4.10 => 3.4.11 
    react-native-gesture-handler: ~2.14.0 => 2.14.1 
    react-native-progress: ^5.0.1 => 5.0.1 
    react-native-reanimated: ~3.6.2 => 3.6.3 
    react-native-responsive-screen: ^1.4.2 => 1.4.2 
    react-native-safe-area-context: 4.8.2 => 4.8.2 
    react-native-screens: ~3.29.0 => 3.29.0 
    react-native-size-matters: ^0.4.2 => 0.4.2 
    react-native-svg: ^14.1.0 => 14.1.0 
    react-native-ui-lib: ^7.17.2 => 7.18.3 
    react-native-url-polyfill: ^2.0.0 => 2.0.0 
    react-native-web: ~0.19.6 => 0.19.10 
    react-native-webview: 13.6.4 => 13.6.4 
    react-native-youtube-iframe: ^2.3.0 => 2.3.0 
    react-test-renderer: 18.2.0 => 18.2.0 
    rn-tourguide: ^3.3.0 => 3.3.0 
    tamagui: ^1.89.29 => 1.92.1 
    typescript: ^5.1.3 => 5.4.3 
    vexo-analytics: ^1.3.13 => 1.3.13 
    zod: ^3.22.4 => 3.22.4

debug output

console.log
    <View>
      <View
        accessibilityRole="checkbox"
        accessibilityState={
          {
            "checked": false,
          }
        }
        checked={false}
        focusVisibleStyle={{}}
        focusable={true}
        id="checkbox-1"
        style={
          {
            "alignItems": "center",
            "backgroundColor": "hsl(0, 0%, 96.3%)",
            "borderBottomColor": "hsl(0, 0%, 90.0%)",
            "borderBottomLeftRadius": 9.642857142857142,
            "borderBottomRightRadius": 9.642857142857142,
            "borderBottomWidth": 1,
            "borderLeftColor": "hsl(0, 0%, 90.0%)",
            "borderLeftWidth": 1,
            "borderRightColor": "hsl(0, 0%, 90.0%)",
            "borderRightWidth": 1,
            "borderStyle": "solid",
            "borderTopColor": "hsl(0, 0%, 90.0%)",
            "borderTopLeftRadius": 9.642857142857142,
            "borderTopRightRadius": 9.642857142857142,
            "borderTopWidth": 1,
            "flexDirection": "column",
            "height": 35,
            "justifyContent": "center",
            "width": 35,
          }
        }
      />
      <View
        accessibilityRole="button"
        focusVisibleStyle={
          {
            "borderColor": "$background",
          }
        }
        pointerEvents="none"
        style={
          {
            "alignItems": "center",
            "backgroundColor": "hsl(0, 0%, 94.1%)",
            "borderBottomColor": "transparent",
            "borderBottomLeftRadius": 19.285714285714285,
            "borderBottomRightRadius": 19.285714285714285,
            "borderBottomWidth": 1,
            "borderLeftColor": "transparent",
            "borderLeftWidth": 1,
            "borderRightColor": "transparent",
            "borderRightWidth": 1,
            "borderStyle": "solid",
            "borderTopColor": "transparent",
            "borderTopLeftRadius": 19.285714285714285,
            "borderTopRightRadius": 19.285714285714285,
            "borderTopWidth": 1,
            "flexDirection": "row",
            "flexWrap": "nowrap",
            "height": 94.28571428571428,
            "justifyContent": "center",
            "paddingLeft": 38.57142857142857,
            "paddingRight": 38.57142857142857,
          }
        }
      >
        <Text
          lineBreakMode="clip"
          numberOfLines={1}
          style={
            {
              "color": "hsl(0, 0%, 9.0%)",
              "flexGrow": 0,
              "flexShrink": 1,
              "fontFamily": "WorkSans_400Regular",
              "fontSize": 30,
              "letterSpacing": 0,
              "lineHeight": 49.285714285714285,
            }
          }
          suppressHighlighting={true}
          userSelect="none"
        >
          Agree
        </Text>
      </View>
    </View>

      at log (node_modules/@testing-library/react-native/src/helpers/debug-deep.ts:24:13)
@mdjastrzebski
Copy link

@mfrfinbox the repro scenario is missing <MyComponent /> code which is needed to understand the issue. It would be good to also include screen.debug() output to show what Tamagui renders in terms of host components & their props.

Issue TLDR: Tamagui in some cases sets accessibilityRole without setting accessible prop on View:

<View accessibilityRole="radiogroup">

Full output: callstack/react-native-testing-library#1566 (comment)

From my experiments, and hence how RNTL handles accessibility role, having accessibilityRole on a View without accessible prop, does not make Accessibility Inspector/screen reader be aware of such role.

@mfrfinbox
Copy link
Author

mfrfinbox commented May 2, 2024

I've update the repro scenario and added the debug output, additionally here's a picture of the issue;

CleanShot 2024-05-02 at 10 31 17

As we can see in the debug output since accesible attribute is not present it will not find the button (and all other elements that have accesibility role and not accesible attribute out of the box)

I hipe that makes sence and please let me know if you need more info

@mfrfinbox
Copy link
Author

Adding accesible will make it work but I believe this should be there out of the box:

CleanShot 2024-05-02 at 10 38 30

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants