Merge branch 'winh-promise-rejects-fail-tests' into 'master'
Make JavaScript tests fail for unhandled Promise rejections Closes #33845 and #33623 See merge request !12264
This commit is contained in:
commit
801cf92310
8 changed files with 254 additions and 157 deletions
|
@ -17,7 +17,7 @@ export default {
|
|||
methods: {
|
||||
submit(e) {
|
||||
e.preventDefault();
|
||||
if (this.title.trim() === '') return;
|
||||
if (this.title.trim() === '') return Promise.resolve();
|
||||
|
||||
this.error = false;
|
||||
|
||||
|
@ -29,7 +29,10 @@ export default {
|
|||
assignees: [],
|
||||
});
|
||||
|
||||
this.list.newIssue(issue)
|
||||
eventHub.$emit(`scroll-board-list-${this.list.id}`);
|
||||
this.cancel();
|
||||
|
||||
return this.list.newIssue(issue)
|
||||
.then(() => {
|
||||
// Need this because our jQuery very kindly disables buttons on ALL form submissions
|
||||
$(this.$refs.submitButton).enable();
|
||||
|
@ -47,9 +50,6 @@ export default {
|
|||
// Show error message
|
||||
this.error = true;
|
||||
});
|
||||
|
||||
eventHub.$emit(`scroll-board-list-${this.list.id}`);
|
||||
this.cancel();
|
||||
},
|
||||
cancel() {
|
||||
this.title = '';
|
||||
|
|
|
@ -112,8 +112,7 @@ class List {
|
|||
.then((resp) => {
|
||||
const data = resp.json();
|
||||
issue.id = data.iid;
|
||||
})
|
||||
.then(() => {
|
||||
|
||||
if (this.issuesSize > 1) {
|
||||
const moveBeforeIid = this.issues[1].id;
|
||||
gl.boardService.moveIssue(issue.id, null, null, null, moveBeforeIid);
|
||||
|
|
|
@ -40,6 +40,10 @@ class FilteredSearchManager {
|
|||
return [];
|
||||
})
|
||||
.then((searches) => {
|
||||
if (!searches) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Put any searches that may have come in before
|
||||
// we fetched the saved searches ahead of the already saved ones
|
||||
const resultantSearches = this.recentSearchesStore.setRecentSearches(
|
||||
|
|
|
@ -12,6 +12,7 @@ import './mock_data';
|
|||
describe('Issue boards new issue form', () => {
|
||||
let vm;
|
||||
let list;
|
||||
let newIssueMock;
|
||||
const promiseReturn = {
|
||||
json() {
|
||||
return {
|
||||
|
@ -21,7 +22,11 @@ describe('Issue boards new issue form', () => {
|
|||
};
|
||||
|
||||
const submitIssue = () => {
|
||||
vm.$el.querySelector('.btn-success').click();
|
||||
const dummySubmitEvent = {
|
||||
preventDefault() {},
|
||||
};
|
||||
vm.$refs.submitButton = vm.$el.querySelector('.btn-success');
|
||||
return vm.submit(dummySubmitEvent);
|
||||
};
|
||||
|
||||
beforeEach((done) => {
|
||||
|
@ -32,29 +37,35 @@ describe('Issue boards new issue form', () => {
|
|||
gl.issueBoards.BoardsStore.create();
|
||||
gl.IssueBoardsApp = new Vue();
|
||||
|
||||
setTimeout(() => {
|
||||
list = new List(listObj);
|
||||
list = new List(listObj);
|
||||
|
||||
spyOn(gl.boardService, 'newIssue').and.callFake(() => new Promise((resolve, reject) => {
|
||||
if (vm.title === 'error') {
|
||||
reject();
|
||||
} else {
|
||||
resolve(promiseReturn);
|
||||
}
|
||||
}));
|
||||
newIssueMock = Promise.resolve(promiseReturn);
|
||||
spyOn(list, 'newIssue').and.callFake(() => newIssueMock);
|
||||
|
||||
vm = new BoardNewIssueComp({
|
||||
propsData: {
|
||||
list,
|
||||
},
|
||||
}).$mount();
|
||||
vm = new BoardNewIssueComp({
|
||||
propsData: {
|
||||
list,
|
||||
},
|
||||
}).$mount();
|
||||
|
||||
done();
|
||||
}, 0);
|
||||
Vue.nextTick()
|
||||
.then(done)
|
||||
.catch(done.fail);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
Vue.http.interceptors = _.without(Vue.http.interceptors, boardsMockInterceptor);
|
||||
it('calls submit if submit button is clicked', (done) => {
|
||||
spyOn(vm, 'submit');
|
||||
vm.title = 'Testing Title';
|
||||
|
||||
Vue.nextTick()
|
||||
.then(() => {
|
||||
vm.$el.querySelector('.btn-success').click();
|
||||
|
||||
expect(vm.submit.calls.count()).toBe(1);
|
||||
expect(vm.$refs['submit-button']).toBe(vm.$el.querySelector('.btn-success'));
|
||||
})
|
||||
.then(done)
|
||||
.catch(done.fail);
|
||||
});
|
||||
|
||||
it('disables submit button if title is empty', () => {
|
||||
|
@ -64,136 +75,122 @@ describe('Issue boards new issue form', () => {
|
|||
it('enables submit button if title is not empty', (done) => {
|
||||
vm.title = 'Testing Title';
|
||||
|
||||
setTimeout(() => {
|
||||
expect(vm.$el.querySelector('.form-control').value).toBe('Testing Title');
|
||||
expect(vm.$el.querySelector('.btn-success').disabled).not.toBe(true);
|
||||
|
||||
done();
|
||||
}, 0);
|
||||
Vue.nextTick()
|
||||
.then(() => {
|
||||
expect(vm.$el.querySelector('.form-control').value).toBe('Testing Title');
|
||||
expect(vm.$el.querySelector('.btn-success').disabled).not.toBe(true);
|
||||
})
|
||||
.then(done)
|
||||
.catch(done.fail);
|
||||
});
|
||||
|
||||
it('clears title after clicking cancel', (done) => {
|
||||
vm.$el.querySelector('.btn-default').click();
|
||||
|
||||
setTimeout(() => {
|
||||
expect(vm.title).toBe('');
|
||||
done();
|
||||
}, 0);
|
||||
Vue.nextTick()
|
||||
.then(() => {
|
||||
expect(vm.title).toBe('');
|
||||
})
|
||||
.then(done)
|
||||
.catch(done.fail);
|
||||
});
|
||||
|
||||
it('does not create new issue if title is empty', (done) => {
|
||||
submitIssue();
|
||||
|
||||
setTimeout(() => {
|
||||
expect(gl.boardService.newIssue).not.toHaveBeenCalled();
|
||||
done();
|
||||
}, 0);
|
||||
submitIssue()
|
||||
.then(() => {
|
||||
expect(list.newIssue).not.toHaveBeenCalled();
|
||||
})
|
||||
.then(done)
|
||||
.catch(done.fail);
|
||||
});
|
||||
|
||||
describe('submit success', () => {
|
||||
it('creates new issue', (done) => {
|
||||
vm.title = 'submit title';
|
||||
|
||||
setTimeout(() => {
|
||||
submitIssue();
|
||||
|
||||
expect(gl.boardService.newIssue).toHaveBeenCalled();
|
||||
done();
|
||||
}, 0);
|
||||
Vue.nextTick()
|
||||
.then(submitIssue)
|
||||
.then(() => {
|
||||
expect(list.newIssue).toHaveBeenCalled();
|
||||
})
|
||||
.then(done)
|
||||
.catch(done.fail);
|
||||
});
|
||||
|
||||
it('enables button after submit', (done) => {
|
||||
vm.title = 'submit issue';
|
||||
|
||||
setTimeout(() => {
|
||||
submitIssue();
|
||||
|
||||
expect(vm.$el.querySelector('.btn-success').disabled).toBe(false);
|
||||
done();
|
||||
}, 0);
|
||||
Vue.nextTick()
|
||||
.then(submitIssue)
|
||||
.then(() => {
|
||||
expect(vm.$el.querySelector('.btn-success').disabled).toBe(false);
|
||||
})
|
||||
.then(done)
|
||||
.catch(done.fail);
|
||||
});
|
||||
|
||||
it('clears title after submit', (done) => {
|
||||
vm.title = 'submit issue';
|
||||
|
||||
Vue.nextTick(() => {
|
||||
submitIssue();
|
||||
|
||||
setTimeout(() => {
|
||||
Vue.nextTick()
|
||||
.then(submitIssue)
|
||||
.then(() => {
|
||||
expect(vm.title).toBe('');
|
||||
done();
|
||||
}, 0);
|
||||
});
|
||||
});
|
||||
|
||||
it('adds new issue to top of list after submit request', (done) => {
|
||||
vm.title = 'submit issue';
|
||||
|
||||
setTimeout(() => {
|
||||
submitIssue();
|
||||
|
||||
setTimeout(() => {
|
||||
expect(list.issues.length).toBe(2);
|
||||
expect(list.issues[0].title).toBe('submit issue');
|
||||
expect(list.issues[0].subscribed).toBe(true);
|
||||
done();
|
||||
}, 0);
|
||||
}, 0);
|
||||
})
|
||||
.then(done)
|
||||
.catch(done.fail);
|
||||
});
|
||||
|
||||
it('sets detail issue after submit', (done) => {
|
||||
expect(gl.issueBoards.BoardsStore.detail.issue.title).toBe(undefined);
|
||||
vm.title = 'submit issue';
|
||||
|
||||
setTimeout(() => {
|
||||
submitIssue();
|
||||
|
||||
setTimeout(() => {
|
||||
Vue.nextTick()
|
||||
.then(submitIssue)
|
||||
.then(() => {
|
||||
expect(gl.issueBoards.BoardsStore.detail.issue.title).toBe('submit issue');
|
||||
done();
|
||||
}, 0);
|
||||
}, 0);
|
||||
})
|
||||
.then(done)
|
||||
.catch(done.fail);
|
||||
});
|
||||
|
||||
it('sets detail list after submit', (done) => {
|
||||
vm.title = 'submit issue';
|
||||
|
||||
setTimeout(() => {
|
||||
submitIssue();
|
||||
|
||||
setTimeout(() => {
|
||||
Vue.nextTick()
|
||||
.then(submitIssue)
|
||||
.then(() => {
|
||||
expect(gl.issueBoards.BoardsStore.detail.list.id).toBe(list.id);
|
||||
done();
|
||||
}, 0);
|
||||
}, 0);
|
||||
})
|
||||
.then(done)
|
||||
.catch(done.fail);
|
||||
});
|
||||
});
|
||||
|
||||
describe('submit error', () => {
|
||||
it('removes issue', (done) => {
|
||||
beforeEach(() => {
|
||||
newIssueMock = Promise.reject(new Error('My hovercraft is full of eels!'));
|
||||
vm.title = 'error';
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
submitIssue();
|
||||
|
||||
setTimeout(() => {
|
||||
it('removes issue', (done) => {
|
||||
Vue.nextTick()
|
||||
.then(submitIssue)
|
||||
.then(() => {
|
||||
expect(list.issues.length).toBe(1);
|
||||
done();
|
||||
}, 0);
|
||||
}, 0);
|
||||
})
|
||||
.then(done)
|
||||
.catch(done.fail);
|
||||
});
|
||||
|
||||
it('shows error', (done) => {
|
||||
vm.title = 'error';
|
||||
|
||||
setTimeout(() => {
|
||||
submitIssue();
|
||||
|
||||
setTimeout(() => {
|
||||
Vue.nextTick()
|
||||
.then(submitIssue)
|
||||
.then(() => {
|
||||
expect(vm.error).toBe(true);
|
||||
done();
|
||||
}, 0);
|
||||
}, 0);
|
||||
})
|
||||
.then(done)
|
||||
.catch(done.fail);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -150,4 +150,41 @@ describe('List model', () => {
|
|||
expect(list.getIssues).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('newIssue', () => {
|
||||
beforeEach(() => {
|
||||
spyOn(gl.boardService, 'newIssue').and.returnValue(Promise.resolve({
|
||||
json() {
|
||||
return {
|
||||
iid: 42,
|
||||
};
|
||||
},
|
||||
}));
|
||||
});
|
||||
|
||||
it('adds new issue to top of list', (done) => {
|
||||
list.issues.push(new ListIssue({
|
||||
title: 'Testing',
|
||||
iid: _.random(10000),
|
||||
confidential: false,
|
||||
labels: [list.label],
|
||||
assignees: [],
|
||||
}));
|
||||
const dummyIssue = new ListIssue({
|
||||
title: 'new issue',
|
||||
iid: _.random(10000),
|
||||
confidential: false,
|
||||
labels: [list.label],
|
||||
assignees: [],
|
||||
});
|
||||
|
||||
list.newIssue(dummyIssue)
|
||||
.then(() => {
|
||||
expect(list.issues.length).toBe(2);
|
||||
expect(list.issues[0]).toBe(dummyIssue);
|
||||
})
|
||||
.then(done)
|
||||
.catch(done.fail);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -48,18 +48,23 @@ describe('Filtered Search Manager', () => {
|
|||
</div>
|
||||
`);
|
||||
|
||||
spyOn(gl.FilteredSearchDropdownManager.prototype, 'setDropdown').and.callFake(() => {});
|
||||
});
|
||||
|
||||
const initializeManager = () => {
|
||||
/* eslint-disable jasmine/no-unsafe-spy */
|
||||
spyOn(gl.FilteredSearchManager.prototype, 'loadSearchParamsFromURL').and.callFake(() => {});
|
||||
spyOn(gl.FilteredSearchManager.prototype, 'tokenChange').and.callFake(() => {});
|
||||
spyOn(gl.FilteredSearchDropdownManager.prototype, 'setDropdown').and.callFake(() => {});
|
||||
spyOn(gl.FilteredSearchDropdownManager.prototype, 'updateDropdownOffset').and.callFake(() => {});
|
||||
spyOn(gl.utils, 'getParameterByName').and.returnValue(null);
|
||||
spyOn(gl.FilteredSearchVisualTokens, 'unselectTokens').and.callThrough();
|
||||
/* eslint-enable jasmine/no-unsafe-spy */
|
||||
|
||||
input = document.querySelector('.filtered-search');
|
||||
tokensContainer = document.querySelector('.tokens-container');
|
||||
manager = new gl.FilteredSearchManager();
|
||||
manager.setup();
|
||||
});
|
||||
};
|
||||
|
||||
afterEach(() => {
|
||||
manager.cleanup();
|
||||
|
@ -67,33 +72,34 @@ describe('Filtered Search Manager', () => {
|
|||
|
||||
describe('class constructor', () => {
|
||||
const isLocalStorageAvailable = 'isLocalStorageAvailable';
|
||||
let filteredSearchManager;
|
||||
|
||||
beforeEach(() => {
|
||||
spyOn(RecentSearchesService, 'isAvailable').and.returnValue(isLocalStorageAvailable);
|
||||
spyOn(recentSearchesStoreSrc, 'default');
|
||||
spyOn(RecentSearchesRoot.prototype, 'render');
|
||||
|
||||
filteredSearchManager = new gl.FilteredSearchManager();
|
||||
filteredSearchManager.setup();
|
||||
|
||||
return filteredSearchManager;
|
||||
});
|
||||
|
||||
it('should instantiate RecentSearchesStore with isLocalStorageAvailable', () => {
|
||||
manager = new gl.FilteredSearchManager();
|
||||
|
||||
expect(RecentSearchesService.isAvailable).toHaveBeenCalled();
|
||||
expect(recentSearchesStoreSrc.default).toHaveBeenCalledWith({
|
||||
isLocalStorageAvailable,
|
||||
allowedKeys: gl.FilteredSearchTokenKeys.getKeys(),
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('setup', () => {
|
||||
beforeEach(() => {
|
||||
manager = new gl.FilteredSearchManager();
|
||||
});
|
||||
|
||||
it('should not instantiate Flash if an RecentSearchesServiceError is caught', () => {
|
||||
spyOn(RecentSearchesService.prototype, 'fetch').and.callFake(() => Promise.reject(new RecentSearchesServiceError()));
|
||||
spyOn(window, 'Flash');
|
||||
|
||||
filteredSearchManager = new gl.FilteredSearchManager();
|
||||
filteredSearchManager.setup();
|
||||
manager.setup();
|
||||
|
||||
expect(window.Flash).not.toHaveBeenCalled();
|
||||
});
|
||||
|
@ -102,6 +108,7 @@ describe('Filtered Search Manager', () => {
|
|||
describe('searchState', () => {
|
||||
beforeEach(() => {
|
||||
spyOn(gl.FilteredSearchManager.prototype, 'search').and.callFake(() => {});
|
||||
initializeManager();
|
||||
});
|
||||
|
||||
it('should blur button', () => {
|
||||
|
@ -148,6 +155,10 @@ describe('Filtered Search Manager', () => {
|
|||
describe('search', () => {
|
||||
const defaultParams = '?scope=all&utf8=%E2%9C%93&state=opened';
|
||||
|
||||
beforeEach(() => {
|
||||
initializeManager();
|
||||
});
|
||||
|
||||
it('should search with a single word', (done) => {
|
||||
input.value = 'searchTerm';
|
||||
|
||||
|
@ -197,6 +208,10 @@ describe('Filtered Search Manager', () => {
|
|||
});
|
||||
|
||||
describe('handleInputPlaceholder', () => {
|
||||
beforeEach(() => {
|
||||
initializeManager();
|
||||
});
|
||||
|
||||
it('should render placeholder when there is no input', () => {
|
||||
expect(input.placeholder).toEqual(placeholder);
|
||||
});
|
||||
|
@ -223,6 +238,10 @@ describe('Filtered Search Manager', () => {
|
|||
});
|
||||
|
||||
describe('checkForBackspace', () => {
|
||||
beforeEach(() => {
|
||||
initializeManager();
|
||||
});
|
||||
|
||||
describe('tokens and no input', () => {
|
||||
beforeEach(() => {
|
||||
tokensContainer.innerHTML = FilteredSearchSpecHelper.createTokensContainerHTML(
|
||||
|
@ -260,6 +279,10 @@ describe('Filtered Search Manager', () => {
|
|||
});
|
||||
|
||||
describe('removeToken', () => {
|
||||
beforeEach(() => {
|
||||
initializeManager();
|
||||
});
|
||||
|
||||
it('removes token even when it is already selected', () => {
|
||||
tokensContainer.innerHTML = FilteredSearchSpecHelper.createTokensContainerHTML(
|
||||
FilteredSearchSpecHelper.createFilterVisualTokenHTML('milestone', 'none', true),
|
||||
|
@ -291,6 +314,7 @@ describe('Filtered Search Manager', () => {
|
|||
|
||||
describe('removeSelectedTokenKeydown', () => {
|
||||
beforeEach(() => {
|
||||
initializeManager();
|
||||
tokensContainer.innerHTML = FilteredSearchSpecHelper.createTokensContainerHTML(
|
||||
FilteredSearchSpecHelper.createFilterVisualTokenHTML('milestone', 'none', true),
|
||||
);
|
||||
|
@ -344,27 +368,39 @@ describe('Filtered Search Manager', () => {
|
|||
spyOn(gl.FilteredSearchVisualTokens, 'removeSelectedToken').and.callThrough();
|
||||
spyOn(gl.FilteredSearchManager.prototype, 'handleInputPlaceholder').and.callThrough();
|
||||
spyOn(gl.FilteredSearchManager.prototype, 'toggleClearSearchButton').and.callThrough();
|
||||
manager.removeSelectedToken();
|
||||
initializeManager();
|
||||
});
|
||||
|
||||
it('calls FilteredSearchVisualTokens.removeSelectedToken', () => {
|
||||
manager.removeSelectedToken();
|
||||
|
||||
expect(gl.FilteredSearchVisualTokens.removeSelectedToken).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('calls handleInputPlaceholder', () => {
|
||||
manager.removeSelectedToken();
|
||||
|
||||
expect(manager.handleInputPlaceholder).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('calls toggleClearSearchButton', () => {
|
||||
manager.removeSelectedToken();
|
||||
|
||||
expect(manager.toggleClearSearchButton).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('calls update dropdown offset', () => {
|
||||
manager.removeSelectedToken();
|
||||
|
||||
expect(manager.dropdownManager.updateDropdownOffset).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('toggleInputContainerFocus', () => {
|
||||
beforeEach(() => {
|
||||
initializeManager();
|
||||
});
|
||||
|
||||
it('toggles on focus', () => {
|
||||
input.focus();
|
||||
expect(document.querySelector('.filtered-search-box').classList.contains('focus')).toEqual(true);
|
||||
|
|
|
@ -3,17 +3,9 @@ import '~/render_math';
|
|||
import '~/render_gfm';
|
||||
import issuableApp from '~/issue_show/components/app.vue';
|
||||
import eventHub from '~/issue_show/event_hub';
|
||||
import Poll from '~/lib/utils/poll';
|
||||
import issueShowData from '../mock_data';
|
||||
|
||||
const issueShowInterceptor = data => (request, next) => {
|
||||
next(request.respondWith(JSON.stringify(data), {
|
||||
status: 200,
|
||||
headers: {
|
||||
'POLL-INTERVAL': 1,
|
||||
},
|
||||
}));
|
||||
};
|
||||
|
||||
function formatText(text) {
|
||||
return text.trim().replace(/\s\s+/g, ' ');
|
||||
}
|
||||
|
@ -24,10 +16,10 @@ describe('Issuable output', () => {
|
|||
let vm;
|
||||
|
||||
beforeEach(() => {
|
||||
const IssuableDescriptionComponent = Vue.extend(issuableApp);
|
||||
Vue.http.interceptors.push(issueShowInterceptor(issueShowData.initialRequest));
|
||||
|
||||
spyOn(eventHub, '$emit');
|
||||
spyOn(Poll.prototype, 'makeRequest');
|
||||
|
||||
const IssuableDescriptionComponent = Vue.extend(issuableApp);
|
||||
|
||||
vm = new IssuableDescriptionComponent({
|
||||
propsData: {
|
||||
|
@ -54,9 +46,18 @@ describe('Issuable output', () => {
|
|||
});
|
||||
|
||||
it('should render a title/description/edited and update title/description/edited on update', (done) => {
|
||||
setTimeout(() => {
|
||||
const editedText = vm.$el.querySelector('.edited-text');
|
||||
vm.poll.options.successCallback({
|
||||
json() {
|
||||
return issueShowData.initialRequest;
|
||||
},
|
||||
});
|
||||
|
||||
let editedText;
|
||||
Vue.nextTick()
|
||||
.then(() => {
|
||||
editedText = vm.$el.querySelector('.edited-text');
|
||||
})
|
||||
.then(() => {
|
||||
expect(document.querySelector('title').innerText).toContain('this is a title (#1)');
|
||||
expect(vm.$el.querySelector('.title').innerHTML).toContain('<p>this is a title</p>');
|
||||
expect(vm.$el.querySelector('.wiki').innerHTML).toContain('<p>this is a description!</p>');
|
||||
|
@ -64,22 +65,27 @@ describe('Issuable output', () => {
|
|||
expect(formatText(editedText.innerText)).toMatch(/Edited[\s\S]+?by Some User/);
|
||||
expect(editedText.querySelector('.author_link').href).toMatch(/\/some_user$/);
|
||||
expect(editedText.querySelector('time')).toBeTruthy();
|
||||
|
||||
Vue.http.interceptors.push(issueShowInterceptor(issueShowData.secondRequest));
|
||||
|
||||
setTimeout(() => {
|
||||
expect(document.querySelector('title').innerText).toContain('2 (#1)');
|
||||
expect(vm.$el.querySelector('.title').innerHTML).toContain('<p>2</p>');
|
||||
expect(vm.$el.querySelector('.wiki').innerHTML).toContain('<p>42</p>');
|
||||
expect(vm.$el.querySelector('.js-task-list-field').value).toContain('42');
|
||||
expect(vm.$el.querySelector('.edited-text')).toBeTruthy();
|
||||
expect(formatText(vm.$el.querySelector('.edited-text').innerText)).toMatch(/Edited[\s\S]+?by Other User/);
|
||||
expect(editedText.querySelector('.author_link').href).toMatch(/\/other_user$/);
|
||||
expect(editedText.querySelector('time')).toBeTruthy();
|
||||
|
||||
done();
|
||||
})
|
||||
.then(() => {
|
||||
vm.poll.options.successCallback({
|
||||
json() {
|
||||
return issueShowData.secondRequest;
|
||||
},
|
||||
});
|
||||
});
|
||||
})
|
||||
.then(Vue.nextTick)
|
||||
.then(() => {
|
||||
expect(document.querySelector('title').innerText).toContain('2 (#1)');
|
||||
expect(vm.$el.querySelector('.title').innerHTML).toContain('<p>2</p>');
|
||||
expect(vm.$el.querySelector('.wiki').innerHTML).toContain('<p>42</p>');
|
||||
expect(vm.$el.querySelector('.js-task-list-field').value).toContain('42');
|
||||
expect(vm.$el.querySelector('.edited-text')).toBeTruthy();
|
||||
expect(formatText(vm.$el.querySelector('.edited-text').innerText)).toMatch(/Edited[\s\S]+?by Other User/);
|
||||
expect(editedText.querySelector('.author_link').href).toMatch(/\/other_user$/);
|
||||
expect(editedText.querySelector('time')).toBeTruthy();
|
||||
})
|
||||
.then(done)
|
||||
.catch(done.fail);
|
||||
});
|
||||
|
||||
it('shows actions if permissions are correct', (done) => {
|
||||
|
@ -344,21 +350,23 @@ describe('Issuable output', () => {
|
|||
|
||||
describe('open form', () => {
|
||||
it('shows locked warning if form is open & data is different', (done) => {
|
||||
Vue.http.interceptors.push(issueShowInterceptor(issueShowData.initialRequest));
|
||||
vm.poll.options.successCallback({
|
||||
json() {
|
||||
return issueShowData.initialRequest;
|
||||
},
|
||||
});
|
||||
|
||||
Vue.nextTick()
|
||||
.then(() => new Promise((resolve) => {
|
||||
setTimeout(resolve);
|
||||
}))
|
||||
.then(() => {
|
||||
vm.openForm();
|
||||
|
||||
Vue.http.interceptors.push(issueShowInterceptor(issueShowData.secondRequest));
|
||||
|
||||
return new Promise((resolve) => {
|
||||
setTimeout(resolve);
|
||||
vm.poll.options.successCallback({
|
||||
json() {
|
||||
return issueShowData.secondRequest;
|
||||
},
|
||||
});
|
||||
})
|
||||
.then(Vue.nextTick)
|
||||
.then(() => {
|
||||
expect(
|
||||
vm.formState.lockedWarningVisible,
|
||||
|
@ -367,9 +375,8 @@ describe('Issuable output', () => {
|
|||
expect(
|
||||
vm.$el.querySelector('.alert'),
|
||||
).not.toBeNull();
|
||||
|
||||
done();
|
||||
})
|
||||
.then(done)
|
||||
.catch(done.fail);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -22,6 +22,19 @@ window.gl = window.gl || {};
|
|||
window.gl.TEST_HOST = 'http://test.host';
|
||||
window.gon = window.gon || {};
|
||||
|
||||
let hasUnhandledPromiseRejections = false;
|
||||
|
||||
window.addEventListener('unhandledrejection', (event) => {
|
||||
hasUnhandledPromiseRejections = true;
|
||||
console.error('Unhandled promise rejection:');
|
||||
console.error(event.reason.stack || event.reason);
|
||||
});
|
||||
|
||||
const checkUnhandledPromiseRejections = (done) => {
|
||||
expect(hasUnhandledPromiseRejections).toBe(false);
|
||||
done();
|
||||
};
|
||||
|
||||
// HACK: Chrome 59 disconnects if there are too many synchronous tests in a row
|
||||
// because it appears to lock up the thread that communicates to Karma's socket
|
||||
// This async beforeEach gets called on every spec and releases the JS thread long
|
||||
|
@ -63,6 +76,10 @@ testsContext.keys().forEach(function (path) {
|
|||
}
|
||||
});
|
||||
|
||||
it('has no unhandled Promise rejections', (done) => {
|
||||
setTimeout(checkUnhandledPromiseRejections(done), 1000);
|
||||
});
|
||||
|
||||
// if we're generating coverage reports, make sure to include all files so
|
||||
// that we can catch files with 0% coverage
|
||||
// see: https://github.com/deepsweet/istanbul-instrumenter-loader/issues/15
|
||||
|
|
Loading…
Reference in a new issue