question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

findByRole not working properly after element is present in DOM

See original GitHub issue

I have a markup that some elements visibility depends on a condition. Something like this:

@Component({
  changeDetection: ChangeDetectionStrategy.OnPush,
  selector: 'app-test',
  template: `
    <form [formGroup]="formGroup">
      <mat-form-field>
        <mat-label>Number</mat-label>
        <input matInput required formControlName="number" />
      </mat-form-field>

      <ng-container *ngIf="formGroup.get('number').valid">
        <mat-form-field>
          <mat-label>Holder</mat-label>
          <input matInput required formControlName="holder" />
        </mat-form-field>

        <mat-form-field>
          <mat-label>Expiry</mat-label>
          <input matInput required formControlName="expiry" />
        </mat-form-field>

        <mat-form-field>
          <mat-label>CVV</mat-label>
          <input matInput required formControlName="cvv" />
        </mat-form-field>
      </ng-container>
    </form>
  `,
})
export class TestComponent {
  readonly formGroup = this.formBuilder.group({
    number: ['', [Validators.required, Validators.minlength(16), ...otherValidators]],
    holder: ['', [Validators.required, Validators.minlength(5), ...otherValidators]],
    expiry: ['', [Validators.required, Validators.minlength(7), ...otherValidators]],
    cvv: ['', [Validators.required, Validators.minlength(3), ...otherValidators]],
  });

  constructor(private readonly formBuilder: FormBuilder) {}
}

//////// Test file

async function setup() {
  const { rerender } = await render(TestComponent);
  const numberHtmlElementRef = screen.getByRole('textbox', { name: /number/i });
  const holderHtmlElementRef = screen.findByRole('textbox', { name: /holder/i });
  const expiryHtmlElementRef = screen.findByRole('textbox', { name: /expiry/i });
  const cvvHtmlElementRef = screen.findByRole('textbox', { name: /cvv/i });

  return {
    numberHtmlElementRef,
    holderHtmlElementRef,
    expiryHtmlElementRef,
    cvvHtmlElementRef,
    rerender,
  } as const;
}

describe(TestComponent.name, () => {
  test('someDescription', async () => {
    const {
      numberHtmlElementRef,
      holderHtmlElementRef,
      expiryHtmlElementRef,
      cvvHtmlElementRef,
    } = await setup();

    await expect(holderHtmlElementRef).rejects.toThrow(/Unable to find/i); // ok
    await expect(expiryHtmlElementRef).rejects.toThrow(/Unable to find/i); // ok
    await expect(cvvHtmlElementRef).rejects.toThrow(/Unable to find/i); // ok
    await userEvent.type(numberHtmlElementRef, '5353107424870314');
    expect(numberHtmlElementRef).toBeValid(); // ok
    await expect(holderHtmlElementRef).resolves.toBeInTheDocument(); // fail
    await expect(expiryHtmlElementRef).resolves.toBeInTheDocument(); // fail
    await expect(cvvHtmlElementRef).resolves.toBeInTheDocument(); // fail

    // If I don't reuse the element, like this, it works (or using `queryBy`/`getBy`):
    await expect(screen.findByRole('textbox', { name: /holder/i })).resolves.toBeInTheDocument(); // ok
    screen.getByRole('textbox', { name: /holder/i }); // ok
  });
}

So, the fields holder, expiry and cvv are visible once number is valid, but the tests keep failing. I noticed that if I query it again, using getByRole(...) to check if it’s present in document, it works, but shouldn’t findByRole(...) works in this case?

Issue Analytics

  • State:closed
  • Created 3 years ago
  • Comments:5 (5 by maintainers)

github_iconTop GitHub Comments

1reaction
timdeschryvercommented, Nov 8, 2020

I tried this with RTL and the behavior is the same. This is also the intended behavior because we keep a reference, but we have to re-query the element as you’ve also have noticed.

To not repeat the query you can wrap it inside a factory method:

const holderHtmlElementRef = () => screen.findByRole('textbox', { name: /holder/i });

await expect(holderHtmlElementRef()).rejects.toThrow(/Unable to find/i); 
await userEvent.type(numberHtmlElementRef, '5353107424870314');
await expect(holderHtmlElementRef()).resolves.toBeInTheDocument();

RTL reproduction

0reactions
rafaelss95commented, Nov 8, 2020

I tried this with RTL and the behavior is the same. This is also the intended behavior because we keep a reference, but we have to re-query the element as you’ve also have noticed.

To not repeat the query you can wrap it inside a factory method:

const holderHtmlElementRef = () => screen.findByRole('textbox', { name: /holder/i });

await expect(holderHtmlElementRef()).rejects.toThrow(/Unable to find/i); 
await userEvent.type(numberHtmlElementRef, '5353107424870314');
await expect(holderHtmlElementRef()).resolves.toBeInTheDocument();

RTL reproduction

Thanks for the explanation 😃

Read more comments on GitHub >

github_iconTop Results From Across the Web

byRole is not returning the DOM element - Stack Overflow
I have tried all the following approach to wait for element to appear in DOM: waitFor; findByRole instead of getByRole. in both the...
Read more >
ByRole | Testing Library
As a result, querying a superclass role like checkbox will not include elements with a subclass role like switch .
Read more >
findByRole function - rtl.dom.queries library - Dart API
Returns a future with a single element value with the given role value, defaulting to an exact match after waiting 1000ms (or the...
Read more >
How To Test a React App with Jest and React Testing Library
After running npm test for the first time, you will see this output in the terminal: Output. No tests found related to files...
Read more >
Making sure you're using the correct query - Tim Deschryver
Instead of selecting DOM elements via their id attributes or classes, ... There are some cases that this query will not be able...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found