
Research
/Security News
Malicious npm Packages Target WhatsApp Developers with Remote Kill Switch
Two npm packages masquerading as WhatsApp developer libraries include a kill switch that deletes all files if the phone number isn’t whitelisted.
@zerothrow/jest
Advanced tools
🧠 ZeroThrow Layers
• ZT – primitives (try
,tryAsync
,ok
,err
)
• Result – combinators (map
,andThen
,orElse
)
• ZeroThrow – utilities (collect
,firstSuccess
,pipe
)
• @zerothrow/* – ecosystem packages (resilience, jest, etc)
ZeroThrow Ecosystem · Packages ⇢
Jest matchers for ZeroThrow Result types - elegant error handling assertions for your tests.
npm install @zerothrow/jest @zerothrow/core @zerothrow/expect
# or: pnpm add @zerothrow/jest @zerothrow/core @zerothrow/expect
Note:
@zerothrow/core
and@zerothrow/expect
are peer dependencies.
The matchers are automatically registered when you import the package. Simply import it in your test setup file or at the top of your test files:
import '@zerothrow/jest';
import { ZT } from '@zerothrow/core';
describe('My Service', () => {
it('should handle success', () => {
const result = ZT.ok(42);
expect(result).toBeOk();
expect(result).toBeOkWith(42);
});
it('should handle errors', () => {
const result = ZT.err('VALIDATION_ERROR', 'Invalid input');
expect(result).toBeErr();
expect(result).toHaveErrorCode('VALIDATION_ERROR');
expect(result).toHaveErrorMessage('Invalid input');
});
});
The matchers are automatically registered when the module is imported:
// In your test file or setup file
import '@zerothrow/jest';
If you need more control over when matchers are registered:
import { setup, jestMatchers } from '@zerothrow/jest';
// Option 1: Use the setup function
setup();
// Option 2: Register matchers manually
expect.extend(jestMatchers);
The TypeScript types are automatically included. If you're using a custom tsconfig.json
for tests, ensure the types are included:
{
"compilerOptions": {
"types": ["jest", "@zerothrow/jest"]
}
}
toBeOk()
Asserts that a Result is Ok (successful).
const result = ZT.ok('success');
expect(result).toBeOk(); // Passes
const error = ZT.err('FAILED');
expect(error).toBeOk(); // Fails
toBeOkWith(expected)
Asserts that a Result is Ok with a specific value.
const result = ZT.ok({ id: 1, name: 'test' });
expect(result).toBeOkWith({ id: 1, name: 'test' }); // Passes
expect(result).toBeOkWith({ id: 2, name: 'test' }); // Fails
toBeErr()
Asserts that a Result is Err (failure).
const result = ZT.err('ERROR_CODE');
expect(result).toBeErr(); // Passes
const success = ZT.ok('value');
expect(success).toBeErr(); // Fails
toBeErrWith(error)
Asserts that a Result is Err with specific error properties.
const result = ZT.err('VALIDATION_ERROR', 'Email is invalid');
// Match by error code and message
expect(result).toBeErrWith({
code: 'VALIDATION_ERROR',
message: 'Email is invalid'
});
// Match with Error instance
const error = new Error('Something went wrong');
const result2 = ZT.err(error);
expect(result2).toBeErrWith(error);
toHaveErrorCode(code)
Asserts that a Result has a specific error code.
const result = ZT.err('NOT_FOUND', 'User not found');
expect(result).toHaveErrorCode('NOT_FOUND'); // Passes
expect(result).toHaveErrorCode('SERVER_ERROR'); // Fails
toHaveErrorMessage(message)
Asserts that a Result has a specific error message. Supports both string and RegExp matching.
const result = ZT.err('ERROR', 'Connection timeout after 30 seconds');
// Exact string match
expect(result).toHaveErrorMessage('Connection timeout after 30 seconds');
// RegExp match
expect(result).toHaveErrorMessage(/timeout after \d+ seconds/);
import '@zerothrow/jest';
import { ZT } from '@zerothrow/core';
async function fetchUser(id: string) {
return ZT.tryAsync(async () => {
const response = await fetch(`/api/users/${id}`);
if (!response.ok) {
throw new Error('User not found');
}
return response.json();
});
}
test('fetchUser transforms data correctly', async () => {
const result = await fetchUser('123')
.then(r => r
.map(user => ({ ...user, fetched: true }))
.tap(user => expect(user.fetched).toBe(true))
);
expect(result).toBeOk();
expect(result).toBeOkWith(expect.objectContaining({
id: '123',
fetched: true
}));
});
test('fetchUser provides fallback for errors', async () => {
const result = await fetchUser('invalid')
.then(r => r
.tapErr(err => console.error('Fetch failed:', err))
.orElse(() => ZT.ok({ id: 'guest', name: 'Guest User' }))
);
expect(result).toBeOk();
expect(result).toBeOkWith({ id: 'guest', name: 'Guest User' });
});
import '@zerothrow/jest';
import { ZT } from '@zerothrow/core';
function validateEmail(email: string) {
if (!email) {
return ZT.err('REQUIRED', 'Email is required');
}
if (!email.includes('@')) {
return ZT.err('INVALID_FORMAT', 'Email must contain @');
}
return ZT.ok(email);
}
describe('validateEmail', () => {
test('validates required field', () => {
const result = validateEmail('');
expect(result).toBeErr();
expect(result).toHaveErrorCode('REQUIRED');
expect(result).toHaveErrorMessage('Email is required');
});
test('validates format', () => {
const result = validateEmail('notanemail');
expect(result).toBeErrWith({
code: 'INVALID_FORMAT',
message: 'Email must contain @'
});
});
test('transforms valid emails', () => {
const result = validateEmail('USER@EXAMPLE.COM')
.map(email => email.toLowerCase())
.map(email => ({ email, domain: email.split('@')[1] }))
.tap(data => expect(data.domain).toBe('example.com'));
expect(result).toBeOk();
expect(result).toBeOkWith({
email: 'user@example.com',
domain: 'example.com'
});
});
test('chains multiple validations', () => {
const validateAndNormalize = (email: string) =>
validateEmail(email)
.andThen(email => {
if (email.endsWith('.test')) {
return ZT.err('TEST_EMAIL', 'Test emails not allowed');
}
return ZT.ok(email);
})
.map(email => email.replace(/\+.*@/, '@')); // Remove plus addressing
const result = validateAndNormalize('user+tag@example.com');
expect(result).toBeOkWith('user@example.com');
const testResult = validateAndNormalize('user@example.test');
expect(testResult).toHaveErrorCode('TEST_EMAIL');
});
});
import '@zerothrow/jest';
import { ZT, ZeroThrow } from '@zerothrow/core';
class ValidationError extends Error {
constructor(public code: string, message: string) {
super(message);
this.name = 'ValidationError';
}
}
function processData(data: unknown) {
if (!data) {
return ZT.err(new ValidationError('EMPTY_DATA', 'No data provided'));
}
return ZT.ok(data);
}
test('transforms and validates data', () => {
const pipeline = (input: unknown) => processData(input)
.andThen(data => {
if (typeof data !== 'object') {
return ZT.err(new ValidationError('INVALID_TYPE', 'Expected object'));
}
return ZT.ok(data);
})
.map(obj => ({ ...obj, processed: true }))
.tap(result => console.log('Processed:', result));
const result = pipeline({ value: 42 });
expect(result).toBeOk();
expect(result).toBeOkWith({ value: 42, processed: true });
});
test('chains error handling', () => {
const result = processData(null)
.mapErr(err => new ValidationError('WRAPPED', `Wrapped: ${err.message}`))
.tapErr(err => expect(err.code).toBe('WRAPPED'));
expect(result).toBeErr();
expect(result).toHaveErrorMessage(/Wrapped: No data provided/);
});
test('collects multiple validations', () => {
const results = ZeroThrow.collect([
processData({ id: 1 }),
processData({ id: 2 }),
processData(null)
]);
expect(results).toBeErr();
expect(results).toHaveErrorCode('EMPTY_DATA');
});
See the main repository for contribution guidelines.
MIT
FAQs
Jest matchers for ZeroThrow Result types
We found that @zerothrow/jest demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
/Security News
Two npm packages masquerading as WhatsApp developer libraries include a kill switch that deletes all files if the phone number isn’t whitelisted.
Research
/Security News
Socket uncovered 11 malicious Go packages using obfuscated loaders to fetch and execute second-stage payloads via C2 domains.
Security News
TC39 advances 11 JavaScript proposals, with two moving to Stage 4, bringing better math, binary APIs, and more features one step closer to the ECMAScript spec.