feature.spec.jsx 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. import React from 'react';
  2. import {mount} from 'enzyme';
  3. import Feature from 'app/components/acl/feature';
  4. import ConfigStore from 'app/stores/configStore';
  5. import HookStore from 'app/stores/hookStore';
  6. describe('Feature', function() {
  7. const organization = TestStubs.Organization({
  8. features: ['org-foo', 'org-bar', 'bar'],
  9. });
  10. const project = TestStubs.Project({
  11. features: ['project-foo', 'project-bar'],
  12. });
  13. const routerContext = TestStubs.routerContext([
  14. {
  15. organization,
  16. project,
  17. },
  18. ]);
  19. describe('as render prop', function() {
  20. let childrenMock = jest.fn().mockReturnValue(null);
  21. beforeEach(function() {
  22. childrenMock.mockClear();
  23. });
  24. it('has features', function() {
  25. const features = ['org-foo', 'project-foo'];
  26. mount(<Feature features={features}>{childrenMock}</Feature>, routerContext);
  27. expect(childrenMock).toHaveBeenCalledWith({
  28. hasFeature: true,
  29. features,
  30. organization,
  31. project,
  32. });
  33. });
  34. it('has features when requireAll is false', function() {
  35. const features = ['org-foo', 'project-foo', 'apple'];
  36. mount(
  37. <Feature features={features} requireAll={false}>
  38. {childrenMock}
  39. </Feature>,
  40. routerContext
  41. );
  42. expect(childrenMock).toHaveBeenCalledWith({
  43. hasFeature: true,
  44. organization,
  45. project,
  46. features,
  47. });
  48. });
  49. it('has no features', function() {
  50. mount(<Feature features={['org-baz']}>{childrenMock}</Feature>, routerContext);
  51. expect(childrenMock).toHaveBeenCalledWith({
  52. hasFeature: false,
  53. organization,
  54. project,
  55. features: ['org-baz'],
  56. });
  57. });
  58. it('calls render function when no features', function() {
  59. const noFeatureRenderer = jest.fn(() => null);
  60. mount(
  61. <Feature features={['org-baz']} renderDisabled={noFeatureRenderer}>
  62. {childrenMock}
  63. </Feature>,
  64. routerContext
  65. );
  66. expect(childrenMock).not.toHaveBeenCalled();
  67. expect(noFeatureRenderer).toHaveBeenCalledWith({
  68. hasFeature: false,
  69. children: childrenMock,
  70. organization,
  71. project,
  72. features: ['org-baz'],
  73. });
  74. });
  75. it('can specify org from props', function() {
  76. const customOrg = TestStubs.Organization({features: ['org-bazar']});
  77. mount(
  78. <Feature organization={customOrg} features={['org-bazar']}>
  79. {childrenMock}
  80. </Feature>,
  81. routerContext
  82. );
  83. expect(childrenMock).toHaveBeenCalledWith({
  84. hasFeature: true,
  85. organization: customOrg,
  86. project,
  87. features: ['org-bazar'],
  88. });
  89. });
  90. it('can specify project from props', function() {
  91. const customProject = TestStubs.Project({features: ['project-baz']});
  92. mount(
  93. <Feature project={customProject} features={['project-baz']}>
  94. {childrenMock}
  95. </Feature>,
  96. routerContext
  97. );
  98. expect(childrenMock).toHaveBeenCalledWith({
  99. hasFeature: true,
  100. organization,
  101. project: customProject,
  102. features: ['project-baz'],
  103. });
  104. });
  105. it('handles no org/project', function() {
  106. const features = ['org-foo', 'project-foo'];
  107. mount(
  108. <Feature organization={null} project={null} features={features}>
  109. {childrenMock}
  110. </Feature>,
  111. routerContext
  112. );
  113. expect(childrenMock).toHaveBeenCalledWith({
  114. hasFeature: false,
  115. organization: null,
  116. project: null,
  117. features,
  118. });
  119. });
  120. it('handles features prefixed with org/project', function() {
  121. mount(
  122. <Feature features={['organizations:org-bar']}>{childrenMock}</Feature>,
  123. routerContext
  124. );
  125. expect(childrenMock).toHaveBeenCalledWith({
  126. hasFeature: true,
  127. organization,
  128. project,
  129. features: ['organizations:org-bar'],
  130. });
  131. mount(<Feature features={['projects:bar']}>{childrenMock}</Feature>, routerContext);
  132. expect(childrenMock).toHaveBeenCalledWith({
  133. hasFeature: false,
  134. organization,
  135. project,
  136. features: ['projects:bar'],
  137. });
  138. });
  139. it('checks ConfigStore.config.features (e.g. `organizations:create`)', function() {
  140. ConfigStore.config = {
  141. features: new Set(['organizations:create']),
  142. };
  143. mount(
  144. <Feature features={['organizations:create']}>{childrenMock}</Feature>,
  145. routerContext
  146. );
  147. expect(childrenMock).toHaveBeenCalledWith({
  148. hasFeature: true,
  149. organization,
  150. project,
  151. features: ['organizations:create'],
  152. });
  153. });
  154. });
  155. describe('as React node', function() {
  156. it('has features', function() {
  157. const wrapper = mount(
  158. <Feature features={['org-bar']}>
  159. <div>The Child</div>
  160. </Feature>,
  161. routerContext
  162. );
  163. expect(wrapper.find('Feature div').text()).toBe('The Child');
  164. });
  165. it('has no features', function() {
  166. const wrapper = mount(
  167. <Feature features={['org-baz']}>
  168. <div>The Child</div>
  169. </Feature>,
  170. routerContext
  171. );
  172. expect(wrapper.find('Feature div')).toHaveLength(0);
  173. });
  174. it('renders a default disabled component', function() {
  175. const wrapper = mount(
  176. <Feature features={['org-baz']} renderDisabled>
  177. <div>The Child</div>
  178. </Feature>,
  179. routerContext
  180. );
  181. expect(wrapper.exists('ComingSoon')).toBe(true);
  182. expect(wrapper.exists('Feature div[children="The Child"]')).not.toBe(true);
  183. });
  184. it('calls renderDisabled function when no features', function() {
  185. const noFeatureRenderer = jest.fn(() => null);
  186. const children = <div>The Child</div>;
  187. const wrapper = mount(
  188. <Feature features={['org-baz']} renderDisabled={noFeatureRenderer}>
  189. {children}
  190. </Feature>,
  191. routerContext
  192. );
  193. expect(wrapper.find('Feature div')).toHaveLength(0);
  194. expect(noFeatureRenderer).toHaveBeenCalledWith({
  195. hasFeature: false,
  196. children,
  197. organization,
  198. project,
  199. features: ['org-baz'],
  200. });
  201. });
  202. });
  203. describe('using HookStore for renderDisabled', function() {
  204. let hookFn;
  205. beforeEach(function() {
  206. hookFn = jest.fn(() => null);
  207. HookStore.hooks['feature-disabled:org-baz'] = [hookFn];
  208. });
  209. afterEach(function() {
  210. delete HookStore.hooks['feature-disabled:org-baz'];
  211. });
  212. it('calls renderDisabled function from HookStore when no features', function() {
  213. const noFeatureRenderer = jest.fn(() => null);
  214. const children = <div>The Child</div>;
  215. const wrapper = mount(
  216. <Feature features={['org-baz']} renderDisabled={noFeatureRenderer}>
  217. {children}
  218. </Feature>,
  219. routerContext
  220. );
  221. expect(wrapper.find('Feature div')).toHaveLength(0);
  222. expect(noFeatureRenderer).not.toHaveBeenCalled();
  223. expect(hookFn).toHaveBeenCalledWith({
  224. hasFeature: false,
  225. children,
  226. organization,
  227. project,
  228. features: ['org-baz'],
  229. });
  230. });
  231. it('does not check hook store for multiple features', function() {
  232. const noFeatureRenderer = jest.fn(() => null);
  233. const wrapper = mount(
  234. <Feature features={['org-baz', 'org-bazar']} renderDisabled={noFeatureRenderer}>
  235. <div>The Child</div>
  236. </Feature>,
  237. routerContext
  238. );
  239. expect(wrapper.find('Feature div')).toHaveLength(0);
  240. expect(hookFn).not.toHaveBeenCalled();
  241. expect(noFeatureRenderer).toHaveBeenCalled();
  242. });
  243. });
  244. });