added specs
order initial dropdown by lastOpenedAt date
This commit is contained in:
parent
b0d84c1e0e
commit
71b80a11fe
7 changed files with 423 additions and 10 deletions
|
@ -50,6 +50,7 @@ export default {
|
||||||
this.searchText = '';
|
this.searchText = '';
|
||||||
} else {
|
} else {
|
||||||
this.focusedIndex = 0;
|
this.focusedIndex = 0;
|
||||||
|
|
||||||
this.$refs.searchInput.focus();
|
this.$refs.searchInput.focus();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -65,12 +66,20 @@ export default {
|
||||||
case 38:
|
case 38:
|
||||||
// UP
|
// UP
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
if (this.focusedIndex > 0) this.focusedIndex -= 1;
|
if (this.focusedIndex > 0) {
|
||||||
|
this.focusedIndex -= 1;
|
||||||
|
} else {
|
||||||
|
this.focusedIndex = this.filteredBlobsLength - 1;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case 40:
|
case 40:
|
||||||
// DOWN
|
// DOWN
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
if (this.focusedIndex < this.filteredBlobsLength - 1) this.focusedIndex += 1;
|
if (this.focusedIndex < this.filteredBlobsLength - 1) {
|
||||||
|
this.focusedIndex += 1;
|
||||||
|
} else {
|
||||||
|
this.focusedIndex = 0;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
@ -146,7 +155,7 @@ export default {
|
||||||
v-else
|
v-else
|
||||||
class="dropdown-menu-empty-itemhidden"
|
class="dropdown-menu-empty-itemhidden"
|
||||||
>
|
>
|
||||||
<a href="">
|
<a href="#">
|
||||||
<template v-if="loading">
|
<template v-if="loading">
|
||||||
{{ __('Loading...') }}
|
{{ __('Loading...') }}
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -93,6 +93,7 @@ export default {
|
||||||
:size="16"
|
:size="16"
|
||||||
/>
|
/>
|
||||||
{{ file.name }}
|
{{ file.name }}
|
||||||
|
{{ file.lastOpenedAt }}
|
||||||
<file-status-icon
|
<file-status-icon
|
||||||
:file="file"
|
:file="file"
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -37,12 +37,14 @@ export const hasChanges = state => !!state.changedFiles.length;
|
||||||
export const hasMergeRequest = state => !!state.currentMergeRequestId;
|
export const hasMergeRequest = state => !!state.currentMergeRequestId;
|
||||||
|
|
||||||
export const allBlobs = state =>
|
export const allBlobs = state =>
|
||||||
Object.keys(state.entries).reduce((acc, key) => {
|
Object.keys(state.entries)
|
||||||
const entry = state.entries[key];
|
.reduce((acc, key) => {
|
||||||
|
const entry = state.entries[key];
|
||||||
|
|
||||||
if (entry.type === 'blob') {
|
if (entry.type === 'blob') {
|
||||||
acc.push(entry);
|
acc.push(entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
return acc;
|
return acc;
|
||||||
}, []);
|
}, [])
|
||||||
|
.sort((a, b) => b.lastOpenedAt > a.lastOpenedAt);
|
||||||
|
|
|
@ -4,6 +4,7 @@ export default {
|
||||||
[types.SET_FILE_ACTIVE](state, { path, active }) {
|
[types.SET_FILE_ACTIVE](state, { path, active }) {
|
||||||
Object.assign(state.entries[path], {
|
Object.assign(state.entries[path], {
|
||||||
active,
|
active,
|
||||||
|
lastOpenedAt: new Date().getTime(),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (active && !state.entries[path].pending) {
|
if (active && !state.entries[path].pending) {
|
||||||
|
|
|
@ -41,6 +41,7 @@ export const dataStructure = () => ({
|
||||||
viewMode: 'edit',
|
viewMode: 'edit',
|
||||||
previewMode: null,
|
previewMode: null,
|
||||||
size: 0,
|
size: 0,
|
||||||
|
lastOpenedAt: 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
export const decorateData = entity => {
|
export const decorateData = entity => {
|
||||||
|
|
260
spec/javascripts/ide/components/file_finder/index_spec.js
Normal file
260
spec/javascripts/ide/components/file_finder/index_spec.js
Normal file
|
@ -0,0 +1,260 @@
|
||||||
|
import Vue from 'vue';
|
||||||
|
import store from '~/ide/stores';
|
||||||
|
import FindFileComponent from '~/ide/components/file_finder/index.vue';
|
||||||
|
import router from '~/ide/ide_router';
|
||||||
|
import { file, resetStore } from '../../helpers';
|
||||||
|
import { mountComponentWithStore } from '../../../helpers/vue_mount_component_helper';
|
||||||
|
|
||||||
|
describe('IDE File finder item spec', () => {
|
||||||
|
const Component = Vue.extend(FindFileComponent);
|
||||||
|
let vm;
|
||||||
|
|
||||||
|
beforeEach(done => {
|
||||||
|
setFixtures('<div id="app"></div>');
|
||||||
|
|
||||||
|
vm = mountComponentWithStore(Component, {
|
||||||
|
store,
|
||||||
|
el: '#app',
|
||||||
|
});
|
||||||
|
|
||||||
|
setTimeout(done);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
vm.$destroy();
|
||||||
|
|
||||||
|
resetStore(vm.$store);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('with entries', () => {
|
||||||
|
beforeEach(done => {
|
||||||
|
Vue.set(vm.$store.state.entries, 'folder', {
|
||||||
|
...file('folder'),
|
||||||
|
path: 'folder',
|
||||||
|
type: 'folder',
|
||||||
|
});
|
||||||
|
|
||||||
|
Vue.set(vm.$store.state.entries, 'index.js', {
|
||||||
|
...file('index.js'),
|
||||||
|
path: 'index.js',
|
||||||
|
type: 'blob',
|
||||||
|
url: '/index.jsurl',
|
||||||
|
});
|
||||||
|
|
||||||
|
Vue.set(vm.$store.state.entries, 'component.js', {
|
||||||
|
...file('component.js'),
|
||||||
|
path: 'component.js',
|
||||||
|
type: 'blob',
|
||||||
|
});
|
||||||
|
|
||||||
|
setTimeout(done);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders list of blobs', () => {
|
||||||
|
expect(vm.$el.textContent).toContain('index.js');
|
||||||
|
expect(vm.$el.textContent).toContain('component.js');
|
||||||
|
expect(vm.$el.textContent).not.toContain('folder');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('filters entries', done => {
|
||||||
|
vm.searchText = 'index';
|
||||||
|
|
||||||
|
vm.$nextTick(() => {
|
||||||
|
expect(vm.$el.textContent).toContain('index.js');
|
||||||
|
expect(vm.$el.textContent).not.toContain('component.js');
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('listShowCount', () => {
|
||||||
|
it('returns 1 when no filtered entries exist', done => {
|
||||||
|
vm.searchText = 'testing 123';
|
||||||
|
|
||||||
|
vm.$nextTick(() => {
|
||||||
|
expect(vm.listShowCount).toBe(1);
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns entries length when not filtered', () => {
|
||||||
|
expect(vm.listShowCount).toBe(2);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('listHeight', () => {
|
||||||
|
it('returns 55 when entries exist', () => {
|
||||||
|
expect(vm.listHeight).toBe(55);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns 33 when entries dont exist', done => {
|
||||||
|
vm.searchText = 'testing 123';
|
||||||
|
|
||||||
|
vm.$nextTick(() => {
|
||||||
|
expect(vm.listHeight).toBe(33);
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('filteredBlobsLength', () => {
|
||||||
|
it('returns length of filtered blobs', done => {
|
||||||
|
vm.searchText = 'index';
|
||||||
|
|
||||||
|
vm.$nextTick(() => {
|
||||||
|
expect(vm.filteredBlobsLength).toBe(1);
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('watches', () => {
|
||||||
|
describe('searchText', () => {
|
||||||
|
it('resets focusedIndex when updated', done => {
|
||||||
|
vm.focusedIndex = 1;
|
||||||
|
vm.searchText = 'test';
|
||||||
|
|
||||||
|
vm.$nextTick(() => {
|
||||||
|
expect(vm.focusedIndex).toBe(0);
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('fileFindVisible', () => {
|
||||||
|
it('returns searchText when false', done => {
|
||||||
|
vm.searchText = 'test';
|
||||||
|
vm.$store.state.fileFindVisible = true;
|
||||||
|
|
||||||
|
vm
|
||||||
|
.$nextTick()
|
||||||
|
.then(() => {
|
||||||
|
vm.$store.state.fileFindVisible = false;
|
||||||
|
})
|
||||||
|
.then(vm.$nextTick)
|
||||||
|
.then(() => {
|
||||||
|
expect(vm.searchText).toBe('');
|
||||||
|
})
|
||||||
|
.then(done)
|
||||||
|
.catch(done.fail);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('openFile', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
spyOn(router, 'push');
|
||||||
|
spyOn(vm, 'toggleFileFinder');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('closes file finder', () => {
|
||||||
|
vm.openFile(vm.$store.state.entries['index.js']);
|
||||||
|
|
||||||
|
expect(vm.toggleFileFinder).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('pushes to router', () => {
|
||||||
|
vm.openFile(vm.$store.state.entries['index.js']);
|
||||||
|
|
||||||
|
expect(router.push).toHaveBeenCalledWith('/project/index.jsurl');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('onKeyup', () => {
|
||||||
|
it('opens file on enter key', done => {
|
||||||
|
const event = new CustomEvent('keyup');
|
||||||
|
event.keyCode = 13;
|
||||||
|
|
||||||
|
spyOn(vm, 'openFile');
|
||||||
|
|
||||||
|
vm.$refs.searchInput.dispatchEvent(event);
|
||||||
|
|
||||||
|
vm.$nextTick(() => {
|
||||||
|
expect(vm.openFile).toHaveBeenCalledWith(vm.$store.state.entries['index.js']);
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('closes file finder on esc key', done => {
|
||||||
|
const event = new CustomEvent('keyup');
|
||||||
|
event.keyCode = 27;
|
||||||
|
|
||||||
|
spyOn(vm, 'toggleFileFinder');
|
||||||
|
|
||||||
|
vm.$refs.searchInput.dispatchEvent(event);
|
||||||
|
|
||||||
|
vm.$nextTick(() => {
|
||||||
|
expect(vm.toggleFileFinder).toHaveBeenCalled();
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('onKeyDown', () => {
|
||||||
|
let el;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
el = vm.$refs.searchInput;
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('up key', () => {
|
||||||
|
const event = new CustomEvent('keydown');
|
||||||
|
event.keyCode = 38;
|
||||||
|
|
||||||
|
it('resets to last index when at top', () => {
|
||||||
|
el.dispatchEvent(event);
|
||||||
|
|
||||||
|
expect(vm.focusedIndex).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('minus 1 from focusedIndex', () => {
|
||||||
|
vm.focusedIndex = 1;
|
||||||
|
|
||||||
|
el.dispatchEvent(event);
|
||||||
|
|
||||||
|
expect(vm.focusedIndex).toBe(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('down key', () => {
|
||||||
|
const event = new CustomEvent('keydown');
|
||||||
|
event.keyCode = 40;
|
||||||
|
|
||||||
|
it('resets to first index when at bottom', () => {
|
||||||
|
vm.focusedIndex = 1;
|
||||||
|
el.dispatchEvent(event);
|
||||||
|
|
||||||
|
expect(vm.focusedIndex).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('adds 1 to focusedIndex', () => {
|
||||||
|
el.dispatchEvent(event);
|
||||||
|
|
||||||
|
expect(vm.focusedIndex).toBe(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('without entries', () => {
|
||||||
|
it('renders loading text when loading', done => {
|
||||||
|
store.state.loading = true;
|
||||||
|
|
||||||
|
vm.$nextTick(() => {
|
||||||
|
expect(vm.$el.textContent).toContain('Loading...');
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders no files text', () => {
|
||||||
|
expect(vm.$el.textContent).toContain('No files found.');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
139
spec/javascripts/ide/components/file_finder/item_spec.js
Normal file
139
spec/javascripts/ide/components/file_finder/item_spec.js
Normal file
|
@ -0,0 +1,139 @@
|
||||||
|
import Vue from 'vue';
|
||||||
|
import ItemComponent from '~/ide/components/file_finder/item.vue';
|
||||||
|
import { file } from '../../helpers';
|
||||||
|
import createComponent from '../../../helpers/vue_mount_component_helper';
|
||||||
|
|
||||||
|
describe('IDE File finder item spec', () => {
|
||||||
|
const Component = Vue.extend(ItemComponent);
|
||||||
|
let vm;
|
||||||
|
let localFile;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
localFile = {
|
||||||
|
...file(),
|
||||||
|
name: 'test file',
|
||||||
|
path: 'test/file',
|
||||||
|
};
|
||||||
|
|
||||||
|
vm = createComponent(Component, {
|
||||||
|
file: localFile,
|
||||||
|
focused: true,
|
||||||
|
searchText: '',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
vm.$destroy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders file name & path', () => {
|
||||||
|
expect(vm.$el.textContent).toContain('test file');
|
||||||
|
expect(vm.$el.textContent).toContain('test/file');
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('focused', () => {
|
||||||
|
it('adds is-focused class', () => {
|
||||||
|
expect(vm.$el.classList).toContain('is-focused');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not have is-focused class when not focused', done => {
|
||||||
|
vm.focused = false;
|
||||||
|
|
||||||
|
vm.$nextTick(() => {
|
||||||
|
expect(vm.$el.classList).not.toContain('is-focused');
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('changed file icon', () => {
|
||||||
|
it('does not render when not a changed or temp file', () => {
|
||||||
|
expect(vm.$el.querySelector('.diff-changed-stats')).toBe(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders when a changed file', done => {
|
||||||
|
vm.file.changed = true;
|
||||||
|
|
||||||
|
vm.$nextTick(() => {
|
||||||
|
expect(vm.$el.querySelector('.diff-changed-stats')).not.toBe(null);
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders when a temp file', done => {
|
||||||
|
vm.file.tempFile = true;
|
||||||
|
|
||||||
|
vm.$nextTick(() => {
|
||||||
|
expect(vm.$el.querySelector('.diff-changed-stats')).not.toBe(null);
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('emits event when clicked', () => {
|
||||||
|
spyOn(vm, '$emit');
|
||||||
|
|
||||||
|
vm.$el.click();
|
||||||
|
|
||||||
|
expect(vm.$emit).toHaveBeenCalledWith('click', vm.file);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('path', () => {
|
||||||
|
let el;
|
||||||
|
|
||||||
|
beforeEach(done => {
|
||||||
|
vm.searchText = 'file';
|
||||||
|
|
||||||
|
el = vm.$el.querySelector('.diff-changed-file-path');
|
||||||
|
|
||||||
|
vm.$nextTick(done);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('highlights text', () => {
|
||||||
|
expect(el.querySelectorAll('.highlighted').length).toBe(4);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('adds ellipsis to long text', done => {
|
||||||
|
vm.file.path = new Array(70)
|
||||||
|
.fill()
|
||||||
|
.map((_, i) => `${i}-`)
|
||||||
|
.join('');
|
||||||
|
|
||||||
|
vm.$nextTick(() => {
|
||||||
|
expect(el.textContent).toBe(`...${vm.file.path.substr(vm.file.path.length - 60)}`);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('name', () => {
|
||||||
|
let el;
|
||||||
|
|
||||||
|
beforeEach(done => {
|
||||||
|
vm.searchText = 'file';
|
||||||
|
|
||||||
|
el = vm.$el.querySelector('.diff-changed-file-name');
|
||||||
|
|
||||||
|
vm.$nextTick(done);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('highlights text', () => {
|
||||||
|
expect(el.querySelectorAll('.highlighted').length).toBe(4);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not add ellipsis to long text', done => {
|
||||||
|
vm.file.name = new Array(70)
|
||||||
|
.fill()
|
||||||
|
.map((_, i) => `${i}-`)
|
||||||
|
.join('');
|
||||||
|
|
||||||
|
vm.$nextTick(() => {
|
||||||
|
expect(el.textContent).not.toBe(`...${vm.file.name.substr(vm.file.name.length - 60)}`);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
Loading…
Reference in a new issue