diff --git a/app/assets/javascripts/vue_shared/components/markdown/header.vue b/app/assets/javascripts/vue_shared/components/markdown/header.vue index 0899b752cbcbd35756523a6fd548ec4b5fed9fa0..04f50d5dafe2bfc5b452c616b6a130fecb7deb78 100644 --- a/app/assets/javascripts/vue_shared/components/markdown/header.vue +++ b/app/assets/javascripts/vue_shared/components/markdown/header.vue @@ -214,6 +214,7 @@ export default { : description; if (textArea) { + textArea.value = ''; updateText({ textArea, tag: `${text}\n\n---\n\n_${addendum}_`, diff --git a/app/assets/javascripts/vue_shared/components/markdown/mount_markdown_editor.js b/app/assets/javascripts/vue_shared/components/markdown/mount_markdown_editor.js index e12815f0094ce731adfcf58cdcad2426d163e7a1..0838bc47a9fd94eaeeb4adbd43247fefbb3a1ddb 100644 --- a/app/assets/javascripts/vue_shared/components/markdown/mount_markdown_editor.js +++ b/app/assets/javascripts/vue_shared/components/markdown/mount_markdown_editor.js @@ -8,8 +8,8 @@ import { CLEAR_AUTOSAVE_ENTRY_EVENT } from '../../constants'; import MarkdownEditor from './markdown_editor.vue'; import eventHub from './eventhub'; -export const MR_SOURCE_BRANCH = 'merge_request[source_branch]'; -export const MR_TARGET_BRANCH = 'merge_request[target_branch]'; +const MR_SOURCE_BRANCH = 'merge_request[source_branch]'; +const MR_TARGET_BRANCH = 'merge_request[target_branch]'; function organizeQuery(obj, isFallbackKey = false) { if (!obj[MR_SOURCE_BRANCH] && !obj[MR_TARGET_BRANCH]) { diff --git a/app/views/projects/merge_requests/_form.html.haml b/app/views/projects/merge_requests/_form.html.haml index 5f1c72156eb6b3e1936cbc0a069861592eb3e424..6d2e2cfcc542187c726109cf7a95536a2df6633b 100644 --- a/app/views/projects/merge_requests/_form.html.haml +++ b/app/views/projects/merge_requests/_form.html.haml @@ -1,3 +1,4 @@ = gitlab_ui_form_for [@project, @merge_request], html: { class: 'merge-request-form common-note-form js-requires-input js-quick-submit' } do |f| + = render 'source_and_target', mr: @merge_request = render 'shared/issuable/form', f: f, issuable: @merge_request, presenter: @mr_presenter diff --git a/app/views/projects/merge_requests/_source_and_target.html.haml b/app/views/projects/merge_requests/_source_and_target.html.haml new file mode 100644 index 0000000000000000000000000000000000000000..68cd4fe9372e28533d97f801c348b525e2206180 --- /dev/null +++ b/app/views/projects/merge_requests/_source_and_target.html.haml @@ -0,0 +1,10 @@ +%span{ + id: "js-merge-request-metadata", + class: ["js-merge-request-metadata", "gl-display-none"], + data: { + "source-project-id": mr.source_project_id, + "source-branch": mr.source_branch, + "target-project-id": mr.target_project_id, + "target-branch": mr.target_branch + } +} diff --git a/app/views/projects/merge_requests/creations/_new_submit.html.haml b/app/views/projects/merge_requests/creations/_new_submit.html.haml index bec7cb3fd347a3bb0fac1ef7cacafb72102c7f1b..a7151421acba1fd5936038a691c541b98ab54116 100644 --- a/app/views/projects/merge_requests/creations/_new_submit.html.haml +++ b/app/views/projects/merge_requests/creations/_new_submit.html.haml @@ -1,6 +1,7 @@ %h1.page-title.gl-font-size-h-display = _('New merge request') = gitlab_ui_form_for [@project, @merge_request], html: { class: 'merge-request-form common-note-form js-requires-input js-quick-submit' } do |f| + = render "projects/merge_requests/source_and_target", mr: @merge_request = render 'shared/issuable/form', f: f, issuable: @merge_request, commits: @commits, presenter: @mr_presenter = f.hidden_field :source_project_id = f.hidden_field :source_branch diff --git a/ee/app/assets/javascripts/ai/graphql/fill_mr_description.mutation.graphql b/ee/app/assets/javascripts/ai/graphql/fill_mr_description.mutation.graphql index cd93fa925a28107fc94f2793fa4e573d6fe18620..9c00ba1ff7cb0b955b67d7ec5e6a73f9e2664bb0 100644 --- a/ee/app/assets/javascripts/ai/graphql/fill_mr_description.mutation.graphql +++ b/ee/app/assets/javascripts/ai/graphql/fill_mr_description.mutation.graphql @@ -1,5 +1,5 @@ mutation fillInMergeRequestTemplate( - $sourceProjectGqlId: ID + $sourceProjectId: ID $targetProjectGqlId: AiModelID! $source: String! $target: String! @@ -10,7 +10,7 @@ mutation fillInMergeRequestTemplate( input: { fillInMergeRequestTemplate: { resourceId: $targetProjectGqlId - sourceProjectId: $sourceProjectGqlId + sourceProjectId: $sourceProjectId sourceBranch: $source targetBranch: $target title: $mrTitle diff --git a/ee/app/assets/javascripts/vue_shared/components/markdown/mount_markdown_editor.js b/ee/app/assets/javascripts/vue_shared/components/markdown/mount_markdown_editor.js index fcfbf77217cc441ef241809d7374583d4a6a9457..f35ae4ff23e489fc6df81f9693167a9564dd4c1f 100644 --- a/ee/app/assets/javascripts/vue_shared/components/markdown/mount_markdown_editor.js +++ b/ee/app/assets/javascripts/vue_shared/components/markdown/mount_markdown_editor.js @@ -1,12 +1,8 @@ import { __ } from '~/locale'; -import { TYPENAME_USER } from '~/graphql_shared/constants'; +import { TYPENAME_USER, TYPENAME_PROJECT } from '~/graphql_shared/constants'; import { convertToGraphQLId } from '~/graphql_shared/utils'; import aiFillDescriptionMutation from 'ee/ai/graphql/fill_mr_description.mutation.graphql'; -import { - mountMarkdownEditor as mountCEMarkdownEditor, - MR_SOURCE_BRANCH, - MR_TARGET_BRANCH, -} from '~/vue_shared/components/markdown/mount_markdown_editor'; +import { mountMarkdownEditor as mountCEMarkdownEditor } from '~/vue_shared/components/markdown/mount_markdown_editor'; export function mountMarkdownEditor() { const provideEEAiActions = []; @@ -17,29 +13,16 @@ export function mountMarkdownEditor() { description: __('Replace current template with filled in placeholders'), method: 'replace', subscriptionVariables() { - const projectGqlId = convertToGraphQLId( - /* eslint-disable-next-line @gitlab/require-i18n-strings */ - 'Project', - document.getElementById('merge_request_source_project_id').value, - ); + const mrMetadata = document.getElementById('js-merge-request-metadata'); return { userId: convertToGraphQLId(TYPENAME_USER, gon.current_user_id), - resourceId: projectGqlId, + resourceId: convertToGraphQLId(TYPENAME_PROJECT, mrMetadata.dataset.targetProjectId), }; }, apolloMutation() { - /* eslint-disable @gitlab/require-i18n-strings */ - const projectGqlId = convertToGraphQLId( - 'Project', - document.getElementById('merge_request_source_project_id').value, - ); - const targetProjectGqlId = convertToGraphQLId( - 'Project', - document.getElementById('merge_request_target_project_id').value, - ); - /* eslint-enable @gitlab/require-i18n-strings */ - const sourceBranch = document.querySelector(`[name="${MR_SOURCE_BRANCH}"]`).value; - const targetBranch = document.querySelector(`[name="${MR_TARGET_BRANCH}"]`).value; + const mrMetadata = document.getElementById('js-merge-request-metadata'); + const { sourceProjectId, sourceBranch, targetProjectId, targetBranch } = mrMetadata.dataset; + const targetProjectGqlId = convertToGraphQLId(TYPENAME_PROJECT, targetProjectId); const mrTitle = document.getElementById('merge_request_title').value; const mrDescription = document.getElementById('merge_request_description').value; @@ -50,7 +33,7 @@ export function mountMarkdownEditor() { target: targetBranch, templateContent: mrDescription, mrTitle, - projectGqlId, + sourceProjectId, targetProjectGqlId, }, }; diff --git a/ee/spec/frontend/vue_shared/components/markdown/header_spec.js b/ee/spec/frontend/vue_shared/components/markdown/header_spec.js index fcc3955a3f7a7fdea934540fa749ab0d4b799187..abfff48881438750cc6b3a8b3a8c7c106c9dc2b0 100644 --- a/ee/spec/frontend/vue_shared/components/markdown/header_spec.js +++ b/ee/spec/frontend/vue_shared/components/markdown/header_spec.js @@ -2,12 +2,16 @@ import { GlTabs, GlDisclosureDropdown, GlListboxItem } from '@gitlab/ui'; import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; import HeaderComponent from '~/vue_shared/components/markdown/header.vue'; import AiActionsDropdown from 'ee/ai/components/ai_actions_dropdown.vue'; +import { setHTMLFixture, resetHTMLFixture } from 'helpers/fixtures'; describe('Markdown field header component', () => { + document.execCommand = jest.fn(); + let wrapper; - const createWrapper = (props, provide = {}) => { + const createWrapper = ({ props, provide = {}, attachTo = document.body } = {}) => { wrapper = shallowMountExtended(HeaderComponent, { + attachTo, propsData: { previewMarkdown: false, ...props, @@ -22,14 +26,53 @@ describe('Markdown field header component', () => { it.each([true, false])( 'renders/does not render "AI actions" when actions are "%s"', (enabled) => { - createWrapper( - {}, - { + createWrapper({ + provide: { editorAiActions: enabled ? [{ value: 'myAction', title: 'myAction' }] : [], }, - ); + }); expect(findAiActionsButton().exists()).toBe(enabled); }, ); + + describe('generated text responses', () => { + const sha = 'abc123'; + const addendum = ` + +--- + +_This description was generated for revision ${sha} using AI_`; + + beforeEach(() => { + setHTMLFixture(`