Commit 13af95c7 authored by GitLab Bot's avatar GitLab Bot

Merge remote-tracking branch 'upstream/master' into ce-to-ee-2018-06-26

# Conflicts:
#	app/assets/javascripts/boards/components/sidebar/remove_issue.vue

[ci skip]
parents 101cd6d6 76cb447f
......@@ -23,7 +23,10 @@
},
methods: {
removeIssue() {
<<<<<<< HEAD
const board = Store.state.currentBoard;
=======
>>>>>>> upstream/master
const { issue } = this;
const lists = issue.getLists();
const boardLabelIds = board.labels.map(label => label.id);
......
......@@ -26,6 +26,10 @@ export default {
type: String,
required: true,
},
projectPath: {
type: String,
required: true,
},
shouldShow: {
type: Boolean,
required: false,
......@@ -94,18 +98,16 @@ export default {
},
},
mounted() {
this.setEndpoint(this.endpoint);
this
.fetchDiffFiles()
.catch(() => {
createFlash(__('Something went wrong on our end. Please try again!'));
this.setBaseConfig({ endpoint: this.endpoint, projectPath: this.projectPath });
this.fetchDiffFiles().catch(() => {
createFlash(__('Fetching diff files failed. Please reload the page to try again!'));
});
},
created() {
this.adjustView();
},
methods: {
...mapActions(['setEndpoint', 'fetchDiffFiles']),
...mapActions(['setBaseConfig', 'fetchDiffFiles']),
setActive(filePath) {
this.activeFile = filePath;
},
......
<script>
import { mapGetters } from 'vuex';
import { mapGetters, mapState } from 'vuex';
import DiffViewer from '~/vue_shared/components/diff_viewer/diff_viewer.vue';
import { diffModes } from '~/ide/constants';
import InlineDiffView from './inline_diff_view.vue';
import ParallelDiffView from './parallel_diff_view.vue';
......@@ -7,6 +9,7 @@ export default {
components: {
InlineDiffView,
ParallelDiffView,
DiffViewer,
},
props: {
diffFile: {
......@@ -15,7 +18,18 @@ export default {
},
},
computed: {
...mapState({
projectPath: state => state.diffs.projectPath,
endpoint: state => state.diffs.endpoint,
}),
...mapGetters(['isInlineView', 'isParallelView']),
diffMode() {
const diffModeKey = Object.keys(diffModes).find(key => this.diffFile[`${key}File`]);
return diffModes[diffModeKey] || diffModes.replaced;
},
isTextFile() {
return this.diffFile.text;
},
},
};
</script>
......@@ -23,16 +37,26 @@ export default {
<template>
<div class="diff-content">
<div class="diff-viewer">
<template v-if="isTextFile">
<inline-diff-view
v-if="isInlineView"
:diff-file="diffFile"
:diff-lines="diffFile.highlightedDiffLines || []"
/>
<parallel-diff-view
v-if="isParallelView"
v-else-if="isParallelView"
:diff-file="diffFile"
:diff-lines="diffFile.parallelDiffLines || []"
/>
</template>
<diff-viewer
v-else
:diff-mode="diffMode"
:new-path="diffFile.newPath"
:new-sha="diffFile.diffRefs.headSha"
:old-path="diffFile.oldPath"
:old-sha="diffFile.diffRefs.baseSha"
:project-path="projectPath"/>
</div>
</div>
</template>
......@@ -36,7 +36,7 @@ export default {
<table
:class="userColorScheme"
:data-commit-id="commitId"
class="code diff-wrap-lines js-syntax-highlight text-file">
class="code diff-wrap-lines js-syntax-highlight text-file js-diff-inline-view">
<tbody>
<template
v-for="(line, index) in normalizedDiffLines"
......
......@@ -16,6 +16,7 @@ export default function initDiffsApp(store) {
return {
endpoint: dataset.endpoint,
projectPath: dataset.projectPath,
currentUser: convertObjectPropsToCamelCase(JSON.parse(dataset.currentUserData), {
deep: true,
}),
......@@ -31,6 +32,7 @@ export default function initDiffsApp(store) {
props: {
endpoint: this.endpoint,
currentUser: this.currentUser,
projectPath: this.projectPath,
shouldShow: this.activeTab === 'diffs',
},
});
......
......@@ -10,8 +10,9 @@ import {
DIFF_VIEW_COOKIE_NAME,
} from '../constants';
export const setEndpoint = ({ commit }, endpoint) => {
commit(types.SET_ENDPOINT, endpoint);
export const setBaseConfig = ({ commit }, options) => {
const { endpoint, projectPath } = options;
commit(types.SET_BASE_CONFIG, { endpoint, projectPath });
};
export const setLoadingState = ({ commit }, state) => {
......@@ -86,7 +87,7 @@ export const expandAllFiles = ({ commit }) => {
};
export default {
setEndpoint,
setBaseConfig,
setLoadingState,
fetchDiffFiles,
setInlineDiffViewType,
......
......@@ -13,6 +13,7 @@ export default {
state: {
isLoading: true,
endpoint: '',
basePath: '',
commit: null,
diffFiles: [],
mergeRequestDiffs: [],
......
export const SET_ENDPOINT = 'SET_ENDPOINT';
export const SET_BASE_CONFIG = 'SET_BASE_CONFIG';
export const SET_LOADING = 'SET_LOADING';
export const SET_DIFF_DATA = 'SET_DIFF_DATA';
export const SET_DIFF_FILES = 'SET_DIFF_FILES';
......
......@@ -5,8 +5,9 @@ import { findDiffFile, addLineReferences, removeMatchLine, addContextLines } fro
import * as types from './mutation_types';
export default {
[types.SET_ENDPOINT](state, endpoint) {
Object.assign(state, { endpoint });
[types.SET_BASE_CONFIG](state, options) {
const { endpoint, projectPath } = options;
Object.assign(state, { endpoint, projectPath });
},
[types.SET_LOADING](state, isLoading) {
......@@ -73,7 +74,7 @@ export default {
[types.EXPAND_ALL_FILES](state) {
const diffFiles = [];
state.diffFiles.forEach((file) => {
state.diffFiles.forEach(file => {
diffFiles.push({
...file,
collapsed: false,
......
......@@ -95,24 +95,53 @@ export default {
return this.file.changed || this.file.tempFile || this.file.staged;
},
},
mounted() {
if (this.hasPathAtCurrentRoute()) {
this.scrollIntoView(true);
}
},
updated() {
if (this.file.type === 'blob' && this.file.active) {
this.$el.scrollIntoView({
behavior: 'smooth',
block: 'nearest',
});
this.scrollIntoView();
}
},
methods: {
...mapActions(['toggleTreeOpen']),
clickFile() {
// Manual Action if a tree is selected/opened
if (this.isTree && this.$router.currentRoute.path === `/project${this.file.url}`) {
if (this.isTree && this.hasUrlAtCurrentRoute()) {
this.toggleTreeOpen(this.file.path);
}
router.push(`/project${this.file.url}`);
},
scrollIntoView(isInit = false) {
const block = isInit && this.isTree ? 'center' : 'nearest';
this.$el.scrollIntoView({
behavior: 'smooth',
block,
});
},
hasPathAtCurrentRoute() {
if (!this.$router || !this.$router.currentRoute) {
return false;
}
// - strip route up to "/-/" and ending "/"
const routePath = this.$router.currentRoute.path
.replace(/^.*?[/]-[/]/g, '')
.replace(/[/]$/g, '');
// - strip ending "/"
const filePath = this.file.path
.replace(/[/]$/g, '');
return filePath === routePath;
},
hasUrlAtCurrentRoute() {
return this.$router.currentRoute.path === `/project${this.file.url}`;
},
},
};
</script>
......
......@@ -9,6 +9,17 @@ export const toggleTreeOpen = ({ commit }, path) => {
commit(types.TOGGLE_TREE_OPEN, path);
};
export const showTreeEntry = ({ commit, dispatch, state }, path) => {
const entry = state.entries[path];
const parentPath = entry ? entry.parentPath : '';
if (parentPath) {
commit(types.SET_TREE_OPEN, parentPath);
dispatch('showTreeEntry', parentPath);
}
};
export const handleTreeEntryAction = ({ commit, dispatch }, row) => {
if (row.type === 'tree') {
dispatch('toggleTreeOpen', row.path);
......@@ -21,6 +32,8 @@ export const handleTreeEntryAction = ({ commit, dispatch }, row) => {
} else {
dispatch('getFileData', { path: row.path });
}
dispatch('showTreeEntry', row.path);
};
export const getLastCommitData = ({ state, commit, dispatch }, tree = state) => {
......
......@@ -28,6 +28,7 @@ export const TOGGLE_BRANCH_OPEN = 'TOGGLE_BRANCH_OPEN';
// Tree mutation types
export const SET_DIRECTORY_DATA = 'SET_DIRECTORY_DATA';
export const TOGGLE_TREE_OPEN = 'TOGGLE_TREE_OPEN';
export const SET_TREE_OPEN = 'SET_TREE_OPEN';
export const SET_LAST_COMMIT_URL = 'SET_LAST_COMMIT_URL';
export const CREATE_TREE = 'CREATE_TREE';
export const REMOVE_ALL_CHANGES_FILES = 'REMOVE_ALL_CHANGES_FILES';
......
......@@ -6,6 +6,11 @@ export default {
opened: !state.entries[path].opened,
});
},
[types.SET_TREE_OPEN](state, path) {
Object.assign(state.entries[path], {
opened: true,
});
},
[types.CREATE_TREE](state, { treePath }) {
Object.assign(state, {
trees: Object.assign({}, state.trees, {
......
......@@ -47,7 +47,8 @@ import _ from 'underscore';
var _this;
_this = this;
this.fileInput.on('change', function(e) {
return _this.onFileInputChange(e, this);
_this.onFileInputChange(e, this);
this.value = null;
});
this.pickImageEl.on('click', this.onPickImageClick);
this.modalCrop.on('shown.bs.modal', this.onModalShow);
......
......@@ -45,11 +45,15 @@ export default {
return DownloadDiffViewer;
}
},
basePath() {
// We might get the project path from rails with the relative url already setup
return this.projectPath.indexOf('/') === 0 ? '' : `${gon.relative_url_root}/`;
},
fullOldPath() {
return `${gon.relative_url_root}/${this.projectPath}/raw/${this.oldSha}/${this.oldPath}`;
return `${this.basePath}${this.projectPath}/raw/${this.oldSha}/${this.oldPath}`;
},
fullNewPath() {
return `${gon.relative_url_root}/${this.projectPath}/raw/${this.newSha}/${this.newPath}`;
return `${this.basePath}${this.projectPath}/raw/${this.newSha}/${this.newPath}`;
},
},
};
......
/*
* Includes specific styles from the bootstrap4 foler in node_modules
*/
@import "../../../node_modules/bootstrap/scss/functions";
@import "../../../node_modules/bootstrap/scss/variables";
@import "../../../node_modules/bootstrap/scss/mixins";
@import "../../../node_modules/bootstrap/scss/root";
@import "../../../node_modules/bootstrap/scss/reboot";
@import "../../../node_modules/bootstrap/scss/type";
@import "../../../node_modules/bootstrap/scss/images";
@import "../../../node_modules/bootstrap/scss/code";
@import "../../../node_modules/bootstrap/scss/grid";
@import "../../../node_modules/bootstrap/scss/tables";
@import "../../../node_modules/bootstrap/scss/forms";
@import "../../../node_modules/bootstrap/scss/buttons";
@import "../../../node_modules/bootstrap/scss/transitions";
@import "../../../node_modules/bootstrap/scss/dropdown";
@import "../../../node_modules/bootstrap/scss/button-group";
@import "../../../node_modules/bootstrap/scss/input-group";
@import "../../../node_modules/bootstrap/scss/custom-forms";
@import "../../../node_modules/bootstrap/scss/nav";
@import "../../../node_modules/bootstrap/scss/navbar";
@import "../../../node_modules/bootstrap/scss/card";
@import "../../../node_modules/bootstrap/scss/breadcrumb";
@import "../../../node_modules/bootstrap/scss/pagination";
@import "../../../node_modules/bootstrap/scss/badge";
@import "../../../node_modules/bootstrap/scss/alert";
@import "../../../node_modules/bootstrap/scss/progress";
@import "../../../node_modules/bootstrap/scss/media";
@import "../../../node_modules/bootstrap/scss/list-group";
@import "../../../node_modules/bootstrap/scss/close";
@import "../../../node_modules/bootstrap/scss/modal";
@import "../../../node_modules/bootstrap/scss/tooltip";
@import "../../../node_modules/bootstrap/scss/popover";
@import "../../../node_modules/bootstrap/scss/utilities";
@import "../../../node_modules/bootstrap/scss/print";
@import 'framework/variables';
@import 'framework/mixins';
@import '../../../node_modules/bootstrap/scss/bootstrap';
@import 'bootstrap';
@import 'bootstrap_migration';
@import 'framework/layout';
......
......@@ -539,6 +539,10 @@
display: block;
}
}
svg {
vertical-align: text-top;
}
}
}
......
......@@ -502,6 +502,10 @@
border-bottom: 0;
}
.merge-request-details .file-content.image_file img {
max-height: 50vh;
}
.diff-stats-summary-toggler {
padding: 0;
background-color: transparent;
......
......@@ -46,7 +46,6 @@
.btn {
font-size: $gl-font-size;
max-height: 26px;
&[disabled] {
opacity: 0.3;
......
......@@ -20,7 +20,7 @@
%li
= link_to "https://about.gitlab.com/contributing", target: '_blank', class: 'text-nowrap' do
= _("Contribute to GitLab")
= icon('external-link')
= sprite_icon('external-link', size: 16)
%li.divider
- if current_user_menu?(:sign_out)
%li
......
= form_for @cluster, url: namespace_project_cluster_path(@project.namespace, @project, @cluster), as: :cluster, html: { class: 'cluster_integration_form' } do |field|
= form_errors(@cluster)
.form-group.append-bottom-20
.form-group
%h5= s_('ClusterIntegration|Integration status')
%p
- if @cluster.enabled?
......@@ -10,7 +10,7 @@
= s_('ClusterIntegration|Kubernetes cluster integration is enabled for this project.')
- else
= s_('ClusterIntegration|Kubernetes cluster integration is disabled for this project.')
%label.append-bottom-10.js-cluster-enable-toggle-area
%label.append-bottom-0.js-cluster-enable-toggle-area
%button{ type: 'button',
class: "js-project-feature-toggle project-feature-toggle #{'is-checked' if @cluster.enabled?} #{'is-disabled' unless can?(current_user, :update_cluster, @cluster)}",
"aria-label": s_("ClusterIntegration|Toggle Kubernetes cluster"),
......@@ -20,19 +20,26 @@
= sprite_icon('status_success_borderless', size: 16, css_class: 'toggle-icon-svg toggle-status-checked')
= sprite_icon('status_failed_borderless', size: 16, css_class: 'toggle-icon-svg toggle-status-unchecked')
.form-group
%h5= s_('ClusterIntegration|Security')
%p
= s_("ClusterIntegration|The default cluster configuration grants access to a wide set of functionalities needed to successfully build and deploy a containerised application.")
= link_to s_("ClusterIntegration|Learn more about security configuration"), help_page_path('user/project/clusters/index.md', anchor: 'security-implications')
- if has_multiple_clusters?(@project)
.form-group
%h5= s_('ClusterIntegration|Environment scope')
%p
= s_("ClusterIntegration|Choose which of your project's environments will use this Kubernetes cluster.")
= link_to s_("ClusterIntegration|Learn more about environments"), help_page_path('ci/environments')
= field.text_field :environment_scope, class: 'form-control js-select-on-focus', readonly: !has_multiple_clusters?(@project), placeholder: s_('ClusterIntegration|Environment scope')
= field.text_field :environment_scope, class: 'form-control js-select-on-focus', placeholder: s_('ClusterIntegration|Environment scope')
- if can?(current_user, :update_cluster, @cluster)
.form-group
= field.submit _('Save changes'), class: 'btn btn-success'
- unless has_multiple_clusters?(@project)
%h5= s_('ClusterIntegration|Environment scope')
%p
%code *
is the default environment scope for this cluster. This means that all jobs, regardless of their environment, will use this cluster.
= link_to 'More information', ('https://docs.gitlab.com/ee/user/project/clusters/#setting-the-environment-scope')
%h5= s_('ClusterIntegration|Security')
%p
= s_("ClusterIntegration|The default cluster configuration grants access to a wide set of functionalities needed to successfully build and deploy a containerised application.")
= link_to s_("ClusterIntegration|Learn more about security configuration"), help_page_path('user/project/clusters/index.md', anchor: 'security-implications')
......@@ -73,7 +73,8 @@
= render 'projects/commit/pipelines_list', disable_initialization: true, endpoint: pipelines_project_merge_request_path(@project, @merge_request)
#js-diffs-app.diffs.tab-pane{ data: { "is-locked" => @merge_request.discussion_locked?,
endpoint: diffs_project_merge_request_path(@project, @merge_request, 'json', request.query_parameters),
current_user_data: UserSerializer.new(project: @project).represent(current_user, {}, MergeRequestUserEntity).to_json } }
current_user_data: UserSerializer.new(project: @project).represent(current_user, {}, MergeRequestUserEntity).to_json,
project_path: project_path(@merge_request.project)} }
.mr-loading-status
= spinner
......
---
title: Update WebIDE to show file in tree on load
merge_request: 19887
author:
type: changed
---
title: Removes unused bootstrap 4 scss files
merge_request: 19423
author:
type: deprecated
---
title: Change environment scope text depending on number of project clusters. Update
form to only include form-groups
merge_request:
author:
type: changed
---
title: Remove performance bottleneck preventing large wiki pages from displaying
merge_request: 20174
author:
type: performance
---
title: Fixes issue with uploading same image to Profile Avatar twice
merge_request: 20161
author: Chirag Bhatia
type: fixed
---
title: Revert merge request widget button max height
merge_request: 20175
author: George Tsiolis
type: fixed
---
title: Update external link icon in header user dropdown
merge_request: 20150
author: George Tsiolis
type: changed
......@@ -57,6 +57,10 @@ module QA
private
def within_project_deploy_keys
wait(reload: false) do
find_element(:project_deploy_keys)
end
within_element(:project_deploy_keys) do
yield
end
......
// TODO: https://gitlab.com/gitlab-org/gitlab-ce/issues/48034
import Vue from 'vue';
import DiffContentComponent from '~/diffs/components/diff_content.vue';
import store from '~/mr_notes/stores';
import { mountComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
import { GREEN_BOX_IMAGE_URL, RED_BOX_IMAGE_URL } from 'spec/test_constants';
import diffFileMockData from '../mock_data/diff_file';
describe('DiffContent', () => {
const Component = Vue.extend(DiffContentComponent);
let vm;
const getDiffFileMock = () => Object.assign({}, diffFileMockData);
beforeEach(() => {
vm = mountComponentWithStore(Component, {
store,
props: {
diffFile: getDiffFileMock(),
},
});
});
describe('text based files', () => {
it('should render diff inline view', done => {
vm.$store.state.diffs.diffViewType = 'inline';
vm.$nextTick(() => {
expect(vm.$el.querySelectorAll('.js-diff-inline-view').length).toEqual(1);
done();
});
});
it('should render diff parallel view', done => {
vm.$store.state.diffs.diffViewType = 'parallel';
vm.$nextTick(() => {
expect(vm.$el.querySelectorAll('.parallel').length).toEqual(18);
done();
});
});
});
describe('Non-Text diffs', () => {
beforeEach(() => {
vm.diffFile.text = false;
});
describe('image diff', () => {
beforeEach(() => {
vm.diffFile.newPath = GREEN_BOX_IMAGE_URL;
vm.diffFile.newSha = 'DEF';
vm.diffFile.oldPath = RED_BOX_IMAGE_URL;
vm.diffFile.oldSha = 'ABC';
vm.diffFile.viewPath = '';
});
it('should have image diff view in place', done => {
vm.$nextTick(() => {
expect(vm.$el.querySelectorAll('.js-diff-inline-view').length).toEqual(0);
expect(vm.$el.querySelectorAll('.diff-viewer .image').length).toEqual(1);
done();
});
});
});
describe('file diff', () => {
it('should have download buttons in place', done => {
const el = vm.$el;
vm.diffFile.newPath = 'test.abc';
vm.diffFile.newSha = 'DEF';
vm.diffFile.oldPath = 'test.abc';
vm.diffFile.oldSha = 'ABC';
vm.$nextTick(() => {
expect(el.querySelectorAll('.js-diff-inline-view').length).toEqual(0);
expect(el.querySelector('.deleted .file-info').textContent.trim()).toContain('test.abc');
expect(el.querySelector('.deleted .btn.btn-default').textContent.trim()).toContain(
'Download',
);
expect(el.querySelector('.added .file-info').textContent.trim()).toContain('test.abc');
expect(el.querySelector('.added .btn.btn-default').textContent.trim()).toContain(
'Download',
);
done();
});
});
});
});
});
......@@ -12,15 +12,16 @@ import axios from '~/lib/utils/axios_utils';
import testAction from '../../helpers/vuex_action_helper';
describe('DiffsStoreActions', () => {
describe('setEndpoint', () => {
it('should set given endpoint', done => {
describe('setBaseConfig', () => {
it('should set given endpoint and project path', done => {
const endpoint = '/diffs/set/endpoint';
const projectPath = '/root/project';
testAction(
actions.setEndpoint,
endpoint,
{ endpoint: '' },
[{ type: types.SET_ENDPOINT, payload: endpoint }],
actions.setBaseConfig,
{ endpoint, projectPath },
{ endpoint: '', projectPath: '' },
[{ type: types.SET_BASE_CONFIG, payload: { endpoint, projectPath } }],
[],
done,
);
......
......@@ -3,13 +3,15 @@ import * as types from '~/diffs/store/mutation_types';
import { INLINE_DIFF_VIEW_TYPE } from '~/diffs/constants';
describe('DiffsStoreMutations', () => {
describe('SET_ENDPOINT', () => {
it('should set endpoint', () => {
describe('SET_BASE_CONFIG', () => {
it('should set endpoint and project path', () => {
const state = {};
const endpoint = '/diffs/endpoint';
const projectPath = '/root/project';
mutations[types.SET_ENDPOINT](state, endpoint);
mutations[types.SET_BASE_CONFIG](state, { endpoint, projectPath });
expect(state.endpoint).toEqual(endpoint);
expect(state.projectPath).toEqual(projectPath);
});
});
......
......@@ -6,6 +6,7 @@ import diffFileMockData from '../diffs/mock_data/diff_file';
export default function initVueMRPage() {
const diffsAppEndpoint = '/diffs/app/endpoint';
const diffsAppProjectPath = 'testproject';
const mrEl = document.createElement('div');
mrEl.className = 'merge-request fixture-mr';
mrEl.setAttribute('data-mr-action', 'diffs');
......@@ -26,6 +27,7 @@ export default function initVueMRPage() {
const diffsAppEl = document.createElement('div');
diffsAppEl.id = 'js-diffs-app';
diffsAppEl.setAttribute('data-endpoint', diffsAppEndpoint);
diffsAppEl.setAttribute('data-project-path', diffsAppProjectPath);
diffsAppEl.setAttribute('data-current-user-data', JSON.stringify(userDataMock));
document.body.appendChild(diffsAppEl);
......
import * as pathUtils from 'path';
import { decorateData } from '~/ide/stores/utils';
import state from '~/ide/stores/state';
import commitState from '~/ide/stores/modules/commit/state';
......@@ -14,13 +15,34 @@ export const resetStore = store => {
store.replaceState(newState);
};
export const file = (name = 'name', id = name, type = '') =>
export const file = (name = 'name', id = name, type = '', parent = null) =>
decorateData({
id,
type,
icon: 'icon',
url: 'url',
name,
path: name,
path: parent ? `${parent.path}/${name}` : name,
parentPath: parent ? parent.path : '',
lastCommit: {},
});
export const createEntriesFromPaths = paths =>
paths
.map(path => ({
name: pathUtils.basename(path),
dir: pathUtils.dirname(path),
ext: pathUtils.extname(path),
}))
.reduce((entries, path, idx) => {
const { name } = path;
const parent = path.dir ? entries[path.dir] : null;
const type = path.ext ? 'blob' : 'tree';
const entry = file(name, (idx + 1).toString(), type, parent);
return {
[entry.path]: entry,
...entries,
};
}, {});
import Vue from 'vue';
import testAction from 'spec/helpers/vuex_action_helper';
import { showTreeEntry } from '~/ide/stores/actions/tree';
import * as types from '~/ide/stores/mutation_types';
import store from '~/ide/stores';
import service from '~/ide/services';
import router from '~/ide/ide_router';
import { file, resetStore } from '../../helpers';
import { file, resetStore, createEntriesFromPaths } from '../../helpers';
describe('Multi-file store tree actions', () => {
let projectTree;
......@@ -96,6 +99,37 @@ describe('Multi-file store tree actions', () => {
});
});
describe('showTreeEntry', () => {
beforeEach(() => {
const paths = [
'grandparent',
'ancestor',
'grandparent/parent',
'grandparent/aunt',
'grandparent/parent/child.txt',
'grandparent/aunt/cousing.txt',
];
Object.assign(store.state.entries, createEntriesFromPaths(paths));
});
it('opens the parents', done => {
testAction(
showTreeEntry,
'grandparent/parent/child.txt',
store.state,
[
{ type: types.SET_TREE_OPEN, payload: 'grandparent/parent' },
{ type: types.SET_TREE_OPEN, payload: 'grandparent' },
],
[
{ type: 'showTreeEntry' },
],
done,
);
});
});
describe('getLastCommitData', () => {
beforeEach(() => {
spyOn(service, 'getTreeLastCommit').and.returnValue(
......
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