diff --git a/README.md b/README.md index f0a9e9138..0fb732a1f 100644 --- a/README.md +++ b/README.md @@ -14,9 +14,20 @@ You can find the frontend source files in `src/`, the compiled sources in After an `npm install`: -* `npm start` Will run the bundler in watch mode, re-assembling the files on - file change. -* `npm run build` Will compile the assets just once, ready for deployment. You - *must* run this step before sending the patch or CI will fail (so that - sources and built assets are in sync). -* `npm test` To run the linting tools and the tests. +* On one terminal, kickstart the bundler process: + * `npm start` Will run the bundler in watch mode, re-assembling the files on + file change. + * `npm run build` Will compile the assets just once, ready for deployment. You + *must* run this step before sending the patch or CI will fail (so that + sources and built assets are in sync). +* On another terminal, run tests and linting tools: + * `npm test` To run the linting tools and the tests. + * You can find the QUnit tests that depend on running MediaWiki under + `tests/qunit/` + * You can find the isolated QUnit tests under `tests/node-qunit/`, which you + can run with `npm run test:node` + * We recommend you install a file watcher like `nodemon` to watch sources and + auto run linting and tests. + * `npm install -g nodemon` + * Example running linting and node unit tests: + * `nodemon -w src/ --exec "grunt lint:all && npm run test:node"` diff --git a/package.json b/package.json index aee413f1a..78811c247 100644 --- a/package.json +++ b/package.json @@ -3,9 +3,10 @@ "scripts": { "start": "webpack -w", "build": "webpack", - "test": "grunt lint && npm run check-built-assets", + "test": "grunt lint && npm run check-built-assets && npm run test:node", "doc": "jsduck", - "check-built-assets": "echo 'CHECKING BUILD SOURCES ARE COMMITED' && rm -rf test-build && mv resources/dist test-build && npm run build && diff -x '*.map' -qr test-build resources/dist && rm -rf test-build" + "check-built-assets": "echo 'CHECKING BUILD SOURCES ARE COMMITED' && rm -rf test-build && mv resources/dist test-build && npm run build && diff -x '*.map' -qr test-build resources/dist && rm -rf test-build", + "test:node": "mw-node-qunit tests/node-qunit/*.js tests/node-qunit/**/*.js | tap-dot" }, "devDependencies": { "eslint-config-wikimedia": "0.3.0", @@ -17,9 +18,11 @@ "grunt-eslint": "19.0.0", "grunt-jsonlint": "1.1.0", "grunt-stylelint": "^0.6.0", + "mw-node-qunit": "^1.0.1", "redux": "3.6.0", "redux-thunk": "2.2.0", "stylelint-config-wikimedia": "0.3.0", + "tap-dot": "^1.0.5", "webpack": "^1.14.0" } } diff --git a/tests/node-qunit/counts.test.js b/tests/node-qunit/counts.test.js new file mode 100644 index 000000000..cf10e01ca --- /dev/null +++ b/tests/node-qunit/counts.test.js @@ -0,0 +1,64 @@ +var counts = require( '../../src/counts' ); + +QUnit.module( 'ext.popups/counts' ); + +QUnit.test( '#getEditCountBucket', function ( assert ) { + var i, bucket, count, + cases = [ + [ 0, '0 edits' ], + [ 1, '1-4 edits' ], + [ 2, '1-4 edits' ], + [ 4, '1-4 edits' ], + [ 5, '5-99 edits' ], + [ 25, '5-99 edits' ], + [ 50, '5-99 edits' ], + [ 99, '5-99 edits' ], + [ 100, '100-999 edits' ], + [ 101, '100-999 edits' ], + [ 500, '100-999 edits' ], + [ 999, '100-999 edits' ], + [ 1000, '1000+ edits' ], + [ 1500, '1000+ edits' ] + ]; + + assert.expect( cases.length ); + + for ( i = 0; i < cases.length; i++ ) { + count = cases[ i ][ 0 ]; + bucket = counts.getEditCountBucket( count ); + assert.equal( + bucket, + cases[ i ][ 1 ], + 'Edit count bucket is "' + bucket + '" when edit count is ' + count + '.' + ); + } +} ); + +QUnit.test( '#getPreviewCountBucket', function ( assert ) { + var i, count, bucket, + cases = [ + [ -1, 'unknown' ], + [ 0, '0 previews' ], + [ 1, '1-4 previews' ], + [ 2, '1-4 previews' ], + [ 4, '1-4 previews' ], + [ 5, '5-20 previews' ], + [ 10, '5-20 previews' ], + [ 20, '5-20 previews' ], + [ 21, '21+ previews' ], + [ 100, '21+ previews' ], + [ 1000, '21+ previews' ] + ]; + + assert.expect( cases.length ); + + for ( i = 0; i < cases.length; i++ ) { + count = cases[ i ][ 0 ]; + bucket = counts.getPreviewCountBucket( count ); + assert.equal( + bucket, + cases[ i ][ 1 ], + 'Preview count bucket is "' + bucket + '" when preview count is ' + count + '.' + ); + } +} ); diff --git a/tests/node-qunit/reducers/settings.test.js b/tests/node-qunit/reducers/settings.test.js new file mode 100644 index 000000000..a56676f6d --- /dev/null +++ b/tests/node-qunit/reducers/settings.test.js @@ -0,0 +1,114 @@ +var settings = require( '../../../src/reducers/settings' ); + +QUnit.module( 'reducers/settings' ); + +QUnit.test( '@@INIT', function ( assert ) { + var state = settings( undefined, { type: '@@INIT' } ); + + assert.deepEqual( + state, + { + shouldShow: false, + showHelp: false, + shouldShowFooterLink: false + } + ); +} ); + +QUnit.test( 'BOOT', function ( assert ) { + var action = { + type: 'BOOT', + isEnabled: false, + user: { + isAnon: true + } + }; + + assert.deepEqual( + settings( {}, action ), + { + shouldShowFooterLink: true + } + ); + + // --- + + // And when the user is logged out... + action.user.isAnon = false; + + assert.deepEqual( + settings( {}, action ), + { + shouldShowFooterLink: false + }, + 'If the user is logged in, then it doesn\'t signal that the footer link should be shown.' + ); +} ); + +QUnit.test( 'SETTINGS_SHOW', function ( assert ) { + assert.expect( 1 ); + + assert.deepEqual( + settings( {}, { type: 'SETTINGS_SHOW' } ), + { + shouldShow: true, + showHelp: false + }, + 'It should mark the settings dialog as ready to be shown, with no help.' + ); +} ); + +QUnit.test( 'SETTINGS_HIDE', function ( assert ) { + assert.expect( 1 ); + + assert.deepEqual( + settings( {}, { type: 'SETTINGS_HIDE' } ), + { + shouldShow: false, + showHelp: false + }, + 'It should mark the settings dialog as ready to be closed, and hide help.' + ); +} ); + +QUnit.test( 'SETTINGS_CHANGE', function ( assert ) { + var action = function ( wasEnabled, enabled ) { + return { + type: 'SETTINGS_CHANGE', + wasEnabled: wasEnabled, + enabled: enabled + }; + }; + + assert.deepEqual( + settings( {}, action( false, false ) ), + { shouldShow: false }, + 'It should just hide the settings dialog when enabled state stays the same.' + ); + assert.deepEqual( + settings( {}, action( true, true ) ), + { shouldShow: false }, + 'It should just hide the settings dialog when enabled state stays the same.' + ); + + assert.deepEqual( + settings( {}, action( false, true ) ), + { + shouldShow: false, + showHelp: false, + shouldShowFooterLink: false + }, + 'It should hide the settings dialog and help when we enable.' + ); + + assert.deepEqual( + settings( {}, action( true, false ) ), + { + shouldShow: true, + showHelp: true, + shouldShowFooterLink: true + }, + 'It should keep the settings showing and show the help when we disable.' + ); + +} ); diff --git a/tests/qunit/ext.popups/counts.test.js b/tests/qunit/ext.popups/counts.test.js deleted file mode 100644 index 5001ec873..000000000 --- a/tests/qunit/ext.popups/counts.test.js +++ /dev/null @@ -1,66 +0,0 @@ -( function ( mw ) { - - QUnit.module( 'ext.popups/counts' ); - - QUnit.test( '#getEditCountBucket', function ( assert ) { - var i, bucket, count, - cases = [ - [ 0, '0 edits' ], - [ 1, '1-4 edits' ], - [ 2, '1-4 edits' ], - [ 4, '1-4 edits' ], - [ 5, '5-99 edits' ], - [ 25, '5-99 edits' ], - [ 50, '5-99 edits' ], - [ 99, '5-99 edits' ], - [ 100, '100-999 edits' ], - [ 101, '100-999 edits' ], - [ 500, '100-999 edits' ], - [ 999, '100-999 edits' ], - [ 1000, '1000+ edits' ], - [ 1500, '1000+ edits' ] - ]; - - assert.expect( cases.length ); - - for ( i = 0; i < cases.length; i++ ) { - count = cases[ i ][ 0 ]; - bucket = mw.popups.counts.getEditCountBucket( count ); - assert.equal( - bucket, - cases[ i ][ 1 ], - 'Edit count bucket is "' + bucket + '" when edit count is ' + count + '.' - ); - } - } ); - - QUnit.test( '#getPreviewCountBucket', function ( assert ) { - var i, count, bucket, - cases = [ - [ -1, 'unknown' ], - [ 0, '0 previews' ], - [ 1, '1-4 previews' ], - [ 2, '1-4 previews' ], - [ 4, '1-4 previews' ], - [ 5, '5-20 previews' ], - [ 10, '5-20 previews' ], - [ 20, '5-20 previews' ], - [ 21, '21+ previews' ], - [ 100, '21+ previews' ], - [ 1000, '21+ previews' ] - ]; - - QUnit.expect( cases.length ); - - for ( i = 0; i < cases.length; i++ ) { - count = cases[ i ][ 0 ]; - bucket = mw.popups.counts.getPreviewCountBucket( count ); - assert.equal( - bucket, - cases[ i ][ 1 ], - 'Preview count bucket is "' + bucket + '" when preview count is ' + count + '.' - ); - } - } ); - -}( mediaWiki ) ); diff --git a/tests/qunit/ext.popups/reducers/settings.test.js b/tests/qunit/ext.popups/reducers/settings.test.js deleted file mode 100644 index 91b129a49..000000000 --- a/tests/qunit/ext.popups/reducers/settings.test.js +++ /dev/null @@ -1,116 +0,0 @@ -( function ( mw ) { - - QUnit.module( 'ext.popups/reducers#settings' ); - - QUnit.test( '@@INIT', function ( assert ) { - var state = mw.popups.reducers.settings( undefined, { type: '@@INIT' } ); - - assert.deepEqual( - state, - { - shouldShow: false, - showHelp: false, - shouldShowFooterLink: false - } - ); - } ); - - QUnit.test( 'BOOT', function ( assert ) { - var action = { - type: 'BOOT', - isEnabled: false, - user: { - isAnon: true - } - }; - - assert.deepEqual( - mw.popups.reducers.settings( {}, action ), - { - shouldShowFooterLink: true - } - ); - - // --- - - // And when the user is logged out... - action.user.isAnon = false; - - assert.deepEqual( - mw.popups.reducers.settings( {}, action ), - { - shouldShowFooterLink: false - }, - 'If the user is logged in, then it doesn\'t signal that the footer link should be shown.' - ); - } ); - - QUnit.test( 'SETTINGS_SHOW', function ( assert ) { - assert.expect( 1 ); - - assert.deepEqual( - mw.popups.reducers.settings( {}, { type: 'SETTINGS_SHOW' } ), - { - shouldShow: true, - showHelp: false - }, - 'It should mark the settings dialog as ready to be shown, with no help.' - ); - } ); - - QUnit.test( 'SETTINGS_HIDE', function ( assert ) { - assert.expect( 1 ); - - assert.deepEqual( - mw.popups.reducers.settings( {}, { type: 'SETTINGS_HIDE' } ), - { - shouldShow: false, - showHelp: false - }, - 'It should mark the settings dialog as ready to be closed, and hide help.' - ); - } ); - - QUnit.test( 'SETTINGS_CHANGE', function ( assert ) { - var action = function ( wasEnabled, enabled ) { - return { - type: 'SETTINGS_CHANGE', - wasEnabled: wasEnabled, - enabled: enabled - }; - }; - - assert.deepEqual( - mw.popups.reducers.settings( {}, action( false, false ) ), - { shouldShow: false }, - 'It should just hide the settings dialog when enabled state stays the same.' - ); - assert.deepEqual( - mw.popups.reducers.settings( {}, action( true, true ) ), - { shouldShow: false }, - 'It should just hide the settings dialog when enabled state stays the same.' - ); - - assert.deepEqual( - mw.popups.reducers.settings( {}, action( false, true ) ), - { - shouldShow: false, - showHelp: false, - shouldShowFooterLink: false - }, - 'It should hide the settings dialog and help when we enable.' - ); - - assert.deepEqual( - mw.popups.reducers.settings( {}, action( true, false ) ), - { - shouldShow: true, - showHelp: true, - shouldShowFooterLink: true - }, - 'It should keep the settings showing and show the help when we disable.' - ); - - } ); - -}( mediaWiki ) );