)` instead of `setConsentGiven`. See above for more information
183 |
184 | #### `doNotTrack`
185 |
186 | - Default: `false`
187 | If true, dont track users who have set Mozilla's (proposed) Do Not Track setting
188 |
189 | #### `debug`
190 |
191 | - Default: `false`
192 | If true, the plugin will log debug information to the console.
193 |
194 | > The plugin also logs debug information when Nuxt's debug option is set
195 |
196 | #### `verbose`
197 |
198 | - Default: `false`
199 | If true, the plugin will log every tracker function call to the console
200 |
201 | ## Caveats
202 |
203 | ### document.title
204 |
205 | Nuxt.js uses vue-meta to asynchronously update the `document.title`, this means by default we dont know when the `document.title` is changed. Therefore the default behaviour for this plugin is to set the `route.path` as document title.
206 |
207 | If you set the module option `onMetaChange: true`, then this plugin will track page views on the first time some meta data is updated by vue-meta (after navigation). This makes sure the `document.title` is available and updated, but if you have multiple pages without any meta data then those page views **could not be tracked**
208 |
209 | > vue-meta's changed event is only triggered when any meta data changed, make sure all your routes have a [`head`](https://nuxtjs.org/api/pages-head) option.
210 |
211 | When debug is true, this plugin will show warnings in the console when
212 | - it detects pages without a title
213 | - when no vue-meta changed event is triggered within 500ms after navigation (tracking could still occur, the timeout only shows a warning)
214 |
215 | You can also use a combination of manual tracking and a vuex store to keep track of the document.title
216 |
--------------------------------------------------------------------------------
/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | testEnvironment: 'node',
3 |
4 | collectCoverage: false,
5 | collectCoverageFrom: ['lib'],
6 |
7 | setupFilesAfterEnv: ['./test/utils/setup']
8 | }
9 |
--------------------------------------------------------------------------------
/lib/api-methods-list.json:
--------------------------------------------------------------------------------
1 | [
2 | "getHook",
3 | "getQuery",
4 | "getContent",
5 | "isUsingAlwaysUseSendBeacon",
6 | "buildContentImpressionRequest",
7 | "buildContentInteractionRequest",
8 | "buildContentInteractionRequestNode",
9 | "getContentImpressionsRequestsFromNodes",
10 | "getCurrentlyVisibleContentImpressionsRequestsIfNotTrackedYet",
11 | "trackCallbackOnLoad",
12 | "trackCallbackOnReady",
13 | "buildContentImpressionsRequests",
14 | "wasContentImpressionAlreadyTracked",
15 | "appendContentInteractionToRequestIfPossible",
16 | "setupInteractionsTracking",
17 | "trackContentImpressionClickInteraction",
18 | "internalIsNodeVisible",
19 | "isNodeAuthorizedToTriggerInteraction",
20 | "getDomains",
21 | "getConfigIdPageView",
22 | "getConfigDownloadExtensions",
23 | "enableTrackOnlyVisibleContent",
24 | "clearTrackedContentImpressions",
25 | "getTrackedContentImpressions",
26 | "clearEnableTrackOnlyVisibleContent",
27 | "disableLinkTracking",
28 | "getConfigVisitorCookieTimeout",
29 | "getConfigCookieSameSite",
30 | "removeAllAsyncTrackersButFirst",
31 | "getConsentRequestsQueue",
32 | "getRequestQueue",
33 | "unsetPageIsUnloading",
34 | "getRemainingVisitorCookieTimeout",
35 | "hasConsent",
36 | "getVisitorId",
37 | "getVisitorInfo",
38 | "getAttributionInfo",
39 | "getAttributionCampaignName",
40 | "getAttributionCampaignKeyword",
41 | "getAttributionReferrerTimestamp",
42 | "getAttributionReferrerUrl",
43 | "setTrackerUrl",
44 | "getTrackerUrl",
45 | "getMatomoUrl",
46 | "getPiwikUrl",
47 | "addTracker",
48 | "getSiteId",
49 | "setSiteId",
50 | "resetUserId",
51 | "setUserId",
52 | "setVisitorId",
53 | "getUserId",
54 | "setCustomData",
55 | "getCustomData",
56 | "setCustomRequestProcessing",
57 | "appendToTrackingUrl",
58 | "getRequest",
59 | "addPlugin",
60 | "setCustomDimension",
61 | "getCustomDimension",
62 | "deleteCustomDimension",
63 | "setCustomVariable",
64 | "getCustomVariable",
65 | "deleteCustomVariable",
66 | "deleteCustomVariables",
67 | "storeCustomVariablesInCookie",
68 | "setLinkTrackingTimer",
69 | "getLinkTrackingTimer",
70 | "setDownloadExtensions",
71 | "addDownloadExtensions",
72 | "removeDownloadExtensions",
73 | "setDomains",
74 | "enableCrossDomainLinking",
75 | "disableCrossDomainLinking",
76 | "isCrossDomainLinkingEnabled",
77 | "setCrossDomainLinkingTimeout",
78 | "getCrossDomainLinkingUrlParameter",
79 | "setIgnoreClasses",
80 | "setRequestMethod",
81 | "setRequestContentType",
82 | "setGenerationTimeMs",
83 | "setReferrerUrl",
84 | "setCustomUrl",
85 | "getCurrentUrl",
86 | "setDocumentTitle",
87 | "setAPIUrl",
88 | "setDownloadClasses",
89 | "setLinkClasses",
90 | "setCampaignNameKey",
91 | "setCampaignKeywordKey",
92 | "discardHashTag",
93 | "setCookieNamePrefix",
94 | "setCookieDomain",
95 | "getCookieDomain",
96 | "hasCookies",
97 | "setSessionCookie",
98 | "getCookie",
99 | "setCookiePath",
100 | "getCookiePath",
101 | "setVisitorCookieTimeout",
102 | "setSessionCookieTimeout",
103 | "getSessionCookieTimeout",
104 | "setReferralCookieTimeout",
105 | "setConversionAttributionFirstReferrer",
106 | "setSecureCookie",
107 | "setCookieSameSite",
108 | "disableCookies",
109 | "areCookiesEnabled",
110 | "setCookieConsentGiven",
111 | "requireCookieConsent",
112 | "getRememberedCookieConsent",
113 | "forgetCookieConsentGiven",
114 | "rememberCookieConsentGiven",
115 | "deleteCookies",
116 | "setDoNotTrack",
117 | "alwaysUseSendBeacon",
118 | "disableAlwaysUseSendBeacon",
119 | "addListener",
120 | "enableLinkTracking",
121 | "enableJSErrorTracking",
122 | "disablePerformanceTracking",
123 | "enableHeartBeatTimer",
124 | "disableHeartBeatTimer",
125 | "killFrame",
126 | "redirectFile",
127 | "setCountPreRendered",
128 | "trackGoal",
129 | "trackLink",
130 | "getNumTrackedPageViews",
131 | "trackPageView",
132 | "trackAllContentImpressions",
133 | "trackVisibleContentImpressions",
134 | "trackContentImpression",
135 | "trackContentImpressionsWithinNode",
136 | "trackContentInteraction",
137 | "trackContentInteractionNode",
138 | "logAllContentBlocksOnPage",
139 | "trackEvent",
140 | "trackSiteSearch",
141 | "setEcommerceView",
142 | "getEcommerceItems",
143 | "addEcommerceItem",
144 | "removeEcommerceItem",
145 | "clearEcommerceCart",
146 | "trackEcommerceOrder",
147 | "trackEcommerceCartUpdate",
148 | "trackRequest",
149 | "ping",
150 | "disableQueueRequest",
151 | "setRequestQueueInterval",
152 | "queueRequest",
153 | "isConsentRequired",
154 | "getRememberedConsent",
155 | "hasRememberedConsent",
156 | "requireConsent",
157 | "setConsentGiven",
158 | "rememberConsentGiven",
159 | "forgetConsentGiven",
160 | "isUserOptedOut",
161 | "optUserOut",
162 | "forgetUserOptOut"
163 | ]
--------------------------------------------------------------------------------
/lib/module.js:
--------------------------------------------------------------------------------
1 | import { resolve } from 'path'
2 |
3 | const defaults = {
4 | onMetaChange: false,
5 | debug: false,
6 | verbose: false,
7 | siteId: null,
8 | matomoUrl: null,
9 | trackerUrl: null,
10 | scriptUrl: null,
11 | cookies: true,
12 | consentRequired: false,
13 | consentExpires: 0,
14 | doNotTrack: false,
15 | blockLoading: false,
16 | addNoProxyWorkaround: true
17 | }
18 |
19 | export default function matomoModule (moduleOptions) {
20 | const options = Object.assign({}, defaults, moduleOptions)
21 |
22 | // do not enable in dev mode, unless debug is enabled or node-env is set to production
23 | if (this.options.dev && !options.debug && process.env.NODE_ENV !== 'production') {
24 | return
25 | }
26 |
27 | options.trackerUrl = options.trackerUrl || options.matomoUrl + 'piwik.php'
28 | options.scriptUrl = options.scriptUrl || options.matomoUrl + 'piwik.js'
29 |
30 | if (options.addNoProxyWorkaround) {
31 | options.apiMethodsList = options.apiMethodsList || require('./api-methods-list.json')
32 | }
33 |
34 | this.options.head.script.push({
35 | src: options.scriptUrl,
36 | body: true,
37 | defer: true,
38 | async: true
39 | })
40 |
41 | this.addTemplate({
42 | src: resolve(__dirname, 'utils.js'),
43 | fileName: 'matomo/utils.js',
44 | ssr: false
45 | })
46 |
47 | // register plugin
48 | this.addPlugin({
49 | src: resolve(__dirname, 'plugin.js'),
50 | fileName: 'matomo/plugin.js',
51 | ssr: false,
52 | options
53 | })
54 | }
55 |
--------------------------------------------------------------------------------
/lib/plugin.js:
--------------------------------------------------------------------------------
1 | import { debug, warn, isFn, waitUntil, routeOption } from './utils'<% if(isTest) { %>// eslint-disable-line no-unused-vars<% } %>
2 |
3 | <% if (!isDev && options.debug) consola.warn('nuxt-matomo debug is enabled') %>
4 |
5 | export default <%= options.blockLoading ? 'async ' : ''%>(context, inject) => {
6 | const { app: { router, store } } = context
7 |
8 | <% if (options.blockLoading) { %>
9 | await waitUntil(() => window.Piwik)
10 | const tracker = createTracker()
11 | if (!tracker) return
12 | <% } else { %>
13 | let tracker
14 | if (window.Piwik) {
15 | tracker = createTracker()
16 | } else {
17 | // if window.Piwik is not (yet) available, add a Proxy which delays calls
18 | // to the tracker and execute them once the Piwik tracker becomes available
19 | let _tracker // The real Piwik tracker
20 | let delayedCalls = []
21 | const proxyTrackerCall = (fnName, ...args) => {
22 | if (_tracker) {
23 | return _tracker[fnName](...args)
24 | }
25 |
26 | <% if(debug || options.debug) { %>
27 | debug(`Delaying call to tracker: ${fnName}`)
28 | <% } %>
29 | delayedCalls.push([fnName, ...args])
30 | }
31 |
32 | if (typeof Proxy === 'function') {
33 | // Create a Proxy for any tracker property (IE11+)
34 | tracker = new Proxy({}, {
35 | get (target, key) {
36 | return (...args) => proxyTrackerCall(key, ...args)
37 | }
38 | })
39 | <% if (options.addNoProxyWorkaround) { %>
40 | } else {
41 | tracker = {};
42 | <%= JSON.stringify(options.apiMethodsList, null, 8).replace(/"/g, "'").replace(']', ' ]') %>.forEach((fnName) => {
43 | // IE9/10 dont support Proxies, create a proxy map for known api methods
44 | tracker[fnName] = (...args) => proxyTrackerCall(fnName, ...args)
45 | })
46 | <% } %>
47 | }
48 |
49 | <% if(debug || options.debug) { %>
50 | // Log a warning when piwik doesnt become available within 10s (in debug mode)
51 | const hasPiwikCheck = setTimeout(() => {
52 | if (!window.Piwik) {
53 | debug(`window.Piwik was not set within timeout`)
54 | }
55 | }, 10000)
56 | <% } %>
57 |
58 | // Use a getter/setter to know when window.Piwik becomes available
59 | let _windowPiwik
60 | Object.defineProperty(window, 'Piwik', {
61 | configurable: true,
62 | enumerable: true,
63 | get () {
64 | return _windowPiwik
65 | },
66 | set (newVal) {
67 | <% if(debug || options.debug) { %>
68 | clearTimeout(hasPiwikCheck)
69 | if (_windowPiwik) {
70 | debug(`window.Piwik is already defined`)
71 | }
72 | <% } %>
73 |
74 | _windowPiwik = newVal
75 | _tracker = createTracker(delayedCalls)
76 | delayedCalls = undefined
77 | }
78 | })
79 | }
80 | <% } %>
81 |
82 | // inject tracker into app & context
83 | context.$matomo = tracker
84 | inject('matomo', tracker)
85 |
86 | <% if(options.onMetaChange) { %>
87 | // onMetaChange setup
88 | let trackOnMetaChange
89 | <% if(debug || options.debug) { %>
90 | let metaChangeTimeout
91 | <% } %><% } %>
92 |
93 | // define base url
94 | const baseUrl = window.location.protocol +
95 | (window.location.protocol.slice(-1) === ':' ? '' : ':') +
96 | '//' +
97 | window.location.host +
98 | router.options.base.replace(/\/+$/, '')
99 |
100 | const trackRoute = ({ to, componentOption }) => {
101 | <% if(options.onMetaChange) { %>
102 | tracker.setDocumentTitle(document.title)
103 | <% } else { %>
104 | // we might not know the to's page title in vue-router.afterEach, DOM is updated _after_ afterEach
105 | tracker.setDocumentTitle(to.path)
106 | <% } %>
107 | tracker.setCustomUrl(baseUrl + to.fullPath)
108 |
109 | // allow override page settings
110 | const settings = Object.assign(
111 | {},
112 | context.route.meta && context.route.meta.matomo,
113 | componentOption
114 | )
115 |
116 | for (const key in settings) {
117 | const setting = settings[key]
118 | const fn = setting.shift()
119 | if (isFn(tracker[fn])) {
120 | <% if(debug || options.debug) { %>
121 | debug(`Calling matomo.${fn} with args ${JSON.stringify(setting)}`)
122 | <% } %>
123 | tracker[fn].call(null, ...setting)
124 | <% if(debug || options.debug) { %>
125 | } else {
126 | debug(`Unknown matomo function ${fn} with args ${JSON.stringify(setting)}`)
127 | <% } %>
128 | }
129 | }
130 |
131 | <% if(debug || options.debug) { %>
132 | debug(`Tell matomo to track pageview ${to.fullPath}`, document.title)
133 |
134 | <% } %>
135 | // tell Matomo to add a page view (doesnt do anything if tracker is disabled)
136 | tracker.trackPageView()
137 | }
138 |
139 | <% if(options.onMetaChange) { %>
140 | // listen on vue-meta's changed event
141 | const changed = context.app.head.changed
142 | context.app.head.changed = (...args) => {
143 | <% if(debug || options.debug) { %>
144 | clearTimeout(metaChangeTimeout)
145 | console.log
146 | if (!args[0].title) {
147 | warn(`title was updated but empty for ${trackOnMetaChange && trackOnMetaChange.to.fullPath || 'unknown route'}`)
148 | }
149 | <% } %>
150 |
151 | if (trackOnMetaChange) {
152 | trackRoute(trackOnMetaChange)
153 | trackOnMetaChange = null
154 | }
155 |
156 | if (changed && isFn(changed)) {
157 | changed.call(null, ...args)
158 | }
159 | }
160 | <% } %>
161 |
162 | // every time the route changes (fired on initialization too)
163 | router.afterEach((to, from) => {
164 | const componentOption = routeOption('matomo', tracker, from, to, store)
165 | if (componentOption === false) {
166 | <% if(debug || options.debug) { %>
167 | debug(`Component option returned false, wont (automatically) track pageview ${to.fullPath}`)
168 | <% } %>
169 | return
170 | }
171 |
172 | <% if(options.onMetaChange) { %>
173 | if (trackOnMetaChange === undefined) {
174 | // track on initialization
175 | trackRoute({ to, componentOption })
176 | trackOnMetaChange = null
177 | } else {
178 | trackOnMetaChange = { to, componentOption }
179 |
180 | <% if(debug || options.debug) { %>
181 | // set a timeout to track pages without a title/meta update
182 | metaChangeTimeout = setTimeout(() => {
183 | warn(`vue-meta's changed event was not triggered for ${to.fullPath}'`)
184 | }, 500)
185 | <% } %>
186 | }
187 | <% } else { %>
188 | trackRoute({ to, componentOption })
189 | <% } %>
190 | })
191 | }
192 |
193 | function createTracker (delayedCalls = []) {
194 | if (!window.Piwik) {
195 | <% if(debug || options.debug) { %>
196 | debug(`window.Piwik not initialized, unable to create a tracker`)
197 | <% } %>
198 | return
199 | }
200 |
201 | const tracker = window.Piwik.getTracker('<%= options.trackerUrl %>', '<%= options.siteId %>')
202 |
203 | // extend tracker
204 | tracker.setConsent = (val) => {
205 | if (val || val === undefined) {
206 | <% if(options.consentExpires > 0) { %>
207 | tracker.rememberConsentGiven(<%= options.consentExpires %>)
208 | <% } else { %>
209 | tracker.setConsentGiven()
210 | <% } %>
211 | } else {
212 | tracker.forgetConsentGiven()
213 | }
214 | }
215 |
216 | <% if(debug || options.debug) { %>
217 | debug(`Created tracker for siteId <%= options.siteId %> to <%= options.trackerUrl %>`)
218 | <% if(options.verbose) { %>
219 | // wrap all Piwik functions for verbose logging
220 | Object.keys(tracker).forEach((key) => {
221 | const fn = tracker[key]
222 | if (isFn(fn)) {
223 | tracker[key] = (...args) => {
224 | debug(`Calling tracker.${key} with args ${JSON.stringify(args)}`)
225 | return fn.call(tracker, ...args)
226 | }
227 | }
228 | })
229 | <% } %><% } %>
230 |
231 | <% if(options.cookies === false) { %>
232 | tracker.disableCookies()
233 | <% } %>
234 | <% if(options.consentRequired !== false) { %>
235 | tracker.requireConsent()
236 | <% } %>
237 | <% if(options.doNotTrack !== false) { %>
238 | tracker.setDoNotTrack(true)
239 | <% } %>
240 |
241 | while (delayedCalls.length) {
242 | const [fnName, ...args] = delayedCalls.shift()
243 | if (isFn(tracker[fnName])) {
244 | <% if(debug || options.debug) { %>
245 | debug(`Calling delayed ${fnName} on tracker`)
246 | <% } %>
247 | tracker[fnName](...args)
248 | }
249 | }
250 |
251 | return tracker
252 | }
253 |
--------------------------------------------------------------------------------
/lib/utils.js:
--------------------------------------------------------------------------------
1 |
2 | export function debug (msg) {
3 | console.debug(`[nuxt-matomo] ${msg}`)
4 | }
5 |
6 | export function warn (msg) {
7 | console.warn(`[nuxt-matomo] ${msg}`)
8 | }
9 |
10 | export function isFn (fn) {
11 | return typeof fn === 'function'
12 | }
13 |
14 | export function waitFor (time) {
15 | return new Promise(resolve => setTimeout(resolve, time || 0))
16 | }
17 |
18 | export async function waitUntil (condition, timeout = 10000, interval = 10) {
19 | let duration = 0
20 | while (!(isFn(condition) ? condition() : condition)) {
21 | await waitFor(interval)
22 | duration += interval
23 |
24 | if (duration >= timeout) {
25 | break
26 | }
27 | }
28 | }
29 |
30 | export function routeOption (key, thisArg, from, to, ...args) {
31 | const matched = to.matched[0]
32 | const matchedComponent = matched.components.default
33 | return componentOption(matchedComponent, key, thisArg, from, to, ...args)
34 | }
35 |
36 | export function componentOption (component, key, thisArg, ...args) {
37 | if (!component || !component.options || component.options[key] === undefined) {
38 | return null
39 | }
40 |
41 | const option = component.options[key]
42 | return isFn(option) ? option.call(thisArg, ...args) : option
43 | }
44 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "nuxt-matomo",
3 | "version": "1.2.4",
4 | "license": "MIT",
5 | "description": "Matomo analytics for Nuxt.js",
6 | "repository": "https://github.com/pimlie/nuxt-matomo",
7 | "homepage": "https://github.com/pimlie/nuxt-matomo",
8 | "main": "lib/module.js",
9 | "keywords": [
10 | "matomo",
11 | "piwik",
12 | "analytics",
13 | "nuxt",
14 | "nuxt.js",
15 | "nuxtjs"
16 | ],
17 | "files": [
18 | "lib"
19 | ],
20 | "scripts": {
21 | "build": "nuxt build test/fixtures/basic",
22 | "dev": "nuxt test/fixtures/meta-changed",
23 | "lint": "yarn lint:lib && yarn lint:matomo",
24 | "lint:lib": "eslint --ext .js,.vue .",
25 | "lint:matomo": "if [ $(node -e \"console.log(require('fs').existsSync('./test/fixtures/basic/.nuxt/matomo'));\") = \"false\" ]; then yarn test:fixtures; fi && eslint --no-ignore test/fixtures/basic/.nuxt/matomo/",
26 | "release": "yarn lint && yarn test && standard-version",
27 | "test": "yarn test:fixtures && yarn test:e2e",
28 | "test:fixtures": "jest test/fixtures",
29 | "test:e2e": "jest test/e2e",
30 | "download-matomo": "wget -O test/utils/piwik.js https://raw.githubusercontent.com/matomo-org/matomo/master/js/piwik.js",
31 | "create-matomo-api-list": "yarn download-matomo && node ./scripts/createApiMethodsList.js"
32 | },
33 | "publishConfig": {
34 | "access": "public"
35 | },
36 | "devDependencies": {
37 | "@babel/core": "^7.12.10",
38 | "@babel/plugin-syntax-dynamic-import": "^7.8.3",
39 | "@babel/preset-env": "^7.12.11",
40 | "@nuxtjs/eslint-config": "^5.0.0",
41 | "babel-core": "7.0.0-bridge.0",
42 | "babel-eslint": "^10.1.0",
43 | "babel-jest": "^26.6.3",
44 | "eslint": "^7.19.0",
45 | "eslint-config-standard": "^16.0.2",
46 | "eslint-plugin-html": "^6.1.1",
47 | "eslint-plugin-import": "^2.22.1",
48 | "eslint-plugin-jest": "^24.1.3",
49 | "eslint-plugin-node": "^11.1.0",
50 | "eslint-plugin-promise": "^4.2.1",
51 | "eslint-plugin-standard": "^5.0.0",
52 | "eslint-plugin-vue": "^7.5.0",
53 | "get-port": "^5.1.1",
54 | "jest": "^26.6.3",
55 | "jsdom": "^15.1.1",
56 | "nuxt": "^2.14.12",
57 | "puppeteer": "^5.5.0",
58 | "standard-version": "^9.1.0"
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/scripts/createApiMethodsList.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | const fs = require('fs')
4 | const path = require('path')
5 | const { JSDOM, ResourceLoader, VirtualConsole } = require('jsdom')
6 |
7 | class MatomoResourceLoader extends ResourceLoader {
8 | fetch (url, options) {
9 | // Override the contents of this script to do something unusual.
10 | if (url.endsWith('piwik.js')) {
11 | return Promise.resolve(Buffer.from(fs.readFileSync(path.resolve(__dirname, '../test/utils/piwik.js'), 'utf8')))
12 | }
13 |
14 | return super.fetch(url, options)
15 | }
16 | }
17 |
18 | const { window } = new JSDOM('', {
19 | pretendToBeVisual: true,
20 | runScripts: 'dangerously',
21 | resources: new MatomoResourceLoader(),
22 | virtualConsole: new VirtualConsole().sendTo(console)
23 | })
24 |
25 | window.document.addEventListener('DOMContentLoaded', () => {
26 | const tracker = window.Piwik.getTracker('', 1)
27 |
28 | const fns = []
29 | Object.keys(tracker).forEach((fn) => {
30 | if (typeof tracker[fn] === 'function') {
31 | fns.push(fn)
32 | }
33 | })
34 |
35 | fs.writeFileSync(path.resolve(__dirname, '../lib/api-methods-list.json'), JSON.stringify(fns, null, 2))
36 | })
37 |
--------------------------------------------------------------------------------
/test/e2e/basic.test.js:
--------------------------------------------------------------------------------
1 | import { URL } from 'url'
2 | import { Nuxt, getPort, waitFor, waitUntil, expectParams } from '../utils'
3 | import Browser from '../utils/browser'
4 |
5 | let port
6 | const browser = new Browser()
7 | const url = route => `http://localhost:${port}${route}`
8 |
9 | describe('matomo analytics', () => {
10 | let nuxt
11 | let page
12 | let matomoUrl = []
13 | const createTrackerMsg = 'Created tracker for siteId 1 to ./piwik.php'
14 |
15 | beforeAll(async () => {
16 | const config = require('../fixtures/basic/nuxt.config')
17 | nuxt = new Nuxt(config)
18 |
19 | port = await getPort()
20 | await nuxt.server.listen(port, 'localhost')
21 | await browser.start({})
22 |
23 | console.debug = jest.fn()
24 | console.warn = jest.fn()
25 |
26 | nuxt.hook('render:route', (url, result, context) => {
27 | if (url.includes('piwik.php')) {
28 | matomoUrl.push(new URL(url, `http://localhost:${port}`))
29 | }
30 | })
31 | })
32 |
33 | afterAll(async () => {
34 | await nuxt.close()
35 | await browser.close()
36 | })
37 |
38 | afterEach(() => {
39 | console.debug.mockClear()
40 | console.warn.mockClear()
41 | })
42 |
43 | test('matomo is triggered on page load', async () => {
44 | matomoUrl = []
45 | const pageUrl = '/'
46 | page = await browser.page(url(pageUrl))
47 | await waitUntil(() => matomoUrl.length >= 1)
48 |
49 | expect(console.debug).toHaveBeenCalledWith(expect.stringMatching(createTrackerMsg))
50 | expect(console.debug).toHaveBeenCalledWith(expect.stringMatching(`to track pageview ${pageUrl}`))
51 |
52 | expect(await page.$text('h1')).toBe('index')
53 |
54 | expectParams(matomoUrl[0].searchParams, {
55 | idsite: '1',
56 | action_name: pageUrl
57 | })
58 | })
59 |
60 | test('cookies have been set', async () => {
61 | const cookies = await page.cookies()
62 |
63 | expect(cookies[0].name).toEqual(expect.stringMatching('_pk_ses.1.'))
64 | expect(cookies[1].name).toEqual(expect.stringMatching('_pk_id.1.'))
65 | })
66 |
67 | test('matomo is triggered on navigation', async () => {
68 | matomoUrl = []
69 | const pageUrl = '/middleware'
70 | await page.nuxt.navigate(pageUrl)
71 | await waitUntil(() => matomoUrl.length >= 1)
72 |
73 | expect(console.debug).not.toHaveBeenCalledWith(expect.stringMatching(createTrackerMsg))
74 | expect(console.debug).toHaveBeenCalledWith(expect.stringMatching(`to track pageview ${pageUrl}`))
75 |
76 | expect(await page.$text('h1')).toBe('middleware')
77 |
78 | expectParams(matomoUrl[0].searchParams, {
79 | idsite: '1',
80 | action_name: pageUrl
81 | })
82 | })
83 |
84 | test('route.meta from global middleware is used', () => {
85 | expectParams(matomoUrl[0].searchParams, {
86 | cvar: [
87 | ['VisitorType', 'A'],
88 | ['OtherType', 'true']
89 | ]
90 | })
91 | })
92 |
93 | test('matomo prop defined in page component is used', async () => {
94 | matomoUrl = []
95 | const pageUrl = '/component-prop'
96 | await page.nuxt.navigate(pageUrl)
97 | await waitUntil(() => matomoUrl.length >= 1)
98 |
99 | expect(await page.$text('h1')).toBe('component prop')
100 |
101 | expectParams(matomoUrl[0].searchParams, {
102 | idsite: '1',
103 | action_name: pageUrl,
104 | cvar: [
105 | ['VisitorType', 'B'],
106 | ['OtherType', 'true']
107 | ]
108 | })
109 | })
110 |
111 | test('matomo function defined in page component is used', async () => {
112 | matomoUrl = []
113 | const pageUrl = '/component-fn'
114 | await page.nuxt.navigate(pageUrl)
115 | await waitUntil(() => matomoUrl.length >= 1)
116 |
117 | expect(await page.$text('h1')).toBe('component fn')
118 |
119 | expectParams(matomoUrl[0].searchParams, {
120 | idsite: '1',
121 | action_name: pageUrl,
122 | cvar: [
123 | ['VisitorType', 'C'],
124 | ['OtherType', 'true']
125 | ]
126 | })
127 | })
128 |
129 | test('tracker is injected and can be used', async () => {
130 | matomoUrl = []
131 | const pageUrl = '/injected'
132 | await page.nuxt.navigate(pageUrl)
133 | await waitUntil(() => matomoUrl.length >= 2)
134 |
135 | expect(console.debug).not.toHaveBeenCalledWith(expect.stringMatching(createTrackerMsg))
136 | expect(console.debug).toHaveBeenCalledWith(expect.stringMatching(`to track pageview ${pageUrl}`))
137 |
138 | expect(await page.$text('h1')).toBe('injected')
139 |
140 | expectParams(matomoUrl[0].searchParams, {
141 | idsite: '1',
142 | action_name: pageUrl,
143 | cvar: [
144 | ['VisitorType', 'C'],
145 | ['OtherType', 'true']
146 | ]
147 | })
148 |
149 | expectParams(matomoUrl[1].searchParams, {
150 | idsite: '1',
151 | download: 'file'
152 | })
153 | })
154 |
155 | test('can disable automatic tracking to track manually', async () => {
156 | matomoUrl = []
157 | const pageUrl = '/manuallytracked'
158 | await page.nuxt.navigate(pageUrl)
159 | await waitUntil(() => matomoUrl.length >= 1)
160 | await waitFor(100) // wait a bit more
161 |
162 | expect(console.debug).not.toHaveBeenCalledWith(expect.stringMatching(createTrackerMsg))
163 | expect(console.debug).toHaveBeenCalledWith(expect.stringMatching(`wont \\(automatically\\) track pageview ${pageUrl}`))
164 |
165 | expect(await page.$text('h1')).toBe('manually tracked')
166 |
167 | expect(matomoUrl.length).toBe(1)
168 | expectParams(matomoUrl[0].searchParams, {
169 | idsite: '1',
170 | action_name: 'manually tracked',
171 | cvar: [
172 | ['VisitorType', 'C'],
173 | ['OtherType', 'true']
174 | ]
175 | })
176 | })
177 |
178 | test('does not track when consent is required', async () => {
179 | matomoUrl = []
180 | const pageUrl = '/consent'
181 | await page.nuxt.navigate(pageUrl)
182 | await waitFor(250) // wait a bit
183 |
184 | expect(console.debug).not.toHaveBeenCalledWith(expect.stringMatching(createTrackerMsg))
185 | expect(console.debug).toHaveBeenCalledWith(expect.stringMatching(`to track pageview ${pageUrl}`))
186 |
187 | expect(await page.$text('h1')).toBe('consent')
188 | expect(matomoUrl.length).toBe(0)
189 | })
190 |
191 | test('still does not track when consent is required', async () => {
192 | matomoUrl = []
193 | const pageUrl = '/'
194 | await page.nuxt.navigate(pageUrl)
195 | await waitFor(250) // wait a bit
196 |
197 | expect(console.debug).toHaveBeenCalledWith(expect.stringMatching(`to track pageview ${pageUrl}`))
198 |
199 | expect(await page.$text('h1')).toBe('index')
200 | expect(matomoUrl.length).toBe(0)
201 | })
202 |
203 | test('tracking is triggered once consent is given', async () => {
204 | matomoUrl = []
205 |
206 | await page.evaluate($nuxt => $nuxt.$store.commit('matomo/consented'), page.$nuxt)
207 | const store = await page.nuxt.storeState()
208 | expect(store.matomo.consented).toBe(true)
209 |
210 | await waitUntil(() => matomoUrl.length >= 1)
211 |
212 | expect(console.debug).not.toHaveBeenCalled()
213 |
214 | expectParams(matomoUrl[0].searchParams, {
215 | idsite: '1',
216 | action_name: '/',
217 | cvar: [
218 | ['VisitorType', 'A'],
219 | ['OtherType', 'true']
220 | ]
221 | })
222 | })
223 | })
224 |
--------------------------------------------------------------------------------
/test/e2e/meta-changed.test.js:
--------------------------------------------------------------------------------
1 | import { URL } from 'url'
2 | import { Nuxt, getPort, waitUntil, expectParams } from '../utils'
3 | import Browser from '../utils/browser'
4 |
5 | let port
6 | const browser = new Browser()
7 | const url = route => `http://localhost:${port}/app${route}`
8 |
9 | describe('matomo analytics', () => {
10 | let nuxt
11 | let page
12 | let matomoUrl = []
13 | const createTrackerMsg = 'Created tracker for siteId 2 to ./piwik.php'
14 |
15 | beforeAll(async () => {
16 | const config = require('../fixtures/meta-changed/nuxt.config')
17 | nuxt = new Nuxt(config)
18 |
19 | port = await getPort()
20 | await nuxt.server.listen(port, 'localhost')
21 | await browser.start({})
22 |
23 | console.debug = jest.fn()
24 | console.warn = jest.fn()
25 |
26 | nuxt.hook('render:route', (url, result, context) => {
27 | if (url.includes('piwik.php')) {
28 | matomoUrl.push(new URL(url, `http://localhost:${port}`))
29 | }
30 | })
31 | })
32 |
33 | afterAll(async () => {
34 | await nuxt.close()
35 | await browser.close()
36 | })
37 |
38 | afterEach(() => {
39 | console.debug.mockClear()
40 | console.warn.mockClear()
41 | matomoUrl = []
42 | })
43 |
44 | test('matomo is triggered on page load', async () => {
45 | const pagePath = '/page1'
46 | const pageUrl = url(pagePath)
47 | page = await browser.page(pageUrl)
48 | await waitUntil(() => matomoUrl.length >= 1)
49 |
50 | expect(matomoUrl.length).toBe(1)
51 |
52 | expect(console.debug).toHaveBeenCalledWith(expect.stringMatching(createTrackerMsg))
53 | expect(console.debug).toHaveBeenCalledWith(expect.stringMatching(`to track pageview ${pagePath}`))
54 |
55 | expect(await page.$text('h1')).toBe('page1')
56 |
57 | expectParams(matomoUrl[0].searchParams, {
58 | idsite: '2',
59 | action_name: 'page1',
60 | url: `${pageUrl}`
61 | })
62 | })
63 |
64 | test('matomo is triggered on navigation', async () => {
65 | const pageUrl = '/page2'
66 | await page.nuxt.navigate(pageUrl)
67 | await waitUntil(() => matomoUrl.length >= 1)
68 | expect(matomoUrl.length).toBe(1)
69 |
70 | expect(console.debug).not.toHaveBeenCalledWith(expect.stringMatching(createTrackerMsg))
71 | expect(console.debug).toHaveBeenCalledWith(expect.stringMatching(`to track pageview ${pageUrl}`))
72 |
73 | expect(await page.$text('h1')).toBe('page2')
74 |
75 | expectParams(matomoUrl[0].searchParams, {
76 | idsite: '2',
77 | action_name: 'page2'
78 | })
79 | })
80 |
81 | test('warns on empty title', async () => {
82 | const pageUrl = '/notitle'
83 | await page.nuxt.navigate(pageUrl)
84 | await waitUntil(() => matomoUrl.length >= 1)
85 | expect(matomoUrl.length).toBe(1)
86 |
87 | expect(console.debug).not.toHaveBeenCalledWith(expect.stringMatching(createTrackerMsg))
88 | expect(console.debug).toHaveBeenCalledWith(expect.stringMatching(`to track pageview ${pageUrl}`))
89 | expect(console.warn).toHaveBeenCalledWith(expect.stringMatching(`title was updated but empty for ${pageUrl}`))
90 |
91 | expect(await page.$text('h1')).toBe('notitle')
92 |
93 | expectParams(matomoUrl[0].searchParams, {
94 | idsite: '2',
95 | action_name: ''
96 | })
97 | })
98 |
99 | test('warns on meta changed timeout (in debug, test setup)', async () => {
100 | const pageUrl = '/noupdate1'
101 | await page.nuxt.navigate(pageUrl)
102 | await waitUntil(() => matomoUrl.length >= 1)
103 | expect(matomoUrl.length).toBe(1)
104 |
105 | expect(console.debug).not.toHaveBeenCalledWith(expect.stringMatching(createTrackerMsg))
106 | expect(console.debug).toHaveBeenCalledWith(expect.stringMatching(`to track pageview ${pageUrl}`))
107 |
108 | expect(await page.$text('h1')).toBe('noupdate1')
109 | })
110 |
111 | test('warns on meta changed timeout (in debug, the test)', async () => {
112 | const pageUrl = '/noupdate2'
113 | await page.nuxt.navigate(pageUrl)
114 | await waitUntil(() => matomoUrl.length >= 1, 2000)
115 | expect(matomoUrl.length).toBe(0)
116 |
117 | expect(console.debug).not.toHaveBeenCalledWith(expect.stringMatching(createTrackerMsg))
118 | expect(console.debug).not.toHaveBeenCalledWith(expect.stringMatching(`to track pageview ${pageUrl}`))
119 | expect(console.warn).toHaveBeenCalledWith(expect.stringMatching(`changed event was not triggered for ${pageUrl}`))
120 |
121 | expect(await page.$text('h1')).toBe('noupdate2')
122 | })
123 | })
124 |
--------------------------------------------------------------------------------
/test/fixtures/basic/basic.test.js:
--------------------------------------------------------------------------------
1 | import { Nuxt, Builder } from '../../utils'
2 |
3 | describe('Build fixture', () => {
4 | let nuxt
5 | let builder
6 | let buildDone
7 |
8 | beforeAll(async () => {
9 | const config = require('./nuxt.config')
10 | nuxt = new Nuxt(config)
11 |
12 | buildDone = jest.fn()
13 |
14 | nuxt.hook('build:done', buildDone)
15 | builder = await new Builder(nuxt).build()
16 | })
17 |
18 | test('correct build status', () => {
19 | expect(builder._buildStatus).toBe(2)
20 | })
21 |
22 | test('build:done hook called', () => {
23 | expect(buildDone).toHaveBeenCalledTimes(1)
24 | })
25 | })
26 |
--------------------------------------------------------------------------------
/test/fixtures/basic/layouts/default.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
19 |
--------------------------------------------------------------------------------
/test/fixtures/basic/middleware/matomo.js:
--------------------------------------------------------------------------------
1 | export default ({ route }) => {
2 | if (route.name && !['index', 'injected'].includes(route.name)) {
3 | route.meta.matomo = {
4 | someVar1: ['setCustomVariable', 1, 'VisitorType', 'A', 'page'],
5 | someVar2: ['setCustomVariable', 2, 'OtherType', true, 'page']
6 | }
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/test/fixtures/basic/nuxt.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | rootDir: __dirname,
3 | dev: false,
4 | router: {
5 | middleware: 'matomo'
6 | },
7 | modules: [
8 | ['@/../../../', {
9 | debug: true,
10 | siteId: 1,
11 | matomoUrl: './'
12 | }]
13 | ],
14 | matomoLoadDelay: 5000,
15 | build: {
16 | terser: false
17 | },
18 | hooks: {
19 | render: {
20 | before: (server, render) => {
21 | server.app.use((req, res, next) => {
22 | if (server.options.matomoLoadDelay && req.originalUrl.endsWith('piwik.js')) {
23 | setTimeout(next, server.options.matomoLoadDelay)
24 | } else {
25 | next()
26 | }
27 | })
28 | }
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/test/fixtures/basic/pages/component-fn.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
component fn
4 |
5 |
6 |
7 |
16 |
--------------------------------------------------------------------------------
/test/fixtures/basic/pages/component-prop.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
component prop
4 |
5 |
6 |
7 |
14 |
--------------------------------------------------------------------------------
/test/fixtures/basic/pages/consent.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
consent
4 |
5 |
6 |
7 |
19 |
--------------------------------------------------------------------------------
/test/fixtures/basic/pages/headfn.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
headfn
4 | home
5 |
6 |
7 |
8 |
23 |
--------------------------------------------------------------------------------
/test/fixtures/basic/pages/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
index
4 |
5 | middleware
6 | headfn
7 | manually tracked
8 | injected
9 |
10 |
11 |
--------------------------------------------------------------------------------
/test/fixtures/basic/pages/injected.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
injected
4 | home
5 |
6 |
7 |
8 |
15 |
--------------------------------------------------------------------------------
/test/fixtures/basic/pages/manuallytracked.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
manually tracked
4 | home
5 |
6 |
7 |
8 |
29 |
--------------------------------------------------------------------------------
/test/fixtures/basic/pages/middleware.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
middleware
4 | home
5 |
6 |
7 |
8 |
15 |
--------------------------------------------------------------------------------
/test/fixtures/basic/static/piwik.js:
--------------------------------------------------------------------------------
1 | ../../../utils/piwik.js
--------------------------------------------------------------------------------
/test/fixtures/basic/store/matomo.js:
--------------------------------------------------------------------------------
1 | export const state = () => ({
2 | cookies: true,
3 | consented: false
4 | })
5 |
6 | export const mutations = {
7 | cookies (state, noCookies) {
8 | state.cookies = !noCookies
9 | },
10 | consented (state, noConsent) {
11 | state.consented = !noConsent
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/test/fixtures/meta-changed/meta-changed.test.js:
--------------------------------------------------------------------------------
1 | import { Nuxt, Builder } from '../../utils'
2 |
3 | describe('Build fixture', () => {
4 | let nuxt
5 | let builder
6 | let buildDone
7 |
8 | beforeAll(async () => {
9 | const config = require('./nuxt.config')
10 | nuxt = new Nuxt(config)
11 |
12 | buildDone = jest.fn()
13 |
14 | nuxt.hook('build:done', buildDone)
15 | builder = new Builder(nuxt)
16 | await builder.build()
17 | })
18 |
19 | test('correct build status', () => {
20 | expect(builder._buildStatus).toBe(2)
21 | })
22 |
23 | test('build:done hook called', () => {
24 | expect(buildDone).toHaveBeenCalledTimes(1)
25 | })
26 | })
27 |
--------------------------------------------------------------------------------
/test/fixtures/meta-changed/nuxt.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | rootDir: __dirname,
3 | dev: false,
4 | router: {
5 | base: '/app/'
6 | },
7 | build: {
8 | terser: false
9 | },
10 | modules: [
11 | ['@/../../../', {
12 | onMetaChange: true,
13 | debug: true,
14 | siteId: 2,
15 | matomoUrl: './'
16 | }]
17 | ]
18 | }
19 |
--------------------------------------------------------------------------------
/test/fixtures/meta-changed/pages/notitle.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
notitle
4 |
5 |
6 |
7 |
14 |
--------------------------------------------------------------------------------
/test/fixtures/meta-changed/pages/noupdate1.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
noupdate1
4 |
5 |
6 |
--------------------------------------------------------------------------------
/test/fixtures/meta-changed/pages/noupdate2.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
noupdate2
4 |
5 |
6 |
--------------------------------------------------------------------------------
/test/fixtures/meta-changed/pages/page1.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
page1
4 | to page2
5 |
6 |
7 |
8 |
15 |
--------------------------------------------------------------------------------
/test/fixtures/meta-changed/pages/page2.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
page2
4 | to page1
5 |
6 |
7 |
8 |
17 |
--------------------------------------------------------------------------------
/test/fixtures/meta-changed/static/piwik.js:
--------------------------------------------------------------------------------
1 | ../../../utils/piwik.js
--------------------------------------------------------------------------------
/test/utils/browser.js:
--------------------------------------------------------------------------------
1 | import puppeteer from 'puppeteer'
2 |
3 | export default class Browser {
4 | async start (options = {}) {
5 | // https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#puppeteerlaunchoptions
6 | this.browser = await puppeteer.launch(
7 | Object.assign(
8 | {
9 | args: ['--no-sandbox', '--disable-setuid-sandbox'],
10 | executablePath: process.env.PUPPETEER_EXECUTABLE_PATH
11 | },
12 | options
13 | )
14 | )
15 | }
16 |
17 | async close () {
18 | if (!this.browser) { return }
19 | await this.browser.close()
20 | }
21 |
22 | async page (url, globalName = 'nuxt') {
23 | if (!this.browser) { throw new Error('Please call start() before page(url)') }
24 | const page = await this.browser.newPage()
25 |
26 | // pass on console messages
27 | const typeMap = {
28 | debug: 'debug',
29 | warning: 'warn'
30 | }
31 | page.on('console', (msg) => {
32 | if (typeMap[msg.type()]) {
33 | console[typeMap[msg.type()]](msg.text()) // eslint-disable-line no-console
34 | }
35 | })
36 |
37 | await page.goto(url)
38 | page.$nuxtGlobalHandle = `window.$${globalName}`
39 | await page.waitForFunction(`!!${page.$nuxtGlobalHandle}`)
40 | page.html = () =>
41 | page.evaluate(() => window.document.documentElement.outerHTML)
42 | page.$text = (selector, trim) => page.$eval(selector, (el, trim) => {
43 | return trim ? el.textContent.replace(/^\s+|\s+$/g, '') : el.textContent
44 | }, trim)
45 | page.$$text = (selector, trim) =>
46 | page.$$eval(selector, (els, trim) => els.map((el) => {
47 | return trim ? el.textContent.replace(/^\s+|\s+$/g, '') : el.textContent
48 | }), trim)
49 | page.$attr = (selector, attr) =>
50 | page.$eval(selector, (el, attr) => el.getAttribute(attr), attr)
51 | page.$$attr = (selector, attr) =>
52 | page.$$eval(
53 | selector,
54 | (els, attr) => els.map(el => el.getAttribute(attr)),
55 | attr
56 | )
57 |
58 | page.$nuxt = await page.evaluateHandle(page.$nuxtGlobalHandle)
59 |
60 | page.nuxt = {
61 | async navigate (path, waitEnd = true) {
62 | const hook = page.evaluate(`
63 | new Promise(resolve =>
64 | ${page.$nuxtGlobalHandle}.$once('routeChanged', resolve)
65 | ).then(() => new Promise(resolve => setTimeout(resolve, 50)))
66 | `)
67 | await page.evaluate(
68 | ($nuxt, path) => $nuxt.$router.push(path),
69 | page.$nuxt,
70 | path
71 | )
72 | if (waitEnd) {
73 | await hook
74 | }
75 | return { hook }
76 | },
77 | routeData () {
78 | return page.evaluate(($nuxt) => {
79 | return {
80 | path: $nuxt.$route.path,
81 | query: $nuxt.$route.query
82 | }
83 | }, page.$nuxt)
84 | },
85 | loadingData () {
86 | return page.evaluate($nuxt => $nuxt.$loading.$data, page.$nuxt)
87 | },
88 | errorData () {
89 | return page.evaluate($nuxt => $nuxt.nuxt.err, page.$nuxt)
90 | },
91 | storeState () {
92 | return page.evaluate($nuxt => $nuxt.$store.state, page.$nuxt)
93 | }
94 | }
95 |
96 | return page
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/test/utils/index.js:
--------------------------------------------------------------------------------
1 | export { default as getPort } from 'get-port'
2 | export { Nuxt, Builder } from 'nuxt'
3 |
4 | export { isFn, waitFor, waitUntil } from '../../lib/utils'
5 |
6 | export function expectParams (received, expectedParams) {
7 | if (!expectedParams) {
8 | expect(received).toBeFalsy()
9 | } else {
10 | expect(received).toBeTruthy()
11 |
12 | for (const key in expectedParams) { // eslint-disable-line no-unused-vars
13 | if (key === 'cvar') {
14 | expectCvars(received.get(key), expectedParams[key])
15 | } else {
16 | expect(received.get(key)).toBe(expectedParams[key])
17 | }
18 | }
19 | }
20 | }
21 |
22 | export function expectCvars (received, expectedCvars) {
23 | if (!expectedCvars) {
24 | expect(received).toBeFalsy()
25 | } else {
26 | expect(received).toBeTruthy()
27 |
28 | const cvars = JSON.parse(received)
29 |
30 | for (const key in expectedCvars) { // eslint-disable-line no-unused-vars
31 | const expectedCvar = expectedCvars[key]
32 | const cvar = cvars[`${parseInt(key) + 1}`]
33 |
34 | expect(cvar).toBeTruthy()
35 | expect(cvar[0]).toBe(expectedCvar[0])
36 | expect(cvar[1]).toBe(expectedCvar[1])
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/test/utils/setup.js:
--------------------------------------------------------------------------------
1 | jest.setTimeout(60000)
2 |
--------------------------------------------------------------------------------