{
return this.props.location.pathname == route
- };
+ }
render() {
const {countryPackageName, countryPackageVersion, parameters, variables} = this.props
diff --git a/src/components/entity.jsx b/src/components/entity.jsx
index fc64a25b..c09bf5b7 100644
--- a/src/components/entity.jsx
+++ b/src/components/entity.jsx
@@ -14,7 +14,7 @@ class Entity extends React.Component {
intl: intlShape.isRequired,
entityId: PropTypes.string.isRequired,
entity: entityShape.isRequired,
- };
+ }
render() {
diff --git a/src/components/formula.jsx b/src/components/formula.jsx
index 5cf5ded0..3e191c8d 100644
--- a/src/components/formula.jsx
+++ b/src/components/formula.jsx
@@ -15,7 +15,7 @@ class Formula extends React.Component {
source: PropTypes.string.isRequired,
parameters: PropTypes.objectOf(parameterShape).isRequired,
variables: PropTypes.objectOf(variableShape).isRequired,
- };
+ }
render() {
const { startDate, stopDate, content, source } = this.props
@@ -43,17 +43,17 @@ class Formula extends React.Component {
link = (variable) => {
return { variable }
- };
+ }
linkParam = (parameter, linkText) => {
return { linkText }
- };
+ }
isVariable = (substring) => {
// Ignore every text that isn't a single word like a variable must be:
return (! substring.includes(' ') && this.props.variables[substring])
- };
+ }
splitAndLinkVariables = (text, separator) => {
// Only split strings, as trying to split JSX Links would raise an error
@@ -69,7 +69,7 @@ class Formula extends React.Component {
}
return substring
})
- };
+ }
splitAndLinkParams = (text) => {
const recordedParamNodes = []
@@ -132,7 +132,7 @@ class Formula extends React.Component {
return substrings
}
})
- };
+ }
// Change every OpenFisca parameter and variable in the formula by a link to the variable page:
renderLinkedFormula = (formula) => {
@@ -149,7 +149,7 @@ class Formula extends React.Component {
}),
flatten
)(formula)
- };
+ }
}
export default Formula
diff --git a/src/components/list.jsx b/src/components/list.jsx
index 6d69b8d3..f809f7f8 100644
--- a/src/components/list.jsx
+++ b/src/components/list.jsx
@@ -7,7 +7,7 @@ class List extends React.Component {
keyProperty: PropTypes.string,
type: PropTypes.string,
items: PropTypes.array.isRequired,
- };
+ }
render() {
const {children, keyProperty, items, type} = this.props
diff --git a/src/components/pages/country-model-item.jsx b/src/components/pages/country-model-item.jsx
index 3be4568a..c055a478 100644
--- a/src/components/pages/country-model-item.jsx
+++ b/src/components/pages/country-model-item.jsx
@@ -16,7 +16,7 @@ class CountryModelItemPage extends React.Component {
router: routerShape.isRequired,
searchQuery: PropTypes.string.isRequired,
searchResults: PropTypes.array.isRequired,
- };
+ }
static propTypes = {
countryPackageName: PropTypes.string.isRequired,
@@ -26,9 +26,9 @@ class CountryModelItemPage extends React.Component {
entities: PropTypes.objectOf(entityShape).isRequired,
parameters: PropTypes.objectOf(parameterShape).isRequired,
variables: PropTypes.objectOf(variableShape).isRequired,
- };
+ }
- state = {variable: null, parameter: null, entityId: null, entity: null, waitingForResponse: true};
+ state = {variable: null, parameter: null, entityId: null, entity: null, waitingForResponse: true}
fetchPageContent = (name) => {
if (this.props.entities[name]) {
@@ -54,7 +54,7 @@ class CountryModelItemPage extends React.Component {
this.setState({waitingForResponse: false})
this.handleNotFound()
}
- };
+ }
componentDidUpdate(prevProps) {
if (this.props.params.name != prevProps.params.name) {
@@ -73,7 +73,7 @@ class CountryModelItemPage extends React.Component {
query: {q: name, is404: true},
hash: '#not-found',
})
- };
+ }
render() {
const { searchQuery, searchResults } = this.context
diff --git a/src/components/pages/home.jsx b/src/components/pages/home.jsx
index b9f3b679..49927fa3 100644
--- a/src/components/pages/home.jsx
+++ b/src/components/pages/home.jsx
@@ -17,7 +17,7 @@ class HomePage extends React.Component {
searchQuery: PropTypes.string.isRequired,
searchResults: PropTypes.array.isRequired,
setSearchQuery: PropTypes.func.isRequired,
- };
+ }
static propTypes = {
countryPackageName: PropTypes.string.isRequired,
@@ -27,9 +27,9 @@ class HomePage extends React.Component {
entities: PropTypes.objectOf(entityShape).isRequired,
parameters: PropTypes.objectOf(parameterShape).isRequired,
variables: PropTypes.objectOf(variableShape).isRequired,
- };
+ }
- state = {inputValue: ''};
+ state = {inputValue: ''}
componentDidMount() {
this._isMounted = true
@@ -96,7 +96,7 @@ class SearchResults extends React.Component {
static propTypes = {
items: PropTypes.array.isRequired,
searchQuery: PropTypes.string,
- };
+ }
shouldComponentUpdate(nextProps) {
// Optimization: re-render this component only if `searchQuery` changed.
diff --git a/src/components/pages/searchbar.jsx b/src/components/pages/searchbar.jsx
index ab437eec..3915a1c1 100644
--- a/src/components/pages/searchbar.jsx
+++ b/src/components/pages/searchbar.jsx
@@ -13,12 +13,12 @@ class SearchBar extends React.Component {
intl: intlShape.isRequired
}
- state = {inputValue: this.props.initialValue};
+ state = {inputValue: this.props.initialValue}
handleInputChange = (event) => {
this.setState({inputValue: event.target.value})
this.searchInput.scrollIntoView()
- };
+ }
handleSubmit = (event) => {
event.preventDefault()
diff --git a/src/components/parameter.jsx b/src/components/parameter.jsx
index 97e53075..b2669fe8 100644
--- a/src/components/parameter.jsx
+++ b/src/components/parameter.jsx
@@ -14,7 +14,7 @@ class Parameter extends React.Component {
countryPackageVersion: PropTypes.string.isRequired,
intl: intlShape.isRequired,
parameter: parameterShape.isRequired,
- };
+ }
render() {
const {parameter} = this.props
@@ -73,7 +73,7 @@ class Parameter extends React.Component {
)
- };
+ }
renderStartStopValueTable = (parameter, values) => {
return (
@@ -89,7 +89,7 @@ class Parameter extends React.Component {
)
- };
+ }
}
diff --git a/src/components/scale.jsx b/src/components/scale.jsx
index 9953c6f3..ff8a6dcf 100644
--- a/src/components/scale.jsx
+++ b/src/components/scale.jsx
@@ -7,7 +7,7 @@ import { parameterShape } from '../openfisca-proptypes'
class Scale extends React.Component {
static propTypes = {
parameter: parameterShape.isRequired,
- };
+ }
render() {
const {brackets} = this.props.parameter
diff --git a/src/components/variable.jsx b/src/components/variable.jsx
index 20519fd4..7637ed16 100644
--- a/src/components/variable.jsx
+++ b/src/components/variable.jsx
@@ -18,7 +18,7 @@ class Variable extends React.Component {
parameters: PropTypes.objectOf(parameterShape).isRequired,
variable: variableShape.isRequired,
variables: PropTypes.objectOf(variableShape).isRequired,
- };
+ }
render() {
const { variable } = this.props
@@ -214,7 +214,7 @@ class Variable extends React.Component {
}
)
- };
+ }
renderFormulas = (formulas) => {
const startDates = keys(formulas).sort().reverse()
@@ -243,7 +243,7 @@ class Variable extends React.Component {
}
)
- };
+ }
}
export default injectIntl(Variable)
diff --git a/src/server/render.jsx b/src/server/render.jsx
index 5c6b51b9..fb49810a 100644
--- a/src/server/render.jsx
+++ b/src/server/render.jsx
@@ -42,11 +42,14 @@ function loadWebpackAssets() {
return webpackAssets
}
-
-function trimTrailingSlashes(value) {
+const trimTrailingSlashes = (value) => {
return value.replace(/\/+$/, '')
}
+const trimLeadingSlashes = (value) => {
+ return value.replace(/^\/+/, '')
+}
+
function renderHtmlDocument(renderProps, state) {
const appHtml = renderToString(
@@ -76,7 +79,9 @@ function renderHtmlDocument(renderProps, state) {
if (process.env.NODE_ENV === 'development') {
const webpackDevConfig = require('../../webpack.config.dev')
- externalCss = externalCss.map(file => `${webpackDevConfig.output.publicPath}${file}`)
+ externalCss = externalCss
+ .map(file => trimLeadingSlashes(file))
+ .map(file => `${webpackDevConfig.output.publicPath}${file}`)
}
const css = webpackAssets.main.css.concat(externalCss)
const html = renderToStaticMarkup(
diff --git a/src/server/webpack-dev-server.js b/src/server/webpack-dev-server.js
index 57102900..e438c4e6 100644
--- a/src/server/webpack-dev-server.js
+++ b/src/server/webpack-dev-server.js
@@ -1,26 +1,26 @@
import url from 'url'
-
import webpack from 'webpack'
import WebpackDevServer from 'webpack-dev-server'
import webpackDevConfig from '../../webpack.config.dev'
-
const publicURL = url.parse(webpackDevConfig.output.publicPath)
-new WebpackDevServer(webpack(webpackDevConfig), {
+const options = {
+ host: publicURL.hostname,
+ port: publicURL.port,
+ headers: {'Access-Control-Allow-Origin': '*'},
historyApiFallback: true,
- hot: true,
- noInfo: true,
- publicPath: webpackDevConfig.output.publicPath,
- stats: { colors: true },
- quiet: true,
- headers: {
- 'Access-Control-Allow-Origin': '*',
- },
-}).listen(publicURL.port, publicURL.hostname, function (err) {
- if (err)
- return console.error(err)
+ static: {publicPath: webpackDevConfig.output.publicPath}
+}
+
+const compiler = webpack({
+ ...webpackDevConfig,
+ stats: 'errors-only',
+})
- console.log(`Webpack development server listening on ${webpackDevConfig.output.publicPath}`)
+new WebpackDevServer(options, compiler).start().then(() => {
+ console.log(`Webpack development server listening on ${publicURL}`)
+}).catch((err) => {
+ console.error(err)
})
diff --git a/src/server/write-assets.js b/src/server/write-assets.js
index 876840a1..48a2dc42 100644
--- a/src/server/write-assets.js
+++ b/src/server/write-assets.js
@@ -5,35 +5,46 @@
// Inspired from http://webpack.github.io/docs/long-term-caching.html#get-filenames-from-stats
-
import fs from 'fs'
import path from 'path'
+const getChunks = (publicPath, stats, name, ext) => {
+ const json = stats.toJson()
+ let chunk = json.assetsByChunkName[name]
+
+ // a chunk could be a string or an array, so make sure it is an array
+ if (!(Array.isArray(chunk))) chunk = [chunk]
+
+ return chunk
+ .filter(chunk2 => path.extname(chunk2) === `.${ext}`) // filter by extension
+ .map(chunk2 => `${publicPath}${chunk2}`) // add public path to it
+}
// Write only a relevant subset of the stats and attach the public path to it
-export default function writeAssets(filepath) {
- return function writeAssetsFunc(stats) {
- const publicPath = this.options.output.publicPath
- const json = stats.toJson()
-
- function getChunks(name, ext) {
- let chunk = json.assetsByChunkName[name]
- // a chunk could be a string or an array, so make sure it is an array
- if (!(Array.isArray(chunk))) {
- chunk = [chunk]
- }
- return chunk
- .filter(chunk2 => path.extname(chunk2) === `.${ext}`) // filter by extension
- .map(chunk2 => `${publicPath}${chunk2}`) // add public path to it
- }
+const writeAssets = (compiler, assets) => {
+ return (stats) => {
+ const publicPath = compiler.options.output.publicPath
+ const name = 'main'
- const chunkName = 'main'
const content = {
main: {
- css: getChunks(chunkName, 'css'),
- js: getChunks(chunkName, 'js'),
+ css: getChunks(publicPath, stats, name, 'css'),
+ js: getChunks(publicPath, stats, name, 'js'),
},
}
- fs.writeFileSync(filepath, JSON.stringify(content))
+
+ return fs.writeFileSync(assets, JSON.stringify(content))
}
}
+
+class WriteAssetsPlugin {
+ constructor(assets) {
+ this.assets = assets
+ }
+
+ apply(compiler) {
+ compiler.hooks.done.tap('WriteAssetsPlugin', writeAssets(compiler, this.assets))
+ }
+}
+
+export default WriteAssetsPlugin
diff --git a/tests/integration/SearchBarFixture.js b/tests/integration/SearchBarFixture.js
index c5beac92..a3f64426 100644
--- a/tests/integration/SearchBarFixture.js
+++ b/tests/integration/SearchBarFixture.js
@@ -1,14 +1,14 @@
-variableQuery = "disposable"
-variableResultName = "disposable_income"
+variableQuery = 'disposable'
+variableResultName = 'disposable_income'
variableResultDescription = /Actual amount available/
variableDefinitionPeriod = /month/
-scaleQuery = "taxes.social_security_contribution"
-scaleResultDescription = "Social security contribution tax scale"
-parameterQuery = "income tax rate"
-parameterResultTitle = "taxes.income_tax_rate"
+scaleQuery = 'taxes.social_security_contribution'
+scaleResultDescription = 'Social security contribution tax scale'
+parameterQuery = 'income tax rate'
+parameterResultTitle = 'taxes.income_tax_rate'
parameterResultDescription = /Income tax rate/
-stoppedParameterQuery = "benefits.housing_allowance"
+stoppedParameterQuery = 'benefits.housing_allowance'
stoppedParameterDescription = /Housing allowance amount/
-twoWordsVariableQuery = "basic income"
-twoWordsVariableResultName = "basic_income"
-enumVariableQuery = "housing status"
+twoWordsVariableQuery = 'basic income'
+twoWordsVariableResultName = 'basic_income'
+enumVariableQuery = 'housing status'
diff --git a/tests/integration/VariableFixture.js b/tests/integration/VariableFixture.js
index f6cb13e8..0ffe921e 100644
--- a/tests/integration/VariableFixture.js
+++ b/tests/integration/VariableFixture.js
@@ -1,4 +1,4 @@
-variableDependencyTitle = "salary"
-enumVariableTitle = "housing_occupancy_status"
+variableDependencyTitle = 'salary'
+enumVariableTitle = 'housing_occupancy_status'
enumVariableDefault = /tenant/
-enumVariableDefinition = "Owner"
+enumVariableDefinition = 'Owner'
diff --git a/tests/integration/config-ci.js b/tests/integration/config-ci.js
index 6045e237..84675e68 100644
--- a/tests/integration/config-ci.js
+++ b/tests/integration/config-ci.js
@@ -10,7 +10,7 @@ var capabilities = [
platformName: 'Android',
platformVersion: '7.0'
}
- ][process.env.CIRCLE_NODE_INDEX];
+][process.env.CIRCLE_NODE_INDEX]
module.exports = {
seleniumServerURL: {
diff --git a/tests/integration/config.js b/tests/integration/config.js
index b4714555..596089d2 100644
--- a/tests/integration/config.js
+++ b/tests/integration/config.js
@@ -1,5 +1,5 @@
module.exports = {
- name: 'OpenFisca Legislation explorer',
- baseURL: 'http://localhost:2020',
- browser: 'chrome',
+ name: 'OpenFisca Legislation explorer',
+ baseURL: 'http://localhost:2020',
+ browser: 'chrome',
}
diff --git a/tests/unit/addNormalizedDescription.test.js b/tests/unit/addNormalizedDescription.test.js
new file mode 100644
index 00000000..0ba4a77a
--- /dev/null
+++ b/tests/unit/addNormalizedDescription.test.js
@@ -0,0 +1,19 @@
+import {expect} from 'chai'
+
+import {addNormalizedDescription} from '../../src/search'
+
+
+describe('addNormalizedDescription', () => {
+ it('adds normalised description', () => {
+ const result = addNormalizedDescription({
+ name: {description: 'This is a description'},
+ })
+
+ expect(result).to.deep.equal({
+ name: {
+ description: 'This is a description',
+ normalizedDescription: 'this is a description',
+ },
+ })
+ })
+})
diff --git a/tests/unit/findCountryModelItems.test.js b/tests/unit/findCountryModelItems.test.js
new file mode 100644
index 00000000..63d1e3a5
--- /dev/null
+++ b/tests/unit/findCountryModelItems.test.js
@@ -0,0 +1,46 @@
+import {expect} from 'chai'
+
+import {addNormalizedDescription, findCountryModelItems} from '../../src/search'
+
+
+const parameters = addNormalizedDescription({
+ 'aah': {description: 'Allocation adulte handicapé'},
+ 'cotisation': {description: 'cotisation payée sur le salaire (de base)'},
+ 'salaire_de_base': {description: 'Salaire brut'},
+ 'crds_salaire': {description: 'CRDS payée sur les salaires'},
+ 'super_brut': {description: 'Salaire super brut'},
+ 'rsa_base_ressource': {description: 'Base ressource du RSA (salaires, chômage...)'},
+})
+
+describe('findCountryModelItems', () => {
+ describe('Searching a single word', () => {
+ const items = findCountryModelItems({}, parameters, {}, 'salaire')
+ const itemNames = items.map(item => item.name)
+
+ it('filters variables that don’t contain query', () => {
+ expect(itemNames).not.to.include.members(['aah'])
+ })
+
+ it('prioritises items key word in their name', () => {
+ expect(itemNames).to.include.ordered.members(['salaire_de_base', 'crds_salaire'])
+ })
+
+ it('prioritises items with the key word earlier in the description', () => {
+ expect(itemNames.slice(2)).to.include.ordered.members(['super_brut', 'rsa_base_ressource'])
+ })
+ })
+
+ describe('Searching multiple words', () => {
+ const items = findCountryModelItems({}, parameters, {}, 'salaire base')
+ const itemNames = items.map(item => item.name)
+
+ it('prioritises items with all the words in their name', () => {
+ expect(itemNames).to.include.ordered.members(['salaire_de_base', 'rsa_base_ressource'])
+ })
+
+ it('prioritise items with all the words in their description', () => {
+ expect(itemNames.slice(1)).to.include.ordered.members(['rsa_base_ressource', 'cotisation'])
+ })
+ })
+})
+
diff --git a/tests/unit/formulas.js b/tests/unit/formulas.js
deleted file mode 100644
index d819182d..00000000
--- a/tests/unit/formulas.js
+++ /dev/null
@@ -1,136 +0,0 @@
-import React from 'react';
-import Formula from '../../src/components/formula'
-import Adapter from 'enzyme-adapter-react-16';
-import { configure, shallow } from 'enzyme';
-import {flatten} from 'ramda'
-import {Link} from 'react-router'
-import should from 'should'
-import fs from 'fs'
-import path from 'path'
-configure({ adapter: new Adapter() });
-
-const variables = {
- rsa: {},
- rsa_base_ressource: {},
- rsa_fictif: {},
- rsa_eligible: {},
- scolarite: {},
- bourse_college_echelon: {},
-}
-
-const parameters = {
- 'prestations.prestations_familiales.af.bmaf': {},
- 'bourses_education.bourse_college.montant_taux_3': {},
- 'bourses_education.bourse_college.montant_taux_2': {},
- 'bourses_education.bourse_college.montant_taux_1': {}
-}
-
-const variable = {id: 'rsa'}
-
-const component = shallow().instance()
-
-function splitAndLinkParams(formula) {
- return shallow({component.splitAndLinkParams(formula)}
)
-}
-
-
-describe('Add links to parameters', function(){
- it ('should return one link when there is one leaf', function(){
- const formula_content = fs.readFileSync(path.join(__dirname, "assets", "formula2.txt")).toString()
- const output = splitAndLinkParams(formula_content)
- const links = output.find(Link)
- links.should.have.length(1)
- links.get(0).props.to.should.equal('prestations.prestations_familiales.af.bmaf')
- })
- it ('should return a link for each parameter present', function(){
- const formula_content = fs.readFileSync(path.join(__dirname, "assets", "formula1.txt")).toString()
- const output = splitAndLinkParams(formula_content)
- const links = output.find(Link)
- links.should.have.length(4)
- links.get(0).props.to.should.equal('prestations.prestations_familiales.af.bmaf')
- links.get(1).props.to.should.equal('bourses_education.bourse_college.montant_taux_3')
- links.get(2).props.to.should.equal('bourses_education.bourse_college.montant_taux_2')
- links.get(3).props.to.should.equal('bourses_education.bourse_college.montant_taux_1')
- })
- it ('should return a link when embeded in several nodes', function(){
- const formula_content = fs.readFileSync(path.join(__dirname, "assets", "formula4.txt")).toString()
- const output = splitAndLinkParams(formula_content)
- const links = output.find(Link)
- links.should.have.length(1)
- links.get(0).props.to.should.equal('prestations.prestations_familiales.af.bmaf')
- })
-})
-
-function renderLinkedFormula(formula) {
- return shallow({component.renderLinkedFormula(formula)}
)
-}
-
-describe('Add links to the whole formula', function(){
-
- it ('should return 2 links when there is very one parameter and one variable', function(){
- const formula_content = fs.readFileSync(path.join(__dirname, "assets", "formula3.txt")).toString()
- const output = renderLinkedFormula(formula_content)
- const links = output.find(Link)
- links.should.have.length(2)
- links.get(0).props.to.should.equal('rsa_eligible')
- links.get(1).props.to.should.equal('prestations.prestations_familiales.af.bmaf')
-
- })
- it ('should return 2 links when there is one parameter and one variable and a node', function(){
- const formula_content = fs.readFileSync(path.join(__dirname, "assets", "formula2.txt")).toString()
- const output = renderLinkedFormula(formula_content)
- const links = output.find(Link)
- links.should.have.length(2)
- links.get(0).props.to.should.equal('rsa_eligible')
- links.get(1).props.to.should.equal('prestations.prestations_familiales.af.bmaf')
- })
- it ('should return 6 links', function(){
- const formula_content = fs.readFileSync(path.join(__dirname, "assets", "formula1.txt")).toString()
- const output = renderLinkedFormula(formula_content)
- const links = output.find(Link)
- links.should.have.length(6)
- links.get(0).props.to.should.equal('prestations.prestations_familiales.af.bmaf')
- links.get(1).props.to.should.equal('scolarite')
- links.get(2).props.to.should.equal('bourse_college_echelon')
- links.get(3).props.to.should.equal('bourses_education.bourse_college.montant_taux_3')
- links.get(4).props.to.should.equal('bourses_education.bourse_college.montant_taux_2')
- links.get(5).props.to.should.equal('bourses_education.bourse_college.montant_taux_1')
- })
-
-})
-
-describe('Link rendering in variables', function() {
- it('should add links to a variables with double quotes', function() {
- const formula_content = 'simulation.calculate("rsa_base_ressource")'
- const output = renderLinkedFormula(formula_content)
- const links = output.find(Link)
- links.should.have.length(1)
- links.get(0).props.to.should.equal('rsa_base_ressource')
- });
- it('should add links to a variables with simple quotes', function() {
- const formula_content = 'simulation.calculate(\'rsa_base_ressource\')'
- const output = renderLinkedFormula(formula_content)
- const links = output.find(Link)
- links.should.have.length(1)
- links.get(0).props.to.should.equal('rsa_base_ressource')
- });
-
- it('should handle simple quotes and double quotes mixed', function() {
- const formula_content = 'simulation.calculate(\'rsa_base_ressource\') simulation.calculate("rsa_fictif") simulation.calculate(\'rsa_eligible\')'
- const output = renderLinkedFormula(formula_content)
- const links = output.find(Link)
- links.should.have.length(3)
- links.get(0).props.to.should.equal('rsa_base_ressource');
- links.get(1).props.to.should.equal('rsa_fictif');
- links.get(2).props.to.should.equal('rsa_eligible');
- });
-
- it('should be robust to quotes in comments', function() {
- const formula_content = ' # This a a comment with a quote \' simulation.calculate(\'rsa_base_ressource\') simulation.calculate(\'rsa_eligible\')'
- const output = renderLinkedFormula(formula_content)
- const links = output.find(Link)
- links.should.have.length(2)
- links.get(0).props.to.should.equal('rsa_base_ressource');
- links.get(1).props.to.should.equal('rsa_eligible');
- });
-});
diff --git a/tests/unit/formulas.test.js b/tests/unit/formulas.test.js
new file mode 100644
index 00000000..d441b4be
--- /dev/null
+++ b/tests/unit/formulas.test.js
@@ -0,0 +1,139 @@
+import Adapter from 'enzyme-adapter-react-16'
+import React from 'react'
+import fs from 'fs'
+import path from 'path'
+import {Link} from 'react-router'
+import {configure, shallow} from 'enzyme'
+import {expect} from 'chai'
+
+import Formula from '../../src/components/formula'
+
+configure({adapter: new Adapter()})
+
+const variables = {
+ rsa: {},
+ rsa_base_ressource: {},
+ rsa_fictif: {},
+ rsa_eligible: {},
+ scolarite: {},
+ bourse_college_echelon: {},
+}
+
+const parameters = {
+ 'prestations.prestations_familiales.af.bmaf': {},
+ 'bourses_education.bourse_college.montant_taux_3': {},
+ 'bourses_education.bourse_college.montant_taux_2': {},
+ 'bourses_education.bourse_college.montant_taux_1': {}
+}
+
+const variable = {id: 'rsa'}
+
+const component = shallow().instance()
+
+function splitAndLinkParams(formula) {
+ return shallow({component.splitAndLinkParams(formula)}
)
+}
+
+
+describe('Add links to parameters', () => {
+ it('return one link when there is one leaf', () => {
+ const formula_content = fs.readFileSync(path.join(__dirname, 'assets', 'formula2.txt')).toString()
+ const output = splitAndLinkParams(formula_content)
+ const links = output.find(Link)
+ expect(links).to.have.length(1)
+ expect(links.get(0).props).to.include({children: 'prestations.prestations_familiales.af.bmaf'})
+ })
+
+ it('return a link for each parameter present', () => {
+ const formula_content = fs.readFileSync(path.join(__dirname, 'assets', 'formula1.txt')).toString()
+ const output = splitAndLinkParams(formula_content)
+ const links = output.find(Link)
+ expect(links).to.have.length(4)
+ expect(links.get(0).props).to.include({to: 'prestations.prestations_familiales.af.bmaf'})
+ expect(links.get(1).props).to.include({to: 'bourses_education.bourse_college.montant_taux_3'})
+ expect(links.get(2).props).to.include({to: 'bourses_education.bourse_college.montant_taux_2'})
+ expect(links.get(3).props).to.include({to: 'bourses_education.bourse_college.montant_taux_1'})
+ })
+
+ it('return a link when embeded in several nodes', () => {
+ const formula_content = fs.readFileSync(path.join(__dirname, 'assets', 'formula4.txt')).toString()
+ const output = splitAndLinkParams(formula_content)
+ const links = output.find(Link)
+ expect(links).to.have.length(1)
+ expect(links.get(0).props).to.include({to: 'prestations.prestations_familiales.af.bmaf'})
+ })
+})
+
+function renderLinkedFormula(formula) {
+ return shallow({component.renderLinkedFormula(formula)}
)
+}
+
+describe('Add links to the whole formula', () => {
+ it('return 2 links when there is very one parameter and one variable', () => {
+ const formula_content = fs.readFileSync(path.join(__dirname, 'assets', 'formula2.txt')).toString()
+ const output = renderLinkedFormula(formula_content)
+ const links = output.find(Link)
+ expect(links).to.have.length(2)
+ expect(links.get(0).props).to.include({to: 'rsa_eligible'})
+ expect(links.get(1).props).to.include({to: 'prestations.prestations_familiales.af.bmaf'})
+ })
+
+ it('return 2 links when there is one parameter and one variable and a node', () => {
+ const formula_content = fs.readFileSync(path.join(__dirname, 'assets', 'formula2.txt')).toString()
+ const output = renderLinkedFormula(formula_content)
+ const links = output.find(Link)
+ expect(links).to.have.length(2)
+ expect(links.get(0).props).to.include({to: 'rsa_eligible'})
+ expect(links.get(1).props).to.include({to: 'prestations.prestations_familiales.af.bmaf'})
+ })
+
+ it('return 6 links', () => {
+ const formula_content = fs.readFileSync(path.join(__dirname, 'assets', 'formula1.txt')).toString()
+ const output = renderLinkedFormula(formula_content)
+ const links = output.find(Link)
+ expect(links).to.have.length(6)
+ expect(links.get(0).props).to.include({to: 'prestations.prestations_familiales.af.bmaf'})
+ expect(links.get(1).props).to.include({to: 'scolarite'})
+ expect(links.get(2).props).to.include({to: 'bourse_college_echelon'})
+ expect(links.get(3).props).to.include({to: 'bourses_education.bourse_college.montant_taux_3'})
+ expect(links.get(4).props).to.include({to: 'bourses_education.bourse_college.montant_taux_2'})
+ expect(links.get(5).props).to.include({to: 'bourses_education.bourse_college.montant_taux_1'})
+ })
+
+ describe('Link rendering in variables', () => {
+ it('add links to a variables with double quotes', () => {
+ const formula_content = 'simulation.calculate("rsa_base_ressource")'
+ const output = renderLinkedFormula(formula_content)
+ const links = output.find(Link)
+ expect(links).to.have.length(1)
+ expect(links.get(0).props).to.include({to: 'rsa_base_ressource'})
+ })
+
+ it('add links to a variables with simple quotes', () => {
+ const formula_content = 'simulation.calculate(\'rsa_base_ressource\')'
+ const output = renderLinkedFormula(formula_content)
+ const links = output.find(Link)
+ expect(links).to.have.length(1)
+ expect(links.get(0).props).to.include({to: 'rsa_base_ressource'})
+ })
+
+ it('handle simple quotes and double quotes mixed', () => {
+ const formula_content = 'simulation.calculate(\'rsa_base_ressource\') simulation.calculate("rsa_fictif") simulation.calculate(\'rsa_eligible\')'
+ const output = renderLinkedFormula(formula_content)
+ const links = output.find(Link)
+ expect(links).to.have.length(3)
+ expect(links.get(0).props).to.include({to: 'rsa_base_ressource'})
+ expect(links.get(1).props).to.include({to: 'rsa_fictif'})
+ expect(links.get(2).props).to.include({to: 'rsa_eligible'})
+ })
+
+ it('should be robust to quotes in comments', () => {
+ const formula_content = ' # This a a comment with a quote \' simulation.calculate(\'rsa_base_ressource\') simulation.calculate(\'rsa_eligible\')'
+ const output = renderLinkedFormula(formula_content)
+ const links = output.find(Link)
+ expect(links).to.have.length(2)
+ expect(links.get(0).props).to.include({to: 'rsa_base_ressource'})
+ expect(links.get(1).props).to.include({to: 'rsa_eligible'})
+ })
+ })
+})
diff --git a/tests/unit/search.js b/tests/unit/search.js
deleted file mode 100644
index f80b1d1f..00000000
--- a/tests/unit/search.js
+++ /dev/null
@@ -1,46 +0,0 @@
-import {addNormalizedDescription, findCountryModelItems} from '../../src/search'
-import {equals} from "ramda"
-import assert from "assert"
-import should from "should"
-
-const parameters = addNormalizedDescription({
- 'aah': {description: 'Allocation adulte handicapé'},
- 'cotisation': {description: 'cotisation payée sur le salaire (de base)'},
- 'salaire_de_base': {description: 'Salaire brut'},
- 'crds_salaire': {description: 'CRDS payée sur les salaires'},
- 'super_brut': {description: 'Salaire super brut'},
- 'rsa_base_ressource': {description: 'Base ressource du RSA (salaires, chômage...)'},
-});
-
-describe('Searching a single word', function() {
- const results = findCountryModelItems({}, parameters, {}, 'salaire')
- .map(item => item.name);
- it('should filter variables that don’t contain the query', function() {
- results.should.not.containEql('aah');
- });
-
- it('should first prioritize items with the key word in their name', function() {
- results.should.containDeepOrdered(['crds_salaire', 'super_brut']);
- });
-
- it('should then prioritize items with the key word earlier in the name', function() {
- results.should.containDeepOrdered(['salaire_de_base', 'crds_salaire']);
- });
-
- it('should finally prioritize items with the key word earlier in the description', function() {
- results.should.containDeepOrdered(['super_brut', 'cotisation']);
- });
-});
-
-describe('Searching several words', function() {
- const results = findCountryModelItems({}, parameters, {}, 'salaire base')
- .map(item => item.name);
- it('should only return variables that contain all query words', function() {
- results.should.containDeep(['salaire_de_base', 'rsa_base_ressource', 'cotisation']);
- results.should.have.size(3)
- });
-
- it('should prioritize matches in name over matches in description', function() {
- results.should.containDeepOrdered(['salaire_de_base', 'rsa_base_ressource', 'cotisation']);
- });
-});
diff --git a/webpack.config.dev.js b/webpack.config.dev.js
index e9f1928a..a78ead67 100644
--- a/webpack.config.dev.js
+++ b/webpack.config.dev.js
@@ -4,41 +4,31 @@
import path from 'path'
import CopyWebpackPlugin from 'copy-webpack-plugin'
-import ErrorNotificationPlugin from 'webpack-error-notification'
import webpack from 'webpack'
+import WriteAssetsPlugin from './src/server/write-assets'
import config from './src/config'
-import writeAssets from './src/server/write-assets'
-
const assetsPath = path.resolve(__dirname, 'public')
+const webpackAssetsPath = path.resolve(__dirname, 'webpack-assets.json')
const port = config.port + 1
-
module.exports = {
mode: 'development',
// devtool: "eval", // Transformed code
devtool: 'source-map', // Original code
entry: {
'main': [
- 'babel-polyfill',
`webpack-dev-server/client?http://${config.host}:${port}`,
- 'webpack/hot/only-dev-server',
'./src/client.jsx',
],
},
output: {
path: assetsPath,
- filename: '[name]-bundle-[hash].js',
+ filename: '[name]-bundle-[contenthash].js',
publicPath: `http://${config.host}:${port}/`,
},
- target: 'web',
-
- // yaml-js has a reference to `fs`, this is a workaround
- node: {
- fs: 'empty'
- },
module: {
rules: [
{
@@ -47,27 +37,20 @@ module.exports = {
use: [{
loader: 'babel-loader',
options: {
- plugins: [
- ['react-transform', {
- 'transforms': [{
- 'transform': 'react-transform-hmr',
- 'imports': ['react'],
- 'locals': ['module'],
- }]
- }]
- ]
- }
- }]
- }
- ]
+ plugins: [],
+ },
+ }],
+ },
+ ],
},
resolve: {
extensions: ['.js', '.jsx'],
+ fallback: {
+ fs: false,
+ stream: false,
+ },
},
plugins: [
- // hot reload
- new webpack.HotModuleReplacementPlugin(),
-
// print a webpack progress
new webpack.ProgressPlugin((percentage) => {
if (percentage === 1) {
@@ -75,18 +58,11 @@ module.exports = {
}
}),
- new ErrorNotificationPlugin(process.platform === 'linux' && function(msg) {
- if (!this.lastBuildSucceeded) {
- require('child_process').exec('notify-send --hint=int:transient:1 Webpack ' + msg)
- }
- }),
-
new webpack.DefinePlugin({
'process.env': {
API_URL: JSON.stringify(config.apiBaseUrl),
- CHANGELOG_URL: JSON.stringify(config.changelogUrl),
- NODE_ENV: JSON.stringify(process.env.NODE_ENV),
PATHNAME: JSON.stringify(config.pathname),
+ CHANGELOG_URL: JSON.stringify(config.changelogUrl),
}
}),
@@ -94,16 +70,16 @@ module.exports = {
React: 'react', // For babel JSX transformation which generates React.createElement.
}),
- new CopyWebpackPlugin([
- // 'to' values are relative to the public directory configured by output.path
- {from: 'src/assets/style.css', to: '.'},
- {from: 'node_modules/bootstrap/dist', to: 'bootstrap'},
- {from: 'node_modules/highlight.js/styles/github-gist.css', to: '.'},
- {from: 'node_modules/swagger-ui/dist/swagger-ui.css', to: '.'},
- ]),
+ new CopyWebpackPlugin({
+ patterns: [
+ {from: 'src/assets/style.css', to: '.'},
+ {from: 'node_modules/bootstrap/dist', to: 'bootstrap'},
+ {from: 'node_modules/highlight.js/styles/github-gist.css', to: '.'},
+ {from: 'node_modules/swagger-ui/dist/swagger-ui.css', to: '.'},
+ ],
+ }),
- function() {
- this.plugin('done', writeAssets(path.resolve(__dirname, 'webpack-assets.json')).bind(this))
- },
+ new WriteAssetsPlugin(webpackAssetsPath),
],
}
+
diff --git a/webpack.config.prod.babel.js b/webpack.config.prod.babel.js
index 89229220..fcd2c2b7 100644
--- a/webpack.config.prod.babel.js
+++ b/webpack.config.prod.babel.js
@@ -3,30 +3,26 @@ import path from 'path'
import CopyWebpackPlugin from 'copy-webpack-plugin'
import webpack from 'webpack'
+import WriteAssetsPlugin from './src/server/write-assets'
import config from './src/config'
-import writeAssets from './src/server/write-assets'
-import { loadTranslations } from './src/server/lang'
-
+import {loadTranslations} from './src/server/lang'
const assetsPath = path.join(__dirname, 'public')
+const webpackAssetsPath = path.resolve(__dirname, 'webpack-assets.json')
+
const supportedLanguages = Object.keys(loadTranslations(path.join(__dirname, './src/assets/lang/')))
module.exports = {
// devtool: "eval", // Transformed code
devtool: 'source-map', // Original code
entry: {
- 'main': ['babel-polyfill', './src/client.jsx'],
+ 'main': ['./src/client.jsx'],
},
output: {
path: assetsPath,
- filename: '[name]-[hash].js',
+ filename: '[name]-[contenthash].js',
publicPath: config.pathname.endsWith('/') ? config.pathname : `${config.pathname}/`
},
- target: 'web',
- // yaml-js has a reference to `fs`, this is a workaround
- node: {
- fs: 'empty'
- },
module: {
rules: [
{
@@ -40,16 +36,18 @@ module.exports = {
},
resolve: {
extensions: ['.js', '.jsx'],
+ fallback: {
+ fs: false,
+ stream: false,
+ },
},
plugins: [
-
// set global vars
new webpack.DefinePlugin({
'process.env': {
API_URL: JSON.stringify(config.apiBaseUrl),
- CHANGELOG_URL: JSON.stringify(config.changelogUrl),
- NODE_ENV: JSON.stringify(process.env.NODE_ENV),
PATHNAME: JSON.stringify(config.pathname),
+ CHANGELOG_URL: JSON.stringify(config.changelogUrl),
},
}),
@@ -57,13 +55,14 @@ module.exports = {
React: 'react', // For babel JSX transformation which generates React.createElement.
}),
- new CopyWebpackPlugin([
- // 'to' values are relative to the public directory configured by output.path
- {from: 'src/assets/style.css', to: '.'},
- {from: 'node_modules/bootstrap/dist', to: 'bootstrap'},
- {from: 'node_modules/highlight.js/styles/github-gist.css', to: '.'},
- {from: 'node_modules/swagger-ui/dist/swagger-ui.css', to: '.'},
- ]),
+ new CopyWebpackPlugin({
+ patterns: [
+ {from: 'src/assets/style.css', to: '.'},
+ {from: 'node_modules/bootstrap/dist', to: 'bootstrap'},
+ {from: 'node_modules/highlight.js/styles/github-gist.css', to: '.'},
+ {from: 'node_modules/swagger-ui/dist/swagger-ui.css', to: '.'},
+ ],
+ }),
// Only load syntax highlighting for Python
new webpack.ContextReplacementPlugin(
@@ -77,6 +76,6 @@ module.exports = {
new RegExp(`^./(${supportedLanguages.join('|')})$`)
),
- function() { this.plugin('done', writeAssets(path.resolve(__dirname, 'webpack-assets.json')).bind(this)) },
+ new WriteAssetsPlugin(webpackAssetsPath),
],
}