Commit acd5c23c authored by Enrique Alcántara's avatar Enrique Alcántara

Merge branch '196055-wiki-vue' into 'master'

Migrate wiki edit page to vue

See merge request gitlab-org/gitlab!55936
parents 033e84c6 0a3412ee
<script>
import { GlForm, GlIcon, GlLink, GlButton, GlSprintf } from '@gitlab/ui';
import csrf from '~/lib/utils/csrf';
import { setUrlFragment } from '~/lib/utils/url_utility';
import { __, s__, sprintf } from '~/locale';
import MarkdownField from '~/vue_shared/components/markdown/field.vue';
const MARKDOWN_LINK_TEXT = {
markdown: '[Link Title](page-slug)',
rdoc: '{Link title}[link:page-slug]',
asciidoc: 'link:page-slug[Link title]',
org: '[[page-slug]]',
};
export default {
components: {
GlForm,
GlSprintf,
GlIcon,
GlLink,
GlButton,
MarkdownField,
},
inject: ['formatOptions', 'pageInfo'],
data() {
return {
title: this.pageInfo.title?.trim() || '',
format: this.pageInfo.format || 'markdown',
content: this.pageInfo.content?.trim() || '',
commitMessage: '',
};
},
computed: {
csrfToken() {
return csrf.token;
},
formAction() {
return this.pageInfo.persisted ? this.pageInfo.path : this.pageInfo.createPath;
},
helpPath() {
return setUrlFragment(
this.pageInfo.helpPath,
this.pageInfo.persisted ? 'moving-a-wiki-page' : 'creating-a-new-wiki-page',
);
},
commitMessageI18n() {
return this.pageInfo.persisted
? s__('WikiPage|Update %{pageTitle}')
: s__('WikiPage|Create %{pageTitle}');
},
linkExample() {
return MARKDOWN_LINK_TEXT[this.format];
},
submitButtonText() {
if (this.pageInfo.persisted) return __('Save changes');
return s__('WikiPage|Create page');
},
cancelFormPath() {
if (this.pageInfo.persisted) return this.pageInfo.path;
return this.pageInfo.wikiPath;
},
wikiSpecificMarkdownHelpPath() {
return setUrlFragment(this.pageInfo.markdownHelpPath, 'wiki-specific-markdown');
},
},
mounted() {
this.updateCommitMessage();
},
methods: {
handleFormSubmit() {
window.removeEventListener('beforeunload', this.onBeforeUnload);
},
handleContentChange() {
window.addEventListener('beforeunload', this.onBeforeUnload);
},
onBeforeUnload() {
return '';
},
updateCommitMessage() {
if (!this.title) return;
// Replace hyphens with spaces
const newTitle = this.title.replace(/-+/g, ' ');
const newCommitMessage = sprintf(this.commitMessageI18n, { pageTitle: newTitle }, false);
this.commitMessage = newCommitMessage;
},
},
};
</script>
<template>
<gl-form
:action="formAction"
method="post"
class="wiki-form common-note-form gl-mt-3 js-quick-submit"
@submit="handleFormSubmit"
>
<input :value="csrfToken" type="hidden" name="authenticity_token" />
<input v-if="pageInfo.persisted" type="hidden" name="_method" value="put" />
<input
:v-if="pageInfo.persisted"
type="hidden"
name="wiki[last_commit_sha]"
:value="pageInfo.lastCommitSha"
/>
<div class="form-group row">
<div class="col-sm-2 col-form-label">
<label class="control-label-full-width" for="wiki_title">{{ s__('WikiPage|Title') }}</label>
</div>
<div class="col-sm-10">
<input
id="wiki_title"
v-model.trim="title"
name="wiki[title]"
type="text"
class="form-control"
data-qa-selector="wiki_title_textbox"
:required="true"
:autofocus="!pageInfo.persisted"
:placeholder="s__('WikiPage|Page title')"
@input="updateCommitMessage"
/>
<span class="gl-display-inline-block gl-max-w-full gl-mt-2 gl-text-gray-600">
<gl-icon class="gl-mr-n1" name="bulb" />
{{
pageInfo.persisted
? s__(
'WikiPage|Tip: You can move this page by adding the path to the beginning of the title.',
)
: s__(
'WikiPage|Tip: You can specify the full path for the new file. We will automatically create any missing directories.',
)
}}
<gl-link :href="helpPath" target="_blank" data-testid="wiki-title-help-link"
><gl-icon name="question-o" /> {{ __('More Information.') }}</gl-link
>
</span>
</div>
</div>
<div class="form-group row">
<div class="col-sm-2 col-form-label">
<label class="control-label-full-width" for="wiki_format">{{
s__('WikiPage|Format')
}}</label>
</div>
<div class="col-sm-10">
<select id="wiki_format" v-model="format" class="form-control" name="wiki[format]">
<option v-for="(key, label) of formatOptions" :key="key" :value="key">
{{ label }}
</option>
</select>
</div>
</div>
<div class="form-group row">
<div class="col-sm-2 col-form-label">
<label class="control-label-full-width" for="wiki_content">{{
s__('WikiPage|Content')
}}</label>
</div>
<div class="col-sm-10">
<markdown-field
:markdown-preview-path="pageInfo.markdownPreviewPath"
:can-attach-file="true"
:enable-autocomplete="true"
:textarea-value="content"
:markdown-docs-path="pageInfo.markdownHelpPath"
:uploads-path="pageInfo.uploadsPath"
class="bordered-box"
>
<template #textarea>
<textarea
id="wiki_content"
ref="textarea"
v-model.trim="content"
name="wiki[content]"
class="note-textarea js-gfm-input js-autosize markdown-area"
dir="auto"
data-supports-quick-actions="false"
data-qa-selector="wiki_content_textarea"
:autofocus="pageInfo.persisted"
:aria-label="s__('WikiPage|Content')"
:placeholder="s__('WikiPage|Write your content or drag files here…')"
@input="handleContentChange"
>
</textarea>
</template>
</markdown-field>
<div class="clearfix"></div>
<div class="error-alert"></div>
<div class="form-text gl-text-gray-600">
<gl-sprintf
:message="
s__(
'WikiPage|To link to a (new) page, simply type %{linkExample}. More examples are in the %{linkStart}documentation%{linkEnd}.',
)
"
>
<template #linkExample
><code>{{ linkExample }}</code></template
>
<template
#link="// eslint-disable-next-line vue/no-template-shadow
{ content }"
><gl-link
:href="wikiSpecificMarkdownHelpPath"
target="_blank"
data-testid="wiki-markdown-help-link"
>{{ content }}</gl-link
></template
>
</gl-sprintf>
</div>
</div>
</div>
<div class="form-group row">
<div class="col-sm-2 col-form-label">
<label class="control-label-full-width" for="wiki_message">{{
s__('WikiPage|Commit message')
}}</label>
</div>
<div class="col-sm-10">
<input
id="wiki_message"
v-model.trim="commitMessage"
name="wiki[message]"
type="text"
class="form-control"
data-qa-selector="wiki_message_textbox"
:placeholder="s__('WikiPage|Commit message')"
/>
</div>
</div>
<div class="form-actions">
<gl-button
category="primary"
variant="confirm"
type="submit"
data-qa-selector="wiki_submit_button"
data-testid="wiki-submit-button"
:disabled="!content || !title"
>{{ submitButtonText }}</gl-button
>
<gl-button :href="cancelFormPath" class="float-right" data-testid="wiki-cancel-button">{{
__('Cancel')
}}</gl-button>
</div>
</gl-form>
</template>
import $ from 'jquery';
import Vue from 'vue';
import ShortcutsWiki from '~/behaviors/shortcuts/shortcuts_wiki';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
import csrf from '~/lib/utils/csrf';
import Translate from '~/vue_shared/translate';
import GLForm from '../../../gl_form';
import ZenMode from '../../../zen_mode';
import deleteWikiModal from './components/delete_wiki_modal.vue';
import wikiAlert from './components/wiki_alert.vue';
import wikiForm from './components/wiki_form.vue';
import Wikis from './wikis';
const createModalVueApp = () => {
......@@ -61,7 +63,28 @@ const createAlertVueApp = () => {
}
};
const createWikiFormApp = () => {
const el = document.getElementById('js-wiki-form');
if (el) {
const { pageInfo, formatOptions } = el.dataset;
// eslint-disable-next-line no-new
new Vue({
el,
provide: {
formatOptions: JSON.parse(formatOptions),
pageInfo: convertObjectPropsToCamelCase(JSON.parse(pageInfo)),
},
render(createElement) {
return createElement(wikiForm);
},
});
}
};
export default () => {
createModalVueApp();
createAlertVueApp();
createWikiFormApp();
};
import { GlBreakpointInstance as bp } from '@gitlab/ui/dist/utils';
import { s__, sprintf } from '~/locale';
import Tracking from '~/tracking';
import showToast from '~/vue_shared/plugins/global_toast';
const MARKDOWN_LINK_TEXT = {
markdown: '[Link Title](page-slug)',
rdoc: '{Link title}[link:page-slug]',
asciidoc: 'link:page-slug[Link title]',
org: '[[page-slug]]',
};
const TRACKING_EVENT_NAME = 'view_wiki_page';
const TRACKING_CONTEXT_SCHEMA = 'iglu:com.gitlab/wiki_page_context/jsonschema/1-0-1';
......@@ -23,78 +15,11 @@ export default class Wikis {
sidebarToggles[i].addEventListener('click', (e) => this.handleToggleSidebar(e));
}
this.isNewWikiPage = Boolean(document.querySelector('.js-new-wiki-page'));
this.editTitleInput = document.querySelector('form.wiki-form #wiki_title');
this.commitMessageInput = document.querySelector('form.wiki-form #wiki_message');
this.submitButton = document.querySelector('.js-wiki-btn-submit');
this.commitMessageI18n = this.isNewWikiPage
? s__('WikiPageCreate|Create %{pageTitle}')
: s__('WikiPageEdit|Update %{pageTitle}');
if (this.editTitleInput) {
// Initialize the commit message on load
if (this.editTitleInput.value) this.setWikiCommitMessage(this.editTitleInput.value);
// Set the commit message as the page title is changed
this.editTitleInput.addEventListener('keyup', (e) => this.handleWikiTitleChange(e));
}
window.addEventListener('resize', () => this.renderSidebar());
this.renderSidebar();
const changeFormatSelect = document.querySelector('#wiki_format');
const linkExample = document.querySelector('.js-markup-link-example');
if (changeFormatSelect) {
changeFormatSelect.addEventListener('change', (e) => {
linkExample.innerHTML = MARKDOWN_LINK_TEXT[e.target.value];
});
}
this.wikiTextarea = document.querySelector('form.wiki-form #wiki_content');
const wikiForm = document.querySelector('form.wiki-form');
if (this.wikiTextarea) {
this.wikiTextarea.addEventListener('input', () => this.handleWikiContentChange());
wikiForm.addEventListener('submit', () => {
window.onbeforeunload = null;
});
}
Wikis.trackPageView();
Wikis.showToasts();
this.updateSubmitButton();
}
handleWikiContentChange() {
this.updateSubmitButton();
window.onbeforeunload = () => '';
}
handleWikiTitleChange(e) {
this.updateSubmitButton();
this.setWikiCommitMessage(e.target.value);
}
updateSubmitButton() {
if (!this.wikiTextarea) return;
const isEnabled = Boolean(this.wikiTextarea.value.trim() && this.editTitleInput.value.trim());
if (isEnabled) this.submitButton.removeAttribute('disabled');
else this.submitButton.setAttribute('disabled', 'true');
}
setWikiCommitMessage(rawTitle) {
let title = rawTitle;
// Replace hyphens with spaces
if (title) title = title.replace(/-+/g, ' ');
const newCommitMessage = sprintf(this.commitMessageI18n, { pageTitle: title }, false);
this.commitMessageInput.value = newCommitMessage;
}
handleToggleSidebar(e) {
......
......@@ -62,6 +62,11 @@ export default {
required: false,
default: true,
},
uploadsPath: {
type: String,
required: false,
default: '',
},
enableAutocomplete: {
type: Boolean,
required: false,
......@@ -229,6 +234,7 @@ export default {
ref="gl-form"
:class="{ 'gl-mt-3 gl-mb-3': addSpacingClasses }"
class="js-vue-markdown-field md-area position-relative gfm-form"
:data-uploads-path="uploadsPath"
>
<markdown-header
:preview-markdown="previewMarkdown"
......
- form_classes = %w[wiki-form common-note-form gl-mt-3 js-quick-submit]
- page_info = { last_commit_sha: @page.last_commit_sha, persisted: @page.persisted?, title: @page.title, content: @page.content || '', format: @page.format.to_s, uploads_path: uploads_path, path: wiki_page_path(@wiki, @page), wiki_path: wiki_path(@wiki), help_path: help_page_path('user/project/wiki/index'), markdown_help_path: help_page_path('user/markdown'), markdown_preview_path: wiki_page_path(@wiki, @page, action: :preview_markdown), create_path: wiki_path(@wiki, action: :create) }
- if @page.persisted?
- form_action = wiki_page_path(@wiki, @page)
- form_method = :put
- else
- form_action = wiki_path(@wiki, action: :create)
- form_method = :post
- form_classes << 'js-new-wiki-page'
= form_for @page, url: form_action, method: form_method,
html: { class: form_classes },
data: { uploads_path: uploads_path } do |f|
.gl-mt-3
= form_errors(@page, truncate: :title)
- if @page.persisted?
= f.hidden_field :last_commit_sha, value: @page.last_commit_sha
.form-group.row
.col-sm-2.col-form-label= f.label :title, class: 'control-label-full-width'
.col-sm-10
= f.text_field :title, class: 'form-control qa-wiki-title-textbox', value: @page.title, required: true, autofocus: !@page.persisted?, placeholder: s_('Wiki|Page title')
%span.gl-display-inline-block.gl-max-w-full.gl-mt-2.gl-text-gray-600
= sprite_icon('bulb', size: 12, css_class: 'gl-mr-n1')
- if @page.persisted?
= s_("WikiEditPageTip|Tip: You can move this page by adding the path to the beginning of the title.")
= link_to sprite_icon('question-o'), help_page_path('user/project/wiki/index', anchor: 'moving-a-wiki-page'),
target: '_blank', rel: 'noopener noreferrer'
- else
= s_("WikiNewPageTip|Tip: You can specify the full path for the new file. We will automatically create any missing directories.")
= succeed '.' do
= link_to _('More information'), help_page_path('user/project/wiki/index', anchor: 'creating-a-new-wiki-page'),
target: '_blank', rel: 'noopener noreferrer'
.form-group.row
.col-sm-2.col-form-label= f.label :format, class: 'control-label-full-width'
.col-sm-10
.select-wrapper
= f.select :format, options_for_select(Wiki::MARKUPS, {selected: @page.format}), {}, class: 'form-control select-control'
= sprite_icon('chevron-down', css_class: 'gl-absolute gl-top-3 gl-right-3 gl-text-gray-200')
.form-group.row
.col-sm-2.col-form-label= f.label :content, class: 'control-label-full-width'
.col-sm-10
= render layout: 'shared/md_preview', locals: { url: wiki_page_path(@wiki, @page, action: :preview_markdown) } do
= render 'shared/zen', f: f, attr: :content, classes: 'note-textarea qa-wiki-content-textarea', placeholder: s_("WikiPage|Write your content or drag files here…"), autofocus: @page.persisted?
= render 'shared/notes/hints'
.clearfix
.error-alert
.form-text.gl-text-gray-600
= succeed '.' do
- case @page.format.to_s
- when 'rdoc'
- link_example = '{Link title}[link:page-slug]'
- when 'asciidoc'
- link_example = 'link:page-slug[Link title]'
- when 'org'
- link_example = '[[page-slug]]'
- else
- link_example = '[Link Title](page-slug)'
= html_escape(s_('WikiMarkdownTip|To link to a (new) page, simply type %{link_example}')) % { link_example: tag.code(link_example, class: 'js-markup-link-example') }
= succeed '.' do
- markdown_link = link_to s_("WikiMarkdownDocs|documentation"), help_page_path('user/markdown', anchor: 'wiki-specific-markdown')
= (s_("WikiMarkdownDocs|More examples are in the %{docs_link}") % { docs_link: markdown_link }).html_safe
.form-group.row
.col-sm-2.col-form-label= f.label :commit_message, class: 'control-label-full-width'
.col-sm-10= f.text_field :message, class: 'form-control qa-wiki-message-textbox', rows: 18, value: nil
.form-actions
- if @page && @page.persisted?
= f.submit _("Save changes"), class: 'btn gl-button btn-confirm qa-save-changes-button js-wiki-btn-submit', disabled: 'true'
.float-right
= link_to _("Cancel"), wiki_page_path(@wiki, @page), class: 'btn gl-button btn-cancel btn-default'
- else
= f.submit s_("Wiki|Create page"), class: 'btn-confirm gl-button btn qa-create-page-button rspec-create-page-button js-wiki-btn-submit', disabled: 'true'
.float-right
= link_to _("Cancel"), wiki_path(@wiki), class: 'btn gl-button btn-cancel btn-default'
#js-wiki-form{ data: { page_info: page_info.to_json, format_options: Wiki::MARKUPS.to_json } }
......@@ -19977,6 +19977,9 @@ msgstr ""
msgid "More Information"
msgstr ""
msgid "More Information."
msgstr ""
msgid "More Slack commands"
msgstr ""
......@@ -34256,9 +34259,6 @@ msgstr ""
msgid "WikiClone|Start Gollum and edit locally"
msgstr ""
msgid "WikiEditPageTip|Tip: You can move this page by adding the path to the beginning of the title."
msgstr ""
msgid "WikiEdit|There is already a page with the same title in that path."
msgstr ""
......@@ -34331,34 +34331,49 @@ msgstr ""
msgid "WikiHistoricalPage|most recent version"
msgstr ""
msgid "WikiMarkdownDocs|More examples are in the %{docs_link}"
msgid "WikiPageConfirmDelete|Are you sure you want to delete this page?"
msgstr ""
msgid "WikiMarkdownDocs|documentation"
msgid "WikiPageConfirmDelete|Delete page"
msgstr ""
msgid "WikiMarkdownTip|To link to a (new) page, simply type %{link_example}"
msgid "WikiPageConfirmDelete|Delete page %{pageTitle}?"
msgstr ""
msgid "WikiNewPageTip|Tip: You can specify the full path for the new file. We will automatically create any missing directories."
msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{wikiLinkStart}the page%{wikiLinkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
msgid "WikiPageConfirmDelete|Are you sure you want to delete this page?"
msgid "WikiPage|Commit message"
msgstr ""
msgid "WikiPageConfirmDelete|Delete page"
msgid "WikiPage|Content"
msgstr ""
msgid "WikiPageConfirmDelete|Delete page %{pageTitle}?"
msgid "WikiPage|Create %{pageTitle}"
msgstr ""
msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{wikiLinkStart}the page%{wikiLinkEnd} and make sure your changes will not unintentionally remove theirs."
msgid "WikiPage|Create page"
msgstr ""
msgid "WikiPage|Format"
msgstr ""
msgid "WikiPage|Page title"
msgstr ""
msgid "WikiPage|Tip: You can move this page by adding the path to the beginning of the title."
msgstr ""
msgid "WikiPage|Tip: You can specify the full path for the new file. We will automatically create any missing directories."
msgstr ""
msgid "WikiPageCreate|Create %{pageTitle}"
msgid "WikiPage|Title"
msgstr ""
msgid "WikiPageEdit|Update %{pageTitle}"
msgid "WikiPage|To link to a (new) page, simply type %{linkExample}. More examples are in the %{linkStart}documentation%{linkEnd}."
msgstr ""
msgid "WikiPage|Update %{pageTitle}"
msgstr ""
msgid "WikiPage|Write your content or drag files here…"
......@@ -34370,9 +34385,6 @@ msgstr ""
msgid "Wiki|Create New Page"
msgstr ""
msgid "Wiki|Create page"
msgstr ""
msgid "Wiki|Created date"
msgstr ""
......@@ -34385,9 +34397,6 @@ msgstr ""
msgid "Wiki|Page history"
msgstr ""
msgid "Wiki|Page title"
msgstr ""
msgid "Wiki|Page version"
msgstr ""
......
......@@ -9,12 +9,11 @@ module QA
def self.included(base)
super
base.view 'app/views/shared/wikis/_form.html.haml' do
base.view 'app/assets/javascripts/pages/shared/wikis/components/wiki_form.vue' do
element :wiki_title_textbox
element :wiki_content_textarea
element :wiki_message_textbox
element :save_changes_button
element :create_page_button
element :wiki_submit_button
end
base.view 'app/assets/javascripts/pages/shared/wikis/components/delete_wiki_modal.vue' do
......@@ -34,12 +33,8 @@ module QA
fill_element(:wiki_message_textbox, message)
end
def click_save_changes
click_element(:save_changes_button)
end
def click_create_page
click_element(:create_page_button)
def click_submit
click_element(:wiki_submit_button)
end
def delete_page
......
......@@ -27,7 +27,7 @@ module QA
edit.set_message commit_message
end
Page::Project::Wiki::Edit.perform(&:click_create_page)
Page::Project::Wiki::Edit.perform(&:click_submit)
Page::Project::Wiki::Show.perform do |wiki|
expect(wiki).to have_title new_wiki_title
......@@ -46,7 +46,7 @@ module QA
edit.set_message commit_message
end
Page::Project::Wiki::Edit.perform(&:click_create_page)
Page::Project::Wiki::Edit.perform(&:click_submit)
Page::Project::Wiki::Show.perform do |wiki|
expect(wiki).to have_title new_wiki_title
......
......@@ -25,7 +25,7 @@ module QA
edit.set_message commit_message
end
Page::Project::Wiki::Edit.perform(&:click_save_changes)
Page::Project::Wiki::Edit.perform(&:click_submit)
Page::Project::Wiki::Show.perform do |wiki|
expect(wiki).to have_title new_wiki_title
......
......@@ -20,7 +20,7 @@ module QA
edit.set_message('changing the path of the home page')
end
Page::Project::Wiki::Edit.perform(&:click_save_changes)
Page::Project::Wiki::Edit.perform(&:click_submit)
Page::Project::Wiki::Show.perform do |wiki|
expect(wiki).to have_directory('a')
......
......@@ -23,7 +23,7 @@ module QA
EE::Page::Group::Wiki::Edit.perform do |edit|
edit.set_title(wiki_title)
edit.set_content(wiki_content)
edit.click_create_page
edit.click_submit
end
EE::Page::Group::Wiki::Show.perform do |wiki|
......@@ -48,7 +48,7 @@ module QA
EE::Page::Group::Wiki::Edit.perform do |edit|
edit.set_title(wiki_title)
edit.set_content(wiki_content)
edit.click_create_page
edit.click_submit
end
EE::Page::Group::Wiki::Show.perform do |wiki|
......
import { mount } from '@vue/test-utils';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import WikiForm from '~/pages/shared/wikis/components/wiki_form.vue';
describe('WikiForm', () => {
let wrapper;
const findForm = () => wrapper.find('form');
const findTitle = () => wrapper.find('#wiki_title');
const findFormat = () => wrapper.find('#wiki_format');
const findContent = () => wrapper.find('#wiki_content');
const findMessage = () => wrapper.find('#wiki_message');
const findSubmitButton = () => wrapper.findByTestId('wiki-submit-button');
const findCancelButton = () => wrapper.findByTestId('wiki-cancel-button');
const findTitleHelpLink = () => wrapper.findByTestId('wiki-title-help-link');
const findMarkdownHelpLink = () => wrapper.findByTestId('wiki-markdown-help-link');
const pageInfoNew = {
persisted: false,
uploadsPath: '/project/path/-/wikis/attachments',
wikiPath: '/project/path/-/wikis',
helpPath: '/help/user/project/wiki/index',
markdownHelpPath: '/help/user/markdown',
markdownPreviewPath: '/project/path/-/wikis/.md/preview-markdown',
createPath: '/project/path/-/wikis/new',
};
const pageInfoPersisted = {
...pageInfoNew,
persisted: true,
title: 'My page',
content: 'My page content',
format: 'markdown',
path: '/project/path/-/wikis/home',
};
function createWrapper(persisted = false, pageInfo = {}) {
wrapper = extendedWrapper(
mount(
WikiForm,
{
provide: {
formatOptions: {
Markdown: 'markdown',
RDoc: 'rdoc',
AsciiDoc: 'asciidoc',
Org: 'org',
},
pageInfo: {
...(persisted ? pageInfoPersisted : pageInfoNew),
...pageInfo,
},
},
},
{ attachToDocument: true },
),
);
jest.spyOn(wrapper.vm, 'onBeforeUnload');
}
afterEach(() => {
wrapper.destroy();
wrapper = null;
});
it.each`
title | persisted | message
${'my page'} | ${false} | ${'Create my page'}
${'my-page'} | ${false} | ${'Create my page'}
${'somedir/my-page'} | ${false} | ${'Create somedir/my page'}
${'my-page'} | ${true} | ${'Update my page'}
`(
'updates the commit message to $message when title is $title and persisted=$persisted',
async ({ title, message, persisted }) => {
createWrapper(persisted);
findTitle().setValue(title);
await wrapper.vm.$nextTick();
expect(findMessage().element.value).toBe(message);
},
);
it('sets the commit message to "Update My page" when the page first loads when persisted', async () => {
createWrapper(true);
await wrapper.vm.$nextTick();
expect(findMessage().element.value).toBe('Update My page');
});
it.each`
value | text
${'markdown'} | ${'[Link Title](page-slug)'}
${'rdoc'} | ${'{Link title}[link:page-slug]'}
${'asciidoc'} | ${'link:page-slug[Link title]'}
${'org'} | ${'[[page-slug]]'}
`('updates the link help message when format=$value is selected', async ({ value, text }) => {
createWrapper();
findFormat().find(`option[value=${value}]`).setSelected();
await wrapper.vm.$nextTick();
expect(wrapper.text()).toContain(text);
});
it('starts with no unload warning', async () => {
createWrapper();
await wrapper.vm.$nextTick();
window.dispatchEvent(new Event('beforeunload'));
expect(wrapper.vm.onBeforeUnload).not.toHaveBeenCalled();
});
it.each`
persisted | titleHelpText | titleHelpLink
${true} | ${'You can move this page by adding the path to the beginning of the title.'} | ${'/help/user/project/wiki/index#moving-a-wiki-page'}
${false} | ${'You can specify the full path for the new file. We will automatically create any missing directories.'} | ${'/help/user/project/wiki/index#creating-a-new-wiki-page'}
`(
'shows appropriate title help text and help link for when persisted=$persisted',
async ({ persisted, titleHelpLink, titleHelpText }) => {
createWrapper(persisted);
await wrapper.vm.$nextTick();
expect(wrapper.text()).toContain(titleHelpText);
expect(findTitleHelpLink().attributes().href).toEqual(titleHelpLink);
},
);
it('shows correct link for wiki specific markdown docs', async () => {
createWrapper();
await wrapper.vm.$nextTick();
expect(findMarkdownHelpLink().attributes().href).toEqual(
'/help/user/markdown#wiki-specific-markdown',
);
});
describe('when wiki content is updated', () => {
beforeEach(() => {
createWrapper();
const input = findContent();
input.setValue('Lorem ipsum dolar sit!');
input.element.dispatchEvent(new Event('input'));
return wrapper.vm.$nextTick();
});
it('sets before unload warning', () => {
window.dispatchEvent(new Event('beforeunload'));
expect(wrapper.vm.onBeforeUnload).toHaveBeenCalled();
});
it('when form submitted, unsets before unload warning', async () => {
findForm().element.dispatchEvent(new Event('submit'));
await wrapper.vm.$nextTick();
window.dispatchEvent(new Event('beforeunload'));
expect(wrapper.vm.onBeforeUnload).not.toHaveBeenCalled();
});
});
describe('submit button state', () => {
it.each`
title | content | buttonState | disabledAttr
${'something'} | ${'something'} | ${'enabled'} | ${undefined}
${''} | ${'something'} | ${'disabled'} | ${'disabled'}
${'something'} | ${''} | ${'disabled'} | ${'disabled'}
${''} | ${''} | ${'disabled'} | ${'disabled'}
${' '} | ${' '} | ${'disabled'} | ${'disabled'}
`(
"when title='$title', content='$content', then the button is $buttonState'",
async ({ title, content, disabledAttr }) => {
createWrapper();
findTitle().setValue(title);
findContent().setValue(content);
await wrapper.vm.$nextTick();
expect(findSubmitButton().attributes().disabled).toBe(disabledAttr);
},
);
it.each`
persisted | buttonLabel
${true} | ${'Save changes'}
${false} | ${'Create page'}
`('when persisted=$persisted, label is set to $buttonLabel', ({ persisted, buttonLabel }) => {
createWrapper(persisted);
expect(findSubmitButton().text()).toBe(buttonLabel);
});
});
describe('cancel button state', () => {
it.each`
persisted | redirectLink
${false} | ${'/project/path/-/wikis'}
${true} | ${'/project/path/-/wikis/home'}
`(
'when persisted=$persisted, redirects the user to appropriate path',
({ persisted, redirectLink }) => {
createWrapper(persisted);
expect(findCancelButton().attributes().href).toEqual(redirectLink);
},
);
});
});
......@@ -25,6 +25,7 @@ exports[`Snippet Description Edit component rendering matches the snapshot 1`] =
<div
class="js-vue-markdown-field md-area position-relative gfm-form js-expanded"
data-uploads-path=""
>
<markdown-header-stub
linecontent=""
......
......@@ -4,159 +4,6 @@ import Wikis from '~/pages/shared/wikis/wikis';
import Tracking from '~/tracking';
describe('Wikis', () => {
const editFormHtmlFixture = (args) => `<form class="wiki-form ${
args.newPage ? 'js-new-wiki-page' : ''
}">
<input type="text" id="wiki_title" value="My title" />
<input type="text" id="wiki_message" />
<select class="form-control select-control" name="wiki[format]" id="wiki_format">
<option value="markdown">Markdown</option>
<option selected="selected" value="rdoc">RDoc</option>
<option value="asciidoc">AsciiDoc</option>
<option value="org">Org</option>
</select>
<textarea id="wiki_content"></textarea>
<code class="js-markup-link-example">{Link title}[link:page-slug]</code>
<input type="submit" class="js-wiki-btn-submit">
</input>
</form>
`;
let wikis;
let titleInput;
let contentInput;
let messageInput;
let changeFormatSelect;
let linkExample;
const findBeforeUnloadWarning = () => window.onbeforeunload?.();
const findForm = () => document.querySelector('.wiki-form');
const findSubmitButton = () => document.querySelector('.js-wiki-btn-submit');
describe('when the wiki page is being created', () => {
const formHtmlFixture = editFormHtmlFixture({ newPage: true });
beforeEach(() => {
setHTMLFixture(formHtmlFixture);
titleInput = document.getElementById('wiki_title');
messageInput = document.getElementById('wiki_message');
changeFormatSelect = document.querySelector('#wiki_format');
linkExample = document.querySelector('.js-markup-link-example');
wikis = new Wikis();
});
it('binds an event listener to the title input', () => {
wikis.handleWikiTitleChange = jest.fn();
titleInput.dispatchEvent(new Event('keyup'));
expect(wikis.handleWikiTitleChange).toHaveBeenCalled();
});
it('sets the commit message when title changes', () => {
titleInput.value = 'My title';
messageInput.value = '';
titleInput.dispatchEvent(new Event('keyup'));
expect(messageInput.value).toEqual('Create My title');
});
it('replaces hyphens with spaces', () => {
titleInput.value = 'my-hyphenated-title';
titleInput.dispatchEvent(new Event('keyup'));
expect(messageInput.value).toEqual('Create my hyphenated title');
});
});
describe('when the wiki page is being updated', () => {
const formHtmlFixture = editFormHtmlFixture({ newPage: false });
beforeEach(() => {
setHTMLFixture(formHtmlFixture);
titleInput = document.getElementById('wiki_title');
messageInput = document.getElementById('wiki_message');
wikis = new Wikis();
});
it('sets the commit message when title changes, prefixing with "Update"', () => {
titleInput.value = 'My title';
messageInput.value = '';
titleInput.dispatchEvent(new Event('keyup'));
expect(messageInput.value).toEqual('Update My title');
});
it.each`
value | text
${'markdown'} | ${'[Link Title](page-slug)'}
${'rdoc'} | ${'{Link title}[link:page-slug]'}
${'asciidoc'} | ${'link:page-slug[Link title]'}
${'org'} | ${'[[page-slug]]'}
`('updates a message when value=$value is selected', ({ value, text }) => {
changeFormatSelect.value = value;
changeFormatSelect.dispatchEvent(new Event('change'));
expect(linkExample.innerHTML).toBe(text);
});
it('starts with no unload warning', () => {
expect(findBeforeUnloadWarning()).toBeUndefined();
});
describe('when wiki content is updated', () => {
beforeEach(() => {
contentInput = document.getElementById('wiki_content');
contentInput.value = 'Lorem ipsum dolar sit!';
contentInput.dispatchEvent(new Event('input'));
});
it('sets before unload warning', () => {
expect(findBeforeUnloadWarning()).toBe('');
});
it('when form submitted, unsets before unload warning', () => {
findForm().dispatchEvent(new Event('submit'));
expect(findBeforeUnloadWarning()).toBeUndefined();
});
});
});
describe('submit button state', () => {
beforeEach(() => {
setHTMLFixture(editFormHtmlFixture({ newPage: true }));
titleInput = document.getElementById('wiki_title');
contentInput = document.getElementById('wiki_content');
wikis = new Wikis();
});
it.each`
title | text | buttonState | disabledAttr
${'something'} | ${'something'} | ${'enabled'} | ${null}
${''} | ${'something'} | ${'disabled'} | ${'true'}
${'something'} | ${''} | ${'disabled'} | ${'true'}
${''} | ${''} | ${'disabled'} | ${'true'}
${' '} | ${' '} | ${'disabled'} | ${'true'}
`(
"when title='$title', content='$content', then, buttonState='$buttonState'",
({ title, text, disabledAttr }) => {
titleInput.value = title;
titleInput.dispatchEvent(new Event('keyup'));
contentInput.value = text;
contentInput.dispatchEvent(new Event('input'));
expect(findSubmitButton().getAttribute('disabled')).toBe(disabledAttr);
},
);
});
describe('trackPageView', () => {
const trackingPage = 'projects:wikis:show';
const trackingContext = { foo: 'bar' };
......
......@@ -240,7 +240,7 @@ RSpec.shared_examples 'User creates wiki page' do
end
end
it "shows the emoji autocompletion dropdown" do
it "shows the emoji autocompletion dropdown", :js do
click_link("New page")
page.within(".wiki-form") do
......
......@@ -38,19 +38,19 @@ RSpec.shared_examples 'User previews wiki changes' do
end
end
context "when there are no spaces or hyphens in the page name" do
context "when there are no spaces or hyphens in the page name", :js do
let(:wiki_page) { build(:wiki_page, wiki: wiki, title: 'a/b/c/d', content: page_content) }
it_behaves_like 'rewrites relative links'
end
context "when there are spaces in the page name" do
context "when there are spaces in the page name", :js do
let(:wiki_page) { build(:wiki_page, wiki: wiki, title: 'a page/b page/c page/d page', content: page_content) }
it_behaves_like 'rewrites relative links'
end
context "when there are hyphens in the page name" do
context "when there are hyphens in the page name", :js do
let(:wiki_page) { build(:wiki_page, wiki: wiki, title: 'a-page/b-page/c-page/d-page', content: page_content) }
it_behaves_like 'rewrites relative links'
......
......@@ -11,7 +11,7 @@ RSpec.shared_examples 'User updates wiki page' do
sign_in(user)
end
context 'when wiki is empty' do
context 'when wiki is empty', :js do
before do |example|
visit(wiki_path(wiki))
......@@ -57,7 +57,7 @@ RSpec.shared_examples 'User updates wiki page' do
it_behaves_like 'wiki file attachments'
end
context 'when wiki is not empty' do
context 'when wiki is not empty', :js do
let!(:wiki_page) { create(:wiki_page, wiki: wiki, title: 'home', content: 'Home page') }
before do
......@@ -147,7 +147,7 @@ RSpec.shared_examples 'User updates wiki page' do
it_behaves_like 'wiki file attachments'
end
context 'when the page is in a subdir' do
context 'when the page is in a subdir', :js do
let(:page_name) { 'page_name' }
let(:page_dir) { "foo/bar/#{page_name}" }
let!(:wiki_page) { create(:wiki_page, wiki: wiki, title: page_dir, content: 'Home page') }
......
......@@ -24,7 +24,7 @@ RSpec.shared_examples 'User views empty wiki' do
# This mirrors the logic in:
# - app/views/shared/empty_states/_wikis.html.haml
# - WikiHelper#wiki_empty_state_messages
it 'shows the empty state message with the expected elements' do
it 'shows the empty state message with the expected elements', :js do
visit wiki_path(wiki)
if writable
......
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