Commit b2822028 authored by Kushal Pandya's avatar Kushal Pandya

Merge branch '198604-monaco-snippets' into 'master'

Replace ACE with Monaco for Snippets editing/creation

Closes #198604

See merge request gitlab-org/gitlab!25465
parents b340fa7b 6832090c
/* global ace */
import $ from 'jquery';
import Editor from '~/editor/editor_lite';
import setupCollapsibleInputs from './collapsible_input';
export default () => {
const editor = ace.edit('editor');
let editor;
const initAce = () => {
editor = ace.edit('editor');
const form = document.querySelector('.snippet-form-holder form');
const content = document.querySelector('.snippet-file-content');
form.addEventListener('submit', () => {
content.value = editor.getValue();
});
};
$('.snippet-form-holder form').on('submit', () => {
$('.snippet-file-content').val(editor.getValue());
const initMonaco = () => {
const editorEl = document.getElementById('editor');
const contentEl = document.querySelector('.snippet-file-content');
const fileNameEl = document.querySelector('.snippet-file-name');
const form = document.querySelector('.snippet-form-holder form');
editor = new Editor();
editor.createInstance({
el: editorEl,
blobPath: fileNameEl.value,
blobContent: contentEl.value,
});
fileNameEl.addEventListener('change', () => {
editor.updateModelLanguage(fileNameEl.value);
});
form.addEventListener('submit', () => {
contentEl.value = editor.getValue();
});
};
export const initEditor = () => {
if (window?.gon?.features?.monacoSnippets) {
initMonaco();
} else {
initAce();
}
setupCollapsibleInputs();
};
export default () => {
initEditor();
};
......@@ -28,7 +28,7 @@
.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 snippet-file-name qa-snippet-file-name'
.file-content.code
%pre#editor= @snippet.content
%pre#editor{ data: { 'editor-loading': true } }= @snippet.content
= f.hidden_field :content, class: 'snippet-file-content'
.form-group
......
---
title: Replaced ACE with Monaco editor for Snippets
merge_request: 25465
author:
type: added
......@@ -52,7 +52,7 @@ module QA
private
def text_area
find('#editor>textarea', visible: false)
find('#editor textarea', visible: false)
end
end
end
......
......@@ -2,11 +2,10 @@
require 'spec_helper'
describe 'Projects > Snippets > Create Snippet', :js do
include DropzoneHelper
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project, :public) }
shared_examples_for 'snippet editor' do
before do
stub_feature_flags(monaco_snippets: flag)
end
def description_field
find('.js-description-input').find('input,textarea')
......@@ -20,7 +19,8 @@ describe 'Projects > Snippets > Create Snippet', :js do
fill_in 'project_snippet_description', with: 'My Snippet **Description**'
page.within('.file-editor') do
find('.ace_text-input', visible: false).send_keys('Hello World!')
el = flag == true ? find('.inputarea') : find('.ace_text-input', visible: false)
el.send_keys 'Hello World!'
end
end
......@@ -33,6 +33,7 @@ describe 'Projects > Snippets > Create Snippet', :js do
visit project_snippets_path(project)
click_on('New snippet')
wait_for_requests
end
it 'shows collapsible description input' do
......@@ -111,3 +112,22 @@ describe 'Projects > Snippets > Create Snippet', :js do
end
end
end
describe 'Projects > Snippets > Create Snippet', :js do
include DropzoneHelper
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project, :public) }
context 'when using Monaco' do
it_behaves_like "snippet editor" do
let(:flag) { true }
end
end
context 'when using ACE' do
it_behaves_like "snippet editor" do
let(:flag) { false }
end
end
end
......@@ -2,9 +2,7 @@
require 'spec_helper'
describe 'User creates snippet', :js do
let(:user) { create(:user) }
shared_examples_for 'snippet editor' do
def description_field
find('.js-description-input').find('input,textarea')
end
......@@ -12,6 +10,7 @@ describe 'User creates snippet', :js do
before do
stub_feature_flags(allow_possible_spam: false)
stub_feature_flags(snippets_vue: false)
stub_feature_flags(monaco_snippets: flag)
stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false')
Gitlab::CurrentSettings.update!(
......@@ -33,7 +32,8 @@ describe 'User creates snippet', :js do
find('#personal_snippet_visibility_level_20').set(true)
page.within('.file-editor') do
find('.ace_text-input', visible: false).send_keys 'Hello World!'
el = flag == true ? find('.inputarea') : find('.ace_text-input', visible: false)
el.send_keys 'Hello World!'
end
end
......@@ -80,3 +80,19 @@ describe 'User creates snippet', :js do
end
end
end
describe 'User creates snippet', :js do
let_it_be(:user) { create(:user) }
context 'when using Monaco' do
it_behaves_like "snippet editor" do
let(:flag) { true }
end
end
context 'when using ACE' do
it_behaves_like "snippet editor" do
let(:flag) { false }
end
end
end
......@@ -2,13 +2,10 @@
require 'spec_helper'
describe 'User creates snippet', :js do
include DropzoneHelper
let(:user) { create(:user) }
shared_examples_for 'snippet editor' do
before do
stub_feature_flags(snippets_vue: false)
stub_feature_flags(monaco_snippets: flag)
sign_in(user)
visit new_snippet_path
end
......@@ -25,7 +22,8 @@ describe 'User creates snippet', :js do
fill_in 'personal_snippet_description', with: 'My Snippet **Description**'
page.within('.file-editor') do
find('.ace_text-input', visible: false).send_keys 'Hello World!'
el = flag == true ? find('.inputarea') : find('.ace_text-input', visible: false)
el.send_keys 'Hello World!'
end
end
......@@ -109,7 +107,8 @@ describe 'User creates snippet', :js do
fill_in 'personal_snippet_title', with: 'My Snippet Title'
page.within('.file-editor') do
find(:xpath, "//input[@id='personal_snippet_file_name']").set 'snippet+file+name'
find('.ace_text-input', visible: false).send_keys 'Hello World!'
el = flag == true ? find('.inputarea') : find('.ace_text-input', visible: false)
el.send_keys 'Hello World!'
end
click_button 'Create snippet'
......@@ -120,3 +119,21 @@ describe 'User creates snippet', :js do
expect(page).to have_content('Hello World!')
end
end
describe 'User creates snippet', :js do
include DropzoneHelper
let_it_be(:user) { create(:user) }
context 'when using Monaco' do
it_behaves_like "snippet editor" do
let(:flag) { true }
end
end
context 'when using ACE' do
it_behaves_like "snippet editor" do
let(:flag) { false }
end
end
end
import Editor from '~/editor/editor_lite';
import { initEditor } from '~/snippet/snippet_bundle';
import { setHTMLFixture } from 'helpers/fixtures';
jest.mock('~/editor/editor_lite', () => jest.fn());
describe('Snippet editor', () => {
describe('Monaco editor for Snippets', () => {
let oldGon;
let editorEl;
let contentEl;
let fileNameEl;
let form;
const mockName = 'foo.bar';
const mockContent = 'Foo Bar';
const updatedMockContent = 'New Foo Bar';
const mockEditor = {
createInstance: jest.fn(),
updateModelLanguage: jest.fn(),
getValue: jest.fn().mockReturnValueOnce(updatedMockContent),
};
Editor.mockImplementation(() => mockEditor);
function setUpFixture(name, content) {
setHTMLFixture(`
<div class="snippet-form-holder">
<form>
<input class="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('.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(() => {
oldGon = window.gon;
window.gon = { features: { monacoSnippets: true } };
bootstrap(mockName, mockContent);
});
afterEach(() => {
window.gon = oldGon;
});
it('correctly initializes Editor', () => {
expect(mockEditor.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);
});
});
});
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