npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

render-beforeall

v1.0.3

Published

react-testing-library render beforeAll jest test utility

Downloads

5

Readme

Installation

  • NPM: npm i -D render-beforeall YARN: yarn add render-beforeall -D
  • Import this library into your test file ⚠️ Be sure to import this library Before importing @testing-library/react, this is a limitation of react testing library as described here

Jest

  • Load it before all test using jest's setupFilesAfterEnv configuration. jest.config.json

    {
      "setupFilesAfterEnv": ["render-beforeall"]
    }
  • If you are using CRA you could import this in setupTests.js check this explanation

Mocha

By default this behaviour is not enabled in mocha, when using this library it will as this library takes control of when to cleanup and by default is afterEach test. But if your suite happened to behave like this, is probably because someone configured it already following this guide

so you will need to change:

mocha -r ./mocha-watch-cleanup-after-each.js

by

mocha -r render-beforeall

and that's it.

How To Use it

import { renderBeforeAll } from "render-beforeall";
import { render, screen } from "@testing-library/react";
// import ...

describe("UserProfile", () => {
  renderBeforeAll(() => <UserProfile />); // <-- just this line

  it(`should let the user know about new offers`, () => {
    expect(screen.getByText("New offers!")).toBeInTheDocument();
  });

  it(`should present user's address`, () => {
    expect(screen.getByText("1772 Test Street")).toBeInTheDocument();
  });

  // ...
});

Advanced use

Alternatively, you could control manually when the testing-library's cleanup function is called with the following example:

import { settings } from "render-beforeall";

settings.disableAutoCleanup();

// ... something

if (settings.isAutoCleanup()) {
  // do something when autoCleanup is on
}

settings.enableAutoCleanUp();

Best Practices

One State per describe

If your component has several states, avoid changing them as part of a test. By not doing this you might violate F.I.R.S.T.

BAD example


describe(`UserProfile`, () => {
  renderBeforeAll(() => <UserProfile />);

  test(`shows purchased products when unfolding purchases section`, ...);

  test(`shows a product`, ...);

  // more purchases section tests ...

  test(`shows invoices when unfolding invoces section`, ...);

  test(`shows an invoice`, ...);

  // more invoices section tests ...
});

Good Example


describe(`UserProfile`, () => {
  describe(`Purchases`, () => {
    renderBeforeAll(() => <UserProfile />);
    beforeAll(() => userEvent.click(screen.getByText('Purchases')))

    test(`purchases section is shown`, ...);

    test(`shows a product`, ...);

    // more purchases section tests ...
  });

  describe(`Invoices`, () => {
    renderBeforeAll(() => <UserProfile />);
    beforeAll(() => userEvent.click(screen.getByText('Invoices')))

    test(`invoices section is shown`, ...);

    test(`shows an invoice`, ...);

    // more invoices section tests ...
  });
});

Description, notes, motivations and alternatives

react-testing-library render beforeAll test utility

This tool was created to address the lack of subtests in jest and the enforced (and very useful) rule of react-testing-library to clean rendered components in afterEach issue. Allowing to render once and run several tests against the same rendered state, following one test - one assertion principle.

NOTE This tool was not intended to improve performance and there are several caveats you need to take into account when using this tool.

You might violate F.I.R.S.T.

⚠️ WARNING ⚠️ You might violate F.I.R.S.T., the I (Isolated/Independent part) as you render once, a test could change the state (i.e. interacting with the rendered component) and so the test running after that state change might need to know of that precondition.

An alternative to using this library is Write fewer longer tests. Kent Dodds addresses the Isolation problem, and their's argument is that jest is good at pointing out what has failed in the test, which is true. Is my opinion that, jest's error messages are not good enough, I rather see the functional explanation of what got broken, rather than a couldn't find an element with text ... or couldn't find a button that should be expected (maybe because some business rule got broken and might not be that obvious).

Also when following TDD is way easier to follow one test - one assertion is part of the discipline. It's easier to stop work at any moment when you have the test functional description described than the GOD test with several scenarios without any description. Also, way easier to read in a code review, and particularly easier to extract the test report and share it with team members to reduce duplication tests.

I strongly believe that Kent Dodds approach could be ok in small and/or fast projects (MVP) that are not intended to be refactored often or that might die quickly. But in a continuous delivery project, where refactors and new feature additions are high, better to go with one test - one assertion principle for the reasons explained in that link.

Conclusion

It will be a way better solution to have jest subtests similar to python's unittest.subTest work

Rewriting Kent Dodds example:

test("course loads and renders the course information", async () => {
  const courseId = "123";
  const title = "My Awesome Course";
  const subtitle = "Learn super cool things";
  const topics = ["topic 1", "topic 2"];

  getCourseInfo.mockResolvedValueOnce(buildCourse({ title, subtitle, topics }));

  render(<Course courseId={courseId} />);

  subtest("should call the getCourseInfo function properly", () => {
    // danyg> please avoid contract tests you already testing this got properly called by asserting the side-effects of render
    expect(getCourseInfo).toHaveBeenCalledWith(courseId);
    expect(getCourseInfo).toHaveBeenCalledTimes(1);
  });

  subtest("should show a loading spinner", () => {
    const alert = screen.getByRole("alert");
    expect(alert).toHaveTextContent(/loading/i);
  });

  await subtest("should render the title", async () => {
    const titleEl = await screen.findByRole("heading");
    expect(titleEl).toHaveTextContent(title);
  });

  subtest("should render the subtitle", () => {
    expect(screen.getByText(subtitle)).toBeInTheDocument();
  });

  subtest("should render the list of topics", () => {
    const topicElsText = screen
      .getAllByRole("listitem")
      .map((el) => el.textContent);
    expect(topicElsText).toEqual(topics);
  });
});

This way FIRST wouldn't be broken and it will be easier to see what got broken and why.

Examples

Form

describe("RegisterForm", () => {
  describe("Shows the necessary fields", () => {
    renderBeforeAll(() => <RegisterForm />);

    it("shows a username input");

    it("shows a First Name input");

    it("shows a Last Name input");

    it("shows an Email input");

    it("shows an Repeat Email input");

    it("shows a Password input");

    it("shows a Repeat Password input");
  });

  describe("First Name Validations", () => {
    // these kind of test would change the state, do not use renderBeforeAll in this case FIRST could be violated
    it(`Shows an error First Name is empty`, () => {
      render(<RegisterForm />);
      // ...
    });

    it(`Shows an error First Name contain invalid characters`, () => {
      render(<RegisterForm />);
      // ...
    });

    // ...
  });

  // tho fields interacting, no need to use renderBeforeAll
  it(`Shows an error when Email and Repeat Email don't match`, () => {
    render(<RegisterForm />);
    // ...
  });

  it(`Shows an error when Password and Repeat Password don't match`, () => {
    render(<RegisterForm />);
    // ...
  });

  it(`Shows an error when First and Last Name conform an banned word or phrase`, () => {
    render(<RegisterForm />);
    // ...
  });
});

Several States


describe(`UserProfile`, () => {
  describe(`Purchases`, () => {
    renderBeforeAll(() => <UserProfile />);
    beforeAll(() => userEvent.click(screen.getByText('Purchases')))

    test(`purchases section is shown`, ...);

    test(`shows a product`, ...);

    // more purchases section tests ...
  });

  describe(`Invoices`, () => {
    renderBeforeAll(() => <UserProfile />);
    beforeAll(() => userEvent.click(screen.getByText('Invoices')))

    test(`invoices section is shown`, ...);

    test(`shows an invoice`, ...);

    // more invoices section tests ...
  });
});