DEV Community

Cover image for Stop Your Users From Cringing: How to Add Grammar and Spell-Check to Your Cypress Tests
S Chathuranga Jayasinghe
S Chathuranga Jayasinghe

Posted on

1 1

Stop Your Users From Cringing: How to Add Grammar and Spell-Check to Your Cypress Tests

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
Enter fullscreen mode Exit fullscreen mode

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`);
    });
  });
});
Enter fullscreen mode Exit fullscreen mode

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']
    });
  });
});
Enter fullscreen mode Exit fullscreen mode

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')}`);
      }
    });
  });
});
Enter fullscreen mode Exit fullscreen mode

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' });
Enter fullscreen mode Exit fullscreen mode

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"]');
    });
  });
});
Enter fullscreen mode Exit fullscreen mode

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.

Dev Diairies image

User Feedback & The Pivot That Saved The Project

🔥 Check out Episode 3 of Dev Diairies, following a successful Hackathon project turned startup.

Watch full video 🎥

Top comments (0)

👋 Kindness is contagious

Explore this insightful write-up embraced by the inclusive DEV Community. Tech enthusiasts of all skill levels can contribute insights and expand our shared knowledge.

Spreading a simple "thank you" uplifts creators—let them know your thoughts in the discussion below!

At DEV, collaborative learning fuels growth and forges stronger connections. If this piece resonated with you, a brief note of thanks goes a long way.

Okay