You know that feeling when you're browsing a website and suddenly hit a glaring typo? It's like nails on a chalkboard, right? Your users feel the same way. But here's the thing – as developers, we're so focused on making sure our buttons click and our forms submit that we sometimes forget about the words themselves.
I learned this the hard way when a client called me up, slightly panicked, because their brand new marketing page had "recieve" instead of "receive" plastered across the hero section. It had been live for three days. Three whole days of potential customers seeing that typo.
That's when I discovered the beautiful marriage of Cypress and LanguageTool API – a combination that'll save you from those cringe-worthy moments and keep your content looking professional.
Why LanguageTool + Cypress is a Match Made in Heaven
LanguageTool isn't just another spell checker. It's like having that really smart friend who catches your grammar mistakes AND explains why they're wrong. It supports over 20 languages, catches context-sensitive errors, and has a generous free tier that's perfect for testing.
Cypress, on the other hand, is already crawling through your pages, clicking buttons, and checking functionality. Why not have it proofread while it's there?
Setting Up Your Grammar Police
First things first – let's talk about the LanguageTool API. The good news? There's a free public API at https://api.languagetool.org/v2/check
that doesn't require an API key for basic usage. The limits are generous for testing: 20 requests per IP per minute and up to 20KB of text per request.
Installing the Dependencies
bashnpm install --save-dev axios
That's it! We'll use axios to make our API calls, but you could just as easily use fetch or your HTTP client of choice.
Creating Your Custom Cypress Command
Here's where the magic happens. We're going to create a custom Cypress command that grabs text from your page and sends it to LanguageTool for analysis.
Create or update your cypress/support/commands.js
file:
javascriptimport axios from 'axios';
Cypress.Commands.add('checkGrammar', (selector, options = {}) => {
const {
language = 'en-US',
skipWords = []
} = options;
cy.get(selector).then(($element) => {
// Extract text from the element
const text = $element.text().trim();
if (!text) {
cy.log('No text found in element');
return;
}
// Make API call to LanguageTool
cy.request({
method: 'POST',
url: 'https://api.languagetool.org/v2/check',
form: true,
body: {
text: text,
language: language
}
}).then((response) => {
const errors = response.body.matches;
if (errors.length > 0) {
// Filter out words we want to skip (like brand names)
const filteredErrors = errors.filter(error => {
const errorText = text.substring(error.offset, error.offset + error.length);
return !skipWords.some(word =>
errorText.toLowerCase().includes(word.toLowerCase())
);
});
if (filteredErrors.length > 0) {
// Create a readable error message
const errorMessages = filteredErrors.map(error => {
const errorText = text.substring(error.offset, error.offset + error.length);
return `"${errorText}": ${error.message}`;
});
cy.log('Grammar/Spelling errors found:');
errorMessages.forEach(msg => cy.log(msg));
// Fail the test if errors are found
throw new Error(`Grammar/Spelling errors found:\n${errorMessages.join('\n')}`);
}
}
cy.log(`Text checked: "${text.substring(0, 50)}..." - No errors found`);
});
});
});
No More API Key Management!
Good news – you don't need to sign up for anything or manage API keys for basic usage. The public LanguageTool API is free and ready to use right out of the box.
Writing Your First Grammar Test
Now for the fun part – let's write a test that actually uses this new superpower:
javascriptdescribe('Content Quality Tests', () => {
it('should have error-free content on the homepage', () => {
cy.visit('/');
// Check the main heading
cy.checkGrammar('h1');
// Check the hero description
cy.checkGrammar('[data-testid="hero-description"]');
// Check all paragraph text, but skip our brand name "TechnoWiz"
cy.checkGrammar('main p', {
skipWords: ['TechnoWiz', 'API']
});
});
it('should validate blog post content', () => {
cy.visit('/blog/latest-post');
// Check the title and content
cy.checkGrammar('article h1');
cy.checkGrammar('article .content', {
skipWords: ['JavaScript', 'React', 'npm']
});
});
});
Getting Fancy: Batch Checking Multiple Elements
Here's a neat trick I discovered after running this on a content-heavy site. Instead of making individual API calls for each element, you can batch them together:
javascriptCypress.Commands.add('checkMultipleElements', (selectors, options = {}) => {
const texts = [];
const elementMap = new Map();
// Collect all text first
selectors.forEach(selector => {
cy.get(selector).then(($element) => {
const text = $element.text().trim();
if (text) {
elementMap.set(texts.length, selector);
texts.push(text);
}
});
});
// Then check all at once
cy.then(() => {
if (texts.length === 0) return;
const combinedText = texts.join('\n\n');
cy.request({
method: 'POST',
url: 'https://api.languagetool.org/v2/check',
form: true,
body: {
text: combinedText,
language: options.language || 'en-US'
}
}).then((response) => {
const errors = response.body.matches;
if (errors.length > 0) {
// Map errors back to their original elements
const errorMessages = errors.map(error => {
const errorText = combinedText.substring(error.offset, error.offset + error.length);
return `"${errorText}": ${error.message}`;
});
throw new Error(`Content errors found:\n${errorMessages.join('\n')}`);
}
});
});
});
Real-World Tips and Gotchas
After using this setup on several projects, here are some lessons I've learned:
Watch Your API Limits: The free public API allows 20 requests per IP per minute and up to 20KB of text per request. That's actually quite generous for testing, but keep an eye on your usage if you're running lots of tests.
Handle Dynamic Content: If your content changes frequently (like user-generated content), you might want to be more selective about what you check. Focus on static content like headers, navigation, and marketing copy.
Brand Names and Technical Terms: Always use the skipWords
option for your brand names, technical jargon, and industry-specific terms that LanguageTool might flag incorrectly.
Different Languages: If you're building a multilingual site, you can easily switch the language parameter:
javascriptcy.checkGrammar('.spanish-content', { language: 'es' });
cy.checkGrammar('.french-content', { language: 'fr' });
Making It Part of Your CI/CD Pipeline
Here's where this really shines – imagine catching content errors before they go live. Add this to your CI pipeline:
javascript// In your cypress/e2e/content-quality.cy.js
describe('Pre-deployment Content Check', () => {
const criticalPages = [
'/',
'/pricing',
'/about',
'/contact'
];
criticalPages.forEach(page => {
it(`should have quality content on ${page}`, () => {
cy.visit(page);
// Check all headings and main content
cy.checkGrammar('h1, h2, h3');
cy.checkGrammar('main p, .content p');
cy.checkGrammar('[class*="hero"], [class*="cta"]');
});
});
});
The Bottom Line
Look, perfect grammar won't make your app faster or more secure, but it will make your users trust you more. And trust, my friend, is everything in business.
This Cypress + LanguageTool combo has saved me from more embarrassing typos than I care to admit. It's like having a really pedantic proofreader who never gets tired and works for free (well, mostly free).
The best part? Once you've set it up, it just runs. Every time you deploy, every time you test, your content gets a quality check. Your marketing team will love you, your users will respect you, and you'll sleep better knowing that "recieve" will never make it to production again.
So go ahead, give your content the same attention you give your code. Your future self (and your users) will thank you for it.
Top comments (0)