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

@jaypie/testkit

v1.0.29

Published

Test utilities built for Jaypie

Downloads

924

Readme

Jaypie Testkit 🐦‍⬛🫒

Test utilities built for Jaypie

📋 Usage

Installation

npm install --save-dev @jaypie/testkit

Example

Mocking Jaypie

The testkit provides a complete mock for Jaypie including:

  • Log spying (expect(log.warn).toHaveBeenCalled())
  • Default responses for runtime-only functions (connect, sendMessage, submitMetric)
  • No automatic error handling for handlers (which is good in production but obfuscates tests)
  • Most non-utility functions are mocked to allow simple testing
vi.mock("jaypie", async () => vi.importActual("@jaypie/testkit/mock"));

Error Spying

import { ConfigurationError } from "@jaypie/core";

vi.mock("jaypie", async () => vi.importActual("@jaypie/testkit/mock"));

test("ConfigurationError", () => {
  try {
    throw new ConfigurationError("Sorpresa!");
  } catch (error) {
    expect(error).toBeJaypieError();
    expect(ConfigurationError).toHaveBeenCalled();
  }
});

Log Spying

import { log } from "jaypie";

vi.mock("jaypie", async () => vi.importActual("@jaypie/testkit/mock"));

afterEach(() => {
  vi.clearAllMocks();
});

test("log", () => {
  expect(vi.isMockFunction(log.warn)).toBe(true);
  expect(log.warn).not.toHaveBeenCalled();
  log.warn("Danger");
  expect(log.warn).toHaveBeenCalled();
  expect(log.error).not.toHaveBeenCalled();
});

👺 Logging Conventions:

  • Only use log.trace or log.var during "happy path"
  • Use log.debug for edge cases
  • Now you can add an "observability" test that will fail as soon as new code triggers an unexpected edge condition
describe("Observability", () => {
  it("Does not log above trace", async () => {
    // Arrange
    // TODO: "happy path" setup
    // Act
    await myNewFunction(); // TODO: add any "happy path" parameters
    // Assert
    expect(log).not.toBeCalledAboveTrace();
    // or individually:
    expect(log.debug).not.toHaveBeenCalled();
    expect(log.info).not.toHaveBeenCalled();
    expect(log.warn).not.toHaveBeenCalled();
    expect(log.error).not.toHaveBeenCalled();
    expect(log.fatal).not.toHaveBeenCalled();
  });
});

👺 Follow the "arrange, act, assert" pattern

Test Matchers

testSetup.js

import { matchers as jaypieMatchers } from "@jaypie/testkit";
import * as extendedMatchers from "jest-extended";
import { expect } from "vitest";

expect.extend(extendedMatchers);
expect.extend(jaypieMatchers);

test.spec.js

import { ConfigurationError } from "@jaypie/core";

const error = new ConfigurationError();
const json = error.json();
expect(error).toBeJaypieError();
expect(json).toBeJaypieError();

📖 Reference

import { 
  LOG,
  jsonApiErrorSchema,
  jsonApiSchema,
  matchers,
  mockLogFactory,
  restoreLog,
  spyLog,
} from '@jaypie/testkit'

LOG

LOG constant provided by @jaypie/core for convenience

import { log } from "@jaypie/core";
import { LOG } from "@jaypie/testkit";

const libLogger = log.lib({ level: LOG.LEVEL.WARN, lib: "myLib" });

jsonApiErrorSchema

A JSON Schema validator for the JSON:API error schema. Powers the toBeJaypieError matcher (via toMatchSchema).

jsonApiSchema

A JSON Schema validator for the JSON:API data schema.

matchers

export default {
  toBeCalledAboveTrace,
  toBeCalledWithInitialParams,
  toBeClass,
  toBeJaypieError,
  toBeValidSchema: jsonSchemaMatchers.toBeValidSchema,
  toMatchBase64,
  toMatchJwt,
  toMatchMongoId,
  toMatchSchema: jsonSchemaMatchers.toMatchSchema,
  toMatchSignedCookie,
  toMatchUuid4,
  toMatchUuid5,
  toMatchUuid,
  toThrowBadGatewayError,
  toThrowBadRequestError,
  toThrowConfigurationError,
  toThrowForbiddenError,
  toThrowGatewayTimeoutError,
  toThrowInternalError,
  toThrowJaypieError,
  toThrowNotFoundError,
  toThrowUnauthorizedError,
  toThrowUnavailableError,
};

testSetup.js

import { matchers as jaypieMatchers } from "@jaypie/testkit";
import * as extendedMatchers from "jest-extended";
import { expect } from "vitest";

expect.extend(extendedMatchers);
expect.extend(jaypieMatchers);

expect(subject).toBeCalledAboveTrace()

import { log } from "@jaypie/core";

log.trace("Hello, World!");
expect(log).not.toBeCalledAboveTrace();

