Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-ce
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
1
Merge Requests
1
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
gitlab-ce
Commits
2267b71e
Commit
2267b71e
authored
Feb 06, 2019
by
GitLab Bot
Browse files
Options
Browse Files
Download
Plain Diff
Automatic merge of gitlab-org/gitlab-ce master
parents
0ba8aeda
3f3067fc
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
177 additions
and
74 deletions
+177
-74
app/assets/javascripts/behaviors/markdown/copy_as_gfm.js
app/assets/javascripts/behaviors/markdown/copy_as_gfm.js
+22
-11
app/assets/javascripts/behaviors/shortcuts/shortcuts_issuable.js
...ets/javascripts/behaviors/shortcuts/shortcuts_issuable.js
+23
-19
spec/features/markdown/copy_as_gfm_spec.rb
spec/features/markdown/copy_as_gfm_spec.rb
+15
-2
spec/javascripts/behaviors/copy_as_gfm_spec.js
spec/javascripts/behaviors/copy_as_gfm_spec.js
+26
-7
spec/javascripts/behaviors/shortcuts/shortcuts_issuable_spec.js
...avascripts/behaviors/shortcuts/shortcuts_issuable_spec.js
+91
-35
No files found.
app/assets/javascripts/behaviors/markdown/copy_as_gfm.js
View file @
2267b71e
import
$
from
'
jquery
'
;
import
{
DOMParser
}
from
'
prosemirror-model
'
;
import
{
getSelectedFragment
}
from
'
~/lib/utils/common_utils
'
;
import
schema
from
'
./schema
'
;
import
markdownSerializer
from
'
./serializer
'
;
export
class
CopyAsGFM
{
constructor
()
{
...
...
@@ -39,9 +36,13 @@ export class CopyAsGFM {
div
.
appendChild
(
el
.
cloneNode
(
true
));
const
html
=
div
.
innerHTML
;
clipboardData
.
setData
(
'
text/plain
'
,
el
.
textContent
);
clipboardData
.
setData
(
'
text/x-gfm
'
,
this
.
nodeToGFM
(
el
));
clipboardData
.
setData
(
'
text/html
'
,
html
);
CopyAsGFM
.
nodeToGFM
(
el
)
.
then
(
res
=>
{
clipboardData
.
setData
(
'
text/plain
'
,
el
.
textContent
);
clipboardData
.
setData
(
'
text/x-gfm
'
,
res
);
clipboardData
.
setData
(
'
text/html
'
,
html
);
})
.
catch
(()
=>
{});
}
static
pasteGFM
(
e
)
{
...
...
@@ -137,11 +138,21 @@ export class CopyAsGFM {
}
static
nodeToGFM
(
node
)
{
const
wrapEl
=
document
.
createElement
(
'
div
'
);
wrapEl
.
appendChild
(
node
.
cloneNode
(
true
));
const
doc
=
DOMParser
.
fromSchema
(
schema
).
parse
(
wrapEl
);
return
markdownSerializer
.
serialize
(
doc
);
return
Promise
.
all
([
import
(
/* webpackChunkName: 'gfm_copy_extra' */
'
prosemirror-model
'
),
import
(
/* webpackChunkName: 'gfm_copy_extra' */
'
./schema
'
),
import
(
/* webpackChunkName: 'gfm_copy_extra' */
'
./serializer
'
),
])
.
then
(([
prosemirrorModel
,
schema
,
markdownSerializer
])
=>
{
const
{
DOMParser
}
=
prosemirrorModel
;
const
wrapEl
=
document
.
createElement
(
'
div
'
);
wrapEl
.
appendChild
(
node
.
cloneNode
(
true
));
const
doc
=
DOMParser
.
fromSchema
(
schema
.
default
).
parse
(
wrapEl
);
const
res
=
markdownSerializer
.
default
.
serialize
(
doc
);
return
res
;
})
.
catch
(()
=>
{});
}
}
...
...
app/assets/javascripts/behaviors/shortcuts/shortcuts_issuable.js
View file @
2267b71e
...
...
@@ -64,26 +64,30 @@ export default class ShortcutsIssuable extends Shortcuts {
const
el
=
CopyAsGFM
.
transformGFMSelection
(
documentFragment
.
cloneNode
(
true
));
const
blockquoteEl
=
document
.
createElement
(
'
blockquote
'
);
blockquoteEl
.
appendChild
(
el
);
const
text
=
CopyAsGFM
.
nodeToGFM
(
blockquoteEl
);
if
(
text
.
trim
()
===
''
)
{
return
false
;
}
// If replyField already has some content, add a newline before our quote
const
separator
=
(
$replyField
.
val
().
trim
()
!==
''
&&
'
\n\n
'
)
||
''
;
$replyField
.
val
((
a
,
current
)
=>
`
${
current
}${
separator
}${
text
}
\n\n`
)
.
trigger
(
'
input
'
)
.
trigger
(
'
change
'
);
// Trigger autosize
const
event
=
document
.
createEvent
(
'
Event
'
);
event
.
initEvent
(
'
autosize:update
'
,
true
,
false
);
$replyField
.
get
(
0
).
dispatchEvent
(
event
);
CopyAsGFM
.
nodeToGFM
(
blockquoteEl
)
.
then
(
text
=>
{
if
(
text
.
trim
()
===
''
)
{
return
false
;
}
// If replyField already has some content, add a newline before our quote
const
separator
=
(
$replyField
.
val
().
trim
()
!==
''
&&
'
\n\n
'
)
||
''
;
$replyField
.
val
((
a
,
current
)
=>
`
${
current
}${
separator
}${
text
}
\n\n`
)
.
trigger
(
'
input
'
)
.
trigger
(
'
change
'
);
// Trigger autosize
const
event
=
document
.
createEvent
(
'
Event
'
);
event
.
initEvent
(
'
autosize:update
'
,
true
,
false
);
$replyField
.
get
(
0
).
dispatchEvent
(
event
);
// Focus the input field
$replyField
.
focus
();
// Focus the input field
$replyField
.
focus
();
return
false
;
})
.
catch
(()
=>
{});
return
false
;
}
...
...
spec/features/markdown/copy_as_gfm_spec.rb
View file @
2267b71e
...
...
@@ -843,6 +843,7 @@ describe 'Copy as GFM', :js do
def
verify
(
selector
,
gfm
,
target:
nil
)
html
=
html_for_selector
(
selector
)
output_gfm
=
html_to_gfm
(
html
,
'transformCodeSelection'
,
target:
target
)
wait_for_requests
expect
(
output_gfm
.
strip
).
to
eq
(
gfm
.
strip
)
end
end
...
...
@@ -861,6 +862,9 @@ describe 'Copy as GFM', :js do
def
html_to_gfm
(
html
,
transformer
=
'transformGFMSelection'
,
target:
nil
)
js
=
<<~
JS
(function(html) {
// Setting it off so the import already starts
window.CopyAsGFM.nodeToGFM(document.createElement('div'));
var transformer = window.CopyAsGFM[
#{
transformer
.
inspect
}
];
var node = document.createElement('div');
...
...
@@ -875,9 +879,18 @@ describe 'Copy as GFM', :js do
node = transformer(node, target);
if (!node) return null;
return window.CopyAsGFM.nodeToGFM(node);
window.gfmCopytestRes = null;
window.CopyAsGFM.nodeToGFM(node)
.then((res) => {
window.gfmCopytestRes = res;
});
})("
#{
escape_javascript
(
html
)
}
")
JS
page
.
evaluate_script
(
js
)
page
.
execute_script
(
js
)
loop
until
page
.
evaluate_script
(
'window.gfmCopytestRes !== null'
)
page
.
evaluate_script
(
'window.gfmCopytestRes'
)
end
end
spec/javascripts/behaviors/copy_as_gfm_spec.js
View file @
2267b71e
import
{
CopyAsGFM
}
from
'
~/behaviors/markdown/copy_as_gfm
'
;
import
initCopyAsGFM
,
{
CopyAsGFM
}
from
'
~/behaviors/markdown/copy_as_gfm
'
;
describe
(
'
CopyAsGFM
'
,
()
=>
{
describe
(
'
CopyAsGFM.pasteGFM
'
,
()
=>
{
...
...
@@ -79,27 +79,46 @@ describe('CopyAsGFM', () => {
return
clipboardData
;
};
beforeAll
(
done
=>
{
initCopyAsGFM
();
// Fake call to nodeToGfm so the import of lazy bundle happened
CopyAsGFM
.
nodeToGFM
(
document
.
createElement
(
'
div
'
))
.
then
(()
=>
{
done
();
})
.
catch
(
done
.
fail
);
});
beforeEach
(()
=>
spyOn
(
clipboardData
,
'
setData
'
));
describe
(
'
list handling
'
,
()
=>
{
it
(
'
uses correct gfm for unordered lists
'
,
()
=>
{
it
(
'
uses correct gfm for unordered lists
'
,
done
=>
{
const
selection
=
stubSelection
(
'
<li>List Item1</li><li>List Item2</li>
\n
'
,
'
UL
'
);
spyOn
(
window
,
'
getSelection
'
).
and
.
returnValue
(
selection
);
simulateCopy
();
const
expectedGFM
=
'
* List Item1
\n\n
* List Item2
'
;
setTimeout
(()
=>
{
const
expectedGFM
=
'
* List Item1
\n\n
* List Item2
'
;
expect
(
clipboardData
.
setData
).
toHaveBeenCalledWith
(
'
text/x-gfm
'
,
expectedGFM
);
expect
(
clipboardData
.
setData
).
toHaveBeenCalledWith
(
'
text/x-gfm
'
,
expectedGFM
);
done
();
});
});
it
(
'
uses correct gfm for ordered lists
'
,
()
=>
{
it
(
'
uses correct gfm for ordered lists
'
,
done
=>
{
const
selection
=
stubSelection
(
'
<li>List Item1</li><li>List Item2</li>
\n
'
,
'
OL
'
);
spyOn
(
window
,
'
getSelection
'
).
and
.
returnValue
(
selection
);
simulateCopy
();
const
expectedGFM
=
'
1. List Item1
\n\n
1. List Item2
'
;
setTimeout
(()
=>
{
const
expectedGFM
=
'
1. List Item1
\n\n
1. List Item2
'
;
expect
(
clipboardData
.
setData
).
toHaveBeenCalledWith
(
'
text/x-gfm
'
,
expectedGFM
);
expect
(
clipboardData
.
setData
).
toHaveBeenCalledWith
(
'
text/x-gfm
'
,
expectedGFM
);
done
();
});
});
});
});
...
...
spec/javascripts/behaviors/shortcuts/shortcuts_issuable_spec.js
View file @
2267b71e
...
...
@@ -3,17 +3,26 @@
*/
import
$
from
'
jquery
'
;
import
initCopyAsGFM
from
'
~/behaviors/markdown/copy_as_gfm
'
;
import
initCopyAsGFM
,
{
CopyAsGFM
}
from
'
~/behaviors/markdown/copy_as_gfm
'
;
import
ShortcutsIssuable
from
'
~/behaviors/shortcuts/shortcuts_issuable
'
;
initCopyAsGFM
();
const
FORM_SELECTOR
=
'
.js-main-target-form .js-vue-comment-form
'
;
describe
(
'
ShortcutsIssuable
'
,
function
()
{
const
fixtureName
=
'
snippets/show.html.raw
'
;
preloadFixtures
(
fixtureName
);
beforeAll
(
done
=>
{
initCopyAsGFM
();
// Fake call to nodeToGfm so the import of lazy bundle happened
CopyAsGFM
.
nodeToGFM
(
document
.
createElement
(
'
div
'
))
.
then
(()
=>
{
done
();
})
.
catch
(
done
.
fail
);
});
beforeEach
(()
=>
{
loadFixtures
(
fixtureName
);
$
(
'
body
'
).
append
(
...
...
@@ -63,17 +72,22 @@ describe('ShortcutsIssuable', function() {
stubSelection
(
'
<p>Selected text.</p>
'
);
});
it
(
'
leaves existing input intact
'
,
()
=>
{
it
(
'
leaves existing input intact
'
,
done
=>
{
$
(
FORM_SELECTOR
).
val
(
'
This text was already here.
'
);
expect
(
$
(
FORM_SELECTOR
).
val
()).
toBe
(
'
This text was already here.
'
);
ShortcutsIssuable
.
replyWithSelectedText
(
true
);
expect
(
$
(
FORM_SELECTOR
).
val
()).
toBe
(
'
This text was already here.
\n\n
> Selected text.
\n\n
'
);
setTimeout
(()
=>
{
expect
(
$
(
FORM_SELECTOR
).
val
()).
toBe
(
'
This text was already here.
\n\n
> Selected text.
\n\n
'
,
);
done
();
});
});
it
(
'
triggers `input`
'
,
()
=>
{
it
(
'
triggers `input`
'
,
done
=>
{
let
triggered
=
false
;
$
(
FORM_SELECTOR
).
on
(
'
input
'
,
()
=>
{
triggered
=
true
;
...
...
@@ -81,36 +95,48 @@ describe('ShortcutsIssuable', function() {
ShortcutsIssuable
.
replyWithSelectedText
(
true
);
expect
(
triggered
).
toBe
(
true
);
setTimeout
(()
=>
{
expect
(
triggered
).
toBe
(
true
);
done
();
});
});
it
(
'
triggers `focus`
'
,
()
=>
{
it
(
'
triggers `focus`
'
,
done
=>
{
const
spy
=
spyOn
(
document
.
querySelector
(
FORM_SELECTOR
),
'
focus
'
);
ShortcutsIssuable
.
replyWithSelectedText
(
true
);
expect
(
spy
).
toHaveBeenCalled
();
setTimeout
(()
=>
{
expect
(
spy
).
toHaveBeenCalled
();
done
();
});
});
});
describe
(
'
with a one-line selection
'
,
()
=>
{
it
(
'
quotes the selection
'
,
()
=>
{
it
(
'
quotes the selection
'
,
done
=>
{
stubSelection
(
'
<p>This text has been selected.</p>
'
);
ShortcutsIssuable
.
replyWithSelectedText
(
true
);
expect
(
$
(
FORM_SELECTOR
).
val
()).
toBe
(
'
> This text has been selected.
\n\n
'
);
setTimeout
(()
=>
{
expect
(
$
(
FORM_SELECTOR
).
val
()).
toBe
(
'
> This text has been selected.
\n\n
'
);
done
();
});
});
});
describe
(
'
with a multi-line selection
'
,
()
=>
{
it
(
'
quotes the selected lines as a group
'
,
()
=>
{
it
(
'
quotes the selected lines as a group
'
,
done
=>
{
stubSelection
(
'
<p>Selected line one.</p>
\n
<p>Selected line two.</p>
\n
<p>Selected line three.</p>
'
,
);
ShortcutsIssuable
.
replyWithSelectedText
(
true
);
expect
(
$
(
FORM_SELECTOR
).
val
()).
toBe
(
'
> Selected line one.
\n
>
\n
> Selected line two.
\n
>
\n
> Selected line three.
\n\n
'
,
);
setTimeout
(()
=>
{
expect
(
$
(
FORM_SELECTOR
).
val
()).
toBe
(
'
> Selected line one.
\n
>
\n
> Selected line two.
\n
>
\n
> Selected line three.
\n\n
'
,
);
done
();
});
});
});
...
...
@@ -119,17 +145,23 @@ describe('ShortcutsIssuable', function() {
stubSelection
(
'
<p>Selected text.</p>
'
,
true
);
});
it
(
'
does not add anything to the input
'
,
()
=>
{
it
(
'
does not add anything to the input
'
,
done
=>
{
ShortcutsIssuable
.
replyWithSelectedText
(
true
);
expect
(
$
(
FORM_SELECTOR
).
val
()).
toBe
(
''
);
setTimeout
(()
=>
{
expect
(
$
(
FORM_SELECTOR
).
val
()).
toBe
(
''
);
done
();
});
});
it
(
'
triggers `focus`
'
,
()
=>
{
it
(
'
triggers `focus`
'
,
done
=>
{
const
spy
=
spyOn
(
document
.
querySelector
(
FORM_SELECTOR
),
'
focus
'
);
ShortcutsIssuable
.
replyWithSelectedText
(
true
);
expect
(
spy
).
toHaveBeenCalled
();
setTimeout
(()
=>
{
expect
(
spy
).
toHaveBeenCalled
();
done
();
});
});
});
...
...
@@ -138,20 +170,26 @@ describe('ShortcutsIssuable', function() {
stubSelection
(
'
<div class="md">Selected text.</div><p>Invalid selected text.</p>
'
,
true
);
});
it
(
'
only adds the valid part to the input
'
,
()
=>
{
it
(
'
only adds the valid part to the input
'
,
done
=>
{
ShortcutsIssuable
.
replyWithSelectedText
(
true
);
expect
(
$
(
FORM_SELECTOR
).
val
()).
toBe
(
'
> Selected text.
\n\n
'
);
setTimeout
(()
=>
{
expect
(
$
(
FORM_SELECTOR
).
val
()).
toBe
(
'
> Selected text.
\n\n
'
);
done
();
});
});
it
(
'
triggers `focus`
'
,
()
=>
{
it
(
'
triggers `focus`
'
,
done
=>
{
const
spy
=
spyOn
(
document
.
querySelector
(
FORM_SELECTOR
),
'
focus
'
);
ShortcutsIssuable
.
replyWithSelectedText
(
true
);
expect
(
spy
).
toHaveBeenCalled
();
setTimeout
(()
=>
{
expect
(
spy
).
toHaveBeenCalled
();
done
();
});
});
it
(
'
triggers `input`
'
,
()
=>
{
it
(
'
triggers `input`
'
,
done
=>
{
let
triggered
=
false
;
$
(
FORM_SELECTOR
).
on
(
'
input
'
,
()
=>
{
triggered
=
true
;
...
...
@@ -159,7 +197,10 @@ describe('ShortcutsIssuable', function() {
ShortcutsIssuable
.
replyWithSelectedText
(
true
);
expect
(
triggered
).
toBe
(
true
);
setTimeout
(()
=>
{
expect
(
triggered
).
toBe
(
true
);
done
();
});
});
});
...
...
@@ -183,20 +224,26 @@ describe('ShortcutsIssuable', function() {
});
});
it
(
'
adds the quoted selection to the input
'
,
()
=>
{
it
(
'
adds the quoted selection to the input
'
,
done
=>
{
ShortcutsIssuable
.
replyWithSelectedText
(
true
);
expect
(
$
(
FORM_SELECTOR
).
val
()).
toBe
(
'
> *Selected text.*
\n\n
'
);
setTimeout
(()
=>
{
expect
(
$
(
FORM_SELECTOR
).
val
()).
toBe
(
'
> *Selected text.*
\n\n
'
);
done
();
});
});
it
(
'
triggers `focus`
'
,
()
=>
{
it
(
'
triggers `focus`
'
,
done
=>
{
const
spy
=
spyOn
(
document
.
querySelector
(
FORM_SELECTOR
),
'
focus
'
);
ShortcutsIssuable
.
replyWithSelectedText
(
true
);
expect
(
spy
).
toHaveBeenCalled
();
setTimeout
(()
=>
{
expect
(
spy
).
toHaveBeenCalled
();
done
();
});
});
it
(
'
triggers `input`
'
,
()
=>
{
it
(
'
triggers `input`
'
,
done
=>
{
let
triggered
=
false
;
$
(
FORM_SELECTOR
).
on
(
'
input
'
,
()
=>
{
triggered
=
true
;
...
...
@@ -204,7 +251,10 @@ describe('ShortcutsIssuable', function() {
ShortcutsIssuable
.
replyWithSelectedText
(
true
);
expect
(
triggered
).
toBe
(
true
);
setTimeout
(()
=>
{
expect
(
triggered
).
toBe
(
true
);
done
();
});
});
});
...
...
@@ -228,17 +278,23 @@ describe('ShortcutsIssuable', function() {
});
});
it
(
'
does not add anything to the input
'
,
()
=>
{
it
(
'
does not add anything to the input
'
,
done
=>
{
ShortcutsIssuable
.
replyWithSelectedText
(
true
);
expect
(
$
(
FORM_SELECTOR
).
val
()).
toBe
(
''
);
setTimeout
(()
=>
{
expect
(
$
(
FORM_SELECTOR
).
val
()).
toBe
(
''
);
done
();
});
});
it
(
'
triggers `focus`
'
,
()
=>
{
it
(
'
triggers `focus`
'
,
done
=>
{
const
spy
=
spyOn
(
document
.
querySelector
(
FORM_SELECTOR
),
'
focus
'
);
ShortcutsIssuable
.
replyWithSelectedText
(
true
);
expect
(
spy
).
toHaveBeenCalled
();
setTimeout
(()
=>
{
expect
(
spy
).
toHaveBeenCalled
();
done
();
});
});
});
});
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment