Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
449287e15a
commit
b001207ce2
|
|
@ -95,7 +95,7 @@ export default class FilteredSearchVisualTokens {
|
||||||
const tokenType = tokenName.toLowerCase();
|
const tokenType = tokenName.toLowerCase();
|
||||||
const tokenValueContainer = parentElement.querySelector('.value-container');
|
const tokenValueContainer = parentElement.querySelector('.value-container');
|
||||||
const tokenValueElement = tokenValueContainer.querySelector('.value');
|
const tokenValueElement = tokenValueContainer.querySelector('.value');
|
||||||
tokenValueElement.innerText = tokenValue;
|
tokenValueElement.textContent = tokenValue;
|
||||||
|
|
||||||
const visualTokenValue = new VisualTokenValue(tokenValue, tokenType, tokenOperator);
|
const visualTokenValue = new VisualTokenValue(tokenValue, tokenType, tokenOperator);
|
||||||
|
|
||||||
|
|
@ -140,9 +140,9 @@ export default class FilteredSearchVisualTokens {
|
||||||
li.innerHTML = nameHTML + operatorHTML;
|
li.innerHTML = nameHTML + operatorHTML;
|
||||||
}
|
}
|
||||||
|
|
||||||
li.querySelector('.name').innerText = name;
|
li.querySelector('.name').textContent = name;
|
||||||
if (hasOperator) {
|
if (hasOperator) {
|
||||||
li.querySelector('.operator').innerText = operator;
|
li.querySelector('.operator').textContent = operator;
|
||||||
}
|
}
|
||||||
|
|
||||||
const tokensContainer = FilteredSearchContainer.container.querySelector('.tokens-container');
|
const tokensContainer = FilteredSearchContainer.container.querySelector('.tokens-container');
|
||||||
|
|
@ -162,8 +162,8 @@ export default class FilteredSearchVisualTokens {
|
||||||
lastVisualToken.innerHTML = FilteredSearchVisualTokens.createVisualTokenElementHTML({
|
lastVisualToken.innerHTML = FilteredSearchVisualTokens.createVisualTokenElementHTML({
|
||||||
hasOperator: Boolean(operator),
|
hasOperator: Boolean(operator),
|
||||||
});
|
});
|
||||||
lastVisualToken.querySelector('.name').innerText = name;
|
lastVisualToken.querySelector('.name').textContent = name;
|
||||||
lastVisualToken.querySelector('.operator').innerText = operator;
|
lastVisualToken.querySelector('.operator').textContent = operator;
|
||||||
FilteredSearchVisualTokens.renderVisualTokenValue(lastVisualToken, name, value, operator);
|
FilteredSearchVisualTokens.renderVisualTokenValue(lastVisualToken, name, value, operator);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -208,8 +208,8 @@ export default class FilteredSearchVisualTokens {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
const previousTokenName = lastVisualToken.querySelector('.name').innerText;
|
const previousTokenName = lastVisualToken.querySelector('.name').textContent;
|
||||||
const previousTokenOperator = lastVisualToken.querySelector('.operator').innerText;
|
const previousTokenOperator = lastVisualToken.querySelector('.operator').textContent;
|
||||||
const tokensContainer = FilteredSearchContainer.container.querySelector('.tokens-container');
|
const tokensContainer = FilteredSearchContainer.container.querySelector('.tokens-container');
|
||||||
tokensContainer.removeChild(lastVisualToken);
|
tokensContainer.removeChild(lastVisualToken);
|
||||||
|
|
||||||
|
|
@ -234,7 +234,7 @@ export default class FilteredSearchVisualTokens {
|
||||||
const { lastVisualToken } = FilteredSearchVisualTokens.getLastVisualTokenBeforeInput();
|
const { lastVisualToken } = FilteredSearchVisualTokens.getLastVisualTokenBeforeInput();
|
||||||
|
|
||||||
if (lastVisualToken && lastVisualToken.classList.contains('filtered-search-term')) {
|
if (lastVisualToken && lastVisualToken.classList.contains('filtered-search-term')) {
|
||||||
lastVisualToken.querySelector('.name').innerText += ` ${searchTerm}`;
|
lastVisualToken.querySelector('.name').textContent += ` ${searchTerm}`;
|
||||||
} else {
|
} else {
|
||||||
FilteredSearchVisualTokens.addVisualTokenElement({
|
FilteredSearchVisualTokens.addVisualTokenElement({
|
||||||
name: searchTerm,
|
name: searchTerm,
|
||||||
|
|
@ -261,12 +261,12 @@ export default class FilteredSearchVisualTokens {
|
||||||
const value = lastVisualToken.querySelector('.value');
|
const value = lastVisualToken.querySelector('.value');
|
||||||
const name = lastVisualToken.querySelector('.name');
|
const name = lastVisualToken.querySelector('.name');
|
||||||
|
|
||||||
const valueText = value ? value.innerText : '';
|
const valueText = value ? value.textContent : '';
|
||||||
const nameText = name ? name.innerText : '';
|
const nameText = name ? name.textContent : '';
|
||||||
|
|
||||||
if (includeOperator) {
|
if (includeOperator) {
|
||||||
const operator = lastVisualToken.querySelector('.operator');
|
const operator = lastVisualToken.querySelector('.operator');
|
||||||
const operatorText = operator ? operator.innerText : '';
|
const operatorText = operator ? operator.textContent : '';
|
||||||
return valueText || operatorText || nameText;
|
return valueText || operatorText || nameText;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -278,7 +278,7 @@ export default class FilteredSearchVisualTokens {
|
||||||
|
|
||||||
const operator = lastVisualToken && lastVisualToken.querySelector('.operator');
|
const operator = lastVisualToken && lastVisualToken.querySelector('.operator');
|
||||||
|
|
||||||
return operator?.innerText;
|
return operator?.textContent;
|
||||||
}
|
}
|
||||||
|
|
||||||
static removeLastTokenPartial() {
|
static removeLastTokenPartial() {
|
||||||
|
|
@ -346,8 +346,8 @@ export default class FilteredSearchVisualTokens {
|
||||||
|
|
||||||
if (token.classList.contains('filtered-search-token')) {
|
if (token.classList.contains('filtered-search-token')) {
|
||||||
FilteredSearchVisualTokens.addFilterVisualToken(
|
FilteredSearchVisualTokens.addFilterVisualToken(
|
||||||
nameElement.innerText,
|
nameElement.textContent,
|
||||||
operatorElement.innerText,
|
operatorElement.textContent,
|
||||||
null,
|
null,
|
||||||
{
|
{
|
||||||
uppercaseTokenName: nameElement.classList.contains('text-uppercase'),
|
uppercaseTokenName: nameElement.classList.contains('text-uppercase'),
|
||||||
|
|
@ -359,13 +359,13 @@ export default class FilteredSearchVisualTokens {
|
||||||
|
|
||||||
if (!value) {
|
if (!value) {
|
||||||
const valueElement = valueContainerElement.querySelector('.value');
|
const valueElement = valueContainerElement.querySelector('.value');
|
||||||
value = valueElement.innerText;
|
value = valueElement.textContent;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// token is a search term
|
// token is a search term
|
||||||
if (!value) {
|
if (!value) {
|
||||||
value = nameElement.innerText;
|
value = nameElement.textContent;
|
||||||
}
|
}
|
||||||
|
|
||||||
input.value = value;
|
input.value = value;
|
||||||
|
|
|
||||||
|
|
@ -57,13 +57,18 @@ export const updateRelease = ({ dispatch, state, getters }) => {
|
||||||
const { release } = state;
|
const { release } = state;
|
||||||
const milestones = release.milestones ? release.milestones.map(milestone => milestone.title) : [];
|
const milestones = release.milestones ? release.milestones.map(milestone => milestone.title) : [];
|
||||||
|
|
||||||
return (
|
const updatedRelease = convertObjectPropsToSnakeCase(
|
||||||
api
|
{
|
||||||
.updateRelease(state.projectId, state.tagName, {
|
|
||||||
name: release.name,
|
name: release.name,
|
||||||
description: release.description,
|
description: release.description,
|
||||||
milestones,
|
milestones,
|
||||||
})
|
},
|
||||||
|
{ deep: true },
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
api
|
||||||
|
.updateRelease(state.projectId, state.tagName, updatedRelease)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Currently, we delete all existing links and then
|
* Currently, we delete all existing links and then
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ module Types
|
||||||
field :lists,
|
field :lists,
|
||||||
Types::BoardListType.connection_type,
|
Types::BoardListType.connection_type,
|
||||||
null: true,
|
null: true,
|
||||||
description: 'Lists of the project board',
|
description: 'Lists of the board',
|
||||||
resolver: Resolvers::BoardListsResolver,
|
resolver: Resolvers::BoardListsResolver,
|
||||||
extras: [:lookahead]
|
extras: [:lookahead]
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
title: Return 404 response when redirecting request with invalid url.
|
||||||
|
merge_request: 33492
|
||||||
|
author:
|
||||||
|
type: fixed
|
||||||
|
|
@ -56,7 +56,8 @@ Example response:
|
||||||
},
|
},
|
||||||
"position" : 1,
|
"position" : 1,
|
||||||
"max_issue_count": 0,
|
"max_issue_count": 0,
|
||||||
"max_issue_weight": 0
|
"max_issue_weight": 0,
|
||||||
|
"limit_metric": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id" : 2,
|
"id" : 2,
|
||||||
|
|
@ -67,7 +68,8 @@ Example response:
|
||||||
},
|
},
|
||||||
"position" : 2,
|
"position" : 2,
|
||||||
"max_issue_count": 0,
|
"max_issue_count": 0,
|
||||||
"max_issue_weight": 0
|
"max_issue_weight": 0,
|
||||||
|
"limit_metric": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id" : 3,
|
"id" : 3,
|
||||||
|
|
@ -78,7 +80,8 @@ Example response:
|
||||||
},
|
},
|
||||||
"position" : 3,
|
"position" : 3,
|
||||||
"max_issue_count": 0,
|
"max_issue_count": 0,
|
||||||
"max_issue_weight": 0
|
"max_issue_weight": 0,
|
||||||
|
"limit_metric": null
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
@ -131,7 +134,8 @@ Example response:
|
||||||
},
|
},
|
||||||
"position" : 1,
|
"position" : 1,
|
||||||
"max_issue_count": 0,
|
"max_issue_count": 0,
|
||||||
"max_issue_weight": 0
|
"max_issue_weight": 0,
|
||||||
|
"limit_metric": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id" : 2,
|
"id" : 2,
|
||||||
|
|
@ -142,7 +146,8 @@ Example response:
|
||||||
},
|
},
|
||||||
"position" : 2,
|
"position" : 2,
|
||||||
"max_issue_count": 0,
|
"max_issue_count": 0,
|
||||||
"max_issue_weight": 0
|
"max_issue_weight": 0,
|
||||||
|
"limit_metric": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id" : 3,
|
"id" : 3,
|
||||||
|
|
@ -153,7 +158,8 @@ Example response:
|
||||||
},
|
},
|
||||||
"position" : 3,
|
"position" : 3,
|
||||||
"max_issue_count": 0,
|
"max_issue_count": 0,
|
||||||
"max_issue_weight": 0
|
"max_issue_weight": 0,
|
||||||
|
"limit_metric": null
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
@ -205,7 +211,8 @@ Example response:
|
||||||
},
|
},
|
||||||
"position" : 1,
|
"position" : 1,
|
||||||
"max_issue_count": 0,
|
"max_issue_count": 0,
|
||||||
"max_issue_weight": 0
|
"max_issue_weight": 0,
|
||||||
|
"limit_metric": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id" : 2,
|
"id" : 2,
|
||||||
|
|
@ -216,7 +223,8 @@ Example response:
|
||||||
},
|
},
|
||||||
"position" : 2,
|
"position" : 2,
|
||||||
"max_issue_count": 0,
|
"max_issue_count": 0,
|
||||||
"max_issue_weight": 0
|
"max_issue_weight": 0,
|
||||||
|
"limit_metric": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id" : 3,
|
"id" : 3,
|
||||||
|
|
@ -227,7 +235,8 @@ Example response:
|
||||||
},
|
},
|
||||||
"position" : 3,
|
"position" : 3,
|
||||||
"max_issue_count": 0,
|
"max_issue_count": 0,
|
||||||
"max_issue_weight": 0
|
"max_issue_weight": 0,
|
||||||
|
"limit_metric": null
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
@ -362,7 +371,8 @@ Example response:
|
||||||
},
|
},
|
||||||
"position" : 1,
|
"position" : 1,
|
||||||
"max_issue_count": 0,
|
"max_issue_count": 0,
|
||||||
"max_issue_weight": 0
|
"max_issue_weight": 0,
|
||||||
|
"limit_metric": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id" : 2,
|
"id" : 2,
|
||||||
|
|
@ -373,7 +383,8 @@ Example response:
|
||||||
},
|
},
|
||||||
"position" : 2,
|
"position" : 2,
|
||||||
"max_issue_count": 0,
|
"max_issue_count": 0,
|
||||||
"max_issue_weight": 0
|
"max_issue_weight": 0,
|
||||||
|
"limit_metric": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id" : 3,
|
"id" : 3,
|
||||||
|
|
@ -384,7 +395,8 @@ Example response:
|
||||||
},
|
},
|
||||||
"position" : 3,
|
"position" : 3,
|
||||||
"max_issue_count": 0,
|
"max_issue_count": 0,
|
||||||
"max_issue_weight": 0
|
"max_issue_weight": 0,
|
||||||
|
"limit_metric": null
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
|
@ -419,7 +431,8 @@ Example response:
|
||||||
},
|
},
|
||||||
"position" : 1,
|
"position" : 1,
|
||||||
"max_issue_count": 0,
|
"max_issue_count": 0,
|
||||||
"max_issue_weight": 0
|
"max_issue_weight": 0,
|
||||||
|
"limit_metric": null
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -461,7 +474,8 @@ Example response:
|
||||||
},
|
},
|
||||||
"position" : 1,
|
"position" : 1,
|
||||||
"max_issue_count": 0,
|
"max_issue_count": 0,
|
||||||
"max_issue_weight": 0
|
"max_issue_weight": 0,
|
||||||
|
"limit_metric": null
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -496,7 +510,8 @@ Example response:
|
||||||
},
|
},
|
||||||
"position" : 1,
|
"position" : 1,
|
||||||
"max_issue_count": 0,
|
"max_issue_count": 0,
|
||||||
"max_issue_weight": 0
|
"max_issue_weight": 0,
|
||||||
|
"limit_metric": null
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -591,7 +591,7 @@ type Board {
|
||||||
id: ID!
|
id: ID!
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Lists of the project board
|
Lists of the board
|
||||||
"""
|
"""
|
||||||
lists(
|
lists(
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -1541,7 +1541,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "lists",
|
"name": "lists",
|
||||||
"description": "Lists of the project board",
|
"description": "Lists of the board",
|
||||||
"args": [
|
"args": [
|
||||||
{
|
{
|
||||||
"name": "after",
|
"name": "after",
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,36 @@ module Gitlab
|
||||||
module Routing
|
module Routing
|
||||||
extend ActiveSupport::Concern
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
class LegacyRedirector
|
||||||
|
# @params path_type [symbol] type of path to do "-" redirection
|
||||||
|
# https://gitlab.com/gitlab-org/gitlab/-/issues/16854
|
||||||
|
def initialize(path_type)
|
||||||
|
@path_type = path_type
|
||||||
|
end
|
||||||
|
|
||||||
|
def call(_params, request)
|
||||||
|
ensure_valid_uri!(request)
|
||||||
|
|
||||||
|
# Only replace the last occurrence of `path`.
|
||||||
|
#
|
||||||
|
# `request.fullpath` includes the querystring
|
||||||
|
new_path = request.path.sub(%r{/#{@path_type}(/*)(?!.*#{@path_type})}, "/-/#{@path_type}\\1")
|
||||||
|
new_path = "#{new_path}?#{request.query_string}" if request.query_string.present?
|
||||||
|
|
||||||
|
new_path
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def ensure_valid_uri!(request)
|
||||||
|
URI.parse(request.path)
|
||||||
|
rescue URI::InvalidURIError => e
|
||||||
|
# If url is invalid, raise custom error,
|
||||||
|
# which can be ignored by monitoring tools.
|
||||||
|
raise ActionController::RoutingError.new(e.message)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
mattr_accessor :_includers
|
mattr_accessor :_includers
|
||||||
self._includers = []
|
self._includers = []
|
||||||
|
|
||||||
|
|
@ -44,20 +74,10 @@ module Gitlab
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.redirect_legacy_paths(router, *paths)
|
def self.redirect_legacy_paths(router, *paths)
|
||||||
build_redirect_path = lambda do |request, _params, path|
|
|
||||||
# Only replace the last occurrence of `path`.
|
|
||||||
#
|
|
||||||
# `request.fullpath` includes the querystring
|
|
||||||
new_path = request.path.sub(%r{/#{path}(/*)(?!.*#{path})}, "/-/#{path}\\1")
|
|
||||||
new_path = "#{new_path}?#{request.query_string}" if request.query_string.present?
|
|
||||||
|
|
||||||
new_path
|
|
||||||
end
|
|
||||||
|
|
||||||
paths.each do |path|
|
paths.each do |path|
|
||||||
router.match "/#{path}(/*rest)",
|
router.match "/#{path}(/*rest)",
|
||||||
via: [:get, :post, :patch, :delete],
|
via: [:get, :post, :patch, :delete],
|
||||||
to: router.redirect { |params, request| build_redirect_path.call(request, params, path) },
|
to: router.redirect(LegacyRedirector.new(path)),
|
||||||
as: "legacy_#{path}_redirect"
|
as: "legacy_#{path}_redirect"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -76,6 +76,16 @@ msgid_plural "%d URLs scanned"
|
||||||
msgstr[0] ""
|
msgstr[0] ""
|
||||||
msgstr[1] ""
|
msgstr[1] ""
|
||||||
|
|
||||||
|
msgid "%d approver"
|
||||||
|
msgid_plural "%d approvers"
|
||||||
|
msgstr[0] ""
|
||||||
|
msgstr[1] ""
|
||||||
|
|
||||||
|
msgid "%d approver (you've approved)"
|
||||||
|
msgid_plural "%d approvers (you've approved)"
|
||||||
|
msgstr[0] ""
|
||||||
|
msgstr[1] ""
|
||||||
|
|
||||||
msgid "%d changed file"
|
msgid "%d changed file"
|
||||||
msgid_plural "%d changed files"
|
msgid_plural "%d changed files"
|
||||||
msgstr[0] ""
|
msgstr[0] ""
|
||||||
|
|
@ -506,6 +516,9 @@ msgid_plural "%{releases} releases"
|
||||||
msgstr[0] ""
|
msgstr[0] ""
|
||||||
msgstr[1] ""
|
msgstr[1] ""
|
||||||
|
|
||||||
|
msgid "%{remaining_approvals} left"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}"
|
msgid "%{retryButtonStart}Try again%{retryButtonEnd} or %{newFileButtonStart}attach a new file%{newFileButtonEnd}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|
@ -2633,12 +2646,6 @@ msgstr ""
|
||||||
msgid "ApprovalRule|e.g. QA, Security, etc."
|
msgid "ApprovalRule|e.g. QA, Security, etc."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Approvals"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Approvals (you've approved)"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Approve"
|
msgid "Approve"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|
@ -2648,6 +2655,9 @@ msgstr ""
|
||||||
msgid "Approve the current merge request."
|
msgid "Approve the current merge request."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Approved"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Approved by: "
|
msgid "Approved by: "
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|
@ -18654,6 +18664,12 @@ msgstr ""
|
||||||
msgid "Require users to prove ownership of custom domains"
|
msgid "Require users to prove ownership of custom domains"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Required approvals (%{approvals_given} given)"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Required approvals (%{approvals_given} given, you've approved)"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Requirement"
|
msgid "Requirement"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,9 @@ module QA
|
||||||
def web_url
|
def web_url
|
||||||
# TODO
|
# TODO
|
||||||
# workaround
|
# workaround
|
||||||
repository_http_uri.to_s.gsub(".wiki.git", "/-/wikis/#{@file_name.gsub('.md', '')}")
|
# i.e. This replaces the last occurence of the string (case sensitive)
|
||||||
|
# and attaches everything before to the new substring
|
||||||
|
repository_http_uri.to_s.gsub(/(.*)\b\.wiki\.git\b/i, "\\1/-/wikis/#{@file_name.gsub('.md', '')}")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -58,7 +58,9 @@ module QA
|
||||||
def switching_to_wiki_url(url)
|
def switching_to_wiki_url(url)
|
||||||
# TODO
|
# TODO
|
||||||
# workaround
|
# workaround
|
||||||
Git::Location.new(url.to_s.gsub('.git', '.wiki.git'))
|
# i.e. This replaces the last occurence of the string (case sensitive)
|
||||||
|
# and attaches everything before to the new substring
|
||||||
|
Git::Location.new(url.to_s.gsub(/(.*)\bgit\b/i, '\1wiki.git'))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -78,7 +78,8 @@
|
||||||
},
|
},
|
||||||
"position": { "type": ["integer", "null"] },
|
"position": { "type": ["integer", "null"] },
|
||||||
"max_issue_count": { "type": "integer" },
|
"max_issue_count": { "type": "integer" },
|
||||||
"max_issue_weight": { "type": "integer" }
|
"max_issue_weight": { "type": "integer" },
|
||||||
|
"limit_metric": { "type": ["string", "null"] }
|
||||||
},
|
},
|
||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import $ from 'jquery';
|
||||||
import Cookies from 'js-cookie';
|
import Cookies from 'js-cookie';
|
||||||
import loadAwardsHandler from '~/awards_handler';
|
import loadAwardsHandler from '~/awards_handler';
|
||||||
import '~/lib/utils/common_utils';
|
import '~/lib/utils/common_utils';
|
||||||
|
import waitForPromises from './helpers/wait_for_promises';
|
||||||
|
|
||||||
window.gl = window.gl || {};
|
window.gl = window.gl || {};
|
||||||
window.gon = window.gon || {};
|
window.gon = window.gon || {};
|
||||||
|
|
@ -10,28 +11,32 @@ let openAndWaitForEmojiMenu;
|
||||||
let awardsHandler = null;
|
let awardsHandler = null;
|
||||||
const urlRoot = gon.relative_url_root;
|
const urlRoot = gon.relative_url_root;
|
||||||
|
|
||||||
const lazyAssert = function(done, assertFn) {
|
const lazyAssert = (done, assertFn) => {
|
||||||
setTimeout(function() {
|
jest.runOnlyPendingTimers();
|
||||||
|
waitForPromises()
|
||||||
|
.then(() => {
|
||||||
assertFn();
|
assertFn();
|
||||||
done();
|
done();
|
||||||
// Maybe jasmine.clock here?
|
})
|
||||||
}, 333);
|
.catch(e => {
|
||||||
|
throw e;
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
describe('AwardsHandler', function() {
|
describe('AwardsHandler', () => {
|
||||||
preloadFixtures('snippets/show.html');
|
preloadFixtures('snippets/show.html');
|
||||||
beforeEach(function(done) {
|
beforeEach(done => {
|
||||||
loadFixtures('snippets/show.html');
|
loadFixtures('snippets/show.html');
|
||||||
loadAwardsHandler(true)
|
loadAwardsHandler(true)
|
||||||
.then(obj => {
|
.then(obj => {
|
||||||
awardsHandler = obj;
|
awardsHandler = obj;
|
||||||
spyOn(awardsHandler, 'postEmoji').and.callFake((button, url, emoji, cb) => cb());
|
jest.spyOn(awardsHandler, 'postEmoji').mockImplementation((button, url, emoji, cb) => cb());
|
||||||
done();
|
done();
|
||||||
})
|
})
|
||||||
.catch(fail);
|
.catch(done.fail);
|
||||||
|
|
||||||
let isEmojiMenuBuilt = false;
|
let isEmojiMenuBuilt = false;
|
||||||
openAndWaitForEmojiMenu = function() {
|
openAndWaitForEmojiMenu = () => {
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
if (isEmojiMenuBuilt) {
|
if (isEmojiMenuBuilt) {
|
||||||
resolve();
|
resolve();
|
||||||
|
|
@ -49,7 +54,7 @@ describe('AwardsHandler', function() {
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(function() {
|
afterEach(() => {
|
||||||
// restore original url root value
|
// restore original url root value
|
||||||
gon.relative_url_root = urlRoot;
|
gon.relative_url_root = urlRoot;
|
||||||
|
|
||||||
|
|
@ -59,12 +64,12 @@ describe('AwardsHandler', function() {
|
||||||
awardsHandler.destroy();
|
awardsHandler.destroy();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('::showEmojiMenu', function() {
|
describe('::showEmojiMenu', () => {
|
||||||
it('should show emoji menu when Add emoji button clicked', function(done) {
|
it('should show emoji menu when Add emoji button clicked', done => {
|
||||||
$('.js-add-award')
|
$('.js-add-award')
|
||||||
.eq(0)
|
.eq(0)
|
||||||
.click();
|
.click();
|
||||||
lazyAssert(done, function() {
|
lazyAssert(done, () => {
|
||||||
const $emojiMenu = $('.emoji-menu');
|
const $emojiMenu = $('.emoji-menu');
|
||||||
|
|
||||||
expect($emojiMenu.length).toBe(1);
|
expect($emojiMenu.length).toBe(1);
|
||||||
|
|
@ -74,20 +79,20 @@ describe('AwardsHandler', function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should also show emoji menu for the smiley icon in notes', function(done) {
|
it('should also show emoji menu for the smiley icon in notes', done => {
|
||||||
$('.js-add-award.note-action-button').click();
|
$('.js-add-award.note-action-button').click();
|
||||||
lazyAssert(done, function() {
|
lazyAssert(done, () => {
|
||||||
const $emojiMenu = $('.emoji-menu');
|
const $emojiMenu = $('.emoji-menu');
|
||||||
|
|
||||||
expect($emojiMenu.length).toBe(1);
|
expect($emojiMenu.length).toBe(1);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should remove emoji menu when body is clicked', function(done) {
|
it('should remove emoji menu when body is clicked', done => {
|
||||||
$('.js-add-award')
|
$('.js-add-award')
|
||||||
.eq(0)
|
.eq(0)
|
||||||
.click();
|
.click();
|
||||||
lazyAssert(done, function() {
|
lazyAssert(done, () => {
|
||||||
const $emojiMenu = $('.emoji-menu');
|
const $emojiMenu = $('.emoji-menu');
|
||||||
$('body').click();
|
$('body').click();
|
||||||
|
|
||||||
|
|
@ -97,11 +102,11 @@ describe('AwardsHandler', function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not remove emoji menu when search is clicked', function(done) {
|
it('should not remove emoji menu when search is clicked', done => {
|
||||||
$('.js-add-award')
|
$('.js-add-award')
|
||||||
.eq(0)
|
.eq(0)
|
||||||
.click();
|
.click();
|
||||||
lazyAssert(done, function() {
|
lazyAssert(done, () => {
|
||||||
const $emojiMenu = $('.emoji-menu');
|
const $emojiMenu = $('.emoji-menu');
|
||||||
$('.emoji-search').click();
|
$('.emoji-search').click();
|
||||||
|
|
||||||
|
|
@ -112,8 +117,8 @@ describe('AwardsHandler', function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('::addAwardToEmojiBar', function() {
|
describe('::addAwardToEmojiBar', () => {
|
||||||
it('should add emoji to votes block', function() {
|
it('should add emoji to votes block', () => {
|
||||||
const $votesBlock = $('.js-awards-block').eq(0);
|
const $votesBlock = $('.js-awards-block').eq(0);
|
||||||
awardsHandler.addAwardToEmojiBar($votesBlock, 'heart', false);
|
awardsHandler.addAwardToEmojiBar($votesBlock, 'heart', false);
|
||||||
const $emojiButton = $votesBlock.find('[data-name=heart]');
|
const $emojiButton = $votesBlock.find('[data-name=heart]');
|
||||||
|
|
@ -123,7 +128,7 @@ describe('AwardsHandler', function() {
|
||||||
expect($votesBlock.hasClass('hidden')).toBe(false);
|
expect($votesBlock.hasClass('hidden')).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should remove the emoji when we click again', function() {
|
it('should remove the emoji when we click again', () => {
|
||||||
const $votesBlock = $('.js-awards-block').eq(0);
|
const $votesBlock = $('.js-awards-block').eq(0);
|
||||||
awardsHandler.addAwardToEmojiBar($votesBlock, 'heart', false);
|
awardsHandler.addAwardToEmojiBar($votesBlock, 'heart', false);
|
||||||
awardsHandler.addAwardToEmojiBar($votesBlock, 'heart', false);
|
awardsHandler.addAwardToEmojiBar($votesBlock, 'heart', false);
|
||||||
|
|
@ -132,7 +137,7 @@ describe('AwardsHandler', function() {
|
||||||
expect($emojiButton.length).toBe(0);
|
expect($emojiButton.length).toBe(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should decrement the emoji counter', function() {
|
it('should decrement the emoji counter', () => {
|
||||||
const $votesBlock = $('.js-awards-block').eq(0);
|
const $votesBlock = $('.js-awards-block').eq(0);
|
||||||
awardsHandler.addAwardToEmojiBar($votesBlock, 'heart', false);
|
awardsHandler.addAwardToEmojiBar($votesBlock, 'heart', false);
|
||||||
const $emojiButton = $votesBlock.find('[data-name=heart]');
|
const $emojiButton = $votesBlock.find('[data-name=heart]');
|
||||||
|
|
@ -144,8 +149,8 @@ describe('AwardsHandler', function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('::userAuthored', function() {
|
describe('::userAuthored', () => {
|
||||||
it('should update tooltip to user authored title', function() {
|
it('should update tooltip to user authored title', () => {
|
||||||
const $votesBlock = $('.js-awards-block').eq(0);
|
const $votesBlock = $('.js-awards-block').eq(0);
|
||||||
const $thumbsUpEmoji = $votesBlock.find('[data-name=thumbsup]').parent();
|
const $thumbsUpEmoji = $votesBlock.find('[data-name=thumbsup]').parent();
|
||||||
$thumbsUpEmoji.attr('data-title', 'sam');
|
$thumbsUpEmoji.attr('data-title', 'sam');
|
||||||
|
|
@ -156,27 +161,25 @@ describe('AwardsHandler', function() {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should restore tooltip back to initial vote list', function() {
|
it('should restore tooltip back to initial vote list', () => {
|
||||||
jasmine.clock().install();
|
|
||||||
const $votesBlock = $('.js-awards-block').eq(0);
|
const $votesBlock = $('.js-awards-block').eq(0);
|
||||||
const $thumbsUpEmoji = $votesBlock.find('[data-name=thumbsup]').parent();
|
const $thumbsUpEmoji = $votesBlock.find('[data-name=thumbsup]').parent();
|
||||||
$thumbsUpEmoji.attr('data-title', 'sam');
|
$thumbsUpEmoji.attr('data-title', 'sam');
|
||||||
awardsHandler.userAuthored($thumbsUpEmoji);
|
awardsHandler.userAuthored($thumbsUpEmoji);
|
||||||
jasmine.clock().tick(2801);
|
jest.advanceTimersByTime(2801);
|
||||||
jasmine.clock().uninstall();
|
|
||||||
|
|
||||||
expect($thumbsUpEmoji.data('originalTitle')).toBe('sam');
|
expect($thumbsUpEmoji.data('originalTitle')).toBe('sam');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('::getAwardUrl', function() {
|
describe('::getAwardUrl', () => {
|
||||||
it('returns the url for request', function() {
|
it('returns the url for request', () => {
|
||||||
expect(awardsHandler.getAwardUrl()).toBe('http://test.host/snippets/1/toggle_award_emoji');
|
expect(awardsHandler.getAwardUrl()).toBe('http://test.host/snippets/1/toggle_award_emoji');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('::addAward and ::checkMutuality', function() {
|
describe('::addAward and ::checkMutuality', () => {
|
||||||
it('should handle :+1: and :-1: mutuality', function() {
|
it('should handle :+1: and :-1: mutuality', () => {
|
||||||
const awardUrl = awardsHandler.getAwardUrl();
|
const awardUrl = awardsHandler.getAwardUrl();
|
||||||
const $votesBlock = $('.js-awards-block').eq(0);
|
const $votesBlock = $('.js-awards-block').eq(0);
|
||||||
const $thumbsUpEmoji = $votesBlock.find('[data-name=thumbsup]').parent();
|
const $thumbsUpEmoji = $votesBlock.find('[data-name=thumbsup]').parent();
|
||||||
|
|
@ -194,8 +197,8 @@ describe('AwardsHandler', function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('::removeEmoji', function() {
|
describe('::removeEmoji', () => {
|
||||||
it('should remove emoji', function() {
|
it('should remove emoji', () => {
|
||||||
const awardUrl = awardsHandler.getAwardUrl();
|
const awardUrl = awardsHandler.getAwardUrl();
|
||||||
const $votesBlock = $('.js-awards-block').eq(0);
|
const $votesBlock = $('.js-awards-block').eq(0);
|
||||||
awardsHandler.addAward($votesBlock, awardUrl, 'fire', false);
|
awardsHandler.addAward($votesBlock, awardUrl, 'fire', false);
|
||||||
|
|
@ -207,8 +210,8 @@ describe('AwardsHandler', function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('::addYouToUserList', function() {
|
describe('::addYouToUserList', () => {
|
||||||
it('should prepend "You" to the award tooltip', function() {
|
it('should prepend "You" to the award tooltip', () => {
|
||||||
const awardUrl = awardsHandler.getAwardUrl();
|
const awardUrl = awardsHandler.getAwardUrl();
|
||||||
const $votesBlock = $('.js-awards-block').eq(0);
|
const $votesBlock = $('.js-awards-block').eq(0);
|
||||||
const $thumbsUpEmoji = $votesBlock.find('[data-name=thumbsup]').parent();
|
const $thumbsUpEmoji = $votesBlock.find('[data-name=thumbsup]').parent();
|
||||||
|
|
@ -219,7 +222,7 @@ describe('AwardsHandler', function() {
|
||||||
expect($thumbsUpEmoji.data('originalTitle')).toBe('You, sam, jerry, max, and andy');
|
expect($thumbsUpEmoji.data('originalTitle')).toBe('You, sam, jerry, max, and andy');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('handles the special case where "You" is not cleanly comma separated', function() {
|
it('handles the special case where "You" is not cleanly comma separated', () => {
|
||||||
const awardUrl = awardsHandler.getAwardUrl();
|
const awardUrl = awardsHandler.getAwardUrl();
|
||||||
const $votesBlock = $('.js-awards-block').eq(0);
|
const $votesBlock = $('.js-awards-block').eq(0);
|
||||||
const $thumbsUpEmoji = $votesBlock.find('[data-name=thumbsup]').parent();
|
const $thumbsUpEmoji = $votesBlock.find('[data-name=thumbsup]').parent();
|
||||||
|
|
@ -231,8 +234,8 @@ describe('AwardsHandler', function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('::removeYouToUserList', function() {
|
describe('::removeYouToUserList', () => {
|
||||||
it('removes "You" from the front of the tooltip', function() {
|
it('removes "You" from the front of the tooltip', () => {
|
||||||
const awardUrl = awardsHandler.getAwardUrl();
|
const awardUrl = awardsHandler.getAwardUrl();
|
||||||
const $votesBlock = $('.js-awards-block').eq(0);
|
const $votesBlock = $('.js-awards-block').eq(0);
|
||||||
const $thumbsUpEmoji = $votesBlock.find('[data-name=thumbsup]').parent();
|
const $thumbsUpEmoji = $votesBlock.find('[data-name=thumbsup]').parent();
|
||||||
|
|
@ -244,7 +247,7 @@ describe('AwardsHandler', function() {
|
||||||
expect($thumbsUpEmoji.data('originalTitle')).toBe('sam, jerry, max, and andy');
|
expect($thumbsUpEmoji.data('originalTitle')).toBe('sam, jerry, max, and andy');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('handles the special case where "You" is not cleanly comma separated', function() {
|
it('handles the special case where "You" is not cleanly comma separated', () => {
|
||||||
const awardUrl = awardsHandler.getAwardUrl();
|
const awardUrl = awardsHandler.getAwardUrl();
|
||||||
const $votesBlock = $('.js-awards-block').eq(0);
|
const $votesBlock = $('.js-awards-block').eq(0);
|
||||||
const $thumbsUpEmoji = $votesBlock.find('[data-name=thumbsup]').parent();
|
const $thumbsUpEmoji = $votesBlock.find('[data-name=thumbsup]').parent();
|
||||||
|
|
@ -258,7 +261,7 @@ describe('AwardsHandler', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('::searchEmojis', () => {
|
describe('::searchEmojis', () => {
|
||||||
it('should filter the emoji', function(done) {
|
it('should filter the emoji', done => {
|
||||||
openAndWaitForEmojiMenu()
|
openAndWaitForEmojiMenu()
|
||||||
.then(() => {
|
.then(() => {
|
||||||
expect($('[data-name=angel]').is(':visible')).toBe(true);
|
expect($('[data-name=angel]').is(':visible')).toBe(true);
|
||||||
|
|
@ -276,7 +279,7 @@ describe('AwardsHandler', function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should clear the search when searching for nothing', function(done) {
|
it('should clear the search when searching for nothing', done => {
|
||||||
openAndWaitForEmojiMenu()
|
openAndWaitForEmojiMenu()
|
||||||
.then(() => {
|
.then(() => {
|
||||||
awardsHandler.searchEmojis('ali');
|
awardsHandler.searchEmojis('ali');
|
||||||
|
|
@ -298,9 +301,9 @@ describe('AwardsHandler', function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('emoji menu', function() {
|
describe('emoji menu', () => {
|
||||||
const emojiSelector = '[data-name="sunglasses"]';
|
const emojiSelector = '[data-name="sunglasses"]';
|
||||||
const openEmojiMenuAndAddEmoji = function() {
|
const openEmojiMenuAndAddEmoji = () => {
|
||||||
return openAndWaitForEmojiMenu().then(() => {
|
return openAndWaitForEmojiMenu().then(() => {
|
||||||
const $menu = $('.emoji-menu');
|
const $menu = $('.emoji-menu');
|
||||||
const $block = $('.js-awards-block');
|
const $block = $('.js-awards-block');
|
||||||
|
|
@ -315,7 +318,7 @@ describe('AwardsHandler', function() {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
it('should add selected emoji to awards block', function(done) {
|
it('should add selected emoji to awards block', done => {
|
||||||
openEmojiMenuAndAddEmoji()
|
openEmojiMenuAndAddEmoji()
|
||||||
.then(done)
|
.then(done)
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
|
|
@ -323,7 +326,7 @@ describe('AwardsHandler', function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should remove already selected emoji', function(done) {
|
it('should remove already selected emoji', done => {
|
||||||
openEmojiMenuAndAddEmoji()
|
openEmojiMenuAndAddEmoji()
|
||||||
.then(() => {
|
.then(() => {
|
||||||
$('.js-add-award')
|
$('.js-add-award')
|
||||||
|
|
@ -344,13 +347,13 @@ describe('AwardsHandler', function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('frequently used emojis', function() {
|
describe('frequently used emojis', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
// Clear it out
|
// Clear it out
|
||||||
Cookies.set('frequently_used_emojis', '');
|
Cookies.set('frequently_used_emojis', '');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('shouldn\'t have any "Frequently used" heading if no frequently used emojis', function(done) {
|
it('shouldn\'t have any "Frequently used" heading if no frequently used emojis', done => {
|
||||||
return openAndWaitForEmojiMenu()
|
return openAndWaitForEmojiMenu()
|
||||||
.then(() => {
|
.then(() => {
|
||||||
const emojiMenu = document.querySelector('.emoji-menu');
|
const emojiMenu = document.querySelector('.emoji-menu');
|
||||||
|
|
@ -364,7 +367,7 @@ describe('AwardsHandler', function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should have any frequently used section when there are frequently used emojis', function(done) {
|
it('should have any frequently used section when there are frequently used emojis', done => {
|
||||||
awardsHandler.addEmojiToFrequentlyUsedList('8ball');
|
awardsHandler.addEmojiToFrequentlyUsedList('8ball');
|
||||||
|
|
||||||
return openAndWaitForEmojiMenu()
|
return openAndWaitForEmojiMenu()
|
||||||
|
|
@ -383,7 +386,7 @@ describe('AwardsHandler', function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should disregard invalid frequently used emoji that are being attempted to be added', function() {
|
it('should disregard invalid frequently used emoji that are being attempted to be added', () => {
|
||||||
awardsHandler.addEmojiToFrequentlyUsedList('8ball');
|
awardsHandler.addEmojiToFrequentlyUsedList('8ball');
|
||||||
awardsHandler.addEmojiToFrequentlyUsedList('invalid_emoji');
|
awardsHandler.addEmojiToFrequentlyUsedList('invalid_emoji');
|
||||||
awardsHandler.addEmojiToFrequentlyUsedList('grinning');
|
awardsHandler.addEmojiToFrequentlyUsedList('grinning');
|
||||||
|
|
@ -391,7 +394,7 @@ describe('AwardsHandler', function() {
|
||||||
expect(awardsHandler.getFrequentlyUsedEmojis()).toEqual(['8ball', 'grinning']);
|
expect(awardsHandler.getFrequentlyUsedEmojis()).toEqual(['8ball', 'grinning']);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should disregard invalid frequently used emoji already set in cookie', function() {
|
it('should disregard invalid frequently used emoji already set in cookie', () => {
|
||||||
Cookies.set('frequently_used_emojis', '8ball,invalid_emoji,grinning');
|
Cookies.set('frequently_used_emojis', '8ball,invalid_emoji,grinning');
|
||||||
|
|
||||||
expect(awardsHandler.getFrequentlyUsedEmojis()).toEqual(['8ball', 'grinning']);
|
expect(awardsHandler.getFrequentlyUsedEmojis()).toEqual(['8ball', 'grinning']);
|
||||||
|
|
@ -3,7 +3,8 @@ import { clone } from 'lodash';
|
||||||
import MockAdapter from 'axios-mock-adapter';
|
import MockAdapter from 'axios-mock-adapter';
|
||||||
import axios from '~/lib/utils/axios_utils';
|
import axios from '~/lib/utils/axios_utils';
|
||||||
import Sidebar from '~/right_sidebar';
|
import Sidebar from '~/right_sidebar';
|
||||||
import timeoutPromise from './helpers/set_timeout_promise_helper';
|
import waitForPromises from './helpers/wait_for_promises';
|
||||||
|
import { TEST_HOST } from 'spec/test_constants';
|
||||||
|
|
||||||
describe('Issuable right sidebar collapsed todo toggle', () => {
|
describe('Issuable right sidebar collapsed todo toggle', () => {
|
||||||
const fixtureName = 'issues/open-issue.html';
|
const fixtureName = 'issues/open-issue.html';
|
||||||
|
|
@ -23,7 +24,7 @@ describe('Issuable right sidebar collapsed todo toggle', () => {
|
||||||
|
|
||||||
mock = new MockAdapter(axios);
|
mock = new MockAdapter(axios);
|
||||||
|
|
||||||
mock.onPost(`${gl.TEST_HOST}/frontend-fixtures/issues-project/todos`).reply(() => {
|
mock.onPost(`${TEST_HOST}/frontend-fixtures/issues-project/todos`).reply(() => {
|
||||||
const response = clone(todoData);
|
const response = clone(todoData);
|
||||||
|
|
||||||
return [200, response];
|
return [200, response];
|
||||||
|
|
@ -64,7 +65,7 @@ describe('Issuable right sidebar collapsed todo toggle', () => {
|
||||||
it('toggle todo state', done => {
|
it('toggle todo state', done => {
|
||||||
document.querySelector('.js-issuable-todo.sidebar-collapsed-icon').click();
|
document.querySelector('.js-issuable-todo.sidebar-collapsed-icon').click();
|
||||||
|
|
||||||
setTimeout(() => {
|
setImmediate(() => {
|
||||||
expect(
|
expect(
|
||||||
document.querySelector('.js-issuable-todo.sidebar-collapsed-icon .todo-undone'),
|
document.querySelector('.js-issuable-todo.sidebar-collapsed-icon .todo-undone'),
|
||||||
).not.toBeNull();
|
).not.toBeNull();
|
||||||
|
|
@ -82,7 +83,7 @@ describe('Issuable right sidebar collapsed todo toggle', () => {
|
||||||
it('toggle todo state of expanded todo toggle', done => {
|
it('toggle todo state of expanded todo toggle', done => {
|
||||||
document.querySelector('.js-issuable-todo.sidebar-collapsed-icon').click();
|
document.querySelector('.js-issuable-todo.sidebar-collapsed-icon').click();
|
||||||
|
|
||||||
setTimeout(() => {
|
setImmediate(() => {
|
||||||
expect(
|
expect(
|
||||||
document.querySelector('.issuable-sidebar-header .js-issuable-todo').textContent.trim(),
|
document.querySelector('.issuable-sidebar-header .js-issuable-todo').textContent.trim(),
|
||||||
).toBe('Mark as done');
|
).toBe('Mark as done');
|
||||||
|
|
@ -94,7 +95,7 @@ describe('Issuable right sidebar collapsed todo toggle', () => {
|
||||||
it('toggles todo button tooltip', done => {
|
it('toggles todo button tooltip', done => {
|
||||||
document.querySelector('.js-issuable-todo.sidebar-collapsed-icon').click();
|
document.querySelector('.js-issuable-todo.sidebar-collapsed-icon').click();
|
||||||
|
|
||||||
setTimeout(() => {
|
setImmediate(() => {
|
||||||
expect(
|
expect(
|
||||||
document
|
document
|
||||||
.querySelector('.js-issuable-todo.sidebar-collapsed-icon')
|
.querySelector('.js-issuable-todo.sidebar-collapsed-icon')
|
||||||
|
|
@ -108,7 +109,7 @@ describe('Issuable right sidebar collapsed todo toggle', () => {
|
||||||
it('marks todo as done', done => {
|
it('marks todo as done', done => {
|
||||||
document.querySelector('.js-issuable-todo.sidebar-collapsed-icon').click();
|
document.querySelector('.js-issuable-todo.sidebar-collapsed-icon').click();
|
||||||
|
|
||||||
timeoutPromise()
|
waitForPromises()
|
||||||
.then(() => {
|
.then(() => {
|
||||||
expect(
|
expect(
|
||||||
document.querySelector('.js-issuable-todo.sidebar-collapsed-icon .todo-undone'),
|
document.querySelector('.js-issuable-todo.sidebar-collapsed-icon .todo-undone'),
|
||||||
|
|
@ -116,7 +117,7 @@ describe('Issuable right sidebar collapsed todo toggle', () => {
|
||||||
|
|
||||||
document.querySelector('.js-issuable-todo.sidebar-collapsed-icon').click();
|
document.querySelector('.js-issuable-todo.sidebar-collapsed-icon').click();
|
||||||
})
|
})
|
||||||
.then(timeoutPromise)
|
.then(waitForPromises)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
expect(
|
expect(
|
||||||
document.querySelector('.js-issuable-todo.sidebar-collapsed-icon .todo-undone'),
|
document.querySelector('.js-issuable-todo.sidebar-collapsed-icon .todo-undone'),
|
||||||
|
|
@ -133,7 +134,7 @@ describe('Issuable right sidebar collapsed todo toggle', () => {
|
||||||
it('updates aria-label to Mark as done', done => {
|
it('updates aria-label to Mark as done', done => {
|
||||||
document.querySelector('.js-issuable-todo.sidebar-collapsed-icon').click();
|
document.querySelector('.js-issuable-todo.sidebar-collapsed-icon').click();
|
||||||
|
|
||||||
setTimeout(() => {
|
setImmediate(() => {
|
||||||
expect(
|
expect(
|
||||||
document
|
document
|
||||||
.querySelector('.js-issuable-todo.sidebar-collapsed-icon')
|
.querySelector('.js-issuable-todo.sidebar-collapsed-icon')
|
||||||
|
|
@ -147,7 +148,7 @@ describe('Issuable right sidebar collapsed todo toggle', () => {
|
||||||
it('updates aria-label to add todo', done => {
|
it('updates aria-label to add todo', done => {
|
||||||
document.querySelector('.js-issuable-todo.sidebar-collapsed-icon').click();
|
document.querySelector('.js-issuable-todo.sidebar-collapsed-icon').click();
|
||||||
|
|
||||||
timeoutPromise()
|
waitForPromises()
|
||||||
.then(() => {
|
.then(() => {
|
||||||
expect(
|
expect(
|
||||||
document
|
document
|
||||||
|
|
@ -157,7 +158,7 @@ describe('Issuable right sidebar collapsed todo toggle', () => {
|
||||||
|
|
||||||
document.querySelector('.js-issuable-todo.sidebar-collapsed-icon').click();
|
document.querySelector('.js-issuable-todo.sidebar-collapsed-icon').click();
|
||||||
})
|
})
|
||||||
.then(timeoutPromise)
|
.then(waitForPromises)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
expect(
|
expect(
|
||||||
document
|
document
|
||||||
|
|
@ -0,0 +1,169 @@
|
||||||
|
import CommentTypeToggle from '~/comment_type_toggle';
|
||||||
|
import DropLab from '~/droplab/drop_lab';
|
||||||
|
import InputSetter from '~/droplab/plugins/input_setter';
|
||||||
|
|
||||||
|
describe('CommentTypeToggle', () => {
|
||||||
|
const testContext = {};
|
||||||
|
|
||||||
|
describe('class constructor', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
testContext.dropdownTrigger = {};
|
||||||
|
testContext.dropdownList = {};
|
||||||
|
testContext.noteTypeInput = {};
|
||||||
|
testContext.submitButton = {};
|
||||||
|
testContext.closeButton = {};
|
||||||
|
|
||||||
|
testContext.commentTypeToggle = new CommentTypeToggle({
|
||||||
|
dropdownTrigger: testContext.dropdownTrigger,
|
||||||
|
dropdownList: testContext.dropdownList,
|
||||||
|
noteTypeInput: testContext.noteTypeInput,
|
||||||
|
submitButton: testContext.submitButton,
|
||||||
|
closeButton: testContext.closeButton,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set .dropdownTrigger', () => {
|
||||||
|
expect(testContext.commentTypeToggle.dropdownTrigger).toBe(testContext.dropdownTrigger);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set .dropdownList', () => {
|
||||||
|
expect(testContext.commentTypeToggle.dropdownList).toBe(testContext.dropdownList);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set .noteTypeInput', () => {
|
||||||
|
expect(testContext.commentTypeToggle.noteTypeInput).toBe(testContext.noteTypeInput);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set .submitButton', () => {
|
||||||
|
expect(testContext.commentTypeToggle.submitButton).toBe(testContext.submitButton);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set .closeButton', () => {
|
||||||
|
expect(testContext.commentTypeToggle.closeButton).toBe(testContext.closeButton);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set .reopenButton', () => {
|
||||||
|
expect(testContext.commentTypeToggle.reopenButton).toBe(testContext.reopenButton);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('initDroplab', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
testContext.commentTypeToggle = {
|
||||||
|
dropdownTrigger: {},
|
||||||
|
dropdownList: {},
|
||||||
|
noteTypeInput: {},
|
||||||
|
submitButton: {},
|
||||||
|
closeButton: {},
|
||||||
|
setConfig: () => {},
|
||||||
|
};
|
||||||
|
testContext.config = {};
|
||||||
|
|
||||||
|
jest.spyOn(DropLab.prototype, 'init').mockImplementation();
|
||||||
|
jest.spyOn(DropLab.prototype, 'constructor').mockImplementation();
|
||||||
|
|
||||||
|
jest.spyOn(testContext.commentTypeToggle, 'setConfig').mockReturnValue(testContext.config);
|
||||||
|
|
||||||
|
CommentTypeToggle.prototype.initDroplab.call(testContext.commentTypeToggle);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should instantiate a DropLab instance and set .droplab', () => {
|
||||||
|
expect(testContext.commentTypeToggle.droplab instanceof DropLab).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call .setConfig', () => {
|
||||||
|
expect(testContext.commentTypeToggle.setConfig).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call DropLab.prototype.init', () => {
|
||||||
|
expect(DropLab.prototype.init).toHaveBeenCalledWith(
|
||||||
|
testContext.commentTypeToggle.dropdownTrigger,
|
||||||
|
testContext.commentTypeToggle.dropdownList,
|
||||||
|
[InputSetter],
|
||||||
|
testContext.config,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('setConfig', () => {
|
||||||
|
describe('if no .closeButton is provided', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
testContext.commentTypeToggle = {
|
||||||
|
dropdownTrigger: {},
|
||||||
|
dropdownList: {},
|
||||||
|
noteTypeInput: {},
|
||||||
|
submitButton: {},
|
||||||
|
reopenButton: {},
|
||||||
|
};
|
||||||
|
|
||||||
|
testContext.setConfig = CommentTypeToggle.prototype.setConfig.call(
|
||||||
|
testContext.commentTypeToggle,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not add .closeButton related InputSetter config', () => {
|
||||||
|
expect(testContext.setConfig).toEqual({
|
||||||
|
InputSetter: [
|
||||||
|
{
|
||||||
|
input: testContext.commentTypeToggle.noteTypeInput,
|
||||||
|
valueAttribute: 'data-value',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: testContext.commentTypeToggle.submitButton,
|
||||||
|
valueAttribute: 'data-submit-text',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: testContext.commentTypeToggle.reopenButton,
|
||||||
|
valueAttribute: 'data-reopen-text',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: testContext.commentTypeToggle.reopenButton,
|
||||||
|
valueAttribute: 'data-reopen-text',
|
||||||
|
inputAttribute: 'data-alternative-text',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('if no .reopenButton is provided', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
testContext.commentTypeToggle = {
|
||||||
|
dropdownTrigger: {},
|
||||||
|
dropdownList: {},
|
||||||
|
noteTypeInput: {},
|
||||||
|
submitButton: {},
|
||||||
|
closeButton: {},
|
||||||
|
};
|
||||||
|
|
||||||
|
testContext.setConfig = CommentTypeToggle.prototype.setConfig.call(
|
||||||
|
testContext.commentTypeToggle,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not add .reopenButton related InputSetter config', () => {
|
||||||
|
expect(testContext.setConfig).toEqual({
|
||||||
|
InputSetter: [
|
||||||
|
{
|
||||||
|
input: testContext.commentTypeToggle.noteTypeInput,
|
||||||
|
valueAttribute: 'data-value',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: testContext.commentTypeToggle.submitButton,
|
||||||
|
valueAttribute: 'data-submit-text',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: testContext.commentTypeToggle.closeButton,
|
||||||
|
valueAttribute: 'data-close-text',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: testContext.commentTypeToggle.closeButton,
|
||||||
|
valueAttribute: 'data-close-text',
|
||||||
|
inputAttribute: 'data-alternative-text',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -1,9 +1,13 @@
|
||||||
import $ from 'jquery';
|
import axios from 'axios';
|
||||||
|
import MockAdapter from 'axios-mock-adapter';
|
||||||
import FilteredSearchDropdownManager from '~/filtered_search/filtered_search_dropdown_manager';
|
import FilteredSearchDropdownManager from '~/filtered_search/filtered_search_dropdown_manager';
|
||||||
|
|
||||||
describe('Filtered Search Dropdown Manager', () => {
|
describe('Filtered Search Dropdown Manager', () => {
|
||||||
|
let mock;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
spyOn($, 'ajax');
|
mock = new MockAdapter(axios);
|
||||||
|
mock.onGet().reply(200);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('addWordToInput', () => {
|
describe('addWordToInput', () => {
|
||||||
|
|
@ -32,7 +36,7 @@ describe('Filtered Search Dropdown Manager', () => {
|
||||||
const token = document.querySelector('.tokens-container .js-visual-token');
|
const token = document.querySelector('.tokens-container .js-visual-token');
|
||||||
|
|
||||||
expect(token.classList.contains('filtered-search-token')).toEqual(true);
|
expect(token.classList.contains('filtered-search-token')).toEqual(true);
|
||||||
expect(token.querySelector('.name').innerText).toBe('milestone');
|
expect(token.querySelector('.name').textContent).toBe('milestone');
|
||||||
expect(getInputValue()).toBe('');
|
expect(getInputValue()).toBe('');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -42,7 +46,7 @@ describe('Filtered Search Dropdown Manager', () => {
|
||||||
let token = document.querySelector('.tokens-container .js-visual-token');
|
let token = document.querySelector('.tokens-container .js-visual-token');
|
||||||
|
|
||||||
expect(token.classList.contains('filtered-search-token')).toEqual(true);
|
expect(token.classList.contains('filtered-search-token')).toEqual(true);
|
||||||
expect(token.querySelector('.name').innerText).toBe('label');
|
expect(token.querySelector('.name').textContent).toBe('label');
|
||||||
expect(getInputValue()).toBe('');
|
expect(getInputValue()).toBe('');
|
||||||
|
|
||||||
FilteredSearchDropdownManager.addWordToInput({ tokenName: 'label', tokenOperator: '=' });
|
FilteredSearchDropdownManager.addWordToInput({ tokenName: 'label', tokenOperator: '=' });
|
||||||
|
|
@ -50,8 +54,8 @@ describe('Filtered Search Dropdown Manager', () => {
|
||||||
token = document.querySelector('.tokens-container .js-visual-token');
|
token = document.querySelector('.tokens-container .js-visual-token');
|
||||||
|
|
||||||
expect(token.classList.contains('filtered-search-token')).toEqual(true);
|
expect(token.classList.contains('filtered-search-token')).toEqual(true);
|
||||||
expect(token.querySelector('.name').innerText).toBe('label');
|
expect(token.querySelector('.name').textContent).toBe('label');
|
||||||
expect(token.querySelector('.operator').innerText).toBe('=');
|
expect(token.querySelector('.operator').textContent).toBe('=');
|
||||||
expect(getInputValue()).toBe('');
|
expect(getInputValue()).toBe('');
|
||||||
|
|
||||||
FilteredSearchDropdownManager.addWordToInput({
|
FilteredSearchDropdownManager.addWordToInput({
|
||||||
|
|
@ -64,9 +68,9 @@ describe('Filtered Search Dropdown Manager', () => {
|
||||||
token = document.querySelector('.tokens-container .js-visual-token');
|
token = document.querySelector('.tokens-container .js-visual-token');
|
||||||
|
|
||||||
expect(token.classList.contains('filtered-search-token')).toEqual(true);
|
expect(token.classList.contains('filtered-search-token')).toEqual(true);
|
||||||
expect(token.querySelector('.name').innerText).toBe('label');
|
expect(token.querySelector('.name').textContent).toBe('label');
|
||||||
expect(token.querySelector('.operator').innerText).toBe('=');
|
expect(token.querySelector('.operator').textContent).toBe('=');
|
||||||
expect(token.querySelector('.value').innerText).toBe('none');
|
expect(token.querySelector('.value').textContent).toBe('none');
|
||||||
expect(getInputValue()).toBe('');
|
expect(getInputValue()).toBe('');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
@ -79,7 +83,7 @@ describe('Filtered Search Dropdown Manager', () => {
|
||||||
const token = document.querySelector('.tokens-container .js-visual-token');
|
const token = document.querySelector('.tokens-container .js-visual-token');
|
||||||
|
|
||||||
expect(token.classList.contains('filtered-search-token')).toEqual(true);
|
expect(token.classList.contains('filtered-search-token')).toEqual(true);
|
||||||
expect(token.querySelector('.name').innerText).toBe('author');
|
expect(token.querySelector('.name').textContent).toBe('author');
|
||||||
expect(getInputValue()).toBe('');
|
expect(getInputValue()).toBe('');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -97,9 +101,9 @@ describe('Filtered Search Dropdown Manager', () => {
|
||||||
const token = document.querySelector('.tokens-container .js-visual-token');
|
const token = document.querySelector('.tokens-container .js-visual-token');
|
||||||
|
|
||||||
expect(token.classList.contains('filtered-search-token')).toEqual(true);
|
expect(token.classList.contains('filtered-search-token')).toEqual(true);
|
||||||
expect(token.querySelector('.name').innerText).toBe('author');
|
expect(token.querySelector('.name').textContent).toBe('author');
|
||||||
expect(token.querySelector('.operator').innerText).toBe('=');
|
expect(token.querySelector('.operator').textContent).toBe('=');
|
||||||
expect(token.querySelector('.value').innerText).toBe('@root');
|
expect(token.querySelector('.value').textContent).toBe('@root');
|
||||||
expect(getInputValue()).toBe('');
|
expect(getInputValue()).toBe('');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -116,9 +120,9 @@ describe('Filtered Search Dropdown Manager', () => {
|
||||||
const token = document.querySelector('.tokens-container .js-visual-token');
|
const token = document.querySelector('.tokens-container .js-visual-token');
|
||||||
|
|
||||||
expect(token.classList.contains('filtered-search-token')).toEqual(true);
|
expect(token.classList.contains('filtered-search-token')).toEqual(true);
|
||||||
expect(token.querySelector('.name').innerText).toBe('label');
|
expect(token.querySelector('.name').textContent).toBe('label');
|
||||||
expect(token.querySelector('.operator').innerText).toBe('=');
|
expect(token.querySelector('.operator').textContent).toBe('=');
|
||||||
expect(token.querySelector('.value').innerText).toBe('~\'"test me"\'');
|
expect(token.querySelector('.value').textContent).toBe('~\'"test me"\'');
|
||||||
expect(getInputValue()).toBe('');
|
expect(getInputValue()).toBe('');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
@ -1,7 +1,10 @@
|
||||||
|
import axios from 'axios';
|
||||||
|
import MockAdapter from 'axios-mock-adapter';
|
||||||
import FilteredSearchVisualTokens from '~/filtered_search/filtered_search_visual_tokens';
|
import FilteredSearchVisualTokens from '~/filtered_search/filtered_search_visual_tokens';
|
||||||
import FilteredSearchSpecHelper from '../helpers/filtered_search_spec_helper';
|
import FilteredSearchSpecHelper from '../helpers/filtered_search_spec_helper';
|
||||||
|
|
||||||
describe('Filtered Search Visual Tokens', () => {
|
describe('Filtered Search Visual Tokens', () => {
|
||||||
|
let mock;
|
||||||
const subject = FilteredSearchVisualTokens;
|
const subject = FilteredSearchVisualTokens;
|
||||||
|
|
||||||
const findElements = tokenElement => {
|
const findElements = tokenElement => {
|
||||||
|
|
@ -17,6 +20,9 @@ describe('Filtered Search Visual Tokens', () => {
|
||||||
let bugLabelToken;
|
let bugLabelToken;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
mock = new MockAdapter(axios);
|
||||||
|
mock.onGet().reply(200);
|
||||||
|
|
||||||
setFixtures(`
|
setFixtures(`
|
||||||
<ul class="tokens-container">
|
<ul class="tokens-container">
|
||||||
${FilteredSearchSpecHelper.createInputHTML()}
|
${FilteredSearchSpecHelper.createInputHTML()}
|
||||||
|
|
@ -248,15 +254,15 @@ describe('Filtered Search Visual Tokens', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('contains name div', () => {
|
it('contains name div', () => {
|
||||||
expect(tokenElement.querySelector('.name')).toEqual(jasmine.anything());
|
expect(tokenElement.querySelector('.name')).toEqual(expect.anything());
|
||||||
});
|
});
|
||||||
|
|
||||||
it('contains value container div', () => {
|
it('contains value container div', () => {
|
||||||
expect(tokenElement.querySelector('.value-container')).toEqual(jasmine.anything());
|
expect(tokenElement.querySelector('.value-container')).toEqual(expect.anything());
|
||||||
});
|
});
|
||||||
|
|
||||||
it('contains value div', () => {
|
it('contains value div', () => {
|
||||||
expect(tokenElement.querySelector('.value-container .value')).toEqual(jasmine.anything());
|
expect(tokenElement.querySelector('.value-container .value')).toEqual(expect.anything());
|
||||||
});
|
});
|
||||||
|
|
||||||
it('contains selectable class', () => {
|
it('contains selectable class', () => {
|
||||||
|
|
@ -270,12 +276,12 @@ describe('Filtered Search Visual Tokens', () => {
|
||||||
describe('remove token', () => {
|
describe('remove token', () => {
|
||||||
it('contains remove-token button', () => {
|
it('contains remove-token button', () => {
|
||||||
expect(tokenElement.querySelector('.value-container .remove-token')).toEqual(
|
expect(tokenElement.querySelector('.value-container .remove-token')).toEqual(
|
||||||
jasmine.anything(),
|
expect.anything(),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('contains fa-close icon', () => {
|
it('contains fa-close icon', () => {
|
||||||
expect(tokenElement.querySelector('.remove-token .fa-close')).toEqual(jasmine.anything());
|
expect(tokenElement.querySelector('.remove-token .fa-close')).toEqual(expect.anything());
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
@ -453,7 +459,7 @@ describe('Filtered Search Visual Tokens', () => {
|
||||||
valueContainer.dataset.originalValue = originalValue;
|
valueContainer.dataset.originalValue = originalValue;
|
||||||
const avatar = document.createElement('img');
|
const avatar = document.createElement('img');
|
||||||
const valueElement = valueContainer.querySelector('.value');
|
const valueElement = valueContainer.querySelector('.value');
|
||||||
valueElement.insertAdjacentElement('afterbegin', avatar);
|
valueElement.appendChild(avatar);
|
||||||
tokensContainer.innerHTML = FilteredSearchSpecHelper.createTokensContainerHTML(
|
tokensContainer.innerHTML = FilteredSearchSpecHelper.createTokensContainerHTML(
|
||||||
authorToken.outerHTML,
|
authorToken.outerHTML,
|
||||||
);
|
);
|
||||||
|
|
@ -573,7 +579,7 @@ describe('Filtered Search Visual Tokens', () => {
|
||||||
|
|
||||||
it("tokenize's existing input", () => {
|
it("tokenize's existing input", () => {
|
||||||
input.value = 'some text';
|
input.value = 'some text';
|
||||||
spyOn(subject, 'tokenizeInput').and.callThrough();
|
jest.spyOn(subject, 'tokenizeInput');
|
||||||
|
|
||||||
subject.editToken(token);
|
subject.editToken(token);
|
||||||
|
|
||||||
|
|
@ -635,8 +641,8 @@ describe('Filtered Search Visual Tokens', () => {
|
||||||
FilteredSearchSpecHelper.createFilterVisualTokenHTML('label', '=', 'none'),
|
FilteredSearchSpecHelper.createFilterVisualTokenHTML('label', '=', 'none'),
|
||||||
);
|
);
|
||||||
|
|
||||||
spyOn(subject, 'tokenizeInput').and.callFake(() => {});
|
jest.spyOn(subject, 'tokenizeInput').mockImplementation(() => {});
|
||||||
spyOn(subject, 'getLastVisualTokenBeforeInput').and.callThrough();
|
jest.spyOn(subject, 'getLastVisualTokenBeforeInput');
|
||||||
|
|
||||||
subject.moveInputToTheRight();
|
subject.moveInputToTheRight();
|
||||||
|
|
||||||
|
|
@ -711,12 +717,16 @@ describe('Filtered Search Visual Tokens', () => {
|
||||||
|
|
||||||
it('renders a author token value element', () => {
|
it('renders a author token value element', () => {
|
||||||
const { tokenNameElement, tokenValueElement } = findElements(authorToken);
|
const { tokenNameElement, tokenValueElement } = findElements(authorToken);
|
||||||
const tokenName = tokenNameElement.innerText;
|
const tokenName = tokenNameElement.textContent;
|
||||||
const tokenValue = 'new value';
|
const tokenValue = 'new value';
|
||||||
|
|
||||||
subject.renderVisualTokenValue(authorToken, tokenName, tokenValue);
|
subject.renderVisualTokenValue(authorToken, tokenName, tokenValue);
|
||||||
|
|
||||||
expect(tokenValueElement.innerText).toBe(tokenValue);
|
jest.runOnlyPendingTimers();
|
||||||
|
|
||||||
|
setImmediate(() => {
|
||||||
|
expect(tokenValueElement.textContent).toBe(tokenValue);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
@ -5,51 +5,56 @@ import '~/lib/utils/text_utility';
|
||||||
import '~/lib/utils/common_utils';
|
import '~/lib/utils/common_utils';
|
||||||
|
|
||||||
describe('GLForm', () => {
|
describe('GLForm', () => {
|
||||||
describe('when instantiated', function() {
|
const testContext = {};
|
||||||
beforeEach(done => {
|
|
||||||
this.form = $('<form class="gfm-form"><textarea class="js-gfm-input"></form>');
|
|
||||||
this.textarea = this.form.find('textarea');
|
|
||||||
spyOn($.prototype, 'off').and.returnValue(this.textarea);
|
|
||||||
spyOn($.prototype, 'on').and.returnValue(this.textarea);
|
|
||||||
spyOn($.prototype, 'css');
|
|
||||||
|
|
||||||
this.glForm = new GLForm(this.form, false);
|
describe('when instantiated', () => {
|
||||||
setTimeout(() => {
|
beforeEach(done => {
|
||||||
$.prototype.off.calls.reset();
|
testContext.form = $('<form class="gfm-form"><textarea class="js-gfm-input"></form>');
|
||||||
$.prototype.on.calls.reset();
|
testContext.textarea = testContext.form.find('textarea');
|
||||||
$.prototype.css.calls.reset();
|
jest.spyOn($.prototype, 'off').mockReturnValue(testContext.textarea);
|
||||||
|
jest.spyOn($.prototype, 'on').mockReturnValue(testContext.textarea);
|
||||||
|
jest.spyOn($.prototype, 'css').mockImplementation(() => {});
|
||||||
|
|
||||||
|
testContext.glForm = new GLForm(testContext.form, false);
|
||||||
|
|
||||||
|
setImmediate(() => {
|
||||||
|
$.prototype.off.mockClear();
|
||||||
|
$.prototype.on.mockClear();
|
||||||
|
$.prototype.css.mockClear();
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('setupAutosize', () => {
|
describe('setupAutosize', () => {
|
||||||
beforeEach(done => {
|
beforeEach(done => {
|
||||||
this.glForm.setupAutosize();
|
testContext.glForm.setupAutosize();
|
||||||
setTimeout(() => {
|
|
||||||
|
setImmediate(() => {
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should register an autosize event handler on the textarea', () => {
|
it('should register an autosize event handler on the textarea', () => {
|
||||||
expect($.prototype.off).toHaveBeenCalledWith('autosize:resized');
|
expect($.prototype.off).toHaveBeenCalledWith('autosize:resized');
|
||||||
expect($.prototype.on).toHaveBeenCalledWith('autosize:resized', jasmine.any(Function));
|
expect($.prototype.on).toHaveBeenCalledWith('autosize:resized', expect.any(Function));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should register a mouseup event handler on the textarea', () => {
|
it('should register a mouseup event handler on the textarea', () => {
|
||||||
expect($.prototype.off).toHaveBeenCalledWith('mouseup.autosize');
|
expect($.prototype.off).toHaveBeenCalledWith('mouseup.autosize');
|
||||||
expect($.prototype.on).toHaveBeenCalledWith('mouseup.autosize', jasmine.any(Function));
|
expect($.prototype.on).toHaveBeenCalledWith('mouseup.autosize', expect.any(Function));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should set the resize css property to vertical', () => {
|
it('should set the resize css property to vertical', () => {
|
||||||
|
jest.runOnlyPendingTimers();
|
||||||
expect($.prototype.css).toHaveBeenCalledWith('resize', 'vertical');
|
expect($.prototype.css).toHaveBeenCalledWith('resize', 'vertical');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('setHeightData', () => {
|
describe('setHeightData', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
spyOn($.prototype, 'data');
|
jest.spyOn($.prototype, 'data').mockImplementation(() => {});
|
||||||
spyOn($.prototype, 'outerHeight').and.returnValue(200);
|
jest.spyOn($.prototype, 'outerHeight').mockReturnValue(200);
|
||||||
this.glForm.setHeightData();
|
testContext.glForm.setHeightData();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should set the height data attribute', () => {
|
it('should set the height data attribute', () => {
|
||||||
|
|
@ -64,12 +69,12 @@ describe('GLForm', () => {
|
||||||
describe('destroyAutosize', () => {
|
describe('destroyAutosize', () => {
|
||||||
describe('when called', () => {
|
describe('when called', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
spyOn($.prototype, 'data');
|
jest.spyOn($.prototype, 'data').mockImplementation(() => {});
|
||||||
spyOn($.prototype, 'outerHeight').and.returnValue(200);
|
jest.spyOn($.prototype, 'outerHeight').mockReturnValue(200);
|
||||||
spyOn(window, 'outerHeight').and.returnValue(400);
|
window.outerHeight = () => 400;
|
||||||
spyOn(autosize, 'destroy');
|
jest.spyOn(autosize, 'destroy').mockImplementation(() => {});
|
||||||
|
|
||||||
this.glForm.destroyAutosize();
|
testContext.glForm.destroyAutosize();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should call outerHeight', () => {
|
it('should call outerHeight', () => {
|
||||||
|
|
@ -81,7 +86,7 @@ describe('GLForm', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should call autosize destroy', () => {
|
it('should call autosize destroy', () => {
|
||||||
expect(autosize.destroy).toHaveBeenCalledWith(this.textarea);
|
expect(autosize.destroy).toHaveBeenCalledWith(testContext.textarea);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should set the data-height attribute', () => {
|
it('should set the data-height attribute', () => {
|
||||||
|
|
@ -98,11 +103,11 @@ describe('GLForm', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return undefined if the data-height equals the outerHeight', () => {
|
it('should return undefined if the data-height equals the outerHeight', () => {
|
||||||
spyOn($.prototype, 'outerHeight').and.returnValue(200);
|
jest.spyOn($.prototype, 'outerHeight').mockReturnValue(200);
|
||||||
spyOn($.prototype, 'data').and.returnValue(200);
|
jest.spyOn($.prototype, 'data').mockReturnValue(200);
|
||||||
spyOn(autosize, 'destroy');
|
jest.spyOn(autosize, 'destroy').mockImplementation(() => {});
|
||||||
|
|
||||||
expect(this.glForm.destroyAutosize()).toBeUndefined();
|
expect(testContext.glForm.destroyAutosize()).toBeUndefined();
|
||||||
expect(autosize.destroy).not.toHaveBeenCalled();
|
expect(autosize.destroy).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
@ -28,7 +28,7 @@ describe('Global search input dropdown', () => {
|
||||||
|
|
||||||
const groupName = 'Gitlab Org';
|
const groupName = 'Gitlab Org';
|
||||||
|
|
||||||
const removeBodyAttributes = function() {
|
const removeBodyAttributes = () => {
|
||||||
const $body = $('body');
|
const $body = $('body');
|
||||||
|
|
||||||
$body.removeAttr('data-page');
|
$body.removeAttr('data-page');
|
||||||
|
|
@ -38,7 +38,7 @@ describe('Global search input dropdown', () => {
|
||||||
|
|
||||||
// Add required attributes to body before starting the test.
|
// Add required attributes to body before starting the test.
|
||||||
// section would be dashboard|group|project
|
// section would be dashboard|group|project
|
||||||
const addBodyAttributes = function(section) {
|
const addBodyAttributes = section => {
|
||||||
if (section == null) {
|
if (section == null) {
|
||||||
section = 'dashboard';
|
section = 'dashboard';
|
||||||
}
|
}
|
||||||
|
|
@ -57,12 +57,12 @@ describe('Global search input dropdown', () => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const disableProjectIssues = function() {
|
const disableProjectIssues = () => {
|
||||||
document.querySelector('.js-search-project-options').setAttribute('data-issues-disabled', true);
|
document.querySelector('.js-search-project-options').setAttribute('data-issues-disabled', true);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Mock `gl` object in window for dashboard specific page. App code will need it.
|
// Mock `gl` object in window for dashboard specific page. App code will need it.
|
||||||
const mockDashboardOptions = function() {
|
const mockDashboardOptions = () => {
|
||||||
window.gl || (window.gl = {});
|
window.gl || (window.gl = {});
|
||||||
return (window.gl.dashboardOptions = {
|
return (window.gl.dashboardOptions = {
|
||||||
issuesPath: dashboardIssuesPath,
|
issuesPath: dashboardIssuesPath,
|
||||||
|
|
@ -71,7 +71,7 @@ describe('Global search input dropdown', () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Mock `gl` object in window for project specific page. App code will need it.
|
// Mock `gl` object in window for project specific page. App code will need it.
|
||||||
const mockProjectOptions = function() {
|
const mockProjectOptions = () => {
|
||||||
window.gl || (window.gl = {});
|
window.gl || (window.gl = {});
|
||||||
return (window.gl.projectOptions = {
|
return (window.gl.projectOptions = {
|
||||||
'gitlab-ce': {
|
'gitlab-ce': {
|
||||||
|
|
@ -82,7 +82,7 @@ describe('Global search input dropdown', () => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const mockGroupOptions = function() {
|
const mockGroupOptions = () => {
|
||||||
window.gl || (window.gl = {});
|
window.gl || (window.gl = {});
|
||||||
return (window.gl.groupOptions = {
|
return (window.gl.groupOptions = {
|
||||||
'gitlab-org': {
|
'gitlab-org': {
|
||||||
|
|
@ -93,7 +93,7 @@ describe('Global search input dropdown', () => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const assertLinks = function(list, issuesPath, mrsPath) {
|
const assertLinks = (list, issuesPath, mrsPath) => {
|
||||||
if (issuesPath) {
|
if (issuesPath) {
|
||||||
const issuesAssignedToMeLink = `a[href="${issuesPath}/?assignee_username=${userName}"]`;
|
const issuesAssignedToMeLink = `a[href="${issuesPath}/?assignee_username=${userName}"]`;
|
||||||
const issuesIHaveCreatedLink = `a[href="${issuesPath}/?author_username=${userName}"]`;
|
const issuesIHaveCreatedLink = `a[href="${issuesPath}/?author_username=${userName}"]`;
|
||||||
|
|
@ -113,7 +113,7 @@ describe('Global search input dropdown', () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
preloadFixtures('static/global_search_input.html');
|
preloadFixtures('static/global_search_input.html');
|
||||||
beforeEach(function() {
|
beforeEach(() => {
|
||||||
loadFixtures('static/global_search_input.html');
|
loadFixtures('static/global_search_input.html');
|
||||||
|
|
||||||
window.gon = {};
|
window.gon = {};
|
||||||
|
|
@ -123,13 +123,13 @@ describe('Global search input dropdown', () => {
|
||||||
return (widget = initGlobalSearchInput());
|
return (widget = initGlobalSearchInput());
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(function() {
|
afterEach(() => {
|
||||||
// Undo what we did to the shared <body>
|
// Undo what we did to the shared <body>
|
||||||
removeBodyAttributes();
|
removeBodyAttributes();
|
||||||
window.gon = {};
|
window.gon = {};
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should show Dashboard specific dropdown menu', function() {
|
it('should show Dashboard specific dropdown menu', () => {
|
||||||
addBodyAttributes();
|
addBodyAttributes();
|
||||||
mockDashboardOptions();
|
mockDashboardOptions();
|
||||||
widget.searchInput.triggerHandler('focus');
|
widget.searchInput.triggerHandler('focus');
|
||||||
|
|
@ -137,7 +137,7 @@ describe('Global search input dropdown', () => {
|
||||||
return assertLinks(list, dashboardIssuesPath, dashboardMRsPath);
|
return assertLinks(list, dashboardIssuesPath, dashboardMRsPath);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should show Group specific dropdown menu', function() {
|
it('should show Group specific dropdown menu', () => {
|
||||||
addBodyAttributes('group');
|
addBodyAttributes('group');
|
||||||
mockGroupOptions();
|
mockGroupOptions();
|
||||||
widget.searchInput.triggerHandler('focus');
|
widget.searchInput.triggerHandler('focus');
|
||||||
|
|
@ -145,7 +145,7 @@ describe('Global search input dropdown', () => {
|
||||||
return assertLinks(list, groupIssuesPath, groupMRsPath);
|
return assertLinks(list, groupIssuesPath, groupMRsPath);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should show Project specific dropdown menu', function() {
|
it('should show Project specific dropdown menu', () => {
|
||||||
addBodyAttributes('project');
|
addBodyAttributes('project');
|
||||||
mockProjectOptions();
|
mockProjectOptions();
|
||||||
widget.searchInput.triggerHandler('focus');
|
widget.searchInput.triggerHandler('focus');
|
||||||
|
|
@ -153,7 +153,7 @@ describe('Global search input dropdown', () => {
|
||||||
return assertLinks(list, projectIssuesPath, projectMRsPath);
|
return assertLinks(list, projectIssuesPath, projectMRsPath);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should show only Project mergeRequest dropdown menu items when project issues are disabled', function() {
|
it('should show only Project mergeRequest dropdown menu items when project issues are disabled', () => {
|
||||||
addBodyAttributes('project');
|
addBodyAttributes('project');
|
||||||
disableProjectIssues();
|
disableProjectIssues();
|
||||||
mockProjectOptions();
|
mockProjectOptions();
|
||||||
|
|
@ -162,7 +162,7 @@ describe('Global search input dropdown', () => {
|
||||||
assertLinks(list, null, projectMRsPath);
|
assertLinks(list, null, projectMRsPath);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not show category related menu if there is text in the input', function() {
|
it('should not show category related menu if there is text in the input', () => {
|
||||||
addBodyAttributes('project');
|
addBodyAttributes('project');
|
||||||
mockProjectOptions();
|
mockProjectOptions();
|
||||||
widget.searchInput.val('help');
|
widget.searchInput.val('help');
|
||||||
|
|
@ -173,12 +173,12 @@ describe('Global search input dropdown', () => {
|
||||||
expect(list.find(link).length).toBe(0);
|
expect(list.find(link).length).toBe(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not submit the search form when selecting an autocomplete row with the keyboard', function() {
|
it('should not submit the search form when selecting an autocomplete row with the keyboard', () => {
|
||||||
const ENTER = 13;
|
const ENTER = 13;
|
||||||
const DOWN = 40;
|
const DOWN = 40;
|
||||||
addBodyAttributes();
|
addBodyAttributes();
|
||||||
mockDashboardOptions(true);
|
mockDashboardOptions(true);
|
||||||
const submitSpy = spyOnEvent('form', 'submit');
|
const submitSpy = jest.spyOn(document.querySelector('form'), 'submit');
|
||||||
widget.searchInput.triggerHandler('focus');
|
widget.searchInput.triggerHandler('focus');
|
||||||
widget.wrap.trigger($.Event('keydown', { which: DOWN }));
|
widget.wrap.trigger($.Event('keydown', { which: DOWN }));
|
||||||
const enterKeyEvent = $.Event('keydown', { which: ENTER });
|
const enterKeyEvent = $.Event('keydown', { which: ENTER });
|
||||||
|
|
@ -186,16 +186,16 @@ describe('Global search input dropdown', () => {
|
||||||
// This does not currently catch failing behavior. For security reasons,
|
// This does not currently catch failing behavior. For security reasons,
|
||||||
// browsers will not trigger default behavior (form submit, in this
|
// browsers will not trigger default behavior (form submit, in this
|
||||||
// example) on JavaScript-created keypresses.
|
// example) on JavaScript-created keypresses.
|
||||||
expect(submitSpy).not.toHaveBeenTriggered();
|
expect(submitSpy).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('disableDropdown', function() {
|
describe('disableDropdown', () => {
|
||||||
beforeEach(function() {
|
beforeEach(() => {
|
||||||
widget.enableDropdown();
|
widget.enableDropdown();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should close the Dropdown', function() {
|
it('should close the Dropdown', () => {
|
||||||
const toggleSpy = spyOn(widget.dropdownToggle, 'dropdown');
|
const toggleSpy = jest.spyOn(widget.dropdownToggle, 'dropdown');
|
||||||
|
|
||||||
widget.dropdown.addClass('show');
|
widget.dropdown.addClass('show');
|
||||||
widget.disableDropdown();
|
widget.disableDropdown();
|
||||||
|
|
@ -204,9 +204,9 @@ describe('Global search input dropdown', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('enableDropdown', function() {
|
describe('enableDropdown', () => {
|
||||||
it('should open the Dropdown', function() {
|
it('should open the Dropdown', () => {
|
||||||
const toggleSpy = spyOn(widget.dropdownToggle, 'dropdown');
|
const toggleSpy = jest.spyOn(widget.dropdownToggle, 'dropdown');
|
||||||
widget.enableDropdown();
|
widget.enableDropdown();
|
||||||
|
|
||||||
expect(toggleSpy).toHaveBeenCalledWith('toggle');
|
expect(toggleSpy).toHaveBeenCalledWith('toggle');
|
||||||
|
|
@ -21,7 +21,9 @@ function testLabelClicks(labelOrder, done) {
|
||||||
.get(0)
|
.get(0)
|
||||||
.click();
|
.click();
|
||||||
|
|
||||||
setTimeout(() => {
|
jest.runOnlyPendingTimers();
|
||||||
|
|
||||||
|
setImmediate(() => {
|
||||||
const labelsInDropdown = $('.dropdown-content a');
|
const labelsInDropdown = $('.dropdown-content a');
|
||||||
|
|
||||||
expect(labelsInDropdown.length).toBe(10);
|
expect(labelsInDropdown.length).toBe(10);
|
||||||
|
|
@ -38,11 +40,11 @@ function testLabelClicks(labelOrder, done) {
|
||||||
.get(0)
|
.get(0)
|
||||||
.click();
|
.click();
|
||||||
|
|
||||||
setTimeout(() => {
|
setImmediate(() => {
|
||||||
expect($('.sidebar-collapsed-icon').attr('data-original-title')).toBe(labelOrder);
|
expect($('.sidebar-collapsed-icon').attr('data-original-title')).toBe(labelOrder);
|
||||||
done();
|
done();
|
||||||
}, 0);
|
});
|
||||||
}, 0);
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('Issue dropdown sidebar', () => {
|
describe('Issue dropdown sidebar', () => {
|
||||||
|
|
@ -0,0 +1,268 @@
|
||||||
|
/* eslint-disable no-return-assign, no-new, no-underscore-dangle */
|
||||||
|
|
||||||
|
import $ from 'jquery';
|
||||||
|
import LineHighlighter from '~/line_highlighter';
|
||||||
|
|
||||||
|
describe('LineHighlighter', () => {
|
||||||
|
const testContext = {};
|
||||||
|
|
||||||
|
preloadFixtures('static/line_highlighter.html');
|
||||||
|
const clickLine = (number, eventData = {}) => {
|
||||||
|
if ($.isEmptyObject(eventData)) {
|
||||||
|
return $(`#L${number}`).click();
|
||||||
|
}
|
||||||
|
const e = $.Event('click', eventData);
|
||||||
|
return $(`#L${number}`).trigger(e);
|
||||||
|
};
|
||||||
|
beforeEach(() => {
|
||||||
|
loadFixtures('static/line_highlighter.html');
|
||||||
|
testContext.class = new LineHighlighter();
|
||||||
|
testContext.css = testContext.class.highlightLineClass;
|
||||||
|
return (testContext.spies = {
|
||||||
|
__setLocationHash__: jest
|
||||||
|
.spyOn(testContext.class, '__setLocationHash__')
|
||||||
|
.mockImplementation(() => {}),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('behavior', () => {
|
||||||
|
it('highlights one line given in the URL hash', () => {
|
||||||
|
new LineHighlighter({ hash: '#L13' });
|
||||||
|
|
||||||
|
expect($('#LC13')).toHaveClass(testContext.css);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('highlights one line given in the URL hash with given CSS class name', () => {
|
||||||
|
const hiliter = new LineHighlighter({ hash: '#L13', highlightLineClass: 'hilite' });
|
||||||
|
|
||||||
|
expect(hiliter.highlightLineClass).toBe('hilite');
|
||||||
|
expect($('#LC13')).toHaveClass('hilite');
|
||||||
|
expect($('#LC13')).not.toHaveClass('hll');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('highlights a range of lines given in the URL hash', () => {
|
||||||
|
new LineHighlighter({ hash: '#L5-25' });
|
||||||
|
|
||||||
|
expect($(`.${testContext.css}`).length).toBe(21);
|
||||||
|
for (let line = 5; line <= 25; line += 1) {
|
||||||
|
expect($(`#LC${line}`)).toHaveClass(testContext.css);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('scrolls to the first highlighted line on initial load', () => {
|
||||||
|
const spy = jest.spyOn($, 'scrollTo');
|
||||||
|
new LineHighlighter({ hash: '#L5-25' });
|
||||||
|
|
||||||
|
expect(spy).toHaveBeenCalledWith('#L5', expect.anything());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('discards click events', () => {
|
||||||
|
const clickSpy = jest.fn();
|
||||||
|
|
||||||
|
$('a[data-line-number]').click(clickSpy);
|
||||||
|
|
||||||
|
clickLine(13);
|
||||||
|
|
||||||
|
expect(clickSpy.mock.calls[0][0].isDefaultPrevented()).toEqual(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handles garbage input from the hash', () => {
|
||||||
|
const func = () => {
|
||||||
|
return new LineHighlighter({ fileHolderSelector: '#blob-content-holder' });
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(func).not.toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handles hashchange event', () => {
|
||||||
|
const highlighter = new LineHighlighter();
|
||||||
|
|
||||||
|
jest.spyOn(highlighter, 'highlightHash').mockImplementation(() => {});
|
||||||
|
|
||||||
|
window.dispatchEvent(new Event('hashchange'), 'L15');
|
||||||
|
|
||||||
|
expect(highlighter.highlightHash).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('clickHandler', () => {
|
||||||
|
it('handles clicking on a child icon element', () => {
|
||||||
|
const spy = jest.spyOn(testContext.class, 'setHash');
|
||||||
|
$('#L13 i')
|
||||||
|
.mousedown()
|
||||||
|
.click();
|
||||||
|
|
||||||
|
expect(spy).toHaveBeenCalledWith(13);
|
||||||
|
expect($('#LC13')).toHaveClass(testContext.css);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('without shiftKey', () => {
|
||||||
|
it('highlights one line when clicked', () => {
|
||||||
|
clickLine(13);
|
||||||
|
|
||||||
|
expect($('#LC13')).toHaveClass(testContext.css);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('unhighlights previously highlighted lines', () => {
|
||||||
|
clickLine(13);
|
||||||
|
clickLine(20);
|
||||||
|
|
||||||
|
expect($('#LC13')).not.toHaveClass(testContext.css);
|
||||||
|
expect($('#LC20')).toHaveClass(testContext.css);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('sets the hash', () => {
|
||||||
|
const spy = jest.spyOn(testContext.class, 'setHash');
|
||||||
|
clickLine(13);
|
||||||
|
|
||||||
|
expect(spy).toHaveBeenCalledWith(13);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('with shiftKey', () => {
|
||||||
|
it('sets the hash', () => {
|
||||||
|
const spy = jest.spyOn(testContext.class, 'setHash');
|
||||||
|
clickLine(13);
|
||||||
|
clickLine(20, {
|
||||||
|
shiftKey: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(spy).toHaveBeenCalledWith(13);
|
||||||
|
expect(spy).toHaveBeenCalledWith(13, 20);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('without existing highlight', () => {
|
||||||
|
it('highlights the clicked line', () => {
|
||||||
|
clickLine(13, {
|
||||||
|
shiftKey: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect($('#LC13')).toHaveClass(testContext.css);
|
||||||
|
expect($(`.${testContext.css}`).length).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('sets the hash', () => {
|
||||||
|
const spy = jest.spyOn(testContext.class, 'setHash');
|
||||||
|
clickLine(13, {
|
||||||
|
shiftKey: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(spy).toHaveBeenCalledWith(13);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('with existing single-line highlight', () => {
|
||||||
|
it('uses existing line as last line when target is lesser', () => {
|
||||||
|
clickLine(20);
|
||||||
|
clickLine(15, {
|
||||||
|
shiftKey: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect($(`.${testContext.css}`).length).toBe(6);
|
||||||
|
for (let line = 15; line <= 20; line += 1) {
|
||||||
|
expect($(`#LC${line}`)).toHaveClass(testContext.css);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('uses existing line as first line when target is greater', () => {
|
||||||
|
clickLine(5);
|
||||||
|
clickLine(10, {
|
||||||
|
shiftKey: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect($(`.${testContext.css}`).length).toBe(6);
|
||||||
|
for (let line = 5; line <= 10; line += 1) {
|
||||||
|
expect($(`#LC${line}`)).toHaveClass(testContext.css);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('with existing multi-line highlight', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
clickLine(10, {
|
||||||
|
shiftKey: true,
|
||||||
|
});
|
||||||
|
clickLine(13, {
|
||||||
|
shiftKey: true,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('uses target as first line when it is less than existing first line', () => {
|
||||||
|
clickLine(5, {
|
||||||
|
shiftKey: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect($(`.${testContext.css}`).length).toBe(6);
|
||||||
|
for (let line = 5; line <= 10; line += 1) {
|
||||||
|
expect($(`#LC${line}`)).toHaveClass(testContext.css);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('uses target as last line when it is greater than existing first line', () => {
|
||||||
|
clickLine(15, {
|
||||||
|
shiftKey: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect($(`.${testContext.css}`).length).toBe(6);
|
||||||
|
for (let line = 10; line <= 15; line += 1) {
|
||||||
|
expect($(`#LC${line}`)).toHaveClass(testContext.css);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('hashToRange', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
testContext.subject = testContext.class.hashToRange;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('extracts a single line number from the hash', () => {
|
||||||
|
expect(testContext.subject('#L5')).toEqual([5, null]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('extracts a range of line numbers from the hash', () => {
|
||||||
|
expect(testContext.subject('#L5-15')).toEqual([5, 15]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns [null, null] when the hash is not a line number', () => {
|
||||||
|
expect(testContext.subject('#foo')).toEqual([null, null]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('highlightLine', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
testContext.subject = testContext.class.highlightLine;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('highlights the specified line', () => {
|
||||||
|
testContext.subject(13);
|
||||||
|
|
||||||
|
expect($('#LC13')).toHaveClass(testContext.css);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('accepts a String-based number', () => {
|
||||||
|
testContext.subject('13');
|
||||||
|
|
||||||
|
expect($('#LC13')).toHaveClass(testContext.css);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('setHash', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
testContext.subject = testContext.class.setHash;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('sets the location hash for a single line', () => {
|
||||||
|
testContext.subject(5);
|
||||||
|
|
||||||
|
expect(testContext.spies.__setLocationHash__).toHaveBeenCalledWith('#L5');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('sets the location hash for a range', () => {
|
||||||
|
testContext.subject(5, 15);
|
||||||
|
|
||||||
|
expect(testContext.spies.__setLocationHash__).toHaveBeenCalledWith('#L5-15');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -5,12 +5,16 @@ import MergeRequestTabs from '~/merge_request_tabs';
|
||||||
import '~/commit/pipelines/pipelines_bundle';
|
import '~/commit/pipelines/pipelines_bundle';
|
||||||
import '~/lib/utils/common_utils';
|
import '~/lib/utils/common_utils';
|
||||||
import 'vendor/jquery.scrollTo';
|
import 'vendor/jquery.scrollTo';
|
||||||
import initMrPage from './helpers/init_vue_mr_page_helper';
|
import initMrPage from '../javascripts/helpers/init_vue_mr_page_helper';
|
||||||
|
|
||||||
describe('MergeRequestTabs', function() {
|
jest.mock('~/lib/utils/webpack', () => ({
|
||||||
let mrPageMock;
|
resetServiceWorkersPublicPath: jest.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe('MergeRequestTabs', () => {
|
||||||
|
const testContext = {};
|
||||||
const stubLocation = {};
|
const stubLocation = {};
|
||||||
const setLocation = function(stubs) {
|
const setLocation = stubs => {
|
||||||
const defaults = {
|
const defaults = {
|
||||||
pathname: '',
|
pathname: '',
|
||||||
search: '',
|
search: '',
|
||||||
|
|
@ -24,29 +28,25 @@ describe('MergeRequestTabs', function() {
|
||||||
'merge_requests/diff_comment.html',
|
'merge_requests/diff_comment.html',
|
||||||
);
|
);
|
||||||
|
|
||||||
beforeEach(function() {
|
beforeEach(() => {
|
||||||
mrPageMock = initMrPage();
|
initMrPage();
|
||||||
this.class = new MergeRequestTabs({ stubLocation });
|
|
||||||
|
testContext.class = new MergeRequestTabs({ stubLocation });
|
||||||
setLocation();
|
setLocation();
|
||||||
|
|
||||||
this.spies = {
|
testContext.spies = {
|
||||||
history: spyOn(window.history, 'pushState').and.callFake(function() {}),
|
history: jest.spyOn(window.history, 'pushState').mockImplementation(() => {}),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
gl.mrWidget = {};
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(function() {
|
describe('opensInNewTab', () => {
|
||||||
this.class.unbindEvents();
|
|
||||||
this.class.destroyPipelinesView();
|
|
||||||
mrPageMock.restore();
|
|
||||||
$('.js-merge-request-test').remove();
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('opensInNewTab', function() {
|
|
||||||
const windowTarget = '_blank';
|
const windowTarget = '_blank';
|
||||||
let clickTabParams;
|
let clickTabParams;
|
||||||
let tabUrl;
|
let tabUrl;
|
||||||
|
|
||||||
beforeEach(function() {
|
beforeEach(() => {
|
||||||
loadFixtures('merge_requests/merge_request_with_task_list.html');
|
loadFixtures('merge_requests/merge_request_with_task_list.html');
|
||||||
|
|
||||||
tabUrl = $('.commits-tab a').attr('href');
|
tabUrl = $('.commits-tab a').attr('href');
|
||||||
|
|
@ -68,76 +68,76 @@ describe('MergeRequestTabs', function() {
|
||||||
describe('meta click', () => {
|
describe('meta click', () => {
|
||||||
let metakeyEvent;
|
let metakeyEvent;
|
||||||
|
|
||||||
beforeEach(function() {
|
beforeEach(() => {
|
||||||
metakeyEvent = $.Event('click', { keyCode: 91, ctrlKey: true });
|
metakeyEvent = $.Event('click', { keyCode: 91, ctrlKey: true });
|
||||||
});
|
});
|
||||||
|
|
||||||
it('opens page when commits link is clicked', function() {
|
it('opens page when commits link is clicked', () => {
|
||||||
spyOn(window, 'open').and.callFake(function(url, name) {
|
jest.spyOn(window, 'open').mockImplementation((url, name) => {
|
||||||
expect(url).toEqual(tabUrl);
|
expect(url).toEqual(tabUrl);
|
||||||
expect(name).toEqual(windowTarget);
|
expect(name).toEqual(windowTarget);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.class.bindEvents();
|
testContext.class.bindEvents();
|
||||||
$('.merge-request-tabs .commits-tab a').trigger(metakeyEvent);
|
$('.merge-request-tabs .commits-tab a').trigger(metakeyEvent);
|
||||||
|
|
||||||
expect(window.open).toHaveBeenCalled();
|
expect(window.open).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('opens page when commits badge is clicked', function() {
|
it('opens page when commits badge is clicked', () => {
|
||||||
spyOn(window, 'open').and.callFake(function(url, name) {
|
jest.spyOn(window, 'open').mockImplementation((url, name) => {
|
||||||
expect(url).toEqual(tabUrl);
|
expect(url).toEqual(tabUrl);
|
||||||
expect(name).toEqual(windowTarget);
|
expect(name).toEqual(windowTarget);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.class.bindEvents();
|
testContext.class.bindEvents();
|
||||||
$('.merge-request-tabs .commits-tab a .badge').trigger(metakeyEvent);
|
$('.merge-request-tabs .commits-tab a .badge').trigger(metakeyEvent);
|
||||||
|
|
||||||
expect(window.open).toHaveBeenCalled();
|
expect(window.open).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('opens page tab in a new browser tab with Ctrl+Click - Windows/Linux', function() {
|
it('opens page tab in a new browser tab with Ctrl+Click - Windows/Linux', () => {
|
||||||
spyOn(window, 'open').and.callFake(function(url, name) {
|
jest.spyOn(window, 'open').mockImplementation((url, name) => {
|
||||||
expect(url).toEqual(tabUrl);
|
expect(url).toEqual(tabUrl);
|
||||||
expect(name).toEqual(windowTarget);
|
expect(name).toEqual(windowTarget);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.class.clickTab({ ...clickTabParams, metaKey: true });
|
testContext.class.clickTab({ ...clickTabParams, metaKey: true });
|
||||||
|
|
||||||
expect(window.open).toHaveBeenCalled();
|
expect(window.open).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('opens page tab in a new browser tab with Cmd+Click - Mac', function() {
|
it('opens page tab in a new browser tab with Cmd+Click - Mac', () => {
|
||||||
spyOn(window, 'open').and.callFake(function(url, name) {
|
jest.spyOn(window, 'open').mockImplementation((url, name) => {
|
||||||
expect(url).toEqual(tabUrl);
|
expect(url).toEqual(tabUrl);
|
||||||
expect(name).toEqual(windowTarget);
|
expect(name).toEqual(windowTarget);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.class.clickTab({ ...clickTabParams, ctrlKey: true });
|
testContext.class.clickTab({ ...clickTabParams, ctrlKey: true });
|
||||||
|
|
||||||
expect(window.open).toHaveBeenCalled();
|
expect(window.open).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('opens page tab in a new browser tab with Middle-click - Mac/PC', function() {
|
it('opens page tab in a new browser tab with Middle-click - Mac/PC', () => {
|
||||||
spyOn(window, 'open').and.callFake(function(url, name) {
|
jest.spyOn(window, 'open').mockImplementation((url, name) => {
|
||||||
expect(url).toEqual(tabUrl);
|
expect(url).toEqual(tabUrl);
|
||||||
expect(name).toEqual(windowTarget);
|
expect(name).toEqual(windowTarget);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.class.clickTab({ ...clickTabParams, which: 2 });
|
testContext.class.clickTab({ ...clickTabParams, which: 2 });
|
||||||
|
|
||||||
expect(window.open).toHaveBeenCalled();
|
expect(window.open).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('setCurrentAction', function() {
|
describe('setCurrentAction', () => {
|
||||||
let mock;
|
let mock;
|
||||||
|
|
||||||
beforeEach(function() {
|
beforeEach(() => {
|
||||||
mock = new MockAdapter(axios);
|
mock = new MockAdapter(axios);
|
||||||
mock.onAny().reply({ data: {} });
|
mock.onAny().reply({ data: {} });
|
||||||
this.subject = this.class.setCurrentAction;
|
testContext.subject = testContext.class.setCurrentAction;
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
|
|
@ -145,53 +145,53 @@ describe('MergeRequestTabs', function() {
|
||||||
window.history.replaceState({}, '', '/');
|
window.history.replaceState({}, '', '/');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('changes from commits', function() {
|
it('changes from commits', () => {
|
||||||
setLocation({
|
setLocation({
|
||||||
pathname: '/foo/bar/-/merge_requests/1/commits',
|
pathname: '/foo/bar/-/merge_requests/1/commits',
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(this.subject('show')).toBe('/foo/bar/-/merge_requests/1');
|
expect(testContext.subject('show')).toBe('/foo/bar/-/merge_requests/1');
|
||||||
expect(this.subject('diffs')).toBe('/foo/bar/-/merge_requests/1/diffs');
|
expect(testContext.subject('diffs')).toBe('/foo/bar/-/merge_requests/1/diffs');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('changes from diffs', function() {
|
it('changes from diffs', () => {
|
||||||
setLocation({
|
setLocation({
|
||||||
pathname: '/foo/bar/-/merge_requests/1/diffs',
|
pathname: '/foo/bar/-/merge_requests/1/diffs',
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(this.subject('show')).toBe('/foo/bar/-/merge_requests/1');
|
expect(testContext.subject('show')).toBe('/foo/bar/-/merge_requests/1');
|
||||||
expect(this.subject('commits')).toBe('/foo/bar/-/merge_requests/1/commits');
|
expect(testContext.subject('commits')).toBe('/foo/bar/-/merge_requests/1/commits');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('changes from diffs.html', function() {
|
it('changes from diffs.html', () => {
|
||||||
setLocation({
|
setLocation({
|
||||||
pathname: '/foo/bar/-/merge_requests/1/diffs.html',
|
pathname: '/foo/bar/-/merge_requests/1/diffs.html',
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(this.subject('show')).toBe('/foo/bar/-/merge_requests/1');
|
expect(testContext.subject('show')).toBe('/foo/bar/-/merge_requests/1');
|
||||||
expect(this.subject('commits')).toBe('/foo/bar/-/merge_requests/1/commits');
|
expect(testContext.subject('commits')).toBe('/foo/bar/-/merge_requests/1/commits');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('changes from notes', function() {
|
it('changes from notes', () => {
|
||||||
setLocation({
|
setLocation({
|
||||||
pathname: '/foo/bar/-/merge_requests/1',
|
pathname: '/foo/bar/-/merge_requests/1',
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(this.subject('diffs')).toBe('/foo/bar/-/merge_requests/1/diffs');
|
expect(testContext.subject('diffs')).toBe('/foo/bar/-/merge_requests/1/diffs');
|
||||||
expect(this.subject('commits')).toBe('/foo/bar/-/merge_requests/1/commits');
|
expect(testContext.subject('commits')).toBe('/foo/bar/-/merge_requests/1/commits');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('includes search parameters and hash string', function() {
|
it('includes search parameters and hash string', () => {
|
||||||
setLocation({
|
setLocation({
|
||||||
pathname: '/foo/bar/-/merge_requests/1/diffs',
|
pathname: '/foo/bar/-/merge_requests/1/diffs',
|
||||||
search: '?view=parallel',
|
search: '?view=parallel',
|
||||||
hash: '#L15-35',
|
hash: '#L15-35',
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(this.subject('show')).toBe('/foo/bar/-/merge_requests/1?view=parallel#L15-35');
|
expect(testContext.subject('show')).toBe('/foo/bar/-/merge_requests/1?view=parallel#L15-35');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('replaces the current history state', function() {
|
it('replaces the current history state', () => {
|
||||||
setLocation({
|
setLocation({
|
||||||
pathname: '/foo/bar/-/merge_requests/1',
|
pathname: '/foo/bar/-/merge_requests/1',
|
||||||
});
|
});
|
||||||
|
|
@ -204,9 +204,9 @@ describe('MergeRequestTabs', function() {
|
||||||
window.location.href,
|
window.location.href,
|
||||||
);
|
);
|
||||||
|
|
||||||
const newState = this.subject('commits');
|
const newState = testContext.subject('commits');
|
||||||
|
|
||||||
expect(this.spies.history).toHaveBeenCalledWith(
|
expect(testContext.spies.history).toHaveBeenCalledWith(
|
||||||
{
|
{
|
||||||
url: newState,
|
url: newState,
|
||||||
action: 'commits',
|
action: 'commits',
|
||||||
|
|
@ -216,16 +216,16 @@ describe('MergeRequestTabs', function() {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('treats "show" like "notes"', function() {
|
it('treats "show" like "notes"', () => {
|
||||||
setLocation({
|
setLocation({
|
||||||
pathname: '/foo/bar/-/merge_requests/1/commits',
|
pathname: '/foo/bar/-/merge_requests/1/commits',
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(this.subject('show')).toBe('/foo/bar/-/merge_requests/1');
|
expect(testContext.subject('show')).toBe('/foo/bar/-/merge_requests/1');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('expandViewContainer', function() {
|
describe('expandViewContainer', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
$('body').append(
|
$('body').append(
|
||||||
'<div class="content-wrapper"><div class="container-fluid container-limited"></div></div>',
|
'<div class="content-wrapper"><div class="container-fluid container-limited"></div></div>',
|
||||||
|
|
@ -236,59 +236,58 @@ describe('MergeRequestTabs', function() {
|
||||||
$('.content-wrapper').remove();
|
$('.content-wrapper').remove();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('removes container-limited from containers', function() {
|
it('removes container-limited from containers', () => {
|
||||||
this.class.expandViewContainer();
|
testContext.class.expandViewContainer();
|
||||||
|
|
||||||
expect($('.content-wrapper')).not.toContainElement('.container-limited');
|
expect($('.content-wrapper .container-limited')).toHaveLength(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not add container-limited when fluid layout is prefered', function() {
|
it('does not add container-limited when fluid layout is prefered', () => {
|
||||||
$('.content-wrapper .container-fluid').removeClass('container-limited');
|
$('.content-wrapper .container-fluid').removeClass('container-limited');
|
||||||
|
|
||||||
this.class.expandViewContainer(false);
|
testContext.class.expandViewContainer(false);
|
||||||
|
|
||||||
expect($('.content-wrapper')).not.toContainElement('.container-limited');
|
expect($('.content-wrapper .container-limited')).toHaveLength(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does remove container-limited from breadcrumbs', function() {
|
it('does remove container-limited from breadcrumbs', () => {
|
||||||
$('.container-limited').addClass('breadcrumbs');
|
$('.container-limited').addClass('breadcrumbs');
|
||||||
this.class.expandViewContainer();
|
testContext.class.expandViewContainer();
|
||||||
|
|
||||||
expect($('.content-wrapper')).toContainElement('.container-limited');
|
expect($('.content-wrapper .container-limited')).toHaveLength(1);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('tabShown', function() {
|
describe('tabShown', () => {
|
||||||
const mainContent = document.createElement('div');
|
const mainContent = document.createElement('div');
|
||||||
const tabContent = document.createElement('div');
|
const tabContent = document.createElement('div');
|
||||||
|
|
||||||
beforeEach(function() {
|
beforeEach(() => {
|
||||||
spyOn(mainContent, 'getBoundingClientRect').and.returnValue({ top: 10 });
|
jest.spyOn(mainContent, 'getBoundingClientRect').mockReturnValue({ top: 10 });
|
||||||
spyOn(tabContent, 'getBoundingClientRect').and.returnValue({ top: 100 });
|
jest.spyOn(tabContent, 'getBoundingClientRect').mockReturnValue({ top: 100 });
|
||||||
spyOn(document, 'querySelector').and.callFake(function(selector) {
|
jest.spyOn(document, 'querySelector').mockImplementation(selector => {
|
||||||
return selector === '.content-wrapper' ? mainContent : tabContent;
|
return selector === '.content-wrapper' ? mainContent : tabContent;
|
||||||
});
|
});
|
||||||
this.class.currentAction = 'commits';
|
testContext.class.currentAction = 'commits';
|
||||||
});
|
});
|
||||||
|
|
||||||
it('calls window scrollTo with options if document has scrollBehavior', function() {
|
it('calls window scrollTo with options if document has scrollBehavior', () => {
|
||||||
document.documentElement.style.scrollBehavior = '';
|
document.documentElement.style.scrollBehavior = '';
|
||||||
|
|
||||||
spyOn(window, 'scrollTo');
|
jest.spyOn(window, 'scrollTo').mockImplementation(() => {});
|
||||||
|
|
||||||
this.class.tabShown('commits', 'foobar');
|
testContext.class.tabShown('commits', 'foobar');
|
||||||
|
|
||||||
expect(window.scrollTo.calls.first().args[0]).toEqual({ top: 39, behavior: 'smooth' });
|
expect(window.scrollTo.mock.calls[0][0]).toEqual({ top: 39, behavior: 'smooth' });
|
||||||
});
|
});
|
||||||
|
|
||||||
it('calls window scrollTo with two args if document does not have scrollBehavior', function() {
|
it('calls window scrollTo with two args if document does not have scrollBehavior', () => {
|
||||||
spyOnProperty(document.documentElement, 'style', 'get').and.returnValue({});
|
jest.spyOn(document.documentElement, 'style', 'get').mockReturnValue({});
|
||||||
|
jest.spyOn(window, 'scrollTo').mockImplementation(() => {});
|
||||||
|
|
||||||
spyOn(window, 'scrollTo');
|
testContext.class.tabShown('commits', 'foobar');
|
||||||
|
|
||||||
this.class.tabShown('commits', 'foobar');
|
expect(window.scrollTo.mock.calls[0]).toEqual([0, 39]);
|
||||||
|
|
||||||
expect(window.scrollTo.calls.first().args).toEqual([0, 39]);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
@ -10,7 +10,7 @@ let $icon = null;
|
||||||
let $page = null;
|
let $page = null;
|
||||||
let $labelsIcon = null;
|
let $labelsIcon = null;
|
||||||
|
|
||||||
const assertSidebarState = function(state) {
|
const assertSidebarState = state => {
|
||||||
const shouldBeExpanded = state === 'expanded';
|
const shouldBeExpanded = state === 'expanded';
|
||||||
const shouldBeCollapsed = state === 'collapsed';
|
const shouldBeCollapsed = state === 'collapsed';
|
||||||
expect($aside.hasClass('right-sidebar-expanded')).toBe(shouldBeExpanded);
|
expect($aside.hasClass('right-sidebar-expanded')).toBe(shouldBeExpanded);
|
||||||
|
|
@ -21,14 +21,13 @@ const assertSidebarState = function(state) {
|
||||||
expect($icon.hasClass('fa-angle-double-left')).toBe(shouldBeCollapsed);
|
expect($icon.hasClass('fa-angle-double-left')).toBe(shouldBeCollapsed);
|
||||||
};
|
};
|
||||||
|
|
||||||
describe('RightSidebar', function() {
|
describe('RightSidebar', () => {
|
||||||
describe('fixture tests', () => {
|
describe('fixture tests', () => {
|
||||||
const fixtureName = 'issues/open-issue.html';
|
const fixtureName = 'issues/open-issue.html';
|
||||||
preloadFixtures(fixtureName);
|
preloadFixtures(fixtureName);
|
||||||
loadJSONFixtures('todos/todos.json');
|
|
||||||
let mock;
|
let mock;
|
||||||
|
|
||||||
beforeEach(function() {
|
beforeEach(() => {
|
||||||
loadFixtures(fixtureName);
|
loadFixtures(fixtureName);
|
||||||
mock = new MockAdapter(axios);
|
mock = new MockAdapter(axios);
|
||||||
new Sidebar(); // eslint-disable-line no-new
|
new Sidebar(); // eslint-disable-line no-new
|
||||||
|
|
@ -43,7 +42,7 @@ describe('RightSidebar', function() {
|
||||||
mock.restore();
|
mock.restore();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should expand/collapse the sidebar when arrow is clicked', function() {
|
it('should expand/collapse the sidebar when arrow is clicked', () => {
|
||||||
assertSidebarState('expanded');
|
assertSidebarState('expanded');
|
||||||
$toggle.click();
|
$toggle.click();
|
||||||
assertSidebarState('collapsed');
|
assertSidebarState('collapsed');
|
||||||
|
|
@ -51,28 +50,29 @@ describe('RightSidebar', function() {
|
||||||
assertSidebarState('expanded');
|
assertSidebarState('expanded');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should float over the page and when sidebar icons clicked', function() {
|
it('should float over the page and when sidebar icons clicked', () => {
|
||||||
$labelsIcon.click();
|
$labelsIcon.click();
|
||||||
assertSidebarState('expanded');
|
assertSidebarState('expanded');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should collapse when the icon arrow clicked while it is floating on page', function() {
|
it('should collapse when the icon arrow clicked while it is floating on page', () => {
|
||||||
$labelsIcon.click();
|
$labelsIcon.click();
|
||||||
assertSidebarState('expanded');
|
assertSidebarState('expanded');
|
||||||
$toggle.click();
|
$toggle.click();
|
||||||
assertSidebarState('collapsed');
|
assertSidebarState('collapsed');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should broadcast todo:toggle event when add todo clicked', function(done) {
|
it('should broadcast todo:toggle event when add todo clicked', done => {
|
||||||
const todos = getJSONFixture('todos/todos.json');
|
const todos = getJSONFixture('todos/todos.json');
|
||||||
mock.onPost(/(.*)\/todos$/).reply(200, todos);
|
mock.onPost(/(.*)\/todos$/).reply(200, todos);
|
||||||
|
|
||||||
const todoToggleSpy = spyOnEvent(document, 'todo:toggle');
|
const todoToggleSpy = jest.fn();
|
||||||
|
$(document).on('todo:toggle', todoToggleSpy);
|
||||||
|
|
||||||
$('.issuable-sidebar-header .js-issuable-todo').click();
|
$('.issuable-sidebar-header .js-issuable-todo').click();
|
||||||
|
|
||||||
setTimeout(() => {
|
setImmediate(() => {
|
||||||
expect(todoToggleSpy.calls.count()).toEqual(1);
|
expect(todoToggleSpy.mock.calls.length).toEqual(1);
|
||||||
|
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
|
@ -0,0 +1,46 @@
|
||||||
|
import $ from 'jquery';
|
||||||
|
import Shortcuts from '~/behaviors/shortcuts/shortcuts';
|
||||||
|
|
||||||
|
describe('Shortcuts', () => {
|
||||||
|
const fixtureName = 'snippets/show.html';
|
||||||
|
const createEvent = (type, target) =>
|
||||||
|
$.Event(type, {
|
||||||
|
target,
|
||||||
|
});
|
||||||
|
|
||||||
|
preloadFixtures(fixtureName);
|
||||||
|
|
||||||
|
describe('toggleMarkdownPreview', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
loadFixtures(fixtureName);
|
||||||
|
|
||||||
|
jest.spyOn(document.querySelector('.js-new-note-form .js-md-preview-button'), 'focus');
|
||||||
|
jest.spyOn(document.querySelector('.edit-note .js-md-preview-button'), 'focus');
|
||||||
|
|
||||||
|
new Shortcuts(); // eslint-disable-line no-new
|
||||||
|
});
|
||||||
|
|
||||||
|
it('focuses preview button in form', () => {
|
||||||
|
Shortcuts.toggleMarkdownPreview(
|
||||||
|
createEvent('KeyboardEvent', document.querySelector('.js-new-note-form .js-note-text')),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
document.querySelector('.js-new-note-form .js-md-preview-button').focus,
|
||||||
|
).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('focues preview button inside edit comment form', () => {
|
||||||
|
document.querySelector('.js-note-edit').click();
|
||||||
|
|
||||||
|
Shortcuts.toggleMarkdownPreview(
|
||||||
|
createEvent('KeyboardEvent', document.querySelector('.edit-note .js-note-text')),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
document.querySelector('.js-new-note-form .js-md-preview-button').focus,
|
||||||
|
).not.toHaveBeenCalled();
|
||||||
|
expect(document.querySelector('.edit-note .js-md-preview-button').focus).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -26,10 +26,12 @@ describe('User Popovers', () => {
|
||||||
loadFixtures(fixtureTemplate);
|
loadFixtures(fixtureTemplate);
|
||||||
|
|
||||||
const usersCacheSpy = () => Promise.resolve(dummyUser);
|
const usersCacheSpy = () => Promise.resolve(dummyUser);
|
||||||
spyOn(UsersCache, 'retrieveById').and.callFake(userId => usersCacheSpy(userId));
|
jest.spyOn(UsersCache, 'retrieveById').mockImplementation(userId => usersCacheSpy(userId));
|
||||||
|
|
||||||
const userStatusCacheSpy = () => Promise.resolve(dummyUserStatus);
|
const userStatusCacheSpy = () => Promise.resolve(dummyUserStatus);
|
||||||
spyOn(UsersCache, 'retrieveStatusById').and.callFake(userId => userStatusCacheSpy(userId));
|
jest
|
||||||
|
.spyOn(UsersCache, 'retrieveStatusById')
|
||||||
|
.mockImplementation(userId => userStatusCacheSpy(userId));
|
||||||
|
|
||||||
popovers = initUserPopovers(document.querySelectorAll(selector));
|
popovers = initUserPopovers(document.querySelectorAll(selector));
|
||||||
});
|
});
|
||||||
|
|
@ -53,6 +55,8 @@ describe('User Popovers', () => {
|
||||||
let userLink;
|
let userLink;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
UsersCache.retrieveById.mockReset();
|
||||||
|
|
||||||
userLink = document.querySelector(selector);
|
userLink = document.querySelector(selector);
|
||||||
|
|
||||||
triggerEvent('mouseenter', userLink);
|
triggerEvent('mouseenter', userLink);
|
||||||
|
|
@ -68,7 +72,7 @@ describe('User Popovers', () => {
|
||||||
const [firstPopover] = popovers;
|
const [firstPopover] = popovers;
|
||||||
|
|
||||||
expect(firstPopover.$props.user).toEqual(
|
expect(firstPopover.$props.user).toEqual(
|
||||||
jasmine.objectContaining({
|
expect.objectContaining({
|
||||||
name,
|
name,
|
||||||
userId,
|
userId,
|
||||||
username,
|
username,
|
||||||
|
|
@ -1,10 +1,13 @@
|
||||||
import $ from 'jquery';
|
import $ from 'jquery';
|
||||||
|
import axios from 'axios';
|
||||||
|
import MockAdapter from 'axios-mock-adapter';
|
||||||
import Dropzone from 'dropzone';
|
import Dropzone from 'dropzone';
|
||||||
import Mousetrap from 'mousetrap';
|
import Mousetrap from 'mousetrap';
|
||||||
import ZenMode from '~/zen_mode';
|
import ZenMode from '~/zen_mode';
|
||||||
import initNotes from '~/init_notes';
|
import initNotes from '~/init_notes';
|
||||||
|
|
||||||
describe('ZenMode', () => {
|
describe('ZenMode', () => {
|
||||||
|
let mock;
|
||||||
let zen;
|
let zen;
|
||||||
let dropzoneForElementSpy;
|
let dropzoneForElementSpy;
|
||||||
const fixtureName = 'snippets/show.html';
|
const fixtureName = 'snippets/show.html';
|
||||||
|
|
@ -28,10 +31,13 @@ describe('ZenMode', () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
mock = new MockAdapter(axios);
|
||||||
|
mock.onGet().reply(200);
|
||||||
|
|
||||||
loadFixtures(fixtureName);
|
loadFixtures(fixtureName);
|
||||||
initNotes();
|
initNotes();
|
||||||
|
|
||||||
dropzoneForElementSpy = spyOn(Dropzone, 'forElement').and.callFake(() => ({
|
dropzoneForElementSpy = jest.spyOn(Dropzone, 'forElement').mockImplementation(() => ({
|
||||||
enable: () => true,
|
enable: () => true,
|
||||||
}));
|
}));
|
||||||
zen = new ZenMode();
|
zen = new ZenMode();
|
||||||
|
|
@ -49,20 +55,20 @@ describe('ZenMode', () => {
|
||||||
$('.div-dropzone').addClass('js-invalid-dropzone');
|
$('.div-dropzone').addClass('js-invalid-dropzone');
|
||||||
exitZen();
|
exitZen();
|
||||||
|
|
||||||
expect(dropzoneForElementSpy.calls.count()).toEqual(0);
|
expect(dropzoneForElementSpy.mock.calls.length).toEqual(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should call dropzone if element is dropzone valid', () => {
|
it('should call dropzone if element is dropzone valid', () => {
|
||||||
$('.div-dropzone').removeClass('js-invalid-dropzone');
|
$('.div-dropzone').removeClass('js-invalid-dropzone');
|
||||||
exitZen();
|
exitZen();
|
||||||
|
|
||||||
expect(dropzoneForElementSpy.calls.count()).toEqual(2);
|
expect(dropzoneForElementSpy.mock.calls.length).toEqual(2);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('on enter', () => {
|
describe('on enter', () => {
|
||||||
it('pauses Mousetrap', () => {
|
it('pauses Mousetrap', () => {
|
||||||
const mouseTrapPauseSpy = spyOn(Mousetrap, 'pause');
|
const mouseTrapPauseSpy = jest.spyOn(Mousetrap, 'pause');
|
||||||
enterZen();
|
enterZen();
|
||||||
|
|
||||||
expect(mouseTrapPauseSpy).toHaveBeenCalled();
|
expect(mouseTrapPauseSpy).toHaveBeenCalled();
|
||||||
|
|
@ -90,14 +96,14 @@ describe('ZenMode', () => {
|
||||||
beforeEach(enterZen);
|
beforeEach(enterZen);
|
||||||
|
|
||||||
it('unpauses Mousetrap', () => {
|
it('unpauses Mousetrap', () => {
|
||||||
const mouseTrapUnpauseSpy = spyOn(Mousetrap, 'unpause');
|
const mouseTrapUnpauseSpy = jest.spyOn(Mousetrap, 'unpause');
|
||||||
exitZen();
|
exitZen();
|
||||||
|
|
||||||
expect(mouseTrapUnpauseSpy).toHaveBeenCalled();
|
expect(mouseTrapUnpauseSpy).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('restores the scroll position', () => {
|
it('restores the scroll position', () => {
|
||||||
spyOn(zen, 'scrollTo');
|
jest.spyOn(zen, 'scrollTo').mockImplementation(() => {});
|
||||||
exitZen();
|
exitZen();
|
||||||
|
|
||||||
expect(zen.scrollTo).toHaveBeenCalled();
|
expect(zen.scrollTo).toHaveBeenCalled();
|
||||||
|
|
@ -1,168 +0,0 @@
|
||||||
import CommentTypeToggle from '~/comment_type_toggle';
|
|
||||||
import InputSetter from '~/droplab/plugins/input_setter';
|
|
||||||
|
|
||||||
describe('CommentTypeToggle', function() {
|
|
||||||
describe('class constructor', function() {
|
|
||||||
beforeEach(function() {
|
|
||||||
this.dropdownTrigger = {};
|
|
||||||
this.dropdownList = {};
|
|
||||||
this.noteTypeInput = {};
|
|
||||||
this.submitButton = {};
|
|
||||||
this.closeButton = {};
|
|
||||||
|
|
||||||
this.commentTypeToggle = new CommentTypeToggle({
|
|
||||||
dropdownTrigger: this.dropdownTrigger,
|
|
||||||
dropdownList: this.dropdownList,
|
|
||||||
noteTypeInput: this.noteTypeInput,
|
|
||||||
submitButton: this.submitButton,
|
|
||||||
closeButton: this.closeButton,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should set .dropdownTrigger', function() {
|
|
||||||
expect(this.commentTypeToggle.dropdownTrigger).toBe(this.dropdownTrigger);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should set .dropdownList', function() {
|
|
||||||
expect(this.commentTypeToggle.dropdownList).toBe(this.dropdownList);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should set .noteTypeInput', function() {
|
|
||||||
expect(this.commentTypeToggle.noteTypeInput).toBe(this.noteTypeInput);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should set .submitButton', function() {
|
|
||||||
expect(this.commentTypeToggle.submitButton).toBe(this.submitButton);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should set .closeButton', function() {
|
|
||||||
expect(this.commentTypeToggle.closeButton).toBe(this.closeButton);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should set .reopenButton', function() {
|
|
||||||
expect(this.commentTypeToggle.reopenButton).toBe(this.reopenButton);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('initDroplab', function() {
|
|
||||||
beforeEach(function() {
|
|
||||||
this.commentTypeToggle = {
|
|
||||||
dropdownTrigger: {},
|
|
||||||
dropdownList: {},
|
|
||||||
noteTypeInput: {},
|
|
||||||
submitButton: {},
|
|
||||||
closeButton: {},
|
|
||||||
setConfig: () => {},
|
|
||||||
};
|
|
||||||
this.config = {};
|
|
||||||
|
|
||||||
this.droplab = jasmine.createSpyObj('droplab', ['init']);
|
|
||||||
|
|
||||||
this.droplabConstructor = spyOnDependency(CommentTypeToggle, 'DropLab').and.returnValue(
|
|
||||||
this.droplab,
|
|
||||||
);
|
|
||||||
spyOn(this.commentTypeToggle, 'setConfig').and.returnValue(this.config);
|
|
||||||
|
|
||||||
CommentTypeToggle.prototype.initDroplab.call(this.commentTypeToggle);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should instantiate a DropLab instance', function() {
|
|
||||||
expect(this.droplabConstructor).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should set .droplab', function() {
|
|
||||||
expect(this.commentTypeToggle.droplab).toBe(this.droplab);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should call .setConfig', function() {
|
|
||||||
expect(this.commentTypeToggle.setConfig).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should call DropLab.prototype.init', function() {
|
|
||||||
expect(this.droplab.init).toHaveBeenCalledWith(
|
|
||||||
this.commentTypeToggle.dropdownTrigger,
|
|
||||||
this.commentTypeToggle.dropdownList,
|
|
||||||
[InputSetter],
|
|
||||||
this.config,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('setConfig', function() {
|
|
||||||
describe('if no .closeButton is provided', function() {
|
|
||||||
beforeEach(function() {
|
|
||||||
this.commentTypeToggle = {
|
|
||||||
dropdownTrigger: {},
|
|
||||||
dropdownList: {},
|
|
||||||
noteTypeInput: {},
|
|
||||||
submitButton: {},
|
|
||||||
reopenButton: {},
|
|
||||||
};
|
|
||||||
|
|
||||||
this.setConfig = CommentTypeToggle.prototype.setConfig.call(this.commentTypeToggle);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not add .closeButton related InputSetter config', function() {
|
|
||||||
expect(this.setConfig).toEqual({
|
|
||||||
InputSetter: [
|
|
||||||
{
|
|
||||||
input: this.commentTypeToggle.noteTypeInput,
|
|
||||||
valueAttribute: 'data-value',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
input: this.commentTypeToggle.submitButton,
|
|
||||||
valueAttribute: 'data-submit-text',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
input: this.commentTypeToggle.reopenButton,
|
|
||||||
valueAttribute: 'data-reopen-text',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
input: this.commentTypeToggle.reopenButton,
|
|
||||||
valueAttribute: 'data-reopen-text',
|
|
||||||
inputAttribute: 'data-alternative-text',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('if no .reopenButton is provided', function() {
|
|
||||||
beforeEach(function() {
|
|
||||||
this.commentTypeToggle = {
|
|
||||||
dropdownTrigger: {},
|
|
||||||
dropdownList: {},
|
|
||||||
noteTypeInput: {},
|
|
||||||
submitButton: {},
|
|
||||||
closeButton: {},
|
|
||||||
};
|
|
||||||
|
|
||||||
this.setConfig = CommentTypeToggle.prototype.setConfig.call(this.commentTypeToggle);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not add .reopenButton related InputSetter config', function() {
|
|
||||||
expect(this.setConfig).toEqual({
|
|
||||||
InputSetter: [
|
|
||||||
{
|
|
||||||
input: this.commentTypeToggle.noteTypeInput,
|
|
||||||
valueAttribute: 'data-value',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
input: this.commentTypeToggle.submitButton,
|
|
||||||
valueAttribute: 'data-submit-text',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
input: this.commentTypeToggle.closeButton,
|
|
||||||
valueAttribute: 'data-close-text',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
input: this.commentTypeToggle.closeButton,
|
|
||||||
valueAttribute: 'data-close-text',
|
|
||||||
inputAttribute: 'data-alternative-text',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
@ -1,261 +0,0 @@
|
||||||
/* eslint-disable dot-notation, no-return-assign, no-new, no-underscore-dangle */
|
|
||||||
|
|
||||||
import $ from 'jquery';
|
|
||||||
import LineHighlighter from '~/line_highlighter';
|
|
||||||
|
|
||||||
describe('LineHighlighter', function() {
|
|
||||||
preloadFixtures('static/line_highlighter.html');
|
|
||||||
const clickLine = function(number, eventData = {}) {
|
|
||||||
if ($.isEmptyObject(eventData)) {
|
|
||||||
return $(`#L${number}`).click();
|
|
||||||
}
|
|
||||||
const e = $.Event('click', eventData);
|
|
||||||
return $(`#L${number}`).trigger(e);
|
|
||||||
};
|
|
||||||
beforeEach(function() {
|
|
||||||
loadFixtures('static/line_highlighter.html');
|
|
||||||
this['class'] = new LineHighlighter();
|
|
||||||
this.css = this['class'].highlightLineClass;
|
|
||||||
return (this.spies = {
|
|
||||||
__setLocationHash__: spyOn(this['class'], '__setLocationHash__').and.callFake(function() {}),
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('behavior', function() {
|
|
||||||
it('highlights one line given in the URL hash', function() {
|
|
||||||
new LineHighlighter({ hash: '#L13' });
|
|
||||||
|
|
||||||
expect($('#LC13')).toHaveClass(this.css);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('highlights one line given in the URL hash with given CSS class name', function() {
|
|
||||||
const hiliter = new LineHighlighter({ hash: '#L13', highlightLineClass: 'hilite' });
|
|
||||||
|
|
||||||
expect(hiliter.highlightLineClass).toBe('hilite');
|
|
||||||
expect($('#LC13')).toHaveClass('hilite');
|
|
||||||
expect($('#LC13')).not.toHaveClass('hll');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('highlights a range of lines given in the URL hash', function() {
|
|
||||||
new LineHighlighter({ hash: '#L5-25' });
|
|
||||||
|
|
||||||
expect($(`.${this.css}`).length).toBe(21);
|
|
||||||
for (let line = 5; line <= 25; line += 1) {
|
|
||||||
expect($(`#LC${line}`)).toHaveClass(this.css);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
it('scrolls to the first highlighted line on initial load', function() {
|
|
||||||
const spy = spyOn($, 'scrollTo');
|
|
||||||
new LineHighlighter({ hash: '#L5-25' });
|
|
||||||
|
|
||||||
expect(spy).toHaveBeenCalledWith('#L5', jasmine.anything());
|
|
||||||
});
|
|
||||||
|
|
||||||
it('discards click events', function() {
|
|
||||||
const spy = spyOnEvent('a[data-line-number]', 'click');
|
|
||||||
clickLine(13);
|
|
||||||
|
|
||||||
expect(spy).toHaveBeenPrevented();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('handles garbage input from the hash', function() {
|
|
||||||
const func = function() {
|
|
||||||
return new LineHighlighter({ fileHolderSelector: '#blob-content-holder' });
|
|
||||||
};
|
|
||||||
|
|
||||||
expect(func).not.toThrow();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('handles hashchange event', () => {
|
|
||||||
const highlighter = new LineHighlighter();
|
|
||||||
|
|
||||||
spyOn(highlighter, 'highlightHash');
|
|
||||||
|
|
||||||
window.dispatchEvent(new Event('hashchange'), 'L15');
|
|
||||||
|
|
||||||
expect(highlighter.highlightHash).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('clickHandler', function() {
|
|
||||||
it('handles clicking on a child icon element', function() {
|
|
||||||
const spy = spyOn(this['class'], 'setHash').and.callThrough();
|
|
||||||
$('#L13 i')
|
|
||||||
.mousedown()
|
|
||||||
.click();
|
|
||||||
|
|
||||||
expect(spy).toHaveBeenCalledWith(13);
|
|
||||||
expect($('#LC13')).toHaveClass(this.css);
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('without shiftKey', function() {
|
|
||||||
it('highlights one line when clicked', function() {
|
|
||||||
clickLine(13);
|
|
||||||
|
|
||||||
expect($('#LC13')).toHaveClass(this.css);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('unhighlights previously highlighted lines', function() {
|
|
||||||
clickLine(13);
|
|
||||||
clickLine(20);
|
|
||||||
|
|
||||||
expect($('#LC13')).not.toHaveClass(this.css);
|
|
||||||
expect($('#LC20')).toHaveClass(this.css);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('sets the hash', function() {
|
|
||||||
const spy = spyOn(this['class'], 'setHash').and.callThrough();
|
|
||||||
clickLine(13);
|
|
||||||
|
|
||||||
expect(spy).toHaveBeenCalledWith(13);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('with shiftKey', function() {
|
|
||||||
it('sets the hash', function() {
|
|
||||||
const spy = spyOn(this['class'], 'setHash').and.callThrough();
|
|
||||||
clickLine(13);
|
|
||||||
clickLine(20, {
|
|
||||||
shiftKey: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(spy).toHaveBeenCalledWith(13);
|
|
||||||
expect(spy).toHaveBeenCalledWith(13, 20);
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('without existing highlight', function() {
|
|
||||||
it('highlights the clicked line', function() {
|
|
||||||
clickLine(13, {
|
|
||||||
shiftKey: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
expect($('#LC13')).toHaveClass(this.css);
|
|
||||||
expect($(`.${this.css}`).length).toBe(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('sets the hash', function() {
|
|
||||||
const spy = spyOn(this['class'], 'setHash');
|
|
||||||
clickLine(13, {
|
|
||||||
shiftKey: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(spy).toHaveBeenCalledWith(13);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('with existing single-line highlight', function() {
|
|
||||||
it('uses existing line as last line when target is lesser', function() {
|
|
||||||
clickLine(20);
|
|
||||||
clickLine(15, {
|
|
||||||
shiftKey: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
expect($(`.${this.css}`).length).toBe(6);
|
|
||||||
for (let line = 15; line <= 20; line += 1) {
|
|
||||||
expect($(`#LC${line}`)).toHaveClass(this.css);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
it('uses existing line as first line when target is greater', function() {
|
|
||||||
clickLine(5);
|
|
||||||
clickLine(10, {
|
|
||||||
shiftKey: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
expect($(`.${this.css}`).length).toBe(6);
|
|
||||||
for (let line = 5; line <= 10; line += 1) {
|
|
||||||
expect($(`#LC${line}`)).toHaveClass(this.css);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('with existing multi-line highlight', function() {
|
|
||||||
beforeEach(function() {
|
|
||||||
clickLine(10, {
|
|
||||||
shiftKey: true,
|
|
||||||
});
|
|
||||||
clickLine(13, {
|
|
||||||
shiftKey: true,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('uses target as first line when it is less than existing first line', function() {
|
|
||||||
clickLine(5, {
|
|
||||||
shiftKey: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
expect($(`.${this.css}`).length).toBe(6);
|
|
||||||
for (let line = 5; line <= 10; line += 1) {
|
|
||||||
expect($(`#LC${line}`)).toHaveClass(this.css);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
it('uses target as last line when it is greater than existing first line', function() {
|
|
||||||
clickLine(15, {
|
|
||||||
shiftKey: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
expect($(`.${this.css}`).length).toBe(6);
|
|
||||||
for (let line = 10; line <= 15; line += 1) {
|
|
||||||
expect($(`#LC${line}`)).toHaveClass(this.css);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('hashToRange', function() {
|
|
||||||
beforeEach(function() {
|
|
||||||
this.subject = this['class'].hashToRange;
|
|
||||||
});
|
|
||||||
|
|
||||||
it('extracts a single line number from the hash', function() {
|
|
||||||
expect(this.subject('#L5')).toEqual([5, null]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('extracts a range of line numbers from the hash', function() {
|
|
||||||
expect(this.subject('#L5-15')).toEqual([5, 15]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('returns [null, null] when the hash is not a line number', function() {
|
|
||||||
expect(this.subject('#foo')).toEqual([null, null]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('highlightLine', function() {
|
|
||||||
beforeEach(function() {
|
|
||||||
this.subject = this['class'].highlightLine;
|
|
||||||
});
|
|
||||||
|
|
||||||
it('highlights the specified line', function() {
|
|
||||||
this.subject(13);
|
|
||||||
|
|
||||||
expect($('#LC13')).toHaveClass(this.css);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('accepts a String-based number', function() {
|
|
||||||
this.subject('13');
|
|
||||||
|
|
||||||
expect($('#LC13')).toHaveClass(this.css);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('setHash', function() {
|
|
||||||
beforeEach(function() {
|
|
||||||
this.subject = this['class'].setHash;
|
|
||||||
});
|
|
||||||
|
|
||||||
it('sets the location hash for a single line', function() {
|
|
||||||
this.subject(5);
|
|
||||||
|
|
||||||
expect(this.spies.__setLocationHash__).toHaveBeenCalledWith('#L5');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('sets the location hash for a range', function() {
|
|
||||||
this.subject(5, 15);
|
|
||||||
|
|
||||||
expect(this.spies.__setLocationHash__).toHaveBeenCalledWith('#L5-15');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
@ -1,46 +0,0 @@
|
||||||
import $ from 'jquery';
|
|
||||||
import Shortcuts from '~/behaviors/shortcuts/shortcuts';
|
|
||||||
|
|
||||||
describe('Shortcuts', () => {
|
|
||||||
const fixtureName = 'snippets/show.html';
|
|
||||||
const createEvent = (type, target) =>
|
|
||||||
$.Event(type, {
|
|
||||||
target,
|
|
||||||
});
|
|
||||||
|
|
||||||
preloadFixtures(fixtureName);
|
|
||||||
|
|
||||||
describe('toggleMarkdownPreview', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
loadFixtures(fixtureName);
|
|
||||||
|
|
||||||
spyOnEvent('.js-new-note-form .js-md-preview-button', 'focus');
|
|
||||||
spyOnEvent('.edit-note .js-md-preview-button', 'focus');
|
|
||||||
|
|
||||||
new Shortcuts(); // eslint-disable-line no-new
|
|
||||||
});
|
|
||||||
|
|
||||||
it('focuses preview button in form', () => {
|
|
||||||
Shortcuts.toggleMarkdownPreview(
|
|
||||||
createEvent('KeyboardEvent', document.querySelector('.js-new-note-form .js-note-text')),
|
|
||||||
);
|
|
||||||
|
|
||||||
expect('focus').toHaveBeenTriggeredOn('.js-new-note-form .js-md-preview-button');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('focues preview button inside edit comment form', done => {
|
|
||||||
document.querySelector('.js-note-edit').click();
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
Shortcuts.toggleMarkdownPreview(
|
|
||||||
createEvent('KeyboardEvent', document.querySelector('.edit-note .js-note-text')),
|
|
||||||
);
|
|
||||||
|
|
||||||
expect('focus').not.toHaveBeenTriggeredOn('.js-new-note-form .js-md-preview-button');
|
|
||||||
expect('focus').toHaveBeenTriggeredOn('.edit-note .js-md-preview-button');
|
|
||||||
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
@ -22,4 +22,25 @@ describe Gitlab::Routing do
|
||||||
expect(subject).to respond_to(:namespace_project_path)
|
expect(subject).to respond_to(:namespace_project_path)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe Gitlab::Routing::LegacyRedirector do
|
||||||
|
subject { described_class.new(:wikis) }
|
||||||
|
|
||||||
|
let(:request) { double(:request, path: path, query_string: '') }
|
||||||
|
let(:path) { '/gitlab-org/gitlab-test/wikis/home' }
|
||||||
|
|
||||||
|
it 'returns "-" scoped url' do
|
||||||
|
expect(subject.call({}, request)).to eq('/gitlab-org/gitlab-test/-/wikis/home')
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'invalid uri characters' do
|
||||||
|
let(:path) { '/gitlab-org/gitlab-test/wikis/home[' }
|
||||||
|
|
||||||
|
it 'raises error' do
|
||||||
|
expect do
|
||||||
|
subject.call({}, request)
|
||||||
|
end.to raise_error(ActionController::RoutingError)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -4812,6 +4812,32 @@ describe Project do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe '#execute_services' do
|
||||||
|
let(:service) { create(:slack_service, push_events: true, merge_requests_events: false, active: true) }
|
||||||
|
|
||||||
|
it 'executes services with the specified scope' do
|
||||||
|
data = 'any data'
|
||||||
|
|
||||||
|
expect(SlackService).to receive(:allocate).and_wrap_original do |method|
|
||||||
|
method.call.tap do |instance|
|
||||||
|
expect(instance).to receive(:async_execute).with(data).once
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
service.project.execute_services(data, :push_hooks)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does not execute services that don\'t match the specified scope' do
|
||||||
|
expect(SlackService).not_to receive(:allocate).and_wrap_original do |method|
|
||||||
|
method.call.tap do |instance|
|
||||||
|
expect(instance).not_to receive(:async_execute)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
service.project.execute_services(anything, :merge_request_hooks)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe '#has_active_hooks?' do
|
describe '#has_active_hooks?' do
|
||||||
let_it_be(:project) { create(:project) }
|
let_it_be(:project) { create(:project) }
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue