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 🙏

© 2026 – Pkg Stats / Ryan Hefner

@neowhale/test-utils

v0.1.3

Published

Test utilities and mock factories for WhaleTools storefronts — Playwright route interceptors, mock data, and state helpers

Downloads

361

Readme

Includes Playwright route interceptors, typed mock data factories, and state-seeding helpers for end-to-end testing.

Table of Contents


Installation

npm install --save-dev @neowhale/test-utils

Peer Dependencies

| Package | Version | Required | |---------|---------|----------| | @neowhale/storefront | >=0.1.0 | Yes | | @playwright/test | >=1.40.0 | No (only for /playwright entry) |

If you only use the core factories (no Playwright), @playwright/test is not required.


Quick Start

Using core factories (no Playwright dependency)

import {
  createMockProduct,
  createMockCart,
  createMockCartItem,
  createMockCustomer,
} from '@neowhale/test-utils'

const product = createMockProduct({ name: 'Purple Haze' })
const item = createMockCartItem({ product_id: product.id, unit_price: 35 })
const cart = createMockCart({ items: [item] })
const customer = createMockCustomer({ first_name: 'John' })

console.log(cart.total) // auto-computed: subtotal + 7.5% tax

Using Playwright route interceptors

import { test, expect } from '@playwright/test'
import { mockAllRoutes } from '@neowhale/test-utils/playwright'

const config = {
  storeId: 'your-store-uuid-here',
}

test('homepage loads products', async ({ page }) => {
  const { cart } = await mockAllRoutes(page, config)

  await page.goto('/')
  await expect(page.getByText('Test Product')).toBeVisible()
})

Entry Points

| Import Path | Description | Requires Playwright | |-------------|-------------|---------------------| | @neowhale/test-utils | Core factories, types, counter resets | No | | @neowhale/test-utils/playwright | Route interceptors, helpers, utilities | Yes |


API Reference: Core

Import from @neowhale/test-utils.

WhaleTestConfig

Configuration object used by all Playwright route interceptors and helpers.

interface WhaleTestConfig {
  /** Store UUID (required) */
  storeId: string
  /** localStorage key prefix. Defaults to "whale" */
  storagePrefix?: string
  /** Client-side proxy path. Defaults to "/api/gw" */
  proxyPath?: string
}

Factory Functions

All factory functions accept an optional overrides parameter (Partial<T>) that is spread over the defaults. Internal auto-incrementing counters ensure each call produces unique IDs.

createMockProduct(overrides?): Product

Creates a product with two default pricing tiers (1g at $10, 3.5g at $35), status "active", type "simple", and stock of 100.

const product = createMockProduct()
// { id: 'prod-001', name: 'Test Product 1', slug: 'test-product-1', ... }

const custom = createMockProduct({
  name: 'Blue Dream',
  stock_quantity: 50,
})

createMockPricingTier(overrides?): PricingTier

Creates a single pricing tier. Called internally by createMockProduct but also available directly.

const tier = createMockPricingTier({ label: '7g', quantity: 7, default_price: 60 })

createMockCart(overrides?): Cart

Creates a cart. When items are provided, subtotal, tax_amount (7.5%), and total are automatically computed from the item line totals unless explicitly overridden.

const emptyCart = createMockCart()
// { id: 'cart-001', items: [], subtotal: 0, tax_amount: 0, total: 0, ... }

const item = createMockCartItem({ unit_price: 35, quantity: 2 })
const cart = createMockCart({ items: [item] })
// subtotal: 70, tax_amount: 5.25, total: 75.25

createMockCartItem(overrides?): CartItem

Creates a cart item. line_total is auto-computed as unit_price * quantity unless explicitly overridden.

const item = createMockCartItem({ unit_price: 10, quantity: 3 })
// { id: 'ci-001', line_total: 30, ... }

createMockCustomer(overrides?): Customer

Creates a customer with defaults of Jane Doe, Standard loyalty tier, and zero lifetime spend.

