Back to Repositories

Testing Node Processing and HTML Transformation in GatsbyJS

This test suite validates the processing and transformation of HTML content, specifically focusing on image and link handling in the Gatsby WordPress source plugin. The tests ensure proper regex matching, string replacement, and Gatsby image service integration.

Test Coverage Overview

The test suite provides comprehensive coverage of node processing functionality in the Gatsby WordPress source plugin.

  • HTML image transformation and regex matching
  • WordPress link detection and transformation
  • String search and replace operations
  • Gatsby Image service integration with HTML fields
  • Edge cases including various URL formats and special characters

Implementation Analysis

The tests utilize Jest’s testing framework with async/await patterns and mocking capabilities.

Key implementation features include:
  • Custom regex patterns for image and link detection
  • Global store management using asyncLocalStorage
  • Mocked media item fetching
  • Transform functions for HTML content processing

Technical Details

Testing infrastructure includes:
  • Jest as the primary testing framework
  • execall for regex pattern matching
  • Custom store implementation with asyncLocalStorage
  • Mocked Gatsby helper functions and actions
  • WordPress URL and content fixtures

Best Practices Demonstrated

The test suite exemplifies strong testing practices through isolated unit tests, comprehensive assertions, and proper test organization.

  • Modular test case organization
  • Thorough edge case coverage
  • Proper test setup and teardown
  • Clear test descriptions and expectations
  • Effective use of fixtures and mocking

gatsbyjs/gatsby

packages/gatsby-source-wordpress/__tests__/process-node.test.js

            
import execall from "execall"

import { replaceNodeHtmlImages } from "../dist/steps/source-nodes/create-nodes/process-node"

import {
  getImgSrcRemoteFileMatchesFromNodeString,
  getImgTagMatches,
  getWpLinkRegex,
  searchAndReplaceNodeStrings,
} from "../dist/steps/source-nodes/create-nodes/process-node"

import { createStore, asyncLocalStorage } from "../dist/store"


const store = { store: createStore(), key: `test` }

const withGlobalStore = fn => () => asyncLocalStorage.run(store, fn)

const wpUrl = `wp.fakesite.com`

test(`HTML image transformation regex matches images`, async () => {
  const nodeString = `<img src=\\"https://${wpUrl}/wp-content/uploads/2020/01/©SDM-Yep-©Hi-000-Header.jpg\\" />

  <img src=\\"http://${wpUrl}/wp-content/uploads/2020/01/©SDM-Yep-©Hi-000-Header.jpg\\" />

  <img src=\\"/wp-content/uploads/2020/01/©SDM-Yep-©Hi-000-Header.jpg\\" />`

  const matches = getImgSrcRemoteFileMatchesFromNodeString(nodeString)

  expect(matches.length).toBe(3)

  const imgTagMatches = getImgTagMatches({
    nodeString,
    wpUrl: `https://${wpUrl}`,
  })

  expect(imgTagMatches.length).toBe(3)
})

test(`HTML link transformation regex matches links`, withGlobalStore(async () => {
  const nodeString = `<a href=\\"https://${wpUrl}/wp-content/uploads/2020/01/©SDM-Yep-©Hi-000-Header.jpg\\" />Not a transformable link</a>

  <a href=\\"https://other-site.com/hi\\" />Not a transformable link</a>

  <a href=\\"https://${wpUrl}/page-1\\">
    page 1
  </a>

  <a href=\\"https://${wpUrl}/\\">Home</a>`

  const wpLinkRegex = getWpLinkRegex(`https://${wpUrl}`)
  const matches = execall(wpLinkRegex, nodeString)

  expect(matches.length).toBe(2)
}))

test(`Search and replace node strings using regex matches`, withGlobalStore(async () => {
  const nodeString = `Some stuff in a random string

  A new line with some stuff!

  We need to test some <a href=\\"https://old-site.com/hi\\" />link</a> as well!`

  const result = searchAndReplaceNodeStrings({
    nodeString,
    node: { __typename: `FakeTypeName`, id: `cG9zdDo0OQ==` },
    pluginOptions: {
      searchAndReplace: [
        { search: /(S|s)ome stuff/gm, replace: `some other thing` },
        { search: `https://old-site.com`, replace: `https://new-site.com` },
      ]
    }
  })

  expect(result).toBe(`some other thing in a random string

  A new line with some other thing!

  We need to test some <a href=\\"https://new-site.com/hi\\" />link</a> as well!`)
}))

