Merge pull request #1 from metamaps/develop
Updates from metamaps base 04/07/2017
This commit is contained in:
commit
db6d2e77da
13 changed files with 200 additions and 71 deletions
|
@ -42,6 +42,7 @@ class Map < ApplicationRecord
|
|||
after_create :after_created
|
||||
after_update :after_updated
|
||||
after_save :update_deferring_topics_and_synapses, if: :permission_changed?
|
||||
before_destroy :before_destroyed
|
||||
|
||||
delegate :count, to: :topics, prefix: :topic # same as `def topic_count; topics.count; end`
|
||||
delegate :count, to: :synapses, prefix: :synapse
|
||||
|
@ -158,4 +159,10 @@ class Map < ApplicationRecord
|
|||
end
|
||||
end
|
||||
handle_asynchronously :after_updated_async
|
||||
|
||||
def before_destroyed
|
||||
Map.where(source_id: id).find_each do |forked_map|
|
||||
forked_map.update(source_id: nil)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -37,8 +37,8 @@ class Mapping < ApplicationRecord
|
|||
'map_' + map.id.to_s,
|
||||
type: 'synapseAdded',
|
||||
synapse: mappable.filtered,
|
||||
topic1: mappable.topic1.filtered,
|
||||
topic2: mappable.topic2.filtered,
|
||||
topic1: mappable.topic1&.filtered,
|
||||
topic2: mappable.topic2&.filtered,
|
||||
mapping_id: id
|
||||
)
|
||||
meta = { 'mapping_id': id }
|
||||
|
|
|
@ -37,13 +37,13 @@
|
|||
map = notification.notified_object.map %>
|
||||
added topic <span class="in-bold"><%= topic.name %></span> to map <span class="in-bold"><%= map.name %></span>
|
||||
<% when TOPIC_CONNECTED_1 %>
|
||||
<% topic1 = notification.notified_object.topic1
|
||||
topic2 = notification.notified_object.topic2 %>
|
||||
connected <span class="in-bold"><%= topic1.name %></span> to <span class="in-bold"><%= topic2.name %></span>
|
||||
<% topic1 = notification.notified_object&.topic1 %>
|
||||
<% topic2 = notification.notified_object&.topic2 %>
|
||||
connected <span class="in-bold"><%= topic1&.name %></span> to <span class="in-bold"><%= topic2&.name %></span>
|
||||
<% when TOPIC_CONNECTED_2 %>
|
||||
<% topic1 = notification.notified_object.topic1
|
||||
topic2 = notification.notified_object.topic2 %>
|
||||
connected <span class="in-bold"><%= topic2.name %></span> to <span class="in-bold"><%= topic1.name %></span>
|
||||
<% topic1 = notification.notified_object&.topic1 %>
|
||||
<% topic2 = notification.notified_object&.topic2 %>
|
||||
connected <span class="in-bold"><%= topic2&.name %></span> to <span class="in-bold"><%= topic1&.name %></span>
|
||||
<% when MESSAGE_FROM_DEVS %>
|
||||
<%= notification.subject %>
|
||||
<% end %>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# frozen_string_literal: true
|
||||
METAMAPS_VERSION = '3.4'
|
||||
METAMAPS_VERSION = '3.4.1'
|
||||
METAMAPS_BUILD = `git log -1 --pretty=%H`.chomp[0..11].freeze
|
||||
METAMAPS_LAST_UPDATED = `git log -1 --pretty='%ad'`.split(' ').values_at(1, 2, 4).join(' ').freeze
|
||||
|
|
5
frontend/test/.eslintrc.js
Normal file
5
frontend/test/.eslintrc.js
Normal file
|
@ -0,0 +1,5 @@
|
|||
module.exports = {
|
||||
"rules": {
|
||||
"no-unused-expressions": "off"
|
||||
}
|
||||
}
|
|
@ -1,10 +1,8 @@
|
|||
/* global describe, it */
|
||||
|
||||
import chai from 'chai'
|
||||
import { expect } from 'chai'
|
||||
|
||||
import Import from '../src/Metamaps/Import'
|
||||
|
||||
const { expect } = chai
|
||||
import Import from '../../src/Metamaps/Import.js'
|
||||
|
||||
describe('Metamaps.Import.js', function() {
|
||||
it('has a topic whitelist', function() {
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
/* global describe, it */
|
||||
|
||||
import chai from 'chai'
|
||||
import { expect } from 'chai'
|
||||
|
||||
import Util from '../src/Metamaps/Util'
|
||||
|
||||
const { expect } = chai
|
||||
import Util from '../../src/Metamaps/Util'
|
||||
|
||||
describe('Metamaps.Util.js', function() {
|
||||
describe('splitLine', function() {
|
||||
|
|
|
@ -1,50 +0,0 @@
|
|||
/* global describe, it */
|
||||
import React from 'react'
|
||||
import TestUtils from 'react-addons-test-utils' // ES6
|
||||
import ImportDialogBox from '../../src/components/ImportDialogBox.js'
|
||||
import Dropzone from 'react-dropzone'
|
||||
import chai from 'chai'
|
||||
|
||||
const { expect } = chai
|
||||
|
||||
describe('ImportDialogBox', function() {
|
||||
it('has an Export CSV button', function(done) {
|
||||
const onExport = format => {
|
||||
if (format === 'csv') done()
|
||||
}
|
||||
const detachedComp = TestUtils.renderIntoDocument(<ImportDialogBox onExport={onExport} />)
|
||||
const button = TestUtils.findRenderedDOMComponentWithClass(detachedComp, 'export-csv')
|
||||
const buttonNode = React.findDOMNode(button)
|
||||
expect(button).to.exist;
|
||||
TestUtils.Simulate.click(buttonNode)
|
||||
})
|
||||
|
||||
it('has an Export JSON button', function(done) {
|
||||
const onExport = format => {
|
||||
if (format === 'json') done()
|
||||
}
|
||||
const detachedComp = TestUtils.renderIntoDocument(<ImportDialogBox onExport={onExport} />)
|
||||
const button = TestUtils.findRenderedDOMComponentWithClass(detachedComp, 'export-json')
|
||||
const buttonNode = React.findDOMNode(button)
|
||||
expect(button).to.exist;
|
||||
TestUtils.Simulate.click(buttonNode)
|
||||
})
|
||||
|
||||
it('has a Download screenshot button', function(done) {
|
||||
const downloadScreenshot = () => { done() }
|
||||
const detachedComp = TestUtils.renderIntoDocument(<ImportDialogBox downloadScreenshot={downloadScreenshot()} />)
|
||||
const button = TestUtils.findRenderedDOMComponentWithClass(detachedComp, 'download-screenshot')
|
||||
const buttonNode = React.findDOMNode(button)
|
||||
expect(button).to.exist;
|
||||
TestUtils.Simulate.click(buttonNode)
|
||||
})
|
||||
|
||||
it('has a file uploader', function(done) {
|
||||
const uploadedFile = { file: 'mock a file' }
|
||||
const onFileAdded = file => { if (file === uploadedFile) done() }
|
||||
const detachedComp = TestUtils.renderIntoDocument(<ImportDialogBox onExport={() => {}} onFileAdded={onFileAdded} />)
|
||||
const dropzone = TestUtils.findRenderedComponentWithType(detachedComp, Dropzone)
|
||||
expect(dropzone).to.exist;
|
||||
dropzone.props.onDropAccepted([uploadedFile], { preventDefault: () => {} })
|
||||
})
|
||||
})
|
58
frontend/test/components/MapView/ImportDialogBox.spec.js
Normal file
58
frontend/test/components/MapView/ImportDialogBox.spec.js
Normal file
|
@ -0,0 +1,58 @@
|
|||
/* global describe, it */
|
||||
import React from 'react'
|
||||
import ImportDialogBox from '../../../src/components/MapView/ImportDialogBox.js'
|
||||
import Dropzone from 'react-dropzone'
|
||||
import { expect } from 'chai'
|
||||
import { shallow } from 'enzyme'
|
||||
import sinon from 'sinon'
|
||||
|
||||
describe('ImportDialogBox', function() {
|
||||
const csvExport = sinon.spy()
|
||||
const jsonExport = sinon.spy()
|
||||
const onExport = format => () => {
|
||||
if (format === 'csv') {
|
||||
csvExport()
|
||||
} else if (format === 'json') {
|
||||
jsonExport()
|
||||
}
|
||||
}
|
||||
|
||||
const testExportButton = ({ description, cssClass, exporter }) => {
|
||||
it(description, () => {
|
||||
const wrapper = shallow(<ImportDialogBox onExport={onExport} />)
|
||||
const button = wrapper.find(cssClass)
|
||||
expect(button).to.exist
|
||||
button.simulate('click')
|
||||
expect(exporter).to.have.property('callCount', 1)
|
||||
})
|
||||
}
|
||||
testExportButton({
|
||||
description: 'has an Export CSV button',
|
||||
cssClass: '.export-csv',
|
||||
exporter: csvExport
|
||||
})
|
||||
testExportButton({
|
||||
description: 'has an Export JSON button',
|
||||
cssClass: '.export-json',
|
||||
exporter: jsonExport
|
||||
})
|
||||
|
||||
it('has a Download screenshot button', () => {
|
||||
const downloadScreenshot = sinon.spy()
|
||||
const wrapper = shallow(<ImportDialogBox onExport={() => null} downloadScreenshot={downloadScreenshot} />)
|
||||
const button = wrapper.find('.download-screenshot')
|
||||
expect(button).to.exist
|
||||
button.simulate('click')
|
||||
expect(downloadScreenshot).to.have.property('callCount', 1)
|
||||
})
|
||||
|
||||
it('has a file uploader', () => {
|
||||
const uploadedFile = {}
|
||||
const onFileAdded = sinon.spy()
|
||||
const wrapper = shallow(<ImportDialogBox onExport={() => null} onFileAdded={onFileAdded} />)
|
||||
const dropzone = wrapper.find(Dropzone)
|
||||
dropzone.props().onDropAccepted([uploadedFile], { preventDefault: () => {} })
|
||||
expect(onFileAdded).to.have.property('callCount', 1)
|
||||
expect(onFileAdded.calledWith(uploadedFile)).to.equal(true)
|
||||
})
|
||||
})
|
101
frontend/test/components/common/InfoAndHelp.spec.js
Normal file
101
frontend/test/components/common/InfoAndHelp.spec.js
Normal file
|
@ -0,0 +1,101 @@
|
|||
/* global describe, it */
|
||||
import React from 'react'
|
||||
import { expect } from 'chai'
|
||||
import { shallow } from 'enzyme'
|
||||
import sinon from 'sinon'
|
||||
|
||||
import InfoAndHelp from '../../../src/components/common/InfoAndHelp.js'
|
||||
import MapInfoBox from '../../../src/components/MapView/MapInfoBox.js'
|
||||
|
||||
function assertTooltip({ wrapper, description, cssClass, tooltipText, callback }) {
|
||||
it(description, function() {
|
||||
expect(wrapper.find(cssClass)).to.exist
|
||||
expect(wrapper.find(`${cssClass} .tooltipsAbove`).text()).to.equal(tooltipText)
|
||||
wrapper.find(cssClass).simulate('click')
|
||||
expect(callback).to.have.property('callCount', 1)
|
||||
})
|
||||
}
|
||||
|
||||
function assertContent({ currentUser, map }) {
|
||||
const onInfoClick = sinon.spy()
|
||||
const onHelpClick = sinon.spy()
|
||||
const onStarClick = sinon.spy()
|
||||
const wrapper = shallow(
|
||||
<InfoAndHelp map={map} currentUser={currentUser}
|
||||
onInfoClick={onInfoClick}
|
||||
onHelpClick={onHelpClick}
|
||||
onMapStar={onStarClick}
|
||||
mapIsStarred={false}
|
||||
/>)
|
||||
|
||||
if (map) {
|
||||
it('renders MapInfoBox', () => expect(wrapper.find(MapInfoBox)).to.exist)
|
||||
assertTooltip({
|
||||
wrapper,
|
||||
description: 'renders Map Info icon',
|
||||
cssClass: '.mapInfoIcon',
|
||||
tooltipText: 'Map Info',
|
||||
callback: onInfoClick
|
||||
})
|
||||
} else {
|
||||
it('does not render MapInfoBox', () => expect(wrapper.find(MapInfoBox).length).to.equal(0))
|
||||
it('does not render Map Info icon', () => expect(wrapper.find('.mapInfoIcon').length).to.equal(0))
|
||||
}
|
||||
|
||||
if (map && currentUser) {
|
||||
it('renders Star icon', () => {
|
||||
expect(wrapper.find('.starMap')).to.exist
|
||||
wrapper.find('.starMap').simulate('click')
|
||||
expect(onStarClick).to.have.property('callCount', 1)
|
||||
})
|
||||
} else {
|
||||
it('does not render the Star icon', () => expect(wrapper.find('.starMap').length).to.equal(0))
|
||||
}
|
||||
|
||||
// common content
|
||||
assertTooltip({
|
||||
wrapper,
|
||||
description: 'renders Help icon',
|
||||
cssClass: '.openCheatsheet',
|
||||
tooltipText: 'Help',
|
||||
callback: onHelpClick
|
||||
})
|
||||
it('renders clearfloat at the end', function() {
|
||||
const clearfloat = wrapper.find('.clearfloat')
|
||||
expect(clearfloat).to.exist
|
||||
expect(wrapper.find('.infoAndHelp').children().last()).to.eql(clearfloat)
|
||||
})
|
||||
}
|
||||
|
||||
function assertStarLogic({ mapIsStarred }) {
|
||||
const onMapStar = sinon.spy()
|
||||
const onMapUnstar = sinon.spy()
|
||||
const wrapper = shallow(
|
||||
<InfoAndHelp map={{}} currentUser={{}}
|
||||
onMapStar={onMapStar}
|
||||
onMapUnstar={onMapUnstar}
|
||||
mapIsStarred={mapIsStarred}
|
||||
/>)
|
||||
const starWrapper = wrapper.find('.starMap')
|
||||
starWrapper.simulate('click')
|
||||
it(mapIsStarred ? 'has unstar content' : 'has star content', () => {
|
||||
expect(starWrapper.hasClass('starred')).to.equal(mapIsStarred)
|
||||
expect(starWrapper.find('.tooltipsAbove').text()).to.equal(mapIsStarred ? 'Unstar' : 'Star')
|
||||
expect(onMapStar).to.have.property('callCount', mapIsStarred ? 0 : 1)
|
||||
expect(onMapUnstar).to.have.property('callCount', mapIsStarred ? 1 : 0)
|
||||
})
|
||||
}
|
||||
|
||||
describe('InfoAndHelp', function() {
|
||||
describe('no currentUser, map is present', function() {
|
||||
assertContent({ currentUser: null, map: {} })
|
||||
})
|
||||
describe('currentUser is present, map is present', function() {
|
||||
assertContent({ currentUser: {}, map: {} })
|
||||
})
|
||||
describe('no currentUser, no map', function() {
|
||||
assertContent({ currentUser: null, map: null })
|
||||
})
|
||||
assertStarLogic({ mapIsStarred: true })
|
||||
assertStarLogic({ mapIsStarred: false })
|
||||
})
|
|
@ -5,12 +5,12 @@ const win = doc.defaultView
|
|||
global.document = doc
|
||||
global.window = win
|
||||
|
||||
// take all properties of the window object and also attach it to the
|
||||
// take all properties of the window object and also attach it to the
|
||||
// mocha global object
|
||||
propagateToGlobal(win)
|
||||
|
||||
// from mocha-jsdom https://github.com/rstacruz/mocha-jsdom/blob/master/index.js#L80
|
||||
function propagateToGlobal (window) {
|
||||
function propagateToGlobal(window) {
|
||||
for (let key in window) {
|
||||
if (!window.hasOwnProperty(key)) continue
|
||||
if (key in global) continue
|
|
@ -5,7 +5,7 @@
|
|||
"scripts": {
|
||||
"build": "webpack",
|
||||
"build:watch": "webpack --watch",
|
||||
"test": "mocha-webpack --webpack-config webpack.test.config.js --require frontend/test/support/dom.js frontend/test",
|
||||
"test": "mocha-webpack --webpack-config webpack.test.config.js --require frontend/test_support/dom.js --recursive frontend/test",
|
||||
"eslint": "eslint frontend",
|
||||
"eslint:fix": "eslint --fix frontend"
|
||||
},
|
||||
|
@ -58,6 +58,7 @@
|
|||
"babel-eslint": "^7.1.1",
|
||||
"chai": "^3.5.0",
|
||||
"circular-dependency-plugin": "^2.0.0",
|
||||
"enzyme": "^2.8.2",
|
||||
"eslint": "^3.11.1",
|
||||
"eslint-config-standard": "^6.2.1",
|
||||
"eslint-plugin-promise": "^3.4.0",
|
||||
|
@ -66,7 +67,8 @@
|
|||
"jsdom": "^9.11.0",
|
||||
"mocha": "^3.2.0",
|
||||
"mocha-webpack": "^0.7.0",
|
||||
"react-addons-test-utils": "^15.4.2"
|
||||
"react-addons-test-utils": "^15.5.1",
|
||||
"sinon": "^2.2.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"raml2html": "4.0.5"
|
||||
|
|
|
@ -1,5 +1,15 @@
|
|||
const config = require('./webpack.config')
|
||||
|
||||
config.target = 'node'
|
||||
config.externals = config.externals.concat([
|
||||
'react/lib/ExecutionEnvironment',
|
||||
'react/lib/ReactContext',
|
||||
'react/addons',
|
||||
'react-test-renderer/shallow',
|
||||
'react-dom/test-utils',
|
||||
'canvas',
|
||||
'bufferutil',
|
||||
'utf-8-validate'
|
||||
])
|
||||
|
||||
module.exports = config
|
||||
|
|
Loading…
Reference in a new issue