log.warn("Look out, World!");
expect(log).toBeCalledAboveTrace();

expect(subject).toBeJaypieError()

Validates instance objects:

try {
  throw new Error("Sorpresa!");
} catch (error) {
  expect(error).not.toBeJaypieError();
}

Validates plain old JSON:

expect({ errors: [ { status, title, detail } ] }).toBeJaypieError();

Jaypie errors, which are ProjectErrors, all have a .json() to convert

expect(subject).toBeValidSchema()

import { jsonApiErrorSchema, jsonApiSchema } from "@jaypie/testkit";

expect(jsonApiErrorSchema).toBeValidSchema();
expect(jsonApiSchema).toBeValidSchema();
expect({ project: "mayhem" }).not.toBeValidSchema();

From jest-json-schema toBeValidSchema.js (not documented in README)

expect(subject).toMatchSchema(schema)

import { jsonApiErrorSchema, jsonApiSchema } from "@jaypie/testkit";
import { ConfigurationError } from "@jaypie/core";

const error = new ConfigurationError();
const json = error.json();
expect(json).toMatchSchema(jsonApiErrorSchema);
expect(json).not.toMatchSchema(jsonApiSchema);

From jest-json-schema; see README

expect(subject).toMatch*() Regular Expression Matchers

Note: these regular expressions matchers so not verify the value is value, only that it matches the pattern (it "looks like" something). For example, expect("123e4567-e89b-12d3-a456-426614174000").toMatchUuid() will pass because the string matches a UUID pattern, even though it is not a valid UUID.

  • toMatchBase64
  • toMatchJwt
  • toMatchMongoId
  • toMatchSignedCookie
  • toMatchUuid4
  • toMatchUuid5
  • toMatchUuid

expect(subject).toThrowJaypieError()

import { ConfigurationError } from "@jaypie/core";

const error = new ConfigurationError();
expect(() => {
  throw error;
}).toThrowJaypieError();

Do not forget to await expect when passing async functions:

import { ConfigurationError } from "@jaypie/core";

const error = new ConfigurationError();
await expect(async () => {
  throw error;
}).toThrowJaypieError();

// Breaks and causes a false-positive because `expect` did not `await`
// expect(async () => {}).toThrowJaypieError();
// > Error: Expected function to throw a JaypieError, but it did not throw.

mockLogFactory()

Creates a mock of the log provided by @jaypie/core.

import { mockLogFactory } from "@jaypie/testkit";

const log = mockLogFactory();
log.warn("Danger");
expect(log.warn).toHaveBeenCalled();
expect(log.error).not.toHaveBeenCalled();

restoreLog(log)

Restores the log provided by @jaypie/core, commonly performed afterEach with spyLog in beforeEach. See example with spyLog.

spyLog(log)

Spies on the log provided by @jaypie/core, commonly performed beforeEach with restoreLog in afterEach. Not necessary when mocking the entire Jaypie module.

import { restoreLog, spyLog } from "@jaypie/testkit";
import { log } from "@jaypie/core";

beforeEach(() => {
  spyLog(log);
});
afterEach(() => {
  restoreLog(log);
  vi.clearAllMocks();
});

test("log", () => {
  log.warn("Danger");
  expect(log.warn).toHaveBeenCalled();
  expect(log.error).not.toHaveBeenCalled();
});

sqsTestRecords(message, message, ...) or sqsTestRecords([...])

Generates an event object for testing SQS Lambda functions with as many messages as provided. Note, test will accept more than ten messages, but AWS will only send ten at a time.

import { sqsTestRecords } from "@jaypie/testkit";

const event = sqsTestRecords(
  { MessageId: "1", Body: "Hello, World!" },
  { MessageId: "2", Body: "Goodbye, World!" }
);

🌠 Wishlist

  • matcher toBeHttpStatus
  • matcher toBeJaypieAny
  • matcher toBeJaypieData
  • matcher toBeJaypieDataObject
  • matcher toBeJaypieDataArray
  • ...@knowdev/jest

📝 Changelog

| Date | Version | Summary | | ---------- | ------- | -------------- | | 9/15/2024 | 1.0.29 | All errors exported as mocks | | 9/14/2024 | 1.0.28 | Matchers toThrowBadGatewayError, toThrowGatewayTimeoutError, toThrowUnavailableError | | 9/13/2024 | 1.0.27 | Matcher toBeCalledAboveTrace | | 7/16/2024 | 1.0.21 | Export Jaypie mock as default | | 3/20/2024 | 1.0.2 | Export LOG | | 3/16/2024 | 1.0.0 | Artists ship | | 3/15/2024 | 0.1.0 | Initial deploy | | 3/15/2024 | 0.0.1 | Initial commit |

📜 License

Published by Finlayson Studio. All rights reserved