Skip to content

Commit

Permalink
Allow for Stubing LaunchDarkly in cypress environment (#48)
Browse files Browse the repository at this point in the history
  • Loading branch information
benjamin-t-frost authored May 11, 2021
1 parent 1bff297 commit e37ab02
Show file tree
Hide file tree
Showing 5 changed files with 112 additions and 53 deletions.
30 changes: 15 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,10 @@ Vue.use(VueLd, {

#### Additional Plugin Options

| key | description | default | type |
| :-------------------- | ---------------------------------------------------------------------------------- | ------- | --------- |
| `readyBeforeIdentify` | If set to false, the `$ld.ready` will only be true after identify has been called. | `true` | `Boolean` |
| key | description | default | type |
| :-------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | ----------- | ------------------ |
| `readyBeforeIdentify` | If set to false, the `$ld.ready` will only be true after identify has been called. | `true` | `Boolean` |
| `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` |

### Template

Expand Down Expand Up @@ -95,12 +96,11 @@ export default {

#### Arguments

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

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

### LDRouteGuard Component

Expand All @@ -123,12 +123,12 @@ const route = {

#### Props

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

## Development

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "vue-ld",
"version": "0.1.8",
"version": "0.1.9",
"description": "A Vue.js wrapper for the LaunchDarkly SDK for Browser JavaScript",
"main": "dist/index.cjs.js",
"module": "dist/index.es.js",
Expand Down
2 changes: 1 addition & 1 deletion src/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
export { default as VueLd } from './plugin';
export { default as VueLd, initialize } from './plugin';
export { default as ldRedirectMixin } from './mixins/ldRedirect';
export { default as LDRouteGuard } from './components/LDRouteGuard.vue';
98 changes: 62 additions & 36 deletions src/plugin.js
Original file line number Diff line number Diff line change
@@ -1,46 +1,72 @@
import * as LDClient from 'launchdarkly-js-client-sdk';
import { formatFlags } from './utils';

export default {
install(Vue, options) {
const { clientSideId, user, options: ldOptions, readyBeforeIdentify = true } = options;
export const initialize = ({ clientSideId, user, ldOptions, readyBeforeIdentify }) => {
const ldClient = LDClient.initialize(clientSideId, user, ldOptions);
const $ld = {
ldClient,
identify({ newUser, hash, callback }, vueLdCallback) {
return new Promise((r) => {
this.ready = false;
ldClient.identify(newUser, hash, callback).then(() => {
this.ready = true;
if (vueLdCallback) {
const boundVueLdCallback = vueLdCallback.bind($ld);
boundVueLdCallback();
}
r();
});
});
},
flags: {},
ready: false,
};

const ldClient = LDClient.initialize(clientSideId, user, ldOptions);
ldClient.on('ready', () => {
$ld.flags = formatFlags(ldClient.allFlags());
$ld.ready = readyBeforeIdentify;
});

const $ld = Vue.observable({
ldClient,
identify({ newUser, hash, callback }, vueLdCallback) {
return new Promise((r) => {
this.ready = false;
ldClient.identify(newUser, hash, callback).then(() => {
this.ready = true;
if (vueLdCallback) {
const boundVueLdCallback = vueLdCallback.bind($ld);
boundVueLdCallback();
}
r();
});
});
},
flags: {},
ready: false,
});
ldClient.on('change', (changes) => {
const flattenedFlags = Object.fromEntries(
Object.keys(changes).map((key) => [key, changes[key].current])
);
$ld.flags = {
...$ld.flags,
...formatFlags(flattenedFlags),
};
});
return $ld;
};

const stub = ({ flagsStub, readyBeforeIdentify }) => {
return {
identify() {
this.ready = true;
},
flags: flagsStub,
ready: readyBeforeIdentify,
};
};

export default {
async install(Vue, options) {
const {
clientSideId,
user,
options: ldOptions,
flagsStub,
readyBeforeIdentify = true,
} = options;

ldClient.on('ready', () => {
$ld.flags = formatFlags(ldClient.allFlags());
$ld.ready = readyBeforeIdentify;
});
let $ld;

ldClient.on('change', (changes) => {
const flattenedFlags = Object.fromEntries(
Object.keys(changes).map((key) => [key, changes[key].current])
);
$ld.flags = {
...$ld.flags,
...formatFlags(flattenedFlags),
};
});
if (flagsStub) {
$ld = stub({ flagsStub, readyBeforeIdentify });
} else {
$ld = initialize({ clientSideId, user, ldOptions, readyBeforeIdentify });
}
// eslint-disable-next-line no-param-reassign
Vue.prototype.$ld = $ld;
Vue.prototype.$ld = Vue.observable($ld);
},
};
33 changes: 33 additions & 0 deletions tests/unit/vue-ld.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -95,4 +95,37 @@ describe('VueLd Plugin', () => {
expect(vueLdCallback).toBeCalled();
expect(vueLdCallback.mock.instances[0]).toBe(wrapper.vm.$ld);
});

it('stubs flags when passed the option', async () => {
localVue.use(VueLd, {
...vueLdOptions,
/*
Using a proxy like this will allow you to return true for everything
not explicitly on the base object or set later.
*/
flagsStub: new Proxy(
{
never: false,
},
{
get(obj, prop) {
const value = obj[prop];
return value === undefined ? true : value;
},
}
),
});
wrapper = mount(Component, {
localVue,
});

expect(wrapper.vm.$ld.flags.never).toBe(false);
expect(wrapper.vm.$ld.flags.anythingElse).toBe(true);

wrapper.vm.$ld.flags.neverLater = false;
expect(wrapper.vm.$ld.flags.neverLater).toBe(false);

delete wrapper.vm.$ld.flags.neverLater;
expect(wrapper.vm.$ld.flags.anythingElse).toBe(true);
});
});

0 comments on commit e37ab02

Please sign in to comment.