Commit b33510a2 authored by Jose Ivan Vargas's avatar Jose Ivan Vargas

Merge branch '220797-remove-snippets-edit-vue-feature-flag' into 'master'

Remove snippets_edit_vue feature flag

See merge request gitlab-org/gitlab!43868
parents c5e00ece e0532966
import { initEditorLite } from '~/blob/utils';
import setupCollapsibleInputs from './collapsible_input';
let editor;
const initMonaco = () => {
const editorEl = document.getElementById('editor');
const contentEl = document.querySelector('.snippet-file-content');
const fileNameEl = document.querySelector('.js-snippet-file-name');
const form = document.querySelector('.snippet-form-holder form');
editor = initEditorLite({
el: editorEl,
blobPath: fileNameEl.value,
blobContent: contentEl.value,
});
fileNameEl.addEventListener('change', () => {
editor.updateModelLanguage(fileNameEl.value);
});
form.addEventListener('submit', () => {
contentEl.value = editor.getValue();
});
};
export default () => {
initMonaco();
setupCollapsibleInputs();
};
import $ from 'jquery';
import initSnippet from '~/snippet/snippet_bundle';
import ZenMode from '~/zen_mode'; import ZenMode from '~/zen_mode';
import GLForm from '~/gl_form';
import { SnippetEditInit } from '~/snippets'; import { SnippetEditInit } from '~/snippets';
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
const form = document.querySelector('.snippet-form'); SnippetEditInit();
const personalSnippetOptions = {
members: false,
issues: false,
mergeRequests: false,
epics: false,
milestones: false,
labels: false,
snippets: false,
vulnerabilities: false,
};
const projectSnippetOptions = {};
const options =
form.dataset.snippetType === 'project' || form.dataset.projectPath
? projectSnippetOptions
: personalSnippetOptions;
if (gon?.features?.snippetsEditVue) {
SnippetEditInit();
} else {
initSnippet();
new GLForm($(form), options); // eslint-disable-line no-new
}
new ZenMode(); // eslint-disable-line no-new new ZenMode(); // eslint-disable-line no-new
}); });
...@@ -112,12 +112,6 @@ export default { ...@@ -112,12 +112,6 @@ export default {
} }
return this.snippet.webUrl; return this.snippet.webUrl;
}, },
titleFieldId() {
return `${this.isProjectSnippet ? 'project' : 'personal'}_snippet_title`;
},
descriptionFieldId() {
return `${this.isProjectSnippet ? 'project' : 'personal'}_snippet_description`;
},
newSnippetSchema() { newSnippetSchema() {
return { return {
title: '', title: '',
...@@ -228,14 +222,13 @@ export default { ...@@ -228,14 +222,13 @@ export default {
/> />
<template v-else> <template v-else>
<title-field <title-field
:id="titleFieldId" id="snippet-title"
v-model="snippet.title" v-model="snippet.title"
data-qa-selector="snippet_title_field" data-qa-selector="snippet_title_field"
required required
:autofocus="true" :autofocus="true"
/> />
<snippet-description-edit <snippet-description-edit
:id="descriptionFieldId"
v-model="snippet.description" v-model="snippet.description"
:markdown-preview-path="markdownPreviewPath" :markdown-preview-path="markdownPreviewPath"
:markdown-docs-path="markdownDocsPath" :markdown-docs-path="markdownDocsPath"
......
...@@ -18,10 +18,6 @@ ...@@ -18,10 +18,6 @@
} }
} }
.snippet-form-holder .file-holder .file-title {
padding: 2px;
}
.markdown-snippet-copy { .markdown-snippet-copy {
position: fixed; position: fixed;
top: -10px; top: -10px;
......
- if Feature.enabled?(:snippets_edit_vue, default_enabled: true) - available_visibility_levels = available_visibility_levels(@snippet)
- available_visibility_levels = available_visibility_levels(@snippet) #js-snippet-edit.snippet-form{ data: {'project_path': @snippet.project&.full_path, 'snippet-gid': @snippet.new_record? ? '' : @snippet.to_global_id, 'markdown-preview-path': preview_markdown_path(parent), 'markdown-docs-path': help_page_path('user/markdown'), 'visibility-help-link': help_page_path("public_access/public_access"), 'visibility_levels': available_visibility_levels, 'selected_level': snippets_selected_visibility_level(available_visibility_levels, @snippet.visibility_level), 'multiple_levels_restricted': multiple_visibility_levels_restricted? } }
#js-snippet-edit.snippet-form{ data: {'project_path': @snippet.project&.full_path, 'snippet-gid': @snippet.new_record? ? '' : @snippet.to_global_id, 'markdown-preview-path': preview_markdown_path(parent), 'markdown-docs-path': help_page_path('user/markdown'), 'visibility-help-link': help_page_path("public_access/public_access"), 'visibility_levels': available_visibility_levels, 'selected_level': snippets_selected_visibility_level(available_visibility_levels, @snippet.visibility_level), 'multiple_levels_restricted': multiple_visibility_levels_restricted? } }
- else
.snippet-form-holder
= form_for @snippet, url: url,
html: { class: "snippet-form js-requires-input js-quick-submit common-note-form" },
data: { "snippet-type": @snippet.project_id ? 'project' : 'personal'} do |f|
= form_errors(@snippet)
.form-group
= f.label :title, class: 'label-bold'
= f.text_field :title, class: 'form-control', required: true, autofocus: true
.form-group.js-description-input
- description_placeholder = s_('Snippets|Optionally add a description about what your snippet does or how to use it...')
- is_expanded = @snippet.description && !@snippet.description.empty?
= f.label :description, s_("Snippets|Description (optional)"), class: 'label-bold'
.js-collapsible-input
.js-collapsed{ class: ('d-none' if is_expanded) }
= text_field_tag nil, nil, class: 'form-control', placeholder: description_placeholder
.js-expanded{ class: ('d-none' if !is_expanded) }
= render layout: 'shared/md_preview', locals: { url: preview_markdown_path(@project), referenced_users: true } do
= render 'shared/zen', f: f, attr: :description, classes: 'note-textarea', placeholder: description_placeholder
= render 'shared/notes/hints'
.form-group.file-editor
= f.label :file_name, s_('Snippets|File')
.file-holder.snippet
.js-file-title.file-title-flex-parent
= f.text_field :file_name, placeholder: s_("Snippets|Give your file a name to add code highlighting, e.g. example.rb for Ruby"), class: 'form-control js-snippet-file-name'
.file-content.code
#editor{ data: { 'editor-loading': true } }<
%pre.editor-loading-content= @snippet.content
= f.hidden_field :content, class: 'snippet-file-content'
.form-group
.font-weight-bold
= _('Visibility level')
= link_to sprite_icon('question-o'), help_page_path('public_access/public_access'), target: '_blank'
= render 'shared/visibility_level', f: f, visibility_level: @snippet.visibility_level, can_change_visibility_level: true, form_model: @snippet, with_label: false
- if params[:files]
- params[:files].each_with_index do |file, index|
= hidden_field_tag "files[]", file, id: "files_#{index}"
.form-actions
- if @snippet.new_record?
= f.submit 'Create snippet', class: "btn-success btn gl-button"
- else
= f.submit 'Save changes', class: "btn-success btn gl-button"
- if @snippet.project_id
= link_to "Cancel", project_snippets_path(@project), class: "btn gl-button btn-default"
- else
= link_to "Cancel", snippets_path(@project), class: "btn gl-button btn-default"
---
name: snippets_edit_vue
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/25667
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/207239
group: group::editor
type: development
default_enabled: true
...@@ -46,7 +46,6 @@ module Gitlab ...@@ -46,7 +46,6 @@ module Gitlab
push_frontend_feature_flag(:snippets_vue, default_enabled: true) push_frontend_feature_flag(:snippets_vue, default_enabled: true)
push_frontend_feature_flag(:monaco_blobs, default_enabled: true) push_frontend_feature_flag(:monaco_blobs, default_enabled: true)
push_frontend_feature_flag(:monaco_ci, default_enabled: true) push_frontend_feature_flag(:monaco_ci, default_enabled: true)
push_frontend_feature_flag(:snippets_edit_vue, default_enabled: true)
push_frontend_feature_flag(:webperf_experiment, default_enabled: false) push_frontend_feature_flag(:webperf_experiment, default_enabled: false)
push_frontend_feature_flag(:snippets_binary_blob, default_enabled: false) push_frontend_feature_flag(:snippets_binary_blob, default_enabled: false)
push_frontend_feature_flag(:usage_data_api, default_enabled: false) push_frontend_feature_flag(:usage_data_api, default_enabled: false)
......
...@@ -23976,18 +23976,12 @@ msgstr "" ...@@ -23976,18 +23976,12 @@ msgstr ""
msgid "Snippets|Description (optional)" msgid "Snippets|Description (optional)"
msgstr "" msgstr ""
msgid "Snippets|File"
msgstr ""
msgid "Snippets|Files" msgid "Snippets|Files"
msgstr "" msgstr ""
msgid "Snippets|Give your file a name to add code highlighting, e.g. example.rb for Ruby" msgid "Snippets|Give your file a name to add code highlighting, e.g. example.rb for Ruby"
msgstr "" msgstr ""
msgid "Snippets|Optionally add a description about what your snippet does or how to use it..."
msgstr ""
msgid "Snippets|Optionally add a description about what your snippet does or how to use it…" msgid "Snippets|Optionally add a description about what your snippet does or how to use it…"
msgstr "" msgstr ""
......
...@@ -17,115 +17,81 @@ RSpec.describe 'Projects > Snippets > Create Snippet', :js do ...@@ -17,115 +17,81 @@ RSpec.describe 'Projects > Snippets > Create Snippet', :js do
let(:file_content) { 'Hello World!' } let(:file_content) { 'Hello World!' }
let(:md_description) { 'My Snippet **Description**' } let(:md_description) { 'My Snippet **Description**' }
let(:description) { 'My Snippet Description' } let(:description) { 'My Snippet Description' }
let(:snippet_title_field) { 'project_snippet_title' }
shared_examples 'snippet creation' do def fill_form
def fill_form snippet_fill_in_form(title: title, content: file_content, description: md_description)
snippet_fill_in_form(title: title, content: file_content, description: md_description) end
end
it 'shows collapsible description input' do
collapsed = description_field
expect(page).not_to have_field(snippet_description_field) before do
expect(collapsed).to be_visible sign_in(user)
collapsed.click visit new_project_snippet_path(project)
end
expect(page).to have_field(snippet_description_field) it 'shows collapsible description input' do
expect(collapsed).not_to be_visible collapsed = snippet_description_field_collapsed
end
it 'creates a new snippet' do expect(page).not_to have_field(snippet_description_locator)
fill_form expect(collapsed).to be_visible
click_button('Create snippet')
wait_for_requests
expect(page).to have_content(title) collapsed.click
expect(page).to have_content(file_content)
page.within(snippet_description_view_selector) do
expect(page).to have_content(description)
expect(page).to have_selector('strong')
end
end
it 'uploads a file when dragging into textarea' do expect(page).to have_field(snippet_description_locator)
fill_form expect(collapsed).not_to be_visible
dropzone_file Rails.root.join('spec', 'fixtures', 'banana_sample.gif') end
expect(snippet_description_value).to have_content('banana_sample')
click_button('Create snippet') it 'creates a new snippet' do
wait_for_requests fill_form
click_button('Create snippet')
wait_for_requests
link = find('a.no-attachment-icon img[alt="banana_sample"]')['src'] expect(page).to have_content(title)
expect(link).to match(%r{/#{Regexp.escape(project.full_path)}/uploads/\h{32}/banana_sample\.gif\z}) expect(page).to have_content(file_content)
page.within('.snippet-header .snippet-description') do
expect(page).to have_content(description)
expect(page).to have_selector('strong')
end end
end
context 'when the git operation fails' do it 'uploads a file when dragging into textarea' do
let(:error) { 'Error creating the snippet' } fill_form
dropzone_file Rails.root.join('spec', 'fixtures', 'banana_sample.gif')
before do
allow_next_instance_of(Snippets::CreateService) do |instance|
allow(instance).to receive(:create_commit).and_raise(StandardError, error)
end
fill_form expect(snippet_description_value).to have_content('banana_sample')
click_button('Create snippet') click_button('Create snippet')
wait_for_requests wait_for_requests
end
it 'renders the new page and displays the error' do link = find('a.no-attachment-icon img[alt="banana_sample"]')['src']
expect(page).to have_content(error) expect(link).to match(%r{/#{Regexp.escape(project.full_path)}/uploads/\h{32}/banana_sample\.gif\z})
expect(page).to have_content('New Snippet')
end
end
end end
context 'Vue application' do context 'when the git operation fails' do
let(:snippet_description_field) { 'snippet-description' } let(:error) { 'Error creating the snippet' }
let(:snippet_description_view_selector) { '.snippet-header .snippet-description' }
before do before do
sign_in(user) allow_next_instance_of(Snippets::CreateService) do |instance|
allow(instance).to receive(:create_commit).and_raise(StandardError, error)
visit new_project_snippet_path(project) end
end
it_behaves_like 'snippet creation'
it 'does not allow submitting the form without title and content' do
fill_in snippet_title_field, with: title
expect(page).not_to have_button('Create snippet') fill_form
snippet_fill_in_form(title: title, content: file_content) click_button('Create snippet')
expect(page).to have_button('Create snippet') wait_for_requests
end end
end
context 'non-Vue application' do
let(:snippet_description_field) { 'project_snippet_description' }
let(:snippet_description_view_selector) { '.snippet-header .description' }
before do
stub_feature_flags(snippets_vue: false)
stub_feature_flags(snippets_edit_vue: false)
sign_in(user)
visit new_project_snippet_path(project) it 'renders the new page and displays the error' do
expect(page).to have_content(error)
expect(page).to have_content('New Snippet')
end end
end
it_behaves_like 'snippet creation' it 'does not allow submitting the form without title and content' do
snippet_fill_in_title(title)
it 'displays validation errors' do expect(page).not_to have_button('Create snippet')
fill_in snippet_title_field, with: title
click_button('Create snippet')
wait_for_requests
expect(page).to have_selector('#error_explanation') snippet_fill_in_form(title: title, content: file_content)
end expect(page).to have_button('Create snippet')
end end
end end
...@@ -9,9 +9,7 @@ RSpec.describe 'Projects > Snippets > User updates a snippet', :js do ...@@ -9,9 +9,7 @@ RSpec.describe 'Projects > Snippets > User updates a snippet', :js do
let_it_be(:project) { create(:project, namespace: user.namespace) } let_it_be(:project) { create(:project, namespace: user.namespace) }
let_it_be(:snippet, reload: true) { create(:project_snippet, :repository, project: project, author: user) } let_it_be(:snippet, reload: true) { create(:project_snippet, :repository, project: project, author: user) }
let(:snippet_title_field) { 'project_snippet_title' } before do
def bootstrap_snippet
project.add_maintainer(user) project.add_maintainer(user)
sign_in(user) sign_in(user)
...@@ -20,64 +18,36 @@ RSpec.describe 'Projects > Snippets > User updates a snippet', :js do ...@@ -20,64 +18,36 @@ RSpec.describe 'Projects > Snippets > User updates a snippet', :js do
wait_for_all_requests wait_for_all_requests
end end
shared_examples 'snippet update' do it 'displays the snippet blob path and content' do
it 'displays the snippet blob path and content' do blob = snippet.blobs.first
blob = snippet.blobs.first
aggregate_failures do
expect(snippet_get_first_blob_path).to eq blob.path
expect(snippet_get_first_blob_value).to have_content(blob.data.strip)
end
end
it 'updates a snippet' do
fill_in('project_snippet_title', with: 'Snippet new title')
click_button('Save')
expect(page).to have_content('Snippet new title') aggregate_failures do
end expect(snippet_get_first_blob_path).to eq blob.path
expect(snippet_get_first_blob_value).to have_content(blob.data.strip)
context 'when the git operation fails' do
before do
allow_next_instance_of(Snippets::UpdateService) do |instance|
allow(instance).to receive(:create_commit).and_raise(StandardError, 'Error Message')
end
fill_in(snippet_title_field, with: 'Snippet new title')
fill_in(snippet_blob_path_field, match: :first, with: 'new_file_name')
click_button('Save')
end
it 'renders edit page and displays the error' do
expect(page.find('.flash-container')).to have_content('Error updating the snippet - Error Message')
expect(page).to have_content('Edit Snippet')
end
end end
end end
context 'Vue application' do it 'updates a snippet' do
before do fill_in('snippet-title', with: 'Snippet new title')
bootstrap_snippet click_button('Save')
end
it_behaves_like 'snippet update' do expect(page).to have_content('Snippet new title')
let(:snippet_blob_path_field) { 'snippet_file_name' }
let(:snippet_blob_content_selector) { '.file-content' }
end
end end
context 'non-Vue application' do context 'when the git operation fails' do
before do before do
stub_feature_flags(snippets_vue: false) allow_next_instance_of(Snippets::UpdateService) do |instance|
stub_feature_flags(snippets_edit_vue: false) allow(instance).to receive(:create_commit).and_raise(StandardError, 'Error Message')
end
bootstrap_snippet snippet_fill_in_form(title: 'Snippet new title', file_name: 'new_file_name')
click_button('Save')
end end
it_behaves_like 'snippet update' do it 'renders edit page and displays the error' do
let(:snippet_blob_path_field) { 'project_snippet_file_name' } expect(page.find('.flash-container')).to have_content('Error updating the snippet - Error Message')
let(:snippet_blob_content_selector) { '.file-content' } expect(page).to have_content('Edit Snippet')
end end
end end
end end
...@@ -2,9 +2,11 @@ ...@@ -2,9 +2,11 @@
require 'spec_helper' require 'spec_helper'
RSpec.shared_examples_for 'snippet editor' do RSpec.describe 'snippet editor with spam', skip: "Will be handled in https://gitlab.com/gitlab-org/gitlab/-/issues/217722" do
include_context 'includes Spam constants' include_context 'includes Spam constants'
let_it_be(:user) { create(:user) }
def description_field def description_field
find('.js-description-input').find('input,textarea') find('.js-description-input').find('input,textarea')
end end
...@@ -119,24 +121,3 @@ RSpec.shared_examples_for 'snippet editor' do ...@@ -119,24 +121,3 @@ RSpec.shared_examples_for 'snippet editor' do
end end
end end
end end
RSpec.describe 'User creates snippet', :js do
let_it_be(:user) { create(:user) }
context 'Vue application' do
before do
stub_feature_flags(snippets_edit_vue: false)
end
it_behaves_like "snippet editor"
end
context 'non-Vue application' do
before do
stub_feature_flags(snippets_vue: false)
stub_feature_flags(snippets_edit_vue: false)
end
it_behaves_like "snippet editor"
end
end
...@@ -13,163 +13,127 @@ RSpec.describe 'User creates snippet', :js do ...@@ -13,163 +13,127 @@ RSpec.describe 'User creates snippet', :js do
let(:md_description) { 'My Snippet **Description**' } let(:md_description) { 'My Snippet **Description**' }
let(:description) { 'My Snippet Description' } let(:description) { 'My Snippet Description' }
let(:created_snippet) { Snippet.last } let(:created_snippet) { Snippet.last }
let(:snippet_title_field) { 'personal_snippet_title' } let(:snippet_title_field) { 'snippet-title' }
def description_field before do
find('.js-description-input').find('input,textarea') sign_in(user)
visit new_snippet_path
end end
shared_examples 'snippet creation' do def fill_form
def fill_form snippet_fill_in_form(title: title, content: file_content, description: md_description)
snippet_fill_in_form(title: title, content: file_content, description: md_description) end
end
it 'Authenticated user creates a snippet' do it 'Authenticated user creates a snippet' do
fill_form fill_form
click_button('Create snippet') click_button('Create snippet')
wait_for_requests wait_for_requests
expect(page).to have_content(title) expect(page).to have_content(title)
page.within(snippet_description_view_selector) do page.within(snippet_description_view_selector) do
expect(page).to have_content(description) expect(page).to have_content(description)
expect(page).to have_selector('strong') expect(page).to have_selector('strong')
end
expect(page).to have_content(file_content)
end end
expect(page).to have_content(file_content)
end
it 'uploads a file when dragging into textarea' do it 'uploads a file when dragging into textarea' do
fill_form fill_form
dropzone_file Rails.root.join('spec', 'fixtures', 'banana_sample.gif') dropzone_file Rails.root.join('spec', 'fixtures', 'banana_sample.gif')
expect(snippet_description_value).to have_content('banana_sample')
click_button('Create snippet')
wait_for_requests
link = find('a.no-attachment-icon img.js-lazy-loaded[alt="banana_sample"]')['src']
expect(link).to match(%r{/uploads/-/system/personal_snippet/#{Snippet.last.id}/\h{32}/banana_sample\.gif\z})
reqs = inspect_requests { visit("#{link}?ran=#{SecureRandom.base64(20)}") } expect(snippet_description_value).to have_content('banana_sample')
expect(reqs.first.status_code).to eq(200)
end
context 'when the git operation fails' do click_button('Create snippet')
let(:error) { 'Error creating the snippet' } wait_for_requests
before do link = find('a.no-attachment-icon img.js-lazy-loaded[alt="banana_sample"]')['src']
allow_next_instance_of(Snippets::CreateService) do |instance| expect(link).to match(%r{/uploads/-/system/personal_snippet/#{Snippet.last.id}/\h{32}/banana_sample\.gif\z})
allow(instance).to receive(:create_commit).and_raise(StandardError, error)
end
fill_form reqs = inspect_requests { visit("#{link}?ran=#{SecureRandom.base64(20)}") }
click_button('Create snippet') expect(reqs.first.status_code).to eq(200)
wait_for_requests end
end
it 'renders the new page and displays the error' do context 'when the git operation fails' do
expect(page).to have_content(error) let(:error) { 'Error creating the snippet' }
expect(page).to have_content('New Snippet')
action = find('form.snippet-form')['action'] before do
expect(action).to include("/snippets") allow_next_instance_of(Snippets::CreateService) do |instance|
end allow(instance).to receive(:create_commit).and_raise(StandardError, error)
end
context 'when snippets default visibility level is restricted' do
before do
stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::PRIVATE],
default_snippet_visibility: Gitlab::VisibilityLevel::PRIVATE)
end end
it 'creates a snippet using the lowest available visibility level as default' do fill_form
visit new_snippet_path click_button('Create snippet')
wait_for_requests
fill_form
click_button('Create snippet')
wait_for_requests
expect(find('.blob-content')).to have_content(file_content)
expect(Snippet.last.visibility_level).to eq(Gitlab::VisibilityLevel::INTERNAL)
end
end end
it_behaves_like 'personal snippet with references' do it 'renders the new page and displays the error' do
let(:container) { snippet_description_view_selector } expect(page).to have_content(error)
let(:md_description) { references } expect(page).to have_content('New Snippet')
subject do action = find('form.snippet-form')['action']
fill_form expect(action).to include("/snippets")
click_button('Create snippet')
wait_for_requests
end
end end
end end
context 'Vue application' do context 'when snippets default visibility level is restricted' do
let(:snippet_description_field) { 'snippet-description' }
let(:snippet_description_view_selector) { '.snippet-header .snippet-description' }
before do before do
sign_in(user) stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::PRIVATE],
default_snippet_visibility: Gitlab::VisibilityLevel::PRIVATE)
visit new_snippet_path
end end
it_behaves_like 'snippet creation' it 'creates a snippet using the lowest available visibility level as default' do
visit new_snippet_path
it 'validation fails for the first time' do fill_form
fill_in snippet_title_field, with: title
expect(page).not_to have_button('Create snippet') click_button('Create snippet')
wait_for_requests
snippet_fill_in_form(title: title, content: file_content) expect(find('.blob-content')).to have_content(file_content)
expect(page).to have_button('Create snippet') expect(Snippet.last.visibility_level).to eq(Gitlab::VisibilityLevel::INTERNAL)
end end
end end
context 'non-Vue application' do it_behaves_like 'personal snippet with references' do
let(:snippet_description_field) { 'personal_snippet_description' } let(:container) { snippet_description_view_selector }
let(:snippet_description_view_selector) { '.snippet-header .description' } let(:md_description) { references }
before do subject do
stub_feature_flags(snippets_vue: false) fill_form
stub_feature_flags(snippets_edit_vue: false) click_button('Create snippet')
sign_in(user)
visit new_snippet_path wait_for_requests
end end
end
it_behaves_like 'snippet creation' it 'validation fails for the first time' do
fill_in snippet_title_field, with: title
it 'validation fails for the first time' do expect(page).not_to have_button('Create snippet')
fill_in snippet_title_field, with: title
click_button('Create snippet')
expect(page).to have_selector('#error_explanation') snippet_fill_in_form(title: title, content: file_content)
end expect(page).to have_button('Create snippet')
end
it 'previews a snippet with file' do it 'previews a snippet with file' do
# Click placeholder first to expand full description field # Click placeholder first to expand full description field
description_field.click snippet_fill_in_description('My Snippet')
fill_in snippet_description_field, with: 'My Snippet' dropzone_file Rails.root.join('spec', 'fixtures', 'banana_sample.gif')
dropzone_file Rails.root.join('spec', 'fixtures', 'banana_sample.gif') find('.js-md-preview-button').click
find('.js-md-preview-button').click
page.within('.md-preview-holder') do page.within('.md-preview-holder') do
expect(page).to have_content('My Snippet') expect(page).to have_content('My Snippet')
link = find('a.no-attachment-icon img.js-lazy-loaded[alt="banana_sample"]')['src'] link = find('a.no-attachment-icon img.js-lazy-loaded[alt="banana_sample"]')['src']
expect(link).to match(%r{/uploads/-/system/user/#{user.id}/\h{32}/banana_sample\.gif\z}) expect(link).to match(%r{/uploads/-/system/user/#{user.id}/\h{32}/banana_sample\.gif\z})
# Adds a cache buster for checking if the image exists as Selenium is now handling the cached requests # Adds a cache buster for checking if the image exists as Selenium is now handling the cached requests
# not anymore as requests when they come straight from memory cache. # not anymore as requests when they come straight from memory cache.
reqs = inspect_requests { visit("#{link}?ran=#{SecureRandom.base64(20)}") } # accept_confirm is needed because of https://gitlab.com/gitlab-org/gitlab/-/issues/262102
expect(reqs.first.status_code).to eq(200) reqs = inspect_requests { accept_confirm { visit("#{link}?ran=#{SecureRandom.base64(20)}") } }
end expect(reqs.first.status_code).to eq(200)
end end
end end
end end
...@@ -11,107 +11,77 @@ RSpec.describe 'User edits snippet', :js do ...@@ -11,107 +11,77 @@ RSpec.describe 'User edits snippet', :js do
let_it_be(:user) { create(:user) } let_it_be(:user) { create(:user) }
let_it_be(:snippet, reload: true) { create(:personal_snippet, :repository, :public, file_name: file_name, content: content, author: user) } let_it_be(:snippet, reload: true) { create(:personal_snippet, :repository, :public, file_name: file_name, content: content, author: user) }
let(:snippet_title_field) { 'personal_snippet_title' } before do
sign_in(user)
shared_examples 'snippet editing' do visit edit_snippet_path(snippet)
it 'displays the snippet blob path and content' do wait_for_all_requests
blob = snippet.blobs.first end
aggregate_failures do
expect(snippet_get_first_blob_path).to eq blob.path
expect(snippet_get_first_blob_value).to have_content(blob.data.strip)
end
end
it 'updates the snippet' do
fill_in snippet_title_field, with: 'New Snippet Title'
click_button('Save changes')
wait_for_requests
expect(page).to have_content('New Snippet Title')
end
it 'updates the snippet with files attached' do
dropzone_file Rails.root.join('spec', 'fixtures', 'banana_sample.gif')
expect(snippet_description_value).to have_content('banana_sample')
click_button('Save changes') it 'displays the snippet blob path and content' do
wait_for_requests blob = snippet.blobs.first
link = find('a.no-attachment-icon img:not(.lazy)[alt="banana_sample"]')['src'] aggregate_failures do
expect(link).to match(%r{/uploads/-/system/personal_snippet/#{snippet.id}/\h{32}/banana_sample\.gif\z}) expect(snippet_get_first_blob_path).to eq blob.path
expect(snippet_get_first_blob_value).to have_content(blob.data.strip)
end end
end
it 'updates the snippet to make it internal' do it 'updates the snippet' do
choose 'Internal' snippet_fill_in_title('New Snippet Title')
expect(page).not_to have_selector('.gl-spinner')
click_button 'Save changes'
wait_for_requests
expect(page).to have_no_selector('[data-testid="lock-icon"]') click_button('Save changes')
expect(page).to have_selector('[data-testid="shield-icon"]') wait_for_requests
end
it 'updates the snippet to make it public' do expect(page).to have_content('New Snippet Title')
choose 'Public' end
click_button 'Save changes' it 'updates the snippet with files attached' do
wait_for_requests dropzone_file Rails.root.join('spec', 'fixtures', 'banana_sample.gif')
expect(snippet_description_value).to have_content('banana_sample')
expect(page).to have_no_selector('[data-testid="lock-icon"]') click_button('Save changes')
expect(page).to have_selector('[data-testid="earth-icon"]') wait_for_requests
end
context 'when the git operation fails' do link = find('a.no-attachment-icon img:not(.lazy)[alt="banana_sample"]')['src']
before do expect(link).to match(%r{/uploads/-/system/personal_snippet/#{snippet.id}/\h{32}/banana_sample\.gif\z})
allow_next_instance_of(Snippets::UpdateService) do |instance| end
allow(instance).to receive(:create_commit).and_raise(StandardError, 'Error Message')
end
fill_in snippet_title_field, with: 'New Snippet Title' it 'updates the snippet to make it internal' do
fill_in snippet_blob_path_field, with: 'new_file_name', match: :first snippet_fill_in_visibility('Internal')
click_button('Save changes') click_button 'Save changes'
end wait_for_requests
it 'renders edit page and displays the error' do expect(page).to have_no_selector('[data-testid="lock-icon"]')
expect(page.find('.flash-container')).to have_content('Error updating the snippet - Error Message') expect(page).to have_selector('[data-testid="shield-icon"]')
expect(page).to have_content('Edit Snippet')
end
end
end end
context 'Vue application' do it 'updates the snippet to make it public' do
it_behaves_like 'snippet editing' do snippet_fill_in_visibility('Public')
let(:snippet_blob_path_field) { 'snippet_file_name' }
let(:snippet_blob_content_selector) { '.file-content' }
let(:snippet_description_field) { 'snippet-description' }
before do click_button 'Save changes'
sign_in(user) wait_for_requests
visit edit_snippet_path(snippet) expect(page).to have_no_selector('[data-testid="lock-icon"]')
wait_for_all_requests expect(page).to have_selector('[data-testid="earth-icon"]')
end
end
end end
context 'non-Vue application' do context 'when the git operation fails' do
it_behaves_like 'snippet editing' do before do
let(:snippet_blob_path_field) { 'personal_snippet_file_name' } allow_next_instance_of(Snippets::UpdateService) do |instance|
let(:snippet_blob_content_selector) { '.file-content' } allow(instance).to receive(:create_commit).and_raise(StandardError, 'Error Message')
let(:snippet_description_field) { 'personal_snippet_description' } end
before do snippet_fill_in_form(title: 'New Snippet Title', file_name: 'new_file_name')
stub_feature_flags(snippets_vue: false)
stub_feature_flags(snippets_edit_vue: false)
sign_in(user) click_button('Save changes')
end
visit edit_snippet_path(snippet) it 'renders edit page and displays the error' do
wait_for_all_requests expect(page.find('.flash-container')).to have_content('Error updating the snippet - Error Message')
end expect(page).to have_content('Edit Snippet')
end end
end end
end end
import { setHTMLFixture } from 'helpers/fixtures';
import Editor from '~/editor/editor_lite';
import initEditor from '~/snippet/snippet_bundle';
jest.mock('~/editor/editor_lite', () => jest.fn());
describe('Snippet editor', () => {
let editorEl;
let contentEl;
let fileNameEl;
let form;
const mockName = 'foo.bar';
const mockContent = 'Foo Bar';
const updatedMockContent = 'New Foo Bar';
const mockEditor = {
updateModelLanguage: jest.fn(),
getValue: jest.fn().mockReturnValueOnce(updatedMockContent),
};
const createInstance = jest.fn().mockImplementation(() => ({ ...mockEditor }));
Editor.mockImplementation(() => ({
createInstance,
}));
function setUpFixture(name, content) {
setHTMLFixture(`
<div class="snippet-form-holder">
<form>
<input class="js-snippet-file-name" type="text" value="${name}">
<input class="snippet-file-content" type="hidden" value="${content}">
<pre id="editor"></pre>
</form>
</div>
`);
}
function bootstrap(name = '', content = '') {
setUpFixture(name, content);
editorEl = document.getElementById('editor');
contentEl = document.querySelector('.snippet-file-content');
fileNameEl = document.querySelector('.js-snippet-file-name');
form = document.querySelector('.snippet-form-holder form');
initEditor();
}
function createEvent(name) {
return new Event(name, {
view: window,
bubbles: true,
cancelable: true,
});
}
beforeEach(() => {
bootstrap(mockName, mockContent);
});
it('correctly initializes Editor', () => {
expect(createInstance).toHaveBeenCalledWith({
el: editorEl,
blobPath: mockName,
blobContent: mockContent,
});
});
it('listens to file name changes and updates syntax highlighting of code', () => {
expect(mockEditor.updateModelLanguage).not.toHaveBeenCalled();
const event = createEvent('change');
fileNameEl.value = updatedMockContent;
fileNameEl.dispatchEvent(event);
expect(mockEditor.updateModelLanguage).toHaveBeenCalledWith(updatedMockContent);
});
it('listens to form submit event and populates the hidden field with most recent version of the content', () => {
expect(contentEl.value).toBe(mockContent);
const event = createEvent('submit');
form.dispatchEvent(event);
expect(contentEl.value).toBe(updatedMockContent);
});
});
import '~/snippet/snippet_edit'; import '~/snippet/snippet_edit';
import { triggerDOMEvent } from 'jest/helpers/dom_events_helper'; import { triggerDOMEvent } from 'jest/helpers/dom_events_helper';
import { SnippetEditInit } from '~/snippets'; import { SnippetEditInit } from '~/snippets';
import initSnippet from '~/snippet/snippet_bundle';
jest.mock('~/snippet/snippet_bundle');
jest.mock('~/snippets'); jest.mock('~/snippets');
jest.mock('~/gl_form'); jest.mock('~/gl_form');
describe('Snippet edit form initialization', () => { describe('Snippet edit form initialization', () => {
const setFF = flag => {
gon.features = { snippetsEditVue: flag };
};
let features;
beforeEach(() => { beforeEach(() => {
features = gon.features;
setFixtures('<div class="snippet-form"></div>'); setFixtures('<div class="snippet-form"></div>');
}); });
afterEach(() => { it('correctly initializes Vue Snippet Edit form', () => {
gon.features = features;
});
it.each`
name | flag | isVue
${'Regular'} | ${false} | ${false}
${'Vue'} | ${true} | ${true}
`('correctly initializes $name Snippet Edit form', ({ flag, isVue }) => {
initSnippet.mockClear();
SnippetEditInit.mockClear(); SnippetEditInit.mockClear();
setFF(flag);
triggerDOMEvent('DOMContentLoaded'); triggerDOMEvent('DOMContentLoaded');
if (isVue) { expect(SnippetEditInit).toHaveBeenCalled();
expect(initSnippet).not.toHaveBeenCalled();
expect(SnippetEditInit).toHaveBeenCalled();
} else {
expect(initSnippet).toHaveBeenCalled();
expect(SnippetEditInit).not.toHaveBeenCalled();
}
}); });
}); });
...@@ -10,39 +10,69 @@ module Spec ...@@ -10,39 +10,69 @@ module Spec
include ActionView::Helpers::JavaScriptHelper include ActionView::Helpers::JavaScriptHelper
include Spec::Support::Helpers::Features::EditorLiteSpecHelpers include Spec::Support::Helpers::Features::EditorLiteSpecHelpers
def snippet_description_locator
'snippet-description'
end
def snippet_blob_path_locator
'snippet_file_name'
end
def snippet_description_view_selector
'.snippet-header .snippet-description'
end
def snippet_description_field_collapsed
find('.js-description-input').find('input,textarea')
end
def snippet_get_first_blob_path def snippet_get_first_blob_path
page.find_field(snippet_blob_path_field, match: :first).value page.find_field('snippet_file_name', match: :first).value
end end
def snippet_get_first_blob_value def snippet_get_first_blob_value
page.find(snippet_blob_content_selector, match: :first) page.find('.file-content', match: :first)
end end
def snippet_description_value def snippet_description_value
page.find_field(snippet_description_field).value page.find_field(snippet_description_locator).value
end
def snippet_fill_in_visibility(text)
page.find('#visibility-level-setting').choose(text)
end end
def snippet_fill_in_form(title:, content:, description: '') def snippet_fill_in_title(value)
# fill_in snippet_title_field, with: title fill_in 'snippet-title', with: value
# editor_set_value(content) end
fill_in snippet_title_field, with: title
if description def snippet_fill_in_description(value)
# Click placeholder first to expand full description field # Click placeholder first to expand full description field
description_field.click snippet_description_field_collapsed.click
fill_in snippet_description_field, with: description fill_in snippet_description_locator, with: value
end end
def snippet_fill_in_content(value)
page.within('.file-editor') do page.within('.file-editor') do
el = find('.inputarea') el = find('.inputarea')
el.send_keys content el.send_keys value
end end
end end
private def snippet_fill_in_file_name(value)
fill_in(snippet_blob_path_locator, match: :first, with: value)
end
def snippet_fill_in_form(title: nil, content: nil, file_name: nil, description: nil, visibility: nil)
snippet_fill_in_title(title) if title
def description_field snippet_fill_in_description(description) if description
find('.js-description-input').find('input,textarea')
snippet_fill_in_file_name(file_name) if file_name
snippet_fill_in_content(content) if content
snippet_fill_in_visibility(visibility) if visibility
end end
end end
end end
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment