Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
		
							parent
							
								
									ce8a0b9084
								
							
						
					
					
						commit
						dd4bee69b7
					
				|  | @ -10,17 +10,24 @@ | ||||||
| *.md @gl-docsteam | *.md @gl-docsteam | ||||||
| 
 | 
 | ||||||
| # Frontend maintainers should see everything in `app/assets/` | # Frontend maintainers should see everything in `app/assets/` | ||||||
| app/assets/ @gitlab-org/maintainers/frontend |  | ||||||
| *.scss @annabeldunstone @gitlab-org/maintainers/frontend | *.scss @annabeldunstone @gitlab-org/maintainers/frontend | ||||||
| /scripts/frontend/ @gitlab-org/maintainers/frontend | *.js @gitlab-org/maintainers/frontend | ||||||
|  | /app/assets/ @gitlab-org/maintainers/frontend | ||||||
|  | /ee/app/assets/ @gitlab-org/maintainers/frontend | ||||||
|  | /spec/javascripts/ @gitlab-org/maintainers/frontend | ||||||
|  | /ee/spec/javascripts/ @gitlab-org/maintainers/frontend | ||||||
|  | /spec/frontend/ @gitlab-org/maintainers/frontend | ||||||
|  | /ee/spec/frontend/ @gitlab-org/maintainers/frontend | ||||||
| 
 | 
 | ||||||
| # Database maintainers should review changes in `db/` | # Database maintainers should review changes in `db/` | ||||||
| db/ @gitlab-org/maintainers/database | /db/ @gitlab-org/maintainers/database | ||||||
| lib/gitlab/background_migration/ @gitlab-org/maintainers/database |  | ||||||
| lib/gitlab/database/ @gitlab-org/maintainers/database |  | ||||||
| lib/gitlab/sql/ @gitlab-org/maintainers/database |  | ||||||
| lib/gitlab/github_import/ @gitlab-org/maintainers/database |  | ||||||
| /ee/db/ @gitlab-org/maintainers/database | /ee/db/ @gitlab-org/maintainers/database | ||||||
|  | /lib/gitlab/background_migration/ @gitlab-org/maintainers/database | ||||||
|  | /ee/lib/ee/gitlab/background_migration/ @gitlab-org/maintainers/database | ||||||
|  | /lib/gitlab/database/ @gitlab-org/maintainers/database | ||||||
|  | /ee/lib/gitlab/database/ @gitlab-org/maintainers/database | ||||||
|  | /lib/gitlab/sql/ @gitlab-org/maintainers/database | ||||||
|  | /lib/gitlab/github_import/ @gitlab-org/maintainers/database | ||||||
| /app/finders/ @gitlab-org/maintainers/database | /app/finders/ @gitlab-org/maintainers/database | ||||||
| /ee/app/finders/ @gitlab-org/maintainers/database | /ee/app/finders/ @gitlab-org/maintainers/database | ||||||
| 
 | 
 | ||||||
|  | @ -40,14 +47,14 @@ lib/gitlab/github_import/ @gitlab-org/maintainers/database | ||||||
| # Engineering Productivity owned files | # Engineering Productivity owned files | ||||||
| /.gitlab-ci.yml @gl-quality/eng-prod | /.gitlab-ci.yml @gl-quality/eng-prod | ||||||
| /.gitlab/ci/ @gl-quality/eng-prod | /.gitlab/ci/ @gl-quality/eng-prod | ||||||
|  | /.gitlab/ci/docs.gitlab-ci.yml @gl-quality/eng-prod @gl-docsteam | ||||||
|  | /.gitlab/ci/releases.gitlab-ci.yml @gl-quality/eng-prod @gitlab-org/delivery | ||||||
| /.gitlab/CODEOWNERS @gl-quality/eng-prod | /.gitlab/CODEOWNERS @gl-quality/eng-prod | ||||||
| Dangerfile @gl-quality/eng-prod | Dangerfile @gl-quality/eng-prod | ||||||
| /danger/ @gl-quality/eng-prod | /danger/ @gl-quality/eng-prod | ||||||
| /lib/gitlab/danger/ @gl-quality/eng-prod | /lib/gitlab/danger/ @gl-quality/eng-prod | ||||||
| /scripts/ @gl-quality/eng-prod | /scripts/ @gl-quality/eng-prod | ||||||
| 
 | /scripts/frontend/ @gl-quality/eng-prod @gitlab-org/maintainers/frontend | ||||||
| # Delivery owner files |  | ||||||
| /.gitlab/ci/releases.gitlab-ci.yml @gitlab-org/delivery |  | ||||||
| 
 | 
 | ||||||
| # Telemetry owner files | # Telemetry owner files | ||||||
| /ee/lib/gitlab/usage_data_counters/ @gitlab-org/growth/telemetry | /ee/lib/gitlab/usage_data_counters/ @gitlab-org/growth/telemetry | ||||||
|  |  | ||||||
|  | @ -4,6 +4,9 @@ import Cookies from 'js-cookie'; | ||||||
| import { parseBoolean, scrollToElement } from '~/lib/utils/common_utils'; | import { parseBoolean, scrollToElement } from '~/lib/utils/common_utils'; | ||||||
| import { s__ } from '~/locale'; | import { s__ } from '~/locale'; | ||||||
| import { glEmojiTag } from '~/emoji'; | import { glEmojiTag } from '~/emoji'; | ||||||
|  | import Tracking from '~/tracking'; | ||||||
|  | 
 | ||||||
|  | const trackingMixin = Tracking.mixin(); | ||||||
| 
 | 
 | ||||||
