Expressive, readable, framework-agnostic BDD-style assertion library for JavaScript testing.
—
Methods for testing string content, patterns, and formatting.
Test that a string starts with a specified prefix.
/**
* Assert that the string starts with the specified prefix
* @param prefix - The expected prefix string
* @param description - Optional error message
* @returns This assertion for chaining
*/
startWith(prefix: string, description?: string): Assertion;Usage:
import should from 'should';
'hello world'.should.startWith('hello');
'JavaScript'.should.startWith('Java');
'test123'.should.startWith('test');
// Case sensitive
'Hello'.should.not.startWith('hello');
'Hello'.should.startWith('H');
// Empty string
'anything'.should.startWith('');
''.should.startWith('');
// With description
const filename = 'config.json';
filename.should.startWith('config', 'Filename should start with config');
// Chaining with other assertions
const url = 'https://example.com';
url.should.startWith('https').and.be.a.String();Test that a string ends with a specified suffix.
/**
* Assert that the string ends with the specified suffix
* @param suffix - The expected suffix string
* @param description - Optional error message
* @returns This assertion for chaining
*/
endWith(suffix: string, description?: string): Assertion;Usage:
'hello world'.should.endWith('world');
'test.js'.should.endWith('.js');
'document.pdf'.should.endWith('.pdf');
// Case sensitive
'Hello'.should.not.endWith('LLO');
'Hello'.should.endWith('o');
// Empty string
'anything'.should.endWith('');
''.should.endWith('');
// With description
const email = 'user@example.com';
email.should.endWith('.com', 'Email should end with .com domain');
// File extension validation
const jsFile = 'script.js';
jsFile.should.endWith('.js').and.be.a.String();Test that a string matches a pattern, function, or object matcher.
/**
* Assert that the string matches the specified pattern, function, or object
* @param pattern - RegExp, function, or object to match against
* @param description - Optional error message
* @returns This assertion for chaining
*/
match(pattern: RegExp | Function | object, description?: string): Assertion;Usage:
// Regular expression matching
'hello123'.should.match(/^hello\d+$/);
'test@example.com'.should.match(/\w+@\w+\.\w+/);
'2023-12-25'.should.match(/^\d{4}-\d{2}-\d{2}$/);
// Case insensitive regex
'Hello World'.should.match(/hello world/i);
// Function matching
'positive123'.should.match(str => str.includes('positive'));
'test'.should.match(str => str.length === 4);
// Object matching (for complex patterns)
const phonePattern = {
test: (str) => /^\d{3}-\d{3}-\d{4}$/.test(str)
};
'123-456-7890'.should.match(phonePattern);
// With description
const password = 'MyPassword123!';
password.should.match(/^(?=.*[A-Z])(?=.*\d)(?=.*[!@#$%^&*])/,
'Password should contain uppercase, digit, and special character');function validateEmail(email) {
email.should.be.a.String();
email.should.match(/^[^\s@]+@[^\s@]+\.[^\s@]+$/);
email.should.not.be.empty();
// Additional checks
email.should.not.startWith('@');
email.should.not.endWith('@');
email.should.match(/@/); // Must contain @
}
// Usage
const validEmail = 'user@example.com';
validateEmail(validEmail);function validateURL(url) {
url.should.be.a.String();
url.should.match(/^https?:\/\//); // Must start with http:// or https://
url.should.not.be.empty();
}
// Protocol-specific validation
function validateHTTPS(url) {
url.should.startWith('https://');
url.should.not.startWith('http://');
}
const secureUrl = 'https://secure.example.com';
validateHTTPS(secureUrl);function validateJavaScriptFile(filepath) {
filepath.should.be.a.String();
filepath.should.endWith('.js');
filepath.should.not.be.empty();
}
function validateImageFile(filepath) {
filepath.should.be.a.String();
filepath.should.match(/\.(jpg|jpeg|png|gif|webp)$/i);
}
const jsFile = 'components/Header.js';
validateJavaScriptFile(jsFile);
const imageFile = 'assets/logo.PNG';
validateImageFile(imageFile);// Phone number formats
function validatePhoneNumber(phone) {
phone.should.be.a.String();
phone.should.match(/^\(\d{3}\) \d{3}-\d{4}$|^\d{3}-\d{3}-\d{4}$/);
}
// Date formats
function validateDateFormat(date) {
date.should.be.a.String();
date.should.match(/^\d{4}-\d{2}-\d{2}$/); // YYYY-MM-DD
}
// Time formats
function validateTimeFormat(time) {
time.should.be.a.String();
time.should.match(/^([01]?[0-9]|2[0-3]):[0-5][0-9]$/); // HH:MM (24-hour)
}
validatePhoneNumber('(555) 123-4567');
validateDateFormat('2023-12-25');
validateTimeFormat('14:30');// Password strength
function validateStrongPassword(password) {
password.should.be.a.String();
password.should.match(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/,
'Password must contain uppercase, lowercase, digit, special char, min 8 chars');
}
// Username validation
function validateUsername(username) {
username.should.be.a.String();
username.should.match(/^[a-zA-Z0-9_-]{3,20}$/);
username.should.not.startWith('-');
username.should.not.endWith('-');
}
// HTML tag validation
function validateHTMLTag(tag) {
tag.should.be.a.String();
tag.should.startWith('<');
tag.should.endWith('>');
tag.should.match(/^<\/?[a-zA-Z][\w-]*(?:\s[^>]*)?>/);
}
validateUsername('user_123');
validateHTMLTag('<div class="container">');// Check for specific content
function validateContainsKeywords(text, keywords) {
text.should.be.a.String();
keywords.forEach(keyword => {
text.should.match(new RegExp(keyword, 'i')); // Case insensitive
});
}
// Code validation
function validateJavaScriptCode(code) {
code.should.be.a.String();
code.should.not.be.empty();
// Should contain function or variable declarations
code.should.match(/\b(function|const|let|var|class)\b/);
}
// Markdown validation
function validateMarkdownHeader(text) {
text.should.be.a.String();
text.should.startWith('#');
text.should.match(/^#{1,6}\s+/); // 1-6 # followed by space
}
const jsCode = 'const hello = () => "world";';
validateJavaScriptCode(jsCode);
const header = '## Section Title';
validateMarkdownHeader(header);String assertions in Should.js focus on content testing, prefix/suffix validation, and pattern matching specifically for string values.
Note: The match() method shown above also works with collections and has additional variants (matchEach(), matchEvery(), matchAny(), matchSome()). These are general collection methods, not string-specific. For complete documentation on pattern matching capabilities, see Pattern Matching.
String assertions can be combined with length and emptiness tests:
const text = 'Hello World';
// Length validation
text.should.be.a.String().and.have.length(11);
text.should.not.be.empty();
// Pattern with length constraints
const code = 'ABC123';
code.should.match(/^[A-Z]{3}\d{3}$/).and.have.length(6);
// Minimum/maximum length with content validation
const title = 'My Article Title';
title.should.be.a.String()
.and.have.property('length').above(5)
.and.below(50);
title.should.not.be.empty();// Case-sensitive matching
'JavaScript'.should.startWith('Java'); // ✓
'JavaScript'.should.not.startWith('java'); // ✓
'test.JS'.should.endWith('.JS'); // ✓
'test.JS'.should.not.endWith('.js'); // ✓
// Case-insensitive with regex
'Hello World'.should.match(/hello/i); // ✓
'Hello World'.should.not.match(/hello/); // ✓ (case sensitive fails)
// Custom case-insensitive validation
function validateExtension(filename, ext) {
filename.should.be.a.String();
filename.should.match(new RegExp(`\\.${ext}$`, 'i'));
}
validateExtension('document.PDF', 'pdf'); // ✓
validateExtension('image.JPG', 'jpg'); // ✓All string assertions support negation and can be chained:
const filename = 'document.txt';
// Multiple conditions
filename.should.be.a.String()
.and.not.be.empty()
.and.startWith('doc')
.and.endWith('.txt')
.and.match(/^[a-z]+\.(txt|doc)$/);
// Negation examples
filename.should.not.startWith('test');
filename.should.not.endWith('.js');
filename.should.not.match(/^\d+/); // Should not start with digitsInstall with Tessl CLI
npx tessl i tessl/npm-should