Skip to content

Commit

Permalink
feat: Add Session Replay configurations to collect inline assets (#763)
Browse files Browse the repository at this point in the history
  • Loading branch information
metal-messiah authored Oct 9, 2023
1 parent e5af1b5 commit cef08dd
Show file tree
Hide file tree
Showing 7 changed files with 82 additions and 4 deletions.
3 changes: 3 additions & 0 deletions src/common/config/state/init.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ const model = () => {
harvestTimeSeconds: 60,
sampling_rate: 50, // float from 0 - 100
error_sampling_rate: 50, // float from 0 - 100
collect_fonts: false, // serialize fonts for collection without public asset url, this is currently broken in RRWeb -- https://github.com/rrweb-io/rrweb/issues/1304. When fixed, revisit with test cases
inline_images: false, // serialize images for collection without public asset url
inline_stylesheet: true, // serialize css for collection without public asset url
// recording config settings
mask_all_inputs: true,
// this has a getter/setter to facilitate validation of the selectors
Expand Down
5 changes: 4 additions & 1 deletion src/features/session_replay/aggregate/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -346,7 +346,7 @@ export class Aggregate extends AggregateBase {
// set the fallbacks as early as possible
this.setTimestamps()
this.recording = true
const { block_class, ignore_class, mask_text_class, block_selector, mask_input_options, mask_text_selector, mask_all_inputs } = getConfigurationValue(this.agentIdentifier, 'session_replay')
const { block_class, ignore_class, mask_text_class, block_selector, mask_input_options, mask_text_selector, mask_all_inputs, inline_images, inline_stylesheet, collect_fonts } = getConfigurationValue(this.agentIdentifier, 'session_replay')
// set up rrweb configurations for maximum privacy --
// https://newrelic.atlassian.net/wiki/spaces/O11Y/pages/2792293280/2023+02+28+Browser+-+Session+Replay#Configuration-options
const stop = recorder({
Expand All @@ -358,6 +358,9 @@ export class Aggregate extends AggregateBase {
maskInputOptions: mask_input_options,
maskTextSelector: mask_text_selector,
maskAllInputs: mask_all_inputs,
inlineImages: inline_images,
inlineStylesheet: inline_stylesheet,
collectFonts: collect_fonts,
checkoutEveryNms: CHECKOUT_MS[this.mode]
})

Expand Down
Binary file added tests/assets/font.woff
Binary file not shown.
3 changes: 2 additions & 1 deletion tests/assets/rrweb-instrumented.html
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,14 @@
position: absolute; right: 50px; top: 200px;
}
</style>
<link rel="stylesheet" type="text/css" href="style.css">
{init}
{config}
{loader}
</head>
<body>
this is a page that provides several types of elements with selectors that session_replay can interact with based on how it is configured
<hr />
<img class="left" src="/slowimage" />
<hr />
<textarea id="plain"></textarea>
<textarea id="ignore" class="nr-ignore"></textarea>
Expand All @@ -33,6 +33,7 @@
<input type="text" id="text-input">
<hr />
<button onclick="moveImage()">Click</button>
<img src="https://upload.wikimedia.org/wikipedia/commons/d/d7/House_of_Commons_Chamber_1.png" />
<a href="./rrweb-instrumented.html" target="_blank">New Tab</a>
<script>
function moveImage(){
Expand Down
5 changes: 3 additions & 2 deletions tests/assets/rrweb-record.html
Original file line number Diff line number Diff line change
Expand Up @@ -14,24 +14,25 @@
position: absolute; right: 50px; top: 200px;
}
</style>
<link rel="stylesheet" type="text/css" href="style.css">
{init}
{config}
<script>
localStorage.clear()
NREUM.init.session_replay = {enabled: true, harvestTimeSeconds: 5, error_sampling_rate: 0, sampling_rate: 100}
NREUM.init.session_replay = {enabled: true, harvestTimeSeconds: 5, error_sampling_rate: 0, sampling_rate: 100, collect_fonts: true}
NREUM.init.privacy.cookies_enabled = true
</script>
{loader}
</head>
<body>
this is a page intended to "manually" set up session replay and bypass the default "off" configs
<hr />
<img class="left" src="/slowimage" />
<hr />
<textarea></textarea>
<hr />
<button onclick="moveImage()">Click</button>
<a href="./rrweb-record.html" target="_blank">New Tab</a>
<img src="https://upload.wikimedia.org/wikipedia/commons/d/d7/House_of_Commons_Chamber_1.png" />
<script>
function moveImage(){
document.querySelector("img").classList.toggle("left")
Expand Down
16 changes: 16 additions & 0 deletions tests/assets/style.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
@font-face {
font-family: MyWebFont;
src: url('font.woff') format('woff')
}

.test {
color: black
}

#test2 {
color: white
}

body{
font-family: MyWebFont, sans-serif;;
}
54 changes: 54 additions & 0 deletions tests/specs/session-replay/rrweb-configuration.e2e.js
Original file line number Diff line number Diff line change
Expand Up @@ -281,4 +281,58 @@ describe.withBrowsersMatching(notIE)('RRWeb Configuration', () => {
expect(JSON.stringify(body).includes('testing')).toBeFalsy()
})
})

describe('inline assets', () => {
it('inline_images false DOES NOT add data url', async () => {
await browser.url(await browser.testHandle.assetURL('rrweb-instrumented.html', config()))
.then(() => browser.waitForFeatureAggregate('session_replay'))

const { request: { body } } = await browser.testHandle.expectBlob()

const snapshotNode = body.find(x => x.type === 2)
const htmlNode = snapshotNode.data.node.childNodes.find(x => x.tagName === 'html')
const bodyNode = htmlNode.childNodes.find(x => x.tagName === 'body')
const imgNode = bodyNode.childNodes.find(x => x.tagName === 'img' && x.attributes.src.includes('wikimedia.org'))
expect(!!imgNode.attributes.rr_dataURL).toEqual(false)
})

it('inline_images true DOES add data url', async () => {
await browser.url(await browser.testHandle.assetURL('rrweb-instrumented.html', config({ session_replay: { inline_images: true } })))
.then(() => browser.waitForFeatureAggregate('session_replay'))

const { request: { body } } = await browser.testHandle.expectBlob()

const snapshotNode = body.find(x => x.type === 2)
const htmlNode = snapshotNode.data.node.childNodes.find(x => x.tagName === 'html')
const bodyNode = htmlNode.childNodes.find(x => x.tagName === 'body')
const imgNode = bodyNode.childNodes.find(x => x.tagName === 'img' && x.attributes.src.includes('wikimedia.org'))
expect(!!imgNode.attributes.rr_dataURL).toEqual(true)
})

it('inline_stylesheet false DOES NOT add inline text', async () => {
await browser.url(await browser.testHandle.assetURL('rrweb-instrumented.html', config({ session_replay: { inline_stylesheet: false } })))
.then(() => browser.waitForFeatureAggregate('session_replay'))

const { request: { body } } = await browser.testHandle.expectBlob()

const snapshotNode = body.find(x => x.type === 2)
const htmlNode = snapshotNode.data.node.childNodes.find(x => x.tagName === 'html')
const headNode = htmlNode.childNodes.find(x => x.tagName === 'head')
const linkNode = headNode.childNodes.find(x => x.tagName === 'link' && x.attributes.type === 'text/css')
expect(!!linkNode.attributes._cssText).toEqual(false)
})

it('inline_stylesheet true DOES NOT add inline text', async () => {
await browser.url(await browser.testHandle.assetURL('rrweb-instrumented.html', config()))
.then(() => browser.waitForFeatureAggregate('session_replay'))

const { request: { body } } = await browser.testHandle.expectBlob()

const snapshotNode = body.find(x => x.type === 2)
const htmlNode = snapshotNode.data.node.childNodes.find(x => x.tagName === 'html')
const headNode = htmlNode.childNodes.find(x => x.tagName === 'head')
const linkNode = headNode.childNodes.find(x => x.tagName === 'link' && x.attributes.type === 'text/css')
expect(!!linkNode.attributes._cssText).toEqual(true)
})
})
})

0 comments on commit cef08dd

Please sign in to comment.