0.1.7 • Published 3 years ago

jest-globals v0.1.7

Weekly downloads
-
License
MIT
Repository
github
Last release
3 years ago

🎭 jest-globals

Code Style: Prettier TypeScript: Strict NPM version Join the chat at https://gitter.im/Codecademy/jest-globals

Mocks out global variables with Jest spies.

Usage

jest-globals sets up a collection of global mocks such as location and navigator and assigns them to global variables on globalThis. If a typeof window !== undefined then it will assign the same values to window as well.

Your source code can then still refer to global variables normally...

// Source file: ./example.ts
export function example() {
  window.location.assign("/path");
}

...and your test files can import and use their test versions, as long as jest-globals is imported before the code under test:

// Test file: ./example.test.ts
import { location } from "jest-globals";
import { example } from "./example";

describe("example", () => {
  it("assigns /path to location", () => {
    example();
    expect(location.assign).toHaveBeenCalledWith("/path");
  });
});

See #Custom Usage below for more details.

Available Globals

For each global available, you can import it from jest-globals directly.

addEventListener

import { addEventListener } from "jest-globals";

expect(addEventListener).toHaveBeenCalledWith("load", expect.any(Function));

alert

import { alert } from "jest-globals";

expect(alert).toHaveBeenCalledWith(expect.any(Function));

blur

import { blur } from "jest-globals";

expect(blur).toHaveBeenCalledTimes(1);

close

import { close } from "jest-globals";

expect(close).toHaveBeenCalledTimes(1);

confirm

import { confirm } from "jest-globals";

expect(confirm).toHaveBeenCalledTimes(1);

dispatchEvent

import { dispatchEvent } from "jest-globals";

expect(dispatchEvent).toHaveBeenCalledTimes(1);

focus

import { focus } from "jest-globals";

expect(focus).toHaveBeenCalledTimes(1);

getSelection

Use createMockSelection to create a valid Selection with stub data for any field not explicitly provided.

import { createMockSelection, getSelection } from "jest-globals";

getSelection.mockReturnValue(
  createMockSelection({
    focusOffset: 2,
  })
);

localStorage

localStorage is assigned an instance of a MockStorage class of assigned items under an items Map.

import { localStorage } from "jest-globals";

localStorage.setItem("powerLevel", "9001");

expect(localStorage.setItem).toHaveBeenCalledWith("powerLevel", "9001");
expect(localStorage.items.getItem("powerLevel")).toEqual("9001");

MockStorage's constructor includes a beforeEach(() => this.items.clear()).

location

import { location } from "jest-globals";

expect(location.assign).toHaveBeenCalledWith("/path");

matchMedia

import { createMockMediaQueryList, matchMedia } from "jest-globals";

matchMedia.mockReturnValue(
  createMockMediaQueryList({
    //
  })
);

moveBy

import { moveBy } from "jest-globals";

expect(moveBy).toHaveBeenCalledWith(12, 34);

navigator

import { navigator } from "jest-globals";

navigator.mockUserAgent.mockReturnValue("Mozilla/123");
navigator.clipboard.readText.mockResolvedValue("It's over 9,000!");
expect(navigator.serviceWorker.register).toHaveBeenCalled();

open

import { open } from "jest-globals";

expect(open).toHaveBeenCalledWith("https://hi.joshuakgoldberg.com");

performance

import { performance } from "jest-globals";

performance.now.mockReturnValue(9001);

postMessage

import { postMessage } from "jest-globals";

expect(postMessage).toHaveBeenCalledWith("Is anybody out there?", "*");

print

import { print } from "jest-globals";

expect(print).toHaveBeenCalledTimes(1);

prompt

import { prompt } from "jest-globals";

expect(prompt).toHaveBeenCalledWith("Hello my baby");

removeEventListener

import { removeEventListener } from "jest-globals";

expect(removeEventListener).toHaveBeenCalledWith(expect.any(Function));

requestAnimationFrame

import { requestAnimationFrame } from "jest-globals";

expect(requestAnimationFrame).toHaveBeenCalledWith(expect.any(Function));

requestIdleCallback

import { requestIdleCallback } from "jest-globals";

