@servicetitan/launchdarkly-service 30.3.1 → 31.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/create-flag-keys.js +5 -1
- package/dist/create-flag-keys.js.map +1 -1
- package/dist/feature-flag-provider.d.ts.map +1 -1
- package/dist/feature-flag-provider.js +10 -4
- package/dist/feature-flag-provider.js.map +1 -1
- package/dist/feature-flag-store.js +32 -29
- package/dist/feature-flag-store.js.map +1 -1
- package/dist/get-ld-client.js +5 -1
- package/dist/get-ld-client.js.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/ld-provider.js +33 -17
- package/dist/ld-provider.js.map +1 -1
- package/dist/ld-service-provider.js +10 -3
- package/dist/ld-service-provider.js.map +1 -1
- package/dist/tests/feature-flag-provider.test.d.ts +2 -0
- package/dist/tests/feature-flag-provider.test.d.ts.map +1 -0
- package/dist/tests/feature-flag-provider.test.js +46 -0
- package/dist/tests/feature-flag-provider.test.js.map +1 -0
- package/dist/tests/feature-flag-store.test.d.ts +2 -0
- package/dist/tests/feature-flag-store.test.d.ts.map +1 -0
- package/dist/tests/feature-flag-store.test.js +32 -0
- package/dist/tests/feature-flag-store.test.js.map +1 -0
- package/dist/tests/get-ld-client.test.d.ts +2 -0
- package/dist/tests/get-ld-client.test.d.ts.map +1 -0
- package/dist/tests/get-ld-client.test.js +61 -0
- package/dist/tests/get-ld-client.test.js.map +1 -0
- package/dist/tests/ld-provider.test.d.ts +2 -0
- package/dist/tests/ld-provider.test.d.ts.map +1 -0
- package/dist/tests/ld-provider.test.js +182 -0
- package/dist/tests/ld-provider.test.js.map +1 -0
- package/dist/tests/ld-service-provider.test.d.ts +2 -0
- package/dist/tests/ld-service-provider.test.d.ts.map +1 -0
- package/dist/tests/ld-service-provider.test.js +42 -0
- package/dist/tests/ld-service-provider.test.js.map +1 -0
- package/dist/tests/with-feature-flag.test.d.ts +2 -0
- package/dist/tests/with-feature-flag.test.d.ts.map +1 -0
- package/dist/tests/with-feature-flag.test.js +73 -0
- package/dist/tests/with-feature-flag.test.js.map +1 -0
- package/dist/types.js +1 -0
- package/dist/types.js.map +1 -1
- package/dist/with-feature-flag.js +3 -2
- package/dist/with-feature-flag.js.map +1 -1
- package/package.json +6 -6
- package/src/feature-flag-provider.tsx +4 -2
package/dist/create-flag-keys.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"
|
|
1
|
+
{"version":3,"sources":["../src/create-flag-keys.ts"],"sourcesContent":["import { FlagKey } from './types';\n\ntype FlagKeys<S> = {\n [K in keyof S]: FlagKey<S, K>;\n};\n\nexport function createFlagKeys<S>(...keys: (keyof S)[]): FlagKeys<S> {\n return keys.reduce((result, key) => ({ ...result, [key]: key }), {} as FlagKeys<S>);\n}\n"],"names":["createFlagKeys","keys","reduce","result","key"],"mappings":"AAMA,OAAO,SAASA,eAAkB,GAAGC,IAAiB;IAClD,OAAOA,KAAKC,MAAM,CAAC,CAACC,QAAQC,MAAS,CAAA;YAAE,GAAGD,MAAM;YAAE,CAACC,IAAI,EAAEA;QAAI,CAAA,GAAI,CAAC;AACtE"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"feature-flag-provider.d.ts","sourceRoot":"","sources":["../src/feature-flag-provider.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,iBAAiB,
|
|
1
|
+
{"version":3,"file":"feature-flag-provider.d.ts","sourceRoot":"","sources":["../src/feature-flag-provider.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,iBAAiB,EAAuB,MAAM,OAAO,CAAC;AAI/D,wBAAgB,mBAAmB,CAAC,EAAE,QAAQ,EAAE,EAAE,iBAAiB,6BAWlE"}
|
|
@@ -1,13 +1,19 @@
|
|
|
1
1
|
import { useDependencies } from '@servicetitan/react-ioc';
|
|
2
2
|
import { useFlags } from 'launchdarkly-react-client-sdk';
|
|
3
|
-
import { useEffect } from 'react';
|
|
3
|
+
import { useEffect, useState } from 'react';
|
|
4
4
|
import { FeatureFlagStore } from './feature-flag-store';
|
|
5
5
|
export function FeatureFlagProvider({ children }) {
|
|
6
|
+
const [initialized, setInitialized] = useState(false);
|
|
6
7
|
const [{ setFlags }] = useDependencies(FeatureFlagStore);
|
|
7
8
|
const flags = useFlags();
|
|
8
|
-
useEffect(()
|
|
9
|
+
useEffect(()=>{
|
|
9
10
|
setFlags(flags);
|
|
10
|
-
|
|
11
|
-
|
|
11
|
+
setInitialized(true);
|
|
12
|
+
}, [
|
|
13
|
+
flags,
|
|
14
|
+
setFlags
|
|
15
|
+
]);
|
|
16
|
+
return initialized ? children : null;
|
|
12
17
|
}
|
|
18
|
+
|
|
13
19
|
//# sourceMappingURL=feature-flag-provider.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"
|
|
1
|
+
{"version":3,"sources":["../src/feature-flag-provider.tsx"],"sourcesContent":["import { useDependencies } from '@servicetitan/react-ioc';\nimport { useFlags } from 'launchdarkly-react-client-sdk';\nimport { PropsWithChildren, useEffect, useState } from 'react';\n\nimport { FeatureFlagStore } from './feature-flag-store';\n\nexport function FeatureFlagProvider({ children }: PropsWithChildren) {\n const [initialized, setInitialized] = useState(false);\n const [{ setFlags }] = useDependencies(FeatureFlagStore);\n const flags = useFlags();\n\n useEffect(() => {\n setFlags(flags);\n setInitialized(true);\n }, [flags, setFlags]);\n\n return initialized ? children : null;\n}\n"],"names":["useDependencies","useFlags","useEffect","useState","FeatureFlagStore","FeatureFlagProvider","children","initialized","setInitialized","setFlags","flags"],"mappings":"AAAA,SAASA,eAAe,QAAQ,0BAA0B;AAC1D,SAASC,QAAQ,QAAQ,gCAAgC;AACzD,SAA4BC,SAAS,EAAEC,QAAQ,QAAQ,QAAQ;AAE/D,SAASC,gBAAgB,QAAQ,uBAAuB;AAExD,OAAO,SAASC,oBAAoB,EAAEC,QAAQ,EAAqB;IAC/D,MAAM,CAACC,aAAaC,eAAe,GAAGL,SAAS;IAC/C,MAAM,CAAC,EAAEM,QAAQ,EAAE,CAAC,GAAGT,gBAAgBI;IACvC,MAAMM,QAAQT;IAEdC,UAAU;QACNO,SAASC;QACTF,eAAe;IACnB,GAAG;QAACE;QAAOD;KAAS;IAEpB,OAAOF,cAAcD,WAAW;AACpC"}
|
|
@@ -1,45 +1,48 @@
|
|
|
1
|
-
|
|
1
|
+
function _define_property(obj, key, value) {
|
|
2
|
+
if (key in obj) {
|
|
3
|
+
Object.defineProperty(obj, key, {
|
|
4
|
+
value: value,
|
|
5
|
+
enumerable: true,
|
|
6
|
+
configurable: true,
|
|
7
|
+
writable: true
|
|
8
|
+
});
|
|
9
|
+
} else {
|
|
10
|
+
obj[key] = value;
|
|
11
|
+
}
|
|
12
|
+
return obj;
|
|
13
|
+
}
|
|
14
|
+
function _ts_decorate(decorators, target, key, desc) {
|
|
2
15
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
16
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
-
else for
|
|
17
|
+
else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
18
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
-
}
|
|
7
|
-
|
|
19
|
+
}
|
|
20
|
+
function _ts_metadata(k, v) {
|
|
8
21
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
|
-
}
|
|
22
|
+
}
|
|
10
23
|
import { injectable } from '@servicetitan/react-ioc';
|
|
11
24
|
import { action, makeObservable, observable } from 'mobx';
|
|
12
|
-
|
|
13
|
-
constructor()
|
|
25
|
+
export class FeatureFlagStore {
|
|
26
|
+
constructor(){
|
|
14
27
|
// @ts-expect-error because 'LDFlagSet' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
writable: true,
|
|
19
|
-
value: {}
|
|
20
|
-
});
|
|
21
|
-
Object.defineProperty(this, "setFlags", {
|
|
22
|
-
enumerable: true,
|
|
23
|
-
configurable: true,
|
|
24
|
-
writable: true,
|
|
25
|
-
value: (flags) => {
|
|
26
|
-
this.flags = flags;
|
|
27
|
-
}
|
|
28
|
+
_define_property(this, "flags", {});
|
|
29
|
+
_define_property(this, "setFlags", (flags)=>{
|
|
30
|
+
this.flags = flags;
|
|
28
31
|
});
|
|
29
32
|
makeObservable(this);
|
|
30
33
|
}
|
|
31
|
-
}
|
|
32
|
-
|
|
34
|
+
}
|
|
35
|
+
_ts_decorate([
|
|
33
36
|
observable,
|
|
34
|
-
|
|
37
|
+
_ts_metadata("design:type", typeof T === "undefined" ? Object : T)
|
|
35
38
|
], FeatureFlagStore.prototype, "flags", void 0);
|
|
36
|
-
|
|
37
|
-
action
|
|
38
|
-
__metadata("design:type", Object)
|
|
39
|
+
_ts_decorate([
|
|
40
|
+
action
|
|
39
41
|
], FeatureFlagStore.prototype, "setFlags", void 0);
|
|
40
|
-
FeatureFlagStore =
|
|
42
|
+
FeatureFlagStore = _ts_decorate([
|
|
41
43
|
injectable(),
|
|
42
|
-
|
|
44
|
+
_ts_metadata("design:type", Function),
|
|
45
|
+
_ts_metadata("design:paramtypes", [])
|
|
43
46
|
], FeatureFlagStore);
|
|
44
|
-
|
|
47
|
+
|
|
45
48
|
//# sourceMappingURL=feature-flag-store.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"
|
|
1
|
+
{"version":3,"sources":["../src/feature-flag-store.ts"],"sourcesContent":["import { injectable } from '@servicetitan/react-ioc';\nimport { LDFlagSet } from 'launchdarkly-react-client-sdk';\nimport { action, makeObservable, observable } from 'mobx';\n\n@injectable()\nexport class FeatureFlagStore<T extends LDFlagSet = LDFlagSet> {\n // @ts-expect-error because 'LDFlagSet' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint\n @observable flags: T = {} as LDFlagSet;\n\n constructor() {\n makeObservable(this);\n }\n\n @action\n setFlags = (flags: T) => {\n this.flags = flags;\n };\n}\n"],"names":["injectable","action","makeObservable","observable","FeatureFlagStore","constructor","flags","setFlags"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAAA,SAASA,UAAU,QAAQ,0BAA0B;AAErD,SAASC,MAAM,EAAEC,cAAc,EAAEC,UAAU,QAAQ,OAAO;AAG1D,OAAO,MAAMC;IAITC,aAAc;QAHd,yJAAyJ;QACzJ,uBAAYC,SAAW,CAAC;QAMxB,uBACAC,YAAW,CAACD;YACR,IAAI,CAACA,KAAK,GAAGA;QACjB;QANIJ,eAAe,IAAI;IACvB;AAMJ"}
|
package/dist/get-ld-client.js
CHANGED
|
@@ -3,9 +3,13 @@ const ldClients = new Map();
|
|
|
3
3
|
export function getLDClient({ clientSideID, context, options }) {
|
|
4
4
|
let client = ldClients.get(clientSideID);
|
|
5
5
|
if (!client) {
|
|
6
|
-
client = initialize(clientSideID, context !== null && context !== void 0 ? context : {
|
|
6
|
+
client = initialize(clientSideID, context !== null && context !== void 0 ? context : {
|
|
7
|
+
kind: 'user',
|
|
8
|
+
anonymous: true
|
|
9
|
+
}, options);
|
|
7
10
|
ldClients.set(clientSideID, client);
|
|
8
11
|
}
|
|
9
12
|
return client;
|
|
10
13
|
}
|
|
14
|
+
|
|
11
15
|
//# sourceMappingURL=get-ld-client.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"
|
|
1
|
+
{"version":3,"sources":["../src/get-ld-client.ts"],"sourcesContent":["import { LDClient, initialize } from 'launchdarkly-js-client-sdk';\nimport { ProviderConfig } from 'launchdarkly-react-client-sdk';\n\nconst ldClients: Map<string, LDClient> = new Map();\n\nexport function getLDClient({ clientSideID, context, options }: ProviderConfig) {\n let client = ldClients.get(clientSideID);\n if (!client) {\n client = initialize(clientSideID, context ?? { kind: 'user', anonymous: true }, options);\n ldClients.set(clientSideID, client);\n }\n return client;\n}\n"],"names":["initialize","ldClients","Map","getLDClient","clientSideID","context","options","client","get","kind","anonymous","set"],"mappings":"AAAA,SAAmBA,UAAU,QAAQ,6BAA6B;AAGlE,MAAMC,YAAmC,IAAIC;AAE7C,OAAO,SAASC,YAAY,EAAEC,YAAY,EAAEC,OAAO,EAAEC,OAAO,EAAkB;IAC1E,IAAIC,SAASN,UAAUO,GAAG,CAACJ;IAC3B,IAAI,CAACG,QAAQ;QACTA,SAASP,WAAWI,cAAcC,oBAAAA,qBAAAA,UAAW;YAAEI,MAAM;YAAQC,WAAW;QAAK,GAAGJ;QAChFL,UAAUU,GAAG,CAACP,cAAcG;IAChC;IACA,OAAOA;AACX"}
|
package/dist/index.js
CHANGED
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["export * from './create-flag-keys';\nexport * from './feature-flag-store';\nexport * from './get-ld-client';\nexport * from './ld-provider';\nexport * from './ld-service-provider';\nexport * from './types';\nexport * from './with-feature-flag';\n"],"names":[],"mappings":"AAAA,cAAc,qBAAqB;AACnC,cAAc,uBAAuB;AACrC,cAAc,kBAAkB;AAChC,cAAc,gBAAgB;AAC9B,cAAc,wBAAwB;AACtC,cAAc,UAAU;AACxB,cAAc,sBAAsB"}
|
package/dist/ld-provider.js
CHANGED
|
@@ -1,52 +1,68 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { Log } from '@servicetitan/log-service';
|
|
3
3
|
import { provide, useOptionalDependencies } from '@servicetitan/react-ioc';
|
|
4
|
-
import { LDProvider as BaseLDProvider
|
|
4
|
+
import { LDProvider as BaseLDProvider } from 'launchdarkly-react-client-sdk';
|
|
5
5
|
import { useEffect, useReducer, useState } from 'react';
|
|
6
6
|
import { FeatureFlagProvider } from './feature-flag-provider';
|
|
7
7
|
import { FeatureFlagStore } from './feature-flag-store';
|
|
8
8
|
import { getLDClient } from './get-ld-client';
|
|
9
9
|
import { LD_SERVICE_TOKEN } from './types';
|
|
10
10
|
export const LDProvider = provide({
|
|
11
|
-
singletons: [
|
|
12
|
-
|
|
11
|
+
singletons: [
|
|
12
|
+
FeatureFlagStore
|
|
13
|
+
]
|
|
14
|
+
})((props)=>{
|
|
13
15
|
const { children, clientSideID, context, projectName, waitForInitialization = true } = props;
|
|
14
16
|
const [ldClient, setLDClient] = useState();
|
|
15
17
|
const [ldService, log] = useOptionalDependencies(LD_SERVICE_TOKEN, Log);
|
|
16
|
-
const [counter, forceUpdate] = useReducer((x)
|
|
17
|
-
useEffect(()
|
|
18
|
+
const [counter, forceUpdate] = useReducer((x)=>x + 1, 0);
|
|
19
|
+
useEffect(()=>{
|
|
18
20
|
(async function initializeClient() {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
21
|
+
const config = {
|
|
22
|
+
clientSideID,
|
|
23
|
+
context
|
|
24
|
+
};
|
|
25
|
+
var _ldService_getClient;
|
|
26
|
+
const client = (_ldService_getClient = ldService === null || ldService === void 0 ? void 0 : ldService.getClient(config)) !== null && _ldService_getClient !== void 0 ? _ldService_getClient : getLDClient(config);
|
|
22
27
|
if (waitForInitialization) {
|
|
23
28
|
try {
|
|
24
29
|
await client.waitForInitialization(5);
|
|
25
|
-
}
|
|
26
|
-
catch (error) {
|
|
30
|
+
} catch (error) {
|
|
27
31
|
log === null || log === void 0 ? void 0 : log.error({
|
|
28
32
|
category: 'LaunchDarkly',
|
|
29
33
|
code: 'InitializationFailed',
|
|
30
34
|
error,
|
|
31
|
-
message: `"${projectName}" client failed to initialize
|
|
35
|
+
message: `"${projectName}" client failed to initialize`
|
|
32
36
|
});
|
|
33
37
|
}
|
|
34
38
|
}
|
|
35
39
|
setLDClient(client);
|
|
36
40
|
})();
|
|
37
|
-
|
|
38
|
-
}, [
|
|
39
|
-
|
|
41
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
42
|
+
}, [
|
|
43
|
+
clientSideID
|
|
44
|
+
]); // only reload if clientSideID changes
|
|
45
|
+
useEffect(()=>{
|
|
40
46
|
if (!waitForInitialization && ldClient) {
|
|
41
47
|
ldClient.on('initialized', forceUpdate);
|
|
42
|
-
return ()
|
|
48
|
+
return ()=>{
|
|
43
49
|
ldClient.off('initialized', forceUpdate);
|
|
44
50
|
};
|
|
45
51
|
}
|
|
46
|
-
}, [
|
|
52
|
+
}, [
|
|
53
|
+
ldClient,
|
|
54
|
+
waitForInitialization
|
|
55
|
+
]);
|
|
47
56
|
if (!ldClient) {
|
|
48
57
|
return null;
|
|
49
58
|
}
|
|
50
|
-
return
|
|
59
|
+
return /*#__PURE__*/ _jsx(BaseLDProvider, {
|
|
60
|
+
clientSideID: clientSideID,
|
|
61
|
+
ldClient: ldClient,
|
|
62
|
+
children: /*#__PURE__*/ _jsx(FeatureFlagProvider, {
|
|
63
|
+
children: children
|
|
64
|
+
})
|
|
65
|
+
}, counter.toString());
|
|
51
66
|
});
|
|
67
|
+
|
|
52
68
|
//# sourceMappingURL=ld-provider.js.map
|
package/dist/ld-provider.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"
|
|
1
|
+
{"version":3,"sources":["../src/ld-provider.tsx"],"sourcesContent":["import { Log } from '@servicetitan/log-service';\nimport { provide, useOptionalDependencies } from '@servicetitan/react-ioc';\nimport { LDClient } from 'launchdarkly-js-client-sdk';\nimport {\n LDProvider as BaseLDProvider,\n ProviderConfig as BaseProviderConfig,\n} from 'launchdarkly-react-client-sdk';\nimport { FC, PropsWithChildren, useEffect, useReducer, useState } from 'react';\n\nimport { FeatureFlagProvider } from './feature-flag-provider';\nimport { FeatureFlagStore } from './feature-flag-store';\nimport { getLDClient } from './get-ld-client';\nimport { LD_SERVICE_TOKEN } from './types';\n\nexport type ProviderConfig = Pick<BaseProviderConfig, 'clientSideID' | 'context'> & {\n projectName: string;\n waitForInitialization?: boolean;\n};\n\nexport const LDProvider: FC<PropsWithChildren<ProviderConfig>> = provide({\n singletons: [FeatureFlagStore],\n})(props => {\n const { children, clientSideID, context, projectName, waitForInitialization = true } = props;\n const [ldClient, setLDClient] = useState<LDClient>();\n const [ldService, log] = useOptionalDependencies(LD_SERVICE_TOKEN, Log);\n const [counter, forceUpdate] = useReducer((x: number) => x + 1, 0);\n\n useEffect(() => {\n (async function initializeClient() {\n const config = { clientSideID, context };\n const client = ldService?.getClient(config) ?? getLDClient(config);\n if (waitForInitialization) {\n try {\n await client.waitForInitialization(5);\n } catch (error) {\n log?.error({\n category: 'LaunchDarkly',\n code: 'InitializationFailed',\n error,\n message: `\"${projectName}\" client failed to initialize`,\n });\n }\n }\n setLDClient(client);\n })();\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [clientSideID]); // only reload if clientSideID changes\n\n useEffect(() => {\n if (!waitForInitialization && ldClient) {\n ldClient.on('initialized', forceUpdate);\n return () => {\n ldClient.off('initialized', forceUpdate);\n };\n }\n }, [ldClient, waitForInitialization]);\n\n if (!ldClient) {\n return null;\n }\n\n return (\n <BaseLDProvider key={counter.toString()} clientSideID={clientSideID} ldClient={ldClient}>\n <FeatureFlagProvider>{children}</FeatureFlagProvider>\n </BaseLDProvider>\n );\n});\n"],"names":["Log","provide","useOptionalDependencies","LDProvider","BaseLDProvider","useEffect","useReducer","useState","FeatureFlagProvider","FeatureFlagStore","getLDClient","LD_SERVICE_TOKEN","singletons","props","children","clientSideID","context","projectName","waitForInitialization","ldClient","setLDClient","ldService","log","counter","forceUpdate","x","initializeClient","config","client","getClient","error","category","code","message","on","off","toString"],"mappings":";AAAA,SAASA,GAAG,QAAQ,4BAA4B;AAChD,SAASC,OAAO,EAAEC,uBAAuB,QAAQ,0BAA0B;AAE3E,SACIC,cAAcC,cAAc,QAEzB,gCAAgC;AACvC,SAAgCC,SAAS,EAAEC,UAAU,EAAEC,QAAQ,QAAQ,QAAQ;AAE/E,SAASC,mBAAmB,QAAQ,0BAA0B;AAC9D,SAASC,gBAAgB,QAAQ,uBAAuB;AACxD,SAASC,WAAW,QAAQ,kBAAkB;AAC9C,SAASC,gBAAgB,QAAQ,UAAU;AAO3C,OAAO,MAAMR,aAAoDF,QAAQ;IACrEW,YAAY;QAACH;KAAiB;AAClC,GAAGI,CAAAA;IACC,MAAM,EAAEC,QAAQ,EAAEC,YAAY,EAAEC,OAAO,EAAEC,WAAW,EAAEC,wBAAwB,IAAI,EAAE,GAAGL;IACvF,MAAM,CAACM,UAAUC,YAAY,GAAGb;IAChC,MAAM,CAACc,WAAWC,IAAI,GAAGpB,wBAAwBS,kBAAkBX;IACnE,MAAM,CAACuB,SAASC,YAAY,GAAGlB,WAAW,CAACmB,IAAcA,IAAI,GAAG;IAEhEpB,UAAU;QACL,CAAA,eAAeqB;YACZ,MAAMC,SAAS;gBAAEZ;gBAAcC;YAAQ;gBACxBK;YAAf,MAAMO,SAASP,CAAAA,uBAAAA,sBAAAA,gCAAAA,UAAWQ,SAAS,CAACF,qBAArBN,kCAAAA,uBAAgCX,YAAYiB;YAC3D,IAAIT,uBAAuB;gBACvB,IAAI;oBACA,MAAMU,OAAOV,qBAAqB,CAAC;gBACvC,EAAE,OAAOY,OAAO;oBACZR,gBAAAA,0BAAAA,IAAKQ,KAAK,CAAC;wBACPC,UAAU;wBACVC,MAAM;wBACNF;wBACAG,SAAS,CAAC,CAAC,EAAEhB,YAAY,6BAA6B,CAAC;oBAC3D;gBACJ;YACJ;YACAG,YAAYQ;QAChB,CAAA;IACA,uDAAuD;IAC3D,GAAG;QAACb;KAAa,GAAG,sCAAsC;IAE1DV,UAAU;QACN,IAAI,CAACa,yBAAyBC,UAAU;YACpCA,SAASe,EAAE,CAAC,eAAeV;YAC3B,OAAO;gBACHL,SAASgB,GAAG,CAAC,eAAeX;YAChC;QACJ;IACJ,GAAG;QAACL;QAAUD;KAAsB;IAEpC,IAAI,CAACC,UAAU;QACX,OAAO;IACX;IAEA,qBACI,KAACf;QAAwCW,cAAcA;QAAcI,UAAUA;kBAC3E,cAAA,KAACX;sBAAqBM;;OADLS,QAAQa,QAAQ;AAI7C,GAAG"}
|
|
@@ -2,9 +2,16 @@ import { provide } from '@servicetitan/react-ioc';
|
|
|
2
2
|
import { getLDClient } from './get-ld-client';
|
|
3
3
|
import { LD_SERVICE_TOKEN } from './types';
|
|
4
4
|
const ldService = {
|
|
5
|
-
getClient: getLDClient
|
|
5
|
+
getClient: getLDClient
|
|
6
6
|
};
|
|
7
7
|
export const LDServiceProvider = provide({
|
|
8
|
-
singletons: [
|
|
9
|
-
|
|
8
|
+
singletons: [
|
|
9
|
+
{
|
|
10
|
+
provide: LD_SERVICE_TOKEN,
|
|
11
|
+
useValue: ldService,
|
|
12
|
+
inherit: true
|
|
13
|
+
}
|
|
14
|
+
]
|
|
15
|
+
})(({ children })=>children);
|
|
16
|
+
|
|
10
17
|
//# sourceMappingURL=ld-service-provider.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"
|
|
1
|
+
{"version":3,"sources":["../src/ld-service-provider.tsx"],"sourcesContent":["import { provide } from '@servicetitan/react-ioc';\n\nimport { getLDClient } from './get-ld-client';\nimport { LD_SERVICE_TOKEN, LDService } from './types';\n\nconst ldService: LDService = {\n getClient: getLDClient,\n};\n\nexport const LDServiceProvider = provide({\n singletons: [{ provide: LD_SERVICE_TOKEN, useValue: ldService, inherit: true }],\n})(({ children }) => children);\n"],"names":["provide","getLDClient","LD_SERVICE_TOKEN","ldService","getClient","LDServiceProvider","singletons","useValue","inherit","children"],"mappings":"AAAA,SAASA,OAAO,QAAQ,0BAA0B;AAElD,SAASC,WAAW,QAAQ,kBAAkB;AAC9C,SAASC,gBAAgB,QAAmB,UAAU;AAEtD,MAAMC,YAAuB;IACzBC,WAAWH;AACf;AAEA,OAAO,MAAMI,oBAAoBL,QAAQ;IACrCM,YAAY;QAAC;YAAEN,SAASE;YAAkBK,UAAUJ;YAAWK,SAAS;QAAK;KAAE;AACnF,GAAG,CAAC,EAAEC,QAAQ,EAAE,GAAKA,UAAU"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"feature-flag-provider.test.d.ts","sourceRoot":"","sources":["../../src/tests/feature-flag-provider.test.tsx"],"names":[],"mappings":"AAAA,OAAO,2BAA2B,CAAC"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import '@testing-library/jest-dom';
|
|
3
|
+
import { Provider } from '@servicetitan/react-ioc';
|
|
4
|
+
import { render, screen } from '@testing-library/react';
|
|
5
|
+
import { FeatureFlagProvider } from '../feature-flag-provider';
|
|
6
|
+
import { FeatureFlagStore } from '../feature-flag-store';
|
|
7
|
+
import { useFlags } from 'launchdarkly-react-client-sdk';
|
|
8
|
+
jest.mock('launchdarkly-react-client-sdk', ()=>({
|
|
9
|
+
useFlags: jest.fn()
|
|
10
|
+
}));
|
|
11
|
+
describe(`[launchdarkly-service] ${FeatureFlagProvider.name}`, ()=>{
|
|
12
|
+
const flags = {
|
|
13
|
+
foo: 'bar',
|
|
14
|
+
baz: 'qux'
|
|
15
|
+
};
|
|
16
|
+
const mockFeatureFlagStore = {
|
|
17
|
+
setFlags: jest.fn()
|
|
18
|
+
};
|
|
19
|
+
const Component = ()=>{
|
|
20
|
+
return `<Component />`;
|
|
21
|
+
};
|
|
22
|
+
beforeEach(()=>{
|
|
23
|
+
jest.mocked(useFlags).mockImplementation(()=>flags);
|
|
24
|
+
});
|
|
25
|
+
const subject = ()=>render(/*#__PURE__*/ _jsx(Provider, {
|
|
26
|
+
singletons: [
|
|
27
|
+
{
|
|
28
|
+
provide: FeatureFlagStore,
|
|
29
|
+
useValue: mockFeatureFlagStore
|
|
30
|
+
}
|
|
31
|
+
],
|
|
32
|
+
children: /*#__PURE__*/ _jsx(FeatureFlagProvider, {
|
|
33
|
+
children: /*#__PURE__*/ _jsx(Component, {})
|
|
34
|
+
})
|
|
35
|
+
}));
|
|
36
|
+
test('renders children', ()=>{
|
|
37
|
+
subject();
|
|
38
|
+
expect(screen.getByText(`<Component />`)).toBeInTheDocument();
|
|
39
|
+
});
|
|
40
|
+
test('sets feature flags in store', ()=>{
|
|
41
|
+
subject();
|
|
42
|
+
expect(mockFeatureFlagStore.setFlags).toHaveBeenCalledWith(flags);
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
//# sourceMappingURL=feature-flag-provider.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/tests/feature-flag-provider.test.tsx"],"sourcesContent":["import '@testing-library/jest-dom';\nimport { Provider } from '@servicetitan/react-ioc';\nimport { FC } from 'react';\nimport { render, screen } from '@testing-library/react';\n\nimport { FeatureFlagProvider } from '../feature-flag-provider';\nimport { FeatureFlagStore } from '../feature-flag-store';\nimport { useFlags } from 'launchdarkly-react-client-sdk';\n\njest.mock('launchdarkly-react-client-sdk', () => ({\n useFlags: jest.fn(),\n}));\n\ndescribe(`[launchdarkly-service] ${FeatureFlagProvider.name}`, () => {\n const flags = { foo: 'bar', baz: 'qux' };\n const mockFeatureFlagStore = {\n setFlags: jest.fn(),\n };\n\n const Component: FC = () => {\n return `<Component />`;\n };\n\n beforeEach(() => {\n jest.mocked(useFlags).mockImplementation(() => flags);\n });\n\n const subject = () =>\n render(\n <Provider singletons={[{ provide: FeatureFlagStore, useValue: mockFeatureFlagStore }]}>\n <FeatureFlagProvider>\n <Component />\n </FeatureFlagProvider>\n </Provider>\n );\n\n test('renders children', () => {\n subject();\n\n expect(screen.getByText(`<Component />`)).toBeInTheDocument();\n });\n\n test('sets feature flags in store', () => {\n subject();\n\n expect(mockFeatureFlagStore.setFlags).toHaveBeenCalledWith(flags);\n });\n});\n"],"names":["Provider","render","screen","FeatureFlagProvider","FeatureFlagStore","useFlags","jest","mock","fn","describe","name","flags","foo","baz","mockFeatureFlagStore","setFlags","Component","beforeEach","mocked","mockImplementation","subject","singletons","provide","useValue","test","expect","getByText","toBeInTheDocument","toHaveBeenCalledWith"],"mappings":";AAAA,OAAO,4BAA4B;AACnC,SAASA,QAAQ,QAAQ,0BAA0B;AAEnD,SAASC,MAAM,EAAEC,MAAM,QAAQ,yBAAyB;AAExD,SAASC,mBAAmB,QAAQ,2BAA2B;AAC/D,SAASC,gBAAgB,QAAQ,wBAAwB;AACzD,SAASC,QAAQ,QAAQ,gCAAgC;AAEzDC,KAAKC,IAAI,CAAC,iCAAiC,IAAO,CAAA;QAC9CF,UAAUC,KAAKE,EAAE;IACrB,CAAA;AAEAC,SAAS,CAAC,uBAAuB,EAAEN,oBAAoBO,IAAI,EAAE,EAAE;IAC3D,MAAMC,QAAQ;QAAEC,KAAK;QAAOC,KAAK;IAAM;IACvC,MAAMC,uBAAuB;QACzBC,UAAUT,KAAKE,EAAE;IACrB;IAEA,MAAMQ,YAAgB;QAClB,OAAO,CAAC,aAAa,CAAC;IAC1B;IAEAC,WAAW;QACPX,KAAKY,MAAM,CAACb,UAAUc,kBAAkB,CAAC,IAAMR;IACnD;IAEA,MAAMS,UAAU,IACZnB,qBACI,KAACD;YAASqB,YAAY;gBAAC;oBAAEC,SAASlB;oBAAkBmB,UAAUT;gBAAqB;aAAE;sBACjF,cAAA,KAACX;0BACG,cAAA,KAACa;;;IAKjBQ,KAAK,oBAAoB;QACrBJ;QAEAK,OAAOvB,OAAOwB,SAAS,CAAC,CAAC,aAAa,CAAC,GAAGC,iBAAiB;IAC/D;IAEAH,KAAK,+BAA+B;QAChCJ;QAEAK,OAAOX,qBAAqBC,QAAQ,EAAEa,oBAAoB,CAACjB;IAC/D;AACJ"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"feature-flag-store.test.d.ts","sourceRoot":"","sources":["../../src/tests/feature-flag-store.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { FeatureFlagStore } from '../feature-flag-store';
|
|
2
|
+
describe(`[launchdarkly-service] ${FeatureFlagStore.name}`, ()=>{
|
|
3
|
+
const subject = ()=>new FeatureFlagStore();
|
|
4
|
+
test('has no flags initially', ()=>{
|
|
5
|
+
expect(subject().flags).toEqual({});
|
|
6
|
+
});
|
|
7
|
+
describe('setFlags', ()=>{
|
|
8
|
+
let store;
|
|
9
|
+
let flags;
|
|
10
|
+
beforeEach(()=>{
|
|
11
|
+
store = new FeatureFlagStore();
|
|
12
|
+
flags = {
|
|
13
|
+
foo: 'bar'
|
|
14
|
+
};
|
|
15
|
+
});
|
|
16
|
+
const subject = (...args)=>store.setFlags(...args);
|
|
17
|
+
test('sets flags', ()=>{
|
|
18
|
+
subject(flags);
|
|
19
|
+
expect(store.flags).toEqual(flags);
|
|
20
|
+
});
|
|
21
|
+
test('stores copy of flags', ()=>{
|
|
22
|
+
subject(flags);
|
|
23
|
+
const originalFlags = {
|
|
24
|
+
...flags
|
|
25
|
+
};
|
|
26
|
+
flags.baz = 'qux';
|
|
27
|
+
expect(store.flags).toEqual(originalFlags);
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
//# sourceMappingURL=feature-flag-store.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/tests/feature-flag-store.test.ts"],"sourcesContent":["import { LDFlagSet } from 'launchdarkly-js-client-sdk';\nimport { FeatureFlagStore } from '../feature-flag-store';\n\ndescribe(`[launchdarkly-service] ${FeatureFlagStore.name}`, () => {\n const subject = () => new FeatureFlagStore();\n\n test('has no flags initially', () => {\n expect(subject().flags).toEqual({});\n });\n\n describe('setFlags', () => {\n let store: FeatureFlagStore;\n let flags: LDFlagSet;\n\n beforeEach(() => {\n store = new FeatureFlagStore();\n flags = { foo: 'bar' };\n });\n\n const subject = (...args: Parameters<FeatureFlagStore['setFlags']>) =>\n store.setFlags(...args);\n\n test('sets flags', () => {\n subject(flags);\n\n expect(store.flags).toEqual(flags);\n });\n\n test('stores copy of flags', () => {\n subject(flags);\n\n const originalFlags = { ...flags };\n flags.baz = 'qux';\n\n expect(store.flags).toEqual(originalFlags);\n });\n });\n});\n"],"names":["FeatureFlagStore","describe","name","subject","test","expect","flags","toEqual","store","beforeEach","foo","args","setFlags","originalFlags","baz"],"mappings":"AACA,SAASA,gBAAgB,QAAQ,wBAAwB;AAEzDC,SAAS,CAAC,uBAAuB,EAAED,iBAAiBE,IAAI,EAAE,EAAE;IACxD,MAAMC,UAAU,IAAM,IAAIH;IAE1BI,KAAK,0BAA0B;QAC3BC,OAAOF,UAAUG,KAAK,EAAEC,OAAO,CAAC,CAAC;IACrC;IAEAN,SAAS,YAAY;QACjB,IAAIO;QACJ,IAAIF;QAEJG,WAAW;YACPD,QAAQ,IAAIR;YACZM,QAAQ;gBAAEI,KAAK;YAAM;QACzB;QAEA,MAAMP,UAAU,CAAC,GAAGQ,OAChBH,MAAMI,QAAQ,IAAID;QAEtBP,KAAK,cAAc;YACfD,QAAQG;YAERD,OAAOG,MAAMF,KAAK,EAAEC,OAAO,CAACD;QAChC;QAEAF,KAAK,wBAAwB;YACzBD,QAAQG;YAER,MAAMO,gBAAgB;gBAAE,GAAGP,KAAK;YAAC;YACjCA,MAAMQ,GAAG,GAAG;YAEZT,OAAOG,MAAMF,KAAK,EAAEC,OAAO,CAACM;QAChC;IACJ;AACJ"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"get-ld-client.test.d.ts","sourceRoot":"","sources":["../../src/tests/get-ld-client.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { initialize } from 'launchdarkly-js-client-sdk';
|
|
2
|
+
import { getLDClient } from '../get-ld-client';
|
|
3
|
+
jest.mock('launchdarkly-js-client-sdk', ()=>({
|
|
4
|
+
initialize: jest.fn()
|
|
5
|
+
}));
|
|
6
|
+
describe(`[launchdarkly-service] ${getLDClient.name}`, ()=>{
|
|
7
|
+
const mockClient = Symbol('mockClient');
|
|
8
|
+
let config;
|
|
9
|
+
const subject = ()=>getLDClient(config);
|
|
10
|
+
beforeEach(()=>{
|
|
11
|
+
jest.clearAllMocks();
|
|
12
|
+
jest.mocked(initialize).mockImplementation(()=>mockClient);
|
|
13
|
+
config = {
|
|
14
|
+
clientSideID: makeId()
|
|
15
|
+
};
|
|
16
|
+
});
|
|
17
|
+
function anonymousUserContext() {
|
|
18
|
+
return {
|
|
19
|
+
kind: 'user',
|
|
20
|
+
anonymous: true
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
test('returns new LaunchDarkly client with anonymous user context', ()=>{
|
|
24
|
+
expect(subject()).toBe(mockClient);
|
|
25
|
+
expect(initialize).toHaveBeenCalledWith(config.clientSideID, anonymousUserContext(), undefined);
|
|
26
|
+
});
|
|
27
|
+
describe('when given a context', ()=>{
|
|
28
|
+
beforeEach(()=>config.context = {
|
|
29
|
+
kind: 'multi'
|
|
30
|
+
});
|
|
31
|
+
test('initializes client with context', ()=>{
|
|
32
|
+
subject();
|
|
33
|
+
expect(initialize).toHaveBeenCalledWith(config.clientSideID, config.context, undefined);
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
describe('when given options', ()=>{
|
|
37
|
+
beforeEach(()=>config.options = {
|
|
38
|
+
hash: 'foo'
|
|
39
|
+
});
|
|
40
|
+
test('initializes client with options', ()=>{
|
|
41
|
+
subject();
|
|
42
|
+
expect(initialize).toHaveBeenCalledWith(config.clientSideID, anonymousUserContext(), config.options);
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
describe('when client already exists', ()=>{
|
|
46
|
+
const existingClient = Symbol('existingClient');
|
|
47
|
+
beforeEach(()=>{
|
|
48
|
+
jest.mocked(initialize).mockImplementationOnce(()=>existingClient);
|
|
49
|
+
subject();
|
|
50
|
+
});
|
|
51
|
+
test('returns existing client', ()=>{
|
|
52
|
+
expect(subject()).toBe(existingClient);
|
|
53
|
+
expect(initialize).toHaveBeenCalledTimes(1);
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
function makeId() {
|
|
58
|
+
return Math.round(1e6 * Math.random()).toString(36);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
//# sourceMappingURL=get-ld-client.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/tests/get-ld-client.test.ts"],"sourcesContent":["import { initialize } from 'launchdarkly-js-client-sdk';\n\nimport { getLDClient } from '../get-ld-client';\n\njest.mock('launchdarkly-js-client-sdk', () => ({\n initialize: jest.fn(),\n}));\n\ndescribe(`[launchdarkly-service] ${getLDClient.name}`, () => {\n const mockClient: any = Symbol('mockClient');\n let config: Parameters<typeof getLDClient>[0];\n\n const subject = () => getLDClient(config);\n\n beforeEach(() => {\n jest.clearAllMocks();\n jest.mocked(initialize).mockImplementation(() => mockClient);\n config = { clientSideID: makeId() };\n });\n\n function anonymousUserContext() {\n return { kind: 'user', anonymous: true };\n }\n\n test('returns new LaunchDarkly client with anonymous user context', () => {\n expect(subject()).toBe(mockClient);\n expect(initialize).toHaveBeenCalledWith(\n config.clientSideID,\n anonymousUserContext(),\n undefined\n );\n });\n\n describe('when given a context', () => {\n beforeEach(() => (config.context = { kind: 'multi' }));\n\n test('initializes client with context', () => {\n subject();\n\n expect(initialize).toHaveBeenCalledWith(config.clientSideID, config.context, undefined);\n });\n });\n\n describe('when given options', () => {\n beforeEach(() => (config.options = { hash: 'foo' }));\n\n test('initializes client with options', () => {\n subject();\n\n expect(initialize).toHaveBeenCalledWith(\n config.clientSideID,\n anonymousUserContext(),\n config.options\n );\n });\n });\n\n describe('when client already exists', () => {\n const existingClient: any = Symbol('existingClient');\n\n beforeEach(() => {\n jest.mocked(initialize).mockImplementationOnce(() => existingClient);\n subject();\n });\n\n test('returns existing client', () => {\n expect(subject()).toBe(existingClient);\n expect(initialize).toHaveBeenCalledTimes(1);\n });\n });\n});\n\nfunction makeId() {\n return Math.round(1e6 * Math.random()).toString(36);\n}\n"],"names":["initialize","getLDClient","jest","mock","fn","describe","name","mockClient","Symbol","config","subject","beforeEach","clearAllMocks","mocked","mockImplementation","clientSideID","makeId","anonymousUserContext","kind","anonymous","test","expect","toBe","toHaveBeenCalledWith","undefined","context","options","hash","existingClient","mockImplementationOnce","toHaveBeenCalledTimes","Math","round","random","toString"],"mappings":"AAAA,SAASA,UAAU,QAAQ,6BAA6B;AAExD,SAASC,WAAW,QAAQ,mBAAmB;AAE/CC,KAAKC,IAAI,CAAC,8BAA8B,IAAO,CAAA;QAC3CH,YAAYE,KAAKE,EAAE;IACvB,CAAA;AAEAC,SAAS,CAAC,uBAAuB,EAAEJ,YAAYK,IAAI,EAAE,EAAE;IACnD,MAAMC,aAAkBC,OAAO;IAC/B,IAAIC;IAEJ,MAAMC,UAAU,IAAMT,YAAYQ;IAElCE,WAAW;QACPT,KAAKU,aAAa;QAClBV,KAAKW,MAAM,CAACb,YAAYc,kBAAkB,CAAC,IAAMP;QACjDE,SAAS;YAAEM,cAAcC;QAAS;IACtC;IAEA,SAASC;QACL,OAAO;YAAEC,MAAM;YAAQC,WAAW;QAAK;IAC3C;IAEAC,KAAK,+DAA+D;QAChEC,OAAOX,WAAWY,IAAI,CAACf;QACvBc,OAAOrB,YAAYuB,oBAAoB,CACnCd,OAAOM,YAAY,EACnBE,wBACAO;IAER;IAEAnB,SAAS,wBAAwB;QAC7BM,WAAW,IAAOF,OAAOgB,OAAO,GAAG;gBAAEP,MAAM;YAAQ;QAEnDE,KAAK,mCAAmC;YACpCV;YAEAW,OAAOrB,YAAYuB,oBAAoB,CAACd,OAAOM,YAAY,EAAEN,OAAOgB,OAAO,EAAED;QACjF;IACJ;IAEAnB,SAAS,sBAAsB;QAC3BM,WAAW,IAAOF,OAAOiB,OAAO,GAAG;gBAAEC,MAAM;YAAM;QAEjDP,KAAK,mCAAmC;YACpCV;YAEAW,OAAOrB,YAAYuB,oBAAoB,CACnCd,OAAOM,YAAY,EACnBE,wBACAR,OAAOiB,OAAO;QAEtB;IACJ;IAEArB,SAAS,8BAA8B;QACnC,MAAMuB,iBAAsBpB,OAAO;QAEnCG,WAAW;YACPT,KAAKW,MAAM,CAACb,YAAY6B,sBAAsB,CAAC,IAAMD;YACrDlB;QACJ;QAEAU,KAAK,2BAA2B;YAC5BC,OAAOX,WAAWY,IAAI,CAACM;YACvBP,OAAOrB,YAAY8B,qBAAqB,CAAC;QAC7C;IACJ;AACJ;AAEA,SAASd;IACL,OAAOe,KAAKC,KAAK,CAAC,MAAMD,KAAKE,MAAM,IAAIC,QAAQ,CAAC;AACpD"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ld-provider.test.d.ts","sourceRoot":"","sources":["../../src/tests/ld-provider.test.tsx"],"names":[],"mappings":"AAAA,OAAO,2BAA2B,CAAC"}
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import '@testing-library/jest-dom';
|
|
3
|
+
import { Log } from '@servicetitan/log-service';
|
|
4
|
+
import { Provider, useDependencies } from '@servicetitan/react-ioc';
|
|
5
|
+
import { render, screen, waitFor } from '@testing-library/react';
|
|
6
|
+
import { act, Fragment } from 'react';
|
|
7
|
+
import { FeatureFlagStore } from '../feature-flag-store';
|
|
8
|
+
import { getLDClient } from '../get-ld-client';
|
|
9
|
+
import { LDProvider } from '../ld-provider';
|
|
10
|
+
import { LD_SERVICE_TOKEN } from '../types';
|
|
11
|
+
function formatProps(props) {
|
|
12
|
+
return Object.entries(props).map(([key, value])=>{
|
|
13
|
+
return `${key}=${value.toString()}`;
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
function mockComponent(name) {
|
|
17
|
+
return {
|
|
18
|
+
[name]: ({ children, ...props })=>/*#__PURE__*/ _jsxs(Fragment, {
|
|
19
|
+
children: [
|
|
20
|
+
/*#__PURE__*/ _jsx("div", {
|
|
21
|
+
children: `<${name} ${formatProps(props)} />`
|
|
22
|
+
}),
|
|
23
|
+
children
|
|
24
|
+
]
|
|
25
|
+
})
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
jest.mock('launchdarkly-react-client-sdk', ()=>({
|
|
29
|
+
...mockComponent('LDProvider')
|
|
30
|
+
}));
|
|
31
|
+
jest.mock('../feature-flag-provider', ()=>({
|
|
32
|
+
...mockComponent('FeatureFlagProvider')
|
|
33
|
+
}));
|
|
34
|
+
jest.mock('../get-ld-client', ()=>({
|
|
35
|
+
getLDClient: jest.fn()
|
|
36
|
+
}));
|
|
37
|
+
describe(`[launchdarkly-service] ${LDProvider.name}`, ()=>{
|
|
38
|
+
const log = {
|
|
39
|
+
error: jest.fn()
|
|
40
|
+
};
|
|
41
|
+
const ldClient = {
|
|
42
|
+
on: jest.fn(),
|
|
43
|
+
off: jest.fn(),
|
|
44
|
+
waitUntilReady: jest.fn().mockResolvedValue(undefined),
|
|
45
|
+
waitForInitialization: jest.fn().mockResolvedValue(undefined),
|
|
46
|
+
toString: ()=>'ldClient'
|
|
47
|
+
};
|
|
48
|
+
let singletons;
|
|
49
|
+
let props;
|
|
50
|
+
let providedFeatureFlagStore;
|
|
51
|
+
let renderCount;
|
|
52
|
+
const Component = ()=>{
|
|
53
|
+
[providedFeatureFlagStore] = useDependencies(FeatureFlagStore);
|
|
54
|
+
renderCount += 1;
|
|
55
|
+
return /*#__PURE__*/ _jsx("div", {
|
|
56
|
+
children: '<Component />'
|
|
57
|
+
});
|
|
58
|
+
};
|
|
59
|
+
beforeEach(()=>{
|
|
60
|
+
jest.clearAllMocks();
|
|
61
|
+
jest.mocked(getLDClient).mockReturnValue(ldClient);
|
|
62
|
+
renderCount = 0;
|
|
63
|
+
singletons = [];
|
|
64
|
+
props = {
|
|
65
|
+
clientSideID: 'foo',
|
|
66
|
+
projectName: 'bar'
|
|
67
|
+
};
|
|
68
|
+
});
|
|
69
|
+
const subject = ()=>{
|
|
70
|
+
return render(/*#__PURE__*/ _jsx(Provider, {
|
|
71
|
+
singletons: singletons,
|
|
72
|
+
children: /*#__PURE__*/ _jsx(LDProvider, {
|
|
73
|
+
...props,
|
|
74
|
+
children: /*#__PURE__*/ _jsx(Component, {})
|
|
75
|
+
})
|
|
76
|
+
}));
|
|
77
|
+
};
|
|
78
|
+
function expectLDProvider(props) {
|
|
79
|
+
if (props) {
|
|
80
|
+
expect(screen.getByText(`<LDProvider ${formatProps(props)} />`)).toBeInTheDocument();
|
|
81
|
+
} else {
|
|
82
|
+
expect(screen.getByText(/<LDProvider .* \/>/)).toBeInTheDocument();
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
test('renders base LDProvider and FeatureFlagProvider after client is ready and initialized', async ()=>{
|
|
86
|
+
subject();
|
|
87
|
+
await waitFor(()=>expectLDProvider({
|
|
88
|
+
clientSideID: props.clientSideID,
|
|
89
|
+
ldClient
|
|
90
|
+
}));
|
|
91
|
+
expect(screen.getByText('<FeatureFlagProvider />')).toBeInTheDocument();
|
|
92
|
+
expect(screen.getByText('<Component />')).toBeInTheDocument();
|
|
93
|
+
expect(ldClient.waitForInitialization).toHaveBeenCalled();
|
|
94
|
+
});
|
|
95
|
+
test('provides FeatureFlagStore', async ()=>{
|
|
96
|
+
subject();
|
|
97
|
+
await waitFor(()=>expectLDProvider());
|
|
98
|
+
expect(providedFeatureFlagStore).toBeDefined();
|
|
99
|
+
});
|
|
100
|
+
describe('when waitForInitialization is false', ()=>{
|
|
101
|
+
let initializedCallback;
|
|
102
|
+
beforeEach(()=>{
|
|
103
|
+
var _jest_mocked;
|
|
104
|
+
props.waitForInitialization = false;
|
|
105
|
+
(_jest_mocked = jest.mocked(ldClient.on)) === null || _jest_mocked === void 0 ? void 0 : _jest_mocked.mockImplementation((event, callback)=>{
|
|
106
|
+
if (event === 'initialized') {
|
|
107
|
+
initializedCallback = callback;
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
test('does not wait for client to initialize', async ()=>{
|
|
112
|
+
subject();
|
|
113
|
+
await waitFor(()=>expectLDProvider());
|
|
114
|
+
expect(ldClient.waitForInitialization).not.toHaveBeenCalled();
|
|
115
|
+
});
|
|
116
|
+
test('re-renders when client is initialized', async ()=>{
|
|
117
|
+
subject();
|
|
118
|
+
await waitFor(()=>expectLDProvider());
|
|
119
|
+
expect(renderCount).toBe(1);
|
|
120
|
+
act(()=>initializedCallback === null || initializedCallback === void 0 ? void 0 : initializedCallback());
|
|
121
|
+
expect(renderCount).toBe(2);
|
|
122
|
+
});
|
|
123
|
+
test('deregisters listener when component is unmounted', async ()=>{
|
|
124
|
+
const { unmount } = subject();
|
|
125
|
+
await waitFor(()=>expectLDProvider());
|
|
126
|
+
unmount();
|
|
127
|
+
expect(jest.mocked(ldClient.off)).toHaveBeenCalledWith('initialized', initializedCallback);
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
describe('when initialization fails', ()=>{
|
|
131
|
+
beforeEach(()=>{
|
|
132
|
+
var _jest_mocked;
|
|
133
|
+
(_jest_mocked = jest.mocked(ldClient.waitForInitialization)) === null || _jest_mocked === void 0 ? void 0 : _jest_mocked.mockRejectedValue('Oops!');
|
|
134
|
+
});
|
|
135
|
+
test('renders base LDProvider', async ()=>{
|
|
136
|
+
subject();
|
|
137
|
+
await waitFor(()=>expectLDProvider());
|
|
138
|
+
});
|
|
139
|
+
describe('when log service is provided', ()=>{
|
|
140
|
+
beforeEach(()=>singletons.push({
|
|
141
|
+
provide: Log,
|
|
142
|
+
useValue: log
|
|
143
|
+
}));
|
|
144
|
+
test('logs error', async ()=>{
|
|
145
|
+
subject();
|
|
146
|
+
await waitFor(()=>expectLDProvider());
|
|
147
|
+
expect(log.error).toHaveBeenCalledWith({
|
|
148
|
+
category: 'LaunchDarkly',
|
|
149
|
+
code: 'InitializationFailed',
|
|
150
|
+
error: 'Oops!',
|
|
151
|
+
message: `"${props.projectName}" client failed to initialize`
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
describe('when LDService is provided', ()=>{
|
|
157
|
+
const ldServiceClient = {
|
|
158
|
+
waitUntilReady: jest.fn().mockResolvedValue(undefined),
|
|
159
|
+
waitForInitialization: jest.fn().mockResolvedValue(undefined),
|
|
160
|
+
toString: ()=>'ldServiceClient'
|
|
161
|
+
};
|
|
162
|
+
const ldService = {
|
|
163
|
+
getClient: jest.fn().mockReturnValue(ldServiceClient)
|
|
164
|
+
};
|
|
165
|
+
beforeEach(()=>{
|
|
166
|
+
singletons === null || singletons === void 0 ? void 0 : singletons.push({
|
|
167
|
+
provide: LD_SERVICE_TOKEN,
|
|
168
|
+
useValue: ldService
|
|
169
|
+
});
|
|
170
|
+
});
|
|
171
|
+
test('uses LDService client', async ()=>{
|
|
172
|
+
subject();
|
|
173
|
+
await waitFor(()=>expectLDProvider({
|
|
174
|
+
clientSideID: props.clientSideID,
|
|
175
|
+
ldClient: ldServiceClient
|
|
176
|
+
}));
|
|
177
|
+
expect(ldServiceClient.waitForInitialization).toHaveBeenCalled();
|
|
178
|
+
});
|
|
179
|
+
});
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
//# sourceMappingURL=ld-provider.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/tests/ld-provider.test.tsx"],"sourcesContent":["import '@testing-library/jest-dom';\nimport { Log } from '@servicetitan/log-service';\nimport { Provider, useDependencies } from '@servicetitan/react-ioc';\nimport { render, screen, waitFor } from '@testing-library/react';\nimport { LDClient } from 'launchdarkly-js-client-sdk';\nimport { act, FC, Fragment } from 'react';\n\nimport { FeatureFlagStore } from '../feature-flag-store';\nimport { getLDClient } from '../get-ld-client';\nimport { LDProvider } from '../ld-provider';\nimport { LD_SERVICE_TOKEN, LDService } from '../types';\n\nfunction formatProps(props: Record<string, any>) {\n return Object.entries(props).map(([key, value]) => {\n return `${key}=${value.toString()}`;\n });\n}\n\nfunction mockComponent(name: string) {\n return {\n [name]: ({ children, ...props }: any) => (\n <Fragment>\n <div>{`<${name} ${formatProps(props)} />`}</div>\n {children}\n </Fragment>\n ),\n };\n}\n\njest.mock('launchdarkly-react-client-sdk', () => ({\n ...mockComponent('LDProvider'),\n}));\njest.mock('../feature-flag-provider', () => ({\n ...mockComponent('FeatureFlagProvider'),\n}));\njest.mock('../get-ld-client', () => ({\n getLDClient: jest.fn(),\n}));\n\ndescribe(`[launchdarkly-service] ${LDProvider.name}`, () => {\n const log: Partial<Log> = {\n error: jest.fn(),\n };\n const ldClient: Partial<LDClient> & { toString: () => string } = {\n on: jest.fn(),\n off: jest.fn(),\n waitUntilReady: jest.fn().mockResolvedValue(undefined),\n waitForInitialization: jest.fn().mockResolvedValue(undefined),\n toString: () => 'ldClient',\n };\n let singletons: Required<Parameters<typeof Provider>[0]>['singletons'];\n let props: Parameters<typeof LDProvider>[0];\n let providedFeatureFlagStore: FeatureFlagStore;\n let renderCount: number;\n\n const Component: FC = () => {\n [providedFeatureFlagStore] = useDependencies(FeatureFlagStore);\n renderCount += 1;\n return <div>{'<Component />'}</div>;\n };\n\n beforeEach(() => {\n jest.clearAllMocks();\n jest.mocked(getLDClient).mockReturnValue(ldClient as LDClient);\n renderCount = 0;\n singletons = [];\n props = { clientSideID: 'foo', projectName: 'bar' };\n });\n\n const subject = () => {\n return render(\n <Provider singletons={singletons}>\n <LDProvider {...props}>\n <Component />\n </LDProvider>\n </Provider>\n );\n };\n\n function expectLDProvider(props?: Record<string, any>) {\n if (props) {\n expect(screen.getByText(`<LDProvider ${formatProps(props)} />`)).toBeInTheDocument();\n } else {\n expect(screen.getByText(/<LDProvider .* \\/>/)).toBeInTheDocument();\n }\n }\n\n test('renders base LDProvider and FeatureFlagProvider after client is ready and initialized', async () => {\n subject();\n\n await waitFor(() => expectLDProvider({ clientSideID: props.clientSideID, ldClient }));\n\n expect(screen.getByText('<FeatureFlagProvider />')).toBeInTheDocument();\n expect(screen.getByText('<Component />')).toBeInTheDocument();\n expect(ldClient.waitForInitialization).toHaveBeenCalled();\n });\n\n test('provides FeatureFlagStore', async () => {\n subject();\n\n await waitFor(() => expectLDProvider());\n\n expect(providedFeatureFlagStore).toBeDefined();\n });\n\n describe('when waitForInitialization is false', () => {\n let initializedCallback: Function;\n\n beforeEach(() => {\n props.waitForInitialization = false;\n jest.mocked(ldClient.on)?.mockImplementation((event, callback) => {\n if (event === 'initialized') {\n initializedCallback = callback;\n }\n });\n });\n\n test('does not wait for client to initialize', async () => {\n subject();\n\n await waitFor(() => expectLDProvider());\n\n expect(ldClient.waitForInitialization).not.toHaveBeenCalled();\n });\n\n test('re-renders when client is initialized', async () => {\n subject();\n\n await waitFor(() => expectLDProvider());\n expect(renderCount).toBe(1);\n\n act(() => initializedCallback?.());\n expect(renderCount).toBe(2);\n });\n\n test('deregisters listener when component is unmounted', async () => {\n const { unmount } = subject();\n\n await waitFor(() => expectLDProvider());\n\n unmount();\n\n expect(jest.mocked(ldClient.off)).toHaveBeenCalledWith(\n 'initialized',\n initializedCallback\n );\n });\n });\n\n describe('when initialization fails', () => {\n beforeEach(() => {\n jest.mocked(ldClient.waitForInitialization)?.mockRejectedValue('Oops!');\n });\n\n test('renders base LDProvider', async () => {\n subject();\n\n await waitFor(() => expectLDProvider());\n });\n\n describe('when log service is provided', () => {\n beforeEach(() => singletons.push({ provide: Log, useValue: log }));\n\n test('logs error', async () => {\n subject();\n\n await waitFor(() => expectLDProvider());\n\n expect(log.error).toHaveBeenCalledWith({\n category: 'LaunchDarkly',\n code: 'InitializationFailed',\n error: 'Oops!',\n message: `\"${props.projectName}\" client failed to initialize`,\n });\n });\n });\n });\n\n describe('when LDService is provided', () => {\n const ldServiceClient: Partial<LDClient> & { toString: () => string } = {\n waitUntilReady: jest.fn().mockResolvedValue(undefined),\n waitForInitialization: jest.fn().mockResolvedValue(undefined),\n toString: () => 'ldServiceClient',\n };\n const ldService: LDService = {\n getClient: jest.fn().mockReturnValue(ldServiceClient),\n };\n\n beforeEach(() => {\n singletons?.push({ provide: LD_SERVICE_TOKEN, useValue: ldService });\n });\n\n test('uses LDService client', async () => {\n subject();\n\n await waitFor(() =>\n expectLDProvider({ clientSideID: props.clientSideID, ldClient: ldServiceClient })\n );\n\n expect(ldServiceClient.waitForInitialization).toHaveBeenCalled();\n });\n });\n});\n"],"names":["Log","Provider","useDependencies","render","screen","waitFor","act","Fragment","FeatureFlagStore","getLDClient","LDProvider","LD_SERVICE_TOKEN","formatProps","props","Object","entries","map","key","value","toString","mockComponent","name","children","div","jest","mock","fn","describe","log","error","ldClient","on","off","waitUntilReady","mockResolvedValue","undefined","waitForInitialization","singletons","providedFeatureFlagStore","renderCount","Component","beforeEach","clearAllMocks","mocked","mockReturnValue","clientSideID","projectName","subject","expectLDProvider","expect","getByText","toBeInTheDocument","test","toHaveBeenCalled","toBeDefined","initializedCallback","mockImplementation","event","callback","not","toBe","unmount","toHaveBeenCalledWith","mockRejectedValue","push","provide","useValue","category","code","message","ldServiceClient","ldService","getClient"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ld-service-provider.test.d.ts","sourceRoot":"","sources":["../../src/tests/ld-service-provider.test.tsx"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Provider, useDependencies } from '@servicetitan/react-ioc';
|
|
3
|
+
import { render } from '@testing-library/react';
|
|
4
|
+
import { getLDClient } from '../get-ld-client';
|
|
5
|
+
import { LDServiceProvider } from '../ld-service-provider';
|
|
6
|
+
import { LD_SERVICE_TOKEN } from '../types';
|
|
7
|
+
describe(`[launchdarkly-service] ${LDServiceProvider.name}`, ()=>{
|
|
8
|
+
let providedLDService;
|
|
9
|
+
let singletons;
|
|
10
|
+
const Component = ()=>{
|
|
11
|
+
[providedLDService] = useDependencies(LD_SERVICE_TOKEN);
|
|
12
|
+
return null;
|
|
13
|
+
};
|
|
14
|
+
beforeEach(()=>singletons = []);
|
|
15
|
+
const subject = ()=>{
|
|
16
|
+
return render(/*#__PURE__*/ _jsx(Provider, {
|
|
17
|
+
singletons: singletons,
|
|
18
|
+
children: /*#__PURE__*/ _jsx(LDServiceProvider, {
|
|
19
|
+
children: /*#__PURE__*/ _jsx(Component, {})
|
|
20
|
+
})
|
|
21
|
+
}));
|
|
22
|
+
};
|
|
23
|
+
test('provides LDService instance', ()=>{
|
|
24
|
+
subject();
|
|
25
|
+
expect(providedLDService).toEqual({
|
|
26
|
+
getClient: getLDClient
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
describe('when LDService is already provided', ()=>{
|
|
30
|
+
const ldService = Symbol('ldService');
|
|
31
|
+
beforeEach(()=>singletons === null || singletons === void 0 ? void 0 : singletons.push({
|
|
32
|
+
provide: LD_SERVICE_TOKEN,
|
|
33
|
+
useValue: ldService
|
|
34
|
+
}));
|
|
35
|
+
test('provides existing LDService', ()=>{
|
|
36
|
+
subject();
|
|
37
|
+
expect(providedLDService).toBe(ldService);
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
//# sourceMappingURL=ld-service-provider.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/tests/ld-service-provider.test.tsx"],"sourcesContent":["import { Provider, useDependencies } from '@servicetitan/react-ioc';\nimport { render } from '@testing-library/react';\nimport { FC } from 'react';\n\nimport { getLDClient } from '../get-ld-client';\nimport { LDServiceProvider } from '../ld-service-provider';\nimport { LD_SERVICE_TOKEN, LDService } from '../types';\n\ndescribe(`[launchdarkly-service] ${LDServiceProvider.name}`, () => {\n let providedLDService: LDService;\n let singletons: Parameters<typeof Provider>[0]['singletons'];\n\n const Component: FC = () => {\n [providedLDService] = useDependencies(LD_SERVICE_TOKEN);\n return null;\n };\n\n beforeEach(() => (singletons = []));\n\n const subject = () => {\n return render(\n <Provider singletons={singletons}>\n <LDServiceProvider>\n <Component />\n </LDServiceProvider>\n </Provider>\n );\n };\n\n test('provides LDService instance', () => {\n subject();\n\n expect(providedLDService).toEqual({\n getClient: getLDClient,\n });\n });\n\n describe('when LDService is already provided', () => {\n const ldService = Symbol('ldService');\n\n beforeEach(() => singletons?.push({ provide: LD_SERVICE_TOKEN, useValue: ldService }));\n\n test('provides existing LDService', () => {\n subject();\n\n expect(providedLDService).toBe(ldService);\n });\n });\n});\n"],"names":["Provider","useDependencies","render","getLDClient","LDServiceProvider","LD_SERVICE_TOKEN","describe","name","providedLDService","singletons","Component","beforeEach","subject","test","expect","toEqual","getClient","ldService","Symbol","push","provide","useValue","toBe"],"mappings":";AAAA,SAASA,QAAQ,EAAEC,eAAe,QAAQ,0BAA0B;AACpE,SAASC,MAAM,QAAQ,yBAAyB;AAGhD,SAASC,WAAW,QAAQ,mBAAmB;AAC/C,SAASC,iBAAiB,QAAQ,yBAAyB;AAC3D,SAASC,gBAAgB,QAAmB,WAAW;AAEvDC,SAAS,CAAC,uBAAuB,EAAEF,kBAAkBG,IAAI,EAAE,EAAE;IACzD,IAAIC;IACJ,IAAIC;IAEJ,MAAMC,YAAgB;QAClB,CAACF,kBAAkB,GAAGP,gBAAgBI;QACtC,OAAO;IACX;IAEAM,WAAW,IAAOF,aAAa,EAAE;IAEjC,MAAMG,UAAU;QACZ,OAAOV,qBACH,KAACF;YAASS,YAAYA;sBAClB,cAAA,KAACL;0BACG,cAAA,KAACM;;;IAIjB;IAEAG,KAAK,+BAA+B;QAChCD;QAEAE,OAAON,mBAAmBO,OAAO,CAAC;YAC9BC,WAAWb;QACf;IACJ;IAEAG,SAAS,sCAAsC;QAC3C,MAAMW,YAAYC,OAAO;QAEzBP,WAAW,IAAMF,uBAAAA,iCAAAA,WAAYU,IAAI,CAAC;gBAAEC,SAASf;gBAAkBgB,UAAUJ;YAAU;QAEnFJ,KAAK,+BAA+B;YAChCD;YAEAE,OAAON,mBAAmBc,IAAI,CAACL;QACnC;IACJ;AACJ"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"with-feature-flag.test.d.ts","sourceRoot":"","sources":["../../src/tests/with-feature-flag.test.tsx"],"names":[],"mappings":"AAAA,OAAO,2BAA2B,CAAC"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import '@testing-library/jest-dom';
|
|
3
|
+
import { render, screen } from '@testing-library/react';
|
|
4
|
+
import { useFlags } from 'launchdarkly-react-client-sdk';
|
|
5
|
+
import { WithFeatureFlag } from '../with-feature-flag';
|
|
6
|
+
jest.mock('launchdarkly-react-client-sdk', ()=>({
|
|
7
|
+
useFlags: jest.fn()
|
|
8
|
+
}));
|
|
9
|
+
describe(`[launchdarkly-service] ${WithFeatureFlag.name}`, ()=>{
|
|
10
|
+
const flagKey = 'foo';
|
|
11
|
+
const childContent = 'Flag is ON';
|
|
12
|
+
let props;
|
|
13
|
+
let flagValue;
|
|
14
|
+
beforeEach(()=>{
|
|
15
|
+
props = {
|
|
16
|
+
flagKey,
|
|
17
|
+
children: childContent
|
|
18
|
+
};
|
|
19
|
+
jest.mocked(useFlags).mockImplementation(()=>({
|
|
20
|
+
[flagKey]: flagValue
|
|
21
|
+
}));
|
|
22
|
+
});
|
|
23
|
+
const subject = ()=>render(/*#__PURE__*/ _jsx(WithFeatureFlag, {
|
|
24
|
+
...props
|
|
25
|
+
}));
|
|
26
|
+
function itRendersNothing() {
|
|
27
|
+
test('renders nothing', ()=>{
|
|
28
|
+
subject();
|
|
29
|
+
expect(screen.queryByText(childContent)).not.toBeInTheDocument();
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
function itRendersChildren() {
|
|
33
|
+
test('renders children', ()=>{
|
|
34
|
+
subject();
|
|
35
|
+
expect(screen.getByText(childContent)).toBeInTheDocument();
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
itRendersNothing();
|
|
39
|
+
describe('when flag is turned on', ()=>{
|
|
40
|
+
beforeEach(()=>flagValue = true);
|
|
41
|
+
itRendersChildren();
|
|
42
|
+
describe('with on prop', ()=>{
|
|
43
|
+
const onContent = 'Flag is ENABLED';
|
|
44
|
+
beforeEach(()=>props.on = ()=>onContent);
|
|
45
|
+
test('renders on content', ()=>{
|
|
46
|
+
subject();
|
|
47
|
+
expect(screen.getByText(onContent)).toBeInTheDocument();
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
describe('when flag is turned off', ()=>{
|
|
52
|
+
beforeEach(()=>flagValue = false);
|
|
53
|
+
itRendersNothing();
|
|
54
|
+
describe('with off prop', ()=>{
|
|
55
|
+
const offContent = 'flag is OFF';
|
|
56
|
+
beforeEach(()=>props.off = ()=>offContent);
|
|
57
|
+
test('renders off content', ()=>{
|
|
58
|
+
subject();
|
|
59
|
+
expect(screen.getByText(offContent)).toBeInTheDocument();
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
describe('with flag value', ()=>{
|
|
64
|
+
beforeEach(()=>props.value = 'bar');
|
|
65
|
+
itRendersNothing();
|
|
66
|
+
describe('when flag matches value', ()=>{
|
|
67
|
+
beforeEach(()=>flagValue = props.value);
|
|
68
|
+
itRendersChildren();
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
//# sourceMappingURL=with-feature-flag.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/tests/with-feature-flag.test.tsx"],"sourcesContent":["import '@testing-library/jest-dom';\nimport { render, screen } from '@testing-library/react';\nimport { LDFlagValue, useFlags } from 'launchdarkly-react-client-sdk';\n\nimport { WithFeatureFlag } from '../with-feature-flag';\n\njest.mock('launchdarkly-react-client-sdk', () => ({\n useFlags: jest.fn(),\n}));\n\ndescribe(`[launchdarkly-service] ${WithFeatureFlag.name}`, () => {\n const flagKey = 'foo';\n const childContent = 'Flag is ON';\n let props: Parameters<typeof WithFeatureFlag>[0];\n let flagValue: LDFlagValue;\n\n beforeEach(() => {\n props = { flagKey, children: childContent };\n jest.mocked(useFlags).mockImplementation(() => ({ [flagKey]: flagValue }));\n });\n\n const subject = () => render(<WithFeatureFlag {...props} />);\n\n function itRendersNothing() {\n test('renders nothing', () => {\n subject();\n\n expect(screen.queryByText(childContent)).not.toBeInTheDocument();\n });\n }\n\n function itRendersChildren() {\n test('renders children', () => {\n subject();\n\n expect(screen.getByText(childContent)).toBeInTheDocument();\n });\n }\n\n itRendersNothing();\n\n describe('when flag is turned on', () => {\n beforeEach(() => (flagValue = true));\n\n itRendersChildren();\n\n describe('with on prop', () => {\n const onContent = 'Flag is ENABLED';\n\n beforeEach(() => (props.on = () => onContent));\n\n test('renders on content', () => {\n subject();\n\n expect(screen.getByText(onContent)).toBeInTheDocument();\n });\n });\n });\n\n describe('when flag is turned off', () => {\n beforeEach(() => (flagValue = false));\n\n itRendersNothing();\n\n describe('with off prop', () => {\n const offContent = 'flag is OFF';\n\n beforeEach(() => (props.off = () => offContent));\n\n test('renders off content', () => {\n subject();\n\n expect(screen.getByText(offContent)).toBeInTheDocument();\n });\n });\n });\n\n describe('with flag value', () => {\n beforeEach(() => (props.value = 'bar'));\n\n itRendersNothing();\n\n describe('when flag matches value', () => {\n beforeEach(() => (flagValue = props.value));\n\n itRendersChildren();\n });\n });\n});\n"],"names":["render","screen","useFlags","WithFeatureFlag","jest","mock","fn","describe","name","flagKey","childContent","props","flagValue","beforeEach","children","mocked","mockImplementation","subject","itRendersNothing","test","expect","queryByText","not","toBeInTheDocument","itRendersChildren","getByText","onContent","on","offContent","off","value"],"mappings":";AAAA,OAAO,4BAA4B;AACnC,SAASA,MAAM,EAAEC,MAAM,QAAQ,yBAAyB;AACxD,SAAsBC,QAAQ,QAAQ,gCAAgC;AAEtE,SAASC,eAAe,QAAQ,uBAAuB;AAEvDC,KAAKC,IAAI,CAAC,iCAAiC,IAAO,CAAA;QAC9CH,UAAUE,KAAKE,EAAE;IACrB,CAAA;AAEAC,SAAS,CAAC,uBAAuB,EAAEJ,gBAAgBK,IAAI,EAAE,EAAE;IACvD,MAAMC,UAAU;IAChB,MAAMC,eAAe;IACrB,IAAIC;IACJ,IAAIC;IAEJC,WAAW;QACPF,QAAQ;YAAEF;YAASK,UAAUJ;QAAa;QAC1CN,KAAKW,MAAM,CAACb,UAAUc,kBAAkB,CAAC,IAAO,CAAA;gBAAE,CAACP,QAAQ,EAAEG;YAAU,CAAA;IAC3E;IAEA,MAAMK,UAAU,IAAMjB,qBAAO,KAACG;YAAiB,GAAGQ,KAAK;;IAEvD,SAASO;QACLC,KAAK,mBAAmB;YACpBF;YAEAG,OAAOnB,OAAOoB,WAAW,CAACX,eAAeY,GAAG,CAACC,iBAAiB;QAClE;IACJ;IAEA,SAASC;QACLL,KAAK,oBAAoB;YACrBF;YAEAG,OAAOnB,OAAOwB,SAAS,CAACf,eAAea,iBAAiB;QAC5D;IACJ;IAEAL;IAEAX,SAAS,0BAA0B;QAC/BM,WAAW,IAAOD,YAAY;QAE9BY;QAEAjB,SAAS,gBAAgB;YACrB,MAAMmB,YAAY;YAElBb,WAAW,IAAOF,MAAMgB,EAAE,GAAG,IAAMD;YAEnCP,KAAK,sBAAsB;gBACvBF;gBAEAG,OAAOnB,OAAOwB,SAAS,CAACC,YAAYH,iBAAiB;YACzD;QACJ;IACJ;IAEAhB,SAAS,2BAA2B;QAChCM,WAAW,IAAOD,YAAY;QAE9BM;QAEAX,SAAS,iBAAiB;YACtB,MAAMqB,aAAa;YAEnBf,WAAW,IAAOF,MAAMkB,GAAG,GAAG,IAAMD;YAEpCT,KAAK,uBAAuB;gBACxBF;gBAEAG,OAAOnB,OAAOwB,SAAS,CAACG,aAAaL,iBAAiB;YAC1D;QACJ;IACJ;IAEAhB,SAAS,mBAAmB;QACxBM,WAAW,IAAOF,MAAMmB,KAAK,GAAG;QAEhCZ;QAEAX,SAAS,2BAA2B;YAChCM,WAAW,IAAOD,YAAYD,MAAMmB,KAAK;YAEzCN;QACJ;IACJ;AACJ"}
|
package/dist/types.js
CHANGED
package/dist/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"
|
|
1
|
+
{"version":3,"sources":["../src/types.ts"],"sourcesContent":["import { symbolToken } from '@servicetitan/react-ioc';\nimport { type LDContext, type LDClient, type LDOptions } from 'launchdarkly-js-client-sdk';\n\nexport const LD_SERVICE_TOKEN = symbolToken<LDService>('LD_SERVICE_TOKEN');\n\nexport interface ClientConfig {\n clientSideID: string;\n context?: LDContext;\n options?: LDOptions;\n}\n\nexport interface LDService {\n getClient: (config: ClientConfig) => LDClient;\n}\n\nexport type FlagKey<S, K extends keyof S> = K & {\n typeRef?: S[K];\n};\n"],"names":["symbolToken","LD_SERVICE_TOKEN"],"mappings":"AAAA,SAASA,WAAW,QAAQ,0BAA0B;AAGtD,OAAO,MAAMC,mBAAmBD,YAAuB,oBAAoB"}
|
|
@@ -5,8 +5,9 @@ export function WithFeatureFlag(props) {
|
|
|
5
5
|
const flagValue = useFlags()[flagKey];
|
|
6
6
|
const isEnabled = value !== undefined ? flagValue === value : !!flagValue;
|
|
7
7
|
if (isEnabled) {
|
|
8
|
-
return props.on ? _jsx(props.on, {}) : props.children;
|
|
8
|
+
return props.on ? /*#__PURE__*/ _jsx(props.on, {}) : props.children;
|
|
9
9
|
}
|
|
10
|
-
return props.off ? _jsx(props.off, {}) : null;
|
|
10
|
+
return props.off ? /*#__PURE__*/ _jsx(props.off, {}) : null;
|
|
11
11
|
}
|
|
12
|
+
|
|
12
13
|
//# sourceMappingURL=with-feature-flag.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"
|
|
1
|
+
{"version":3,"sources":["../src/with-feature-flag.tsx"],"sourcesContent":["import { LDFlagSet } from 'launchdarkly-js-client-sdk';\nimport { useFlags } from 'launchdarkly-react-client-sdk';\nimport { ComponentType, ReactNode } from 'react';\nimport { FlagKey } from './types';\n\ninterface BaseProps<S extends LDFlagSet, T extends keyof S> {\n flagKey: FlagKey<S, T>;\n off?: ComponentType;\n value?: S[T];\n}\n\nexport type WithFeatureFlagProps<S extends LDFlagSet, T extends keyof S = keyof S> =\n | (BaseProps<S, T> & { children?: ReactNode; on?: never })\n | (BaseProps<S, T> & { on?: ComponentType; children?: never });\n\nexport function WithFeatureFlag<S extends LDFlagSet, T extends keyof S = keyof S>(\n props: WithFeatureFlagProps<S, T>\n): ReactNode {\n const { flagKey, value } = props;\n const flagValue = useFlags<S>()[flagKey];\n\n const isEnabled = value !== undefined ? flagValue === value : !!flagValue;\n if (isEnabled) {\n return props.on ? <props.on /> : props.children;\n }\n\n return props.off ? <props.off /> : null;\n}\n"],"names":["useFlags","WithFeatureFlag","props","flagKey","value","flagValue","isEnabled","undefined","on","children","off"],"mappings":";AACA,SAASA,QAAQ,QAAQ,gCAAgC;AAczD,OAAO,SAASC,gBACZC,KAAiC;IAEjC,MAAM,EAAEC,OAAO,EAAEC,KAAK,EAAE,GAAGF;IAC3B,MAAMG,YAAYL,UAAa,CAACG,QAAQ;IAExC,MAAMG,YAAYF,UAAUG,YAAYF,cAAcD,QAAQ,CAAC,CAACC;IAChE,IAAIC,WAAW;QACX,OAAOJ,MAAMM,EAAE,iBAAG,KAACN,MAAMM,EAAE,QAAMN,MAAMO,QAAQ;IACnD;IAEA,OAAOP,MAAMQ,GAAG,iBAAG,KAACR,MAAMQ,GAAG,QAAM;AACvC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@servicetitan/launchdarkly-service",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "31.1.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"homepage": "https://docs.st.dev/docs/frontend/launchdarkly-service",
|
|
6
6
|
"repository": {
|
|
@@ -16,8 +16,8 @@
|
|
|
16
16
|
"src"
|
|
17
17
|
],
|
|
18
18
|
"devDependencies": {
|
|
19
|
-
"@servicetitan/log-service": "
|
|
20
|
-
"@servicetitan/react-ioc": "
|
|
19
|
+
"@servicetitan/log-service": "31.1.0",
|
|
20
|
+
"@servicetitan/react-ioc": "31.1.0",
|
|
21
21
|
"@testing-library/dom": "^10.4.0",
|
|
22
22
|
"@testing-library/jest-dom": "^6.6.3",
|
|
23
23
|
"@testing-library/react": "^16.3.0",
|
|
@@ -27,8 +27,8 @@
|
|
|
27
27
|
"react": "~18.3.1"
|
|
28
28
|
},
|
|
29
29
|
"peerDependencies": {
|
|
30
|
-
"@servicetitan/log-service": "
|
|
31
|
-
"@servicetitan/react-ioc": "
|
|
30
|
+
"@servicetitan/log-service": "31.1.0",
|
|
31
|
+
"@servicetitan/react-ioc": "31.1.0",
|
|
32
32
|
"launchdarkly-js-client-sdk": "3.5.0",
|
|
33
33
|
"launchdarkly-react-client-sdk": "3.6.1",
|
|
34
34
|
"mobx": ">= 6.13.7",
|
|
@@ -40,5 +40,5 @@
|
|
|
40
40
|
"cli": {
|
|
41
41
|
"webpack": false
|
|
42
42
|
},
|
|
43
|
-
"gitHead": "
|
|
43
|
+
"gitHead": "d01cfe32c50461245ce52c911710e8eb879290e9"
|
|
44
44
|
}
|
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
import { useDependencies } from '@servicetitan/react-ioc';
|
|
2
2
|
import { useFlags } from 'launchdarkly-react-client-sdk';
|
|
3
|
-
import { PropsWithChildren, useEffect } from 'react';
|
|
3
|
+
import { PropsWithChildren, useEffect, useState } from 'react';
|
|
4
4
|
|
|
5
5
|
import { FeatureFlagStore } from './feature-flag-store';
|
|
6
6
|
|
|
7
7
|
export function FeatureFlagProvider({ children }: PropsWithChildren) {
|
|
8
|
+
const [initialized, setInitialized] = useState(false);
|
|
8
9
|
const [{ setFlags }] = useDependencies(FeatureFlagStore);
|
|
9
10
|
const flags = useFlags();
|
|
10
11
|
|
|
11
12
|
useEffect(() => {
|
|
12
13
|
setFlags(flags);
|
|
14
|
+
setInitialized(true);
|
|
13
15
|
}, [flags, setFlags]);
|
|
14
16
|
|
|
15
|
-
return children;
|
|
17
|
+
return initialized ? children : null;
|
|
16
18
|
}
|