import {render, screen} from 'sentry-test/reactTestingLibrary'; import Feature from 'sentry/components/acl/feature'; import ConfigStore from 'sentry/stores/configStore'; import HookStore from 'sentry/stores/hookStore'; describe('Feature', function () { const organization = TestStubs.Organization({ features: ['org-foo', 'org-bar', 'bar'], }); const project = TestStubs.Project({ features: ['project-foo', 'project-bar'], }); const routerContext = TestStubs.routerContext([ { organization, project, }, ]); describe('as render prop', function () { const childrenMock = jest.fn().mockReturnValue(null); beforeEach(function () { childrenMock.mockClear(); }); it('has features', function () { const features = ['org-foo', 'project-foo']; render({childrenMock}, { context: routerContext, }); expect(childrenMock).toHaveBeenCalledWith({ hasFeature: true, features, organization, project, renderDisabled: false, }); }); it('has features when requireAll is false', function () { const features = ['org-foo', 'project-foo', 'apple']; render( {childrenMock} , {context: routerContext} ); expect(childrenMock).toHaveBeenCalledWith({ hasFeature: true, organization, project, features, renderDisabled: false, }); }); it('has no features', function () { render({childrenMock}, { context: routerContext, }); expect(childrenMock).toHaveBeenCalledWith({ hasFeature: false, organization, project, features: ['org-baz'], renderDisabled: false, }); }); it('calls render function when no features', function () { const noFeatureRenderer = jest.fn(() => null); render( {childrenMock} , {context: routerContext} ); expect(childrenMock).not.toHaveBeenCalled(); expect(noFeatureRenderer).toHaveBeenCalledWith({ hasFeature: false, children: childrenMock, organization, project, features: ['org-baz'], }); }); it('can specify org from props', function () { const customOrg = TestStubs.Organization({features: ['org-bazar']}); render( {childrenMock} , {context: routerContext} ); expect(childrenMock).toHaveBeenCalledWith({ hasFeature: true, organization: customOrg, project, features: ['org-bazar'], renderDisabled: false, }); }); it('can specify project from props', function () { const customProject = TestStubs.Project({features: ['project-baz']}); render( {childrenMock} , {context: routerContext} ); expect(childrenMock).toHaveBeenCalledWith({ hasFeature: true, organization, project: customProject, features: ['project-baz'], renderDisabled: false, }); }); it('handles no org/project', function () { const features = ['org-foo', 'project-foo']; render({childrenMock}, { context: routerContext, }); expect(childrenMock).toHaveBeenCalledWith( expect.objectContaining({ hasFeature: true, organization, project, features, renderDisabled: false, }) ); }); it('handles features prefixed with org/project', function () { render({childrenMock}, { context: routerContext, }); expect(childrenMock).toHaveBeenCalledWith({ hasFeature: true, organization, project, features: ['organizations:org-bar'], renderDisabled: false, }); render({childrenMock}, { context: routerContext, }); expect(childrenMock).toHaveBeenCalledWith({ hasFeature: false, organization, project, features: ['projects:bar'], renderDisabled: false, }); }); it('checks ConfigStore.config.features (e.g. `organizations:create`)', function () { ConfigStore.config = { features: new Set(['organizations:create']), }; render({childrenMock}, { context: routerContext, }); expect(childrenMock).toHaveBeenCalledWith({ hasFeature: true, organization, project, features: ['organizations:create'], renderDisabled: false, }); }); }); describe('no children', function () { it('should display renderDisabled with no feature', function () { render( disabled} />, {context: routerContext} ); expect(screen.getByText('disabled')).toBeInTheDocument(); }); it('should display be empty when on', function () { render( disabled} />, {context: routerContext} ); expect(screen.queryByText('disabled')).not.toBeInTheDocument(); }); }); describe('as React node', function () { it('has features', function () { render(
The Child
, {context: routerContext} ); expect(screen.getByText('The Child')).toBeInTheDocument(); }); it('has no features', function () { render(
The Child
, {context: routerContext} ); expect(screen.queryByText('The Child')).not.toBeInTheDocument(); }); it('renders a default disabled component', function () { render(
The Child
, {context: routerContext} ); expect(screen.getByText('This feature is coming soon!')).toBeInTheDocument(); expect(screen.queryByText('The Child')).not.toBeInTheDocument(); }); it('calls renderDisabled function when no features', function () { const noFeatureRenderer = jest.fn(() => null); const children =
The Child
; render( {children} , {context: routerContext} ); expect(screen.queryByText('The Child')).not.toBeInTheDocument(); expect(noFeatureRenderer).toHaveBeenCalledWith({ hasFeature: false, children, organization, project, features: ['org-baz'], }); }); }); describe('using HookStore for renderDisabled', function () { let hookFn; beforeEach(function () { hookFn = jest.fn(() => null); HookStore.hooks['feature-disabled:org-baz'] = [hookFn]; HookStore.hooks['feature-disabled:test-hook'] = [hookFn]; }); afterEach(function () { delete HookStore.hooks['feature-disabled:org-baz']; }); it('uses hookName if provided', function () { const children =
The Child
; render( {children} , {context: routerContext} ); expect(screen.queryByText('The Child')).not.toBeInTheDocument(); expect(hookFn).toHaveBeenCalledWith({ hasFeature: false, children, organization, project, features: ['org-bazar'], }); }); }); });