Skip to content

Commit 7ec8d02

Browse files
committed
Working patch and test for getVariant
1 parent e464187 commit 7ec8d02

File tree

2 files changed

+68
-6
lines changed
  • dev-packages/browser-integration-tests/suites/integrations/featureFlags/unleash/basic
  • packages/browser/src/integrations/featureFlags/unleash

2 files changed

+68
-6
lines changed

dev-packages/browser-integration-tests/suites/integrations/featureFlags/unleash/basic/test.ts

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { envelopeRequestParser, shouldSkipFeatureFlagsTest, waitForErrorRequest
66

77
const FLAG_BUFFER_SIZE = 100; // Corresponds to constant in featureFlags.ts, in browser utils.
88

9-
sentryTest('Basic test with eviction, update, and no async tasks', async ({ getLocalTestUrl, page }) => {
9+
sentryTest('Basic isEnabled test with eviction, update, and no async tasks', async ({ getLocalTestUrl, page }) => {
1010
if (shouldSkipFeatureFlagsTest()) {
1111
sentryTest.skip();
1212
}
@@ -45,3 +45,43 @@ sentryTest('Basic test with eviction, update, and no async tasks', async ({ getL
4545

4646
expect(event.contexts?.flags?.values).toEqual(expectedFlags);
4747
});
48+
49+
sentryTest('Basic getVariant test with eviction, update, and no async tasks', async ({ getLocalTestUrl, page }) => {
50+
if (shouldSkipFeatureFlagsTest()) {
51+
sentryTest.skip();
52+
}
53+
54+
await page.route('https://dsn.ingest.sentry.io/**/*', route => {
55+
return route.fulfill({
56+
status: 200,
57+
contentType: 'application/json',
58+
body: JSON.stringify({ id: 'test-id' }),
59+
});
60+
});
61+
62+
const url = await getLocalTestUrl({ testDir: __dirname, skipDsnRouteHandler: true });
63+
await page.goto(url);
64+
65+
await page.evaluate(bufferSize => {
66+
const client = new (window as any).UnleashClient();
67+
for (let i = 1; i <= bufferSize; i++) {
68+
client.getVariant(`feat${i}`);
69+
}
70+
client.getVariant(`feat${bufferSize + 1}`); // eviction
71+
client.getVariant('feat3'); // update
72+
}, FLAG_BUFFER_SIZE);
73+
74+
const reqPromise = waitForErrorRequest(page);
75+
await page.locator('#error').click();
76+
const req = await reqPromise;
77+
const event = envelopeRequestParser(req);
78+
79+
const expectedFlags = [{ flag: 'feat2', result: false }];
80+
for (let i = 4; i <= FLAG_BUFFER_SIZE; i++) {
81+
expectedFlags.push({ flag: `feat${i}`, result: false });
82+
}
83+
expectedFlags.push({ flag: `feat${FLAG_BUFFER_SIZE + 1}`, result: false });
84+
expectedFlags.push({ flag: 'feat3', result: false });
85+
86+
expect(event.contexts?.flags?.values).toEqual(expectedFlags);
87+
});

packages/browser/src/integrations/featureFlags/unleash/integration.ts

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,28 @@ import type { Client, Event, EventHint, IntegrationFn } from '@sentry/core';
22

33
import { defineIntegration } from '@sentry/core';
44
import { copyFlagsFromScopeToEvent, insertFlagToScope } from '../../../utils/featureFlags';
5-
import type { UnleashClient, UnleashClientClass } from './types';
5+
import type { IVariant, UnleashClient, UnleashClientClass } from './types';
66

77
/**
8-
* Sentry integration for capturing feature flags from the Unleash SDK.
8+
* Sentry integration for capturing feature flag evaluations from the Unleash SDK.
99
*
1010
* See the [feature flag documentation](https://develop.sentry.dev/sdk/expected-features/#feature-flags) for more information.
1111
*
1212
* @example
1313
* ```
14+
* import { UnleashClient } from 'unleash-proxy-client';
1415
* import * as Sentry from '@sentry/browser';
15-
* TODO:
16+
*
17+
* const unleashIntegration = Sentry.unleashIntegration(UnleashClient);
1618
*
1719
* Sentry.init({
1820
* dsn: '___PUBLIC_DSN___',
19-
* integrations: [TODO:]
21+
* integrations: [unleashIntegration],
2022
* });
23+
*
24+
* const unleashClient = new UnleashClient(...);
25+
* unleashClient.isEnabled('my-feature');
26+
* Sentry.captureException(new Error('something went wrong'));
2127
* ```
2228
*/
2329
export const unleashIntegration = defineIntegration((unleashClientClass: UnleashClientClass) => {
@@ -29,6 +35,8 @@ export const unleashIntegration = defineIntegration((unleashClientClass: Unleash
2935
},
3036

3137
setupOnce() {
38+
const unleashClientPrototype = unleashClientClass.prototype as UnleashClient;
39+
3240
const sentryIsEnabled = {
3341
apply: (
3442
target: (this: UnleashClient, toggleName: string) => boolean,
@@ -40,9 +48,23 @@ export const unleashIntegration = defineIntegration((unleashClientClass: Unleash
4048
return result;
4149
},
4250
};
43-
const unleashClientPrototype = unleashClientClass.prototype as UnleashClient;
4451
const originalIsEnabled = unleashClientPrototype.isEnabled.bind(unleashClientPrototype);
4552
unleashClientPrototype.isEnabled = new Proxy(originalIsEnabled, sentryIsEnabled);
53+
54+
const sentryGetVariant = {
55+
apply: (
56+
target: (this: UnleashClient, toggleName: string) => IVariant,
57+
thisArg: UnleashClient,
58+
args: [toggleName: string],
59+
) => {
60+
const variant = Reflect.apply(target, thisArg, args);
61+
const result = variant.enabled;
62+
insertFlagToScope(args[0], result);
63+
return variant;
64+
},
65+
};
66+
const originalGetVariant = unleashClientPrototype.getVariant.bind(unleashClientPrototype);
67+
unleashClientPrototype.getVariant = new Proxy(originalGetVariant, sentryGetVariant);
4668
},
4769
};
4870
}) satisfies IntegrationFn;

0 commit comments

Comments
 (0)