E2Eテスト 番外編 -metaとOGPの取得-


今回はTestcafeを用いてmetaやOGP情報を取得してjsonに吐き出す処理についてです。

Testcafeでmeta titileやdisctiptionなどを取得するには、クライアント側から任意のシリアライズ可能な値を返すことができるClientFunctionと呼ばれるクライアント関数を使用します。

common.js
import { ClientFunction } from 'testcafe';

const testUrl = 'https://example.com/'

// url path
export const getUrl = ClientFunction(() => location.pathname);
// meta titile
export const getTitle = ClientFunction(() => document.title);
// title文字数
export const getTitleLength = ClientFunction(() => document.title.length);
// meta description
export const getDesc = ClientFunction(() => {
  const description = document.getElementsByName('description')[0]
  if (description === undefined || description.content === '') return null
  return description.content
})
// meta description文字数
export const getDescLength = ClientFunction(() => {
  const description = document.getElementsByName('description')[0]
  if (description === undefined || description.content === '') return null
  return description.content.length
})
// meta keyword
export const getKeyword = ClientFunction(() => {
  const keyword = document.getElementsByName('keyword')[0]
  if (keyword === undefined || keyword.content === '') return null
  return keyword.content
})
// meta keyword個数
export const getKeywordLength = ClientFunction(() => {
  const keyword = document.getElementsByName('keyword')[0]
  if (keyword === undefined || keyword.content === '') return null
  return keyword.content.split(',').length
})
// og:title
export const getOgTitle = ClientFunction(() => {
  const ogTitle = document.querySelector("meta[property='og:title']")
  if (ogTitle === undefined || ogTitle.content === '') return null
  return ogTitle.content
})
// og:url
export const getOgUrl = ClientFunction(() => {
  const ogUrl = document.querySelector("meta[property='og:url']")
  if (ogUrl === undefined || ogUrl.content === '') return null
  return ogUrl.content
})
// og:image
export const getOgImg = ClientFunction(() => {
  const ogImg = document.querySelector("meta[property='og:image']")
  if (ogImg === undefined || ogImg.content === '') return null
  return ogImg.content
})
// og:site_name
export const getOgSiteName = ClientFunction(() => {
  const ogOgSiteName = document.querySelector("meta[property='og:site_name']")
  if (ogOgSiteName === undefined || ogOgSiteName.content === '') return null
  return ogOgSiteName.content
})
// og:description
export const getOgDesc = ClientFunction(() => {
  const ogDesc = document.querySelector("meta[property='og:description']")
  if (ogDesc === undefined || ogDesc.content === '') return null
  return ogDesc.content
})
// meta robots
export const getRobots = ClientFunction(() => {
  const robots = document.getElementsByName('robots')[0]
  if (robots === undefined || robots.content === '') return null
  return robots.content
})
// canonical
export const getCanonicalUrl = ClientFunction(() => {
  const links = document.getElementsByTagName('link')
  for (var i = 0; i < links.length; i++) {
    if (links[i].rel.toLowerCase() == 'canonical') {
      return links[i].href
    }
  }
  return null
})

そしてawaitで呼び出して実行します。
今回はスクリーンショットを撮るテストを開始する前に処理が行われるようTest.beforeメソッドを使いましたが、Test.beforeを使わずテストとして書いていくのもOKです!

capture.js
import { ClientFunction } from 'testcafe'
const common = require('../common')
const { testUrl, getUrl, getTitle, getTitleLength, getDesc, getDescLength, getKeyword, getKeywordLength, getOgTitle, getOgUrl, getOgImg, getOgSiteName, getOgDesc, getCanonicalUrl, getRobots } = common;