const customer = createMockCustomer()
// { id: 'cust-001', first_name: 'Jane', last_name: 'Doe', email: '[email protected]', ... }

const vip = createMockCustomer({ loyalty_tier: 'VIP', total_spent: 5000 })

createMockOrder(overrides?): Order

Creates an order. Like createMockCart, totals (subtotal, tax_amount, total_amount) are auto-computed from items unless overridden. Default status is "completed".

const orderItem = createMockOrderItem({ unit_price: 50 })
const order = createMockOrder({ items: [orderItem] })
// { id: 'order-001', order_number: 'ORD-10001', total_amount: 53.75, ... }

createMockOrderItem(overrides?): OrderItem

Creates an order item. line_total is auto-computed as unit_price * quantity.

const item = createMockOrderItem({ product_name: 'OG Kush', unit_price: 40, quantity: 2 })
// { id: 'oi-001', line_total: 80, ... }

createMockAnalytics(overrides?): CustomerAnalytics

Creates customer analytics data with defaults: rfm_segment: "loyal_customers", ltv_tier: "standard", churn_risk: "low".

const analytics = createMockAnalytics({ lifetime_revenue: 2500, total_orders: 12 })

createMockSendCodeResponse(overrides?): SendCodeResponse

Creates a send-code response. Default: { sent: true }.

const response = createMockSendCodeResponse()

createMockVerifyResponse(overrides?, customer?): VerifyCodeResponse

Creates a verify-code response containing a JWT token hash and customer data. The optional second argument provides a customer to include in the response.

const customer = createMockCustomer({ first_name: 'Alex' })
const response = createMockVerifyResponse({}, customer)
// { object: 'auth_token', token_hash: 'test-jwt-token-abc123', needs_profile: false, customer: ... }

createMockSession(storeId, overrides?): StorefrontSession

Creates a storefront session. Unlike other factories, storeId is a required first argument.

const session = createMockSession('your-store-uuid')
// { id: 'test-session', store_id: 'your-store-uuid', started_at: '...', last_active_at: '...' }

Counter Reset Functions

Factories use auto-incrementing counters for unique IDs. Call these in beforeEach or test.beforeEach to reset counters between tests and get deterministic IDs.

| Function | Resets | |----------|--------| | resetProductCounters() | Product and pricing tier counters | | resetCartCounters() | Cart item counter | | resetCustomerCounters() | Customer counter | | resetOrderCounters() | Order and order item counters |

import {
  resetProductCounters,
  resetCartCounters,
  resetCustomerCounters,
  resetOrderCounters,
} from '@neowhale/test-utils'

test.beforeEach(() => {
  resetProductCounters()
  resetCartCounters()
  resetCustomerCounters()
  resetOrderCounters()
})

API Reference: Playwright

Import from @neowhale/test-utils/playwright. All functions in this section require @playwright/test as a peer dependency.

Route Interceptors

Route interceptors register page.route() handlers that intercept gateway API calls and return mock responses. All accept (page: Page, config: WhaleTestConfig, opts?) as arguments.

mockAllRoutes(page, config, opts?): Promise<CartControl>

Composes all route mocks at once. Returns a CartControl object for manipulating cart state during the test. By default also blocks images and mocks Accept.js.

interface MockAllOpts {
  cart?: MockCartOpts
  auth?: MockAuthOpts
  customer?: MockCustomerOpts
  products?: MockProductOpts
  /** Skip blocking images. Defaults to false. */
  skipImageBlock?: boolean
  /** Skip Accept.js mock. Defaults to false. */
  skipAcceptJs?: boolean
}
const { cart, setCart } = await mockAllRoutes(page, config, {
  skipImageBlock: true,
  auth: { customer: createMockCustomer({ first_name: 'Alex' }) },
})

mockAnalyticsRoutes(page, config): Promise<void>

Mocks storefront session and event endpoints. All routes return 200 responses.

