Commit 76cdbe85 authored by Peter Leitzen's avatar Peter Leitzen

Merge branch '202159-open-fork' into 'master'

When opening Web IDE, follow path to fork if it exists

See merge request gitlab-org/gitlab!36548
parents 3122ee2e d79ec3f6
<script>
import TreeActionLink from './tree_action_link.vue';
import { __ } from '~/locale';
import { webIDEUrl } from '~/lib/utils/url_utility';
export default {
components: {
TreeActionLink,
},
props: {
projectPath: {
type: String,
required: true,
},
refSha: {
type: String,
required: true,
},
canPushCode: {
type: Boolean,
required: false,
default: true,
},
forkPath: {
type: String,
required: false,
default: '',
},
},
computed: {
showLinkToFork() {
return !this.canPushCode && this.forkPath;
},
text() {
return this.showLinkToFork ? __('Edit fork in Web IDE') : __('Web IDE');
},
path() {
const path = this.showLinkToFork ? this.forkPath : this.projectPath;
return webIDEUrl(`/${path}/edit/${this.refSha}/-/${this.$route.params.path || ''}`);
},
},
};
</script>
<template>
<tree-action-link :path="path" :text="text" data-qa-selector="web_ide_button" />
</template>
...@@ -4,18 +4,26 @@ import App from './components/app.vue'; ...@@ -4,18 +4,26 @@ import App from './components/app.vue';
import Breadcrumbs from './components/breadcrumbs.vue'; import Breadcrumbs from './components/breadcrumbs.vue';
import LastCommit from './components/last_commit.vue'; import LastCommit from './components/last_commit.vue';
import TreeActionLink from './components/tree_action_link.vue'; import TreeActionLink from './components/tree_action_link.vue';
import WebIdeLink from './components/web_ide_link.vue';
import DirectoryDownloadLinks from './components/directory_download_links.vue'; import DirectoryDownloadLinks from './components/directory_download_links.vue';
import apolloProvider from './graphql'; import apolloProvider from './graphql';
import { setTitle } from './utils/title'; import { setTitle } from './utils/title';
import { updateFormAction } from './utils/dom'; import { updateFormAction } from './utils/dom';
import { parseBoolean } from '../lib/utils/common_utils'; import { parseBoolean } from '../lib/utils/common_utils';
import { webIDEUrl } from '../lib/utils/url_utility';
import { __ } from '../locale'; import { __ } from '../locale';
export default function setupVueRepositoryList() { export default function setupVueRepositoryList() {
const el = document.getElementById('js-tree-list'); const el = document.getElementById('js-tree-list');
const { dataset } = el; const { dataset } = el;
const { projectPath, projectShortPath, ref, escapedRef, fullName } = dataset; const {
canPushCode,
projectPath,
projectShortPath,
forkPath,
ref,
escapedRef,
fullName,
} = dataset;
const router = createRouter(projectPath, escapedRef); const router = createRouter(projectPath, escapedRef);
apolloProvider.clients.defaultClient.cache.writeData({ apolloProvider.clients.defaultClient.cache.writeData({
...@@ -117,11 +125,12 @@ export default function setupVueRepositoryList() { ...@@ -117,11 +125,12 @@ export default function setupVueRepositoryList() {
el: webIdeLinkEl, el: webIdeLinkEl,
router, router,
render(h) { render(h) {
return h(TreeActionLink, { return h(WebIdeLink, {
props: { props: {
path: webIDEUrl(`/${projectPath}/edit/${ref}/-/${this.$route.params.path || ''}`), projectPath,
text: __('Web IDE'), refSha: ref,
cssClass: 'qa-web-ide-button', forkPath,
canPushCode: parseBoolean(canPushCode),
}, },
}); });
}, },
......
...@@ -191,8 +191,10 @@ module TreeHelper ...@@ -191,8 +191,10 @@ module TreeHelper
def vue_file_list_data(project, ref) def vue_file_list_data(project, ref)
{ {
can_push_code: current_user&.can?(:push_code, project) && "true",
project_path: project.full_path, project_path: project.full_path,
project_short_path: project.path, project_short_path: project.path,
fork_path: current_user&.fork_of(project)&.full_path,
ref: ref, ref: ref,
escaped_ref: ActionDispatch::Journey::Router::Utils.escape_path(ref), escaped_ref: ActionDispatch::Journey::Router::Utils.escape_path(ref),
full_name: project.name_with_namespace full_name: project.name_with_namespace
......
---
title: If a user does not have write access to repo, but a fork exists, the Web IDE
button should take them to the fork
merge_request: 36548
author:
type: added
...@@ -8532,6 +8532,9 @@ msgstr "" ...@@ -8532,6 +8532,9 @@ msgstr ""
msgid "Edit files in the editor and commit changes here" msgid "Edit files in the editor and commit changes here"
msgstr "" msgstr ""
msgid "Edit fork in Web IDE"
msgstr ""
msgid "Edit group: %{group_name}" msgid "Edit group: %{group_name}"
msgstr "" msgstr ""
......
...@@ -56,7 +56,7 @@ module QA ...@@ -56,7 +56,7 @@ module QA
element :new_file_option element :new_file_option
end end
view 'app/assets/javascripts/repository/index.js' do view 'app/assets/javascripts/repository/components/web_ide_link.vue' do
element :web_ide_button element :web_ide_button
end end
......
import WebIdeLink from '~/repository/components/web_ide_link.vue';
import { mount } from '@vue/test-utils';
describe('Web IDE link component', () => {
let wrapper;
function createComponent(props) {
wrapper = mount(WebIdeLink, {
propsData: { ...props },
mocks: {
$route: {
params: {},
},
},
});
}
afterEach(() => {
wrapper.destroy();
});
it('renders link to the Web IDE for a project if only projectPath is given', () => {
createComponent({ projectPath: 'gitlab-org/gitlab', refSha: 'master' });
expect(wrapper.attributes('href')).toBe('/-/ide/project/gitlab-org/gitlab/edit/master/-/');
expect(wrapper.text()).toBe('Web IDE');
});
it('renders link to the Web IDE for a project even if both projectPath and forkPath are given', () => {
createComponent({
projectPath: 'gitlab-org/gitlab',
refSha: 'master',
forkPath: 'my-namespace/gitlab',
});
expect(wrapper.attributes('href')).toBe('/-/ide/project/gitlab-org/gitlab/edit/master/-/');
expect(wrapper.text()).toBe('Web IDE');
});
it('renders link to the forked project if it exists and cannot write to the repo', () => {
createComponent({
projectPath: 'gitlab-org/gitlab',
refSha: 'master',
forkPath: 'my-namespace/gitlab',
canPushCode: false,
});
expect(wrapper.attributes('href')).toBe('/-/ide/project/my-namespace/gitlab/edit/master/-/');
expect(wrapper.text()).toBe('Edit fork in Web IDE');
});
});
...@@ -154,4 +154,58 @@ RSpec.describe TreeHelper do ...@@ -154,4 +154,58 @@ RSpec.describe TreeHelper do
expect(helper.commit_in_single_accessible_branch).to include(escaped_branch_name) expect(helper.commit_in_single_accessible_branch).to include(escaped_branch_name)
end end
end end
describe '#vue_file_list_data' do
before do
allow(helper).to receive(:current_user).and_return(nil)
end
it 'returns a list of attributes related to the project' do
expect(helper.vue_file_list_data(project, sha)).to include(
can_push_code: nil,
fork_path: nil,
escaped_ref: sha,
ref: sha,
project_path: project.full_path,
project_short_path: project.path,
full_name: project.name_with_namespace
)
end
context 'user does not have write access but a personal fork exists' do
include ProjectForksHelper
let_it_be(:user) { create(:user) }
let!(:forked_project) { create(:project, :repository, namespace: user.namespace) }
before do
project.add_guest(user)
fork_project(project, nil, target_project: forked_project)
allow(helper).to receive(:current_user).and_return(user)
end
it 'includes fork_path too' do
expect(helper.vue_file_list_data(project, sha)).to include(
fork_path: forked_project.full_path
)
end
end
context 'user has write access' do
let_it_be(:user) { create(:user) }
before do
project.add_developer(user)
allow(helper).to receive(:current_user).and_return(user)
end
it 'includes can_push_code: true' do
expect(helper.vue_file_list_data(project, sha)).to include(
can_push_code: "true"
)
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