Skip to content

Commit

Permalink
chore: replace karma with playwright
Browse files Browse the repository at this point in the history
  • Loading branch information
Joozty committed Nov 14, 2024
1 parent b4cb914 commit 58acac3
Show file tree
Hide file tree
Showing 13 changed files with 230 additions and 273 deletions.
80 changes: 45 additions & 35 deletions packages/integration-tests/src/server/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import path from 'path'
import { RENDER_AGENT_TEMPLATE } from './render-agent'
import { render } from 'ejs'
import { setServerTimingHeader } from './server-timing'
import * as fs from 'node:fs'

const GLOBAL_TEST_BUFFER_TIMEOUT = 20

Expand Down Expand Up @@ -53,53 +54,62 @@ app.get('/no-server-timings', (_, res) => {
res.sendStatus(200)
})

app.get('/', (_, res) => {
res.sendStatus(200)
})

app.get('*', (req, res) => {
if (typeof req.query.serverTiming === 'string') {
res.setHeader('Server-Timing', req.query.serverTiming)
}

const beaconUrl = new URL(`http://${req.headers.host}/api/v2/spans`)
const defaultFile = '/artifacts/splunk-otel-web.js'
const filePath = path.join(__dirname, '../tests', req.path)
console.log('filePath', filePath)
if (fs.existsSync(filePath)) {
if (req.path.endsWith('.ejs')) {
res.render(req.path.slice(1), {
renderAgent(userOpts = {}, noInit = false, file = defaultFile, cdnVersion = null) {
const options: Record<string, unknown> = {
beaconEndpoint: beaconUrl.toString(),
applicationName: 'splunk-otel-js-dummy-app',
debug: true,
bufferTimeout: GLOBAL_TEST_BUFFER_TIMEOUT,
...userOpts,
}

if (typeof req.query.disableInstrumentation === 'string') {
if (!options.instrumentations) {
options.instrumentations = {}
}

req.query.disableInstrumentation.split(',').forEach((instrumentation) => {
options.instrumentations[instrumentation] = false
})
}

if (typeof req.query.beaconEndpoint === 'string') {
options.beaconEndpoint = req.query.beaconEndpoint
}

if (req.path.endsWith('.ejs')) {
res.render(req.path.slice(1), {
renderAgent(userOpts = {}, noInit = false, file = defaultFile, cdnVersion = null) {
const options: Record<string, unknown> = {
beaconEndpoint: beaconUrl.toString(),
applicationName: 'splunk-otel-js-dummy-app',
debug: true,
bufferTimeout: GLOBAL_TEST_BUFFER_TIMEOUT,
...userOpts,
}

if (typeof req.query.disableInstrumentation === 'string') {
if (!options.instrumentations) {
options.instrumentations = {}
if (cdnVersion) {
file = `https://cdn.signalfx.com/o11y-gdi-rum/${cdnVersion}/splunk-otel-web.js`
}

req.query.disableInstrumentation.split(',').forEach((instrumentation) => {
options.instrumentations[instrumentation] = false
return render(RENDER_AGENT_TEMPLATE, {
file,
noInit,
options: JSON.stringify(options),
otelApiGlobalsFile: '/artifacts/otel-api-globals.js',
})
}

if (typeof req.query.beaconEndpoint === 'string') {
options.beaconEndpoint = req.query.beaconEndpoint
}

if (cdnVersion) {
file = `https://cdn.signalfx.com/o11y-gdi-rum/${cdnVersion}/splunk-otel-web.js`
}

return render(RENDER_AGENT_TEMPLATE, {
file,
noInit,
options: JSON.stringify(options),
otelApiGlobalsFile: '/artifacts/otel-api-globals.js',
})
},
})
},
})
} else {
res.sendFile(filePath)
}
} else {
res.send('Error: Invalid URL')
res.status(404).send('Error: Invalid URL')
}
})

Expand Down
179 changes: 179 additions & 0 deletions packages/integration-tests/src/tests/errors/errors.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
/**
*
* Copyright 2024 Splunk Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
import { expect } from '@playwright/test'
import { test } from '../../utils/test'

test.describe('errors', () => {
test('DOM resource 4xx', async ({ recordPage }) => {
await recordPage.goTo('/errors/views/resource-4xx.ejs')
await recordPage.waitForSpans(
(spans) => spans.filter((span) => span.name === 'eventListener.error').length === 1,
)
const errorSpans = recordPage.receivedSpans.filter((span) => span.name === 'eventListener.error')

expect(errorSpans).toHaveLength(1)

expect(errorSpans[0].tags['component']).toBe('error')
expect(errorSpans[0].tags['error.type']).toBe('error')
expect(errorSpans[0].tags['target_element']).toBe('IMG')
expect(errorSpans[0].tags['target_xpath']).toBe('//html/body/img')
expect(
(errorSpans[0].tags['target_src'] as string).endsWith('/nonexistent.png'),
`Checking target_src: ${errorSpans[0]['target_src']}`,
).toBeTruthy()
})

test('JS syntax error', async ({ recordPage, browserName }) => {
await recordPage.goTo('/errors/views/js-syntax-error.ejs')
await recordPage.waitForSpans((spans) => spans.filter((span) => span.name === 'onerror').length === 1)
const errorSpans = recordPage.receivedSpans.filter((span) => span.name === 'onerror')

expect(errorSpans).toHaveLength(1)

expect(errorSpans[0].tags['component']).toBe('error')
expect(errorSpans[0].tags['error']).toBe('true')
expect(errorSpans[0].tags['error.object']).toBe('SyntaxError')

const errorMessageMap = {
chromium: "Unexpected token ';'",
firefox: "expected expression, got ';'",
webkit: "Unexpected token ';'",
}

expect(errorSpans[0].tags['error.message']).toBe(errorMessageMap[browserName])
})

test('JS unhandled error', async ({ recordPage, browserName }) => {
await recordPage.goTo('/errors/views/unhandled-error.ejs')
await recordPage.waitForSpans((spans) => spans.filter((span) => span.name === 'onerror').length === 1)
const errorSpans = recordPage.receivedSpans.filter((span) => span.name === 'onerror')

expect(errorSpans).toHaveLength(1)

expect(errorSpans[0].tags['component']).toBe('error')
expect(errorSpans[0].tags['error']).toBe('true')
expect(errorSpans[0].tags['error.object']).toBe('TypeError')

const errorMessageMap = {
chromium: "Cannot set properties of null (setting 'prop1')",
firefox: 'test is null',
webkit: "null is not an object (evaluating 'test.prop1 = true')",
}

expect(errorSpans[0].tags['error.message']).toBe(errorMessageMap[browserName])
})

test('unhandled promise rejection', async ({ recordPage }) => {
await recordPage.goTo('/errors/views/unhandled-rejection.ejs')
await recordPage.waitForSpans(
(spans) => spans.filter((span) => span.name === 'unhandledrejection').length === 1,
)
const errorSpans = recordPage.receivedSpans.filter((span) => span.name === 'unhandledrejection')

expect(errorSpans).toHaveLength(1)
expect(errorSpans[0].tags['component']).toBe('error')
expect(errorSpans[0].tags['error']).toBe('true')
expect(errorSpans[0].tags['error.object']).toBe('String')
expect(errorSpans[0].tags['error.message']).toBe('rejection-value')
})

test('manual console.error', async ({ recordPage, browserName }) => {
await recordPage.goTo('/errors/views/console-error.ejs')
await recordPage.waitForSpans((spans) => spans.filter((span) => span.name === 'console.error').length === 1)
const errorSpans = recordPage.receivedSpans.filter((span) => span.name === 'console.error')

expect(errorSpans).toHaveLength(1)
expect(errorSpans[0].tags['component']).toBe('error')
expect(errorSpans[0].tags['error']).toBe('true')
expect(errorSpans[0].tags['error.object']).toBe('TypeError')

const errorMessageMap = {
chromium: "Cannot set properties of null (setting 'anyField')",
firefox: 'someNull is null',
webkit: "null is not an object (evaluating 'someNull.anyField = 'value'')",
}

expect(errorSpans[0].tags['error.message']).toBe(errorMessageMap[browserName])
})

test('SplunkRum.error', async ({ recordPage, browserName }) => {
const url = 'http://localhost:3000/errors/views/splunkrum-error.ejs'
await recordPage.goTo(url)
await recordPage.waitForSpans((spans) => spans.filter((span) => span.name === 'SplunkRum.error').length === 1)
const errorSpans = recordPage.receivedSpans.filter((span) => span.name === 'SplunkRum.error')

expect(errorSpans).toHaveLength(1)
expect(errorSpans[0].tags['component']).toBe('error')
expect(errorSpans[0].tags['error']).toBe('true')
expect(errorSpans[0].tags['error.object']).toBe('TypeError')

const errorMessages = {
chromium: "Cannot set properties of null (setting 'anyField')",
firefox: 'someNull is null',
webkit: "null is not an object (evaluating 'someNull.anyField = 'value'')",
}

expect(errorSpans[0].tags['error.message']).toBe(errorMessages[browserName])

const errorStackMap = {
chromium: `TypeError: Cannot set properties of null (setting 'anyField')\n at ${url}:63:25`,
firefox: `@${url}:63:7\n`,
webkit: `global code@${url}:63:15`,
}

expect(errorSpans[0].tags['error.stack']).toBe(errorStackMap[browserName])
})

test('module can be disabled', async ({ recordPage }) => {
await recordPage.goTo('/errors/views/unhandled-error.ejs?disableInstrumentation=errors')
await recordPage.waitForTimeoutAndFlushData(1000)
const errorSpans = recordPage.receivedSpans.filter((span) => span.name === 'onerror')

expect(errorSpans).toHaveLength(0)
})

test('minified script with source map id', async ({ recordPage }) => {
await recordPage.goTo('/errors/views/minified-file-errors.ejs')
await recordPage.locator('#button1').click()

await recordPage.waitForSpans((spans) => spans.filter((span) => span.name === 'onerror').length === 1)
const errorSpans = recordPage.receivedSpans.filter((span) => span.name === 'onerror')

expect(errorSpans).toHaveLength(1)
expect(errorSpans[0].tags['error.message']).toBe('Error from script1.js')
expect((errorSpans[0].tags['error.source_map_ids'] as string).includes('script1.min.js')).toBeTruthy()
expect(
(errorSpans[0].tags['error.source_map_ids'] as string).includes(
'9663c60664c425cef3b141c167e9b324240ce10ae488726293892b7130266a6b',
),
).toBeTruthy()

await recordPage.locator('#button2').click()
await recordPage.waitForSpans((spans) => spans.filter((span) => span.name === 'onerror').length === 2)
const errorSpans2 = recordPage.receivedSpans.filter((span) => span.name === 'onerror')

expect(errorSpans2).toHaveLength(2)
expect(errorSpans2[1].tags['error.message']).toBe('Error from script2.js')
expect((errorSpans2[1].tags['error.source_map_ids'] as string).includes('script2.min.js')).toBeTruthy()
expect(
(errorSpans2[1].tags['error.source_map_ids'] as string).includes(
'9793573cdc2ab308a0b1996bea14253ec8832bc036210475ded0813cafa27066',
),
).toBeTruthy()
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<title>Errors test</title>
<%- renderAgent({}) %>
<script id="scenario">
var someNull = null;
const someNull = null;
try {
someNull.anyField = 'value';
} catch(e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
<h1>Minified files with source map ids</h1>
<button id="button1">Button 1</button>
<button id="button2">Button 2</button>
<script src="/integration-tests/tests/errors/views/script1.min.js" type="module"></script>
<script src="/integration-tests/tests/errors/views/script2.min.js"></script>
<script src="/errors/views/script1.min.js" type="module"></script>
<script src="/errors/views/script2.min.js"></script>
</body>
</html>
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<head>
<meta charset="UTF-8">
<title>Errors test</title>
<%- renderAgent({allowInsecureBeacon: true}) %>
<%- renderAgent({ allowInsecureBeacon: true }) %>
</head>
<body>
<h1>Dom parsing error simulation</h1>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<title>Errors test</title>
<%- renderAgent({}) %>
<script id="scenario">
var someNull = null;
let someNull = null;
try {
someNull.anyField = 'value';
} catch(e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<script id="scenario">
window.testing = true;
var test = null;
let test = null;
setTimeout(function() {
setTimeout(function () {
window.testing = false;
Expand Down
Loading

0 comments on commit 58acac3

Please sign in to comment.