Routes intercepted:

  • POST /storefront/sessions -- returns a mock session
  • */storefront/sessions/* -- returns { ok: true }
  • POST /storefront/events -- returns { ok: true }
  • */storefront/events/* -- returns { ok: true }

mockCartRoutes(page, config, opts?): Promise<CartControl>

Mocks all cart CRUD routes. Returns a CartControl object.

interface MockCartOpts {
  /** Initial cart state. Defaults to an empty cart. */
  startWith?: Cart
}

interface CartControl {
  /** Current cart state. Mutate directly or use setCart(). */
  cart: Cart
  /** Replace the entire cart state. */
  setCart(cart: Cart): void
}

Routes intercepted:

  • POST /cart and GET /cart -- returns current cart
  • GET /cart/:id -- returns current cart
  • POST /cart/:id/items -- returns current cart
  • PATCH|PUT /cart/:id/items/:itemId -- returns current cart
  • DELETE /cart/:id/items/:itemId -- resets cart to empty, returns it
const item = createMockCartItem({ unit_price: 25 })
const preloadedCart = createMockCart({ items: [item] })

const { cart, setCart } = await mockCartRoutes(page, config, {
  startWith: preloadedCart,
})

// Later in the test, update the cart
setCart(createMockCart({ items: [] }))

mockAuthRoutes(page, config, opts?): Promise<void>

Mocks passwordless auth endpoints (send-code and verify-code).

interface MockAuthOpts {
  /** Customer returned on verify. Defaults to createMockCustomer(). */
  customer?: Customer
}

Routes intercepted:

  • POST /storefront/auth/send-code -- returns { sent: true }
  • POST /storefront/auth/verify-code -- returns token + customer

mockCustomerRoutes(page, config, opts?): Promise<void>

Mocks customer CRUD, analytics, and order listing endpoints.

interface MockCustomerOpts {
  /** Customer data. Defaults to createMockCustomer(). */
  customer?: Customer
  /** Analytics data. Defaults to createMockAnalytics(). */
  analytics?: CustomerAnalytics
  /** Orders to return. Defaults to empty array. */
  orders?: Order[]
}

Routes intercepted:

  • GET /customers/:id -- returns customer
  • POST /customers -- returns customer
  • GET /customers/:id/analytics -- returns analytics
  • GET /orders -- returns order list

mockCheckoutRoute(page, config, order?): Promise<void>

Mocks the checkout endpoint. Returns the given order or a default mock order.

await mockCheckoutRoute(page, config, createMockOrder({ status: 'pending' }))

mockProductRoutes(page, config, opts?): Promise<void>

Mocks product list and detail endpoints. Defaults to returning two generic products.

interface MockProductOpts {
  /** Products for list endpoint. Defaults to two generic products. */
  products?: Product[]
  /** Single product for detail endpoint. Defaults to first product in list. */
  singleProduct?: Product
}

Routes intercepted:

  • GET /products -- returns product list with total count
  • GET /products/:id -- returns single product

mockAcceptJs(page): Promise<void>

Blocks the real Authorize.net Accept.js script (both production and sandbox domains) and injects a mock window.Accept object that synchronously calls the handler with a successful opaque token response.

await mockAcceptJs(page)
// window.Accept.dispatchData(secureData, handler) is now available

Helpers

seedAuthState(page, config, customer?): Promise<void>

Pre-sets authentication state in localStorage using the Zustand persist format. Writes to {storagePrefix}-auth. The seeded session token is set to expire 30 days from the current time.

await seedAuthState(page, config, createMockCustomer({ first_name: 'Alex' }))
await page.goto('/')
// User is now "logged in" from the first navigation

seedCartState(page, config, cartId?): Promise<void>

Pre-sets a cart ID in localStorage using the Zustand persist format. Writes to {storagePrefix}-cart. Defaults to "cart-001".

await seedCartState(page, config, 'my-cart-id')

