* cleanup FEATURE_NEXTJS * fixing some server tests * updating article a for server tests * update h2 to h4 map topic tests * data off on TOCs * updating dropdown article versions links * Update so markdown renders in intros * updating typo and all server tests are now passing * remove nextjs feature flag * head.js tests pass * updating article-version-picker * remove nextjs feature flag browser test * update header.js tests * fix page-titles.js test * fix deprecated-enterprise versions * adding early access * testing * getting childTocItem * fixing table of contents to show child toc items * updated to 2 because the sidebar article also has the same link * remove comment * updating pick * Update TocLandingContext.tsx * update package.json and change className to h4 for h2 * updating with mikes feedback * remove a.active test * React clean up: Delete unnecessary layouts/includes Part 2 (#20143) * Delete unnecessary layouts * setting back tests failing :( * update layouts * delete unnecessary includes * remove github-ae-release-notes and updating layouts * remove a.active test
504 lines
12 KiB
JavaScript
504 lines
12 KiB
JavaScript
import request from 'supertest'
|
|
import nock from 'nock'
|
|
import cheerio from 'cheerio'
|
|
import createApp from '../../lib/app.js'
|
|
import { jest } from '@jest/globals'
|
|
|
|
jest.useFakeTimers()
|
|
|
|
describe('POST /events', () => {
|
|
jest.setTimeout(60 * 1000)
|
|
|
|
const app = createApp()
|
|
let csrfToken = ''
|
|
let agent
|
|
|
|
beforeEach(async () => {
|
|
process.env.AIRTABLE_API_KEY = '$AIRTABLE_API_KEY$'
|
|
process.env.AIRTABLE_BASE_KEY = '$AIRTABLE_BASE_KEY$'
|
|
process.env.HYDRO_SECRET = '$HYDRO_SECRET$'
|
|
process.env.HYDRO_ENDPOINT = 'http://example.com/hydro'
|
|
agent = request.agent(app)
|
|
const csrfRes = await agent.get('/en')
|
|
const $ = cheerio.load(csrfRes.text || '', { xmlMode: true })
|
|
csrfToken = $('meta[name="csrf-token"]').attr('content')
|
|
nock('http://example.com').post('/hydro').reply(200, {})
|
|
})
|
|
|
|
afterEach(() => {
|
|
delete process.env.AIRTABLE_API_KEY
|
|
delete process.env.AIRTABLE_BASE_KEY
|
|
delete process.env.HYDRO_SECRET
|
|
delete process.env.HYDRO_ENDPOINT
|
|
csrfToken = ''
|
|
})
|
|
|
|
async function checkEvent(data, code) {
|
|
return agent
|
|
.post('/events')
|
|
.send(data)
|
|
.set('Accept', 'application/json')
|
|
.set('csrf-token', csrfToken)
|
|
.expect(code)
|
|
}
|
|
|
|
const baseExample = {
|
|
context: {
|
|
// Primitives
|
|
event_id: 'a35d7f88-3f48-4f36-ad89-5e3c8ebc3df7',
|
|
user: '703d32a8-ed0f-45f9-8d78-a913d4dc6f19',
|
|
version: '1.0.0',
|
|
created: '2020-10-02T17:12:18.620Z',
|
|
|
|
// Content information
|
|
path: '/github/docs/issues',
|
|
hostname: 'github.com',
|
|
referrer: 'https://github.com/github/docs',
|
|
search: '?q=is%3Aissue+is%3Aopen+example+',
|
|
href: 'https://github.com/github/docs/issues?q=is%3Aissue+is%3Aopen+example+',
|
|
site_language: 'en',
|
|
|
|
// Device information
|
|
os: 'linux',
|
|
os_version: '18.04',
|
|
browser: 'chrome',
|
|
browser_version: '85.0.4183.121',
|
|
viewport_width: 1418,
|
|
viewport_height: 501,
|
|
|
|
// Location information
|
|
timezone: -7,
|
|
user_language: 'en-US',
|
|
},
|
|
}
|
|
|
|
describe('page', () => {
|
|
const pageExample = { ...baseExample, type: 'page' }
|
|
|
|
it('should record a page event', () => checkEvent(pageExample, 200))
|
|
|
|
it('should require a type', () => checkEvent(baseExample, 400))
|
|
|
|
it('should require an event_id in uuid', () =>
|
|
checkEvent(
|
|
{
|
|
...pageExample,
|
|
context: {
|
|
...pageExample.context,
|
|
event_id: 'asdfghjkl',
|
|
},
|
|
},
|
|
400
|
|
))
|
|
|
|
it('should require a user in uuid', () =>
|
|
checkEvent(
|
|
{
|
|
...pageExample,
|
|
context: {
|
|
...pageExample.context,
|
|
user: 'asdfghjkl',
|
|
},
|
|
},
|
|
400
|
|
))
|
|
|
|
it('should require a version', () =>
|
|
checkEvent(
|
|
{
|
|
...pageExample,
|
|
context: {
|
|
...pageExample.context,
|
|
version: undefined,
|
|
},
|
|
},
|
|
400
|
|
))
|
|
|
|
it('should require created timestamp', () =>
|
|
checkEvent(
|
|
{
|
|
...pageExample,
|
|
context: {
|
|
...pageExample.context,
|
|
timestamp: 1234,
|
|
},
|
|
},
|
|
400
|
|
))
|
|
|
|
it('should allow page_event_id', () =>
|
|
checkEvent(
|
|
{
|
|
...pageExample,
|
|
context: {
|
|
...pageExample.context,
|
|
page_event_id: baseExample.context.event_id,
|
|
},
|
|
},
|
|
200
|
|
))
|
|
|
|
it('should not allow a honeypot token', () =>
|
|
checkEvent(
|
|
{
|
|
...pageExample,
|
|
context: {
|
|
...pageExample.context,
|
|
token: 'zxcv',
|
|
},
|
|
},
|
|
400
|
|
))
|
|
|
|
it('should path be uri-reference', () =>
|
|
checkEvent(
|
|
{
|
|
...pageExample,
|
|
context: {
|
|
...pageExample.context,
|
|
path: ' ',
|
|
},
|
|
},
|
|
400
|
|
))
|
|
|
|
it('should hostname be uri-reference', () =>
|
|
checkEvent(
|
|
{
|
|
...pageExample,
|
|
context: {
|
|
...pageExample.context,
|
|
hostname: ' ',
|
|
},
|
|
},
|
|
400
|
|
))
|
|
|
|
it('should referrer be uri-reference', () =>
|
|
checkEvent(
|
|
{
|
|
...pageExample,
|
|
context: {
|
|
...pageExample.context,
|
|
referrer: ' ',
|
|
},
|
|
},
|
|
400
|
|
))
|
|
|
|
it('should search a string', () =>
|
|
checkEvent(
|
|
{
|
|
...pageExample,
|
|
context: {
|
|
...pageExample.context,
|
|
search: 1234,
|
|
},
|
|
},
|
|
400
|
|
))
|
|
|
|
it('should href be uri', () =>
|
|
checkEvent(
|
|
{
|
|
...pageExample,
|
|
context: {
|
|
...pageExample.context,
|
|
href: '/example',
|
|
},
|
|
},
|
|
400
|
|
))
|
|
|
|
it('should site_language is a valid option', () =>
|
|
checkEvent(
|
|
{
|
|
...pageExample,
|
|
context: {
|
|
...pageExample.context,
|
|
site_language: 'nl',
|
|
},
|
|
},
|
|
400
|
|
))
|
|
|
|
it('should os a valid os option', () =>
|
|
checkEvent(
|
|
{
|
|
...pageExample,
|
|
context: {
|
|
...pageExample.context,
|
|
os: 'ubuntu',
|
|
},
|
|
},
|
|
400
|
|
))
|
|
|
|
it('should os_version a string', () =>
|
|
checkEvent(
|
|
{
|
|
...pageExample,
|
|
context: {
|
|
...pageExample.context,
|
|
os_version: 25,
|
|
},
|
|
},
|
|
400
|
|
))
|
|
|
|
it('should browser a valid option', () =>
|
|
checkEvent(
|
|
{
|
|
...pageExample,
|
|
context: {
|
|
...pageExample.context,
|
|
browser: 'opera',
|
|
},
|
|
},
|
|
400
|
|
))
|
|
|
|
it('should browser_version a string', () =>
|
|
checkEvent(
|
|
{
|
|
...pageExample,
|
|
context: {
|
|
...pageExample.context,
|
|
browser_version: 25,
|
|
},
|
|
},
|
|
400
|
|
))
|
|
|
|
it('should viewport_width a number', () =>
|
|
checkEvent(
|
|
{
|
|
...pageExample,
|
|
context: {
|
|
...pageExample.context,
|
|
viewport_width: -500,
|
|
},
|
|
},
|
|
400
|
|
))
|
|
|
|
it('should viewport_height a number', () =>
|
|
checkEvent(
|
|
{
|
|
...pageExample,
|
|
context: {
|
|
...pageExample.context,
|
|
viewport_height: '53px',
|
|
},
|
|
},
|
|
400
|
|
))
|
|
|
|
it('should timezone in number', () =>
|
|
checkEvent(
|
|
{
|
|
...pageExample,
|
|
context: {
|
|
...pageExample.context,
|
|
timezone: 'GMT-0700',
|
|
},
|
|
},
|
|
400
|
|
))
|
|
|
|
it('should user_language is a string', () =>
|
|
checkEvent(
|
|
{
|
|
...pageExample,
|
|
context: {
|
|
...pageExample.context,
|
|
user_language: true,
|
|
},
|
|
},
|
|
400
|
|
))
|
|
})
|
|
|
|
describe('exit', () => {
|
|
const exitExample = {
|
|
...baseExample,
|
|
type: 'exit',
|
|
exit_render_duration: 0.9,
|
|
exit_first_paint: 0.1,
|
|
exit_dom_interactive: 0.2,
|
|
exit_dom_complete: 0.3,
|
|
exit_visit_duration: 5,
|
|
exit_scroll_length: 0.5,
|
|
}
|
|
|
|
it('should record an exit event', () => checkEvent(exitExample, 200))
|
|
|
|
it('should exit_render_duration is a positive number', () =>
|
|
checkEvent(
|
|
{
|
|
...exitExample,
|
|
exit_render_duration: -0.5,
|
|
},
|
|
400
|
|
))
|
|
|
|
it('exit_first_paint is a number', () =>
|
|
checkEvent({ ...exitExample, exit_first_paint: 'afjdkl' }, 400))
|
|
|
|
it('exit_dom_interactive is a number', () =>
|
|
checkEvent({ ...exitExample, exit_dom_interactive: '202' }, 400))
|
|
|
|
it('exit_visit_duration is a number', () =>
|
|
checkEvent({ ...exitExample, exit_visit_duration: '75' }, 400))
|
|
|
|
it('exit_scroll_length is a number between 0 and 1', () =>
|
|
checkEvent({ ...exitExample, exit_scroll_length: 1.1 }, 400))
|
|
})
|
|
|
|
describe('link', () => {
|
|
const linkExample = {
|
|
...baseExample,
|
|
type: 'link',
|
|
link_url: 'https://example.com',
|
|
}
|
|
|
|
it('should send a link event', () => checkEvent(linkExample, 200))
|
|
|
|
it('link_url is a required uri formatted string', () =>
|
|
checkEvent({ ...linkExample, link_url: 'foo' }, 400))
|
|
})
|
|
|
|
describe('search', () => {
|
|
const searchExample = {
|
|
...baseExample,
|
|
type: 'search',
|
|
search_query: 'github private instances',
|
|
search_context: 'private',
|
|
}
|
|
|
|
it('should record a search event', () => checkEvent(searchExample, 200))
|
|
|
|
it('search_query is required string', () =>
|
|
checkEvent({ ...searchExample, search_query: undefined }, 400))
|
|
|
|
it('search_context is optional string', () =>
|
|
checkEvent({ ...searchExample, search_context: undefined }, 200))
|
|
})
|
|
|
|
describe('navigate', () => {
|
|
const navigateExample = {
|
|
...baseExample,
|
|
type: 'navigate',
|
|
navigate_label: 'drop down',
|
|
}
|
|
|
|
it('should record a navigate event', () => checkEvent(navigateExample, 200))
|
|
|
|
it('navigate_label is optional string', () =>
|
|
checkEvent({ ...navigateExample, navigate_label: undefined }, 200))
|
|
})
|
|
|
|
describe('survey', () => {
|
|
const surveyExample = {
|
|
...baseExample,
|
|
type: 'survey',
|
|
survey_vote: true,
|
|
survey_comment: 'I love this site.',
|
|
survey_email: 'daisy@example.com',
|
|
}
|
|
|
|
it('should record a survey event', () => checkEvent(surveyExample, 200))
|
|
|
|
it('survey_vote is boolean', () =>
|
|
checkEvent({ ...surveyExample, survey_vote: undefined }, 400))
|
|
|
|
it('survey_comment is string', () => {
|
|
checkEvent({ ...surveyExample, survey_comment: 1234 }, 400)
|
|
})
|
|
|
|
it('survey_email is email', () => {
|
|
checkEvent({ ...surveyExample, survey_email: 'daisy' }, 400)
|
|
})
|
|
})
|
|
|
|
describe('experiment', () => {
|
|
const experimentExample = {
|
|
...baseExample,
|
|
type: 'experiment',
|
|
experiment_name: 'change-button-copy',
|
|
experiment_variation: 'treatment',
|
|
experiment_success: true,
|
|
}
|
|
|
|
it('should record an experiment event', () => checkEvent(experimentExample, 200))
|
|
|
|
it('experiment_name is required string', () =>
|
|
checkEvent({ ...experimentExample, experiment_name: undefined }, 400))
|
|
|
|
it('experiment_variation is required string', () =>
|
|
checkEvent({ ...experimentExample, experiment_variation: undefined }, 400))
|
|
|
|
it('experiment_success is optional boolean', () =>
|
|
checkEvent({ ...experimentExample, experiment_success: undefined }, 200))
|
|
})
|
|
|
|
describe('redirect', () => {
|
|
const redirectExample = {
|
|
...baseExample,
|
|
type: 'redirect',
|
|
redirect_from: 'http://example.com/a',
|
|
redirect_to: 'http://example.com/b',
|
|
}
|
|
|
|
it('should record an redirect event', () => checkEvent(redirectExample, 200))
|
|
|
|
it('redirect_from is required url', () =>
|
|
checkEvent({ ...redirectExample, redirect_from: ' ' }, 400))
|
|
|
|
it('redirect_to is required url', () =>
|
|
checkEvent({ ...redirectExample, redirect_to: undefined }, 400))
|
|
})
|
|
|
|
describe('clipboard', () => {
|
|
const clipboardExample = {
|
|
...baseExample,
|
|
type: 'clipboard',
|
|
clipboard_operation: 'copy',
|
|
}
|
|
|
|
it('should record an clipboard event', () => checkEvent(clipboardExample, 200))
|
|
|
|
it('clipboard_operation is required copy, paste, cut', () =>
|
|
checkEvent({ ...clipboardExample, clipboard_operation: 'destroy' }, 400))
|
|
})
|
|
|
|
describe('print', () => {
|
|
const printExample = {
|
|
...baseExample,
|
|
type: 'print',
|
|
}
|
|
|
|
it('should record a print event', () => checkEvent(printExample, 200))
|
|
})
|
|
|
|
describe('preference', () => {
|
|
const preferenceExample = {
|
|
...baseExample,
|
|
type: 'preference',
|
|
preference_name: 'application',
|
|
preference_value: 'cli',
|
|
}
|
|
|
|
it('should record an application event', () => checkEvent(preferenceExample, 200))
|
|
|
|
it('preference_name is string', () => {
|
|
checkEvent({ ...preferenceExample, preference_name: null }, 400)
|
|
})
|
|
|
|
it('preference_value is string', () => {
|
|
checkEvent({ ...preferenceExample, preference_value: null }, 400)
|
|
})
|
|
})
|
|
})
|