From b3986e06460cc2bff35c1c1e7902579b46c1d4c6 Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Mon, 9 Oct 2023 18:07:58 +0000 Subject: Add latest changes from gitlab-org/gitlab@master --- spec/factories/pages_deployments.rb | 4 - spec/features/groups/navbar_spec.rb | 13 - spec/features/markdown/observability_spec.rb | 77 ------ .../markdown/render_observability_spec.js | 43 ---- spec/frontend/ide/init_gitlab_web_ide_spec.js | 36 ++- spec/frontend/observability/index_spec.js | 64 ----- .../observability/observability_app_spec.js | 201 --------------- spec/frontend/observability/skeleton_spec.js | 86 +++---- spec/helpers/groups/observability_helper_spec.rb | 76 ------ spec/helpers/ide_helper_spec.rb | 33 ++- .../filter/inline_observability_filter_spec.rb | 101 -------- spec/lib/gitlab/observability_spec.rb | 203 --------------- .../groups/menus/observability_menu_spec.rb | 93 ------- .../super_sidebar_menus/monitor_menu_spec.rb | 22 -- .../sidebars/groups/super_sidebar_panel_spec.rb | 1 - spec/models/issue_spec.rb | 113 +++++++++ spec/models/pages/lookup_path_spec.rb | 19 -- spec/models/pages_deployment_spec.rb | 38 --- spec/models/project_pages_metadatum_spec.rb | 21 -- spec/models/project_spec.rb | 12 - spec/policies/group_policy_spec.rb | 67 ----- .../groups/observability_controller_spec.rb | 99 -------- spec/requests/projects/issues_controller_spec.rb | 23 -- .../projects/merge_requests/creations_spec.rb | 12 - .../projects/merge_requests_controller_spec.rb | 13 - spec/routing/group_routing_spec.rb | 8 - .../migrate_from_legacy_storage_service_spec.rb | 137 ---------- ...te_legacy_storage_to_deployment_service_spec.rb | 118 --------- spec/services/pages/zip_directory_service_spec.rb | 280 --------------------- spec/services/todo_service_spec.rb | 35 ++- spec/support/ability_check_todo.yml | 2 - spec/support/helpers/navbar_structure_helper.rb | 13 - spec/support/rspec_order_todo.yml | 2 - .../embed_observabilities_examples.rb | 61 ----- .../observability/observability.html.haml_spec.rb | 18 -- 35 files changed, 235 insertions(+), 1909 deletions(-) delete mode 100644 spec/features/markdown/observability_spec.rb delete mode 100644 spec/frontend/behaviors/markdown/render_observability_spec.js delete mode 100644 spec/frontend/observability/index_spec.js delete mode 100644 spec/frontend/observability/observability_app_spec.js delete mode 100644 spec/helpers/groups/observability_helper_spec.rb delete mode 100644 spec/lib/banzai/filter/inline_observability_filter_spec.rb delete mode 100644 spec/lib/sidebars/groups/menus/observability_menu_spec.rb delete mode 100644 spec/lib/sidebars/groups/super_sidebar_menus/monitor_menu_spec.rb delete mode 100644 spec/models/project_pages_metadatum_spec.rb delete mode 100644 spec/requests/groups/observability_controller_spec.rb delete mode 100644 spec/services/pages/migrate_from_legacy_storage_service_spec.rb delete mode 100644 spec/services/pages/migrate_legacy_storage_to_deployment_service_spec.rb delete mode 100644 spec/services/pages/zip_directory_service_spec.rb delete mode 100644 spec/support/shared_examples/observability/embed_observabilities_examples.rb delete mode 100644 spec/views/groups/observability/observability.html.haml_spec.rb (limited to 'spec') diff --git a/spec/factories/pages_deployments.rb b/spec/factories/pages_deployments.rb index d3e2fefb4ae..eaa3a68770f 100644 --- a/spec/factories/pages_deployments.rb +++ b/spec/factories/pages_deployments.rb @@ -8,10 +8,6 @@ FactoryBot.define do filename { nil } end - trait(:migrated) do - filename { PagesDeployment::MIGRATED_FILE_NAME } - end - after(:build) do |deployment, evaluator| file = UploadedFile.new("spec/fixtures/pages.zip", filename: evaluator.filename) diff --git a/spec/features/groups/navbar_spec.rb b/spec/features/groups/navbar_spec.rb index 6a38f0c59a8..76e4e32d138 100644 --- a/spec/features/groups/navbar_spec.rb +++ b/spec/features/groups/navbar_spec.rb @@ -18,7 +18,6 @@ RSpec.describe 'Group navbar', :with_license, feature_category: :navigation do stub_config(dependency_proxy: { enabled: false }) stub_config(registry: { enabled: false }) - stub_feature_flags(observability_group_tab: false) stub_group_wikis(false) group.add_maintainer(user) sign_in(user) @@ -93,16 +92,4 @@ RSpec.describe 'Group navbar', :with_license, feature_category: :navigation do it_behaves_like 'verified navigation bar' end - - context 'when observability tab is enabled' do - before do - stub_feature_flags(observability_group_tab: true) - - insert_observability_nav - - visit group_path(group) - end - - it_behaves_like 'verified navigation bar' - end end diff --git a/spec/features/markdown/observability_spec.rb b/spec/features/markdown/observability_spec.rb deleted file mode 100644 index ec414d4396e..00000000000 --- a/spec/features/markdown/observability_spec.rb +++ /dev/null @@ -1,77 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe 'Observability rendering', :js, feature_category: :metrics do - let_it_be(:group) { create(:group, :public) } - let_it_be(:project) { create(:project, :repository, group: group) } - let_it_be(:user) { create(:user) } - let_it_be(:observable_url) { "https://www.gitlab.com/groups/#{group.path}/-/observability/explore?observability_path=/explore?foo=bar" } - let_it_be(:expected_observable_url) { "https://observe.gitlab.com/-/#{group.id}/explore?foo=bar" } - - before do - stub_config_setting(url: "https://www.gitlab.com") - group.add_developer(user) - sign_in(user) - end - - context 'when user is a developer of the embedded group' do - context 'when embedding in an issue' do - let(:issue) do - create(:issue, project: project, description: observable_url) - end - - before do - visit project_issue_path(project, issue) - wait_for_requests - end - - it_behaves_like 'embeds observability' - end - - context 'when embedding in an MR' do - let(:merge_request) do - create(:merge_request, source_project: project, target_project: project, description: observable_url) - end - - before do - visit merge_request_path(merge_request) - wait_for_requests - end - - it_behaves_like 'embeds observability' - end - end - - context 'when feature flag is disabled' do - before do - stub_feature_flags(observability_group_tab: false) - end - - context 'when embedding in an issue' do - let(:issue) do - create(:issue, project: project, description: observable_url) - end - - before do - visit project_issue_path(project, issue) - wait_for_requests - end - - it_behaves_like 'does not embed observability' - end - - context 'when embedding in an MR' do - let(:merge_request) do - create(:merge_request, source_project: project, target_project: project, description: observable_url) - end - - before do - visit merge_request_path(merge_request) - wait_for_requests - end - - it_behaves_like 'does not embed observability' - end - end -end diff --git a/spec/frontend/behaviors/markdown/render_observability_spec.js b/spec/frontend/behaviors/markdown/render_observability_spec.js deleted file mode 100644 index f464c01ac15..00000000000 --- a/spec/frontend/behaviors/markdown/render_observability_spec.js +++ /dev/null @@ -1,43 +0,0 @@ -import Vue from 'vue'; -import { createWrapper } from '@vue/test-utils'; -import renderObservability from '~/behaviors/markdown/render_observability'; -import { INLINE_EMBED_DIMENSIONS, SKELETON_VARIANT_EMBED } from '~/observability/constants'; -import ObservabilityApp from '~/observability/components/observability_app.vue'; - -describe('renderObservability', () => { - let subject; - - beforeEach(() => { - subject = document.createElement('div'); - subject.classList.add('js-render-observability'); - subject.dataset.frameUrl = 'https://observe.gitlab.com/'; - document.body.appendChild(subject); - }); - - afterEach(() => { - subject.remove(); - }); - - it('should return an array of Vue instances', () => { - const vueInstances = renderObservability([ - ...document.querySelectorAll('.js-render-observability'), - ]); - expect(vueInstances).toEqual([expect.any(Vue)]); - }); - - it('should correctly pass props to the ObservabilityApp component', () => { - const vueInstances = renderObservability([ - ...document.querySelectorAll('.js-render-observability'), - ]); - - const wrapper = createWrapper(vueInstances[0]); - - expect(wrapper.findComponent(ObservabilityApp).props()).toMatchObject({ - observabilityIframeSrc: 'https://observe.gitlab.com/', - skeletonVariant: SKELETON_VARIANT_EMBED, - inlineEmbed: true, - height: INLINE_EMBED_DIMENSIONS.HEIGHT, - width: INLINE_EMBED_DIMENSIONS.WIDTH, - }); - }); -}); diff --git a/spec/frontend/ide/init_gitlab_web_ide_spec.js b/spec/frontend/ide/init_gitlab_web_ide_spec.js index 73c8ad88aa3..6a5bedb0bbb 100644 --- a/spec/frontend/ide/init_gitlab_web_ide_spec.js +++ b/spec/frontend/ide/init_gitlab_web_ide_spec.js @@ -36,9 +36,9 @@ const TEST_START_REMOTE_PARAMS = { remotePath: '/test/projects/f oo', connectionToken: '123abc', }; -const TEST_EDITOR_FONT_SRC_URL = 'http://gitlab.test/assets/jetbrains-mono/JetBrainsMono.woff2'; +const TEST_EDITOR_FONT_SRC_URL = 'http://gitlab.test/assets/gitlab-mono/GitLabMono.woff2'; const TEST_EDITOR_FONT_FORMAT = 'woff2'; -const TEST_EDITOR_FONT_FAMILY = 'JebBrains Mono'; +const TEST_EDITOR_FONT_FAMILY = 'GitLab Mono'; describe('ide/init_gitlab_web_ide', () => { let resolveConfirm; @@ -56,9 +56,20 @@ describe('ide/init_gitlab_web_ide', () => { el.dataset.userPreferencesPath = TEST_USER_PREFERENCES_PATH; el.dataset.mergeRequest = TEST_MR_ID; el.dataset.filePath = TEST_FILE_PATH; - el.dataset.editorFontSrcUrl = TEST_EDITOR_FONT_SRC_URL; - el.dataset.editorFontFormat = TEST_EDITOR_FONT_FORMAT; - el.dataset.editorFontFamily = TEST_EDITOR_FONT_FAMILY; + el.dataset.editorFont = JSON.stringify({ + fallback_font_family: 'monospace', + font_faces: [ + { + family: TEST_EDITOR_FONT_FAMILY, + src: [ + { + url: TEST_EDITOR_FONT_SRC_URL, + format: TEST_EDITOR_FONT_FORMAT, + }, + ], + }, + ], + }); el.dataset.signInPath = TEST_SIGN_IN_PATH; document.body.append(el); @@ -121,9 +132,18 @@ describe('ide/init_gitlab_web_ide', () => { signIn: TEST_SIGN_IN_PATH, }, editorFont: { - srcUrl: TEST_EDITOR_FONT_SRC_URL, - fontFamily: TEST_EDITOR_FONT_FAMILY, - format: TEST_EDITOR_FONT_FORMAT, + fallbackFontFamily: 'monospace', + fontFaces: [ + { + family: TEST_EDITOR_FONT_FAMILY, + src: [ + { + url: TEST_EDITOR_FONT_SRC_URL, + format: TEST_EDITOR_FONT_FORMAT, + }, + ], + }, + ], }, handleStartRemote: expect.any(Function), handleTracking, diff --git a/spec/frontend/observability/index_spec.js b/spec/frontend/observability/index_spec.js deleted file mode 100644 index 25eb048c62b..00000000000 --- a/spec/frontend/observability/index_spec.js +++ /dev/null @@ -1,64 +0,0 @@ -import { createWrapper } from '@vue/test-utils'; -import Vue, { nextTick } from 'vue'; -import renderObservability from '~/observability/index'; -import ObservabilityApp from '~/observability/components/observability_app.vue'; -import { SKELETON_VARIANTS_BY_ROUTE } from '~/observability/constants'; - -describe('renderObservability', () => { - let element; - let vueInstance; - let component; - - const OBSERVABILITY_ROUTES = Object.keys(SKELETON_VARIANTS_BY_ROUTE); - const SKELETON_VARIANTS = Object.values(SKELETON_VARIANTS_BY_ROUTE); - - beforeEach(() => { - element = document.createElement('div'); - element.setAttribute('id', 'js-observability-app'); - element.dataset.observabilityIframeSrc = 'https://observe.gitlab.com/'; - document.body.appendChild(element); - - vueInstance = renderObservability(); - component = createWrapper(vueInstance).findComponent(ObservabilityApp); - }); - - afterEach(() => { - element.remove(); - }); - - it('should return a Vue instance', () => { - expect(vueInstance).toEqual(expect.any(Vue)); - }); - - it('should render the ObservabilityApp component', () => { - expect(component.props('observabilityIframeSrc')).toBe('https://observe.gitlab.com/'); - }); - - describe('skeleton variant', () => { - it.each` - pathDescription | path | variant - ${'dashboards'} | ${OBSERVABILITY_ROUTES[0]} | ${SKELETON_VARIANTS[0]} - ${'explore'} | ${OBSERVABILITY_ROUTES[1]} | ${SKELETON_VARIANTS[1]} - ${'manage dashboards'} | ${OBSERVABILITY_ROUTES[2]} | ${SKELETON_VARIANTS[2]} - ${'any other'} | ${'unknown/route'} | ${SKELETON_VARIANTS[0]} - `( - 'renders the $variant skeleton variant for $pathDescription path', - async ({ path, variant }) => { - component.vm.$router.push(path); - await nextTick(); - - expect(component.props('skeletonVariant')).toBe(variant); - }, - ); - }); - - it('handle route-update events', () => { - component.vm.$router.push('/something?foo=bar'); - component.vm.$emit('route-update', { url: '/some_path' }); - expect(component.vm.$router.currentRoute.path).toBe('/something'); - expect(component.vm.$router.currentRoute.query).toEqual({ - foo: 'bar', - observability_path: '/some_path', - }); - }); -}); diff --git a/spec/frontend/observability/observability_app_spec.js b/spec/frontend/observability/observability_app_spec.js deleted file mode 100644 index 392992a5962..00000000000 --- a/spec/frontend/observability/observability_app_spec.js +++ /dev/null @@ -1,201 +0,0 @@ -import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; -import { stubComponent } from 'helpers/stub_component'; -import ObservabilityApp from '~/observability/components/observability_app.vue'; -import ObservabilitySkeleton from '~/observability/components/skeleton/index.vue'; -import { - MESSAGE_EVENT_TYPE, - INLINE_EMBED_DIMENSIONS, - FULL_APP_DIMENSIONS, - SKELETON_VARIANT_EMBED, -} from '~/observability/constants'; - -import { darkModeEnabled } from '~/lib/utils/color_utils'; - -jest.mock('~/lib/utils/color_utils'); - -describe('ObservabilityApp', () => { - let wrapper; - - const $route = { - pathname: 'https://gitlab.com/gitlab-org/', - path: 'https://gitlab.com/gitlab-org/-/observability/dashboards', - query: { otherQuery: 100 }, - }; - - const mockSkeletonOnContentLoaded = jest.fn(); - - const findIframe = () => wrapper.findByTestId('observability-ui-iframe'); - - const TEST_IFRAME_SRC = 'https://observe.gitlab.com/9970/?groupId=14485840'; - - const TEST_USERNAME = 'test-user'; - - const mountComponent = (props) => { - wrapper = shallowMountExtended(ObservabilityApp, { - propsData: { - observabilityIframeSrc: TEST_IFRAME_SRC, - ...props, - }, - stubs: { - ObservabilitySkeleton: stubComponent(ObservabilitySkeleton, { - methods: { onContentLoaded: mockSkeletonOnContentLoaded }, - }), - }, - mocks: { - $route, - }, - }); - }; - - const dispatchMessageEvent = (message) => - window.dispatchEvent(new MessageEvent('message', message)); - - beforeEach(() => { - gon.current_username = TEST_USERNAME; - }); - - describe('iframe src', () => { - it('should render an iframe with observabilityIframeSrc, decorated with light theme and username', () => { - darkModeEnabled.mockReturnValueOnce(false); - mountComponent(); - const iframe = findIframe(); - - expect(iframe.exists()).toBe(true); - expect(iframe.attributes('src')).toBe( - `${TEST_IFRAME_SRC}&theme=light&username=${TEST_USERNAME}`, - ); - }); - - it('should render an iframe with observabilityIframeSrc decorated with dark theme and username', () => { - darkModeEnabled.mockReturnValueOnce(true); - mountComponent(); - const iframe = findIframe(); - - expect(iframe.exists()).toBe(true); - expect(iframe.attributes('src')).toBe( - `${TEST_IFRAME_SRC}&theme=dark&username=${TEST_USERNAME}`, - ); - }); - }); - - describe('iframe sandbox', () => { - it('should render an iframe with sandbox attributes', () => { - mountComponent(); - const iframe = findIframe(); - - expect(iframe.exists()).toBe(true); - expect(iframe.attributes('sandbox')).toBe('allow-same-origin allow-forms allow-scripts'); - }); - }); - - describe('iframe kiosk query param', () => { - it('when inlineEmbed, it should set the proper kiosk query parameter', () => { - mountComponent({ - inlineEmbed: true, - }); - - const iframe = findIframe(); - - expect(iframe.attributes('src')).toBe( - `${TEST_IFRAME_SRC}&theme=light&username=${TEST_USERNAME}&kiosk=inline-embed`, - ); - }); - }); - - describe('iframe size', () => { - it('should set the specified size', () => { - mountComponent({ - height: INLINE_EMBED_DIMENSIONS.HEIGHT, - width: INLINE_EMBED_DIMENSIONS.WIDTH, - }); - - const iframe = findIframe(); - - expect(iframe.attributes('width')).toBe(INLINE_EMBED_DIMENSIONS.WIDTH); - expect(iframe.attributes('height')).toBe(INLINE_EMBED_DIMENSIONS.HEIGHT); - }); - - it('should fallback to default size', () => { - mountComponent({}); - - const iframe = findIframe(); - - expect(iframe.attributes('width')).toBe(FULL_APP_DIMENSIONS.WIDTH); - expect(iframe.attributes('height')).toBe(FULL_APP_DIMENSIONS.HEIGHT); - }); - }); - - describe('skeleton variant', () => { - it('sets the specified skeleton variant', () => { - mountComponent({ skeletonVariant: SKELETON_VARIANT_EMBED }); - const props = wrapper.findComponent(ObservabilitySkeleton).props(); - - expect(props.variant).toBe(SKELETON_VARIANT_EMBED); - }); - - it('should have a default skeleton variant', () => { - mountComponent(); - const props = wrapper.findComponent(ObservabilitySkeleton).props(); - - expect(props.variant).toBe('dashboards'); - }); - }); - - describe('on GOUI_ROUTE_UPDATE', () => { - it('should emit a route-update event', () => { - mountComponent(); - - const payload = { url: '/explore' }; - dispatchMessageEvent({ - data: { type: MESSAGE_EVENT_TYPE.GOUI_ROUTE_UPDATE, payload }, - origin: 'https://observe.gitlab.com', - }); - - expect(wrapper.emitted('route-update')[0]).toEqual([payload]); - }); - }); - - describe('on GOUI_LOADED', () => { - beforeEach(() => { - mountComponent(); - }); - - it('should call onContentLoaded method', () => { - dispatchMessageEvent({ - data: { type: MESSAGE_EVENT_TYPE.GOUI_LOADED }, - origin: 'https://observe.gitlab.com', - }); - expect(mockSkeletonOnContentLoaded).toHaveBeenCalled(); - }); - - it('should not call onContentLoaded method if origin is different', () => { - dispatchMessageEvent({ - data: { type: MESSAGE_EVENT_TYPE.GOUI_LOADED }, - origin: 'https://example.com', - }); - expect(mockSkeletonOnContentLoaded).not.toHaveBeenCalled(); - }); - - it('should not call onContentLoaded method if event type is different', () => { - dispatchMessageEvent({ - data: { type: 'UNKNOWN_EVENT' }, - origin: 'https://observe.gitlab.com', - }); - expect(mockSkeletonOnContentLoaded).not.toHaveBeenCalled(); - }); - }); - - describe('on unmount', () => { - it('should not emit any even on route update', () => { - mountComponent(); - wrapper.destroy(); - - dispatchMessageEvent({ - data: { type: MESSAGE_EVENT_TYPE.GOUI_ROUTE_UPDATE, payload: { url: '/explore' } }, - origin: 'https://observe.gitlab.com', - }); - - expect(wrapper.emitted('route-update')).toBeUndefined(); - }); - }); -}); diff --git a/spec/frontend/observability/skeleton_spec.js b/spec/frontend/observability/skeleton_spec.js index 979070cfb12..5501fa117e0 100644 --- a/spec/frontend/observability/skeleton_spec.js +++ b/spec/frontend/observability/skeleton_spec.js @@ -3,32 +3,16 @@ import { GlSkeletonLoader, GlAlert, GlLoadingIcon } from '@gitlab/ui'; import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; import Skeleton from '~/observability/components/skeleton/index.vue'; -import DashboardsSkeleton from '~/observability/components/skeleton/dashboards.vue'; -import ExploreSkeleton from '~/observability/components/skeleton/explore.vue'; -import ManageSkeleton from '~/observability/components/skeleton/manage.vue'; -import EmbedSkeleton from '~/observability/components/skeleton/embed.vue'; -import { - SKELETON_VARIANTS_BY_ROUTE, - DEFAULT_TIMERS, - SKELETON_VARIANT_EMBED, -} from '~/observability/constants'; +import { DEFAULT_TIMERS } from '~/observability/constants'; describe('Skeleton component', () => { let wrapper; - const SKELETON_VARIANTS = [...Object.values(SKELETON_VARIANTS_BY_ROUTE), 'spinner']; + const findSpinner = () => wrapper.findComponent(GlLoadingIcon); const findContentWrapper = () => wrapper.findByTestId('content-wrapper'); - const findExploreSkeleton = () => wrapper.findComponent(ExploreSkeleton); - - const findDashboardsSkeleton = () => wrapper.findComponent(DashboardsSkeleton); - - const findManageSkeleton = () => wrapper.findComponent(ManageSkeleton); - - const findEmbedSkeleton = () => wrapper.findComponent(EmbedSkeleton); - const findAlert = () => wrapper.findComponent(GlAlert); const mountComponent = ({ ...props } = {}) => { @@ -39,39 +23,39 @@ describe('Skeleton component', () => { describe('on mount', () => { beforeEach(() => { - mountComponent({ variant: 'explore' }); + mountComponent({ variant: 'spinner' }); }); describe('showing content', () => { it('shows the skeleton if content is not loaded within CONTENT_WAIT_MS', async () => { - expect(findExploreSkeleton().exists()).toBe(false); - expect(findContentWrapper().isVisible()).toBe(false); + expect(findSpinner().exists()).toBe(false); + expect(findContentWrapper().exists()).toBe(false); jest.advanceTimersByTime(DEFAULT_TIMERS.CONTENT_WAIT_MS); await nextTick(); - expect(findExploreSkeleton().exists()).toBe(true); - expect(findContentWrapper().isVisible()).toBe(false); + expect(findSpinner().exists()).toBe(true); + expect(findContentWrapper().exists()).toBe(false); }); it('does not show the skeleton if content loads within CONTENT_WAIT_MS', async () => { - expect(findExploreSkeleton().exists()).toBe(false); - expect(findContentWrapper().isVisible()).toBe(false); + expect(findSpinner().exists()).toBe(false); + expect(findContentWrapper().exists()).toBe(false); wrapper.vm.onContentLoaded(); await nextTick(); - expect(findContentWrapper().isVisible()).toBe(true); - expect(findExploreSkeleton().exists()).toBe(false); + expect(findContentWrapper().exists()).toBe(true); + expect(findSpinner().exists()).toBe(false); jest.advanceTimersByTime(DEFAULT_TIMERS.CONTENT_WAIT_MS); await nextTick(); - expect(findContentWrapper().isVisible()).toBe(true); - expect(findExploreSkeleton().exists()).toBe(false); + expect(findContentWrapper().exists()).toBe(true); + expect(findSpinner().exists()).toBe(false); }); it('hides the skeleton after content loads', async () => { @@ -79,15 +63,15 @@ describe('Skeleton component', () => { await nextTick(); - expect(findExploreSkeleton().exists()).toBe(true); - expect(findContentWrapper().isVisible()).toBe(false); + expect(findSpinner().exists()).toBe(true); + expect(findContentWrapper().exists()).toBe(false); wrapper.vm.onContentLoaded(); await nextTick(); - expect(findContentWrapper().isVisible()).toBe(true); - expect(findExploreSkeleton().exists()).toBe(false); + expect(findContentWrapper().exists()).toBe(true); + expect(findSpinner().exists()).toBe(false); }); }); @@ -99,7 +83,7 @@ describe('Skeleton component', () => { await nextTick(); expect(findAlert().exists()).toBe(true); - expect(findContentWrapper().isVisible()).toBe(false); + expect(findContentWrapper().exists()).toBe(false); }); it('shows the error dialog if content fails to load', async () => { @@ -110,7 +94,7 @@ describe('Skeleton component', () => { await nextTick(); expect(findAlert().exists()).toBe(true); - expect(findContentWrapper().isVisible()).toBe(false); + expect(findContentWrapper().exists()).toBe(false); }); it('does not show the error dialog if content has loaded within TIMEOUT_MS', async () => { @@ -120,36 +104,28 @@ describe('Skeleton component', () => { await nextTick(); expect(findAlert().exists()).toBe(false); - expect(findContentWrapper().isVisible()).toBe(true); + expect(findContentWrapper().exists()).toBe(true); }); }); }); describe('skeleton variant', () => { - it.each` - skeletonType | condition | variant - ${'dashboards'} | ${'variant is dashboards'} | ${SKELETON_VARIANTS[0]} - ${'explore'} | ${'variant is explore'} | ${SKELETON_VARIANTS[1]} - ${'manage'} | ${'variant is manage'} | ${SKELETON_VARIANTS[2]} - ${'embed'} | ${'variant is embed'} | ${SKELETON_VARIANT_EMBED} - ${'spinner'} | ${'variant is spinner'} | ${'spinner'} - ${'default'} | ${'variant is not manage, dashboards or explore'} | ${'unknown'} - `('should render $skeletonType skeleton if $condition', async ({ skeletonType, variant }) => { - mountComponent({ variant }); + it('shows only the spinner variant when variant is spinner', async () => { + mountComponent({ variant: 'spinner' }); jest.advanceTimersByTime(DEFAULT_TIMERS.CONTENT_WAIT_MS); await nextTick(); - const showsDefaultSkeleton = ![...SKELETON_VARIANTS, SKELETON_VARIANT_EMBED].includes( - variant, - ); - expect(findDashboardsSkeleton().exists()).toBe(skeletonType === SKELETON_VARIANTS[0]); - expect(findExploreSkeleton().exists()).toBe(skeletonType === SKELETON_VARIANTS[1]); - expect(findManageSkeleton().exists()).toBe(skeletonType === SKELETON_VARIANTS[2]); - expect(findEmbedSkeleton().exists()).toBe(skeletonType === SKELETON_VARIANT_EMBED); + expect(findSpinner().exists()).toBe(true); + expect(wrapper.findComponent(GlSkeletonLoader).exists()).toBe(false); + }); - expect(wrapper.findComponent(GlSkeletonLoader).exists()).toBe(showsDefaultSkeleton); + it('shows only the default variant when variant is not spinner', async () => { + mountComponent({ variant: 'unknown' }); + jest.advanceTimersByTime(DEFAULT_TIMERS.CONTENT_WAIT_MS); + await nextTick(); - expect(wrapper.findComponent(GlLoadingIcon).exists()).toBe(variant === 'spinner'); + expect(findSpinner().exists()).toBe(false); + expect(wrapper.findComponent(GlSkeletonLoader).exists()).toBe(true); }); }); diff --git a/spec/helpers/groups/observability_helper_spec.rb b/spec/helpers/groups/observability_helper_spec.rb deleted file mode 100644 index f0e6aa0998a..00000000000 --- a/spec/helpers/groups/observability_helper_spec.rb +++ /dev/null @@ -1,76 +0,0 @@ -# frozen_string_literal: true - -require "spec_helper" - -RSpec.describe Groups::ObservabilityHelper do - let(:group) { build_stubbed(:group) } - - describe '#observability_iframe_src' do - before do - allow(Gitlab::Observability).to receive(:build_full_url).and_return('full-url') - end - - it 'returns the iframe src for action: dashboards' do - allow(helper).to receive(:params).and_return({ action: 'dashboards', observability_path: '/foo?bar=foobar' }) - expect(helper.observability_iframe_src(group)).to eq('full-url') - expect(Gitlab::Observability).to have_received(:build_full_url).with(group, '/foo?bar=foobar', '/') - end - - it 'returns the iframe src for action: manage' do - allow(helper).to receive(:params).and_return({ action: 'manage', observability_path: '/foo?bar=foobar' }) - expect(helper.observability_iframe_src(group)).to eq('full-url') - expect(Gitlab::Observability).to have_received(:build_full_url).with(group, '/foo?bar=foobar', '/dashboards') - end - - it 'returns the iframe src for action: explore' do - allow(helper).to receive(:params).and_return({ action: 'explore', observability_path: '/foo?bar=foobar' }) - expect(helper.observability_iframe_src(group)).to eq('full-url') - expect(Gitlab::Observability).to have_received(:build_full_url).with(group, '/foo?bar=foobar', '/explore') - end - - it 'returns the iframe src for action: datasources' do - allow(helper).to receive(:params).and_return({ action: 'datasources', observability_path: '/foo?bar=foobar' }) - expect(helper.observability_iframe_src(group)).to eq('full-url') - expect(Gitlab::Observability).to have_received(:build_full_url).with(group, '/foo?bar=foobar', '/datasources') - end - - it 'returns the iframe src when action is not recognised' do - allow(helper).to receive(:params).and_return({ action: 'unrecognised', observability_path: '/foo?bar=foobar' }) - expect(helper.observability_iframe_src(group)).to eq('full-url') - expect(Gitlab::Observability).to have_received(:build_full_url).with(group, '/foo?bar=foobar', '/') - end - - it 'returns the iframe src when observability_path is missing' do - allow(helper).to receive(:params).and_return({ action: 'dashboards' }) - expect(helper.observability_iframe_src(group)).to eq('full-url') - expect(Gitlab::Observability).to have_received(:build_full_url).with(group, nil, '/') - end - end - - describe '#observability_page_title' do - it 'returns the title for action: dashboards' do - allow(helper).to receive(:params).and_return({ action: 'dashboards' }) - expect(helper.observability_page_title).to eq("Dashboards") - end - - it 'returns the title for action: manage' do - allow(helper).to receive(:params).and_return({ action: 'manage' }) - expect(helper.observability_page_title).to eq("Manage dashboards") - end - - it 'returns the title for action: explore' do - allow(helper).to receive(:params).and_return({ action: 'explore' }) - expect(helper.observability_page_title).to eq("Explore telemetry data") - end - - it 'returns the title for action: datasources' do - allow(helper).to receive(:params).and_return({ action: 'datasources' }) - expect(helper.observability_page_title).to eq("Data sources") - end - - it 'returns the default title for unknown action' do - allow(helper).to receive(:params).and_return({ action: 'unknown' }) - expect(helper.observability_page_title).to eq("Dashboards") - end - end -end diff --git a/spec/helpers/ide_helper_spec.rb b/spec/helpers/ide_helper_spec.rb index 7f657caa986..47500b8e21e 100644 --- a/spec/helpers/ide_helper_spec.rb +++ b/spec/helpers/ide_helper_spec.rb @@ -103,10 +103,7 @@ RSpec.describe IdeHelper, feature_category: :web_ide do 'new-web-ide-help-page-path' => help_page_path('user/project/web_ide/index.md', anchor: 'vscode-reimplementation'), 'csp-nonce' => 'test-csp-nonce', - 'ide-remote-path' => ide_remote_path(remote_host: ':remote_host', remote_path: ':remote_path'), - 'editor-font-family' => 'GitLab Mono', - 'editor-font-format' => 'woff2', - 'editor-font-src-url' => a_string_matching(%r{gitlab-mono/GitLabMono}) + 'ide-remote-path' => ide_remote_path(remote_host: ':remote_host', remote_path: ':remote_path') } end @@ -119,6 +116,34 @@ RSpec.describe IdeHelper, feature_category: :web_ide do .to include(base_data) end + it 'includes editor font configuration' do + ide_data = helper.ide_data(project: nil, fork_info: fork_info, params: params) + editor_font = ::Gitlab::Json.parse(ide_data.fetch('editor-font'), symbolize_names: true) + + expect(editor_font).to include({ + fallback_font_family: 'monospace', + font_faces: [ + { + family: 'GitLab Mono', + display: 'block', + src: [{ + url: a_string_matching(%r{gitlab-mono/GitLabMono-[^I]}), + format: 'woff2' + }] + }, + { + family: 'GitLab Mono', + display: 'block', + style: 'italic', + src: [{ + url: a_string_matching(%r{gitlab-mono/GitLabMono-Italic}), + format: 'woff2' + }] + } + ] + }) + end + it 'does not use new web ide if feature flag is disabled' do stub_feature_flags(vscode_web_ide: false) diff --git a/spec/lib/banzai/filter/inline_observability_filter_spec.rb b/spec/lib/banzai/filter/inline_observability_filter_spec.rb deleted file mode 100644 index 81896faced8..00000000000 --- a/spec/lib/banzai/filter/inline_observability_filter_spec.rb +++ /dev/null @@ -1,101 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe Banzai::Filter::InlineObservabilityFilter, feature_category: :metrics do - include FilterSpecHelper - - let(:input) { %(example) } - let(:doc) { filter(input) } - - before do - allow(Gitlab::Observability).to receive(:embeddable_url).and_return('embeddable-url') - stub_config_setting(url: "https://www.gitlab.com") - end - - describe '#filter?' do - context 'when the document contains a valid observability link' do - let(:url) { "https://www.gitlab.com/groups/some-group/-/observability/test" } - - it 'leaves the original link unchanged' do - expect(doc.at_css('a').to_s).to eq(input) - end - - it 'appends an observability charts placeholder' do - node = doc.at_css('.js-render-observability') - - expect(node).to be_present - expect(node.attribute('data-frame-url').to_s).to eq('embeddable-url') - expect(Gitlab::Observability).to have_received(:embeddable_url).with(url).once - end - end - - context 'with duplicate URLs' do - let(:url) { "https://www.gitlab.com/groups/some-group/-/observability/test" } - let(:input) { %(example1example2) } - - where(:embeddable_url) do - [ - 'not-nil', - nil - ] - end - - with_them do - it 'calls Gitlab::Observability.embeddable_url only once' do - allow(Gitlab::Observability).to receive(:embeddable_url).with(url).and_return(embeddable_url) - - filter(input) - - expect(Gitlab::Observability).to have_received(:embeddable_url).with(url).once - end - end - end - - shared_examples 'does not embed observabilty' do - it 'leaves the original link unchanged' do - expect(doc.at_css('a').to_s).to eq(input) - end - - it 'does not append an observability charts placeholder' do - node = doc.at_css('.js-render-observability') - - expect(node).not_to be_present - end - end - - context 'when the embeddable url is nil' do - let(:url) { "https://www.gitlab.com/groups/some-group/-/something-else/test" } - - before do - allow(Gitlab::Observability).to receive(:embeddable_url).and_return(nil) - end - - it_behaves_like 'does not embed observabilty' - end - - context 'when the document has an unrecognised link' do - let(:url) { "https://www.gitlab.com/groups/some-group/-/something-else/test" } - - it_behaves_like 'does not embed observabilty' - - it 'does not build the embeddable url' do - expect(Gitlab::Observability).not_to have_received(:embeddable_url) - end - end - - context 'when feature flag is disabled' do - let(:url) { "https://www.gitlab.com/groups/some-group/-/observability/test" } - - before do - stub_feature_flags(observability_group_tab: false) - end - - it_behaves_like 'does not embed observabilty' - - it 'does not build the embeddable url' do - expect(Gitlab::Observability).not_to have_received(:embeddable_url) - end - end - end -end diff --git a/spec/lib/gitlab/observability_spec.rb b/spec/lib/gitlab/observability_spec.rb index 04c35f0ee3a..a6d0eafdd55 100644 --- a/spec/lib/gitlab/observability_spec.rb +++ b/spec/lib/gitlab/observability_spec.rb @@ -45,207 +45,4 @@ RSpec.describe Gitlab::Observability, feature_category: :error_tracking do it { is_expected.to eq("#{described_class.observability_url}/v3/tenant/#{project.id}") } end - - describe '.build_full_url' do - let_it_be(:group) { build_stubbed(:group, id: 123) } - let(:observability_url) { described_class.observability_url } - - it 'returns the full observability url for the given params' do - url = described_class.build_full_url(group, '/foo?bar=baz', '/') - expect(url).to eq("https://observe.gitlab.com/-/123/foo?bar=baz") - end - - it 'handles missing / from observability_path' do - url = described_class.build_full_url(group, 'foo?bar=baz', '/') - expect(url).to eq("https://observe.gitlab.com/-/123/foo?bar=baz") - end - - it 'sanitises observability_path' do - url = described_class.build_full_url(group, "/test?groupId=", '/') - expect(url).to eq("https://observe.gitlab.com/-/123/test?groupId=alert('attack!')") - end - - context 'when observability_path is missing' do - it 'builds the url with the fallback_path' do - url = described_class.build_full_url(group, nil, '/fallback') - expect(url).to eq("https://observe.gitlab.com/-/123/fallback") - end - - it 'defaults to / if fallback_path is also missing' do - url = described_class.build_full_url(group, nil, nil) - expect(url).to eq("https://observe.gitlab.com/-/123/") - end - end - end - - describe '.embeddable_url' do - before do - stub_config_setting(url: "https://www.gitlab.com") - # Can't use build/build_stubbed as we want the routes to be generated as well - create(:group, path: 'test-path', id: 123) - end - - context 'when URL is valid' do - where(:input, :expected) do - [ - [ - "https://www.gitlab.com/groups/test-path/-/observability/explore?observability_path=%2Fexplore%3FgroupId%3D14485840%26left%3D%255B%2522now-1h%2522,%2522now%2522,%2522new-sentry.gitlab.net%2522,%257B%257D%255D", - "https://observe.gitlab.com/-/123/explore?groupId=14485840&left=%5B%22now-1h%22,%22now%22,%22new-sentry.gitlab.net%22,%7B%7D%5D" - ], - [ - "https://www.gitlab.com/groups/test-path/-/observability/explore?observability_path=/goto/foo", - "https://observe.gitlab.com/-/123/goto/foo" - ] - ] - end - - with_them do - it 'returns an embeddable observability url' do - expect(described_class.embeddable_url(input)).to eq(expected) - end - end - end - - context 'when URL is invalid' do - where(:input) do - [ - # direct links to observe.gitlab.com - "https://observe.gitlab.com/-/123/explore", - 'https://observe.gitlab.com/v1/auth/start', - - # invalid GitLab URL - "not a link", - "https://foo.bar/groups/test-path/-/observability/explore?observability_path=/explore", - "http://www.gitlab.com/groups/test-path/-/observability/explore?observability_path=/explore", - "https://www.gitlab.com:123/groups/test-path/-/observability/explore?observability_path=/explore", - "https://www.gitlab.com@example.com/groups/test-path/-/observability/explore?observability_path=/explore", - "https://www.gitlab.com/groups/test-path/-/observability/explore?observability_path=@example.com", - - # invalid group/controller/actions - "https://www.gitlab.com/groups/INVALID_GROUP/-/observability/explore?observability_path=/explore", - "https://www.gitlab.com/groups/test-path/-/INVALID_CONTROLLER/explore?observability_path=/explore", - "https://www.gitlab.com/groups/test-path/-/observability/INVALID_ACTION?observability_path=/explore", - - # invalid observablity path - "https://www.gitlab.com/groups/test-path/-/observability/explore", - "https://www.gitlab.com/groups/test-path/-/observability/explore?missing_observability_path=/explore", - "https://www.gitlab.com/groups/test-path/-/observability/explore?observability_path=/not_embeddable", - "https://www.gitlab.com/groups/test-path/-/observability/explore?observability_path=/datasources", - "https://www.gitlab.com/groups/test-path/-/observability/explore?observability_path=not a valid path" - ] - end - - with_them do - it 'returns nil' do - expect(described_class.embeddable_url(input)).to be_nil - end - end - - it 'returns nil if the path detection throws an error' do - test_url = "https://www.gitlab.com/groups/test-path/-/observability/explore" - allow(Rails.application.routes).to receive(:recognize_path).with(test_url) { - raise ActionController::RoutingError, 'test' - } - expect(described_class.embeddable_url(test_url)).to be_nil - end - - it 'returns nil if parsing observaboility path throws an error' do - observability_path = 'some-path' - test_url = "https://www.gitlab.com/groups/test-path/-/observability/explore?observability_path=#{observability_path}" - - allow(URI).to receive(:parse).and_call_original - allow(URI).to receive(:parse).with(observability_path) { - raise URI::InvalidURIError, 'test' - } - - expect(described_class.embeddable_url(test_url)).to be_nil - end - end - end - - describe '.allowed_for_action?' do - let(:group) { build_stubbed(:group) } - let(:user) { build_stubbed(:user) } - - before do - allow(described_class).to receive(:allowed?).and_call_original - end - - it 'returns false if action is nil' do - expect(described_class.allowed_for_action?(user, group, nil)).to eq(false) - end - - describe 'allowed? calls' do - using RSpec::Parameterized::TableSyntax - - where(:action, :permission) do - :foo | :admin_observability - :explore | :read_observability - :datasources | :admin_observability - :manage | :admin_observability - :dashboards | :read_observability - end - - with_them do - it "calls allowed? with #{params[:permission]} when actions is #{params[:action]}" do - described_class.allowed_for_action?(user, group, action) - expect(described_class).to have_received(:allowed?).with(user, group, permission) - end - end - end - end - - describe '.allowed?' do - let(:user) { build_stubbed(:user) } - let(:group) { build_stubbed(:group) } - let(:test_permission) { :read_observability } - - before do - allow(Ability).to receive(:allowed?).and_return(false) - end - - subject do - described_class.allowed?(user, group, test_permission) - end - - it 'checks if ability is allowed for the given user and group' do - allow(Ability).to receive(:allowed?).and_return(true) - - subject - - expect(Ability).to have_received(:allowed?).with(user, test_permission, group) - end - - it 'checks for admin_observability if permission is missing' do - described_class.allowed?(user, group) - - expect(Ability).to have_received(:allowed?).with(user, :admin_observability, group) - end - - it 'returns true if the ability is allowed' do - allow(Ability).to receive(:allowed?).and_return(true) - - expect(subject).to eq(true) - end - - it 'returns false if the ability is not allowed' do - allow(Ability).to receive(:allowed?).and_return(false) - - expect(subject).to eq(false) - end - - it 'returns false if observability url is missing' do - allow(described_class).to receive(:observability_url).and_return("") - - expect(subject).to eq(false) - end - - it 'returns false if group is missing' do - expect(described_class.allowed?(user, nil, :read_observability)).to eq(false) - end - - it 'returns false if user is missing' do - expect(described_class.allowed?(nil, group, :read_observability)).to eq(false) - end - end end diff --git a/spec/lib/sidebars/groups/menus/observability_menu_spec.rb b/spec/lib/sidebars/groups/menus/observability_menu_spec.rb deleted file mode 100644 index 573760cddb6..00000000000 --- a/spec/lib/sidebars/groups/menus/observability_menu_spec.rb +++ /dev/null @@ -1,93 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe Sidebars::Groups::Menus::ObservabilityMenu, feature_category: :navigation do - let(:owner) { build_stubbed(:user) } - let(:root_group) do - build(:group, :private).tap do |g| - g.add_owner(owner) - end - end - - let(:group) { root_group } - let(:user) { owner } - let(:context) { Sidebars::Groups::Context.new(current_user: user, container: group) } - let(:menu) { described_class.new(context) } - - describe '#render?' do - before do - allow(menu).to receive(:can?).and_call_original - end - - context 'when observability#explore is allowed' do - before do - allow(Gitlab::Observability).to receive(:allowed_for_action?).with(user, group, :explore).and_return(true) - end - - it 'returns true' do - expect(menu.render?).to eq true - expect(Gitlab::Observability).to have_received(:allowed_for_action?).with(user, group, :explore) - end - end - - context 'when observability#explore is not allowed' do - before do - allow(Gitlab::Observability).to receive(:allowed_for_action?).with(user, group, :explore).and_return(false) - end - - it 'returns false' do - expect(menu.render?).to eq false - expect(Gitlab::Observability).to have_received(:allowed_for_action?).with(user, group, :explore) - end - end - end - - describe "Menu items" do - before do - allow(Gitlab::Observability).to receive(:allowed_for_action?).and_return(false) - end - - subject { find_menu(menu, item_id) } - - shared_examples 'observability menu entry' do - context 'when action is allowed' do - before do - allow(Gitlab::Observability).to receive(:allowed_for_action?).with(user, group, item_id).and_return(true) - end - - it 'the menu item is added to list of menu items' do - is_expected.not_to be_nil - end - end - - context 'when action is not allowed' do - before do - allow(Gitlab::Observability).to receive(:allowed_for_action?).with(user, group, item_id).and_return(false) - end - - it 'the menu item is added to list of menu items' do - is_expected.to be_nil - end - end - end - - describe 'Explore' do - it_behaves_like 'observability menu entry' do - let(:item_id) { :explore } - end - end - - describe 'Datasources' do - it_behaves_like 'observability menu entry' do - let(:item_id) { :datasources } - end - end - end - - private - - def find_menu(menu, item) - menu.renderable_items.find { |i| i.item_id == item } - end -end diff --git a/spec/lib/sidebars/groups/super_sidebar_menus/monitor_menu_spec.rb b/spec/lib/sidebars/groups/super_sidebar_menus/monitor_menu_spec.rb deleted file mode 100644 index 759975856b8..00000000000 --- a/spec/lib/sidebars/groups/super_sidebar_menus/monitor_menu_spec.rb +++ /dev/null @@ -1,22 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe Sidebars::Groups::SuperSidebarMenus::MonitorMenu, feature_category: :navigation do - subject { described_class.new({}) } - - let(:items) { subject.instance_variable_get(:@items) } - - it 'has title and sprite_icon' do - expect(subject.title).to eq(s_("Navigation|Monitor")) - expect(subject.sprite_icon).to eq("monitor") - end - - it 'defines list of NilMenuItem placeholders' do - expect(items.map(&:class).uniq).to eq([Sidebars::NilMenuItem]) - expect(items.map(&:item_id)).to eq([ - :explore, - :datasources - ]) - end -end diff --git a/spec/lib/sidebars/groups/super_sidebar_panel_spec.rb b/spec/lib/sidebars/groups/super_sidebar_panel_spec.rb index c939dd870c4..fe1491a736e 100644 --- a/spec/lib/sidebars/groups/super_sidebar_panel_spec.rb +++ b/spec/lib/sidebars/groups/super_sidebar_panel_spec.rb @@ -34,7 +34,6 @@ RSpec.describe Sidebars::Groups::SuperSidebarPanel, feature_category: :navigatio Sidebars::Groups::SuperSidebarMenus::SecureMenu, Sidebars::Groups::SuperSidebarMenus::DeployMenu, Sidebars::Groups::SuperSidebarMenus::OperationsMenu, - Sidebars::Groups::SuperSidebarMenus::MonitorMenu, Sidebars::Groups::SuperSidebarMenus::AnalyzeMenu, Sidebars::UncategorizedMenu, Sidebars::Groups::Menus::SettingsMenu diff --git a/spec/models/issue_spec.rb b/spec/models/issue_spec.rb index 1b10a3048c7..e7a5a53c6a0 100644 --- a/spec/models/issue_spec.rb +++ b/spec/models/issue_spec.rb @@ -2077,4 +2077,117 @@ RSpec.describe Issue, feature_category: :team_planning do expect(issue4.linked_items_count).to eq(0) end end + + describe '#readable_by?' do + let_it_be(:admin_user) { create(:user, :admin) } + + subject { issue_subject.readable_by?(user) } + + context 'when issue belongs directly to a project' do + let_it_be_with_reload(:project_issue) { create(:issue, project: reusable_project) } + let_it_be(:project_reporter) { create(:user).tap { |u| reusable_project.add_reporter(u) } } + let_it_be(:project_guest) { create(:user).tap { |u| reusable_project.add_guest(u) } } + + let(:issue_subject) { project_issue } + + context 'when user is in admin mode', :enable_admin_mode do + let(:user) { admin_user } + + it { is_expected.to be_truthy } + end + + context 'when user is a reporter' do + let(:user) { project_reporter } + + it { is_expected.to be_truthy } + + context 'when issues project feature is not enabled' do + before do + reusable_project.project_feature.update!(issues_access_level: ProjectFeature::DISABLED) + end + + it { is_expected.to be_falsey } + end + + context 'when issue is hidden (banned author)' do + before do + issue_subject.author.ban! + end + + it { is_expected.to be_falsey } + end + end + + context 'when user is a guest' do + let(:user) { project_guest } + + context 'when issue is confidential' do + before do + issue_subject.update!(confidential: true) + end + + it { is_expected.to be_falsey } + + context 'when user is assignee of the issue' do + before do + issue_subject.update!(assignees: [user]) + end + + it { is_expected.to be_truthy } + end + end + end + end + + context 'when issue belongs directly to the group' do + let_it_be(:group) { create(:group) } + let_it_be_with_reload(:group_issue) { create(:issue, :group_level, namespace: group) } + let_it_be(:group_reporter) { create(:user).tap { |u| group.add_reporter(u) } } + let_it_be(:group_guest) { create(:user).tap { |u| group.add_guest(u) } } + + let(:issue_subject) { group_issue } + + context 'when user is in admin mode', :enable_admin_mode do + let(:user) { admin_user } + + it { is_expected.to be_truthy } + end + + context 'when user is a reporter' do + let(:user) { group_reporter } + + it { is_expected.to be_truthy } + + context 'when issue is hidden (banned author)' do + before do + issue_subject.author.ban! + end + + it { is_expected.to be_falsey } + end + end + + context 'when user is a guest' do + let(:user) { group_guest } + + it { is_expected.to be_truthy } + + context 'when issue is confidential' do + before do + issue_subject.update!(confidential: true) + end + + it { is_expected.to be_falsey } + + context 'when user is assignee of the issue' do + before do + issue_subject.update!(assignees: [user]) + end + + it { is_expected.to be_truthy } + end + end + end + end + end end diff --git a/spec/models/pages/lookup_path_spec.rb b/spec/models/pages/lookup_path_spec.rb index fdab668c679..08ba823f8fa 100644 --- a/spec/models/pages/lookup_path_spec.rb +++ b/spec/models/pages/lookup_path_spec.rb @@ -99,25 +99,6 @@ RSpec.describe Pages::LookupPath, feature_category: :pages do end end end - - context 'when deployment were created during migration' do - before do - allow(deployment).to receive(:migrated?).and_return(true) - end - - it 'uses deployment from object storage' do - freeze_time do - expect(source).to eq( - type: 'zip', - path: deployment.file.url(expire_at: 1.day.from_now), - global_id: "gid://gitlab/PagesDeployment/#{deployment.id}", - sha256: deployment.file_sha256, - file_size: deployment.size, - file_count: deployment.file_count - ) - end - end - end end end diff --git a/spec/models/pages_deployment_spec.rb b/spec/models/pages_deployment_spec.rb index 916197fe5e9..e74c7ee8612 100644 --- a/spec/models/pages_deployment_spec.rb +++ b/spec/models/pages_deployment_spec.rb @@ -28,16 +28,6 @@ RSpec.describe PagesDeployment, feature_category: :pages do end end - describe '.migrated_from_legacy_storage' do - it 'only returns migrated deployments' do - migrated_deployment = create_migrated_deployment(project) - # create one other deployment - create(:pages_deployment, project: project) - - expect(described_class.migrated_from_legacy_storage).to eq([migrated_deployment]) - end - end - context 'with deployments stored locally and remotely' do before do stub_pages_object_storage(::Pages::DeploymentUploader) @@ -132,34 +122,6 @@ RSpec.describe PagesDeployment, feature_category: :pages do end end - describe '#migrated?' do - it 'returns false for normal deployment' do - deployment = create(:pages_deployment) - - expect(deployment.migrated?).to eq(false) - end - - it 'returns true for migrated deployment' do - deployment = create_migrated_deployment(project) - - expect(deployment.migrated?).to eq(true) - end - end - - def create_migrated_deployment(project) - public_path = File.join(project.pages_path, "public") - FileUtils.mkdir_p(public_path) - File.open(File.join(project.pages_path, "public/index.html"), "w") do |f| - f.write("Hello!") - end - - expect(::Pages::MigrateLegacyStorageToDeploymentService.new(project).execute[:status]).to eq(:success) - - project.reload.pages_metadatum.pages_deployment - ensure - FileUtils.rm_rf(public_path) - end - describe 'default for file_store' do let(:deployment) do filepath = Rails.root.join("spec/fixtures/pages.zip") diff --git a/spec/models/project_pages_metadatum_spec.rb b/spec/models/project_pages_metadatum_spec.rb deleted file mode 100644 index 31a533e0363..00000000000 --- a/spec/models/project_pages_metadatum_spec.rb +++ /dev/null @@ -1,21 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe ProjectPagesMetadatum do - describe '.only_on_legacy_storage' do - it 'returns only deployed records without deployment' do - create(:project) # without pages deployed - - legacy_storage_project = create(:project) - legacy_storage_project.mark_pages_as_deployed - - project_with_deployment = create(:project) - deployment = create(:pages_deployment, project: project_with_deployment) - project_with_deployment.mark_pages_as_deployed - project_with_deployment.update_pages_deployment!(deployment) - - expect(described_class.only_on_legacy_storage).to eq([legacy_storage_project.pages_metadatum]) - end - end -end diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index cc49661b118..23306e46237 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -5297,12 +5297,6 @@ RSpec.describe Project, factory_default: :keep, feature_category: :groups_and_pr end end - describe '#pages_path' do - it 'returns a path where pages are stored' do - expect(project.pages_path).to eq(File.join(Settings.pages.path, project.namespace.full_path, project.path)) - end - end - describe '#migrate_to_hashed_storage!' do let(:project) { create(:project, :empty_repo, :legacy_storage) } @@ -5392,12 +5386,6 @@ RSpec.describe Project, factory_default: :keep, feature_category: :groups_and_pr end end - describe '#pages_path' do - it 'returns a path where pages are stored' do - expect(project.pages_path).to eq(File.join(Settings.pages.path, project.namespace.full_path, project.path)) - end - end - describe '#migrate_to_hashed_storage!' do let(:project) { create(:project, :repository, skip_disk_validation: true) } diff --git a/spec/policies/group_policy_spec.rb b/spec/policies/group_policy_spec.rb index 4aa18c72c45..cb7884b141e 100644 --- a/spec/policies/group_policy_spec.rb +++ b/spec/policies/group_policy_spec.rb @@ -1101,73 +1101,6 @@ RSpec.describe GroupPolicy, feature_category: :system_access do end end - describe 'observability' do - let(:allowed_admin) { be_allowed(:read_observability) && be_allowed(:admin_observability) } - let(:allowed_read) { be_allowed(:read_observability) && be_disallowed(:admin_observability) } - let(:disallowed) { be_disallowed(:read_observability) && be_disallowed(:admin_observability) } - - # rubocop:disable Layout/LineLength - where(:feature_enabled, :admin_matcher, :owner_matcher, :maintainer_matcher, :developer_matcher, :reporter_matcher, :guest_matcher, :non_member_matcher, :anonymous_matcher) do - false | ref(:disallowed) | ref(:disallowed) | ref(:disallowed) | ref(:disallowed) | ref(:disallowed) | ref(:disallowed) | ref(:disallowed) | ref(:disallowed) - true | ref(:allowed_admin) | ref(:allowed_admin) | ref(:allowed_admin) | ref(:allowed_read) | ref(:disallowed) | ref(:disallowed) | ref(:disallowed) | ref(:disallowed) - end - # rubocop:enable Layout/LineLength - - with_them do - before do - stub_feature_flags(observability_group_tab: feature_enabled) - end - - context 'admin', :enable_admin_mode do - let(:current_user) { admin } - - it { is_expected.to admin_matcher } - end - - context 'owner' do - let(:current_user) { owner } - - it { is_expected.to owner_matcher } - end - - context 'maintainer' do - let(:current_user) { maintainer } - - it { is_expected.to maintainer_matcher } - end - - context 'developer' do - let(:current_user) { developer } - - it { is_expected.to developer_matcher } - end - - context 'reporter' do - let(:current_user) { reporter } - - it { is_expected.to reporter_matcher } - end - - context 'with guest' do - let(:current_user) { guest } - - it { is_expected.to guest_matcher } - end - - context 'with non member' do - let(:current_user) { create(:user) } - - it { is_expected.to non_member_matcher } - end - - context 'with anonymous' do - let(:current_user) { nil } - - it { is_expected.to anonymous_matcher } - end - end - end - describe 'dependency proxy' do RSpec.shared_examples 'disabling admin_package feature flag' do before do diff --git a/spec/requests/groups/observability_controller_spec.rb b/spec/requests/groups/observability_controller_spec.rb deleted file mode 100644 index 247535bc990..00000000000 --- a/spec/requests/groups/observability_controller_spec.rb +++ /dev/null @@ -1,99 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe Groups::ObservabilityController, feature_category: :tracing do - let_it_be(:group) { create(:group) } - let_it_be(:user) { create(:user) } - - let(:observability_url) { Gitlab::Observability.observability_url } - let(:path) { nil } - let(:expected_observability_path) { nil } - - shared_examples 'observability route request' do - subject do - get path - response - end - - it_behaves_like 'observability csp policy' do - before_all do - group.add_developer(user) - end - - let(:tested_path) { path } - end - - context 'when user is not authenticated' do - it 'returns 404' do - expect(subject).to have_gitlab_http_status(:not_found) - end - end - - context 'when user is a guest' do - before do - sign_in(user) - end - - it 'returns 404' do - expect(subject).to have_gitlab_http_status(:not_found) - end - end - - context 'when user has the correct permissions' do - before do - sign_in(user) - set_permissions - end - - context 'when observability url is missing' do - before do - allow(Gitlab::Observability).to receive(:observability_url).and_return("") - end - - it 'returns 404' do - expect(subject).to have_gitlab_http_status(:not_found) - end - end - - it 'returns 200' do - expect(subject).to have_gitlab_http_status(:ok) - end - - it 'renders the proper layout' do - expect(subject).to render_template("layouts/group") - expect(subject).to render_template("layouts/fullscreen") - expect(subject).not_to render_template('layouts/nav/breadcrumbs') - expect(subject).to render_template("nav/sidebar/_group") - expect(subject).to render_template("groups/observability/observability") - end - - it 'renders the js-observability-app element correctly' do - element = Nokogiri::HTML.parse(subject.body).at_css('#js-observability-app') - expect(element.attributes['data-observability-iframe-src'].value).to eq(expected_observability_path) - end - end - end - - describe 'GET #explore' do - let(:path) { group_observability_explore_path(group) } - let(:expected_observability_path) { "#{observability_url}/-/#{group.id}/explore" } - - it_behaves_like 'observability route request' do - let(:set_permissions) do - group.add_developer(user) - end - end - end - - describe 'GET #datasources' do - let(:path) { group_observability_datasources_path(group) } - let(:expected_observability_path) { "#{observability_url}/-/#{group.id}/datasources" } - - it_behaves_like 'observability route request' do - let(:set_permissions) do - group.add_maintainer(user) - end - end - end -end diff --git a/spec/requests/projects/issues_controller_spec.rb b/spec/requests/projects/issues_controller_spec.rb index 1ae65939c86..6c9b1966149 100644 --- a/spec/requests/projects/issues_controller_spec.rb +++ b/spec/requests/projects/issues_controller_spec.rb @@ -14,35 +14,12 @@ RSpec.describe Projects::IssuesController, feature_category: :team_planning do let_it_be(:user) { create(:user) } end - describe 'GET #new' do - include_context 'group project issue' - - before do - group.add_developer(user) - login_as(user) - end - - it_behaves_like "observability csp policy", described_class do - let(:tested_path) do - new_project_issue_path(project) - end - end - end - describe 'GET #show' do before do group.add_developer(user) login_as(user) end - it_behaves_like "observability csp policy", described_class do - include_context 'group project issue' - - let(:tested_path) do - project_issue_path(project, issue) - end - end - describe 'incident tabs' do let_it_be(:incident) { create(:incident, project: project) } diff --git a/spec/requests/projects/merge_requests/creations_spec.rb b/spec/requests/projects/merge_requests/creations_spec.rb index 8f55aa90bee..41246d419a1 100644 --- a/spec/requests/projects/merge_requests/creations_spec.rb +++ b/spec/requests/projects/merge_requests/creations_spec.rb @@ -76,17 +76,5 @@ RSpec.describe 'merge requests creations', feature_category: :code_review_workfl end end end - - it_behaves_like "observability csp policy", Projects::MergeRequests::CreationsController do - let(:tested_path) do - project_new_merge_request_path(project, merge_request: { - title: 'Some feature', - source_branch: 'fix', - target_branch: 'feature', - target_project: project, - source_project: project - }) - end - end end end diff --git a/spec/requests/projects/merge_requests_controller_spec.rb b/spec/requests/projects/merge_requests_controller_spec.rb index e6a281d8d59..e549913ccc3 100644 --- a/spec/requests/projects/merge_requests_controller_spec.rb +++ b/spec/requests/projects/merge_requests_controller_spec.rb @@ -14,19 +14,6 @@ RSpec.describe Projects::MergeRequestsController, feature_category: :source_code let(:merge_request) { create :merge_request, source_project: project, author: user } - context 'when logged in' do - before do - group.add_developer(user) - login_as(user) - end - - it_behaves_like "observability csp policy", described_class do - let(:tested_path) do - project_merge_request_path(project, merge_request) - end - end - end - context 'when the author of the merge request is banned', feature_category: :insider_threat do let_it_be(:user) { create(:user, :banned) } diff --git a/spec/routing/group_routing_spec.rb b/spec/routing/group_routing_spec.rb index 3ba7d5ad871..6fab711d05a 100644 --- a/spec/routing/group_routing_spec.rb +++ b/spec/routing/group_routing_spec.rb @@ -72,14 +72,6 @@ RSpec.shared_examples 'groups routing' do expect(get("groups/#{group_path}/-/harbor/repositories/test/artifacts/test/tags")).to route_to('groups/harbor/tags#index', group_id: group_path, repository_id: 'test', artifact_id: 'test') end - it 'routes to the observability controller explore method' do - expect(get("groups/#{group_path}/-/observability/explore")).to route_to('groups/observability#explore', group_id: group_path) - end - - it 'routes to the observability controller datasources method' do - expect(get("groups/#{group_path}/-/observability/datasources")).to route_to('groups/observability#datasources', group_id: group_path) - end - it 'routes to the usage quotas controller' do expect(get("groups/#{group_path}/-/usage_quotas")).to route_to("groups/usage_quotas#index", group_id: group_path) end diff --git a/spec/services/pages/migrate_from_legacy_storage_service_spec.rb b/spec/services/pages/migrate_from_legacy_storage_service_spec.rb deleted file mode 100644 index 48690a035f5..00000000000 --- a/spec/services/pages/migrate_from_legacy_storage_service_spec.rb +++ /dev/null @@ -1,137 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe Pages::MigrateFromLegacyStorageService, feature_category: :pages do - let(:batch_size) { 10 } - let(:mark_projects_as_not_deployed) { false } - let(:service) { described_class.new(Rails.logger, ignore_invalid_entries: false, mark_projects_as_not_deployed: mark_projects_as_not_deployed) } - - shared_examples "migrates projects properly" do - it 'does not try to migrate pages if pages are not deployed' do - expect(::Pages::MigrateLegacyStorageToDeploymentService).not_to receive(:new) - - is_expected.to eq(migrated: 0, errored: 0) - end - - context 'when pages are marked as deployed' do - let(:project) { create(:project) } - - before do - project.mark_pages_as_deployed - end - - context 'when pages directory does not exist' do - context 'when mark_projects_as_not_deployed is set' do - let(:mark_projects_as_not_deployed) { true } - - it 'counts project as migrated' do - expect_next_instance_of(::Pages::MigrateLegacyStorageToDeploymentService, project, ignore_invalid_entries: false, mark_projects_as_not_deployed: true) do |service| - expect(service).to receive(:execute).and_call_original - end - - is_expected.to eq(migrated: 1, errored: 0) - end - end - - it 'counts project as errored' do - expect_next_instance_of(::Pages::MigrateLegacyStorageToDeploymentService, project, ignore_invalid_entries: false, mark_projects_as_not_deployed: false) do |service| - expect(service).to receive(:execute).and_call_original - end - - is_expected.to eq(migrated: 0, errored: 1) - end - end - - context 'when pages directory exists on disk' do - before do - FileUtils.mkdir_p File.join(project.pages_path, "public") - File.open(File.join(project.pages_path, "public/index.html"), "w") do |f| - f.write("Hello!") - end - end - - it 'migrates pages projects without deployments' do - expect_next_instance_of(::Pages::MigrateLegacyStorageToDeploymentService, project, ignore_invalid_entries: false, mark_projects_as_not_deployed: false) do |service| - expect(service).to receive(:execute).and_call_original - end - - expect(project.pages_metadatum.reload.pages_deployment).to eq(nil) - expect(subject).to eq(migrated: 1, errored: 0) - expect(project.pages_metadatum.reload.pages_deployment).to be_present - end - - context 'when deployed already exists for the project' do - before do - deployment = create(:pages_deployment, project: project) - project.set_first_pages_deployment!(deployment) - end - - it 'does not try to migrate project' do - expect(::Pages::MigrateLegacyStorageToDeploymentService).not_to receive(:new) - - is_expected.to eq(migrated: 0, errored: 0) - end - end - end - end - end - - describe '#execute_with_threads' do - subject { service.execute_with_threads(threads: 3, batch_size: batch_size) } - - include_examples "migrates projects properly" - - context 'when there is work for multiple threads' do - let(:batch_size) { 2 } # override to force usage of multiple threads - - it 'uses multiple threads' do - projects = create_list(:project, 20) - projects.each do |project| - project.mark_pages_as_deployed - - FileUtils.mkdir_p File.join(project.pages_path, "public") - File.open(File.join(project.pages_path, "public/index.html"), "w") do |f| - f.write("Hello!") - end - end - - threads = Concurrent::Set.new - - expect(service).to receive(:migrate_project).exactly(20).times.and_wrap_original do |m, *args| - threads.add(Thread.current) - - # sleep to be 100% certain that once thread can't consume all the queue - # it works without it, but I want to avoid making this test flaky - sleep(0.01) - - m.call(*args) - end - - is_expected.to eq(migrated: 20, errored: 0) - expect(threads.length).to eq(3) - end - end - end - - describe "#execute_for_batch" do - subject { service.execute_for_batch(Project.ids) } - - include_examples "migrates projects properly" - - it 'only tries to migrate projects with passed ids' do - projects = create_list(:project, 5) - - projects.each(&:mark_pages_as_deployed) - projects_to_migrate = projects.first(3) - - projects_to_migrate.each do |project| - expect_next_instance_of(::Pages::MigrateLegacyStorageToDeploymentService, project, ignore_invalid_entries: false, mark_projects_as_not_deployed: false) do |service| - expect(service).to receive(:execute).and_call_original - end - end - - expect(service.execute_for_batch(projects_to_migrate.pluck(:id))).to eq(migrated: 0, errored: 3) - end - end -end diff --git a/spec/services/pages/migrate_legacy_storage_to_deployment_service_spec.rb b/spec/services/pages/migrate_legacy_storage_to_deployment_service_spec.rb deleted file mode 100644 index e1cce2c87eb..00000000000 --- a/spec/services/pages/migrate_legacy_storage_to_deployment_service_spec.rb +++ /dev/null @@ -1,118 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe Pages::MigrateLegacyStorageToDeploymentService, feature_category: :pages do - let(:project) { create(:project, :repository) } - let(:service) { described_class.new(project) } - - it 'calls ::Pages::ZipDirectoryService' do - expect_next_instance_of(::Pages::ZipDirectoryService, project.pages_path, ignore_invalid_entries: true) do |zip_service| - expect(zip_service).to receive(:execute).and_call_original - end - - expect(described_class.new(project, ignore_invalid_entries: true).execute[:status]).to eq(:error) - end - - context 'when mark_projects_as_not_deployed is passed' do - let(:service) { described_class.new(project, mark_projects_as_not_deployed: true) } - - it 'marks pages as not deployed if public directory is absent and invalid entries are ignored' do - project.mark_pages_as_deployed - expect(project.pages_metadatum.reload.deployed).to eq(true) - - expect(service.execute).to eq( - status: :success, - message: "Archive not created. Missing public directory in #{project.pages_path}? Marked project as not deployed" - ) - - expect(project.pages_metadatum.reload.deployed).to eq(false) - end - - it 'does not mark pages as not deployed if public directory is absent but pages_deployment exists' do - deployment = create(:pages_deployment, project: project) - project.update_pages_deployment!(deployment) - project.mark_pages_as_deployed - expect(project.pages_metadatum.reload.deployed).to eq(true) - - expect(service.execute).to eq( - status: :success, - message: "Archive not created. Missing public directory in #{project.pages_path}? Marked project as not deployed" - ) - - expect(project.pages_metadatum.reload.deployed).to eq(true) - end - end - - it 'does not mark pages as not deployed if public directory is absent but invalid entries are not ignored' do - project.mark_pages_as_deployed - - expect(project.pages_metadatum.reload.deployed).to eq(true) - - expect(service.execute).to eq( - status: :error, - message: "Archive not created. Missing public directory in #{project.pages_path}" - ) - - expect(project.pages_metadatum.reload.deployed).to eq(true) - end - - it 'removes pages archive when can not save deployment' do - archive = fixture_file_upload("spec/fixtures/pages.zip") - expect_next_instance_of(::Pages::ZipDirectoryService) do |zip_service| - expect(zip_service).to receive(:execute).and_return( - status: :success, archive_path: archive.path, entries_count: 3 - ) - end - - expect_next_instance_of(PagesDeployment) do |deployment| - expect(deployment).to receive(:save!).and_raise("Something") - end - - expect do - service.execute - end.to raise_error("Something") - - expect(File.exist?(archive.path)).to eq(false) - end - - context 'when pages site is deployed to legacy storage' do - before do - FileUtils.mkdir_p File.join(project.pages_path, "public") - File.open(File.join(project.pages_path, "public/index.html"), "w") do |f| - f.write("Hello!") - end - end - - it 'creates pages deployment' do - expect do - expect(described_class.new(project).execute).to eq(status: :success) - end.to change { project.reload.pages_deployments.count }.by(1) - - deployment = project.pages_metadatum.pages_deployment - - Zip::File.open(deployment.file.path) do |zip_file| - expect(zip_file.glob("public").first.ftype).to eq(:directory) - expect(zip_file.glob("public/index.html").first.get_input_stream.read).to eq("Hello!") - end - - expect(deployment.file_count).to eq(2) - expect(deployment.file_sha256).to eq(Digest::SHA256.file(deployment.file.path).hexdigest) - end - - it 'removes tmp pages archive' do - described_class.new(project).execute - - expect(File.exist?(File.join(project.pages_path, '@migrated.zip'))).to eq(false) - end - - it 'does not change pages deployment if it is set' do - old_deployment = create(:pages_deployment, project: project) - project.update_pages_deployment!(old_deployment) - - expect do - described_class.new(project).execute - end.not_to change { project.pages_metadatum.reload.pages_deployment_id }.from(old_deployment.id) - end - end -end diff --git a/spec/services/pages/zip_directory_service_spec.rb b/spec/services/pages/zip_directory_service_spec.rb deleted file mode 100644 index 4917bc65a02..00000000000 --- a/spec/services/pages/zip_directory_service_spec.rb +++ /dev/null @@ -1,280 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe Pages::ZipDirectoryService, feature_category: :pages do - around do |example| - Dir.mktmpdir do |dir| - @work_dir = dir - example.run - end - end - - let(:ignore_invalid_entries) { false } - - let(:service_directory) { @work_dir } - - let(:service) do - described_class.new(service_directory, ignore_invalid_entries: ignore_invalid_entries) - end - - let(:result) do - service.execute - end - - let(:status) { result[:status] } - let(:message) { result[:message] } - let(:archive) { result[:archive_path] } - let(:entries_count) { result[:entries_count] } - - it 'returns true if ZIP64 is enabled' do - expect(::Zip.write_zip64_support).to be true - end - - shared_examples 'handles invalid public directory' do - it 'returns success' do - expect(status).to eq(:success) - expect(archive).to be_nil - expect(entries_count).to be_nil - end - end - - context "when work directory doesn't exist" do - let(:service_directory) { "/tmp/not/existing/dir" } - - include_examples 'handles invalid public directory' - end - - context 'when public directory is absent' do - include_examples 'handles invalid public directory' - end - - context 'when public directory is a symlink' do - before do - create_dir('target') - create_file('./target/index.html', 'hello') - create_link("public", "./target") - end - - include_examples 'handles invalid public directory' - end - - context 'when there is a public directory' do - before do - create_dir('public') - end - - it 'creates the file next the public directory' do - expect(archive).to eq(File.join(@work_dir, "@migrated.zip")) - end - - it 'includes public directory' do - with_zip_file do |zip_file| - entry = zip_file.get_entry("public/") - expect(entry.ftype).to eq(:directory) - end - end - - it 'returns number of entries' do - create_file("public/index.html", "hello") - create_link("public/link.html", "./index.html") - expect(entries_count).to eq(3) # + 'public' directory - end - - it 'removes the old file if it exists' do - # simulate the old run - described_class.new(@work_dir).execute - - with_zip_file do |zip_file| - expect(zip_file.entries.count).to eq(1) - end - end - - it 'ignores other top level files and directories' do - create_file("top_level.html", "hello") - create_dir("public2") - - with_zip_file do |zip_file| - expect { zip_file.get_entry("top_level.html") }.to raise_error(Errno::ENOENT) - expect { zip_file.get_entry("public2/") }.to raise_error(Errno::ENOENT) - end - end - - it 'includes index.html file' do - create_file("public/index.html", "Hello!") - - with_zip_file do |zip_file| - entry = zip_file.get_entry("public/index.html") - expect(zip_file.read(entry)).to eq("Hello!") - end - end - - it 'includes hidden file' do - create_file("public/.hidden.html", "Hello!") - - with_zip_file do |zip_file| - entry = zip_file.get_entry("public/.hidden.html") - expect(zip_file.read(entry)).to eq("Hello!") - end - end - - it 'includes nested directories and files' do - create_dir("public/nested") - create_dir("public/nested/nested2") - create_file("public/nested/nested2/nested.html", "Hello nested") - - with_zip_file do |zip_file| - entry = zip_file.get_entry("public/nested") - expect(entry.ftype).to eq(:directory) - - entry = zip_file.get_entry("public/nested/nested2") - expect(entry.ftype).to eq(:directory) - - entry = zip_file.get_entry("public/nested/nested2/nested.html") - expect(zip_file.read(entry)).to eq("Hello nested") - end - end - - it 'adds a valid symlink' do - create_file("public/target.html", "hello") - create_link("public/link.html", "./target.html") - - with_zip_file do |zip_file| - entry = zip_file.get_entry("public/link.html") - expect(entry.ftype).to eq(:symlink) - expect(zip_file.read(entry)).to eq("./target.html") - end - end - - shared_examples "raises or ignores file" do |raised_exception, file| - it 'raises error' do - expect do - result - end.to raise_error(raised_exception) - end - - context 'when errors are ignored' do - let(:ignore_invalid_entries) { true } - - it 'does not create entry' do - with_zip_file do |zip_file| - expect { zip_file.get_entry(file) }.to raise_error(Errno::ENOENT) - end - end - end - end - - context 'when symlink points outside of public directory' do - before do - create_file("target.html", "hello") - create_link("public/link.html", "../target.html") - end - - include_examples "raises or ignores file", described_class::InvalidEntryError, "public/link.html" - end - - context 'when target of the symlink is absent' do - before do - create_link("public/link.html", "./target.html") - end - - include_examples "raises or ignores file", Errno::ENOENT, "public/link.html" - end - - context 'when targets itself' do - before do - create_link("public/link.html", "./link.html") - end - - include_examples "raises or ignores file", Errno::ELOOP, "public/link.html" - end - - context 'when symlink is absolute and points to outside of directory' do - before do - target = File.join(@work_dir, "target") - FileUtils.touch(target) - - create_link("public/link.html", target) - end - - include_examples "raises or ignores file", described_class::InvalidEntryError, "public/link.html" - end - - context 'when entry has unknown ftype' do - before do - file = create_file("public/index.html", "hello") - - allow(File).to receive(:lstat).and_call_original - expect(File).to receive(:lstat).with(file) { double("lstat", ftype: "unknown") } - end - - include_examples "raises or ignores file", described_class::InvalidEntryError, "public/index.html" - end - - it "includes raw symlink if it's target is a valid directory" do - create_dir("public/target") - create_file("public/target/index.html", "hello") - create_link("public/link", "./target") - - with_zip_file do |zip_file| - expect(zip_file.entries.count).to eq(4) # /public and 3 created above - - entry = zip_file.get_entry("public/link") - expect(entry.ftype).to eq(:symlink) - expect(zip_file.read(entry)).to eq("./target") - end - end - end - - context "validating fixtures pages archives" do - using RSpec::Parameterized::TableSyntax - - where(:fixture_path) do - ["spec/fixtures/pages.zip", "spec/fixtures/pages_non_writeable.zip"] - end - - with_them do - let(:full_fixture_path) { Rails.root.join(fixture_path) } - - it 'a created archives contains exactly the same entries' do - SafeZip::Extract.new(full_fixture_path).extract(directories: ['public'], to: @work_dir) - - with_zip_file do |created_archive| - Zip::File.open(full_fixture_path) do |original_archive| - original_archive.entries do |original_entry| - created_entry = created_archive.get_entry(original_entry.name) - - expect(created_entry.name).to eq(original_entry.name) - expect(created_entry.ftype).to eq(original_entry.ftype) - expect(created_archive.read(created_entry)).to eq(original_archive.read(original_entry)) - end - end - end - end - end - end - - def create_file(name, content) - file_path = File.join(@work_dir, name) - - File.open(file_path, "w") do |f| - f.write(content) - end - - file_path - end - - def create_dir(dir) - Dir.mkdir(File.join(@work_dir, dir)) - end - - def create_link(new_name, target) - File.symlink(target, File.join(@work_dir, new_name)) - end - - def with_zip_file - Zip::File.open(archive) do |zip_file| - yield zip_file - end - end -end diff --git a/spec/services/todo_service_spec.rb b/spec/services/todo_service_spec.rb index 0888c27aab2..0a6b11046f5 100644 --- a/spec/services/todo_service_spec.rb +++ b/spec/services/todo_service_spec.rb @@ -5,6 +5,7 @@ require 'spec_helper' RSpec.describe TodoService, feature_category: :team_planning do include AfterNextHelpers + let_it_be(:group) { create(:group) } let_it_be(:project) { create(:project, :repository) } let_it_be(:author) { create(:user) } let_it_be(:assignee) { create(:user) } @@ -657,11 +658,27 @@ RSpec.describe TodoService, feature_category: :team_planning do end describe '#mark_todo' do - it 'creates a todo from a issue' do + it 'creates a todo from an issue' do service.mark_todo(unassigned_issue, author) should_create_todo(user: author, target: unassigned_issue, action: Todo::MARKED) end + + context 'when issue belongs to a group' do + it 'creates a todo from an issue' do + group_issue = create(:issue, :group_level, namespace: group) + service.mark_todo(group_issue, group_issue.author) + + should_create_todo( + user: group_issue.author, + author: group_issue.author, + target: group_issue, + action: Todo::MARKED, + project: nil, + group: group + ) + end + end end describe '#todo_exists?' do @@ -726,6 +743,22 @@ RSpec.describe TodoService, feature_category: :team_planning do should_create_todo(user: author, target: work_item, action: Todo::MARKED) end + + context 'when work item belongs to a group' do + it 'creates a todo from a work item' do + group_work_item = create(:work_item, :group_level, namespace: group) + service.mark_todo(group_work_item, group_work_item.author) + + should_create_todo( + user: group_work_item.author, + author: group_work_item.author, + target: group_work_item, + action: Todo::MARKED, + project: nil, + group: group + ) + end + end end describe '#todo_exists?' do diff --git a/spec/support/ability_check_todo.yml b/spec/support/ability_check_todo.yml index eafd595b137..a317f49ea94 100644 --- a/spec/support/ability_check_todo.yml +++ b/spec/support/ability_check_todo.yml @@ -66,8 +66,6 @@ ProjectPolicy: - create_test_case - read_group_saml_identity UserPolicy: -- admin_observability - admin_terraform_state -- read_observability # Permanent excludes (please provide a reason): diff --git a/spec/support/helpers/navbar_structure_helper.rb b/spec/support/helpers/navbar_structure_helper.rb index 9a6af5fb8ae..fe39968b002 100644 --- a/spec/support/helpers/navbar_structure_helper.rb +++ b/spec/support/helpers/navbar_structure_helper.rb @@ -85,19 +85,6 @@ module NavbarStructureHelper ) end - def insert_observability_nav - insert_after_nav_item( - _('Kubernetes'), - new_nav_item: { - nav_item: _('Observability'), - nav_sub_items: [ - _('Explore telemetry data'), - _('Data sources') - ] - } - ) - end - def insert_infrastructure_google_cloud_nav insert_after_sub_nav_item( s_('Terraform|Terraform states'), diff --git a/spec/support/rspec_order_todo.yml b/spec/support/rspec_order_todo.yml index 9512ff1a057..a93ed7c1fec 100644 --- a/spec/support/rspec_order_todo.yml +++ b/spec/support/rspec_order_todo.yml @@ -9354,8 +9354,6 @@ - './spec/services/pages_domains/create_acme_order_service_spec.rb' - './spec/services/pages_domains/obtain_lets_encrypt_certificate_service_spec.rb' - './spec/services/pages_domains/retry_acme_order_service_spec.rb' -- './spec/services/pages/migrate_from_legacy_storage_service_spec.rb' -- './spec/services/pages/migrate_legacy_storage_to_deployment_service_spec.rb' - './spec/services/pages/zip_directory_service_spec.rb' - './spec/services/personal_access_tokens/create_service_spec.rb' - './spec/services/personal_access_tokens/last_used_service_spec.rb' diff --git a/spec/support/shared_examples/observability/embed_observabilities_examples.rb b/spec/support/shared_examples/observability/embed_observabilities_examples.rb deleted file mode 100644 index c8d4e9e0d7e..00000000000 --- a/spec/support/shared_examples/observability/embed_observabilities_examples.rb +++ /dev/null @@ -1,61 +0,0 @@ -# frozen_string_literal: true - -RSpec.shared_examples 'embeds observability' do - it 'renders iframe in description' do - page.within('.description') do - expect_observability_iframe(page.html) - end - end - - it 'renders iframe in comment' do - expect(page).not_to have_css('.note-text') - - page.within('.js-main-target-form') do - fill_in('note[note]', with: observable_url) - click_button('Comment') - end - - wait_for_requests - - page.within('.note-text') do - expect_observability_iframe(page.html) - end - end -end - -RSpec.shared_examples 'does not embed observability' do - it 'does not render iframe in description' do - page.within('.description') do - expect_observability_iframe(page.html, to_be_nil: true) - end - end - - it 'does not render iframe in comment' do - expect(page).not_to have_css('.note-text') - - page.within('.js-main-target-form') do - fill_in('note[note]', with: observable_url) - click_button('Comment') - end - - wait_for_requests - - page.within('.note-text') do - expect_observability_iframe(page.html, to_be_nil: true) - end - end -end - -def expect_observability_iframe(html, to_be_nil: false) - iframe = Nokogiri::HTML.parse(html).at_css('#observability-ui-iframe') - - expect(html).to include(observable_url) - - if to_be_nil - expect(iframe).to be_nil - else - expect(iframe).not_to be_nil - iframe_src = "#{expected_observable_url}&theme=light&username=#{user.username}&kiosk=inline-embed" - expect(iframe.attributes['src'].value).to eq(iframe_src) - end -end diff --git a/spec/views/groups/observability/observability.html.haml_spec.rb b/spec/views/groups/observability/observability.html.haml_spec.rb deleted file mode 100644 index 0561737cb39..00000000000 --- a/spec/views/groups/observability/observability.html.haml_spec.rb +++ /dev/null @@ -1,18 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe 'groups/observability/observability.html.haml' do - let(:iframe_url) { "foo.test" } - - before do - allow(view).to receive(:observability_iframe_src).and_return(iframe_url) - end - - it 'renders as expected' do - render - page = Capybara.string(rendered) - div = page.find('#js-observability-app') - expect(div['data-observability-iframe-src']).to eq(iframe_url) - end -end -- cgit v1.2.3