blockImages(page): Promise<void>

Blocks image requests to Supabase storage and the whale-gateway media proxy. Returns a 1x1 transparent PNG for each request. Useful for speeding up tests that do not need real images.

await blockImages(page)

waitForPageReady(page): Promise<void>

Waits for the page to reach networkidle load state.

await page.goto('/')
await waitForPageReady(page)

Utilities

resolveGatewayGlob(config): string

Builds the glob pattern used to match gateway API routes based on proxyPath and storeId.

const glob = resolveGatewayGlob({ storeId: 'abc-123' })
// '**/api/gw/v1/stores/abc-123'

json(data, status?): object

Shorthand for Playwright route.fulfill() options. Returns an object with status, contentType: "application/json", and a stringified body. Status defaults to 200.

await route.fulfill(json({ ok: true }))
await route.fulfill(json({ error: 'Not found' }, 404))

TRANSPARENT_PIXEL: Buffer

A 1x1 transparent PNG encoded as a Node.js Buffer. Used internally by blockImages and available for custom route handlers.

await route.fulfill({
  status: 200,
  contentType: 'image/png',
  body: TRANSPARENT_PIXEL,
})

Full Test Example

A complete Playwright test demonstrating both entry points together:

// tests/checkout-flow.spec.ts
import { test, expect } from '@playwright/test'
import {
  createMockProduct,
  createMockCart,
  createMockCartItem,
  createMockCustomer,
  createMockOrder,
  createMockOrderItem,
  resetProductCounters,
  resetCartCounters,
  resetCustomerCounters,
  resetOrderCounters,
} from '@neowhale/test-utils'
import {
  mockAllRoutes,
  mockCheckoutRoute,
  seedAuthState,
  waitForPageReady,
} from '@neowhale/test-utils/playwright'
import type { WhaleTestConfig } from '@neowhale/test-utils'

const config: WhaleTestConfig = {
  storeId: 'cd2e1122-d511-4edb-be5d-98ef274b4baf',
}

test.beforeEach(() => {
  resetProductCounters()
  resetCartCounters()
  resetCustomerCounters()
  resetOrderCounters()
})

test('authenticated user can complete checkout', async ({ page }) => {
  // 1. Create test data
  const product = createMockProduct({ name: 'Purple Haze', stock_quantity: 50 })
  const customer = createMockCustomer({ first_name: 'Alex', last_name: 'Test' })
  const cartItem = createMockCartItem({
    product_id: product.id,
    product_name: product.name,
    unit_price: 35,
    quantity: 1,
  })
  const cart = createMockCart({ items: [cartItem] })
  const order = createMockOrder({
    items: [createMockOrderItem({ product_name: product.name, unit_price: 35 })],
  })

  // 2. Set up route mocks
  const { setCart } = await mockAllRoutes(page, config, {
    cart: { startWith: cart },
    auth: { customer },
    products: { products: [product], singleProduct: product },
  })
  await mockCheckoutRoute(page, config, order)

  // 3. Seed auth state so user is already logged in
  await seedAuthState(page, config, customer)

  // 4. Navigate and interact
  await page.goto('/checkout')
  await waitForPageReady(page)

  await expect(page.getByText('Purple Haze')).toBeVisible()
  await expect(page.getByText('$35.00')).toBeVisible()

  // 5. Complete checkout
  await page.getByRole('button', { name: /place order/i }).click()

  // 6. Verify confirmation
  await expect(page.getByText(order.order_number)).toBeVisible()
})

Peer Dependencies

{
  "peerDependencies": {
    "@neowhale/storefront": ">=0.1.0",
    "@playwright/test": ">=1.40.0"
  },
  "peerDependenciesMeta": {
    "@playwright/test": {
      "optional": true
    }
  }
}

If you only use the core entry point (@neowhale/test-utils), you do not need @playwright/test installed. The Playwright dependency is only required when importing from @neowhale/test-utils/playwright.