Merge pull request #21 from kremalicious/feature/updates

Updates, cleanup, and testing
This commit is contained in:
Matthias Kretschmann 2020-02-15 15:34:26 +01:00 committed by GitHub
commit f5a0180b98
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 214 additions and 82 deletions

View File

@ -1,6 +1,3 @@
{
"presets": [
["@babel/env"],
["@babel/react"]
]
"presets": [["babel-preset-gatsby-package", { "browser": true }]]
}

View File

@ -1,5 +0,0 @@
version: "2"
checks:
method-lines:
config:
threshold: 40

View File

@ -1,10 +0,0 @@
# EditorConfig is awesome: http://EditorConfig.org
[*]
indent_style = space
indent_size = 2
end_of_line = lf
insert_final_newline = true
charset = utf-8
trim_trailing_whitespace = true

View File

@ -1,10 +1,6 @@
{
"extends": [
"eslint:recommended",
"plugin:react/recommended",
"plugin:prettier/recommended"
],
"plugins": ["react", "prettier"],
"extends": ["eslint:recommended", "plugin:prettier/recommended"],
"plugins": ["prettier"],
"parserOptions": {
"sourceType": "module",
"ecmaFeatures": {
@ -15,6 +11,7 @@
"env": {
"browser": true,
"node": true,
"es6": true
"es6": true,
"jest": true
}
}

1
.gitignore vendored
View File

@ -1,3 +1,4 @@
/*.js
node_modules
package-lock.json
coverage

View File

@ -4,3 +4,5 @@ src
.editorconfig
.eslintrc
.travis.yml
coverage
__tests__

View File

@ -1,5 +1,6 @@
{
"semi": false,
"singleQuote": true,
"trailingComma": "none"
"trailingComma": "none",
"tabWidth": 2
}

View File

@ -1,5 +1,17 @@
language: node_js
node_js: node
before_script:
- curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
- chmod +x ./cc-test-reporter
- ./cc-test-reporter before-build
script:
- npm test
- npm run build
after_script:
- ./cc-test-reporter after-build --exit-code $TRAVIS_TEST_RESULT
notifications:
email: false
email: false

View File

@ -1,4 +1,4 @@
Copyright (c) 2018 Matthias Kretschmann m@kretschmann.io
Copyright (c) 2020 Matthias Kretschmann m@kretschmann.io
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

View File

@ -5,6 +5,7 @@
[![npm package](https://img.shields.io/npm/v/gatsby-plugin-matomo.svg)](https://www.npmjs.com/package/gatsby-plugin-matomo)
[![Build Status](https://travis-ci.com/kremalicious/gatsby-plugin-matomo.svg?branch=master)](https://travis-ci.com/kremalicious/gatsby-plugin-matomo)
[![Maintainability](https://api.codeclimate.com/v1/badges/067339a02f2058f5ba01/maintainability)](https://codeclimate.com/github/kremalicious/gatsby-plugin-matomo/maintainability)
[![Test Coverage](https://api.codeclimate.com/v1/badges/067339a02f2058f5ba01/test_coverage)](https://codeclimate.com/github/kremalicious/gatsby-plugin-matomo/test_coverage)
[![Greenkeeper badge](https://badges.greenkeeper.io/kremalicious/gatsby-plugin-matomo.svg)](https://greenkeeper.io/)
> 🥂 Gatsby plugin to add Matomo (formerly Piwik) onto a site. https://kremalicious.com/gatsby-plugin-matomo/
@ -82,7 +83,7 @@ plugins: [
options: {
siteId: 'YOUR_SITE_ID',
matomoUrl: 'https://YOUR_MATOMO_URL.COM',
siteUrl: 'https://YOUR_LIVE_SITE_URL.COM'
siteUrl: 'https://YOUR_LIVE_SITE_URL.COM',
// All the optional settings
exclude: ['/offline-plugin-app-shell-fallback/'],
requireConsent: false,
@ -118,7 +119,7 @@ See [CHANGELOG.md](CHANGELOG.md).
The MIT License
Copyright (c) 2018 Matthias Kretschmann
Copyright (c) 2020 Matthias Kretschmann
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

View File

@ -4,42 +4,46 @@
"version": "0.7.2",
"author": "Matthias Kretschmann <m@kretschmann.io>",
"scripts": {
"build": "babel src --out-dir . --ignore __tests__",
"start": "babel -w src --out-dir . --ignore __tests__",
"test": "eslint ./src/**/*.js",
"format": "prettier --write 'src/**/*.{js,jsx}'",
"build": "babel src --out-dir . --ignore **/__tests__",
"start": "babel -w src --out-dir . --ignore **/__tests__",
"test": "npm run lint && jest --coverage",
"lint": "eslint ./src/**/*.js",
"format": "prettier --write 'src/**/*.js'",
"release": "release-it --non-interactive",
"changelog": "auto-changelog -p",
"prepublishOnly": "cross-env NODE_ENV=production npm run build"
},
"dependencies": {},
"devDependencies": {
"@babel/cli": "^7.4.4",
"@babel/core": "^7.4.5",
"@babel/preset-env": "^7.4.5",
"@babel/preset-react": "^7.0.0",
"auto-changelog": "^1.13.0",
"cross-env": "^5.2.0",
"eslint": "^5.16.0",
"eslint-config-prettier": "^4.3.0",
"eslint-plugin-prettier": "^3.1.0",
"eslint-plugin-react": "^7.13.0",
"prettier": "^1.18.2",
"react": "^16.8.6",
"react-dom": "^16.8.6",
"release-it": "^12.3.0"
"@babel/cli": "^7.8.4",
"@babel/core": "^7.8.4",
"@babel/runtime": "^7.8.4",
"auto-changelog": "^1.16.2",
"babel-preset-gatsby-package": "^0.2.16",
"cross-env": "^7.0.0",
"eslint": "^6.8.0",
"eslint-config-prettier": "^6.10.0",
"eslint-plugin-prettier": "^3.1.2",
"jest": "^25.1.0",
"prettier": "^1.19.1",
"react": "^16.12.0",
"release-it": "^12.4.3"
},
"homepage": "https://github.com/kremalicious/gatsby-plugin-matomo",
"homepage": "https://kremalicious.com/gatsby-plugin-matomo",
"keywords": [
"gatsby",
"gatsby-plugin",
"analytics",
"tracking",
"matomo",
"piwik"
],
"license": "MIT",
"main": "index.js",
"peerDependencies": {
"gatsby": ">=1.9.0"
"gatsby": ">=2.0.0",
"react": ">=16.4.2",
"react-dom": ">=16.4.2"
},
"repository": "github:kremalicious/gatsby-plugin-matomo",
"bugs": {

View File

@ -0,0 +1,66 @@
import { onRouteUpdate } from '../gatsby-browser'
describe('gatsby-plugin-matomo', () => {
describe('gatsby-browser', () => {
beforeEach(() => {
jest.useFakeTimers()
window._paq = { push: jest.fn() }
})
afterEach(() => {
jest.resetAllMocks()
})
describe('onRouteUpdate', () => {
describe('in non-production env', () => {
beforeAll(() => {
window._paq = { push: jest.fn() }
})
it('does not send page view', () => {
onRouteUpdate({})
expect(window._paq.push).not.toHaveBeenCalled()
})
it('sends page view in dev mode', () => {
window.dev = true
onRouteUpdate({})
expect(window._paq.push).toHaveBeenCalledTimes(1)
})
})
describe('in production env', () => {
let env
beforeAll(() => {
env = process.env.NODE_ENV
process.env.NODE_ENV = 'production'
})
afterAll(() => {
process.env.NODE_ENV = env
})
it('does not send page view when _paq is undefined', () => {
delete window._paq
onRouteUpdate({})
jest.runAllTimers()
expect(setTimeout).not.toHaveBeenCalled()
})
it('sends page view', () => {
onRouteUpdate({})
jest.runAllTimers()
expect(window._paq.push).toHaveBeenCalledTimes(5)
})
it('uses setTimeout with a minimum delay of 32ms', () => {
onRouteUpdate({})
jest.runAllTimers()
expect(setTimeout).toHaveBeenCalledWith(expect.any(Function), 32)
expect(window._paq.push).toHaveBeenCalledTimes(5)
})
})
})
})
})

View File

@ -0,0 +1,74 @@
import { onRenderBody } from '../gatsby-ssr'
describe('gatsby-plugin-google-analytics', () => {
describe('gatsby-ssr', () => {
describe('onRenderBody', () => {
describe('in non-production env', () => {
it('does not set tracking script', () => {
const setHeadComponents = jest.fn()
onRenderBody({ setHeadComponents })
expect(setHeadComponents).not.toHaveBeenCalled()
})
})
describe('in production env', () => {
let env
beforeAll(() => {
env = process.env.NODE_ENV
process.env.NODE_ENV = 'production'
})
afterAll(() => {
process.env.NODE_ENV = env
})
const setup = options => {
const setHeadComponents = jest.fn()
const setPostBodyComponents = jest.fn()
options = Object.assign(
{
siteId: 'TEST_SITE_ID',
matomoUrl: 'TEST_MATOMO_URL',
siteUrl: 'TEST_SITE_URL'
},
options
)
onRenderBody({ setHeadComponents, setPostBodyComponents }, options)
return {
setHeadComponents,
setPostBodyComponents
}
}
it('sets tracking script', () => {
const { setHeadComponents, setPostBodyComponents } = setup()
expect(setHeadComponents).toHaveBeenCalledTimes(1)
expect(setPostBodyComponents).toHaveBeenCalledTimes(1)
})
it('sets siteId', () => {
const { setPostBodyComponents } = setup()
const result = JSON.stringify(setPostBodyComponents.mock.calls[0][0])
expect(result).toMatch(/TEST_SITE_ID/)
})
it('sets matomoUrl', () => {
const { setPostBodyComponents } = setup()
const result = JSON.stringify(setPostBodyComponents.mock.calls[0][0])
expect(result).toMatch(/TEST_MATOMO_URL/)
})
it('sets siteUrl', () => {
const { setPostBodyComponents } = setup()
const result = JSON.stringify(setPostBodyComponents.mock.calls[0][0])
expect(result).toMatch(/TEST_SITE_URL/)
})
})
})
})
})

View File

@ -1,5 +1,3 @@
/* eslint-disable no-console */
let first = true
function getDuration() {
@ -14,15 +12,12 @@ function getDuration() {
return difference
}
exports.onRouteUpdate = ({ location, prevLocation }) => {
if (
(process.env.NODE_ENV === 'production' && typeof _paq !== 'undefined') ||
window.dev === true
) {
const _paq = window._paq || []
const dev = window.dev || null
export const onRouteUpdate = ({ location, prevLocation }) => {
if (process.env.NODE_ENV === 'production' || window.dev === true) {
if (!window._paq) return
const url = location.pathname + location.search + location.hash
const { _paq, dev } = window
const url = location && location.pathname + location.search + location.hash
const prevUrl =
prevLocation &&
prevLocation.pathname + prevLocation.search + prevLocation.hash
@ -40,27 +35,23 @@ exports.onRouteUpdate = ({ location, prevLocation }) => {
_paq.push(['trackAllContentImpressions'])
if (dev) {
console.log(`[Matomo] Page view for: ${url} - ${title}`)
console.debug(`[Matomo] Page view for: ${url} - ${title}`)
}
}
if ('requestAnimationFrame' in window) {
requestAnimationFrame(() => {
requestAnimationFrame(sendPageView)
})
} else {
// simulate 2 rAF calls
setTimeout(sendPageView, 32)
}
// Minimum delay for reactHelmet's requestAnimationFrame
const delay = Math.max(32, 0)
setTimeout(sendPageView, delay)
if (first) {
first = false
_paq.push(['trackEvent', 'javascript', 'load', 'duration', getDuration()])
if (dev) {
console.log(`[Matomo] Tracking duration for: ${url}`)
console.debug(`[Matomo] Tracking duration for: ${url}`)
}
}
}
return null
}

View File

@ -1,3 +1,4 @@
// eslint-disable-next-line no-unused-vars
import React from 'react'
function buildTrackingCode(pluginOptions) {
@ -29,29 +30,28 @@ function buildTrackingCode(pluginOptions) {
})();
if (window.dev === true) {
console.log('[Matomo] Tracking initialized')
console.log('[Matomo] matomoUrl: ${matomoUrl}, siteId: ${siteId}')
console.debug('[Matomo] Tracking initialized')
console.debug('[Matomo] matomoUrl: ${matomoUrl}, siteId: ${siteId}')
}
}
`
return (
<script
key={'gatsby-plugin-matomo'}
key="script-gatsby-plugin-matomo"
dangerouslySetInnerHTML={{ __html: html }}
/>
)
}
function buildTrackingCodeNoJs(pluginOptions, pathname) {
const html = `<img src="${pluginOptions.matomoUrl}/piwik.php?idsite=${
pluginOptions.siteId
}&rec=1&url=${pluginOptions.siteUrl +
const { matomoUrl, siteId, siteUrl } = pluginOptions
const html = `<img src="${matomoUrl}/piwik.php?idsite=${siteId}&rec=1&url=${siteUrl +
pathname}" style="border:0" alt="tracker" />`
return (
<noscript
key={'gatsby-plugin-matomo'}
key="noscript-gatsby-plugin-matomo"
dangerouslySetInnerHTML={{ __html: html }}
/>
)
@ -62,18 +62,19 @@ function buildHead(pluginOptions) {
<link
rel="preconnect"
href={pluginOptions.matomoUrl}
key={'gatsby-plugin-matomo'}
key="preconnect-gatsby-plugin-matomo"
/>
)
}
exports.onRenderBody = (
export const onRenderBody = (
{ setHeadComponents, setPostBodyComponents, pathname },
pluginOptions
) => {
const isProduction = process.env.NODE_ENV === 'production'
let excludePaths = ['/offline-plugin-app-shell-fallback/']
if (typeof pluginOptions.exclude !== 'undefined') {
if (pluginOptions && typeof pluginOptions.exclude !== 'undefined') {
pluginOptions.exclude.map(exclude => {
excludePaths.push(exclude)
})
@ -82,7 +83,7 @@ exports.onRenderBody = (
const isPathExcluded = excludePaths.some(path => pathname === path)
if (
(process.env.NODE_ENV === 'production' || pluginOptions.dev === true) &&
(isProduction || (pluginOptions && pluginOptions.dev === true)) &&
!isPathExcluded
) {
setHeadComponents([buildHead(pluginOptions)])