diff --git a/jest.setup.js b/jest.setup.js index 950ac68e9..a35eb7663 100644 --- a/jest.setup.js +++ b/jest.setup.js @@ -4,3 +4,6 @@ global.mw = mockMediaWiki(); global.$ = require( 'jquery' ); global.mw.util.showPortlet = function () {}; global.mw.Api.prototype.saveOption = function () {}; +global.OO = require( 'oojs' ); +require( 'oojs-ui' ); +require( 'oojs-ui/dist/oojs-ui-wikimediaui.js' ); diff --git a/package-lock.json b/package-lock.json index 0cf9ef77c..71a18c025 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,6 +25,8 @@ "mustache": "3.0.1", "mustache-jest": "1.1.1", "node-fetch": "2.6.7", + "oojs": "6.0.0", + "oojs-ui": "0.46.3", "postcss-less": "6.0.0", "pre-commit": "1.2.2", "stylelint-config-wikimedia": "0.14.0", @@ -10137,6 +10139,23 @@ "integrity": "sha512-fdHt6ZKTOQmVFdFwfM84CuoWAQquQnfqOmIjKFANoQmORTBCExTEOIAPVkYDNSUBAqBt2FtTMIHMplsR3XgU7g==", "dev": true }, + "node_modules/oojs-ui": { + "version": "0.46.3", + "resolved": "https://registry.npmjs.org/oojs-ui/-/oojs-ui-0.46.3.tgz", + "integrity": "sha512-ViZMxh3+H6ZYR37VL9JSyG2CeWETR8BAs1M37PIoeJjOIkDpi4LaZuZD3zt5UiFVJs6Miung3ijy+4zrpaJLNQ==", + "dev": true, + "dependencies": { + "jquery": "3.6.1", + "oojs": "6.0.0", + "wikimedia-ui-base": "0.21.0" + } + }, + "node_modules/oojs-ui/node_modules/jquery": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.6.1.tgz", + "integrity": "sha512-opJeO4nCucVnsjiXOE+/PcCgYw9Gwpvs/a6B1LL/lQhwWwpbVEVYDZ1FokFr8PRc7ghYlrFPuyHuiiDNTQxmcw==", + "dev": true + }, "node_modules/optionator": { "version": "0.9.1", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", @@ -12811,6 +12830,12 @@ "node": ">= 8" } }, + "node_modules/wikimedia-ui-base": { + "version": "0.21.0", + "resolved": "https://registry.npmjs.org/wikimedia-ui-base/-/wikimedia-ui-base-0.21.0.tgz", + "integrity": "sha512-aN4g4td0Im2cr9Ex6mtc13u2nDiH2T39ysY5E5Ik326ZO27zOe3Klz9VFecQNB0BiLQrozLFkt/hyBF/rf1xXg==", + "dev": true + }, "node_modules/wmf-a11y": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/wmf-a11y/-/wmf-a11y-0.0.0.tgz", @@ -20856,6 +20881,25 @@ "integrity": "sha512-fdHt6ZKTOQmVFdFwfM84CuoWAQquQnfqOmIjKFANoQmORTBCExTEOIAPVkYDNSUBAqBt2FtTMIHMplsR3XgU7g==", "dev": true }, + "oojs-ui": { + "version": "0.46.3", + "resolved": "https://registry.npmjs.org/oojs-ui/-/oojs-ui-0.46.3.tgz", + "integrity": "sha512-ViZMxh3+H6ZYR37VL9JSyG2CeWETR8BAs1M37PIoeJjOIkDpi4LaZuZD3zt5UiFVJs6Miung3ijy+4zrpaJLNQ==", + "dev": true, + "requires": { + "jquery": "3.6.1", + "oojs": "6.0.0", + "wikimedia-ui-base": "0.21.0" + }, + "dependencies": { + "jquery": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.6.1.tgz", + "integrity": "sha512-opJeO4nCucVnsjiXOE+/PcCgYw9Gwpvs/a6B1LL/lQhwWwpbVEVYDZ1FokFr8PRc7ghYlrFPuyHuiiDNTQxmcw==", + "dev": true + } + } + }, "optionator": { "version": "0.9.1", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", @@ -22896,6 +22940,12 @@ "isexe": "^2.0.0" } }, + "wikimedia-ui-base": { + "version": "0.21.0", + "resolved": "https://registry.npmjs.org/wikimedia-ui-base/-/wikimedia-ui-base-0.21.0.tgz", + "integrity": "sha512-aN4g4td0Im2cr9Ex6mtc13u2nDiH2T39ysY5E5Ik326ZO27zOe3Klz9VFecQNB0BiLQrozLFkt/hyBF/rf1xXg==", + "dev": true + }, "wmf-a11y": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/wmf-a11y/-/wmf-a11y-0.0.0.tgz", diff --git a/package.json b/package.json index a1747b628..f2430b075 100644 --- a/package.json +++ b/package.json @@ -38,6 +38,8 @@ "mustache": "3.0.1", "mustache-jest": "1.1.1", "node-fetch": "2.6.7", + "oojs": "6.0.0", + "oojs-ui": "0.46.3", "postcss-less": "6.0.0", "pre-commit": "1.2.2", "stylelint-config-wikimedia": "0.14.0", diff --git a/resources/skins.vector.js/popupNotification.js b/resources/skins.vector.js/popupNotification.js index c2bfe3fec..f8cecb6af 100644 --- a/resources/skins.vector.js/popupNotification.js +++ b/resources/skins.vector.js/popupNotification.js @@ -1,3 +1,4 @@ +/** @module PopupNotification */ // Store active notifications to only show one at a time, for use inside clearHints and showHint const /** @type {Record} */ activeNotification = {}; @@ -93,5 +94,8 @@ function removeAll( selector = '.vector-popup-notification' ) { module.exports = { add, remove, - removeAll + removeAll, + tests: { + show + } }; diff --git a/tests/jest/popupNotification.test.js b/tests/jest/popupNotification.test.js new file mode 100644 index 000000000..4e367de86 --- /dev/null +++ b/tests/jest/popupNotification.test.js @@ -0,0 +1,109 @@ +const popUpNotification = require( '../../resources/skins.vector.js/popupNotification.js' ); + +/** + * @type string + */ +let testId; + +/** + * @type string + */ +let testMessage; + +/** + * @type string + */ +let vectorPopupClass; + +/** + * @type {Record} + */ +let activeNotification; + +describe( 'Popup Notification', () => { + beforeEach( () => { + global.window.matchMedia = jest.fn( () => ( {} ) ); + document.body.style = 'direction: ltr'; + jest.spyOn( mw.loader, 'using' ) + .mockImplementation( () => Promise.resolve() ); + testId = 'test-id'; + testMessage = 'test message'; + vectorPopupClass = 'vector-popup-notification'; + activeNotification = []; + } ); + + afterEach( () => { + jest.resetModules(); + } ); + + // test add function + test( 'add', async () => { + const popupWidget = await popUpNotification.add( + document.body, + testMessage, + testId, + [], + 4000, + () => {} + ); + activeNotification[ testId ] = popupWidget; + expect( activeNotification[ testId ] ).toBeDefined(); + expect( activeNotification[ testId ].$element ).toBeDefined(); + expect( activeNotification[ testId ].$element[ 0 ].textContent ) + .toContain( testMessage ); + expect( activeNotification[ testId ].$element[ 0 ].classList + .contains( vectorPopupClass ) ).toBe( true ); + } ); + + // test remove function + test( 'remove', async () => { + const popupWidget = await popUpNotification.add( + document.body, + testMessage, + testId, + [], + 4000, + () => {} + ); + activeNotification[ testId ] = popupWidget; + expect( activeNotification[ testId ].visible ).toBe( true ); + popUpNotification.remove( activeNotification[ testId ] ); + expect( document.body.contains( activeNotification[ testId ].$element[ 0 ] ) ) + .toBe( false ); + } ); + + // test show function + test( 'show', async () => { + const popupWidget = await popUpNotification.add( + document.body, + testMessage, + testId, + [], + 4000, + () => {} + ); + activeNotification[ testId ] = popupWidget; + expect( activeNotification[ testId ].visible ).toBe( true ); + activeNotification[ testId ].toggle( false ); + expect( activeNotification[ testId ].visible ).toBe( false ); + popUpNotification.tests.show( activeNotification[ testId ] ); + expect( activeNotification[ testId ].visible ).toBe( true ); + } ); + + // test removeAll function + test( 'removeAll', async () => { + const popupWidget = await popUpNotification.add( + document.body, + testMessage, + testId, + [], + 4000, + () => {} + ); + activeNotification[ testId ] = popupWidget; + expect( activeNotification[ testId ].visible ).toBe( true ); + popUpNotification.removeAll(); + expect( document.body.contains( activeNotification[ testId ].$element[ 0 ] ) ) + .toBe( false ); + } ); +} );