Skip to content

Commit c27f766

Browse files
committed
Init browser-integration-tests
1 parent 334aa51 commit c27f766

File tree

8 files changed

+119
-7
lines changed

8 files changed

+119
-7
lines changed

dev-packages/browser-integration-tests/package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,12 @@
5151
"webpack": "^5.95.0"
5252
},
5353
"devDependencies": {
54+
"@sentry/types": "8.37.1",
5455
"@types/glob": "8.0.0",
5556
"@types/node": "^14.18.0",
5657
"@types/pako": "^2.0.0",
57-
"glob": "8.0.3"
58+
"glob": "8.0.3",
59+
"launchdarkly-js-client-sdk": "^3.5.0"
5860
},
5961
"volta": {
6062
"extends": "../../package.json"
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import * as Sentry from '@sentry/browser';
2+
import { launchDarklyIntegration } from '@sentry/browser';
3+
4+
window.Sentry = Sentry;
5+
window.LDIntegration = launchDarklyIntegration();
6+
7+
Sentry.init({
8+
dsn: 'https://[email protected]/1337',
9+
sampleRate: 1.0,
10+
integrations: [window.LDIntegration],
11+
});
12+
13+
// TODO: can't type this unless we duplicate to every test.ts file
14+
window.MockLaunchDarkly = {
15+
initialize(_clientId, context, options) {
16+
// const flagUsedHandler = options?.inspectors?.[0].method;
17+
18+
return {
19+
variation(key, defaultValue) {
20+
// flagUsedHandler?.(key, { value: defaultValue }, context);
21+
return defaultValue;
22+
},
23+
};
24+
},
25+
};
26+
27+
console.log(window.MockLaunchDarkly)
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
document.getElementById('error').addEventListener('click', () => {
2+
throw new Error('Button triggered error');
3+
});
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<meta charset="utf-8" />
5+
</head>
6+
<body>
7+
<button id="error">Throw Error</button>
8+
</body>
9+
</html>
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import { expect } from '@playwright/test';
2+
3+
import { sentryTest } from '../../../../utils/fixtures';
4+
5+
import { envelopeRequestParser, waitForErrorRequest } from '../../../../utils/helpers';
6+
import { buildLaunchDarklyFlagUsedHandler } from '@sentry/browser';
7+
import type { LDContext, LDOptions, LDFlagValue, LDClient } from 'launchdarkly-js-client-sdk';
8+
import type { Event } from '@sentry/types';
9+
10+
// const MockLaunchDarkly = { //TODO:
11+
// initialize(
12+
// _clientId: string,
13+
// context: LDContext,
14+
// options: LDOptions,
15+
// ) {
16+
// const flagUsedHandler = options?.inspectors?.[0].method;
17+
18+
// return {
19+
// variation(key: string, defaultValue: LDFlagValue) {
20+
// flagUsedHandler?.(key, { value: defaultValue }, context);
21+
// return defaultValue;
22+
// },
23+
// };
24+
// },
25+
// };
26+
27+
sentryTest('e2e test', async ({ getLocalTestPath, page }) => {
28+
let errorEventId: string = 'invalid_id';
29+
await page.route('https://dsn.ingest.sentry.io/**/*', route => {
30+
const event = envelopeRequestParser(route.request());
31+
// error events have no type field
32+
if (event && !event.type && event.event_id) {
33+
errorEventId = event.event_id;
34+
}
35+
36+
return route.fulfill({
37+
status: 200,
38+
contentType: 'application/json',
39+
body: JSON.stringify({ id: 'test-id' }),
40+
});
41+
});
42+
43+
const url = await getLocalTestPath({ testDir: __dirname, skipDsnRouteHandler: true });
44+
await page.goto(url);
45+
46+
// TODO: could this be in init.js?
47+
const ldClient = await page.evaluate(() => {
48+
return (window as any).MockLaunchDarkly.initialize(
49+
'example-client-id',
50+
{ kind: 'user', key: 'example-context-key' },
51+
{ inspectors: [buildLaunchDarklyFlagUsedHandler()] },
52+
) as LDClient;
53+
});
54+
55+
ldClient.variation('feat1', false);
56+
ldClient.variation('feat2', false);
57+
ldClient.variation('feat3', false);
58+
ldClient.variation('feat2', true);
59+
// TODO: eviction not tested
60+
61+
// trigger error
62+
await page.locator('#error').click();
63+
64+
const req = await waitForErrorRequest(page);
65+
const event = envelopeRequestParser(req);
66+
67+
expect(event.contexts?.flags?.values).toEqual([
68+
{ flag: 'feat1', result: false },
69+
{ flag: 'feat3', result: false },
70+
{ flag: 'feat2', result: true },
71+
]);
72+
});

package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,6 @@
6363
"packages/gatsby",
6464
"packages/google-cloud-serverless",
6565
"packages/integration-shims",
66-
"packages/launchdarkly",
6766
"packages/nestjs",
6867
"packages/nextjs",
6968
"packages/node",

packages/browser/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,4 +75,4 @@ export type { Span } from '@sentry/types';
7575
export { makeBrowserOfflineTransport } from './transports/offline';
7676
export { browserProfilingIntegration } from './profiling/integration';
7777
export { spotlightBrowserIntegration } from './integrations/spotlight';
78-
export { launchDarklyIntegration, buildLaunchDarklyFlagUsedInspector } from './integrations/launchdarkly';
78+
export { launchDarklyIntegration, buildLaunchDarklyFlagUsedHandler } from './integrations/launchdarkly';

packages/browser/src/integrations/launchdarkly.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import { defineIntegration, getCurrentScope } from '@sentry/core';
1616
* import * as LaunchDarkly from 'launchdarkly-js-client-sdk';
1717
*
1818
* Sentry.init(..., integrations: [launchDarklyIntegration()])
19-
* const ldClient = LaunchDarkly.initialize(..., {inspectors: [buildLaunchDarklyFlagUsedInspector()]});
19+
* const ldClient = LaunchDarkly.initialize(..., {inspectors: [buildLaunchDarklyFlagUsedHandler()]});
2020
* ```
2121
*/
2222
export const launchDarklyIntegration = defineIntegration(() => {
@@ -43,13 +43,14 @@ export const launchDarklyIntegration = defineIntegration(() => {
4343
* This needs to be registered separately in the LD SDK initialize() options,
4444
* after initializing Sentry.
4545
*/
46-
export function buildLaunchDarklyFlagUsedInspector(): LDInspectionFlagUsedHandler {
46+
export function buildLaunchDarklyFlagUsedHandler(): LDInspectionFlagUsedHandler {
4747
return {
4848
name: 'sentry-flag-auditor',
4949
type: 'flag-used',
5050

5151
// We don't want the handler to impact the performance of the user's flag evaluations.
52-
synchronous: false,
52+
synchronous: false, // TODO: this could lead to race conditions where an error directly after an eval might not contain the eval
53+
// TODO: the flag buffer itself isn't thread-safe, yet this handler and the event processor could access it at the same time.
5354

5455
/**
5556
* Handle a flag evaluation by storing its name and value on the current scope.
@@ -62,7 +63,6 @@ export function buildLaunchDarklyFlagUsedInspector(): LDInspectionFlagUsedHandle
6263
}
6364
const flagBuffer = scopeContexts.flags.values;
6465
insertToFlagBuffer(flagBuffer, flagKey, flagDetail.value);
65-
console.log('inserted')
6666
}
6767
return;
6868
},

0 commit comments

Comments
 (0)