Skip to content

Commit 2a0d867

Browse files
committed
fix: update remote configuration e2e
1 parent 5bef505 commit 2a0d867

1 file changed

Lines changed: 110 additions & 49 deletions

File tree

Lines changed: 110 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
import type { Page } from '@playwright/test'
2+
import type { RemoteConfiguration } from '@datadog/browser-rum-core'
23
import { test, expect } from '@playwright/test'
34
import { createTest, html, waitForServersIdle } from '../../lib/framework'
45

56
const RC_APP_ID = 'e2e'
67
const CACHE_KEY = `dd_rc_${RC_APP_ID}`
78

89
test.describe('remote configuration', () => {
9-
createTest('should be fetched and applied')
10+
createTest('should be fetched on first load, cached, and applied after reload')
1011
.withRum({
1112
sessionSampleRate: 100,
1213
remoteConfigurationId: 'e2e',
@@ -17,107 +18,139 @@ test.describe('remote configuration', () => {
1718
.run(async ({ page }) => {
1819
await waitForRemoteConfigurationToBeApplied(page)
1920
const initConfiguration = await page.evaluate(() => window.DD_RUM!.getInitConfiguration()!)
21+
expect(initConfiguration.applicationId).toBe(RC_APP_ID)
2022
expect(initConfiguration.sessionSampleRate).toBe(1)
2123
})
2224

23-
createTest('should resolve an option value from a cookie')
25+
createTest('should preserve init configuration on first load and populate cache from background fetch')
2426
.withRum({
2527
remoteConfigurationId: 'e2e',
2628
})
29+
.withRemoteConfiguration({
30+
rum: { applicationId: RC_APP_ID, sessionSampleRate: 1 },
31+
})
32+
.run(async ({ page }) => {
33+
const initConfiguration = await page.evaluate(() => window.DD_RUM!.getInitConfiguration()!)
34+
expect(initConfiguration.applicationId).not.toBe(RC_APP_ID)
35+
36+
await page.waitForFunction((key) => localStorage.getItem(key) !== null, CACHE_KEY)
37+
const stored = await page.evaluate(
38+
(key) => JSON.parse(localStorage.getItem(key)!) as { version: number; config: object },
39+
CACHE_KEY
40+
)
41+
expect(stored.version).toBe(1)
42+
expect(stored.config).toEqual({ applicationId: RC_APP_ID, sessionSampleRate: 1 })
43+
})
44+
45+
createTest('should resolve an option value from a cookie')
46+
.withRum({ remoteConfigurationId: 'e2e' })
2747
.withRemoteConfiguration({
2848
rum: { applicationId: RC_APP_ID, version: { rcSerializedType: 'dynamic', strategy: 'cookie', name: 'e2e_rc' } },
2949
})
30-
.withBody(html`
50+
.withHead(html`
51+
${seedCache({
52+
rum: { applicationId: RC_APP_ID, version: { rcSerializedType: 'dynamic', strategy: 'cookie', name: 'e2e_rc' } },
53+
})}
3154
<script>
3255
document.cookie = 'e2e_rc=my-version;'
3356
</script>
3457
`)
3558
.run(async ({ page }) => {
36-
await waitForRemoteConfigurationToBeApplied(page)
3759
const initConfiguration = await page.evaluate(() => window.DD_RUM!.getInitConfiguration()!)
3860
expect(initConfiguration.version).toBe('my-version')
3961
})
4062

4163
createTest('should resolve an option value from an element content')
42-
.withRum({
43-
remoteConfigurationId: 'e2e',
44-
})
64+
.withRum({ remoteConfigurationId: 'e2e' })
4565
.withRemoteConfiguration({
4666
rum: {
4767
applicationId: RC_APP_ID,
4868
version: { rcSerializedType: 'dynamic', strategy: 'dom', selector: '#version' },
4969
},
5070
})
51-
.withHead(html`<span id="version">123</span>`)
71+
.withHead(html`
72+
${seedCache({
73+
rum: { applicationId: RC_APP_ID, version: { rcSerializedType: 'dynamic', strategy: 'dom', selector: '#version' } },
74+
})}
75+
<span id="version">123</span>
76+
`)
5277
.run(async ({ page }) => {
53-
await waitForRemoteConfigurationToBeApplied(page)
5478
const initConfiguration = await page.evaluate(() => window.DD_RUM!.getInitConfiguration()!)
5579
expect(initConfiguration.version).toBe('123')
5680
})
5781

5882
createTest('should resolve an option value from an element attribute')
59-
.withRum({
60-
remoteConfigurationId: 'e2e',
61-
})
83+
.withRum({ remoteConfigurationId: 'e2e' })
6284
.withRemoteConfiguration({
6385
rum: {
6486
applicationId: RC_APP_ID,
6587
version: { rcSerializedType: 'dynamic', strategy: 'dom', selector: '#version', attribute: 'data-version' },
6688
},
6789
})
68-
.withHead(html`<span id="version" data-version="123"></span>`)
90+
.withHead(html`
91+
${seedCache({
92+
rum: {
93+
applicationId: RC_APP_ID,
94+
version: { rcSerializedType: 'dynamic', strategy: 'dom', selector: '#version', attribute: 'data-version' },
95+
},
96+
})}
97+
<span id="version" data-version="123"></span>
98+
`)
6999
.run(async ({ page }) => {
70-
await waitForRemoteConfigurationToBeApplied(page)
71100
const initConfiguration = await page.evaluate(() => window.DD_RUM!.getInitConfiguration()!)
72101
expect(initConfiguration.version).toBe('123')
73102
})
74103

75104
createTest('should resolve an option value from js variable')
76-
.withRum({
77-
remoteConfigurationId: 'e2e',
78-
})
105+
.withRum({ remoteConfigurationId: 'e2e' })
79106
.withRemoteConfiguration({
80107
rum: {
81108
applicationId: 'e2e',
82109
version: { rcSerializedType: 'dynamic', strategy: 'js', path: 'dataLayer.version' },
83110
},
84111
})
85112
.withHead(html`
113+
${seedCache({
114+
rum: {
115+
applicationId: 'e2e',
116+
version: { rcSerializedType: 'dynamic', strategy: 'js', path: 'dataLayer.version' },
117+
},
118+
})}
86119
<script>
87120
window.dataLayer = { version: 'js-version' }
88121
</script>
89122
`)
90123
.run(async ({ page }) => {
91-
await waitForRemoteConfigurationToBeApplied(page)
92124
const initConfiguration = await page.evaluate(() => window.DD_RUM!.getInitConfiguration()!)
93125
expect(initConfiguration.version).toBe('js-version')
94126
})
95127

96128
createTest('should resolve an option value from localStorage')
97-
.withRum({
98-
remoteConfigurationId: 'e2e',
99-
})
129+
.withRum({ remoteConfigurationId: 'e2e' })
100130
.withRemoteConfiguration({
101131
rum: {
102132
applicationId: 'e2e',
103133
version: { rcSerializedType: 'dynamic', strategy: 'localStorage', key: 'dd_app_version' },
104134
},
105135
})
106-
.withBody(html`
136+
.withHead(html`
137+
${seedCache({
138+
rum: {
139+
applicationId: 'e2e',
140+
version: { rcSerializedType: 'dynamic', strategy: 'localStorage', key: 'dd_app_version' },
141+
},
142+
})}
107143
<script>
108144
localStorage.setItem('dd_app_version', 'localStorage-version')
109145
</script>
110146
`)
111147
.run(async ({ page }) => {
112-
await waitForRemoteConfigurationToBeApplied(page)
113148
const initConfiguration = await page.evaluate(() => window.DD_RUM!.getInitConfiguration()!)
114149
expect(initConfiguration.version).toBe('localStorage-version')
115150
})
116151

117152
createTest('should resolve an option value from localStorage with an extractor')
118-
.withRum({
119-
remoteConfigurationId: 'e2e',
120-
})
153+
.withRum({ remoteConfigurationId: 'e2e' })
121154
.withRemoteConfiguration({
122155
rum: {
123156
applicationId: 'e2e',
@@ -129,86 +162,114 @@ test.describe('remote configuration', () => {
129162
},
130163
},
131164
})
132-
.withBody(html`
165+
.withHead(html`
166+
${seedCache({
167+
rum: {
168+
applicationId: 'e2e',
169+
version: {
170+
rcSerializedType: 'dynamic',
171+
strategy: 'localStorage',
172+
key: 'dd_app_version',
173+
extractor: { rcSerializedType: 'regex', value: '\\d+\\.\\d+\\.\\d+' },
174+
},
175+
},
176+
})}
133177
<script>
134178
localStorage.setItem('dd_app_version', 'version-1.2.3-beta')
135179
</script>
136180
`)
137181
.run(async ({ page }) => {
138-
await waitForRemoteConfigurationToBeApplied(page)
139182
const initConfiguration = await page.evaluate(() => window.DD_RUM!.getInitConfiguration()!)
140183
expect(initConfiguration.version).toBe('1.2.3')
141184
})
142185

143186
createTest('should resolve to undefined when localStorage key is missing')
144-
.withRum({
145-
remoteConfigurationId: 'e2e',
146-
})
187+
.withRum({ remoteConfigurationId: 'e2e' })
147188
.withRemoteConfiguration({
148189
rum: {
149190
applicationId: 'e2e',
150191
version: { rcSerializedType: 'dynamic', strategy: 'localStorage', key: 'non_existent_key' },
151192
},
152193
})
194+
.withHead(
195+
seedCache({
196+
rum: {
197+
applicationId: 'e2e',
198+
version: { rcSerializedType: 'dynamic', strategy: 'localStorage', key: 'non_existent_key' },
199+
},
200+
})
201+
)
153202
.run(async ({ page }) => {
154-
await waitForRemoteConfigurationToBeApplied(page)
155203
const initConfiguration = await page.evaluate(() => window.DD_RUM!.getInitConfiguration()!)
156204
expect(initConfiguration.version).toBeUndefined()
157205
})
158206

159207
createTest('should resolve user context')
160-
.withRum({
161-
remoteConfigurationId: 'e2e',
162-
})
208+
.withRum({ remoteConfigurationId: 'e2e' })
163209
.withRemoteConfiguration({
164210
rum: {
165211
applicationId: RC_APP_ID,
166212
user: [{ key: 'id', value: { rcSerializedType: 'dynamic', strategy: 'cookie', name: 'e2e_rc' } }],
167213
},
168214
})
169-
.withBody(html`
215+
.withHead(html`
216+
${seedCache({
217+
rum: {
218+
applicationId: RC_APP_ID,
219+
user: [{ key: 'id', value: { rcSerializedType: 'dynamic', strategy: 'cookie', name: 'e2e_rc' } }],
220+
},
221+
})}
170222
<script>
171223
document.cookie = 'e2e_rc=my-user-id;'
172224
</script>
173225
`)
174226
.run(async ({ page }) => {
175-
await waitForRemoteConfigurationToBeApplied(page)
176227
const user = await page.evaluate(() => window.DD_RUM!.getUser())
177228
expect(user.id).toBe('my-user-id')
178229
})
179230

180231
createTest('should resolve global context')
181-
.withRum({
182-
remoteConfigurationId: 'e2e',
183-
})
232+
.withRum({ remoteConfigurationId: 'e2e' })
184233
.withRemoteConfiguration({
185234
rum: {
186235
applicationId: RC_APP_ID,
187-
context: [
188-
{
189-
key: 'foo',
190-
value: { rcSerializedType: 'dynamic', strategy: 'cookie', name: 'e2e_rc' },
191-
},
192-
],
236+
context: [{ key: 'foo', value: { rcSerializedType: 'dynamic', strategy: 'cookie', name: 'e2e_rc' } }],
193237
},
194238
})
195-
.withBody(html`
239+
.withHead(html`
240+
${seedCache({
241+
rum: {
242+
applicationId: RC_APP_ID,
243+
context: [{ key: 'foo', value: { rcSerializedType: 'dynamic', strategy: 'cookie', name: 'e2e_rc' } }],
244+
},
245+
})}
196246
<script>
197247
document.cookie = 'e2e_rc=bar;'
198248
</script>
199249
`)
200250
.run(async ({ page }) => {
201-
await waitForRemoteConfigurationToBeApplied(page)
202251
const globalContext = await page.evaluate(() => window.DD_RUM!.getGlobalContext())
203252
expect(globalContext.foo).toEqual('bar')
204253
})
205254
})
206255

256+
/* Embeds a synchronous <script> that pre-populates the remote-configuration cache before the SDK
257+
* loads. It must run before the SDK script in <head>, so pass the returned HTML through .withHead().
258+
* JSON.stringify() is applied twice on purpose: the inner call serializes the cache entry, the
259+
* outer one wraps it as a valid JS string literal with proper escaping.
260+
*/
261+
function seedCache(remoteConfig: RemoteConfiguration) {
262+
const entry = JSON.stringify({ version: 1, config: remoteConfig.rum, fetchedAt: 1000 })
263+
return html`<script>
264+
localStorage.setItem('${CACHE_KEY}', ${JSON.stringify(entry)})
265+
</script>`
266+
}
267+
207268
/* The background fetch on the initial page load writes the remote configuration into localStorage;
208269
* reloading lets the SDK pick it up synchronously on the next init().
209270
*/
210271
async function waitForRemoteConfigurationToBeApplied(page: Page) {
211272
await page.waitForFunction((key) => localStorage.getItem(key) !== null, CACHE_KEY)
212273
await page.reload()
213274
await waitForServersIdle()
214-
}
275+
}

0 commit comments

Comments
 (0)