[core/api] added tests for FeatureFlags.tsx

This commit is contained in:
Bilawal Hameed
2020-03-27 11:09:07 +01:00
parent abe68bc55b
commit 4e38f03221
2 changed files with 183 additions and 2 deletions
@@ -0,0 +1,155 @@
/*
* Copyright 2020 Spotify AB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React, { useContext } from 'react';
import { render } from '@testing-library/react';
import {
FeatureFlags,
FeatureFlagsContext,
FeatureFlagsContextProvider,
} from './FeatureFlags';
import { FeatureFlagState } from '../apis/definitions/featureFlags';
describe('FeatureFlags', () => {
beforeEach(() => {
window.localStorage.clear();
});
describe('#get', () => {
it('defaults to .NotEnabled', () => {
expect(FeatureFlags.get('enable-feature-flag')).toBe(
FeatureFlagState.NotEnabled,
);
});
it('returns a .Enabled state', () => {
FeatureFlags.set('enable-feature-flag', FeatureFlagState.Enabled);
expect(FeatureFlags.get('enable-feature-flag')).toBe(
FeatureFlagState.Enabled,
);
});
it('returns a .NotEnabled state', () => {
FeatureFlags.set('enable-feature-flag', FeatureFlagState.NotEnabled);
expect(FeatureFlags.get('enable-feature-flag')).toBe(
FeatureFlagState.NotEnabled,
);
});
});
describe('#set', () => {
it('fails if name is less than three characters', () => {
expect(() => {
FeatureFlags.set('ab', FeatureFlagState.NotEnabled);
}).toThrow(/minimum length of three characters/i);
});
it('fails if name is greater than 150 characters', () => {
expect(() => {
FeatureFlags.set(
'loremipsumdolorsitametconsecteturadipiscingelitnuncvitaeportaexaullamcorperturpismaurisutmattisnequemorbisediaculisauguevivamuspulvinarcursuseratblandithendreritquisqueuttinciduntmagnavestibulumblanditaugueat',
FeatureFlagState.NotEnabled,
);
}).toThrow(/not exceed 150 characters/i);
});
it('fails if name does not start with a lowercase letter', () => {
expect(() => {
FeatureFlags.set('123456789', FeatureFlagState.NotEnabled);
}).toThrow(/start with a lowercase letter/i);
});
it('fails if name contains characters other than lowercase letters, numbers and hyphens', () => {
expect(() => {
FeatureFlags.set('Invalid_Feature_Flag', FeatureFlagState.NotEnabled);
}).toThrow(/only contain lowercase letters, numbers and hyphens/i);
});
it('fails if state is not recognized from FeatureFlagState', () => {
expect(() => {
// @ts-ignore
FeatureFlags.set('valid-feature-flag', 'invalid state');
}).toThrow(/requires a recognized value from the FeatureFlagState/i);
});
it('sets a feature flag to enabled', () => {
expect(window.localStorage.featureFlags).toBeUndefined();
FeatureFlags.set('valid-feature-flag', FeatureFlagState.Enabled);
expect(window.localStorage.featureFlags).toBe(
'{"valid-feature-flag":true}',
);
});
it('sets a feature flag to disabled', () => {
expect(window.localStorage.featureFlags).toBeUndefined();
FeatureFlags.set('valid-feature-flag', FeatureFlagState.NotEnabled);
expect(window.localStorage.featureFlags).toBe('{}');
});
it('sets a feature flag to disabled then enabled', () => {
expect(window.localStorage.featureFlags).toBeUndefined();
FeatureFlags.set('valid-feature-flag', FeatureFlagState.NotEnabled);
FeatureFlags.set('valid-feature-flag', FeatureFlagState.Enabled);
expect(window.localStorage.featureFlags).toBe(
'{"valid-feature-flag":true}',
);
});
it('sets multiple feature flags', () => {
expect(window.localStorage.featureFlags).toBeUndefined();
FeatureFlags.set('valid-feature-flag', FeatureFlagState.Enabled);
FeatureFlags.set('another-valid-feature-flag', FeatureFlagState.Enabled);
expect(window.localStorage.featureFlags).toBe(
'{"valid-feature-flag":true,"another-valid-feature-flag":true}',
);
});
});
});
describe('FeatureFlagsContext', () => {
it('returns an empty list without the context', () => {
expect.assertions(1);
const Component = () => {
const { registeredFeatureFlags } = useContext(FeatureFlagsContext);
expect(registeredFeatureFlags).toEqual([]);
return null;
};
render(<Component />);
});
it('returns a list of registered feature flags', () => {
expect.assertions(1);
const mockFeatureFlags = [
{ name: 'feature-flag-one', pluginId: 'plugin-one' },
{ name: 'feature-flag-two', pluginId: 'plugin-two' },
];
const Component = () => {
const { registeredFeatureFlags } = useContext(FeatureFlagsContext);
expect(registeredFeatureFlags).toBe(mockFeatureFlags);
return null;
};
render(
<FeatureFlagsContextProvider registeredFeatureFlags={mockFeatureFlags}>
<Component />
</FeatureFlagsContextProvider>,
);
});
});
+28 -2
View File
@@ -56,8 +56,34 @@ class FeatureFlagsImpl implements FeatureFlagsApi {
set(name: FeatureFlagName, state: FeatureFlagState): void {
const flags = this.getUserEnabledFeatureFlags();
if (state === FeatureFlagState.NotEnabled) flags.delete(name);
if (state === FeatureFlagState.Enabled) flags.add(name);
if (name.length < 3) {
throw new Error(
'The `name` argument must have a minimum length of three characters.',
);
}
if (name.length > 150) {
throw new Error('The `name` argument must not exceed 150 characters.');
}
if (!name.match(/^[a-z]+[a-z0-9-]+$/)) {
throw new Error(
'The `name` argument must start with a lowercase letter and only contain lowercase letters, numbers and hyphens.' +
'Examples: feature-flag-one, alpha, release-2020',
);
}
if (state === FeatureFlagState.Enabled) {
flags.add(name);
} else if (state === FeatureFlagState.NotEnabled) {
flags.delete(name);
} else {
throw new Error(
'The `state` argument requires a recognized value from the FeatureFlagState enum. ' +
'Please check the Backstage documentation to see all the available options.' +
'Example values: FeatureFlagState.NotEnabled, FeatureFlagState.Enabled',
);
}
window.localStorage.setItem(
this.localStorageKey,