jest.mock(`../dist/steps/source-nodes/fetch-nodes/fetch-referenced-media-items.js`, () => {
  return {
    __esModule: true,
    ...jest.requireActual(`../dist/steps/source-nodes/fetch-nodes/fetch-referenced-media-items.js`),
    default: jest.fn(() => require(`./fixtures/media`).referencedMediaItems)
  }
})


test(`Gatsby Image service works in html fields via replaceNodeHtmlImages`, withGlobalStore(async () => {
  const node = {
    content: `\n<p>Welcome to WordPress. This is your first post. Edit or deleteit, then start writing!</p>\n\n\n\n<p></p>\n\n\n\n<figureclass="wp-block-image size-large"><img loading="lazy" width="1024" height="768" src="http://wpgatsby.local/wp-content/uploads/2022/02/sasha-set-GURzQwO8Li0-unsplash-1024x768.jpg" alt=""class="wp-image-115" srcset="http://wpgatsby.local/wp-content/uploads/2022/02/sasha-set-GURzQwO8Li0-unsplash-1024x768.jpg 1024w,http://wpgatsby.local/wp-content/uploads/2022/02/sasha-set-GURzQwO8Li0-unsplash-300x225.jpg 300w, http://wpgatsby.local/wp-content/uploads/2022/02/sasha-set-GURzQwO8Li0-unsplash-768x576.jpg 768w,http://wpgatsby.local/wp-content/uploads/2022/02/sasha-set-GURzQwO8Li0-unsplash-1536x1152.jpg 1536w, http://wpgatsby.local/wp-content/uploads/2022/02/sasha-set-GURzQwO8Li0-unsplash-2048x1536.jpg 2048w"sizes="(max-width: 1024px) 100vw, 1024px" /></figure>\n<figure class="wp-block-image size-large"><img src="http://wpgatsby.local/wp-content/uploads/2022/04/gaussian2.svg" alt="" class="wp-image-11836"/></figure>`,
    id: `cG9zdDox`,
    modifiedGmt: `2022-02-18T23:18:00`,
    __typename: `Post`
  }

  const gatsbyImageUrlPart = `/_gatsby/image`
  const gatsbyFileUrlPart = `/_gatsby/file`
  const nodeString = JSON.stringify(node)

  const updatedNodeString = await replaceNodeHtmlImages({
    nodeString,
    node,
    helpers: {
      reporter: console,
      actions: {
        addGatsbyImageSourceUrl: jest.fn(),
        createJobV2: jest.fn(),
      },
    },
    wpUrl: `http://wpgatsby.local/`,
    pluginOptions: {
      html: {
        useGatsbyImage: true,
      }
    }
  })

  expect(updatedNodeString).not.toEqual(nodeString)
  expect(updatedNodeString).toInclude(gatsbyImageUrlPart)
  expect(updatedNodeString).toInclude(gatsbyFileUrlPart)

  const imageMatches = execall(/\/_gatsby\/image/gm, updatedNodeString)
  expect(imageMatches.length).toBe(15)

  const fileMatches = execall(/\/_gatsby\/file/gm, updatedNodeString)
  expect(fileMatches.length).toBe(1)


  const transformedNodeStringNoHtmlImages = await replaceNodeHtmlImages({
    nodeString,
    node,
    helpers: {
      reporter: console,
      actions: {
        addGatsbyImageSourceUrl: jest.fn(),
        createJobV2: jest.fn(),
      },
    },
    wpUrl: `http://wpgatsby.local/`,
    pluginOptions: {
      html: {
        useGatsbyImage: false
      }
    }
  })

  expect(transformedNodeStringNoHtmlImages).toEqual(nodeString)

  const noImageMatches = execall(/\/_gatsby\/image/gm, transformedNodeStringNoHtmlImages)

  expect(noImageMatches.length).toBe(0)

  expect(transformedNodeStringNoHtmlImages).not.toInclude(gatsbyImageUrlPart)
  expect(transformedNodeStringNoHtmlImages).not.toInclude(gatsbyFileUrlPart)
})
)