Skip to content

Commit ed52579

Browse files
authored
Gavsum/ch37466/add invert flag and redirect suffix (#38)
* suffix working, first component of flag invert * fix keep alive redirects, more tests * update readme * naming and comments
1 parent da78447 commit ed52579

File tree

6 files changed

+113
-32
lines changed

6 files changed

+113
-32
lines changed

README.md

+12-9
Original file line numberDiff line numberDiff line change
@@ -94,10 +94,12 @@ export default {
9494

9595
#### Arguments
9696

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

102104
### LDRouteGuard Component
103105

@@ -120,11 +122,12 @@ const route = {
120122

121123
#### Props
122124

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

129132
## Development
130133

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.7",
3+
"version": "0.1.8",
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/components/LDRouteGuard.vue

+3-2
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,12 @@ export default {
1010
props: {
1111
component: { type: [Function, Object, Promise], required: true },
1212
requiredFeatureFlag: { type: String, required: true },
13-
to: { type: [String, Object], required: true },
13+
to: { type: [String, Object, Function], required: true },
14+
invertFlag: { type: Boolean, required: false, default: false },
1415
},
1516
computed: {
1617
show() {
17-
return this.$ld.ready && this.$ld.flags[this.requiredFeatureFlag];
18+
return this.$ld.ready && this.ldRedirectFlagValue;
1819
},
1920
importedComponent() {
2021
// Handle dynamically imported components

src/mixins/ldRedirect.js

+39-12
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,32 @@
1-
export default (requiredFeatureFlag, to) => {
1+
export default (requiredFeatureFlag, to, invertFlag) => {
22
return {
33
data() {
44
return {
55
ldRedirectReadyWatcher: null,
66
ldRedirectFlagWatcher: null,
7+
ldRedirectHasBeenDeactivated: false,
78
};
89
},
910
computed: {
11+
ldRedirectFlagValue() {
12+
return this.invertFlag || invertFlag
13+
? !this.$ld.flags[requiredFeatureFlag || this.requiredFeatureFlag]
14+
: this.$ld.flags[requiredFeatureFlag || this.requiredFeatureFlag];
15+
},
1016
ldRedirectShouldRedirect() {
11-
return this.$ld.ready && !this.$ld.flags[requiredFeatureFlag || this.requiredFeatureFlag];
17+
return this.$ld.ready && !this.ldRedirectFlagValue;
1218
},
1319
ldRedirectShouldDestroy() {
14-
return this.$ld.ready && this.$ld.flags[requiredFeatureFlag || this.requiredFeatureFlag];
20+
return this.$ld.ready && this.ldRedirectFlagValue;
21+
},
22+
ldRedirectResolveTo() {
23+
// handles 'to' redirect values passed as functions
24+
const redirectVal = to == null ? this.ldRedirectTo : to;
25+
if (typeof redirectVal === 'function') {
26+
const boundRedirectTo = redirectVal.bind(this);
27+
return boundRedirectTo();
28+
}
29+
return redirectVal;
1530
},
1631
},
1732
methods: {
@@ -22,7 +37,7 @@ export default (requiredFeatureFlag, to) => {
2237
},
2338
() => {
2439
if (this.ldRedirectShouldRedirect) {
25-
this.$router.push(to == null ? this.ldRedirectTo : to);
40+
this.$router.push(this.ldRedirectResolveTo);
2641
} else if (this.ldRedirectShouldDestroy) {
2742
this.ldRedirectDestroyWatchers();
2843
}
@@ -32,11 +47,11 @@ export default (requiredFeatureFlag, to) => {
3247
setLdRedirectFlagWatcher() {
3348
this.ldRedirectFlagWatcher = this.$watch(
3449
() => {
35-
return this.$ld.flags[requiredFeatureFlag || this.requiredFeatureFlag];
50+
return this.ldRedirectFlagValue;
3651
},
3752
() => {
3853
if (this.ldRedirectShouldRedirect) {
39-
this.$router.push(to == null ? this.ldRedirectTo : to);
54+
this.$router.push(this.ldRedirectResolveTo);
4055
} else if (this.ldRedirectShouldDestroy) {
4156
this.ldRedirectDestroyWatchers();
4257
}
@@ -53,14 +68,26 @@ export default (requiredFeatureFlag, to) => {
5368
this.ldRedirectFlagWatcher = null;
5469
}
5570
},
71+
ldRedirectHandler() {
72+
if (this.$ld.ready && !this.ldRedirectFlagValue) {
73+
this.$router.push(this.ldRedirectResolveTo);
74+
} else if (!this.ldReady) {
75+
this.setLdRedirectReadyWatcher();
76+
this.setLdRedirectFlagWatcher();
77+
}
78+
},
5679
},
57-
mounted() {
58-
if (this.$ld.ready && !this.$ld.flags[requiredFeatureFlag || this.requiredFeatureFlag]) {
59-
this.$router.push(to == null ? this.ldRedirectTo : to);
60-
} else if (!this.ldReady) {
61-
this.setLdRedirectReadyWatcher();
62-
this.setLdRedirectFlagWatcher();
80+
activated() {
81+
// activated lifecycle trigger used for keep-alive components
82+
if (this.ldRedirectHasBeenDeactivated) {
83+
this.ldRedirectHandler();
6384
}
6485
},
86+
mounted() {
87+
this.ldRedirectHandler();
88+
},
89+
deactivated() {
90+
this.ldRedirectHasBeenDeactivated = true;
91+
},
6592
};
6693
};

tests/unit/ld-redirect.spec.js

+42-4
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,11 @@ const Component = {
1010
template: '<div></div>',
1111
};
1212

13-
const mixins = [ldRedirect('myFlag', '/')];
13+
const createMixin = (props) => {
14+
return props
15+
? [ldRedirect(props.flag, props.redirect, props.invertFlag)]
16+
: [ldRedirect('myFlag', '/', false)];
17+
};
1418

1519
describe('ldRedirectMixin', () => {
1620
let server;
@@ -23,7 +27,7 @@ describe('ldRedirectMixin', () => {
2327
let localVue;
2428
let mocks;
2529
let wrapper;
26-
const finishSetup = async () => {
30+
const finishSetup = async (mixins) => {
2731
localVue = createLocalVue();
2832
localVue.use(VueLd, vueLdOptions);
2933
mocks = {
@@ -45,7 +49,7 @@ describe('ldRedirectMixin', () => {
4549
const flags = cloneDeep(flagsResponse);
4650
flags.myFlag.value = false;
4751
server.respondWith([200, { 'Content-Type': 'application/json' }, JSON.stringify(flags)]);
48-
await finishSetup();
52+
await finishSetup(createMixin());
4953
expect(wrapper.vm.$ld.flags.myFlag).toBe(false);
5054
expect(wrapper.vm.$router.push).toHaveBeenCalled();
5155
});
@@ -56,10 +60,44 @@ describe('ldRedirectMixin', () => {
5660
{ 'Content-Type': 'application/json' },
5761
JSON.stringify(flagsResponse),
5862
]);
59-
await finishSetup();
63+
await finishSetup(createMixin());
6064
expect(wrapper.vm.$ld.flags.myFlag).toBe(true);
6165
expect(wrapper.vm.$router.push).not.toHaveBeenCalled();
6266
expect(wrapper.vm.ldRedirectReadyWatcher).toBe(null);
6367
expect(wrapper.vm.ldRedirectFlagWatcher).toBe(null);
6468
});
69+
70+
it('correctly handles redirect as object', async () => {
71+
const flags = cloneDeep(flagsResponse);
72+
flags.myFlag.value = false;
73+
server.respondWith([200, { 'Content-Type': 'application/json' }, JSON.stringify(flags)]);
74+
const redirectObj = { to: 'some.route' };
75+
await finishSetup(createMixin({ flag: 'myFlag', redirect: redirectObj, invertFlag: false }));
76+
expect(wrapper.vm.$ld.flags.myFlag).toBe(false);
77+
expect(wrapper.vm.$router.push).toBeCalledWith(redirectObj);
78+
});
79+
80+
it('correctly handles redirect as function', async () => {
81+
const flags = cloneDeep(flagsResponse);
82+
flags.myFlag.value = false;
83+
server.respondWith([200, { 'Content-Type': 'application/json' }, JSON.stringify(flags)]);
84+
const redirectObj = { to: 'some.route' };
85+
const redirectFunc = () => {
86+
return redirectObj;
87+
};
88+
await finishSetup(createMixin({ flag: 'myFlag', redirect: redirectFunc, invertFlag: false }));
89+
expect(wrapper.vm.$ld.flags.myFlag).toBe(false);
90+
expect(wrapper.vm.$router.push).toBeCalledWith(redirectObj);
91+
});
92+
93+
it('redirects on true featureflag if invertFlag is set', async () => {
94+
const flags = cloneDeep(flagsResponse);
95+
flags.myFlag.value = true;
96+
server.respondWith([200, { 'Content-Type': 'application/json' }, JSON.stringify(flags)]);
97+
await finishSetup(
98+
createMixin({ flag: 'myFlag', redirect: { to: 'some.route' }, invertFlag: true })
99+
);
100+
expect(wrapper.vm.$ld.flags.myFlag).toBe(true);
101+
expect(wrapper.vm.$router.push).toHaveBeenCalled();
102+
});
65103
});

tests/unit/ld-route-guard.spec.js

+16-4
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ describe('ldRedirectMixin', () => {
2727
let localVue;
2828
let mocks;
2929
let wrapper;
30-
const finishSetup = async (component = EmptyComponent) => {
30+
const finishSetup = async (component, invertFlag) => {
3131
localVue = createLocalVue();
3232
localVue.use(VueLd, vueLdOptions);
3333
mocks = {
@@ -41,6 +41,7 @@ describe('ldRedirectMixin', () => {
4141
component,
4242
requiredFeatureFlag: 'myFlag',
4343
to: '/',
44+
invertFlag,
4445
},
4546
});
4647
await ldClientReady(wrapper);
@@ -54,7 +55,7 @@ describe('ldRedirectMixin', () => {
5455
const flags = cloneDeep(flagsResponse);
5556
flags.myFlag.value = false;
5657
server.respondWith([200, { 'Content-Type': 'application/json' }, JSON.stringify(flags)]);
57-
await finishSetup();
58+
await finishSetup(EmptyComponent, false);
5859
expect(wrapper.vm.$router.push).toHaveBeenCalled();
5960
expect(wrapper.findComponent(EmptyComponent).exists()).toBe(false);
6061
});
@@ -65,7 +66,7 @@ describe('ldRedirectMixin', () => {
6566
server.respondWith([200, { 'Content-Type': 'application/json' }, JSON.stringify(flags)]);
6667

6768
const dynamicEmptyComponent = new Promise((resolve) => resolve(EmptyComponent));
68-
await finishSetup(dynamicEmptyComponent);
69+
await finishSetup(dynamicEmptyComponent, false);
6970
expect(wrapper.vm.$router.push).toHaveBeenCalled();
7071
});
7172

@@ -75,8 +76,19 @@ describe('ldRedirectMixin', () => {
7576
{ 'Content-Type': 'application/json' },
7677
JSON.stringify(flagsResponse),
7778
]);
78-
await finishSetup();
79+
await finishSetup(EmptyComponent, false);
7980
expect(wrapper.vm.$router.push).not.toHaveBeenCalled();
8081
expect(wrapper.findComponent(EmptyComponent).exists()).toBe(true);
8182
});
83+
84+
it('redirects with featureflag if invertFlag is set', async () => {
85+
server.respondWith([
86+
200,
87+
{ 'Content-Type': 'application/json' },
88+
JSON.stringify(flagsResponse),
89+
]);
90+
await finishSetup(EmptyComponent, true);
91+
expect(wrapper.vm.$router.push).toHaveBeenCalled();
92+
expect(wrapper.findComponent(EmptyComponent).exists()).toBe(false);
93+
});
8294
});

0 commit comments

Comments
 (0)