Skip to content

Commit e37ab02

Browse files
Allow for Stubing LaunchDarkly in cypress environment (#48)
1 parent 1bff297 commit e37ab02

File tree

5 files changed

+112
-53
lines changed

5 files changed

+112
-53
lines changed

README.md

+15-15
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,10 @@ Vue.use(VueLd, {
3636

3737
#### Additional Plugin Options
3838

39-
| key | description | default | type |
40-
| :-------------------- | ---------------------------------------------------------------------------------- | ------- | --------- |
41-
| `readyBeforeIdentify` | If set to false, the `$ld.ready` will only be true after identify has been called. | `true` | `Boolean` |
39+
| key | description | default | type |
40+
| :-------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | ----------- | ------------------ |
41+
| `readyBeforeIdentify` | If set to false, the `$ld.ready` will only be true after identify has been called. | `true` | `Boolean` |
42+
| `flagsStub` | If provided, the ldClient will not be initialized and `$ld.flags` will set to the provided stub; this can be helpful in e2e tests. | `undefined` | `Object` / `Proxy` |
4243

4344
### Template
4445

@@ -95,12 +96,11 @@ export default {
9596

9697
#### Arguments
9798

98-
| key | description | type |
99-
| :------------- | ---------------------------------------------------------------------------------------------- | ---------------------------------- |
100-
| `requiredFlag` | If the given feature flag is false, the user will be redirected to the given route. | `string` |
101-
| `to` | The path which vue router will push. Functions passed are expected to resolve to a valid path. | `string`, `object`, or `function` |
102-
| `invertFlag` | If set to true the the inverse of the requiredFlag's value will be used. | `boolean` |
103-
99+
| key | description | type |
100+
| :------------- | ---------------------------------------------------------------------------------------------- | --------------------------------- |
101+
| `requiredFlag` | If the given feature flag is false, the user will be redirected to the given route. | `string` |
102+
| `to` | The path which vue router will push. Functions passed are expected to resolve to a valid path. | `string`, `object`, or `function` |
103+
| `invertFlag` | If set to true the the inverse of the requiredFlag's value will be used. | `boolean` |
104104

105105
### LDRouteGuard Component
106106

@@ -123,12 +123,12 @@ const route = {
123123

124124
#### Props
125125

126-
| key | description | type |
127-
| :------------- | ---------------------------------------------------------------------------------------------- | ---------------------------------- |
128-
| `component` | The component to be rendered given the required feature flag is true. | `vue component` |
129-
| `requiredFlag` | If the given feature flag is false, the user will be redirected to the given route. | `string` |
130-
| `to` | The path which vue router will push. Functions passed are expected to resolve to a valid path. | `string`, `object`, or `function` |
131-
| `invertFlag` | If set to true the the inverse of the requiredFlag's value will be used. | `boolean` |
126+
| key | description | type |
127+
| :------------- | ---------------------------------------------------------------------------------------------- | --------------------------------- |
128+
| `component` | The component to be rendered given the required feature flag is true. | `vue component` |
129+
| `requiredFlag` | If the given feature flag is false, the user will be redirected to the given route. | `string` |
130+
| `to` | The path which vue router will push. Functions passed are expected to resolve to a valid path. | `string`, `object`, or `function` |
131+
| `invertFlag` | If set to true the the inverse of the requiredFlag's value will be used. | `boolean` |
132132

133133
## Development
134134

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "vue-ld",
3-
"version": "0.1.8",
3+
"version": "0.1.9",
44
"description": "A Vue.js wrapper for the LaunchDarkly SDK for Browser JavaScript",
55
"main": "dist/index.cjs.js",
66
"module": "dist/index.es.js",

src/index.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
export { default as VueLd } from './plugin';
1+
export { default as VueLd, initialize } from './plugin';
22
export { default as ldRedirectMixin } from './mixins/ldRedirect';
33
export { default as LDRouteGuard } from './components/LDRouteGuard.vue';

src/plugin.js

+62-36
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,72 @@
11
import * as LDClient from 'launchdarkly-js-client-sdk';
22
import { formatFlags } from './utils';
33

4-
export default {
5-
install(Vue, options) {
6-
const { clientSideId, user, options: ldOptions, readyBeforeIdentify = true } = options;
4+
export const initialize = ({ clientSideId, user, ldOptions, readyBeforeIdentify }) => {
5+
const ldClient = LDClient.initialize(clientSideId, user, ldOptions);
6+
const $ld = {
7+
ldClient,
8+
identify({ newUser, hash, callback }, vueLdCallback) {
9+
return new Promise((r) => {
10+
this.ready = false;
11+
ldClient.identify(newUser, hash, callback).then(() => {
12+
this.ready = true;
13+
if (vueLdCallback) {
14+
const boundVueLdCallback = vueLdCallback.bind($ld);
15+
boundVueLdCallback();
16+
}
17+
r();
18+
});
19+
});
20+
},
21+
flags: {},
22+
ready: false,
23+
};
724

8-
const ldClient = LDClient.initialize(clientSideId, user, ldOptions);
25+
ldClient.on('ready', () => {
26+
$ld.flags = formatFlags(ldClient.allFlags());
27+
$ld.ready = readyBeforeIdentify;
28+
});
929

10-
const $ld = Vue.observable({
11-
ldClient,
12-
identify({ newUser, hash, callback }, vueLdCallback) {
13-
return new Promise((r) => {
14-
this.ready = false;
15-
ldClient.identify(newUser, hash, callback).then(() => {
16-
this.ready = true;
17-
if (vueLdCallback) {
18-
const boundVueLdCallback = vueLdCallback.bind($ld);
19-
boundVueLdCallback();
20-
}
21-
r();
22-
});
23-
});
24-
},
25-
flags: {},
26-
ready: false,
27-
});
30+
ldClient.on('change', (changes) => {
31+
const flattenedFlags = Object.fromEntries(
32+
Object.keys(changes).map((key) => [key, changes[key].current])
33+
);
34+
$ld.flags = {
35+
...$ld.flags,
36+
...formatFlags(flattenedFlags),
37+
};
38+
});
39+
return $ld;
40+
};
41+
42+
const stub = ({ flagsStub, readyBeforeIdentify }) => {
43+
return {
44+
identify() {
45+
this.ready = true;
46+
},
47+
flags: flagsStub,
48+
ready: readyBeforeIdentify,
49+
};
50+
};
51+
52+
export default {
53+
async install(Vue, options) {
54+
const {
55+
clientSideId,
56+
user,
57+
options: ldOptions,
58+
flagsStub,
59+
readyBeforeIdentify = true,
60+
} = options;
2861

29-
ldClient.on('ready', () => {
30-
$ld.flags = formatFlags(ldClient.allFlags());
31-
$ld.ready = readyBeforeIdentify;
32-
});
62+
let $ld;
3363

34-
ldClient.on('change', (changes) => {
35-
const flattenedFlags = Object.fromEntries(
36-
Object.keys(changes).map((key) => [key, changes[key].current])
37-
);
38-
$ld.flags = {
39-
...$ld.flags,
40-
...formatFlags(flattenedFlags),
41-
};
42-
});
64+
if (flagsStub) {
65+
$ld = stub({ flagsStub, readyBeforeIdentify });
66+
} else {
67+
$ld = initialize({ clientSideId, user, ldOptions, readyBeforeIdentify });
68+
}
4369
// eslint-disable-next-line no-param-reassign
44-
Vue.prototype.$ld = $ld;
70+
Vue.prototype.$ld = Vue.observable($ld);
4571
},
4672
};

tests/unit/vue-ld.spec.js

+33
Original file line numberDiff line numberDiff line change
@@ -95,4 +95,37 @@ describe('VueLd Plugin', () => {
9595
expect(vueLdCallback).toBeCalled();
9696
expect(vueLdCallback.mock.instances[0]).toBe(wrapper.vm.$ld);
9797
});
98+
99+
it('stubs flags when passed the option', async () => {
100+
localVue.use(VueLd, {
101+
...vueLdOptions,
102+
/*
103+
Using a proxy like this will allow you to return true for everything
104+
not explicitly on the base object or set later.
105+
*/
106+
flagsStub: new Proxy(
107+
{
108+
never: false,
109+
},
110+
{
111+
get(obj, prop) {
112+
const value = obj[prop];
113+
return value === undefined ? true : value;
114+
},
115+
}
116+
),
117+
});
118+
wrapper = mount(Component, {
119+
localVue,
120+
});
121+
122+
expect(wrapper.vm.$ld.flags.never).toBe(false);
123+
expect(wrapper.vm.$ld.flags.anythingElse).toBe(true);
124+
125+
wrapper.vm.$ld.flags.neverLater = false;
126+
expect(wrapper.vm.$ld.flags.neverLater).toBe(false);
127+
128+
delete wrapper.vm.$ld.flags.neverLater;
129+
expect(wrapper.vm.$ld.flags.anythingElse).toBe(true);
130+
});
98131
});

0 commit comments

Comments
 (0)