const screenShot = (testName, testUrl) => {

  fixture(testName).page(testUrl)
  test
    .before( async t => {
        const pageUrl = await getUrl();
        const pageTitle = await getTitle();
        const pageTitleLength = await getTitleLength();
        const pageDesc = await getDesc();
        const pageDescLength = await getDescLength();
        const pageKeyword = await getKeyword();
        const pageKeywordLength = await getKeywordLength();
        const OgTitle = await getOgTitle();
        const OgUrl = await getOgUrl();
        const OgImg = await getOgImg();
        const OgSiteName = await getOgSiteName();
        const OgDesc = await getOgDesc();
        const canonicalUrl = await getCanonicalUrl();
        const robotsContent = await getRobots();
    })
    (testName, async t => {
      //スクリーンショットの処理
    });


}
screenShot('top', `${testUrl}`)

これでtitileとdiscription etc...を取得することができました!
次に取得した値をjsonに吐き出す処理を書いていきます。

capture.js
import { ClientFunction } from 'testcafe'
const fs = require('fs')
const common = require('../common')
const { testUrl, getUrl, getTitle, getTitleLength, getDesc, getDescLength, getKeyword, getKeywordLength, getOgTitle, getOgUrl, getOgImg, getOgSiteName, getOgDesc, getCanonicalUrl, getRobots } = common;

const masterData = []

const screenShot = (testName, testUrl) => {

  fixture(testName).page(testUrl)
  test
    .before( async t => {
        const pageUrl = await getUrl();
        const pageTitle = await getTitle();
        const pageTitleLength = await getTitleLength();
        const pageDesc = await getDesc();
        const pageDescLength = await getDescLength();
        const pageKeyword = await getKeyword();
        const pageKeywordLength = await getKeywordLength();
        const OgTitle = await getOgTitle();
        const OgUrl = await getOgUrl();
        const OgImg = await getOgImg();
        const OgSiteName = await getOgSiteName();
        const OgDesc = await getOgDesc();
        const canonicalUrl = await getCanonicalUrl();
        const robotsContent = await getRobots();

        const data = {
          page: pageUrl,
          title: [
            {
              text: pageTitle,
              '最大29文字程度': pageTitleLength ? pageTitleLength <= '29' : pageTitleLength >= '29'
            }
          ],
          description: [
            {
              text: pageDesc,
              '最大110字程度': pageDescLength ? pageDescLength <= '110' : pageDescLength >= '110'
            }
          ],
          keyword: [
            {
              text: pageKeyword,
              '5〜6個程度': pageKeywordLength ? pageKeywordLength <= '6' : pageKeywordLength >= '6'
            }
          ],
          'og:title': OgTitle,
          'og:url': OgUrl,
          'og:image': OgImg,
          'og:site_name': OgSiteName,
          'og:description': OgDesc,
          canonical: canonicalUrl,
          robots: robotsContent
        }
        masterData.push(data)
        let str = JSON.stringify({meta: masterData}, null, ' ')
        fs.writeFileSync('./screenshots/data.json', str);
    })
    (testName, async t => {
      //スクリーンショットの処理
    });


}
screenShot('top', `${testUrl}`)

まずconst masterData = []で空の配列を作成し、pushで値を入れ込んでいきます。
そしてJSON.stringifyでJSON文字列に変換し、fs.writeFileSyncで指定したjsonに書き込んで終了です!
生成されたjsonが↓こちら。

data.json
{
 "meta": [
  {
   "page": "/",
   "title": [
    {
     "text": "株式会社Sample",
     "最大29文字程度": true
    }
   ],
   "description": [
    {
     "text": "株式会社Sampleのdescriptionが入ります。",
     "最大110字程度": true
    }
   ],
   "keyword": [
    {
     "text": "株式会社Sample,サンプル,sample,北海道,東京,大阪,福岡",
     "5〜6個程度": false
    }
   ],
   "og:title": "株式会社Sample",
   "og:url": "https://example.com/",
   "og:image": "https://example.com/img/ogp.png?1603865417442",
   "og:site_name": "株式会社Sample",
   "og:description": "株式会社Sampleのog:descriptionが入ります。",
   "canonical": "",
   "robots": null
  }
 ]
}

綺麗に生成することができました!

ちなみにtitileやdescriptionなどにはSEOに最適とされる文字数があるので、設定した文字数を超えている場合はfalse、設定値に収まる場合はtrueを。robotsなどタグ自体がないものはnullを返すようにしています。