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 18, 2024
1 parent 6b201de commit 3b69e48
Show file tree
Hide file tree
Showing 19 changed files with 292 additions and 205 deletions.
4 changes: 3 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion packages/integration-tests/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
"@types/node": "^22.9.0",
"cors": "^2.8.5",
"ejs": "^3.1.10",
"express": "^4.21.1"
"express": "^4.21.1",
"on-headers": "^1.0.2"
},
"bugs": {
"url": "https://github.com/signalfx/splunk-otel-js-web/issues"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/**
*
* 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 { Request, Response, NextFunction } from 'express'

export const delayMiddleware = async (req: Request, res: Response, next: NextFunction) => {
if (typeof req.query.delay === 'string') {
const timeout = parseInt(req.query.delay)
await new Promise((resolve) => setTimeout(resolve, timeout))
}

next()
}
20 changes: 20 additions & 0 deletions packages/integration-tests/src/server/middlewares/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/**
*
* 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.
*
*/
export * from './delay-middleware'
export * from './server-timing-middleware'
export * from './no-cache-middleware'
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/**
*
* 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 { Request, Response, NextFunction } from 'express'
import onHeaders from 'on-headers'

export const noCacheMiddleware = (req: Request, res: Response, next: NextFunction) => {
if (typeof req.query.noCache === 'string') {
onHeaders(res, () => {
res.setHeader('Cache-Control', 'no-store, max-age=0')
res.removeHeader('Etag')
res.removeHeader('Last-Modified')
})
}

next()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/**
*
* 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 { NextFunction, Request, Response } from 'express'
import { setServerTimingHeader } from '../server-timing'

export const serverTimingMiddleware = (req: Request, res: Response, next: NextFunction) => {
if (typeof req.query.serverTiming === 'string') {
res.setHeader('Server-Timing', req.query.serverTiming)
} else {
setServerTimingHeader(res)
}

next()
}
20 changes: 5 additions & 15 deletions packages/integration-tests/src/server/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ import express from 'express'
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'
import cors from 'cors'
import { serverTimingMiddleware, delayMiddleware, noCacheMiddleware } from './middlewares'

const GLOBAL_TEST_BUFFER_TIMEOUT = 20

Expand All @@ -33,20 +33,9 @@ app.set('views', path.join(__dirname, '../tests'))
app.use(cors())
app.use(express.json())
app.use(express.static(path.join(__dirname, '../../../web/dist')))
app.use(async (req, res, next) => {
if (typeof req.query.delay === 'string') {
const timeout = parseInt(req.query.delay)
await new Promise((resolve) => setTimeout(resolve, timeout))
}

if (typeof req.query.serverTiming === 'string') {
res.setHeader('Server-Timing', req.query.serverTiming)
} else {
setServerTimingHeader(res)
}

next()
})
app.use(delayMiddleware)
app.use(serverTimingMiddleware)
app.use(noCacheMiddleware)

app.post('/api/v2/spans', (req, res) => {
res.send('')
Expand All @@ -65,6 +54,7 @@ app.get('/some-data', (req, response) => {
})

app.get('/no-server-timings', (_, res) => {
res.removeHeader('Server-Timing')
res.sendStatus(200)
})

Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
/**
*
* 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('resource observer', () => {
test('should report resource loads happening after page load', async ({ recordPage }) => {
await recordPage.goTo('/resource-observer/resources.ejs')

await recordPage.waitForSpans((spans) => spans.filter((span) => span.name === 'guard-span').length === 1)
const agentSpans = recordPage.receivedSpans.filter(
(span) => typeof span.tags['http.url'] === 'string' && span.tags['http.url'].endsWith('splunk-otel-web.js'),
)
const imageSpans = recordPage.receivedSpans.filter(
(span) => typeof span.tags['http.url'] === 'string' && span.tags['http.url'].endsWith('image.png?noCache'),
)
const imageBlackSpans = recordPage.receivedSpans.filter(
(span) =>
typeof span.tags['http.url'] === 'string' &&
span.tags['http.url'].endsWith('splunk-black.svg?delay=100'),
)
const scriptSpans = recordPage.receivedSpans.filter(
(span) => typeof span.tags['http.url'] === 'string' && span.tags['http.url'].endsWith('test.js'),
)

expect(agentSpans).toHaveLength(1)
expect(imageSpans).toHaveLength(1)

expect(imageBlackSpans).toHaveLength(1)
expect(imageBlackSpans[0].annotations.length).toBe(8)
expect(imageBlackSpans[0].tags['http.url']).toBe(
'http://localhost:3000/resource-observer/assets/splunk-black.svg?delay=100',
)

expect(scriptSpans).toHaveLength(1)
expect(scriptSpans[0].annotations.length).toBe(8)
expect(scriptSpans[0].tags['http.url']).toBe('http://localhost:3000/resource-observer/assets/test.js')
})

test('resources can be ignored', async ({ recordPage }) => {
await recordPage.goTo('/resource-observer/resources-ignored.ejs')

await recordPage.waitForSpans((spans) => spans.filter((span) => span.name === 'guard-span').length === 1)
const imageBlackSpans = recordPage.receivedSpans.filter(
(span) => typeof span.tags['http.url'] === 'string' && span.tags['http.url'].endsWith('splunk-black.svg'),
)
const scriptSpans = recordPage.receivedSpans.filter(
(span) => typeof span.tags['http.url'] === 'string' && span.tags['http.url'].endsWith('test.js'),
)

expect(imageBlackSpans).toHaveLength(0)
expect(scriptSpans).toHaveLength(0)
})

test('should create one span for cached resource', async ({ recordPage }) => {
await recordPage.goTo('/resource-observer/resources-twice.ejs')

await recordPage.waitForSpans((spans) => spans.filter((span) => span.name === 'guard-span').length === 1)
const imageBlackSpans = recordPage.receivedSpans.filter(
(span) => typeof span.tags['http.url'] === 'string' && span.tags['http.url'].endsWith('splunk-black.svg'),
)

expect(imageBlackSpans).toHaveLength(1)
expect(imageBlackSpans[0].tags['component']).toBe('document-load')
})

test('should create two spans for non cached resource', async ({ recordPage, browserName }) => {
if (['firefox', 'webkit'].includes(browserName)) {
// TODO: Investigate why this test is failing on Firefox and Webkit
test.skip()
}

await recordPage.goTo('/resource-observer/resources-twice-no-cache.ejs')

await recordPage.waitForSpans((spans) => spans.filter((span) => span.name === 'guard-span').length === 1)
const imageSpans = recordPage.receivedSpans.filter(
(span) => typeof span.tags['http.url'] === 'string' && span.tags['http.url'].endsWith('image.png?noCache'),
)

expect(imageSpans).toHaveLength(2)
expect(imageSpans[0].tags['component']).toBe('document-load')
expect(imageSpans[1].tags['component']).toBe('splunk-post-doc-load-resource')
expect(imageSpans[0].traceId).not.toBe(imageSpans[1].traceId)
})

// test.only('should propagate tracing context to created spans', async ({ recordPage, browserName }) => {
// await recordPage.goTo('/resource-observer/resources-custom-context.ejs')
//
// await recordPage.waitForSpans((spans) => spans.filter((span) => span.name === 'guard-span').length === 1)
//
// const imageRootSpans = recordPage.receivedSpans.filter(
// (span) =>
// typeof span.tags['http.url'] === 'string' &&
// span.tags['http.url'].endsWith('splunk-black.svg') &&
// span.tags['component'] === 'splunk-post-doc-load-resource',
// )
// const imageParentSpans = recordPage.receivedSpans.filter((span) => span.name === 'image-parent')
// const imageChildSpans = recordPage.receivedSpans.filter(
// (span) =>
// typeof span.tags['http.url'] === 'string' &&
// span.tags['http.url'].endsWith('splunk-black.svg') &&
// span.tags['component'] === 'splunk-post-doc-load-resource',
// )
//
// expect(imageRootSpans).toHaveLength(1)
// expect(imageParentSpans).toHaveLength(1)
// expect(imageChildSpans).toHaveLength(1)
//
// expect(imageRootSpans[0].traceId).not.toBe(imageParentSpans[0].traceId)
// expect(imageRootSpans[0].parentId).toBeUndefined()
//
// console.log(imageChildSpans[0])
// expect(imageChildSpans[0].parentId).toBe(imageParentSpans[0].id)
// })
})
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@
</head>
<body>
<h1>Resource observer test</h1>
<img src="/utils/devServer/assets/no-cache.png" alt="no-cache">
<img src="assets/image.png?noCache" alt="no-cache">

<script>
function loadScript(url) {
var p = new Promise(function(resolve, _) {
var s = document.createElement( 'script' );
s.setAttribute( 'src', url || '/utils/devServer/assets/test.js' );
const p = new Promise(function(resolve, _) {
const s = document.createElement( 'script' );
s.setAttribute( 'src', url || 'assets/test.js' );
s.onload = function() {
console.log('script resolved', performance.now());
resolve();
Expand All @@ -23,12 +23,12 @@
});
return p;
}
function loadImg(url) {
console.log('image load start', performance.now());
var p = new Promise(function(resolve, _) {
var img = document.createElement('img');
img.src = url || '/utils/devServer/assets/splunk-black.png';
const p = new Promise(function(resolve, _) {
const img = document.createElement('img');
img.src = url || 'assets/splunk-black.svg';
img.onload = function() {
console.log('image resolved', performance.now());
resolve();
Expand All @@ -40,20 +40,20 @@
}
function loadScriptCustomContext() {
var span = SplunkRum.provider.getTracer('default').startSpan('script-parent');
var context = OtelApiGlobals.context;
var trace = OtelApiGlobals.trace
const span = SplunkRum.provider.getTracer('default').startSpan('script-parent');
const context = OtelApiGlobals.context;
const trace = OtelApiGlobals.trace
return context.with(trace.setSpan(context.active(), span), function() {
return loadScript('/utils/devServer/assets/test-alt.js').finally(() => span.end());
return loadScript('assets/test-alt.js').finally(() => span.end());
});
}
function loadImageCustomContext() {
var span = SplunkRum.provider.getTracer('default').startSpan('image-parent');
var context = OtelApiGlobals.context;
var trace = OtelApiGlobals.trace;
const span = SplunkRum.provider.getTracer('default').startSpan('image-parent');
const context = OtelApiGlobals.context;
const trace = OtelApiGlobals.trace;
return context.with(trace.setSpan(context.active(), span), function() {
return loadImg('/utils/devServer/assets/splunk-black.svg').finally(() => span.end());
return loadImg('assets/splunk-black.svg').finally(() => span.end());
});
}
Expand Down
Loading

0 comments on commit 3b69e48

Please sign in to comment.