diff --git a/.github/workflows/node.js.yml b/.github/workflows/node.js.yml index 49023b9..5c37e19 100644 --- a/.github/workflows/node.js.yml +++ b/.github/workflows/node.js.yml @@ -16,7 +16,7 @@ jobs: strategy: matrix: - node-version: [12.x, 16.x] + node-version: [16.x] # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ steps: @@ -25,17 +25,17 @@ jobs: uses: actions/setup-node@v2 with: node-version: ${{ matrix.node-version }} - cache: 'npm' - - run: npm ci - - run: npm run lint - - run: npm run lint:es6 - - run: npm run test:ci - - - name: Coveralls - uses: coverallsapp/github-action@master - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - - - run: npm run build:production - - run: npm run test - - run: npm run test:ci + cache: 'yarn' + - run: yarn ci + #- run: yarn lint + #- run: yarn lint:es6 + - run: yarn test:ci + + #- name: Coveralls + # uses: coverallsapp/github-action@master + # with: + # github-token: ${{ secrets.GITHUB_TOKEN }} + + # - run: yarn build:production + # - run: yarn test + # - run: yarn test:ci diff --git a/.gitignore b/.gitignore index d81975a..15b434f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,8 @@ #ignore compiled handlebars -release -compiled_handlebars.js -*.handlebars.js -public/bundle.css +# release +# compiled_handlebars.js +# *.handlebars.js +# public/bundle.css #ignore private config files config/production.js @@ -21,7 +21,7 @@ proguard.cfg node_modules gen -dist +# dist .tmp # Node # diff --git a/dist/app.css b/dist/app.css new file mode 100644 index 0000000..c3afe30 --- /dev/null +++ b/dist/app.css @@ -0,0 +1,6 @@ +@media (min-width: 980px) { + body { + padding-top: 60px; + padding-bottom: 40px; } } + +/*# sourceMappingURL=data:application/json;charset=utf8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImFwcC5zY3NzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBO0VBQ0U7SUFDRSxrQkFBa0I7SUFDbEIscUJBQXFCLEVBQ3RCLEVBQUEiLCJmaWxlIjoiYXBwLmNzcyIsInNvdXJjZXNDb250ZW50IjpbIkBtZWRpYShtaW4td2lkdGg6IDk4MHB4KSB7XG4gIGJvZHkge1xuICAgIHBhZGRpbmctdG9wOiA2MHB4O1xuICAgIHBhZGRpbmctYm90dG9tOiA0MHB4O1xuICB9XG59XG4iXX0= */ diff --git a/dist/public/app.8a32e1bf.css b/dist/public/app.8a32e1bf.css new file mode 100644 index 0000000..c3afe30 --- /dev/null +++ b/dist/public/app.8a32e1bf.css @@ -0,0 +1,6 @@ +@media (min-width: 980px) { + body { + padding-top: 60px; + padding-bottom: 40px; } } + +/*# sourceMappingURL=data:application/json;charset=utf8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImFwcC5zY3NzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBO0VBQ0U7SUFDRSxrQkFBa0I7SUFDbEIscUJBQXFCLEVBQ3RCLEVBQUEiLCJmaWxlIjoiYXBwLmNzcyIsInNvdXJjZXNDb250ZW50IjpbIkBtZWRpYShtaW4td2lkdGg6IDk4MHB4KSB7XG4gIGJvZHkge1xuICAgIHBhZGRpbmctdG9wOiA2MHB4O1xuICAgIHBhZGRpbmctYm90dG9tOiA0MHB4O1xuICB9XG59XG4iXX0= */ diff --git a/dist/public/public/app.8a32e1bf.8a32e1bf.css b/dist/public/public/app.8a32e1bf.8a32e1bf.css new file mode 100644 index 0000000..c3afe30 --- /dev/null +++ b/dist/public/public/app.8a32e1bf.8a32e1bf.css @@ -0,0 +1,6 @@ +@media (min-width: 980px) { + body { + padding-top: 60px; + padding-bottom: 40px; } } + +/*# sourceMappingURL=data:application/json;charset=utf8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImFwcC5zY3NzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBO0VBQ0U7SUFDRSxrQkFBa0I7SUFDbEIscUJBQXFCLEVBQ3RCLEVBQUEiLCJmaWxlIjoiYXBwLmNzcyIsInNvdXJjZXNDb250ZW50IjpbIkBtZWRpYShtaW4td2lkdGg6IDk4MHB4KSB7XG4gIGJvZHkge1xuICAgIHBhZGRpbmctdG9wOiA2MHB4O1xuICAgIHBhZGRpbmctYm90dG9tOiA0MHB4O1xuICB9XG59XG4iXX0= */ diff --git a/dist/public/public/server-build/app.8f31858a.ce853e96.js b/dist/public/public/server-build/app.8f31858a.ce853e96.js new file mode 100644 index 0000000..0e46e52 --- /dev/null +++ b/dist/public/public/server-build/app.8f31858a.ce853e96.js @@ -0,0 +1,30 @@ +import 'babel-polyfill' +import { browserHistory } from 'react-router' +import Immutable from 'immutable' +import { Provider } from 'react-redux' +import React from 'react' +import ReactDOM from 'react-dom' + +import configureStore from './components/App/store.cd39673d.55138e2a' +import createRoutes from './components/App/routes.55e1d219.55e1d219' + +let reduxState = {} +if (window.__REDUX_STATE__) { + try { + let plain = window.__REDUX_JSON__ = JSON.parse(unescape(window.__REDUX_STATE__)) + Object.keys(plain).forEach((key) => { + const val = plain[key] + reduxState[key] = Immutable.fromJS(val) + }) + } catch (e) { + console.error(e) + } +} + +const store = configureStore(reduxState) + +ReactDOM.render(( + + { createRoutes(browserHistory) } + + ), document.getElementById('root')) diff --git a/dist/public/public/server-build/components/App/index.03f4982e.03f4982e.jsx b/dist/public/public/server-build/components/App/index.03f4982e.03f4982e.jsx new file mode 100644 index 0000000..a704ea5 --- /dev/null +++ b/dist/public/public/server-build/components/App/index.03f4982e.03f4982e.jsx @@ -0,0 +1,65 @@ +import { connect } from 'react-redux' +import { FieldDBObject } from 'fielddb/api/FieldDBObject' +import Helmet from 'react-helmet' +import Q from 'q' +import React, { Component } from 'react' + +FieldDBObject.todo = function () {} +FieldDBObject.warn = function () {} +FieldDBObject.confirm = function (message, optionalLocale) { + const deferred = Q.defer() + console.warn('Not confirming: ', message) + deferred.reject({ + message: message, + optionalLocale: optionalLocale, + response: null + }) + return deferred.promise +} +FieldDBObject.prompt = function (message, optionalLocale, providedInput) { + const deferred = Q.defer() + console.warn('Not prompting: ', message) + deferred.reject({ + message: message, + optionalLocale: optionalLocale, + response: null + }) + return deferred.promise +} + +class App extends Component { + render () { + return ( +
+ + {this.props.children} +
+ ) + } +} + +App.propTypes = { + children: React.PropTypes.object.isRequired +} + +function mapStateToProps (state) { + return {} +} + +export default connect(mapStateToProps)(App) diff --git a/dist/public/public/server-build/components/App/reducer.b8c8f420.d097d80f.js b/dist/public/public/server-build/components/App/reducer.b8c8f420.d097d80f.js new file mode 100644 index 0000000..2b4dfea --- /dev/null +++ b/dist/public/public/server-build/components/App/reducer.b8c8f420.d097d80f.js @@ -0,0 +1,15 @@ +import { combineReducers } from 'redux' + +import corpora from '../Corpora/reducer.a73b209e.cbc08383' +import corpusMaskDetail from '../CorpusMask/reducer.a2f91f7e.89940f0f' +import userMaskDetail from '../UserMask/reducer.301b46b4.2b496824' +import searchResults from '../Search/reducer.b87d5ff2.06816636' + +const rootReducer = combineReducers({ + corpora, + corpusMaskDetail, + userMaskDetail, + searchResults +}) + +export default rootReducer diff --git a/dist/public/public/server-build/components/App/routes.55e1d219.55e1d219.js b/dist/public/public/server-build/components/App/routes.55e1d219.55e1d219.js new file mode 100644 index 0000000..357f35b --- /dev/null +++ b/dist/public/public/server-build/components/App/routes.55e1d219.55e1d219.js @@ -0,0 +1,25 @@ +import React from 'react' +import { Router, Route } from 'react-router' + +import App from '../App' +import Corpora from '../Corpora' +import CorpusMaskContainer from '../CorpusMask' +import UserMaskContainer from '../UserMask' +import SearchContainer from '../Search' + +export default function (history) { + return ( + + + + + + + + + + + + + ) +} diff --git a/dist/public/public/server-build/components/App/store.cd39673d.55138e2a.js b/dist/public/public/server-build/components/App/store.cd39673d.55138e2a.js new file mode 100644 index 0000000..4df9830 --- /dev/null +++ b/dist/public/public/server-build/components/App/store.cd39673d.55138e2a.js @@ -0,0 +1,40 @@ +import { createStore, applyMiddleware } from 'redux' +import createLogger from 'redux-logger' +import thunkMiddleware from 'redux-thunk' + +import apiMiddleware from '../../middleware/api.cab420cd.cab420cd' +import rootReducer from './reducer.b8c8f420.d097d80f' + +const logger = createLogger({ + level: 'info', + collapsed: false, + logger: console, + predicate: (getState, action) => true +}) + +let middlewares = [ + thunkMiddleware, + apiMiddleware +] + +if (process.env.NODE_ENV !== 'production') { + middlewares = [...middlewares, logger] +} + +const createStoreWithMiddleware = applyMiddleware( + ...middlewares +)(createStore) + +export default function configureStore (initialState) { + const store = createStoreWithMiddleware(rootReducer, initialState) + + if (module.hot) { + // Enable Webpack hot module replacement for reducers + module.hot.accept('./reducer.b8c8f420.d097d80f', () => { + const nextRootReducer = require('./reducer.b8c8f420.d097d80f') + store.replaceReducer(nextRootReducer) + }) + } + + return store +} diff --git a/dist/public/public/server-build/components/Corpora/Corpora.ae1ec4c6.ae1ec4c6.jsx b/dist/public/public/server-build/components/Corpora/Corpora.ae1ec4c6.ae1ec4c6.jsx new file mode 100644 index 0000000..d0b6d4a --- /dev/null +++ b/dist/public/public/server-build/components/Corpora/Corpora.ae1ec4c6.ae1ec4c6.jsx @@ -0,0 +1,45 @@ +import React, { Component } from 'react' +import { Link } from 'react-router' +import { List } from 'immutable' + +class Corpora extends Component { + render () { + return ( +
+

Corpora

+ { + this.props.corpora.map((connection) => { + let website = connection.get('website') || '' + if (connection.get('searchKeywords')) { + website = `${website}/search/${connection.get('searchKeywords')}` + } else { + website = `${website}/search` + } + return ( +
+ + Corpus image + +
+

+ + {connection.get('title')} + +

+

{connection.get('description')}

+
+
+ ) + }) + } +
+ ) + } +} + +Corpora.propTypes = { + corpora: React.PropTypes.instanceOf(List).isRequired, + className: React.PropTypes.string.isRequired +} + +export default Corpora diff --git a/dist/public/public/server-build/components/Corpora/Corpora.test.c3f89306.ba440db8.js b/dist/public/public/server-build/components/Corpora/Corpora.test.c3f89306.ba440db8.js new file mode 100644 index 0000000..b271768 --- /dev/null +++ b/dist/public/public/server-build/components/Corpora/Corpora.test.c3f89306.ba440db8.js @@ -0,0 +1,45 @@ +import Immutable from 'immutable' +import { Link } from 'react-router' +import React from 'react' +import { shallow } from 'enzyme' + +import Corpora from './Corpora.ae1ec4c6.ae1ec4c6.jsx' + +describe('Component::Corpora', function () { + let props + beforeEach(function () { + props = { + corpora: Immutable.fromJS([ + { + dbname: 1, + title: 'the title 1', + website: 'https://example.org' + }, + { + dbname: 2, + title: 'the title 2', + searchKeywords: 'morphemes:nay OR gloss:caus' + }, + { + dbname: 3, + title: 'the title 3' + } + ]) + } + }) + function renderDoc () { + return shallow() + } + + it('renders corpora', function () { + let doc = renderDoc() + let corpusMaskComps = doc.find(Link) + + expect(corpusMaskComps.length).to.equal(props.corpora.size * 2) + expect(corpusMaskComps.nodes[0].props.to).to.equal('https://example.org/search') + expect(corpusMaskComps.nodes[1].props.children).to.equal('the title 1') + + expect(corpusMaskComps.nodes[2].props.to).to.equal('/search/morphemes:nay OR gloss:caus') + expect(corpusMaskComps.nodes[3].props.children).to.equal('the title 2') + }) +}) diff --git a/dist/public/public/server-build/components/Corpora/CorporaContainer.test.1fd183e2.9fbdb2b5.js b/dist/public/public/server-build/components/Corpora/CorporaContainer.test.1fd183e2.9fbdb2b5.js new file mode 100644 index 0000000..e83d2ad --- /dev/null +++ b/dist/public/public/server-build/components/Corpora/CorporaContainer.test.1fd183e2.9fbdb2b5.js @@ -0,0 +1,32 @@ +import Immutable from 'immutable' +import React from 'react' +import { shallow } from 'enzyme' + +import Corpora from './Corpora.ae1ec4c6.ae1ec4c6.jsx' +import { CorpusMaskContainer } from './index.f8ab0cb7.d66c11eb.jsx' + +describe('Container::Corpora', function () { + let props + beforeEach(function () { + props = { + loadCorpora: sinon.stub(), + corpora: Immutable.fromJS([ + { + dbname: 1, + title: 'corpusMask title 1' + }, + { + dbname: 2, + title: 'corpusMask title 1' + } + ]) + } + }) + + it('renders Corpora with corpora in props', function () { + let doc = shallow() + let corporaComp = doc.find(Corpora) + + expect(corporaComp.props().corpora).to.equal(props.corpora) + }) +}) diff --git a/dist/public/public/server-build/components/Corpora/actions.7c029780.8bd8e91a.js b/dist/public/public/server-build/components/Corpora/actions.7c029780.8bd8e91a.js new file mode 100644 index 0000000..6a1dd50 --- /dev/null +++ b/dist/public/public/server-build/components/Corpora/actions.7c029780.8bd8e91a.js @@ -0,0 +1,13 @@ +import { CALL_API } from '../../middleware/api.cab420cd.cab420cd' + +export const LOADED_CORPORA = Symbol('LOADED_CORPORA') +export const ADD_CORPUS_MASK = Symbol('ADD_CORPUS_MASK') +export function loadCorpora () { + return { + [CALL_API]: { + method: 'get', + path: '/api/corpora', + successType: LOADED_CORPORA + } + } +} diff --git a/dist/public/public/server-build/components/Corpora/actions.test.88bdeb11.eb2a37f5.js b/dist/public/public/server-build/components/Corpora/actions.test.88bdeb11.eb2a37f5.js new file mode 100644 index 0000000..14a27bd --- /dev/null +++ b/dist/public/public/server-build/components/Corpora/actions.test.88bdeb11.eb2a37f5.js @@ -0,0 +1,16 @@ +import { CALL_API } from 'middleware/api' + +import * as actionCreator from './actions.7c029780.8bd8e91a' + +describe('Action::CorpusMask', function () { + describe('#loadCorpora()', function () { + it('returns action `CALL_API` info', function () { + let action = actionCreator.loadCorpora() + expect(action[CALL_API]).to.deep.equal({ + method: 'get', + path: '/api/corpora', + successType: actionCreator.LOADED_CORPORA + }) + }) + }) +}) diff --git a/dist/public/public/server-build/components/Corpora/index.f8ab0cb7.d66c11eb.jsx b/dist/public/public/server-build/components/Corpora/index.f8ab0cb7.d66c11eb.jsx new file mode 100644 index 0000000..36752b8 --- /dev/null +++ b/dist/public/public/server-build/components/Corpora/index.f8ab0cb7.d66c11eb.jsx @@ -0,0 +1,50 @@ +import { connect } from 'react-redux' +import React, { Component } from 'react' +import Helmet from 'react-helmet' + +import Corpora from './Corpora.ae1ec4c6.ae1ec4c6.jsx' +import { loadCorpora } from './actions.7c029780.8bd8e91a' + +class CorpusMaskContainer extends Component { + static fetchData ({store}) { + return store.dispatch(loadCorpora()) + } + + componentDidMount () { + this.props.loadCorpora() + } + + render () { + const description = 'Every LingSync corpus can have a Public URL, a place where you can share your data or let visitors search the parts of your corpus which you would like to make public. By default, all corpora are private not searchable unless you are a member of the corpus.' + + return ( +
+ + LingSync.org Public URLs + + +

Public URLs

+

{description}

+ +
+ ) + } +} + +CorpusMaskContainer.propTypes = { + loadCorpora: React.PropTypes.func.isRequired, + corpora: React.PropTypes.object.isRequired +} + +function mapStateToProps (state) { + return { + corpora: state.corpora + } +} + +export { CorpusMaskContainer } +export default connect(mapStateToProps, { + loadCorpora +})(CorpusMaskContainer) diff --git a/dist/public/public/server-build/components/Corpora/reducer.a73b209e.cbc08383.js b/dist/public/public/server-build/components/Corpora/reducer.a73b209e.cbc08383.js new file mode 100644 index 0000000..0689f3b --- /dev/null +++ b/dist/public/public/server-build/components/Corpora/reducer.a73b209e.cbc08383.js @@ -0,0 +1,17 @@ +import * as ActionType from './actions.7c029780.8bd8e91a' + +import Immutable from 'immutable' + +let defaultState = Immutable.fromJS([]) +function corporaReducer (state = defaultState, action) { + switch (action.type) { + case ActionType.LOADED_CORPORA: + return Immutable.fromJS(action.response) + case ActionType.ADD_CORPUS_MASK: + return state.push(Immutable.Map(action.payload)) + default: + return state + } +} + +export default corporaReducer diff --git a/dist/public/public/server-build/components/Corpora/reducer.test.4da6fb00.f5b06445.js b/dist/public/public/server-build/components/Corpora/reducer.test.4da6fb00.f5b06445.js new file mode 100644 index 0000000..df38a5c --- /dev/null +++ b/dist/public/public/server-build/components/Corpora/reducer.test.4da6fb00.f5b06445.js @@ -0,0 +1,25 @@ +import * as ActionType from './actions.7c029780.8bd8e91a' +import corpusMaskReducer from './reducer.a73b209e.cbc08383' + +describe('Reducer::CorpusMask', function () { + it('returns an empty array as default state', function () { + let action = { + type: 'unknown' + } + let newState = corpusMaskReducer(undefined, action) + expect(newState.toJS()).to.deep.equal([]) + }) + + describe('on LOADED_CORPORA', function () { + it('returns the `response` in given action', function () { + let action = { + type: ActionType.LOADED_CORPORA, + response: { + responseKey: 'responseVal' + } + } + let newState = corpusMaskReducer(undefined, action) + expect(newState.toJS()).to.deep.equal(action.response) + }) + }) +}) diff --git a/dist/public/public/server-build/components/CorpusMask/CorpusMaskContainer.test.21040e25.715064e2.js b/dist/public/public/server-build/components/CorpusMask/CorpusMaskContainer.test.21040e25.715064e2.js new file mode 100644 index 0000000..9d35d4b --- /dev/null +++ b/dist/public/public/server-build/components/CorpusMask/CorpusMaskContainer.test.21040e25.715064e2.js @@ -0,0 +1,180 @@ +import { browserHistory } from 'react-router' +import Immutable from 'immutable' +import { mount, shallow } from 'enzyme' +import React from 'react' + +import { CorpusMaskContainer } from './index.ee30a68b.fa9ebec6.jsx' + +describe('Container::CorpusMask', function () { + function renderDoc (props) { + return shallow() + } + function prepareDoc (props) { + return mount() + } + it('fetches corpusMask details on mounted', function () { + const props = { + loadCorpusMaskDetail: sinon.stub(), + params: { + dbname: 'something', + teamname: 'someone' + }, + children: [], // To avoid rendering the Search component + corpusMask: Immutable.fromJS({ + dbname: 'abc', + title: 'the-corpusMask-title', + team: { + id: 1234, + name: 'jack' + }, + fields: [], + prototypeApp: { + url: '' + }, + connection: { + dbname: 'abc' + } + }), + searchResults: Immutable.fromJS([]) + } + const doc = prepareDoc(props) + expect(props.loadCorpusMaskDetail).to.have.been.calledWith({ + teamname: 'someone', + dbname: props.params.dbname, + history: browserHistory + }) + + expect(doc).to.have.keys(['component', 'root', 'node', 'nodes', 'length', 'options', 'complexSelector']) + + const images = doc.find('img') + expect(images).to.exist + expect(images.length).to.equal(2) + + const avatar = images.nodes[0] + expect(avatar.getAttribute('src')).to.equal('https://secure.gravatar.com/avatar/900150983cd24fb0d6963f7d28e17f72.jpg?s=96&d=retro&r=pg') + }) + + it('should support OLAC metadata in header', function () { + const props = { + loadCorpusMaskDetail: sinon.stub(), + params: { + dbname: 'something', + teamname: 'someone' + }, + corpusMask: Immutable.fromJS({ + dbname: 'abc', + title: 'the-corpusMask-title', + connection: { + dbname: 'abc' + }, + description: 'hi \n* a \n* list', + dateCreated: 1411424261782, + dateModified: 1490462360903, + keywords: 'kartuli, batumi, natural speech', + copyright: 'Georgian Together Users', + termsOfUse: 'any \nterms', + team: { + id: '123-efg', + name: 'jack' + }, + fields: [{ + id: 'subject', + value: 'Kartuli' + }], + prototypeApp: { + url: '' + } + }), + searchResults: Immutable.fromJS([]) + } + const doc = renderDoc(props) + + const meta = doc.find('meta') + expect(meta).to.exist + expect(meta.length).to.equal(20) + + const description = doc.find('meta[name="description"]') + expect(description).to.exist + expect(description.length).to.equal(1) + expect(description.node.props.content).to.equal('hi \n* a \n* list') + + const keywords = doc.find('meta[name="keywords"]') + expect(keywords).to.exist + expect(keywords.length).to.equal(1) + expect(keywords.node.props.content).to.equal('kartuli, batumi, natural speech') + + const date = doc.find('meta[name="date"]') + expect(date).to.exist + expect(date.length).to.equal(1) + expect(date.node.props.content).to.equal('2017-03-25T17:19:20.903Z') + + const dateCreated = doc.find('meta[name="dateCreated"]') + expect(dateCreated).to.exist + expect(dateCreated.length).to.equal(1) + expect(dateCreated.node.props.content).to.equal('2014-09-22T22:17:41.782Z') + + const subject = doc.find('meta[name="subject"]') + expect(subject).to.exist + expect(subject.length).to.equal(1) + expect(subject.node.props.content).to.equal('Kartuli') + + const copyright = doc.find('meta[name="copyright"]') + expect(copyright).to.exist + expect(copyright.length).to.equal(1) + expect(copyright.node.props.content).to.equal('Georgian Together Users') + + const terms = doc.find('meta[name="terms"]') + expect(terms).to.exist + expect(terms.length).to.equal(1) + expect(terms.node.props.content).to.equal('any \nterms') + + const access = doc.find('meta[name="access"]') + expect(access).to.exist + expect(access.length).to.equal(1) + expect(access.node.props.content).to.equal('Contact myemail@myemail.org if you would like to gain (read, write, comment, contributor, export, search) access to the non-public parts of this database') + }) + + it('should support markdown formatted text', function () { + const props = { + loadCorpusMaskDetail: sinon.stub(), + params: { + dbname: 'something', + teamname: 'someone' + }, + corpusMask: Immutable.fromJS({ + dbname: 'abc', + connection: { + dbname: 'abc' + }, + title: 'the-corpusMask-title', + description: 'hi \n* a \n* list', + termsOfUse: 'some \nterms', + team: { + id: 1234, + name: 'jack' + }, + fields: [], + prototypeApp: { + url: '' + } + }), + searchResults: Immutable.fromJS([]) + } + const doc = renderDoc(props) + + const description = doc.find('.description') + expect(description).to.exist + expect(description.length).to.equal(1) + expect(description.node.props.dangerouslySetInnerHTML.__html).to.equal('

hi

\n
    \n
  • a
  • \n
  • list
  • \n
\n') + + const termsOfUse = doc.find('.terms') + expect(termsOfUse).to.exist + expect(termsOfUse.length).to.equal(1) + expect(termsOfUse.node.props.dangerouslySetInnerHTML.__html).to.equal('

some
terms

\n') + + // terms should be an anchor which can be linked to + const termsAnchor = doc.find('#terms') + expect(termsAnchor).to.exist + expect(termsAnchor.length).to.equal(1) + }) +}) diff --git a/dist/public/public/server-build/components/CorpusMask/actions.79abbedc.f4bb2286.js b/dist/public/public/server-build/components/CorpusMask/actions.79abbedc.f4bb2286.js new file mode 100644 index 0000000..b1eac9b --- /dev/null +++ b/dist/public/public/server-build/components/CorpusMask/actions.79abbedc.f4bb2286.js @@ -0,0 +1,15 @@ +import { CALL_API } from '../../middleware/api.cab420cd.cab420cd' + +export const LOADED_CORPUS_MASK_DETAIL = Symbol('LOADED_CORPUS_MASK_DETAIL') +export function loadCorpusMaskDetail ({teamname, dbname, history}) { + return { + [CALL_API]: { + method: 'get', + path: `/api/${teamname || 'corpora'}/${dbname}`, + successType: LOADED_CORPUS_MASK_DETAIL, + afterError: () => { + history.push('/') + } + } + } +} diff --git a/dist/public/public/server-build/components/CorpusMask/actions.test.1c456b40.104ac6f6.js b/dist/public/public/server-build/components/CorpusMask/actions.test.1c456b40.104ac6f6.js new file mode 100644 index 0000000..c758c1c --- /dev/null +++ b/dist/public/public/server-build/components/CorpusMask/actions.test.1c456b40.104ac6f6.js @@ -0,0 +1,45 @@ +import { CALL_API } from 'middleware/api' + +import * as actionCreator from './actions.79abbedc.f4bb2286' + +describe('Action::CorpusMask', function () { + describe('#loadCorpusMaskDetail({id})', function () { + let dbname = 'the-dbname' + it('returns a CHAIN_API to fetch corpusMask first', function () { + let action = actionCreator.loadCorpusMaskDetail({ + dbname + }) + let callApi = action[CALL_API] + expect(callApi.method).to.equal('get') + expect(callApi.path).to.equal(`/api/corpora/${dbname}`) + expect(callApi.successType).to.equal(actionCreator.LOADED_CORPUS_MASK_DETAIL) + }) + it('navigates to root when request error', () => { + let mockHistory = { + push: sinon.stub() + } + let action = actionCreator.loadCorpusMaskDetail({ + dbname, + history: mockHistory + }) + let callApi = action[CALL_API] + expect(callApi.afterError).to.be.an.instanceOf(Function) + callApi.afterError() + + expect(mockHistory.push).to.have.been.calledWith('/') + }) + it('fetches team data after fetching corpusMask', function () { + let action = actionCreator.loadCorpusMaskDetail({ + teamname: 'someone', + dbname: 'somecorpus' + }) + + expect(action).to.deep.equal({}) + // expect(action).to.deep.equal({ + // method: 'get', + // path: '/api/someone/somecorpus', + // successType: actionCreator.LOADED_CORPUS_MASK_DETAIL + // }) + }) + }) +}) diff --git a/dist/public/public/server-build/components/CorpusMask/index.ee30a68b.fa9ebec6.jsx b/dist/public/public/server-build/components/CorpusMask/index.ee30a68b.fa9ebec6.jsx new file mode 100644 index 0000000..cbf3c84 --- /dev/null +++ b/dist/public/public/server-build/components/CorpusMask/index.ee30a68b.fa9ebec6.jsx @@ -0,0 +1,255 @@ +import { browserHistory } from 'react-router' +import { connect } from 'react-redux' +import Helmet from 'react-helmet' +import Immutable from 'immutable' +import React, { Component } from 'react' +import { CorpusMask } from 'fielddb/api/corpus/CorpusMask' +import { DatumFields } from 'fielddb/api/datum/DatumFields' +import marked from 'marked' + +import { loadCorpusMaskDetail } from './actions.79abbedc.f4bb2286' +import UserMask from '../UserMask/UserMask.4c617afe.63b610f3.jsx' +import DataList from '../DataList' +import Search from '../Search' + +let defaultCorpus +marked.setOptions({ + renderer: new marked.Renderer(), + gfm: true, + tables: true, + breaks: true, + pedantic: false, + sanitize: true, + smartLists: true, + smartypants: false +}) + +class CorpusMaskContainer extends Component { + static fetchData ({store, params, history}) { + let {teamname, dbname} = params + return store.dispatch(loadCorpusMaskDetail({ + teamname, + dbname, + history + })) + } + componentDidMount () { + let {teamname, dbname} = this.props.params + this.props.loadCorpusMaskDetail({ + teamname, + dbname, + history: browserHistory + }) + } + render () { + let { corpusMask } = this.props + const fielddbCorpusMask = new CorpusMask(corpusMask.toJS()) + + if (!corpusMask || !corpusMask.get('team')) { + return ( +
+ ) + } + + let title = `${corpusMask.getIn(['team', 'name'])} - ${corpusMask.get('title')}` + if (!defaultCorpus) { + defaultCorpus = new CorpusMask(CorpusMask.prototype.defaults) + } + let thisCorpusFields = new DatumFields(corpusMask.get('fields').toJS()) + let fields = defaultCorpus.fields.clone() + fields.merge('self', thisCorpusFields, true) + + let date = new Date(corpusMask.get('dateModified') || corpusMask.get('dateCreated')).toJSON() + const searchParams = { + dbname: corpusMask.get('dbname'), + searchIn: corpusMask.get('searchKeywords'), + teamname: corpusMask.getIn(['team', 'username']) + } + const identifier = corpusMask.get('dbname') + '/' + corpusMask.get('_id') + '?rev=' + (corpusMask.get('_rev') || 'repaired') + + const descriptionFormatted = marked(corpusMask.get('description') || '') + const termsOfUse = corpusMask.get('termsOfUse') || '' + const termsOfUseFormatted = marked(termsOfUse || '') + + return ( +
+ + {title} + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+

{corpusMask.get('title')}

+
+ + Corpus image + +
+
+
+
+
+
+
+
+
+