metamaps--metamaps/frontend/test/Metamaps/Util.spec.js

326 lines
12 KiB
JavaScript
Raw Permalink Normal View History

2018-03-17 19:06:42 +00:00
/* global describe, it, afterEach */
import { expect } from 'chai'
2018-03-17 19:06:42 +00:00
import sinon from 'sinon'
import Util from '../../src/Metamaps/Util'
2018-03-19 15:33:57 +00:00
import * as EmojiMart from 'emoji-mart'
2018-03-17 19:06:42 +00:00
const sandbox = sinon.sandbox.create()
2016-11-07 20:25:08 +00:00
describe('Metamaps.Util.js', function() {
describe('splitLine', function() {
it('splits on words', function() {
expect(Util.splitLine('test test test', 10))
.to.equal('test test\ntest')
})
// TODO this test seems like it's incorrect behaviour
2016-11-07 20:25:08 +00:00
it('splits mid-word if need be', function() {
expect(Util.splitLine('test test', 2))
.to.equal('te\nt\nte\nt')
})
2016-11-07 20:25:08 +00:00
it('splits words over 30 chars', function() {
expect(Util.splitLine('suprainterpostantidisestablishmentarianism', 30))
.to.equal('suprainterpostantidisestablish\nentarianism')
})
})
2016-11-07 20:25:08 +00:00
describe('nowDateFormatted', function() {
2018-03-17 19:06:42 +00:00
function assertNowDateFormatted(expected, { month, day, year }) {
const date = {
getDate: () => day,
getMonth: () => month - 1, // 0 to 11
getFullYear: () => year
}
expect(Util.nowDateFormatted(date)).to.equal(expected)
}
it('formats dates with one digit properly', function() {
assertNowDateFormatted('01/01/2000', { month: 1, day: 1, year: 2000 })
})
it('formats dates with two digits properly', function() {
assertNowDateFormatted('10/10/2000', { month: 10, day: 10, year: 2000 })
})
})
2016-11-07 20:25:08 +00:00
describe('decodeEntities', function() {
2018-03-17 19:06:42 +00:00
function assertDecodeEntities(expected, { textContent, innerText, desc }) {
const paragraph = { textContent, innerText }
2018-03-17 19:50:35 +00:00
sandbox.stub(document, 'createElement').withArgs('p').returns(paragraph)
2018-03-17 19:06:42 +00:00
const actual = Util.decodeEntities(desc)
expect(actual).to.equal(expected)
expect(paragraph.innerHTML).to.equal(desc)
}
afterEach(function() {
sandbox.restore()
})
it('returns textContent if available', function() {
assertDecodeEntities('textContent',
{ textContent: 'textContent', innerText: 'innerText', desc: 'desc' })
})
it('otherwise returns innerText', function() {
assertDecodeEntities('innerText',
{ innerText: 'innerText', desc: 'desc' })
})
})
2016-11-07 20:25:08 +00:00
describe('getDistance', function() {
it('(0,0) -> (0,0) = 0', function() {
expect(Util.getDistance({ x: 0, y: 0 }, { x: 0, y: 0 }))
.to.equal(0)
})
2016-11-07 20:25:08 +00:00
it('(-5,0) -> (5,0) = 10', function() {
expect(Util.getDistance({ x: -5, y: 0 }, { x: 5, y: 0 }))
.to.equal(10)
})
2016-11-07 20:25:08 +00:00
it('(0,0) -> (5,7) = 8.6023', function() {
expect(Util.getDistance({ x: 0, y: 0 }, { x: 5, y: 7 }).toFixed(4))
.to.equal('8.6023')
})
})
2018-03-21 03:13:34 +00:00
describe('coords/pixels conversions', function() {
function mockMGraph({ x, y, sx, sy, px, py, width, height, ox, oy }) {
return {
canvas: {
getSize: () => ({ width, height }),
getPos: () => ({ x: px, y: py }),
translateOffsetX: ox,
translateOffsetY: oy,
scaleOffsetX: sx,
scaleOffsetY: sy
}
}
}
function assertConversion(testFunction, expectedX, expectedY,
2018-03-17 19:50:35 +00:00
{ x, y, sx, sy, px, py, width, height, ox, oy }) {
2018-03-19 15:33:57 +00:00
const mGraph = mockMGraph({
x, y, sx, sy, px, py, width, height, ox, oy
})
2018-03-17 19:06:42 +00:00
const coords = { x, y }
2018-03-21 03:13:34 +00:00
const actual = testFunction(mGraph, coords)
2018-03-17 19:06:42 +00:00
expect(actual.x).to.equal(expectedX)
expect(actual.y).to.equal(expectedY)
}
2018-03-21 03:13:34 +00:00
it('coordsToPixels returns 0,0 for null canvas', function() {
expect(Util.coordsToPixels(null, {}).x).to.equal(0)
expect(Util.coordsToPixels(null, {}).y).to.equal(0)
})
2018-03-21 03:13:34 +00:00
it('coordsToPixels', function() {
assertConversion(Util.coordsToPixels, 0, 0,
2018-03-17 19:50:35 +00:00
{ x: 0, y: 0, sx: 1, sy: 1, px: 0, py: 0, width: 0, height: 0, ox: 0, oy: 0 })
2018-03-21 03:13:34 +00:00
assertConversion(Util.coordsToPixels, 1, 1,
2018-03-17 19:50:35 +00:00
{ x: 1, y: 1, sx: 1, sy: 1, px: 0, py: 0, width: 0, height: 0, ox: 0, oy: 0 })
2018-03-21 03:13:34 +00:00
assertConversion(Util.coordsToPixels, 2, 1,
2018-03-17 19:50:35 +00:00
{ x: 1, y: 1, sx: 2, sy: 1, px: 0, py: 0, width: 0, height: 0, ox: 0, oy: 0 })
2018-03-21 03:13:34 +00:00
assertConversion(Util.coordsToPixels, 2, 2,
2018-03-17 19:50:35 +00:00
{ x: 1, y: 1, sx: 2, sy: 2, px: 0, py: 0, width: 0, height: 0, ox: 0, oy: 0 })
2018-03-21 03:13:34 +00:00
assertConversion(Util.coordsToPixels, 3, 2,
2018-03-17 19:50:35 +00:00
{ x: 1, y: 1, sx: 2, sy: 2, px: 1, py: 0, width: 0, height: 0, ox: 0, oy: 0 })
2018-03-21 03:13:34 +00:00
assertConversion(Util.coordsToPixels, 3, 3,
2018-03-17 19:50:35 +00:00
{ x: 1, y: 1, sx: 2, sy: 2, px: 1, py: 1, width: 0, height: 0, ox: 0, oy: 0 })
2018-03-21 03:13:34 +00:00
assertConversion(Util.coordsToPixels, 4, 3,
2018-03-17 19:50:35 +00:00
{ x: 1, y: 1, sx: 2, sy: 2, px: 1, py: 1, width: 2, height: 0, ox: 0, oy: 0 })
2018-03-21 03:13:34 +00:00
assertConversion(Util.coordsToPixels, 4, 4,
2018-03-17 19:50:35 +00:00
{ x: 1, y: 1, sx: 2, sy: 2, px: 1, py: 1, width: 2, height: 2, ox: 0, oy: 0 })
2018-03-21 03:13:34 +00:00
assertConversion(Util.coordsToPixels, 9, 4,
2018-03-17 19:50:35 +00:00
{ x: 1, y: 1, sx: 2, sy: 2, px: 1, py: 1, width: 2, height: 2, ox: 5, oy: 0 })
2018-03-21 03:13:34 +00:00
assertConversion(Util.coordsToPixels, 9, 9,
2018-03-17 19:50:35 +00:00
{ x: 1, y: 1, sx: 2, sy: 2, px: 1, py: 1, width: 2, height: 2, ox: 5, oy: 5 })
2018-03-17 19:06:42 +00:00
})
2018-03-21 03:13:34 +00:00
it('pixelsToCoords returns 0,0 for null canvas', function() {
expect(Util.pixelsToCoords(null, {}).x).to.equal(0)
expect(Util.pixelsToCoords(null, {}).y).to.equal(0)
})
2018-03-21 03:13:34 +00:00
it('pixelsToCoords', function() {
assertConversion(Util.pixelsToCoords, 0, 0,
2018-03-17 19:50:35 +00:00
{ x: 0, y: 0, px: 0, py: 0, width: 0, height: 0, ox: 0, oy: 0, sx: 1, sy: 1 })
2018-03-21 03:13:34 +00:00
assertConversion(Util.pixelsToCoords, 5, 5,
2018-03-17 19:50:35 +00:00
{ x: 5, y: 5, px: 0, py: 0, width: 0, height: 0, ox: 0, oy: 0, sx: 1, sy: 1 })
2018-03-21 03:13:34 +00:00
assertConversion(Util.pixelsToCoords, 4, 5,
2018-03-17 19:50:35 +00:00
{ x: 5, y: 5, px: 1, py: 0, width: 0, height: 0, ox: 0, oy: 0, sx: 1, sy: 1 })
2018-03-21 03:13:34 +00:00
assertConversion(Util.pixelsToCoords, 4, 4,
2018-03-17 19:50:35 +00:00
{ x: 5, y: 5, px: 1, py: 1, width: 0, height: 0, ox: 0, oy: 0, sx: 1, sy: 1 })
2018-03-21 03:13:34 +00:00
assertConversion(Util.pixelsToCoords, 3, 4,
2018-03-17 19:50:35 +00:00
{ x: 5, y: 5, px: 1, py: 1, width: 2, height: 0, ox: 0, oy: 0, sx: 1, sy: 1 })
2018-03-21 03:13:34 +00:00
assertConversion(Util.pixelsToCoords, 3, 3,
2018-03-17 19:50:35 +00:00
{ x: 5, y: 5, px: 1, py: 1, width: 2, height: 2, ox: 0, oy: 0, sx: 1, sy: 1 })
2018-03-21 03:13:34 +00:00
assertConversion(Util.pixelsToCoords, 2, 3,
2018-03-17 19:50:35 +00:00
{ x: 5, y: 5, px: 1, py: 1, width: 2, height: 2, ox: 1, oy: 0, sx: 1, sy: 1 })
2018-03-21 03:13:34 +00:00
assertConversion(Util.pixelsToCoords, 2, 2,
2018-03-17 19:50:35 +00:00
{ x: 5, y: 5, px: 1, py: 1, width: 2, height: 2, ox: 1, oy: 1, sx: 1, sy: 1 })
2018-03-21 03:13:34 +00:00
assertConversion(Util.pixelsToCoords, 4, 2,
2018-03-17 19:50:35 +00:00
{ x: 5, y: 5, px: 1, py: 1, width: 2, height: 2, ox: 1, oy: 1, sx: 0.5, sy: 1 })
2018-03-21 03:13:34 +00:00
assertConversion(Util.pixelsToCoords, 4, 4,
2018-03-17 19:50:35 +00:00
{ x: 5, y: 5, px: 1, py: 1, width: 2, height: 2, ox: 1, oy: 1, sx: 0.5, sy: 0.5 })
2018-03-17 19:06:42 +00:00
})
})
2016-11-07 20:25:08 +00:00
describe('getPastelColor', function() {
it('1 => fefefe', function() {
expect(Util.getPastelColor({ rseed: 1, gseed: 1, bseed: 1 }))
.to.equal(Util.colorLuminance('#fefefe', -0.4))
})
2016-11-07 20:25:08 +00:00
it('0 => 7f7f7f', function() {
expect(Util.getPastelColor({ rseed: 0, gseed: 0, bseed: 0 }))
.to.equal(Util.colorLuminance('#7f7f7f', -0.4))
})
})
2016-11-07 20:25:08 +00:00
describe('colorLuminance', function() {
describe('-0.4 lum', function() {
it('white => ?', function() {
expect(Util.colorLuminance('#ffffff', -0.4)).to.equal('#999999')
})
2016-11-07 20:25:08 +00:00
it('black => ?', function() {
expect(Util.colorLuminance('#000000', -0.4)).to.equal('#000000')
})
2016-11-07 20:25:08 +00:00
it('7f7f7f => ?', function() {
expect(Util.colorLuminance('#7f7f7f', -0.4)).to.equal('#4c4c4c')
})
})
2016-11-07 20:25:08 +00:00
describe('other lum values', function() {
it('-1', function() {
expect(Util.colorLuminance('#7f7f7f', -1)).to.equal('#000000')
})
2016-11-07 20:25:08 +00:00
it('-0.5', function() {
expect(Util.colorLuminance('#7f7f7f', -0.5)).to.equal('#404040')
})
2016-11-07 20:25:08 +00:00
it('0', function() {
expect(Util.colorLuminance('#7f7f7f', 0)).to.equal('#7f7f7f')
})
2016-11-07 20:25:08 +00:00
it('0.5', function() {
expect(Util.colorLuminance('#7f7f7f', 0.5)).to.equal('#bfbfbf')
})
2016-11-07 20:25:08 +00:00
it('1', function() {
expect(Util.colorLuminance('#7f7f7f', 1)).to.equal('#fefefe')
})
})
})
2016-11-07 20:25:08 +00:00
describe('openLink', function() {
2018-03-17 19:06:42 +00:00
function stubWindow({ popupsAllowed }) {
2018-03-17 19:50:35 +00:00
const open = sandbox.stub(window, 'open').returns(popupsAllowed)
const alert = sandbox.stub(window, 'alert')
2018-03-17 19:06:42 +00:00
return { open, alert }
}
afterEach(function() {
sandbox.restore()
})
it('blank url returns true', function() {
expect(Util.openLink('')).to.equal(true)
})
it('popus allowed returns true', function() {
stubWindow({ popupsAllowed: true })
expect(Util.openLink('https://www.google.ca')).to.equal(true)
})
it('popups blocked shows alert', function() {
const { alert } = stubWindow({ popupsAllowed: false })
expect(Util.openLink('https://www.google.ca')).to.equal(false)
expect(alert.calledWith('Please allow popups in order to open the link'))
.to.equal(true)
})
})
2016-11-07 20:25:08 +00:00
describe('mdToHTML', function() {
it('filters xss', function() {
const md = '<script>alert("xss");</script>'
const html = '<!-- raw HTML omitted -->'
expect(Util.mdToHTML(md).trim()).to.equal(html)
})
2016-11-07 20:25:08 +00:00
it('bold and italics', function() {
const md = '**Bold** *Italics*'
const html = '<p><strong>Bold</strong> <em>Italics</em></p>'
expect(Util.mdToHTML(md).trim()).to.equal(html)
})
Into master: two finger pan/zoom, map and topic follows (for internal testing) on the UI, map activity emails (#1084) * fix topic spec * fix synapse/mapping spec * brakeman csrf warning suppressed :| * follows for maps in the ui for internal testing only still (#1072) * follows for maps in the ui for testers * require user for these actions * match how map follow works * include ability to unfollow from email * fixup templates * add unfollow_from_email to the policies * Update _cheatsheet.html.erb Clean up text, clarify, and bring in line with current functionality * topicsRegex and synapsesRegex should allow commas (#1073) * even better import csv regexes * prevent double prompt on file drop import * topic card in react (#1031) * its coming along * links bar * scssify a bunch * metacode image working a bit better * metacode selector in react topic card * riek editing for name field on topic card * riek submit on enter * factor out Title and Links from Topic Card component, but not the listeners * create working Desc editor * styling is much better now * textarea min height for desc * disallow images in topic card markdown * shift enter is linebreak, enter is save * attachments split out, but it's pretty buggy * move listeners into Links.js * slightly wider metacodeTitle * fix positioning on metacode selector * fix metacode selection * move metacode and permissions into subcomponents * fixes * prevent editing on desc/title if not authorized to edit * fix topic card draggability * fix embedly * fix md test * remove the removed link card manually with jquery * fix test syntax * eslint * more eslin * reuse authorizedToEdit * convert metacode sets to a json object for react * add the html in react whoop * fix metacode styling * sort wasn't working * finishing metacode select * readd the above link input border * fix syntax * multiline title editable textarea * more portable metacode selector component * factor out #metacodeOptions into one react component with a callback :D:D:D * render metacodeOptions in right click menu with react * render metacodeOptions in right click menu with react * fix up right click menu's metacode editing * fix topic card title character counter * ignore metamaps secret bundle in ag * simplify Attachments props * factor out embedly card into its own component; it seems to help * link resetter * fix edit icon on title in topic card * move mapCount and synapseCount hover/click logic to react * fix up the showMore control * metacode selection tweaks * tweak links bar spacing in topic card * rubocop * remove TODOs * more badass permissions selector * close permission selector when you click outside * fix overeager metacode selector * more modular attachments component * fix bug in Desc.js * fix right click styling * permission changes are different than edit rights * bad module ref * ensure maxLength on topic titles * hellz yeah (#1074) * fix drop from two touches to one * don't commit activity service * ability to select/unselect all metacodes in custom set with keyboard shortcut (fix #390) (#1078) * ability to select/unselect all metacodes in custom set with keyboard shortcut * select all button * nicer all/none buttons * set up react testing (#1080) * install mocha-webpack. also switch hark to npm version instead of github version * well, mocha-webpack runs * add jsdom for tests * upgrade to webpack 2 * fix npm run test errors * ImportDialogBox component tests * Fixes bug where pressing delete key while editing text will suggest... (#1083) * Fixes bug where pressing delete key while editing text will suggest the deletion of selected map entities * Changed the DEL key to remove entities instead of delete them * temporarily disable code climate duplication engine * add topic following for internal testing * daily map activity emails (#1081) * data prepared, task setup * add the basics of the email template * cover granular permissions * unfollow this map * break out permissions tests better * rename so test runs
2017-03-07 03:49:46 +00:00
it('links', function() {
const md = '[Link](https://metamaps.cc)'
const html = '<p><a href="https://metamaps.cc">Link</a></p>'
expect(Util.mdToHTML(md).trim()).to.equal(html)
})
it('images are not rendered', function() {
const md = '![Image](https://example.org/image.png)'
const html = '<p>![Image](https://example.org/image.png)</p>'
expect(Util.mdToHTML(md).trim()).to.equal(html)
})
})
2016-11-07 20:25:08 +00:00
describe('logCanvasAttributes', function() {
2018-03-17 19:06:42 +00:00
it('returns correct canvas attributes', function() {
const canvas = {
2018-03-17 19:50:35 +00:00
scaleOffsetX: 1,
scaleOffsetY: 2,
2018-03-17 19:06:42 +00:00
canvases: [{ size: { width: 3, height: 4 } }]
}
2018-03-17 19:50:35 +00:00
sinon.stub(Util, 'pixelsToCoords').returnsArg(1)
2018-03-17 19:06:42 +00:00
const actual = Util.logCanvasAttributes(canvas)
expect(actual.scaleX).to.equal(1)
expect(actual.scaleY).to.equal(2)
// stub will return the x/y coords passed to pixelsToCoords
expect(actual.centreCoords.x).to.equal(3 / 2)
expect(actual.centreCoords.y).to.equal(4 / 2)
})
})
2016-11-07 20:25:08 +00:00
describe('resizeCanvas', function() {
2018-03-17 20:15:48 +00:00
it('resizes, scales, and translates canvas', function() {
const oldAttr = {
scaleX: 1,
scaleY: 2,
centreCoords: { x: 3, y: 4 }
}
const newAttr = {
centreCoords: { x: 5, y: 6 }
}
const canvas = {
resize: sinon.spy(),
scale: sinon.spy(),
translate: sinon.spy()
}
sinon.stub(Util, 'logCanvasAttributes').withArgs(canvas)
.onFirstCall().returns(oldAttr)
.onSecondCall().returns(newAttr)
const jQueryStub = sinon.stub().returns({
width: () => 7,
height: () => 8
})
Util.resizeCanvas(canvas, jQueryStub)
expect(canvas.resize.calledWith(7, 8), 'resize not called')
.to.equal(true)
expect(canvas.scale.calledWith(1, 2), 'scale not called')
.to.equal(true)
expect(canvas.translate.calledWith(5 - 3, 6 - 4),
'translate not called')
2018-03-17 20:15:48 +00:00
.to.equal(true)
})
2018-03-21 03:13:34 +00:00
})
2018-03-19 15:33:57 +00:00
describe('emoji', function() {
EmojiMart.emojiIndex = {
emojis: {
emoji1: { native: '__EMOJI1__', colons: ':emoji1:' },
emoji2: { native: '__EMOJI2__', colons: ':emoji2:' }
2018-03-21 03:13:34 +00:00
},
emoticons: {
':)': 'emoji1',
':(': 'emoji2'
2018-03-19 15:33:57 +00:00
}
}
2018-03-21 03:13:34 +00:00
const withEmojiText = 'test __EMOJI1__ test __EMOJI2__ test'
const noEmojiText = 'test :emoji1: test :emoji2: test'
2018-03-19 15:33:57 +00:00
const emoticonsText = 'test :) test :( test'
const emoticonsReplacedText = 'test __EMOJI1__ test __EMOJI2__ test'
it('removeEmoji replaces emoji with text', function() {
expect(Util.removeEmoji(withEmojiText)).to.equal(noEmojiText)
})
it('addEmoji replaces text with emoji', function() {
expect(Util.addEmoji(noEmojiText, { emoticons: false })).to.equal(withEmojiText)
2018-03-19 15:33:57 +00:00
})
it('addEmoji replaces emoticons with emoji', function() {
2018-03-19 15:33:57 +00:00
expect(Util.addEmoji(emoticonsText, { emoticons: true }))
.to.equal(emoticonsReplacedText)
})
})
})