| const popoverStates = { | const popoverStates = { | ||||||
|   suggest_gitlab_ci_yml: { |   suggest_gitlab_ci_yml: { | ||||||
|  | @ -27,6 +30,7 @@ export default { | ||||||
|     GlIcon, |     GlIcon, | ||||||
|     GlButton, |     GlButton, | ||||||
|   }, |   }, | ||||||
|  |   mixins: [trackingMixin], | ||||||
|   props: { |   props: { | ||||||
|     target: { |     target: { | ||||||
|       type: String, |       type: String, | ||||||
|  | @ -40,10 +44,18 @@ export default { | ||||||
|       type: String, |       type: String, | ||||||
|       required: true, |       required: true, | ||||||
|     }, |     }, | ||||||
|  |     humanAccess: { | ||||||
|  |       type: String, | ||||||
|  |       required: true, | ||||||
|  |     }, | ||||||
|   }, |   }, | ||||||
|   data() { |   data() { | ||||||
|     return { |     return { | ||||||
|       popoverDismissed: parseBoolean(Cookies.get(this.dismissKey)), |       popoverDismissed: parseBoolean(Cookies.get(this.dismissKey)), | ||||||
|  |       tracking: { | ||||||
|  |         label: this.trackLabel, | ||||||
|  |         property: this.humanAccess, | ||||||
|  |       }, | ||||||
|     }; |     }; | ||||||
|   }, |   }, | ||||||
|   computed: { |   computed: { | ||||||
|  | @ -60,12 +72,17 @@ export default { | ||||||
|   mounted() { |   mounted() { | ||||||
|     if (this.trackLabel === 'suggest_commit_first_project_gitlab_ci_yml' && !this.popoverDismissed) |     if (this.trackLabel === 'suggest_commit_first_project_gitlab_ci_yml' && !this.popoverDismissed) | ||||||
|       scrollToElement(document.querySelector(this.target)); |       scrollToElement(document.querySelector(this.target)); | ||||||
|  | 
 | ||||||
|  |     this.trackOnShow(); | ||||||
|   }, |   }, | ||||||
|   methods: { |   methods: { | ||||||
|     onDismiss() { |     onDismiss() { | ||||||
|       this.popoverDismissed = true; |       this.popoverDismissed = true; | ||||||
|       Cookies.set(this.dismissKey, this.popoverDismissed, { expires: 365 }); |       Cookies.set(this.dismissKey, this.popoverDismissed, { expires: 365 }); | ||||||
|     }, |     }, | ||||||
|  |     trackOnShow() { | ||||||
|  |       if (!this.popoverDismissed) this.track(); | ||||||
|  |     }, | ||||||
|   }, |   }, | ||||||
| }; | }; | ||||||
| </script> | </script> | ||||||
|  |  | ||||||
|  | @ -10,6 +10,7 @@ export default el => | ||||||
|           target: el.dataset.target, |           target: el.dataset.target, | ||||||
|           trackLabel: el.dataset.trackLabel, |           trackLabel: el.dataset.trackLabel, | ||||||
|           dismissKey: el.dataset.dismissKey, |           dismissKey: el.dataset.dismissKey, | ||||||
|  |           humanAccess: el.dataset.humanAccess, | ||||||
|         }, |         }, | ||||||
|       }); |       }); | ||||||
|     }, |     }, | ||||||
|  |  | ||||||
|  | @ -21,7 +21,7 @@ export default { | ||||||
|     }; |     }; | ||||||
|   }, |   }, | ||||||
|   computed: { |   computed: { | ||||||
|     ...mapState(['entries', 'promotionSvgPath', 'links']), |     ...mapState(['entries', 'promotionSvgPath', 'links', 'codesandboxBundlerUrl']), | ||||||
|     ...mapGetters(['packageJson', 'currentProject']), |     ...mapGetters(['packageJson', 'currentProject']), | ||||||
|     normalizedEntries() { |     normalizedEntries() { | ||||||
|       return Object.keys(this.entries).reduce((acc, path) => { |       return Object.keys(this.entries).reduce((acc, path) => { | ||||||
|  | @ -106,12 +106,7 @@ export default { | ||||||
|       return this.loadFileContent(this.mainEntry) |       return this.loadFileContent(this.mainEntry) | ||||||
|         .then(() => this.$nextTick()) |         .then(() => this.$nextTick()) | ||||||
|         .then(() => { |         .then(() => { | ||||||
|           this.initManager('#ide-preview', this.sandboxOpts, { |           this.initManager(); | ||||||
|             fileResolver: { |  | ||||||
|               isFile: p => Promise.resolve(Boolean(this.entries[createPathWithExt(p)])), |  | ||||||
|               readFile: p => this.loadFileContent(createPathWithExt(p)).then(content => content), |  | ||||||
|             }, |  | ||||||
|           }); |  | ||||||
| 
 | 
 | ||||||
|           this.listener = listen(e => { |           this.listener = listen(e => { | ||||||
|             switch (e.type) { |             switch (e.type) { | ||||||
|  | @ -139,8 +134,18 @@ export default { | ||||||
|         this.manager.updatePreview(this.sandboxOpts); |         this.manager.updatePreview(this.sandboxOpts); | ||||||
|       }, 250); |       }, 250); | ||||||
|     }, |     }, | ||||||
|     initManager(el, opts, resolver) { |     initManager() { | ||||||
|       this.manager = new Manager(el, opts, resolver); |       const { codesandboxBundlerUrl: bundlerURL } = this; | ||||||
|  | 
 | ||||||
|  |       const settings = { | ||||||
|  |         fileResolver: { | ||||||
|  |           isFile: p => Promise.resolve(Boolean(this.entries[createPathWithExt(p)])), | ||||||
|  |           readFile: p => this.loadFileContent(createPathWithExt(p)).then(content => content), | ||||||
|  |         }, | ||||||
|  |         ...(bundlerURL ? { bundlerURL } : {}), | ||||||
|  |       }; | ||||||
|  | 
 | ||||||
|  |       this.manager = new Manager('#ide-preview', this.sandboxOpts, settings); | ||||||
|     }, |     }, | ||||||
|   }, |   }, | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | @ -53,6 +53,7 @@ export function initIde(el, options = {}) { | ||||||
|         clientsidePreviewEnabled: parseBoolean(el.dataset.clientsidePreviewEnabled), |         clientsidePreviewEnabled: parseBoolean(el.dataset.clientsidePreviewEnabled), | ||||||
|         renderWhitespaceInCode: parseBoolean(el.dataset.renderWhitespaceInCode), |         renderWhitespaceInCode: parseBoolean(el.dataset.renderWhitespaceInCode), | ||||||
|         editorTheme: window.gon?.user_color_scheme || DEFAULT_THEME, |         editorTheme: window.gon?.user_color_scheme || DEFAULT_THEME, | ||||||
|  |         codesandboxBundlerUrl: el.dataset.codesandboxBundlerUrl, | ||||||
|       }); |       }); | ||||||
|     }, |     }, | ||||||
|     methods: { |     methods: { | ||||||
|  |  | ||||||
|  | @ -34,4 +34,5 @@ export default () => ({ | ||||||
|   clientsidePreviewEnabled: false, |   clientsidePreviewEnabled: false, | ||||||
|   renderWhitespaceInCode: false, |   renderWhitespaceInCode: false, | ||||||
|   editorTheme: DEFAULT_THEME, |   editorTheme: DEFAULT_THEME, | ||||||
|  |   codesandboxBundlerUrl: null, | ||||||
| }); | }); | ||||||
|  |  | ||||||
|  | @ -1,6 +1,6 @@ | ||||||
| <script> | <script> | ||||||
| import $ from 'jquery'; | import $ from 'jquery'; | ||||||
| import _ from 'underscore'; | import { intersection } from 'lodash'; | ||||||
| 
 | 
 | ||||||
| import '~/smart_interval'; | import '~/smart_interval'; | ||||||
| 
 | 
 | ||||||
|  | @ -38,7 +38,7 @@ export default { | ||||||
|       } else { |       } else { | ||||||
|         changedCommands = []; |         changedCommands = []; | ||||||
|       } |       } | ||||||
|       if (changedCommands && _.intersection(subscribedCommands, changedCommands).length) { |       if (changedCommands && intersection(subscribedCommands, changedCommands).length) { | ||||||
|         this.mediator.fetch(); |         this.mediator.fetch(); | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|  |  | ||||||
|  | @ -1,6 +1,6 @@ | ||||||
| import $ from 'jquery'; | import $ from 'jquery'; | ||||||
| import '~/gl_dropdown'; | import '~/gl_dropdown'; | ||||||
| import _ from 'underscore'; | import { escape as esc } from 'lodash'; | ||||||
| import { __ } from '~/locale'; | import { __ } from '~/locale'; | ||||||
| 
 | 
 | ||||||
| function isValidProjectId(id) { | function isValidProjectId(id) { | ||||||
|  | @ -49,7 +49,7 @@ class SidebarMoveIssue { | ||||||
|       renderRow: project => ` |       renderRow: project => ` | ||||||
|         <li> |         <li> | ||||||
|           <a href="#" class="js-move-issue-dropdown-item"> |           <a href="#" class="js-move-issue-dropdown-item"> | ||||||
|             ${_.escape(project.name_with_namespace)} |             ${esc(project.name_with_namespace)} | ||||||
|           </a> |           </a> | ||||||
|         </li> |         </li> | ||||||
|       `,
 |       `,
 | ||||||
|  |  | ||||||
|  | @ -0,0 +1,17 @@ | ||||||
|  | # frozen_string_literal: true | ||||||
|  | 
 | ||||||
|  | module ClientsidePreviewCSP | ||||||
|  |   extend ActiveSupport::Concern | ||||||
|  | 
 | ||||||
|  |   included do | ||||||
|  |     content_security_policy do |p| | ||||||
|  |       next if p.directives.blank? | ||||||
|  |       next unless Gitlab::CurrentSettings.web_ide_clientside_preview_enabled? | ||||||
|  | 
 | ||||||
|  |       default_frame_src = p.directives['frame-src'] || p.directives['default-src'] | ||||||
|  |       frame_src_values = Array.wrap(default_frame_src) | [Gitlab::CurrentSettings.web_ide_clientside_preview_bundler_url].compact | ||||||
|  | 
 | ||||||
|  |       p.frame_src(*frame_src_values) | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  | end | ||||||
|  | @ -3,6 +3,7 @@ | ||||||
| class IdeController < ApplicationController | class IdeController < ApplicationController | ||||||
|   layout 'fullscreen' |   layout 'fullscreen' | ||||||
| 
 | 
 | ||||||
|  |   include ClientsidePreviewCSP | ||||||
|   include StaticObjectExternalStorageCSP |   include StaticObjectExternalStorageCSP | ||||||
| 
 | 
 | ||||||
|   def index |   def index | ||||||
|  |  | ||||||
|  | @ -353,4 +353,8 @@ module BlobHelper | ||||||
|   def suggest_pipeline_commit_cookie_name |   def suggest_pipeline_commit_cookie_name | ||||||
|     "suggest_gitlab_ci_yml_commit_#{@project.id}" |     "suggest_gitlab_ci_yml_commit_#{@project.id}" | ||||||
|   end |   end | ||||||
|  | 
 | ||||||
|  |   def human_access | ||||||
|  |     @project.team.human_max_access(current_user&.id).try(:downcase) | ||||||
|  |   end | ||||||
| end | end | ||||||
|  |  | ||||||
|  | @ -10,8 +10,9 @@ module IdeHelper | ||||||
|       "promotion-svg-path": image_path('illustrations/web-ide_promotion.svg'), |       "promotion-svg-path": image_path('illustrations/web-ide_promotion.svg'), | ||||||
|       "ci-help-page-path" => help_page_path('ci/quick_start/README'), |       "ci-help-page-path" => help_page_path('ci/quick_start/README'), | ||||||
|       "web-ide-help-page-path" => help_page_path('user/project/web_ide/index.html'), |       "web-ide-help-page-path" => help_page_path('user/project/web_ide/index.html'), | ||||||
|       "clientside-preview-enabled": Gitlab::CurrentSettings.current_application_settings.web_ide_clientside_preview_enabled.to_s, |       "clientside-preview-enabled": Gitlab::CurrentSettings.web_ide_clientside_preview_enabled?.to_s, | ||||||
|       "render-whitespace-in-code": current_user.render_whitespace_in_code.to_s |       "render-whitespace-in-code": current_user.render_whitespace_in_code.to_s, | ||||||
|  |       "codesandbox-bundler-url": Gitlab::CurrentSettings.web_ide_clientside_preview_bundler_url | ||||||
|     } |     } | ||||||
|   end |   end | ||||||
| end | end | ||||||
|  |  | ||||||
|  | @ -351,6 +351,12 @@ module ApplicationSettingImplementation | ||||||
|     static_objects_external_storage_url.present? |     static_objects_external_storage_url.present? | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|  |   # This will eventually be configurable | ||||||
|  |   # https://gitlab.com/gitlab-org/gitlab/issues/208161 | ||||||
|  |   def web_ide_clientside_preview_bundler_url | ||||||
|  |     'https://sandbox-prod.gitlab-static.net' | ||||||
|  |   end | ||||||
|  | 
 | ||||||
|   private |   private | ||||||
| 
 | 
 | ||||||
|   def separate_whitelists(string_array) |   def separate_whitelists(string_array) | ||||||
|  |  | ||||||
|  | @ -257,7 +257,7 @@ class WikiPage | ||||||
|   def title_changed? |   def title_changed? | ||||||
|     if persisted? |     if persisted? | ||||||
|       old_title, old_dir = wiki.page_title_and_dir(self.class.unhyphenize(@page.url_path)) |       old_title, old_dir = wiki.page_title_and_dir(self.class.unhyphenize(@page.url_path)) | ||||||
|       new_title, new_dir = wiki.page_title_and_dir(title) |       new_title, new_dir = wiki.page_title_and_dir(self.class.unhyphenize(title)) | ||||||
| 
 | 
 | ||||||
|       new_title != old_title || (title.include?('/') && new_dir != old_dir) |       new_title != old_title || (title.include?('/') && new_dir != old_dir) | ||||||
|     else |     else | ||||||
|  |  | ||||||
|  | @ -23,7 +23,8 @@ | ||||||
|         .js-suggest-gitlab-ci-yml{ data: { toggle: 'popover', |         .js-suggest-gitlab-ci-yml{ data: { toggle: 'popover', | ||||||
|           target: '#gitlab-ci-yml-selector', |           target: '#gitlab-ci-yml-selector', | ||||||
|           track_label: 'suggest_gitlab_ci_yml', |           track_label: 'suggest_gitlab_ci_yml', | ||||||
|           dismiss_key: "suggest_gitlab_ci_yml_#{@project.id}" } } |           dismiss_key: "suggest_gitlab_ci_yml_#{@project.id}", | ||||||
|  |           human_access: human_access } } | ||||||
| 
 | 
 | ||||||
|     .file-buttons |     .file-buttons | ||||||
|       - if is_markdown |       - if is_markdown | ||||||
|  |  | ||||||
|  | @ -17,4 +17,5 @@ | ||||||
|       .js-suggest-gitlab-ci-yml-commit-changes{ data: { toggle: 'popover', |       .js-suggest-gitlab-ci-yml-commit-changes{ data: { toggle: 'popover', | ||||||
|         target: '#commit-changes', |         target: '#commit-changes', | ||||||
|         track_label: 'suggest_commit_first_project_gitlab_ci_yml', |         track_label: 'suggest_commit_first_project_gitlab_ci_yml', | ||||||
|         dismiss_key: "suggest_commit_first_project_gitlab_ci_yml_#{@project.id}" } } |         dismiss_key: "suggest_commit_first_project_gitlab_ci_yml_#{@project.id}", | ||||||
|  |         human_access: human_access } } | ||||||
|  |  | ||||||
|  | @ -0,0 +1,5 @@ | ||||||
|  | --- | ||||||
|  | title: Make hostname configurable for smartcard authentication | ||||||
|  | merge_request: 26411 | ||||||
|  | author: | ||||||
|  | type: added | ||||||
|  | @ -0,0 +1,5 @@ | ||||||
|  | --- | ||||||
|  | title: Fix code search pagination on a custom branch | ||||||
|  | merge_request: 25984 | ||||||
|  | author: | ||||||
|  | type: fixed | ||||||
|  | @ -0,0 +1,5 @@ | ||||||
|  | --- | ||||||
|  | title: Fix WikiPage#title_changed for paths with spaces | ||||||
|  | merge_request: 27087 | ||||||
|  | author: | ||||||
|  | type: fixed | ||||||
|  | @ -0,0 +1,5 @@ | ||||||
|  | --- | ||||||
|  | title: Update Web IDE clientside preview bundler to use GitLab managed server | ||||||
|  | merge_request: 21520 | ||||||
|  | author: | ||||||
|  | type: changed | ||||||
|  | @ -752,7 +752,9 @@ production: &base | ||||||
|     # Path to a file containing a CA certificate |     # Path to a file containing a CA certificate | ||||||
|     ca_file: '/etc/ssl/certs/CA.pem' |     ca_file: '/etc/ssl/certs/CA.pem' | ||||||
| 
 | 
 | ||||||
|     # Port where the client side certificate is requested by the webserver (NGINX/Apache) |     # Host and port where the client side certificate is requested by the | ||||||
|  |     # webserver (NGINX/Apache) | ||||||
|  |     # client_certificate_required_host: smartcard.gitlab.example.com | ||||||
|     # client_certificate_required_port: 3444 |     # client_certificate_required_port: 3444 | ||||||
| 
 | 
 | ||||||
|     # Browser session with smartcard sign-in is required for Git access |     # Browser session with smartcard sign-in is required for Git access | ||||||
|  |  | ||||||
|  | @ -77,6 +77,7 @@ end | ||||||
| Gitlab.ee do | Gitlab.ee do | ||||||
|   Settings['smartcard'] ||= Settingslogic.new({}) |   Settings['smartcard'] ||= Settingslogic.new({}) | ||||||
|   Settings.smartcard['enabled'] = false if Settings.smartcard['enabled'].nil? |   Settings.smartcard['enabled'] = false if Settings.smartcard['enabled'].nil? | ||||||
|  |   Settings.smartcard['client_certificate_required_host'] = Settings.gitlab['host'] if Settings.smartcard['client_certificate_required_host'].nil? | ||||||
|   Settings.smartcard['client_certificate_required_port'] = 3444 if Settings.smartcard['client_certificate_required_port'].nil? |   Settings.smartcard['client_certificate_required_port'] = 3444 if Settings.smartcard['client_certificate_required_port'].nil? | ||||||
|   Settings.smartcard['required_for_git_access'] = false if Settings.smartcard['required_for_git_access'].nil? |   Settings.smartcard['required_for_git_access'] = false if Settings.smartcard['required_for_git_access'].nil? | ||||||
|   Settings.smartcard['san_extensions'] = false if Settings.smartcard['san_extensions'].nil? |   Settings.smartcard['san_extensions'] = false if Settings.smartcard['san_extensions'].nil? | ||||||
|  |  | ||||||
|  | @ -60,7 +60,7 @@ | ||||||
|     "chart.js": "2.7.2", |     "chart.js": "2.7.2", | ||||||
|     "classlist-polyfill": "^1.2.0", |     "classlist-polyfill": "^1.2.0", | ||||||
|     "clipboard": "^1.7.1", |     "clipboard": "^1.7.1", | ||||||
|     "codesandbox-api": "^0.0.20", |     "codesandbox-api": "0.0.23", | ||||||
|     "compression-webpack-plugin": "^3.0.1", |     "compression-webpack-plugin": "^3.0.1", | ||||||
|     "copy-webpack-plugin": "^5.0.5", |     "copy-webpack-plugin": "^5.0.5", | ||||||
|     "core-js": "^3.6.4", |     "core-js": "^3.6.4", | ||||||
|  | @ -110,7 +110,7 @@ | ||||||
|     "raw-loader": "^4.0.0", |     "raw-loader": "^4.0.0", | ||||||
|     "sanitize-html": "^1.22.0", |     "sanitize-html": "^1.22.0", | ||||||
|     "select2": "3.5.2-browserify", |     "select2": "3.5.2-browserify", | ||||||
|     "smooshpack": "^0.0.54", |     "smooshpack": "^0.0.62", | ||||||
|     "sortablejs": "^1.10.2", |     "sortablejs": "^1.10.2", | ||||||
|     "sql.js": "^0.4.0", |     "sql.js": "^0.4.0", | ||||||
|     "stickyfilljs": "^2.1.0", |     "stickyfilljs": "^2.1.0", | ||||||
|  |  | ||||||
|  | @ -2,36 +2,41 @@ | ||||||
| 
 | 
 | ||||||
| cd "$(dirname "$0")/.." | cd "$(dirname "$0")/.." | ||||||
| echo "=> Linting documents at path $(pwd) as $(whoami)..." | echo "=> Linting documents at path $(pwd) as $(whoami)..." | ||||||
|  | echo | ||||||
|  | ERRORCODE=0 | ||||||
| 
 | 
 | ||||||
| # Use long options (e.g. --header instead of -H) for curl examples in documentation. | # Use long options (e.g. --header instead of -H) for curl examples in documentation. | ||||||
| echo '=> Checking for cURL short options...' | echo '=> Checking for cURL short options...' | ||||||
|  | echo | ||||||
| grep --extended-regexp --recursive --color=auto 'curl (.+ )?-[^- ].*' doc/ >/dev/null 2>&1 | grep --extended-regexp --recursive --color=auto 'curl (.+ )?-[^- ].*' doc/ >/dev/null 2>&1 | ||||||
| if [ $? -eq 0 ] | if [ $? -eq 0 ] | ||||||
| then | then | ||||||
|   echo '✖ ERROR: Short options for curl should not be used in documentation! |   echo '✖ ERROR: Short options for curl should not be used in documentation! | ||||||
|          Use long options (e.g., --header instead of -H):' >&2 |          Use long options (e.g., --header instead of -H):' >&2 | ||||||
|   grep --extended-regexp --recursive --color=auto 'curl (.+ )?-[^- ].*' doc/ |   grep --extended-regexp --recursive --color=auto 'curl (.+ )?-[^- ].*' doc/ | ||||||
|   exit 1 |   ((ERRORCODE++)) | ||||||
| fi | fi | ||||||
| 
 | 
 | ||||||
| # Ensure that the CHANGELOG.md does not contain duplicate versions | # Ensure that the CHANGELOG.md does not contain duplicate versions | ||||||
| DUPLICATE_CHANGELOG_VERSIONS=$(grep --extended-regexp '^## .+' CHANGELOG.md | sed -E 's| \(.+\)||' | sort -r | uniq -d) | DUPLICATE_CHANGELOG_VERSIONS=$(grep --extended-regexp '^## .+' CHANGELOG.md | sed -E 's| \(.+\)||' | sort -r | uniq -d) | ||||||
| echo '=> Checking for CHANGELOG.md duplicate entries...' | echo '=> Checking for CHANGELOG.md duplicate entries...' | ||||||
|  | echo | ||||||
| if [ "${DUPLICATE_CHANGELOG_VERSIONS}" != "" ] | if [ "${DUPLICATE_CHANGELOG_VERSIONS}" != "" ] | ||||||
| then | then | ||||||
|   echo '✖ ERROR: Duplicate versions in CHANGELOG.md:' >&2 |   echo '✖ ERROR: Duplicate versions in CHANGELOG.md:' >&2 | ||||||
|   echo "${DUPLICATE_CHANGELOG_VERSIONS}" >&2 |   echo "${DUPLICATE_CHANGELOG_VERSIONS}" >&2 | ||||||
|   exit 1 |   ((ERRORCODE++)) | ||||||
| fi | fi | ||||||
| 
 | 
 | ||||||
| # Make sure no files in doc/ are executable | # Make sure no files in doc/ are executable | ||||||
| EXEC_PERM_COUNT=$(find doc/ -type f -perm 755 | wc -l) | EXEC_PERM_COUNT=$(find doc/ -type f -perm 755 | wc -l) | ||||||
| echo "=> Checking $(pwd)/doc for executable permissions..." | echo "=> Checking $(pwd)/doc for executable permissions..." | ||||||
|  | echo | ||||||
| if [ "${EXEC_PERM_COUNT}" -ne 0 ] | if [ "${EXEC_PERM_COUNT}" -ne 0 ] | ||||||
| then | then | ||||||
|   echo '✖ ERROR: Executable permissions should not be used in documentation! Use `chmod 644` to the files in question:' >&2 |   echo '✖ ERROR: Executable permissions should not be used in documentation! Use `chmod 644` to the files in question:' >&2 | ||||||
|   find doc/ -type f -perm 755 |   find doc/ -type f -perm 755 | ||||||
|   exit 1 |   ((ERRORCODE++)) | ||||||
| fi | fi | ||||||
| 
 | 
 | ||||||
| # Do not use 'README.md', instead use 'index.md' | # Do not use 'README.md', instead use 'index.md' | ||||||
|  | @ -39,13 +44,14 @@ fi | ||||||
| NUMBER_READMES=46 | NUMBER_READMES=46 | ||||||
| FIND_READMES=$(find doc/ -name "README.md" | wc -l) | FIND_READMES=$(find doc/ -name "README.md" | wc -l) | ||||||
| echo '=> Checking for new README.md files...' | echo '=> Checking for new README.md files...' | ||||||
|  | echo | ||||||
| if [ ${FIND_READMES} -ne $NUMBER_READMES ] | if [ ${FIND_READMES} -ne $NUMBER_READMES ] | ||||||
| then | then | ||||||
|   echo |   echo | ||||||
|   echo '  ✖ ERROR: New README.md file(s) detected, prefer index.md over README.md.' >&2 |   echo '  ✖ ERROR: New README.md file(s) detected, prefer index.md over README.md.' >&2 | ||||||
|   echo '  https://docs.gitlab.com/ee/development/documentation/styleguide.html#work-with-directories-and-files' |   echo '  https://docs.gitlab.com/ee/development/documentation/styleguide.html#work-with-directories-and-files' | ||||||
|   echo |   echo | ||||||
|   exit 1 |   ((ERRORCODE++)) | ||||||
| fi | fi | ||||||
| 
 | 
 | ||||||
| MD_DOC_PATH=${MD_DOC_PATH:-doc} | MD_DOC_PATH=${MD_DOC_PATH:-doc} | ||||||
|  | @ -64,7 +70,7 @@ function run_locally_or_in_docker() { | ||||||
|     echo |     echo | ||||||
|     echo "  ✖ ERROR: '${cmd}' not found. Install '${cmd}' or Docker to proceed." >&2 |     echo "  ✖ ERROR: '${cmd}' not found. Install '${cmd}' or Docker to proceed." >&2 | ||||||
|     echo |     echo | ||||||
|     exit 1 |     ((ERRORCODE++)) | ||||||
|   fi |   fi | ||||||
| 
 | 
 | ||||||
|   if [ $? -ne 0 ] |   if [ $? -ne 0 ] | ||||||
|  | @ -72,15 +78,22 @@ function run_locally_or_in_docker() { | ||||||
|     echo |     echo | ||||||
|     echo "  ✖ ERROR: '${cmd}' failed with errors." >&2 |     echo "  ✖ ERROR: '${cmd}' failed with errors." >&2 | ||||||
|     echo |     echo | ||||||
|     exit 1 |     ((ERRORCODE++)) | ||||||
|   fi |   fi | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| echo '=> Linting markdown style...' | echo '=> Linting markdown style...' | ||||||
|  | echo | ||||||
| run_locally_or_in_docker 'markdownlint' "--config .markdownlint.json ${MD_DOC_PATH}" | run_locally_or_in_docker 'markdownlint' "--config .markdownlint.json ${MD_DOC_PATH}" | ||||||
| 
 | 
 | ||||||
| echo '=> Linting prose...' | echo '=> Linting prose...' | ||||||
| run_locally_or_in_docker 'vale' "--minAlertLevel error ${MD_DOC_PATH}" | run_locally_or_in_docker 'vale' "--minAlertLevel error ${MD_DOC_PATH}" | ||||||
| 
 | 
 | ||||||
| echo "✔ Linting passed" | if [ $ERRORCODE -ne 0 ] | ||||||
| exit 0 | then | ||||||
|  |   echo "✖ ${ERRORCODE} lint test(s) failed. Review the log carefully to see full listing." | ||||||
|  |   exit 1 | ||||||
|  | else | ||||||
|  |   echo "✔ Linting passed" | ||||||
|  |   exit 0 | ||||||
|  | fi | ||||||
|  |  | ||||||
|  | @ -28,9 +28,9 @@ HOOK_DATA = <<~HOOK | ||||||
| 
 | 
 | ||||||
|   if [ -e "$harness" ] |   if [ -e "$harness" ] | ||||||
|   then |   then | ||||||
|     if [[ ("$url" != *"dev.gitlab.org"*) && ("$url" != *"gitlab-org/security/"*) ]] |     if [["$url" != *"gitlab-org/security/"*]] | ||||||
|     then |     then | ||||||
|       echo "Pushing to remotes other than dev.gitlab.org and gitlab.com/gitlab-org/security has been disabled!" |       echo "Pushing to remotes other than gitlab.com/gitlab-org/security has been disabled!" | ||||||
|       echo "Run scripts/security-harness to disable this check." |       echo "Run scripts/security-harness to disable this check." | ||||||
|       echo |       echo | ||||||
| 
 | 
 | ||||||
|  | @ -58,7 +58,7 @@ def toggle | ||||||
|   else |   else | ||||||
|     FileUtils.touch(harness_path) |     FileUtils.touch(harness_path) | ||||||
| 
 | 
 | ||||||
|     puts "#{SHELL_GREEN}Security harness installed -- you will only be able to push to dev.gitlab.org or gitlab.com/gitlab-org/security!#{SHELL_CLEAR}" |     puts "#{SHELL_GREEN}Security harness installed -- you will only be able to push to gitlab.com/gitlab-org/security!#{SHELL_CLEAR}" | ||||||
|   end |   end | ||||||
| end | end | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -0,0 +1,35 @@ | ||||||
|  | # frozen_string_literal: true | ||||||
|  | 
 | ||||||
|  | require 'spec_helper' | ||||||
|  | 
 | ||||||
|  | describe 'IDE Clientside Preview CSP' do | ||||||
|  |   let_it_be(:user) { create(:user) } | ||||||
|  | 
 | ||||||
|  |   shared_context 'disable feature' do | ||||||
|  |     before do | ||||||
|  |       allow_next_instance_of(ApplicationSetting) do |instance| | ||||||
|  |         allow(instance).to receive(:web_ide_clientside_preview_enabled?).and_return(false) | ||||||
|  |       end | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  | 
 | ||||||
|  |   it_behaves_like 'setting CSP', 'frame-src' do | ||||||
|  |     let(:whitelisted_url) { 'https://sandbox.gitlab-static.test' } | ||||||
|  |     let(:extended_controller_class) { IdeController } | ||||||
|  | 
 | ||||||
|  |     subject do | ||||||
|  |       visit ide_path | ||||||
|  | 
 | ||||||
|  |       response_headers['Content-Security-Policy'] | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|  |     before do | ||||||
|  |       allow_next_instance_of(ApplicationSetting) do |instance| | ||||||
|  |         allow(instance).to receive(:web_ide_clientside_preview_enabled?).and_return(true) | ||||||
|  |         allow(instance).to receive(:web_ide_clientside_preview_bundler_url).and_return(whitelisted_url) | ||||||
|  |       end | ||||||
|  | 
 | ||||||
|  |       sign_in(user) | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  | end | ||||||
|  | @ -11,7 +11,7 @@ describe 'Static Object External Storage Content Security Policy' do | ||||||
|     end |     end | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   it_behaves_like 'setting CSP connect-src' do |   it_behaves_like 'setting CSP', 'connect-src' do | ||||||
|     let_it_be(:whitelisted_url) { 'https://static-objects.test' } |     let_it_be(:whitelisted_url) { 'https://static-objects.test' } | ||||||
|     let_it_be(:extended_controller_class) { IdeController } |     let_it_be(:extended_controller_class) { IdeController } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -12,7 +12,7 @@ describe 'Sourcegraph Content Security Policy' do | ||||||
|     end |     end | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   it_behaves_like 'setting CSP connect-src' do |   it_behaves_like 'setting CSP', 'connect-src' do | ||||||
|     let_it_be(:whitelisted_url) { 'https://sourcegraph.test' } |     let_it_be(:whitelisted_url) { 'https://sourcegraph.test' } | ||||||
|     let_it_be(:extended_controller_class) { Projects::BlobController } |     let_it_be(:extended_controller_class) { Projects::BlobController } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1,6 +1,7 @@ | ||||||
| import { shallowMount } from '@vue/test-utils'; | import { shallowMount } from '@vue/test-utils'; | ||||||
| import Popover from '~/blob/suggest_gitlab_ci_yml/components/popover.vue'; | import Popover from '~/blob/suggest_gitlab_ci_yml/components/popover.vue'; | ||||||
| import Cookies from 'js-cookie'; | import Cookies from 'js-cookie'; | ||||||
|  | import { mockTracking, unmockTracking } from 'helpers/tracking_helper'; | ||||||
| import * as utils from '~/lib/utils/common_utils'; | import * as utils from '~/lib/utils/common_utils'; | ||||||
| 
 | 
 | ||||||
| jest.mock('~/lib/utils/common_utils', () => ({ | jest.mock('~/lib/utils/common_utils', () => ({ | ||||||
|  | @ -11,6 +12,8 @@ jest.mock('~/lib/utils/common_utils', () => ({ | ||||||
| const target = 'gitlab-ci-yml-selector'; | const target = 'gitlab-ci-yml-selector'; | ||||||
| const dismissKey = 'suggest_gitlab_ci_yml_99'; | const dismissKey = 'suggest_gitlab_ci_yml_99'; | ||||||
| const defaultTrackLabel = 'suggest_gitlab_ci_yml'; | const defaultTrackLabel = 'suggest_gitlab_ci_yml'; | ||||||
|  | const commitTrackLabel = 'suggest_commit_first_project_gitlab_ci_yml'; | ||||||
|  | const humanAccess = 'owner'; | ||||||
| 
 | 
 | ||||||
| describe('Suggest gitlab-ci.yml Popover', () => { | describe('Suggest gitlab-ci.yml Popover', () => { | ||||||
|   let wrapper; |   let wrapper; | ||||||
|  | @ -21,6 +24,7 @@ describe('Suggest gitlab-ci.yml Popover', () => { | ||||||
|         target, |         target, | ||||||
|         trackLabel, |         trackLabel, | ||||||
|         dismissKey, |         dismissKey, | ||||||
|  |         humanAccess, | ||||||
|       }, |       }, | ||||||
|     }); |     }); | ||||||
|   } |   } | ||||||
|  | @ -50,15 +54,43 @@ describe('Suggest gitlab-ci.yml Popover', () => { | ||||||
|       expect(wrapper.vm.popoverDismissed).toEqual(true); |       expect(wrapper.vm.popoverDismissed).toEqual(true); | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|     beforeEach(() => { |     afterEach(() => { | ||||||
|       Cookies.remove(dismissKey); |       Cookies.remove(dismissKey); | ||||||
|     }); |     }); | ||||||
|   }); |   }); | ||||||
| 
 | 
 | ||||||
|  |   describe('tracking', () => { | ||||||
|  |     let trackingSpy; | ||||||
|  | 
 | ||||||
|  |     beforeEach(() => { | ||||||
|  |       createWrapper(commitTrackLabel); | ||||||
|  |       trackingSpy = mockTracking('_category_', wrapper.element, jest.spyOn); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     afterEach(() => { | ||||||
|  |       unmockTracking(); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     it('sends a tracking event with the expected properties for the popover being viewed', () => { | ||||||
|  |       const expectedCategory = undefined; | ||||||
|  |       const expectedAction = undefined; | ||||||
|  |       const expectedLabel = 'suggest_commit_first_project_gitlab_ci_yml'; | ||||||
|  |       const expectedProperty = 'owner'; | ||||||
|  | 
 | ||||||
|  |       document.body.dataset.page = 'projects:blob:new'; | ||||||
|  | 
 | ||||||
|  |       wrapper.vm.trackOnShow(); | ||||||
|  | 
 | ||||||
|  |       expect(trackingSpy).toHaveBeenCalledWith(expectedCategory, expectedAction, { | ||||||
|  |         label: expectedLabel, | ||||||
|  |         property: expectedProperty, | ||||||
|  |       }); | ||||||
|  |     }); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|   describe('when the popover is mounted with the trackLabel of the Confirm button popover at the bottom of the page', () => { |   describe('when the popover is mounted with the trackLabel of the Confirm button popover at the bottom of the page', () => { | ||||||
|     it('calls scrollToElement so that the Confirm button and popover will be in sight', () => { |     it('calls scrollToElement so that the Confirm button and popover will be in sight', () => { | ||||||
|       const scrollToElementSpy = jest.spyOn(utils, 'scrollToElement'); |       const scrollToElementSpy = jest.spyOn(utils, 'scrollToElement'); | ||||||
|       const commitTrackLabel = 'suggest_commit_first_project_gitlab_ci_yml'; |  | ||||||
| 
 | 
 | ||||||
|       createWrapper(commitTrackLabel); |       createWrapper(commitTrackLabel); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -16,6 +16,17 @@ const dummyPackageJson = () => ({ | ||||||
|     main: 'index.js', |     main: 'index.js', | ||||||
|   }), |   }), | ||||||
| }); | }); | ||||||
|  | const expectedSandpackOptions = () => ({ | ||||||
|  |   files: {}, | ||||||
|  |   entry: '/index.js', | ||||||
|  |   showOpenInCodeSandbox: true, | ||||||
|  | }); | ||||||
|  | const expectedSandpackSettings = () => ({ | ||||||
|  |   fileResolver: { | ||||||
|  |     isFile: expect.any(Function), | ||||||
|  |     readFile: expect.any(Function), | ||||||
|  |   }, | ||||||
|  | }); | ||||||
| 
 | 
 | ||||||
| describe('IDE clientside preview', () => { | describe('IDE clientside preview', () => { | ||||||
|   let wrapper; |   let wrapper; | ||||||
|  | @ -84,6 +95,46 @@ describe('IDE clientside preview', () => { | ||||||
|       return waitForCalls(); |       return waitForCalls(); | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|  |     it('creates sandpack manager', () => { | ||||||
|  |       expect(smooshpack.Manager).toHaveBeenCalledWith( | ||||||
|  |         '#ide-preview', | ||||||
|  |         expectedSandpackOptions(), | ||||||
|  |         expectedSandpackSettings(), | ||||||
|  |       ); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     it('pings usage', () => { | ||||||
|  |       expect(storeClientsideActions.pingUsage).toHaveBeenCalledTimes(1); | ||||||
|  |     }); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   describe('with codesandboxBundlerUrl', () => { | ||||||
|  |     const TEST_BUNDLER_URL = 'https://test.gitlab-static.test'; | ||||||
|  | 
 | ||||||
|  |     beforeEach(() => { | ||||||
|  |       createComponent({ | ||||||
|  |         getters: { packageJson: dummyPackageJson }, | ||||||
|  |         state: { codesandboxBundlerUrl: TEST_BUNDLER_URL }, | ||||||
|  |       }); | ||||||
|  | 
 | ||||||
|  |       return waitForCalls(); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     it('creates sandpack manager with bundlerURL', () => { | ||||||
|  |       expect(smooshpack.Manager).toHaveBeenCalledWith('#ide-preview', expectedSandpackOptions(), { | ||||||
|  |         ...expectedSandpackSettings(), | ||||||
|  |         bundlerURL: TEST_BUNDLER_URL, | ||||||
|  |       }); | ||||||
|  |     }); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   describe('with codesandboxBundlerURL', () => { | ||||||
|  |     beforeEach(() => { | ||||||
|  |       createComponent({ getters: { packageJson: dummyPackageJson } }); | ||||||
|  | 
 | ||||||
|  |       return waitForCalls(); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|     it('creates sandpack manager', () => { |     it('creates sandpack manager', () => { | ||||||
|       expect(smooshpack.Manager).toHaveBeenCalledWith( |       expect(smooshpack.Manager).toHaveBeenCalledWith( | ||||||
|         '#ide-preview', |         '#ide-preview', | ||||||
|  | @ -100,10 +151,6 @@ describe('IDE clientside preview', () => { | ||||||
|         }, |         }, | ||||||
|       ); |       ); | ||||||
|     }); |     }); | ||||||
| 
 |  | ||||||
|     it('pings usage', () => { |  | ||||||
|       expect(storeClientsideActions.pingUsage).toHaveBeenCalledTimes(1); |  | ||||||
|     }); |  | ||||||
|   }); |   }); | ||||||
| 
 | 
 | ||||||
|   describe('computed', () => { |   describe('computed', () => { | ||||||
|  |  | ||||||
|  | @ -480,8 +480,8 @@ describe WikiPage do | ||||||
| 
 | 
 | ||||||
|     let(:untitled_page) { described_class.new(wiki) } |     let(:untitled_page) { described_class.new(wiki) } | ||||||
|     let(:directory_page) do |     let(:directory_page) do | ||||||
|       create_page('parent/child', 'test content') |       create_page('parent directory/child page', 'test content') | ||||||
|       wiki.find_page('parent/child') |       wiki.find_page('parent directory/child page') | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|     where(:page, :title, :changed) do |     where(:page, :title, :changed) do | ||||||
|  | @ -494,15 +494,25 @@ describe WikiPage do | ||||||
| 
 | 
 | ||||||
|       :existing_page  | nil                             | false |       :existing_page  | nil                             | false | ||||||
|       :existing_page  | 'test page'                     | false |       :existing_page  | 'test page'                     | false | ||||||
|  |       :existing_page  | 'test-page'                     | false | ||||||
|       :existing_page  | '/test page'                    | false |       :existing_page  | '/test page'                    | false | ||||||
|  |       :existing_page  | '/test-page'                    | false | ||||||
|  |       :existing_page  | ' test page '                   | true | ||||||
|       :existing_page  | 'new title'                     | true |       :existing_page  | 'new title'                     | true | ||||||
|  |       :existing_page  | 'new-title'                     | true | ||||||
| 
 | 
 | ||||||
|       :directory_page | nil                             | false |       :directory_page | nil                             | false | ||||||
|       :directory_page | 'parent/child' | false |       :directory_page | 'parent directory/child page'   | false | ||||||
|       :directory_page | 'child'        | false |       :directory_page | 'parent-directory/child page'   | false | ||||||
|       :directory_page | '/child'       | true |       :directory_page | 'parent-directory/child-page'   | false | ||||||
|       :directory_page | 'parent/other' | true |       :directory_page | 'child page'                    | false | ||||||
|       :directory_page | 'other/child'  | true |       :directory_page | 'child-page'                    | false | ||||||
|  |       :directory_page | '/child page'                   | true | ||||||
|  |       :directory_page | 'parent directory/other'        | true | ||||||
|  |       :directory_page | 'parent-directory/other'        | true | ||||||
|  |       :directory_page | 'parent-directory / child-page' | true | ||||||
|  |       :directory_page | 'other directory/child page'    | true | ||||||
|  |       :directory_page | 'other-directory/child page'    | true | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|     with_them do |     with_them do | ||||||
|  |  | ||||||
|  | @ -1,6 +1,6 @@ | ||||||
| # frozen_string_literal: true | # frozen_string_literal: true | ||||||
| 
 | 
 | ||||||
| RSpec.shared_examples 'setting CSP connect-src' do | RSpec.shared_examples 'setting CSP' do |rule_name| | ||||||
|   let_it_be(:default_csp_values) { "'self' https://some-cdn.test" } |   let_it_be(:default_csp_values) { "'self' https://some-cdn.test" } | ||||||
| 
 | 
 | ||||||
|   shared_context 'csp config' do |csp_rule| |   shared_context 'csp config' do |csp_rule| | ||||||
|  | @ -10,7 +10,7 @@ RSpec.shared_examples 'setting CSP connect-src' do | ||||||
|       end |       end | ||||||
| 
 | 
 | ||||||
|       expect_next_instance_of(extended_controller_class) do |controller| |       expect_next_instance_of(extended_controller_class) do |controller| | ||||||
|         expect(controller).to receive(:current_content_security_policy).and_return(csp) |         expect(controller).to receive(:current_content_security_policy).at_least(:once).and_return(csp) | ||||||
|       end |       end | ||||||
|     end |     end | ||||||
|   end |   end | ||||||
|  | @ -23,55 +23,55 @@ RSpec.shared_examples 'setting CSP connect-src' do | ||||||
|     end |     end | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   describe 'when a CSP config exists for connect-src' do |   describe "when a CSP config exists for #{rule_name}" do | ||||||
|     include_context 'csp config', :connect_src |     include_context 'csp config', rule_name.parameterize.underscore.to_sym | ||||||
| 
 | 
 | ||||||
|     context 'when feature is enabled' do |     context 'when feature is enabled' do | ||||||
|       it 'appends to connect-src' do |       it "appends to #{rule_name}" do | ||||||
|         is_expected.to eql("connect-src #{default_csp_values} #{whitelisted_url}") |         is_expected.to eql("#{rule_name} #{default_csp_values} #{whitelisted_url}") | ||||||
|       end |       end | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|     context 'when feature is disabled' do |     context 'when feature is disabled' do | ||||||
|       include_context 'disable feature' |       include_context 'disable feature' | ||||||
| 
 | 
 | ||||||
|       it 'keeps original connect-src' do |       it "keeps original #{rule_name}" do | ||||||
|         is_expected.to eql("connect-src #{default_csp_values}") |         is_expected.to eql("#{rule_name} #{default_csp_values}") | ||||||
|       end |       end | ||||||
|     end |     end | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   describe 'when a CSP config exists for default-src but not connect-src' do |   describe "when a CSP config exists for default-src but not #{rule_name}" do | ||||||
|     include_context 'csp config', :default_src |     include_context 'csp config', :default_src | ||||||
| 
 | 
 | ||||||
|     context 'when feature is enabled' do |     context 'when feature is enabled' do | ||||||
|       it 'uses default-src values in connect-src' do |       it "uses default-src values in #{rule_name}" do | ||||||
|         is_expected.to eql("default-src #{default_csp_values}; connect-src #{default_csp_values} #{whitelisted_url}") |         is_expected.to eql("default-src #{default_csp_values}; #{rule_name} #{default_csp_values} #{whitelisted_url}") | ||||||
|       end |       end | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|     context 'when feature is disabled' do |     context 'when feature is disabled' do | ||||||
|       include_context 'disable feature' |       include_context 'disable feature' | ||||||
| 
 | 
 | ||||||
|       it 'does not add connect-src' do |       it "does not add #{rule_name}" do | ||||||
|         is_expected.to eql("default-src #{default_csp_values}") |         is_expected.to eql("default-src #{default_csp_values}") | ||||||
|       end |       end | ||||||
|     end |     end | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   describe 'when a CSP config exists for font-src but not connect-src' do |   describe "when a CSP config exists for font-src but not #{rule_name}" do | ||||||
|     include_context 'csp config', :font_src |     include_context 'csp config', :font_src | ||||||
| 
 | 
 | ||||||
|     context 'when feature is enabled' do |     context 'when feature is enabled' do | ||||||
|       it 'uses default-src values in connect-src' do |       it "uses default-src values in #{rule_name}" do | ||||||
|         is_expected.to eql("font-src #{default_csp_values}; connect-src #{whitelisted_url}") |         is_expected.to eql("font-src #{default_csp_values}; #{rule_name} #{whitelisted_url}") | ||||||
|       end |       end | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|     context 'when feature is disabled' do |     context 'when feature is disabled' do | ||||||
|       include_context 'disable feature' |       include_context 'disable feature' | ||||||
| 
 | 
 | ||||||
|       it 'does not add connect-src' do |       it "does not add #{rule_name}" do | ||||||
|         is_expected.to eql("font-src #{default_csp_values}") |         is_expected.to eql("font-src #{default_csp_values}") | ||||||
|       end |       end | ||||||
|     end |     end | ||||||
|  |  | ||||||
							
								
								
									
										18
									
								
								yarn.lock
								
								
								
								
							
							
						
						
									
										18
									
								
								yarn.lock
								
								
								
								
							|  | @ -2779,10 +2779,10 @@ code-point-at@^1.0.0: | ||||||
|   resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" |   resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" | ||||||
|   integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= |   integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= | ||||||
| 
 | 
 | ||||||
| codesandbox-api@^0.0.20: | codesandbox-api@0.0.23: | ||||||
|   version "0.0.20" |   version "0.0.23" | ||||||
|   resolved "https://registry.yarnpkg.com/codesandbox-api/-/codesandbox-api-0.0.20.tgz#174bcd76c9f31521175c6bceabc37da6b1fbc30b" |   resolved "https://registry.yarnpkg.com/codesandbox-api/-/codesandbox-api-0.0.23.tgz#bf650a21b5f3c2369e03f0c19d10b4e2ba255b4f" | ||||||
|   integrity sha512-jhxZzAmjCKBZad8QWMeueiQVFE87igK6F2DBOEVFFJO6jgTXT8qjuzGYepr+B8bjgo/icN7bc/2xmEMBA63s2w== |   integrity sha512-fFGBkIghDkQILh7iHYlpZU5sfWncCDb92FQSFE4rR3VBcTfUsD5VZgpQi+JjZQuwWIdfl4cOhcIFrUYwshUezA== | ||||||
| 
 | 
 | ||||||
| codesandbox-import-util-types@^1.2.11: | codesandbox-import-util-types@^1.2.11: | ||||||
|   version "1.2.11" |   version "1.2.11" | ||||||
|  | @ -10334,12 +10334,12 @@ slugify@^1.3.1: | ||||||
|   resolved "https://registry.yarnpkg.com/slugify/-/slugify-1.3.1.tgz#f572127e8535329fbc6c1edb74ab856b61ad7de2" |   resolved "https://registry.yarnpkg.com/slugify/-/slugify-1.3.1.tgz#f572127e8535329fbc6c1edb74ab856b61ad7de2" | ||||||
|   integrity sha512-6BwyhjF5tG5P8s+0DPNyJmBSBePG6iMyhjvIW5zGdA3tFik9PtK+yNkZgTeiroCRGZYgkHftFA62tGVK1EI9Kw== |   integrity sha512-6BwyhjF5tG5P8s+0DPNyJmBSBePG6iMyhjvIW5zGdA3tFik9PtK+yNkZgTeiroCRGZYgkHftFA62tGVK1EI9Kw== | ||||||
| 
 | 
 | ||||||
| smooshpack@^0.0.54: | smooshpack@^0.0.62: | ||||||
|   version "0.0.54" |   version "0.0.62" | ||||||
|   resolved "https://registry.yarnpkg.com/smooshpack/-/smooshpack-0.0.54.tgz#9044358b85052d348b801f385678c8a0c76f2bb6" |   resolved "https://registry.yarnpkg.com/smooshpack/-/smooshpack-0.0.62.tgz#cb31b9f808f73de3146b050f84d044eb353b5503" | ||||||
|   integrity sha512-yIwEWb17hqoW5IaWyzO6O6nxY89I5UdRoGIZy5hihoqXP9OYcoMbBTxKwS57MeXSKdNA2rtk86rlCcOgAYIgrA== |   integrity sha512-lFuJV2f504/U78sifWy0V2FyoE/8mTgOXM4DL918ncNxAxbtu236XSCLAH3SQwXZWn0JdmRnWs/XU4+sIUVVmQ== | ||||||
|   dependencies: |   dependencies: | ||||||
|     codesandbox-api "^0.0.20" |     codesandbox-api "0.0.23" | ||||||
|     codesandbox-import-utils "^1.2.3" |     codesandbox-import-utils "^1.2.3" | ||||||
|     lodash.isequal "^4.5.0" |     lodash.isequal "^4.5.0" | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue