diff --git a/package.json b/package.json index 7a08b8f7e..5d83b71d8 100644 --- a/package.json +++ b/package.json @@ -8,10 +8,7 @@ "engines": { "node": ">=8.10.0 <10.0.0" }, - "browserslist": [ - "> 1% in FR", - "not ie < 11" - ], + "browserslist": ["> 1% in FR", "not ie < 11"], "dependencies": { "@babel/core": "=7.0.0-beta.51", "@babel/plugin-proposal-decorators": "=7.0.0-beta.51", @@ -92,20 +89,29 @@ "yaml-loader": "^0.5.0" }, "scripts": { - "pretest": "LIST=`git diff --name-only HEAD..HEAD^ | grep .*\\.js | grep -v json`; if [ \"$LIST\" ]; then eslint $LIST; fi && flow check", + "pretest": + "LIST=`git diff --name-only HEAD..HEAD^ | grep .*\\.js | grep -v json`; if [ \"$LIST\" ]; then eslint $LIST; fi && flow check", "start": "node source/server.js", "externalize": "node source/externalize.js", "prepare": "flow-typed install", "compile": "webpack --config source/webpack.prod.js", - "test": "mocha-webpack --webpack-config source/webpack.test.js --require source-map-support/register --include componentTestSetup.js --require test/helpers/browser.js \"./{,!(node_modules)/**/}!(webpack).test.js\"", - "test-watch": "mocha-webpack --webpack-config source/webpack.test.js --require source-map-support/register --require test/helpers/browser.js \"test/**/*.test.js\" --watch", - "test-meca": "mocha-webpack --webpack-config source/webpack.test.js --require source-map-support/register --require test/helpers/browser.js test/mecanisms.test.js --watch", - "test-rules": "mocha-webpack --webpack-config source/webpack.test.js --require source-map-support/register --require test/helpers/browser.js test/real-rules.test.js --watch", - "test-inversions": "mocha-webpack --webpack-config source/webpack.test.js --require source-map-support/register --require test/helpers/browser.js \"test/inversion.test.js\" --watch", - "test-components": "mocha-webpack --webpack-config source/webpack.test.js --require source-map-support/register --include componentTestSetup.js --require test/helpers/browser.js \"source/components/**/*.test.js\" --watch", + "test": + "mocha-webpack --webpack-config source/webpack.test.js --require source-map-support/register --include componentTestSetup.js --require test/helpers/browser.js \"./{,!(node_modules)/**/}!(webpack).test.js\"", + "test-watch": + "mocha-webpack --webpack-config source/webpack.test.js --require source-map-support/register --require test/helpers/browser.js \"test/**/*.test.js\" --watch", + "test-meca": + "mocha-webpack --webpack-config source/webpack.test.js --require source-map-support/register --require test/helpers/browser.js test/mecanisms.test.js --watch", + "test-rules": + "mocha-webpack --webpack-config source/webpack.test.js --require source-map-support/register --require test/helpers/browser.js test/real-rules.test.js --watch", + "test-inversions": + "mocha-webpack --webpack-config source/webpack.test.js --require source-map-support/register --require test/helpers/browser.js \"test/inversion.test.js\" --watch", + "test-components": + "mocha-webpack --webpack-config source/webpack.test.js --require source-map-support/register --include componentTestSetup.js --require test/helpers/browser.js \"source/components/**/*.test.js\" --watch", "heroku-postbuild": "yarn install --production=false && yarn compile", - "eslint": "LIST=`git diff --cached --name-only HEAD | grep .*\\.js | grep -v json`; if [ \"$LIST\" ]; then eslint $LIST; fi", - "eslint-check": "eslint --print-config .eslintrc | eslint-config-prettier-check" + "eslint": + "LIST=`git diff --cached --name-only HEAD | grep .*\\.js | grep -v json`; if [ \"$LIST\" ]; then eslint $LIST; fi", + "eslint-check": + "eslint --print-config .eslintrc | eslint-config-prettier-check" }, "devDependencies": { "@babel/preset-flow": "^7.0.0-beta.51", diff --git a/source/engine/rules.js b/source/engine/rules.js index b6426ec5d..54fd708c7 100644 --- a/source/engine/rules.js +++ b/source/engine/rules.js @@ -187,9 +187,7 @@ export let nestedSituationToPathMap = situation => { let rec = (o, currentPath) => typeof o === 'object' ? chain(([k, v]) => rec(v, [...currentPath, trim(k)]), toPairs(o)) - : typeof o === 'string' - ? [[currentPath.join(' . '), o]] - : new Error('oups, all leaf values were expected to be strings') + : [[currentPath.join(' . '), o + '']] return fromPairs(rec(situation, [])) } diff --git a/source/i18n.js b/source/i18n.js index 92ca9c3bf..dda5ef05f 100644 --- a/source/i18n.js +++ b/source/i18n.js @@ -4,14 +4,22 @@ import queryString from 'query-string' import { getIframeOption, parseDataAttributes } from './utils' import enTranslations from './locales/en.yaml' +let getFromSessionStorage = where => + typeof sessionStorage !== 'undefined' ? sessionStorage[where] : null + +let setToSessionStorage = (where, what) => + typeof sessionStorage !== 'undefined' && + do { + sessionStorage[where] = what + } + let lang = getIframeOption('lang') || queryString.parse(location.search)['lang'] || - parseDataAttributes(sessionStorage['lang']) || + parseDataAttributes(getFromSessionStorage('lang')) || 'fr' -sessionStorage['lang'] = lang - +setToSessionStorage('lang', lang) i18next.init( { debug: false, diff --git a/source/reducers/reducers.js b/source/reducers/reducers.js index 416ede9ea..534ec2a35 100644 --- a/source/reducers/reducers.js +++ b/source/reducers/reducers.js @@ -67,7 +67,8 @@ function conversationSteps( if (type === 'RESET_SIMULATION') return { foldedSteps: [], unfolded: null } if (type !== 'STEP_ACTION') return state - if (name === 'fold') return { foldedSteps: [...state.foldedSteps, step] } + if (name === 'fold') + return { foldedSteps: [...state.foldedSteps, step], unfoldedStep: null } if (name === 'unfold') { // if a step had already been unfolded, bring it back ! return { diff --git a/source/selectors/analyseSelectors.js b/source/selectors/analyseSelectors.js index 69f275097..73fa3fa3f 100644 --- a/source/selectors/analyseSelectors.js +++ b/source/selectors/analyseSelectors.js @@ -28,7 +28,7 @@ import { export let flatRulesSelector = createSelector( state => state.lang, - state => state.rules, + (state, props) => props && props.rules, (lang, rules) => rules || (lang === 'en' ? baseRulesEn : baseRulesFr) ) diff --git a/test/conversation.test.js b/test/conversation.test.js new file mode 100644 index 000000000..31cc3ac55 --- /dev/null +++ b/test/conversation.test.js @@ -0,0 +1,136 @@ +import { expect } from 'chai' +import dedent from 'dedent-js' +import yaml from 'js-yaml' +import { enrichRule } from '../source/engine/rules' +import reducers from '../source/reducers/reducers' +import { currentQuestionSelector } from '../source/selectors/analyseSelectors' +import { merge, assocPath } from 'ramda' + +let baseState = { + conversationSteps: { foldedSteps: [] }, + form: { conversation: { values: {} } } +} + +describe('conversation', function() { + it('should start with the first missing variable', function() { + let rawRules = [ + // TODO - this won't work without the indirection, figure out why + { nom: 'startHere', formule: { somme: ['a', 'b'] }, espace: 'top' }, + { nom: 'a', espace: 'top', formule: 'aa' }, + { nom: 'b', espace: 'top', formule: 'bb' }, + { nom: 'aa', question: '?', titre: 'a', espace: 'top' }, + { nom: 'bb', question: '?', titre: 'b', espace: 'top' } + ], + rules = rawRules.map(enrichRule), + state = merge(baseState, { + targetNames: ['startHere'] + }), + currentQuestion = currentQuestionSelector(state, { rules }) + + expect(currentQuestion).to.equal('top . aa') + }) + it('should deal with double unfold', function() { + let rawRules = [ + // TODO - this won't work without the indirection, figure out why + { + nom: 'startHere', + formule: { somme: ['a', 'b', 'c'] }, + espace: 'top' + }, + { nom: 'a', espace: 'top', formule: 'aa' }, + { nom: 'b', espace: 'top', formule: 'bb' }, + { nom: 'c', espace: 'top', formule: 'cc' }, + { nom: 'aa', question: '?', titre: 'a', espace: 'top' }, + { nom: 'bb', question: '?', titre: 'b', espace: 'top' }, + { nom: 'cc', question: '?', titre: 'c', espace: 'top' } + ], + rules = rawRules.map(enrichRule) + + let step1 = merge(baseState, { + targetNames: ['startHere'] + }) + let step2 = reducers( + assocPath( + ['form', 'conversation', 'values'], + { top: { aa: '1' } }, + step1 + ), + { + type: 'STEP_ACTION', + name: 'fold', + step: 'top . aa' + } + ) + + let step3 = reducers( + assocPath( + ['form', 'conversation', 'values'], + { top: { bb: '1', aa: '1' } }, + step2 + ), + { + type: 'STEP_ACTION', + name: 'fold', + step: 'top . bb' + } + ) + let step4 = reducers(step3, { + type: 'STEP_ACTION', + name: 'unfold', + step: 'top . aa' + }) + let lastStep = reducers(step4, { + type: 'STEP_ACTION', + name: 'unfold', + step: 'top . bb' + }) + + expect(currentQuestionSelector(lastStep, { rules })).to.equal('top . bb') + expect(lastStep.conversationSteps).to.have.property('foldedSteps') + expect(lastStep.conversationSteps.foldedSteps).to.have.lengthOf(1) + expect(lastStep.conversationSteps.foldedSteps[0]).to.equal('top . aa') + }) + + it('should first ask for questions without defaults, then those with defaults', function() { + let rawRules = dedent` + - nom: net + formule: brut - cotisation + + - nom: brut + format: euro + + - nom: cotisation + formule: + multiplication: + assiette: brut + variations: + - si: cadre + taux: 77% + - si: ≠ cadre + taux: 80% + - nom: cadre + par défaut: non + `, + rules = yaml.safeLoad(rawRules).map(enrichRule) + + let step1 = merge(baseState, { + targetNames: ['net'] + }) + + expect(currentQuestionSelector(step1, { rules })).to.equal('brut') + + let step2 = reducers( + assocPath(['form', 'conversation', 'values', 'brut'], '2300', step1), + { + type: 'STEP_ACTION', + name: 'fold', + step: 'brut' + } + ) + + expect(step2.conversationSteps).to.have.property('foldedSteps') + expect(step2.conversationSteps.foldedSteps).to.have.lengthOf(1) + expect(step2.conversationSteps.foldedSteps[0]).to.equal('brut') + expect(currentQuestionSelector(step2, { rules })).to.equal('cadre') + }) +})