expect(requestIdleCallback).toHaveBeenCalledWith(expect.any(Function));

resizeBy

import { resizeBy } from "jest-globals";

expect(resizeBy).toHaveBeenCalledWith(12, 34);

resizeTo

import { resizeTo } from "jest-globals";

expect(resizeTo).toHaveBeenCalledWith(123, 456);

scrollBy

import { scrollBy } from "jest-globals";

expect(scrollBy).toHaveBeenCalledWith(12, 34);

scrollTo

import { scrollTo } from "jest-globals";

expect(scrollTo).toHaveBeenCalledWith(123, 456);

sessionStorage

sessionStorage is assigned an instance of a MockStorage class that keeps track of assigned items under an items member of type Map<string, string>:

import { sessionStorage } from "jest-globals";

sessionStorage.setItem("powerLevel", "9001");

expect(sessionStorage.setItem).toHaveBeenCalledWith("powerLevel", "9001");
expect(sessionStorage.items.getItem("powerLevel")).toEqual("9001");

MockStorage's constructor includes a beforeEach(() => this.items.clear()).

setImmediate

import { setImmediate } from "jest-globals";

expect(setImmediate).toHaveBeenCalledWith(expect.any(Function));

stop

import { stop } from "jest-globals";

expect(stop).toHaveBeenCalledTimes(1);

top

window.top is by default a reference to an identical mock window object, except its .top references itself.

import { top } from "jest-globals";

expect(top.postMessage).toHaveBeenCalledWith("I'll be back one day", "*");

Strongly Typed Usage

jest-globals is written in TypeScript and generally type safe. Imported objects from jest-globals are typed as having jest.Mocks for their functions with parameters and return types corresponding to their original mocks.

Global mocks that return complex original objects therefore require mockReturnValue and co. to be provided with objects that match up to the original type:

import { getSelection } from "jest-globals";

getSelection.mockReturnValue({
  focusOffset: 2,
});
// Error: Argument of type '{ focusOffset: number; }' is not assignable to parameter of type 'Selection'.
//   Type '{ focusOffset: number; }' is missing the following properties from type 'Selection': anchorNode, anchorOffset, focusNode, focusOffset, and 16 more.

Each of these APIs has a corresponding createMock* function exported by jest-globals that takes in a Partial of the returned type and fills in any missing fields:

import { createMockSelection, getSelection } from "jest-globals";

getSelection.mockReturnValue(
  createMockSelection({
    focusOffset: 2,
  })
);

What's Not Provided

The following global members are complex enough that they warrant their own dedicated packages:

Custom Usage

Auto-Sorted Involvement

If you'd like jest-globals to run after library code, it may be inconvenient to also have a linter plugin thatt auto-sorts your imports.

// example.test.js
import "jest-globals"; // runs first 😔
import userEvent from "@testing-library/user-event";

If your configuration puts imports under a prefix such as ~/ last, you can create a file whose sole purpose is to import jest-globals:

// tests/globals
export * from "jest-globals";
// example.test.js
import userEvent from "@testing-library/user-event";
import "~/tests/globals"; // runs last 😌

Another (less type safe) workaround is to use Jest's moduleNameMapper to allow importing under a name like zzzest-globals, pushing it alphabetically below other absolute imports:

{
  "moduleNameMapper": {
    "^zzzest-globals$": "jest-globals"
  }
}
// example.test.js
import userEvent from "@testing-library/user-event";
import "zzzest-globals"; // runs last 😌

Permanent Involvement

If you'd like jest-globals to always be run before all your files, you can include it in your setupFilesAfterEnv:

// ./jest.setup.js
require("jest-globals");

Development

Requires:

After forking the repo from GitHub:

git clone https://github.com/<your-name-here>/jest-globals
cd jest-globals
yarn

Contribution Guidelines

We'd love to have you contribute! Check the issue tracker for issues labeled accepting prs to find bug fixes and feature requests the community can work on. If this is your first time working with this code, the good first issue label indicates good introductory issues.

Please note that this project is released with a Contributor Covenant. By participating in this project you agree to abide by its terms. See CODE_OF_CONDUCT.md.