Commit dc1e6b43 authored by Filipa Lacerda's avatar Filipa Lacerda

Merge branch 'multi-file-editor-separate-commits-call' into 'master'

Multi-file editor fetch log data from a different endpoint

Closes #38360

See merge request gitlab-org/gitlab-ce!15132
parents ad526918 bd5ea484
<script> <script>
import { mapActions, mapGetters } from 'vuex'; import { mapActions, mapGetters } from 'vuex';
import timeAgoMixin from '../../vue_shared/mixins/timeago'; import timeAgoMixin from '../../vue_shared/mixins/timeago';
import skeletonLoadingContainer from '../../vue_shared/components/skeleton_loading_container.vue';
export default { export default {
mixins: [ mixins: [
timeAgoMixin, timeAgoMixin,
], ],
components: {
skeletonLoadingContainer,
},
props: { props: {
file: { file: {
type: Object, type: Object,
...@@ -16,6 +20,9 @@ ...@@ -16,6 +20,9 @@
...mapGetters([ ...mapGetters([
'isCollapsed', 'isCollapsed',
]), ]),
isSubmodule() {
return this.file.type === 'submodule';
},
fileIcon() { fileIcon() {
return { return {
'fa-spinner fa-spin': this.file.loading, 'fa-spinner fa-spin': this.file.loading,
...@@ -31,6 +38,9 @@ ...@@ -31,6 +38,9 @@
shortId() { shortId() {
return this.file.id.substr(0, 8); return this.file.id.substr(0, 8);
}, },
submoduleColSpan() {
return !this.isCollapsed && this.isSubmodule ? 3 : 1;
},
}, },
methods: { methods: {
...mapActions([ ...mapActions([
...@@ -44,7 +54,7 @@ ...@@ -44,7 +54,7 @@
<tr <tr
class="file" class="file"
@click.prevent="clickedTreeRow(file)"> @click.prevent="clickedTreeRow(file)">
<td> <td :colspan="submoduleColSpan">
<i <i
class="fa fa-fw file-icon" class="fa fa-fw file-icon"
:class="fileIcon" :class="fileIcon"
...@@ -58,7 +68,7 @@ ...@@ -58,7 +68,7 @@
> >
{{ file.name }} {{ file.name }}
</a> </a>
<template v-if="file.type === 'submodule' && file.id"> <template v-if="isSubmodule && file.id">
@ @
<span class="commit-sha"> <span class="commit-sha">
<a <a
...@@ -71,15 +81,20 @@ ...@@ -71,15 +81,20 @@
</template> </template>
</td> </td>
<template v-if="!isCollapsed"> <template v-if="!isCollapsed && !isSubmodule">
<td class="hidden-sm hidden-xs"> <td class="hidden-sm hidden-xs">
<a <a
v-if="file.lastCommit.message"
@click.stop @click.stop
:href="file.lastCommit.url" :href="file.lastCommit.url"
class="commit-message" class="commit-message"
> >
{{ file.lastCommit.message }} {{ file.lastCommit.message }}
</a> </a>
<skeleton-loading-container
v-else
:small="true"
/>
</td> </td>
<td class="commit-update hidden-xs text-right"> <td class="commit-update hidden-xs text-right">
...@@ -89,6 +104,11 @@ ...@@ -89,6 +104,11 @@
> >
{{ timeFormated(file.lastCommit.updatedAt) }} {{ timeFormated(file.lastCommit.updatedAt) }}
</span> </span>
<skeleton-loading-container
v-else
class="animation-container-right"
:small="true"
/>
</td> </td>
</template> </template>
</tr> </tr>
......
<script> <script>
import { mapGetters } from 'vuex'; import { mapGetters } from 'vuex';
import skeletonLoadingContainer from '../../vue_shared/components/skeleton_loading_container.vue';
export default { export default {
components: {
skeletonLoadingContainer,
},
computed: { computed: {
...mapGetters([ ...mapGetters([
'isCollapsed', 'isCollapsed',
]), ]),
}, },
methods: {
lineOfCode(n) {
return `skeleton-line-${n}`;
},
},
}; };
</script> </script>
...@@ -21,36 +20,24 @@ ...@@ -21,36 +20,24 @@
aria-label="Loading files" aria-label="Loading files"
> >
<td> <td>
<div <skeleton-loading-container
class="animation-container animation-container-small"> :small="true"
<div />
v-for="n in 6"
:key="n"
:class="lineOfCode(n)">
</div>
</div>
</td> </td>
<template v-if="!isCollapsed"> <template v-if="!isCollapsed">
<td <td
class="hidden-sm hidden-xs"> class="hidden-sm hidden-xs">
<div class="animation-container"> <skeleton-loading-container
<div :small="true"
v-for="n in 6" />
:key="n"
:class="lineOfCode(n)">
</div>
</div>
</td> </td>
<td <td
class="hidden-xs"> class="hidden-xs">
<div class="animation-container animation-container-small animation-container-right"> <skeleton-loading-container
<div class="animation-container-right"
v-for="n in 6" :small="true"
:key="n" />
:class="lineOfCode(n)">
</div>
</div>
</td> </td>
</template> </template>
</tr> </tr>
......
...@@ -80,7 +80,7 @@ export default { ...@@ -80,7 +80,7 @@ export default {
/> />
<repo-file <repo-file
v-for="(file, index) in treeList" v-for="(file, index) in treeList"
:key="index" :key="file.key"
:file="file" :file="file"
/> />
</tbody> </tbody>
......
...@@ -30,4 +30,11 @@ export default { ...@@ -30,4 +30,11 @@ export default {
commit(projectId, payload) { commit(projectId, payload) {
return Api.commitMultiple(projectId, payload); return Api.commitMultiple(projectId, payload);
}, },
getTreeLastCommit(endpoint) {
return Vue.http.get(endpoint, {
params: {
format: 'json',
},
});
},
}; };
...@@ -64,7 +64,7 @@ export const checkCommitStatus = ({ state }) => service.getBranchData( ...@@ -64,7 +64,7 @@ export const checkCommitStatus = ({ state }) => service.getBranchData(
}) })
.catch(() => flash('Error checking branch data. Please try again.')); .catch(() => flash('Error checking branch data. Please try again.'));
export const commitChanges = ({ commit, state, dispatch }, { payload, newMr }) => export const commitChanges = ({ commit, state, dispatch, getters }, { payload, newMr }) =>
service.commit(state.project.id, payload) service.commit(state.project.id, payload)
.then((data) => { .then((data) => {
const { branch } = payload; const { branch } = payload;
...@@ -73,12 +73,28 @@ export const commitChanges = ({ commit, state, dispatch }, { payload, newMr }) = ...@@ -73,12 +73,28 @@ export const commitChanges = ({ commit, state, dispatch }, { payload, newMr }) =
return; return;
} }
const lastCommit = {
commit_path: `${state.project.url}/commit/${data.id}`,
commit: {
message: data.message,
authored_date: data.committed_date,
},
};
flash(`Your changes have been committed. Commit ${data.short_id} with ${data.stats.additions} additions, ${data.stats.deletions} deletions.`, 'notice'); flash(`Your changes have been committed. Commit ${data.short_id} with ${data.stats.additions} additions, ${data.stats.deletions} deletions.`, 'notice');
if (newMr) { if (newMr) {
redirectToUrl(`${state.endpoints.newMergeRequestUrl}${branch}`); redirectToUrl(`${state.endpoints.newMergeRequestUrl}${branch}`);
} else { } else {
commit(types.SET_COMMIT_REF, data.id); commit(types.SET_COMMIT_REF, data.id);
getters.changedFiles.forEach((entry) => {
commit(types.SET_LAST_COMMIT_DATA, {
entry,
lastCommit,
});
});
dispatch('discardAllChanges'); dispatch('discardAllChanges');
dispatch('closeAllFiles'); dispatch('closeAllFiles');
dispatch('toggleEditMode'); dispatch('toggleEditMode');
......
...@@ -27,6 +27,8 @@ export const closeFile = ({ commit, state, dispatch }, { file, force = false }) ...@@ -27,6 +27,8 @@ export const closeFile = ({ commit, state, dispatch }, { file, force = false })
} else if (!state.openFiles.length) { } else if (!state.openFiles.length) {
pushState(file.parentTreeUrl); pushState(file.parentTreeUrl);
} }
dispatch('getLastCommitData');
}; };
export const setFileActive = ({ commit, state, getters, dispatch }, file) => { export const setFileActive = ({ commit, state, getters, dispatch }, file) => {
......
...@@ -7,10 +7,11 @@ import { ...@@ -7,10 +7,11 @@ import {
setPageTitle, setPageTitle,
findEntry, findEntry,
createTemp, createTemp,
createOrMergeEntry,
} from '../utils'; } from '../utils';
export const getTreeData = ( export const getTreeData = (
{ commit, state }, { commit, state, dispatch },
{ endpoint = state.endpoints.rootEndpoint, tree = state } = {}, { endpoint = state.endpoints.rootEndpoint, tree = state } = {},
) => { ) => {
commit(types.TOGGLE_LOADING, tree); commit(types.TOGGLE_LOADING, tree);
...@@ -24,14 +25,20 @@ export const getTreeData = ( ...@@ -24,14 +25,20 @@ export const getTreeData = (
return res.json(); return res.json();
}) })
.then((data) => { .then((data) => {
const prevLastCommitPath = tree.lastCommitPath;
if (!state.isInitialRoot) { if (!state.isInitialRoot) {
commit(types.SET_ROOT, data.path === '/'); commit(types.SET_ROOT, data.path === '/');
} }
commit(types.SET_DIRECTORY_DATA, { data, tree }); dispatch('updateDirectoryData', { data, tree });
commit(types.SET_PARENT_TREE_URL, data.parent_tree_url); commit(types.SET_PARENT_TREE_URL, data.parent_tree_url);
commit(types.SET_LAST_COMMIT_URL, { tree, url: data.last_commit_path });
commit(types.TOGGLE_LOADING, tree); commit(types.TOGGLE_LOADING, tree);
if (prevLastCommitPath !== null) {
dispatch('getLastCommitData', tree);
}
pushState(endpoint); pushState(endpoint);
}) })
.catch(() => { .catch(() => {
...@@ -48,7 +55,7 @@ export const toggleTreeOpen = ({ commit, dispatch }, { endpoint, tree }) => { ...@@ -48,7 +55,7 @@ export const toggleTreeOpen = ({ commit, dispatch }, { endpoint, tree }) => {
pushState(tree.parentTreeUrl); pushState(tree.parentTreeUrl);
commit(types.SET_PREVIOUS_URL, tree.parentTreeUrl); commit(types.SET_PREVIOUS_URL, tree.parentTreeUrl);
commit(types.SET_DIRECTORY_DATA, { data, tree }); dispatch('updateDirectoryData', { data, tree });
} else { } else {
commit(types.SET_PREVIOUS_URL, endpoint); commit(types.SET_PREVIOUS_URL, endpoint);
dispatch('getTreeData', { endpoint, tree }); dispatch('getTreeData', { endpoint, tree });
...@@ -108,3 +115,48 @@ export const createTempTree = ({ state, commit, dispatch }, name) => { ...@@ -108,3 +115,48 @@ export const createTempTree = ({ state, commit, dispatch }, name) => {
}); });
} }
}; };
export const getLastCommitData = ({ state, commit, dispatch, getters }, tree = state) => {
if (tree.lastCommitPath === null || getters.isCollapsed) return;
service.getTreeLastCommit(tree.lastCommitPath)
.then((res) => {
const lastCommitPath = normalizeHeaders(res.headers)['MORE-LOGS-URL'] || null;
commit(types.SET_LAST_COMMIT_URL, { tree, url: lastCommitPath });
return res.json();
})
.then((data) => {
data.forEach((lastCommit) => {
const entry = findEntry(tree, lastCommit.type, lastCommit.file_name);
if (entry) {
commit(types.SET_LAST_COMMIT_DATA, { entry, lastCommit });
}
});
dispatch('getLastCommitData', tree);
})
.catch(() => flash('Error fetching log data.'));
};
export const updateDirectoryData = ({ commit, state }, { data, tree }) => {
const level = tree.level !== undefined ? tree.level + 1 : 0;
const parentTreeUrl = data.parent_tree_url ? `${data.parent_tree_url}${data.path}` : state.endpoints.rootUrl;
const createEntry = (entry, type) => createOrMergeEntry({
tree,
entry,
level,
type,
parentTreeUrl,
});
const formattedData = [
...data.trees.map(t => createEntry(t, 'tree')),
...data.submodules.map(m => createEntry(m, 'submodule')),
...data.blobs.map(b => createEntry(b, 'blob')),
];
commit(types.SET_DIRECTORY_DATA, { tree, data: formattedData });
};
...@@ -4,11 +4,13 @@ export const SET_COMMIT_REF = 'SET_COMMIT_REF'; ...@@ -4,11 +4,13 @@ export const SET_COMMIT_REF = 'SET_COMMIT_REF';
export const SET_PARENT_TREE_URL = 'SET_PARENT_TREE_URL'; export const SET_PARENT_TREE_URL = 'SET_PARENT_TREE_URL';
export const SET_ROOT = 'SET_ROOT'; export const SET_ROOT = 'SET_ROOT';
export const SET_PREVIOUS_URL = 'SET_PREVIOUS_URL'; export const SET_PREVIOUS_URL = 'SET_PREVIOUS_URL';
export const SET_LAST_COMMIT_DATA = 'SET_LAST_COMMIT_DATA';
// Tree mutation types // Tree mutation types
export const SET_DIRECTORY_DATA = 'SET_DIRECTORY_DATA'; export const SET_DIRECTORY_DATA = 'SET_DIRECTORY_DATA';
export const TOGGLE_TREE_OPEN = 'TOGGLE_TREE_OPEN'; export const TOGGLE_TREE_OPEN = 'TOGGLE_TREE_OPEN';
export const CREATE_TMP_TREE = 'CREATE_TMP_TREE'; export const CREATE_TMP_TREE = 'CREATE_TMP_TREE';
export const SET_LAST_COMMIT_URL = 'SET_LAST_COMMIT_URL';
// File mutation types // File mutation types
export const SET_FILE_DATA = 'SET_FILE_DATA'; export const SET_FILE_DATA = 'SET_FILE_DATA';
......
...@@ -48,6 +48,13 @@ export default { ...@@ -48,6 +48,13 @@ export default {
previousUrl, previousUrl,
}); });
}, },
[types.SET_LAST_COMMIT_DATA](state, { entry, lastCommit }) {
Object.assign(entry.lastCommit, {
url: lastCommit.commit_path,
message: lastCommit.commit.message,
updatedAt: lastCommit.commit.authored_date,
});
},
...fileMutations, ...fileMutations,
...treeMutations, ...treeMutations,
...branchMutations, ...branchMutations,
......
import * as types from '../mutation_types'; import * as types from '../mutation_types';
import * as utils from '../utils';
export default { export default {
[types.TOGGLE_TREE_OPEN](state, tree) { [types.TOGGLE_TREE_OPEN](state, tree) {
...@@ -8,30 +7,8 @@ export default { ...@@ -8,30 +7,8 @@ export default {
}); });
}, },
[types.SET_DIRECTORY_DATA](state, { data, tree }) { [types.SET_DIRECTORY_DATA](state, { data, tree }) {
const level = tree.level !== undefined ? tree.level + 1 : 0;
const parentTreeUrl = data.parent_tree_url ? `${data.parent_tree_url}${data.path}` : state.endpoints.rootUrl;
Object.assign(tree, { Object.assign(tree, {
tree: [ tree: data,
...data.trees.map(t => utils.decorateData({
...t,
type: 'tree',
parentTreeUrl,
level,
}, state.project.url)),
...data.submodules.map(m => utils.decorateData({
...m,
type: 'submodule',
parentTreeUrl,
level,
}, state.project.url)),
...data.blobs.map(b => utils.decorateData({
...b,
type: 'blob',
parentTreeUrl,
level,
}, state.project.url)),
],
}); });
}, },
[types.SET_PARENT_TREE_URL](state, url) { [types.SET_PARENT_TREE_URL](state, url) {
...@@ -39,6 +16,11 @@ export default { ...@@ -39,6 +16,11 @@ export default {
parentTreeUrl: url, parentTreeUrl: url,
}); });
}, },
[types.SET_LAST_COMMIT_URL](state, { tree = state, url }) {
Object.assign(tree, {
lastCommitPath: url,
});
},
[types.CREATE_TMP_TREE](state, { parent, tmpEntry }) { [types.CREATE_TMP_TREE](state, { parent, tmpEntry }) {
parent.tree.push(tmpEntry); parent.tree.push(tmpEntry);
}, },
......
...@@ -8,6 +8,7 @@ export default () => ({ ...@@ -8,6 +8,7 @@ export default () => ({
endpoints: {}, endpoints: {},
isRoot: false, isRoot: false,
isInitialRoot: false, isInitialRoot: false,
lastCommitPath: '',
loading: false, loading: false,
onTopOfBranch: false, onTopOfBranch: false,
openFiles: [], openFiles: [],
......
export const dataStructure = () => ({ export const dataStructure = () => ({
id: '', id: '',
key: '',
type: '', type: '',
name: '', name: '',
url: '', url: '',
...@@ -12,7 +13,12 @@ export const dataStructure = () => ({ ...@@ -12,7 +13,12 @@ export const dataStructure = () => ({
opened: false, opened: false,
active: false, active: false,
changed: false, changed: false,
lastCommit: {}, lastCommitPath: '',
lastCommit: {
url: '',
message: '',
updatedAt: '',
},
tree_url: '', tree_url: '',
blamePath: '', blamePath: '',
commitsPath: '', commitsPath: '',
...@@ -27,14 +33,13 @@ export const dataStructure = () => ({ ...@@ -27,14 +33,13 @@ export const dataStructure = () => ({
base64: false, base64: false,
}); });
export const decorateData = (entity, projectUrl = '') => { export const decorateData = (entity) => {
const { const {
id, id,
type, type,
url, url,
name, name,
icon, icon,
last_commit,
tree_url, tree_url,
path, path,
renderError, renderError,
...@@ -51,6 +56,7 @@ export const decorateData = (entity, projectUrl = '') => { ...@@ -51,6 +56,7 @@ export const decorateData = (entity, projectUrl = '') => {
return { return {
...dataStructure(), ...dataStructure(),
id, id,
key: `${name}-${type}-${id}`,
type, type,
name, name,
url, url,
...@@ -66,12 +72,6 @@ export const decorateData = (entity, projectUrl = '') => { ...@@ -66,12 +72,6 @@ export const decorateData = (entity, projectUrl = '') => {
renderError, renderError,
content, content,
base64, base64,
// eslint-disable-next-line camelcase
lastCommit: last_commit ? {
url: `${projectUrl}/commit/${last_commit.id}`,
message: last_commit.message,
updatedAt: last_commit.committed_date,
} : {},
}; };
}; };
...@@ -106,3 +106,22 @@ export const createTemp = ({ name, path, type, level, changed, content, base64 } ...@@ -106,3 +106,22 @@ export const createTemp = ({ name, path, type, level, changed, content, base64 }
renderError: base64, renderError: base64,
}); });
}; };
export const createOrMergeEntry = ({ tree, entry, type, parentTreeUrl, level }) => {
const found = findEntry(tree, type, entry.name);
if (found) {
return Object.assign({}, found, {
id: entry.id,
url: entry.url,
tempFile: false,
});
}
return decorateData({
...entry,
type,
parentTreeUrl,
level,
});
};
<script>
export default {
props: {
small: {
type: Boolean,
required: false,
default: false,
},
lines: {
type: Number,
required: false,
default: 6,
},
},
computed: {
lineClasses() {
return new Array(this.lines).fill().map((_, i) => `skeleton-line-${i + 1}`);
},
},
};
</script>
<template>
<div
class="animation-container"
:class="{
'animation-container-small': small,
}"
>
<div
v-for="(css, index) in lineClasses"
:key="index"
:class="css"
>
</div>
</div>
</template>
...@@ -56,9 +56,12 @@ class Projects::RefsController < Projects::ApplicationController ...@@ -56,9 +56,12 @@ class Projects::RefsController < Projects::ApplicationController
contents[@offset, @limit].to_a.map do |content| contents[@offset, @limit].to_a.map do |content|
file = @path ? File.join(@path, content.name) : content.name file = @path ? File.join(@path, content.name) : content.name
last_commit = @repo.last_commit_for_path(@commit.id, file) last_commit = @repo.last_commit_for_path(@commit.id, file)
commit_path = project_commit_path(@project, last_commit) if last_commit
{ {
file_name: content.name, file_name: content.name,
commit: last_commit commit: last_commit,
type: content.type,
commit_path: commit_path
} }
end end
end end
...@@ -70,6 +73,11 @@ class Projects::RefsController < Projects::ApplicationController ...@@ -70,6 +73,11 @@ class Projects::RefsController < Projects::ApplicationController
respond_to do |format| respond_to do |format|
format.html { render_404 } format.html { render_404 }
format.json do
response.headers["More-Logs-Url"] = @more_log_url
render json: @logs
end
format.js format.js
end end
end end
......
...@@ -3,10 +3,6 @@ class BlobEntity < Grape::Entity ...@@ -3,10 +3,6 @@ class BlobEntity < Grape::Entity
expose :id, :path, :name, :mode expose :id, :path, :name, :mode
expose :last_commit do |blob|
request.project.repository.last_commit_for_path(blob.commit_id, blob.path)
end
expose :icon do |blob| expose :icon do |blob|
IconsHelper.file_type_icon_class('file', blob.mode, blob.name) IconsHelper.file_type_icon_class('file', blob.mode, blob.name)
end end
......
...@@ -3,10 +3,6 @@ class TreeEntity < Grape::Entity ...@@ -3,10 +3,6 @@ class TreeEntity < Grape::Entity
expose :id, :path, :name, :mode expose :id, :path, :name, :mode
expose :last_commit do |tree|
request.project.repository.last_commit_for_path(tree.commit_id, tree.path)
end
expose :icon do |tree| expose :icon do |tree|
IconsHelper.file_type_icon_class('folder', tree.mode, tree.name) IconsHelper.file_type_icon_class('folder', tree.mode, tree.name)
end end
......
...@@ -18,4 +18,8 @@ class TreeRootEntity < Grape::Entity ...@@ -18,4 +18,8 @@ class TreeRootEntity < Grape::Entity
project_tree_path(request.project, File.join(request.ref, parent_tree_path)) project_tree_path(request.project, File.join(request.ref, parent_tree_path))
end end
expose :last_commit_path do |tree|
logs_file_project_ref_path(request.project, request.ref, tree.path)
end
end end
...@@ -23,12 +23,15 @@ describe Projects::RefsController do ...@@ -23,12 +23,15 @@ describe Projects::RefsController do
xhr :get, xhr :get,
:logs_tree, :logs_tree,
namespace_id: project.namespace.to_param, namespace_id: project.namespace.to_param,
project_id: project, id: 'master', project_id: project,
path: 'foo/bar/baz.html', format: format id: 'master',
path: 'foo/bar/baz.html',
format: format
end end
it 'never throws MissingTemplate' do it 'never throws MissingTemplate' do
expect { default_get }.not_to raise_error expect { default_get }.not_to raise_error
expect { xhr_get(:json) }.not_to raise_error
expect { xhr_get }.not_to raise_error expect { xhr_get }.not_to raise_error
end end
...@@ -42,5 +45,12 @@ describe Projects::RefsController do ...@@ -42,5 +45,12 @@ describe Projects::RefsController do
xhr_get(:js) xhr_get(:js)
expect(response).to be_success expect(response).to be_success
end end
it 'renders JSON' do
xhr_get(:json)
expect(response).to be_success
expect(json_response).to be_kind_of(Array)
end
end end
end end
...@@ -31,10 +31,5 @@ feature 'Multi-file editor new directory', :js do ...@@ -31,10 +31,5 @@ feature 'Multi-file editor new directory', :js do
click_button('Commit 1 file') click_button('Commit 1 file')
expect(page).to have_selector('td', text: 'commit message') expect(page).to have_selector('td', text: 'commit message')
click_link('foldername')
expect(page).to have_selector('td', text: 'commit message', count: 2)
expect(page).to have_selector('td', text: '.gitkeep')
end end
end end
...@@ -20,7 +20,7 @@ describe('RepoFile', () => { ...@@ -20,7 +20,7 @@ describe('RepoFile', () => {
resetStore(vm.$store); resetStore(vm.$store);
}); });
it('renders link, icon, name and last commit details', () => { it('renders link, icon and name', () => {
const RepoFile = Vue.extend(repoFile); const RepoFile = Vue.extend(repoFile);
vm = new RepoFile({ vm = new RepoFile({
store, store,
...@@ -37,10 +37,9 @@ describe('RepoFile', () => { ...@@ -37,10 +37,9 @@ describe('RepoFile', () => {
expect(vm.$el.querySelector(`.${vm.file.icon}`).style.marginLeft).toEqual('0px'); expect(vm.$el.querySelector(`.${vm.file.icon}`).style.marginLeft).toEqual('0px');
expect(name.href).toMatch(`/${vm.file.url}`); expect(name.href).toMatch(`/${vm.file.url}`);
expect(name.textContent.trim()).toEqual(vm.file.name); expect(name.textContent.trim()).toEqual(vm.file.name);
expect(vm.$el.querySelector('.commit-message').textContent.trim()).toBe(vm.file.lastCommit.message);
expect(vm.$el.querySelector('.commit-update').textContent.trim()).toBe(updated);
expect(fileIcon.classList.contains(vm.file.icon)).toBeTruthy(); expect(fileIcon.classList.contains(vm.file.icon)).toBeTruthy();
expect(fileIcon.style.marginLeft).toEqual(`${vm.file.level * 10}px`); expect(fileIcon.style.marginLeft).toEqual(`${vm.file.level * 10}px`);
expect(vm.$el.querySelectorAll('.animation-container').length).toBe(2);
}); });
it('does render if hasFiles is true and is loading tree', () => { it('does render if hasFiles is true and is loading tree', () => {
......
import Vue from 'vue';
import skeletonLoadingContainer from '~/vue_shared/components/skeleton_loading_container.vue';
import mountComponent from '../../helpers/vue_mount_component_helper';
describe('Skeleton loading container', () => {
let vm;
beforeEach(() => {
const component = Vue.extend(skeletonLoadingContainer);
vm = mountComponent(component);
});
afterEach(() => {
vm.$destroy();
});
it('renders 6 skeleton lines by default', () => {
expect(vm.$el.querySelector('.skeleton-line-6')).not.toBeNull();
});
it('renders in full mode by default', () => {
expect(vm.$el.classList.contains('animation-container-small')).toBeFalsy();
});
describe('small', () => {
beforeEach((done) => {
vm.small = true;
Vue.nextTick(done);
});
it('renders in small mode', () => {
expect(vm.$el.classList.contains('animation-container-small')).toBeTruthy();
});
});
describe('lines', () => {
beforeEach((done) => {
vm.lines = 5;
Vue.nextTick(done);
});
it('renders 5 lines', () => {
expect(vm.$el.querySelector('.skeleton-line-5')).not.toBeNull();
expect(vm.$el.querySelector('.skeleton-line-6')).toBeNull();
});
});
});
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