diff --git a/Makefile b/Makefile index 5c2f29356c..3d705bfb10 100644 --- a/Makefile +++ b/Makefile @@ -35,13 +35,12 @@ pull_translations: cd src/i18n/messages \ && atlas pull $(ATLAS_OPTIONS) \ translations/frontend-component-ai-translations/src/i18n/messages:frontend-component-ai-translations \ - translations/frontend-lib-content-components/src/i18n/messages:frontend-lib-content-components \ translations/frontend-platform/src/i18n/messages:frontend-platform \ translations/paragon/src/i18n/messages:paragon \ translations/frontend-component-footer/src/i18n/messages:frontend-component-footer \ translations/frontend-app-course-authoring/src/i18n/messages:frontend-app-course-authoring - $(intl_imports) frontend-component-ai-translations frontend-lib-content-components frontend-platform paragon frontend-component-footer frontend-app-course-authoring + $(intl_imports) frontend-component-ai-translations frontend-platform paragon frontend-component-footer frontend-app-course-authoring # This target is used by Travis. validate-no-uncommitted-package-lock-changes: diff --git a/README.rst b/README.rst index d0680a32e5..0b2d30be71 100644 --- a/README.rst +++ b/README.rst @@ -145,10 +145,6 @@ Feature Description When a corresponding waffle flag is set, upon editing a block in Studio, the view is rendered by this MFE instead of by the XBlock's authoring view. The user remains in Studio. -.. note:: - - The new editors themselves are currently implemented in a repository outside ``openedx``: `frontend-lib-content-components `_, a dependency of this MFE. This repository is slated to be moved to the ``openedx`` org, however. - Feature: New Proctoring Exams View ================================== diff --git a/package-lock.json b/package-lock.json index 860a39a8a1..4af4fb43fc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,11 @@ "version": "0.1.0", "license": "AGPL-3.0", "dependencies": { + "@codemirror/lang-html": "^6.0.0", + "@codemirror/lang-xml": "^6.0.0", + "@codemirror/lint": "^6.2.1", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0", "@dnd-kit/core": "^6.1.0", "@dnd-kit/modifiers": "^7.0.0", "@dnd-kit/sortable": "^8.0.0", @@ -17,7 +22,6 @@ "@edx/frontend-component-footer": "^14.0.3", "@edx/frontend-component-header": "^5.3.3", "@edx/frontend-enterprise-hotjar": "^2.0.0", - "@edx/frontend-lib-content-components": "^2.6.0", "@edx/frontend-platform": "^8.0.3", "@edx/openedx-atlas": "^0.6.0", "@openedx-plugins/course-app-calculator": "file:plugins/course-apps/calculator", @@ -32,16 +36,22 @@ "@openedx-plugins/course-app-xpert_unit_summary": "file:plugins/course-apps/xpert_unit_summary", "@openedx/frontend-plugin-framework": "^1.2.1", "@openedx/paragon": "^22.5.1", + "@redux-devtools/extension": "^3.3.0", "@reduxjs/toolkit": "1.9.7", "@tanstack/react-query": "4.36.1", + "@tinymce/tinymce-react": "^3.14.0", "classnames": "2.5.1", + "codemirror": "^6.0.0", "email-validator": "2.0.4", + "fast-xml-parser": "^4.0.10", "file-saver": "^2.0.5", "formik": "2.4.6", + "frontend-components-tinymce-advanced-plugins": "^1.0.3", "jszip": "^3.10.1", "lodash": "4.17.21", "meilisearch": "^0.41.0", "moment": "2.30.1", + "moment-shortformat": "^2.1.0", "npm": "^10.8.1", "prop-types": "^15.8.1", "react": "17.0.2", @@ -49,6 +59,7 @@ "react-dom": "17.0.2", "react-error-boundary": "^4.0.13", "react-helmet": "^6.1.0", + "react-onclickoutside": "^6.13.0", "react-redux": "7.2.9", "react-responsive": "9.0.2", "react-router": "6.23.1", @@ -57,14 +68,20 @@ "react-textarea-autosize": "^8.5.3", "react-transition-group": "4.4.5", "redux": "4.0.5", + "redux-logger": "^3.0.6", + "redux-mock-store": "^1.5.4", + "redux-thunk": "^2.4.1", + "reselect": "^4.1.5", "start": "^5.1.0", + "tinymce": "^5.10.4", "universal-cookie": "^4.0.4", "uuid": "^3.4.0", + "xmlchecker": "^0.1.0", "yup": "0.31.1" }, "devDependencies": { "@edx/browserslist-config": "1.2.0", - "@edx/react-unit-test-utils": "2.1.1", + "@edx/react-unit-test-utils": "3.0.0", "@edx/reactifex": "^1.0.3", "@edx/stylelint-config-edx": "2.3.3", "@edx/typescript-config": "^1.0.1", @@ -73,6 +90,7 @@ "@testing-library/react": "12.1.5", "@testing-library/react-hooks": "^8.0.1", "@testing-library/user-event": "^13.2.1", + "@types/lodash": "^4.17.7", "axios": "^0.28.0", "axios-mock-adapter": "1.22.0", "eslint-import-resolver-webpack": "^0.13.8", @@ -2036,9 +2054,9 @@ "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==" }, "node_modules/@codemirror/autocomplete": { - "version": "6.16.2", - "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.16.2.tgz", - "integrity": "sha512-MjfDrHy0gHKlPWsvSsikhO1+BOh+eBHNgfH1OXs1+DAf30IonQldgMM3kxLDTG9ktE7kDLaA1j/l7KMPA4KNfw==", + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.18.0.tgz", + "integrity": "sha512-5DbOvBbY4qW5l57cjDsmmpDh3/TeK1vXfTHa+BUMrRzdWdcxKZ4U4V7vQaTtOpApNU4kLS4FQ6cINtLg245LXA==", "dependencies": { "@codemirror/language": "^6.0.0", "@codemirror/state": "^6.0.0", @@ -2132,9 +2150,9 @@ } }, "node_modules/@codemirror/lint": { - "version": "6.8.0", - "resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.8.0.tgz", - "integrity": "sha512-lsFofvaw0lnPRJlQylNsC4IRt/1lI4OD/yYslrSGVndOJfStc58v+8p9dgGiD90ktOfL7OhBWns1ZETYgz0EJA==", + "version": "6.8.1", + "resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.8.1.tgz", + "integrity": "sha512-IZ0Y7S4/bpaunwggW2jYqwLuHj0QtESf5xcROewY6+lDNwZ/NzvR4t+vpYgg9m7V8UXLPYqG+lu3DF470E5Oxg==", "dependencies": { "@codemirror/state": "^6.0.0", "@codemirror/view": "^6.0.0", @@ -2157,9 +2175,9 @@ "integrity": "sha512-QkEyUiLhsJoZkbumGZlswmAhA7CBU02Wrz7zvH4SrcifbsqwlXShVXg65f3v/ts57W3dqyamEriMhij1Z3Zz4A==" }, "node_modules/@codemirror/view": { - "version": "6.28.1", - "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.28.1.tgz", - "integrity": "sha512-BUWr+zCJpMkA/u69HlJmR+YkV4yPpM81HeMkOMZuwFa8iM5uJdEPKAs1icIRZKkKmy0Ub1x9/G3PQLTXdpBxrQ==", + "version": "6.30.0", + "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.30.0.tgz", + "integrity": "sha512-96Nmn8OeLh6aONQprIeYk8hGVnEuYpWuxKSkdsODOx9hWPxyuyZGvmvxV/JmLsp+CubMO1PsLaN5TNNgrl0UrQ==", "dependencies": { "@codemirror/state": "^6.4.0", "style-mod": "^4.1.0", @@ -2358,7 +2376,8 @@ "node_modules/@edx/browserslist-config": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@edx/browserslist-config/-/browserslist-config-1.2.0.tgz", - "integrity": "sha512-T1+6P52Yx7SMkmoIr4O0Q3m/DyRdrLTJbv1xVijdRLFEq1hqdafEs+Ln1423U5LSkTePb9AOkEtL1G0RZLFl1w==" + "integrity": "sha512-T1+6P52Yx7SMkmoIr4O0Q3m/DyRdrLTJbv1xVijdRLFEq1hqdafEs+Ln1423U5LSkTePb9AOkEtL1G0RZLFl1w==", + "dev": true }, "node_modules/@edx/eslint-config": { "version": "4.1.0", @@ -2635,112 +2654,6 @@ "react-router-dom": "^6.0.0" } }, - "node_modules/@edx/frontend-lib-content-components": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@edx/frontend-lib-content-components/-/frontend-lib-content-components-2.6.0.tgz", - "integrity": "sha512-dqx94SSbaVSztkyInNH7GbBzMSyFvKdx1zFWa4itWeSf1cUlfvD7QBZfHC5US8kh+CHW7YvrQg6whaF2F2neNg==", - "dependencies": { - "@codemirror/lang-html": "^6.0.0", - "@codemirror/lang-xml": "^6.0.0", - "@codemirror/lint": "^6.2.1", - "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.0.0", - "@dnd-kit/core": "^6.0.8", - "@dnd-kit/sortable": "^7.0.2", - "@dnd-kit/utilities": "^3.2.1", - "@edx/browserslist-config": "^1.1.1", - "@reduxjs/toolkit": "^1.8.1", - "@tinymce/tinymce-react": "^3.14.0", - "babel-polyfill": "6.26.0", - "classnames": "^2.5.1", - "codemirror": "^6.0.0", - "fast-xml-parser": "^4.0.10", - "frontend-components-tinymce-advanced-plugins": "^1.0.3", - "lodash-es": "^4.17.21", - "lodash.flatten": "^4.4.0", - "moment": "^2.29.4", - "moment-shortformat": "^2.1.0", - "react-dropzone": "^14.2.3", - "react-onclickoutside": "^6.13.0", - "react-redux": "^7.2.8", - "react-responsive": "8.2.0", - "react-transition-group": "4.4.2", - "redux": "4.1.2", - "redux-devtools-extension": "^2.13.9", - "redux-logger": "^3.0.6", - "redux-mock-store": "^1.5.4", - "redux-thunk": "^2.4.1", - "reselect": "^4.1.5", - "tinymce": "^5.10.4", - "video-react": "^0.15.0", - "video.js": "^7.18.1", - "xmlchecker": "^0.1.0" - }, - "peerDependencies": { - "@edx/frontend-platform": "^7.0.1 || ^8.0.0", - "@openedx/paragon": "^21.5.7 || ^22.0.0", - "prop-types": "^15.5.10", - "react": "^16.14.0 || ^17.0.0", - "react-dom": "^16.14.0 || ^17.0.0" - } - }, - "node_modules/@edx/frontend-lib-content-components/node_modules/@dnd-kit/sortable": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@dnd-kit/sortable/-/sortable-7.0.2.tgz", - "integrity": "sha512-wDkBHHf9iCi1veM834Gbk1429bd4lHX4RpAwT0y2cHLf246GAvU2sVw/oxWNpPKQNQRQaeGXhAVgrOl1IT+iyA==", - "license": "MIT", - "dependencies": { - "@dnd-kit/utilities": "^3.2.0", - "tslib": "^2.0.0" - }, - "peerDependencies": { - "@dnd-kit/core": "^6.0.7", - "react": ">=16.8.0" - } - }, - "node_modules/@edx/frontend-lib-content-components/node_modules/react-responsive": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/react-responsive/-/react-responsive-8.2.0.tgz", - "integrity": "sha512-iagCqVrw4QSjhxKp3I/YK6+ODkWY6G+YPElvdYKiUUbywwh9Ds0M7r26Fj2/7dWFFbOpcGnJE6uE7aMck8j5Qg==", - "license": "MIT", - "dependencies": { - "hyphenate-style-name": "^1.0.0", - "matchmediaquery": "^0.3.0", - "prop-types": "^15.6.1", - "shallow-equal": "^1.1.0" - }, - "engines": { - "node": ">= 0.10" - }, - "peerDependencies": { - "react": ">=16.8.0" - } - }, - "node_modules/@edx/frontend-lib-content-components/node_modules/react-transition-group": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.2.tgz", - "integrity": "sha512-/RNYfRAMlZwDSr6z4zNKV6xu53/e2BuaBbGhbyYIXTrmgu/bGHzmqOs7mJSJBHy9Ud+ApHx3QjrkKSp1pxvlFg==", - "license": "BSD-3-Clause", - "dependencies": { - "@babel/runtime": "^7.5.5", - "dom-helpers": "^5.0.1", - "loose-envify": "^1.4.0", - "prop-types": "^15.6.2" - }, - "peerDependencies": { - "react": ">=16.6.0", - "react-dom": ">=16.6.0" - } - }, - "node_modules/@edx/frontend-lib-content-components/node_modules/redux": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/redux/-/redux-4.1.2.tgz", - "integrity": "sha512-SH8PglcebESbd/shgf6mii6EIoRM0zrQyjcuQ+ojmfxjTtE0z9Y8pa62iA/OJ58qjP6j27uyW4kUF4jl/jd6sw==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.9.2" - } - }, "node_modules/@edx/frontend-platform": { "version": "8.0.3", "resolved": "https://registry.npmjs.org/@edx/frontend-platform/-/frontend-platform-8.0.3.tgz", @@ -2798,9 +2711,9 @@ } }, "node_modules/@edx/react-unit-test-utils": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@edx/react-unit-test-utils/-/react-unit-test-utils-2.1.1.tgz", - "integrity": "sha512-ZiBGHQtAMBCs8ZjCGeDKkUQC/tXELe2GkCxqBNsmnypB8iM9U2NoBZJ3OrF0ZreoeoDDzDOlzABqLL76B4taTw==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@edx/react-unit-test-utils/-/react-unit-test-utils-3.0.0.tgz", + "integrity": "sha512-RQ+yFXXkrukGC3RciqjYKwNLCIVkCyctPFMn93eXMTCUY2fqTs6T6RjDGCLAI2/H7DMXPV8DjaDHQNQXIlJNAA==", "dev": true, "dependencies": { "@edx/browserslist-config": "^1.1.1", @@ -3944,9 +3857,9 @@ } }, "node_modules/@lezer/lr": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.4.1.tgz", - "integrity": "sha512-CHsKq8DMKBf9b3yXPDIU4DbH+ZJd/sJdYOW2llbW/HudP5u0VS6Bfq1hLYfgU7uAYGFIyGGQIsSOXGPEErZiJw==", + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.4.2.tgz", + "integrity": "sha512-pu0K1jCIdnQ12aWNaAVU5bzi7Bd1w54J3ECgANPmYLtQKP0HBj2cE/5coBD66MT10xbtIuUr7tg0Shbsvk0mDA==", "dependencies": { "@lezer/common": "^1.0.0" } @@ -4685,6 +4598,18 @@ "url": "https://opencollective.com/popperjs" } }, + "node_modules/@redux-devtools/extension": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/@redux-devtools/extension/-/extension-3.3.0.tgz", + "integrity": "sha512-X34S/rC8S/M1BIrkYD1mJ5f8vlH0BDqxXrs96cvxSBo4FhMdbhU+GUGsmNYov1xjSyLMHgo8NYrUG8bNX7525g==", + "dependencies": { + "@babel/runtime": "^7.23.2", + "immutable": "^4.3.4" + }, + "peerDependencies": { + "redux": "^3.1.0 || ^4.0.0 || ^5.0.0" + } + }, "node_modules/@reduxjs/toolkit": { "version": "1.9.7", "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-1.9.7.tgz", @@ -5492,6 +5417,12 @@ "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==" }, + "node_modules/@types/lodash": { + "version": "4.17.7", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.7.tgz", + "integrity": "sha512-8wTvZawATi/lsmNu10/j2hk1KEP0IvjubqPE3cu1Xz7xfXXt5oCq3SNUz4fMIP4XGF9Ky+Ue2tBA3hcS7LSBlA==", + "dev": true + }, "node_modules/@types/mime": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", @@ -6433,52 +6364,6 @@ "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", "peer": true }, - "node_modules/@videojs/http-streaming": { - "version": "2.16.3", - "resolved": "https://registry.npmjs.org/@videojs/http-streaming/-/http-streaming-2.16.3.tgz", - "integrity": "sha512-91CJv5PnFBzNBvyEjt+9cPzTK/xoVixARj2g7ZAvItA+5bx8VKdk5RxCz/PP2kdzz9W+NiDUMPkdmTsosmy69Q==", - "dependencies": { - "@babel/runtime": "^7.12.5", - "@videojs/vhs-utils": "3.0.5", - "aes-decrypter": "3.1.3", - "global": "^4.4.0", - "m3u8-parser": "4.8.0", - "mpd-parser": "^0.22.1", - "mux.js": "6.0.1", - "video.js": "^6 || ^7" - }, - "engines": { - "node": ">=8", - "npm": ">=5" - }, - "peerDependencies": { - "video.js": "^6 || ^7" - } - }, - "node_modules/@videojs/vhs-utils": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@videojs/vhs-utils/-/vhs-utils-3.0.5.tgz", - "integrity": "sha512-PKVgdo8/GReqdx512F+ombhS+Bzogiofy1LgAj4tN8PfdBx3HSS7V5WfJotKTqtOWGwVfSWsrYN/t09/DSryrw==", - "dependencies": { - "@babel/runtime": "^7.12.5", - "global": "^4.4.0", - "url-toolkit": "^2.2.1" - }, - "engines": { - "node": ">=8", - "npm": ">=5" - } - }, - "node_modules/@videojs/xhr": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@videojs/xhr/-/xhr-2.6.0.tgz", - "integrity": "sha512-7J361GiN1tXpm+gd0xz2QWr3xNWBE+rytvo8J3KuggFaLg+U37gZQ2BuPLcnkfGffy2e+ozY70RHC8jt7zjA6Q==", - "dependencies": { - "@babel/runtime": "^7.5.5", - "global": "~4.4.0", - "is-function": "^1.0.1" - } - }, "node_modules/@webassemblyjs/ast": { "version": "1.12.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.12.1.tgz", @@ -6651,14 +6536,6 @@ } } }, - "node_modules/@xmldom/xmldom": { - "version": "0.8.10", - "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.10.tgz", - "integrity": "sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==", - "engines": { - "node": ">=10.0.0" - } - }, "node_modules/@xtuc/ieee754": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", @@ -6751,17 +6628,6 @@ "node": ">=8.9" } }, - "node_modules/aes-decrypter": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/aes-decrypter/-/aes-decrypter-3.1.3.tgz", - "integrity": "sha512-VkG9g4BbhMBy+N5/XodDeV6F02chEk9IpgRTq/0bS80y4dzy79VH2Gtms02VXomf3HmyRe3yyJYkJ990ns+d6A==", - "dependencies": { - "@babel/runtime": "^7.12.5", - "@videojs/vhs-utils": "^3.0.5", - "global": "^4.4.0", - "pkcs7": "^1.0.4" - } - }, "node_modules/agent-base": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", @@ -9325,11 +9191,6 @@ "url": "https://github.com/fb55/entities?sponsor=1" } }, - "node_modules/dom-walk": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.2.tgz", - "integrity": "sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w==" - }, "node_modules/domelementtype": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", @@ -10549,9 +10410,9 @@ "integrity": "sha512-MWipKbbYiYI0UC7cl8m/i/IWTqfC8YXsqjzybjddLsFjStroQzsHXkc73JutMvBiXmOvapk+axIl79ig5t55Bw==" }, "node_modules/fast-xml-parser": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.4.0.tgz", - "integrity": "sha512-kLY3jFlwIYwBNDojclKsNAC12sfD6NwW74QB2CoNGPvtVxjliYehVunB3HYyNi+n4Tt1dAcgwYvmKF/Z18flqg==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.4.1.tgz", + "integrity": "sha512-xkjOecfnKGkSsOwtZ5Pz7Us/T6mrbPQrq0nh+aCO5V9nk5NLWmasAHumTKjiPJPWANe+kAZ84Jc8ooJkzZ88Sw==", "funding": [ { "type": "github", @@ -11339,15 +11200,6 @@ "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==" }, - "node_modules/global": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/global/-/global-4.4.0.tgz", - "integrity": "sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w==", - "dependencies": { - "min-document": "^2.19.0", - "process": "^0.11.10" - } - }, "node_modules/global-modules": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", @@ -12210,11 +12062,6 @@ "node": ">=8" } }, - "node_modules/individual": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/individual/-/individual-2.0.0.tgz", - "integrity": "sha512-pWt8hBCqJsUWI/HtcfWod7+N9SgAqyPEaF7JQjwzjn5vGrpg6aQ5qeAFQ7dx//UH4J1O+7xqew+gCeeFt6xN/g==" - }, "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -12499,11 +12346,6 @@ "node": ">=8" } }, - "node_modules/is-function": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-function/-/is-function-1.0.2.tgz", - "integrity": "sha512-lw7DUp0aWXYg+CBCN+JKkcE0Q2RayZnSvnZBlwgxHBQhqt5pZNVy4Ri7H9GmmXkdu7LUthszM+Tor1u/2iBcpQ==" - }, "node_modules/is-generator-fn": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", @@ -14064,11 +13906,6 @@ "resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-3.1.2.tgz", "integrity": "sha512-UfpWE/VZn0iP50d8cz9NrZLM9lSWhcJ+0Gt/nm4by88UL+J1SiKN8/5dkjMmbEzwL2CAe+67GsegCbIKtbp75A==" }, - "node_modules/keycode": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/keycode/-/keycode-2.2.1.tgz", - "integrity": "sha512-Rdgz9Hl9Iv4QKi8b0OlCRQEzp4AgVxyCtz5S/+VIHezDmrDhkp2N2TqBWOLz0/gbeREXOOiI9/4b8BY9uw2vFg==" - }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -14244,11 +14081,6 @@ "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" }, - "node_modules/lodash.flatten": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", - "integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==" - }, "node_modules/lodash.isequal": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", @@ -14281,11 +14113,6 @@ "integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==", "dev": true }, - "node_modules/lodash.throttle": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz", - "integrity": "sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==" - }, "node_modules/lodash.truncate": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", @@ -14353,16 +14180,6 @@ "lz-string": "bin/bin.js" } }, - "node_modules/m3u8-parser": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/m3u8-parser/-/m3u8-parser-4.8.0.tgz", - "integrity": "sha512-UqA2a/Pw3liR6Df3gwxrqghCP17OpPlQj6RBPLYygf/ZSQ4MoSgvdvhvt35qV+3NaaA0FSZx93Ix+2brT1U7cA==", - "dependencies": { - "@babel/runtime": "^7.12.5", - "@videojs/vhs-utils": "^3.0.5", - "global": "^4.4.0" - } - }, "node_modules/magic-string": { "version": "0.30.9", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.9.tgz", @@ -14663,14 +14480,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/min-document": { - "version": "2.19.0", - "resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.0.tgz", - "integrity": "sha512-9Wy1B3m3f66bPPmU5hdA4DR4PB2OfDU/+GS3yAB7IQozE3tqXaVv2zOjgla7MEGSRv95+ILmOuvhLkOK6wJtCQ==", - "dependencies": { - "dom-walk": "^0.1.0" - } - }, "node_modules/min-indent": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", @@ -14772,20 +14581,6 @@ "color-name": "^1.1.4" } }, - "node_modules/mpd-parser": { - "version": "0.22.1", - "resolved": "https://registry.npmjs.org/mpd-parser/-/mpd-parser-0.22.1.tgz", - "integrity": "sha512-fwBebvpyPUU8bOzvhX0VQZgSohncbgYwUyJJoTSNpmy7ccD2ryiCvM7oRkn/xQH5cv73/xU7rJSNCLjdGFor0Q==", - "dependencies": { - "@babel/runtime": "^7.12.5", - "@videojs/vhs-utils": "^3.0.5", - "@xmldom/xmldom": "^0.8.3", - "global": "^4.4.0" - }, - "bin": { - "mpd-to-m3u8-json": "bin/parse.js" - } - }, "node_modules/mrmime": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.0.tgz", @@ -14816,22 +14611,6 @@ "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==" }, - "node_modules/mux.js": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/mux.js/-/mux.js-6.0.1.tgz", - "integrity": "sha512-22CHb59rH8pWGcPGW5Og7JngJ9s+z4XuSlYvnxhLuc58cA1WqGDQPzuG8I+sPm1/p0CdgpzVTaKW408k5DNn8w==", - "dependencies": { - "@babel/runtime": "^7.11.2", - "global": "^4.4.0" - }, - "bin": { - "muxjs-transmux": "bin/transmux.js" - }, - "engines": { - "node": ">=8", - "npm": ">=5" - } - }, "node_modules/nanoid": { "version": "3.3.7", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", @@ -17898,17 +17677,6 @@ "node": ">= 6" } }, - "node_modules/pkcs7": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/pkcs7/-/pkcs7-1.0.4.tgz", - "integrity": "sha512-afRERtHn54AlwaF2/+LFszyAANTCggGilmcmILUzEjvs3XgFZT+xE6+QWQcAGmu4xajy+Xtj7acLOPdx5/eXWQ==", - "dependencies": { - "@babel/runtime": "^7.5.5" - }, - "bin": { - "pkcs7": "bin/cli.js" - } - }, "node_modules/pkg-dir": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-7.0.0.tgz", @@ -18823,14 +18591,6 @@ "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", "dev": true }, - "node_modules/process": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", - "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", - "engines": { - "node": ">= 0.6.0" - } - }, "node_modules/process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", @@ -19941,15 +19701,6 @@ "symbol-observable": "^1.2.0" } }, - "node_modules/redux-devtools-extension": { - "version": "2.13.9", - "resolved": "https://registry.npmjs.org/redux-devtools-extension/-/redux-devtools-extension-2.13.9.tgz", - "integrity": "sha512-cNJ8Q/EtjhQaZ71c8I9+BPySIBVEKssbPpskBfsXqb8HJ002A3KRVHfeRzwRo6mGPqsm7XuHTqNSNeS1Khig0A==", - "deprecated": "Package moved to @redux-devtools/extension.", - "peerDependencies": { - "redux": "^3.1.0 || ^4.0.0" - } - }, "node_modules/redux-logger": { "version": "3.0.6", "resolved": "https://registry.npmjs.org/redux-logger/-/redux-logger-3.0.6.tgz", @@ -20259,14 +20010,6 @@ "queue-microtask": "^1.2.2" } }, - "node_modules/rust-result": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/rust-result/-/rust-result-1.0.0.tgz", - "integrity": "sha512-6cJzSBU+J/RJCF063onnQf0cDUOHs9uZI1oroSGnHOph+CQTIJ5Pp2hK5kEQq1+7yE/EEWfulSNXAQ2jikPthA==", - "dependencies": { - "individual": "^2.0.0" - } - }, "node_modules/rxjs": { "version": "7.8.1", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", @@ -20311,14 +20054,6 @@ } ] }, - "node_modules/safe-json-parse": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/safe-json-parse/-/safe-json-parse-4.0.0.tgz", - "integrity": "sha512-RjZPPHugjK0TOzFrLZ8inw44s9bKox99/0AZW9o/BEQVrJfhI+fIHMErnPyRa89/yRXUUr93q+tiN6zhoVV4wQ==", - "dependencies": { - "rust-result": "^1.0.0" - } - }, "node_modules/safe-regex-test": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz", @@ -22516,11 +22251,6 @@ "requires-port": "^1.0.0" } }, - "node_modules/url-toolkit": { - "version": "2.2.5", - "resolved": "https://registry.npmjs.org/url-toolkit/-/url-toolkit-2.2.5.tgz", - "integrity": "sha512-mtN6xk+Nac+oyJ/PrI7tzfmomRVNFIWKUbG8jdYFt52hxbiReFAXIjYskvu64/dvuW71IcB7lV8l0HvZMac6Jg==" - }, "node_modules/use-callback-ref": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.2.tgz", @@ -22675,55 +22405,6 @@ "node": ">= 0.8" } }, - "node_modules/video-react": { - "version": "0.15.0", - "resolved": "https://registry.npmjs.org/video-react/-/video-react-0.15.0.tgz", - "integrity": "sha512-wF3BwG1qikkSX11nu0KsygxeWehzMaYpd4Uvy1sKLFnCNk794f9TPL4q/+mMmLJ8uYb5DSlgg6VraTHyihiMHQ==", - "dependencies": { - "@babel/runtime": "^7.4.5", - "classnames": "^2.2.6", - "lodash.throttle": "^4.1.1", - "prop-types": "^15.7.2", - "redux": "^4.0.1" - }, - "peerDependencies": { - "react": "^15.0.0 || ^16.0.0 || ^17.0.0", - "react-dom": "^15.0.0 || ^16.0.0 || ^17.0.0" - } - }, - "node_modules/video.js": { - "version": "7.21.6", - "resolved": "https://registry.npmjs.org/video.js/-/video.js-7.21.6.tgz", - "integrity": "sha512-m41TbODrUCToVfK1aljVd296CwDQnCRewpIm5tTXMuV87YYSGw1H+VDOaV45HlpcWSsTWWLF++InDgGJfthfUw==", - "dependencies": { - "@babel/runtime": "^7.12.5", - "@videojs/http-streaming": "2.16.3", - "@videojs/vhs-utils": "^3.0.4", - "@videojs/xhr": "2.6.0", - "aes-decrypter": "3.1.3", - "global": "^4.4.0", - "keycode": "^2.2.0", - "m3u8-parser": "4.8.0", - "mpd-parser": "0.22.1", - "mux.js": "6.0.1", - "safe-json-parse": "4.0.0", - "videojs-font": "3.2.0", - "videojs-vtt.js": "^0.15.5" - } - }, - "node_modules/videojs-font": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/videojs-font/-/videojs-font-3.2.0.tgz", - "integrity": "sha512-g8vHMKK2/JGorSfqAZQUmYYNnXmfec4MLhwtEFS+mMs2IDY398GLysy6BH6K+aS1KMNu/xWZ8Sue/X/mdQPliA==" - }, - "node_modules/videojs-vtt.js": { - "version": "0.15.5", - "resolved": "https://registry.npmjs.org/videojs-vtt.js/-/videojs-vtt.js-0.15.5.tgz", - "integrity": "sha512-yZbBxvA7QMYn15Lr/ZfhhLPrNpI/RmCSCqgIff57GC2gIrV5YfyzLfLyZMj0NnZSAz8syB4N0nHXpZg9MyrMOQ==", - "dependencies": { - "global": "^4.3.1" - } - }, "node_modules/w3c-keyname": { "version": "2.2.8", "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz", @@ -23548,7 +23229,6 @@ "version": "0.1.0", "peerDependencies": { "@edx/frontend-app-course-authoring": "*", - "@edx/frontend-lib-content-components": "*", "@edx/frontend-platform": "*", "@openedx/paragon": "*", "@reduxjs/toolkit": "*", diff --git a/package.json b/package.json index f3daa13742..7c59b263c6 100644 --- a/package.json +++ b/package.json @@ -37,6 +37,11 @@ "url": "https://github.com/openedx/frontend-app-course-authoring/issues" }, "dependencies": { + "@codemirror/lang-html": "^6.0.0", + "@codemirror/lang-xml": "^6.0.0", + "@codemirror/lint": "^6.2.1", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0", "@dnd-kit/core": "^6.1.0", "@dnd-kit/modifiers": "^7.0.0", "@dnd-kit/sortable": "^8.0.0", @@ -45,7 +50,6 @@ "@edx/frontend-component-footer": "^14.0.3", "@edx/frontend-component-header": "^5.3.3", "@edx/frontend-enterprise-hotjar": "^2.0.0", - "@edx/frontend-lib-content-components": "^2.6.0", "@edx/frontend-platform": "^8.0.3", "@edx/openedx-atlas": "^0.6.0", "@openedx-plugins/course-app-calculator": "file:plugins/course-apps/calculator", @@ -60,16 +64,22 @@ "@openedx-plugins/course-app-xpert_unit_summary": "file:plugins/course-apps/xpert_unit_summary", "@openedx/frontend-plugin-framework": "^1.2.1", "@openedx/paragon": "^22.5.1", + "@redux-devtools/extension": "^3.3.0", "@reduxjs/toolkit": "1.9.7", "@tanstack/react-query": "4.36.1", + "@tinymce/tinymce-react": "^3.14.0", "classnames": "2.5.1", + "codemirror": "^6.0.0", "email-validator": "2.0.4", + "fast-xml-parser": "^4.0.10", "file-saver": "^2.0.5", "formik": "2.4.6", + "frontend-components-tinymce-advanced-plugins": "^1.0.3", "jszip": "^3.10.1", "lodash": "4.17.21", "meilisearch": "^0.41.0", "moment": "2.30.1", + "moment-shortformat": "^2.1.0", "npm": "^10.8.1", "prop-types": "^15.8.1", "react": "17.0.2", @@ -77,6 +87,7 @@ "react-dom": "17.0.2", "react-error-boundary": "^4.0.13", "react-helmet": "^6.1.0", + "react-onclickoutside": "^6.13.0", "react-redux": "7.2.9", "react-responsive": "9.0.2", "react-router": "6.23.1", @@ -85,14 +96,20 @@ "react-textarea-autosize": "^8.5.3", "react-transition-group": "4.4.5", "redux": "4.0.5", + "redux-logger": "^3.0.6", + "redux-mock-store": "^1.5.4", + "redux-thunk": "^2.4.1", + "reselect": "^4.1.5", "start": "^5.1.0", + "tinymce": "^5.10.4", "universal-cookie": "^4.0.4", "uuid": "^3.4.0", + "xmlchecker": "^0.1.0", "yup": "0.31.1" }, "devDependencies": { "@edx/browserslist-config": "1.2.0", - "@edx/react-unit-test-utils": "2.1.1", + "@edx/react-unit-test-utils": "3.0.0", "@edx/reactifex": "^1.0.3", "@edx/stylelint-config-edx": "2.3.3", "@edx/typescript-config": "^1.0.1", @@ -101,6 +118,7 @@ "@testing-library/react": "12.1.5", "@testing-library/react-hooks": "^8.0.1", "@testing-library/user-event": "^13.2.1", + "@types/lodash": "^4.17.7", "axios": "^0.28.0", "axios-mock-adapter": "1.22.0", "eslint-import-resolver-webpack": "^0.13.8", diff --git a/plugins/course-apps/live/Settings.jsx b/plugins/course-apps/live/Settings.jsx index a8f09257b5..ed409d997b 100644 --- a/plugins/course-apps/live/Settings.jsx +++ b/plugins/course-apps/live/Settings.jsx @@ -3,10 +3,10 @@ import { useDispatch, useSelector } from 'react-redux'; import { camelCase } from 'lodash'; import { Icon } from '@openedx/paragon'; import { injectIntl, intlShape } from '@edx/frontend-platform/i18n'; -import { SelectableBox } from '@edx/frontend-lib-content-components'; import PropTypes from 'prop-types'; import * as Yup from 'yup'; import { useNavigate } from 'react-router-dom'; +import SelectableBox from 'CourseAuthoring/editors/sharedComponents/SelectableBox'; import AppSettingsModal from 'CourseAuthoring/pages-and-resources/app-settings-modal/AppSettingsModal'; import { useModel } from 'CourseAuthoring/generic/model-store'; import Loading from 'CourseAuthoring/generic/Loading'; diff --git a/plugins/course-apps/live/package.json b/plugins/course-apps/live/package.json index 50f38b725d..6fbd074feb 100644 --- a/plugins/course-apps/live/package.json +++ b/plugins/course-apps/live/package.json @@ -4,7 +4,6 @@ "description": "Live course configuration for courses using it", "peerDependencies": { "@edx/frontend-app-course-authoring": "*", - "@edx/frontend-lib-content-components": "*", "@edx/frontend-platform": "*", "@openedx/paragon": "*", "@reduxjs/toolkit": "*", diff --git a/renovate.json b/renovate.json index dae6f07d08..29e7a5d344 100644 --- a/renovate.json +++ b/renovate.json @@ -19,15 +19,6 @@ "matchPackagePatterns": ["@edx", "@openedx"], "matchUpdateTypes": ["minor", "patch"], "automerge": false - }, - { - "matchPackagePatterns": ["@edx/frontend-lib-content-components"], - "matchUpdateTypes": ["minor", "patch"], - "automerge": false, - "schedule": [ - "after 1am", - "before 11pm" - ] } ] } diff --git a/src/CourseAuthoringRoutes.test.jsx b/src/CourseAuthoringRoutes.test.jsx index 3a38fe7c24..b72e340c16 100644 --- a/src/CourseAuthoringRoutes.test.jsx +++ b/src/CourseAuthoringRoutes.test.jsx @@ -21,9 +21,10 @@ jest.mock('react-router-dom', () => ({ }), })); -// Mock the TinyMceWidget from frontend-lib-content-components -jest.mock('@edx/frontend-lib-content-components', () => ({ - TinyMceWidget: () =>
Widget
, +// Mock the TinyMceWidget +jest.mock('./editors/sharedComponents/TinyMceWidget', () => ({ + __esModule: true, // Required to mock a default export + default: () =>
Widget
, Footer: () =>
Footer
, prepareEditorRef: jest.fn(() => ({ refReady: true, diff --git a/src/advanced-settings/AdvancedSettings.jsx b/src/advanced-settings/AdvancedSettings.jsx index 565bec3b7c..67bd62b55f 100644 --- a/src/advanced-settings/AdvancedSettings.jsx +++ b/src/advanced-settings/AdvancedSettings.jsx @@ -6,7 +6,7 @@ import { } from '@openedx/paragon'; import { CheckCircle, Info, Warning } from '@openedx/paragon/icons'; import { FormattedMessage, injectIntl, intlShape } from '@edx/frontend-platform/i18n'; -import Placeholder from '@edx/frontend-lib-content-components'; +import Placeholder from '../editors/Placeholder'; import AlertProctoringError from '../generic/AlertProctoringError'; import { useModel } from '../generic/model-store'; diff --git a/src/certificates/Certificates.jsx b/src/certificates/Certificates.jsx index fd67f74542..ef199f0992 100644 --- a/src/certificates/Certificates.jsx +++ b/src/certificates/Certificates.jsx @@ -1,7 +1,7 @@ import { Helmet } from 'react-helmet'; import PropTypes from 'prop-types'; -import Placeholder from '@edx/frontend-lib-content-components'; +import Placeholder from '../editors/Placeholder'; import { RequestStatus } from '../data/constants'; import Loading from '../generic/Loading'; import useCertificates from './hooks/useCertificates'; diff --git a/src/content-tags-drawer/ContentTagsCollapsible.jsx b/src/content-tags-drawer/ContentTagsCollapsible.jsx index 66bd7e100a..345fda8e27 100644 --- a/src/content-tags-drawer/ContentTagsCollapsible.jsx +++ b/src/content-tags-drawer/ContentTagsCollapsible.jsx @@ -12,9 +12,10 @@ import { Icon, } from '@openedx/paragon'; import { Tag, KeyboardArrowDown, KeyboardArrowUp } from '@openedx/paragon/icons'; -import { SelectableBox } from '@edx/frontend-lib-content-components'; import { useIntl } from '@edx/frontend-platform/i18n'; import { debounce } from 'lodash'; + +import SelectableBox from '../editors/sharedComponents/SelectableBox'; import messages from './messages'; import ContentTagsDropDownSelector from './ContentTagsDropDownSelector'; diff --git a/src/content-tags-drawer/ContentTagsDropDownSelector.jsx b/src/content-tags-drawer/ContentTagsDropDownSelector.jsx index 394ab6b56e..cce94ca3dc 100644 --- a/src/content-tags-drawer/ContentTagsDropDownSelector.jsx +++ b/src/content-tags-drawer/ContentTagsDropDownSelector.jsx @@ -5,13 +5,13 @@ import { Spinner, Button, } from '@openedx/paragon'; -import { SelectableBox } from '@edx/frontend-lib-content-components'; import { useIntl, FormattedMessage } from '@edx/frontend-platform/i18n'; import { ArrowDropDown, ArrowDropUp, Add } from '@openedx/paragon/icons'; import PropTypes from 'prop-types'; -import messages from './messages'; +import SelectableBox from '../editors/sharedComponents/SelectableBox'; import { useTaxonomyTagsData } from './data/apiHooks'; +import messages from './messages'; const HighlightedText = ({ text, highlight }) => { if (!highlight) { diff --git a/src/course-outline/page-alerts/PageAlerts.jsx b/src/course-outline/page-alerts/PageAlerts.jsx index be7e981350..172e407749 100644 --- a/src/course-outline/page-alerts/PageAlerts.jsx +++ b/src/course-outline/page-alerts/PageAlerts.jsx @@ -4,7 +4,6 @@ import { uniqBy } from 'lodash'; import { getConfig } from '@edx/frontend-platform'; import { useDispatch, useSelector } from 'react-redux'; import { FormattedMessage, useIntl } from '@edx/frontend-platform/i18n'; -import { ErrorAlert } from '@edx/frontend-lib-content-components'; import { Campaign as CampaignIcon, InfoOutline as InfoOutlineIcon, @@ -16,6 +15,7 @@ import { } from '@openedx/paragon'; import { Link } from 'react-router-dom'; +import ErrorAlert from '../../editors/sharedComponents/ErrorAlerts/ErrorAlert'; import { RequestStatus } from '../../data/constants'; import AlertMessage from '../../generic/alert-message'; import AlertProctoringError from '../../generic/AlertProctoringError'; diff --git a/src/course-unit/CourseUnit.jsx b/src/course-unit/CourseUnit.jsx index 6857626d1c..2b54f876e6 100644 --- a/src/course-unit/CourseUnit.jsx +++ b/src/course-unit/CourseUnit.jsx @@ -7,8 +7,8 @@ import { getConfig } from '@edx/frontend-platform'; import { useIntl, injectIntl } from '@edx/frontend-platform/i18n'; import { Warning as WarningIcon } from '@openedx/paragon/icons'; import { SortableContext, verticalListSortingStrategy } from '@dnd-kit/sortable'; -import { DraggableList } from '@edx/frontend-lib-content-components'; +import DraggableList from '../editors/sharedComponents/DraggableList'; import { getProcessingNotification } from '../generic/processing-notification/data/selectors'; import SubHeader from '../generic/sub-header/SubHeader'; import { RequestStatus } from '../data/constants'; diff --git a/src/course-updates/CourseUpdates.test.jsx b/src/course-updates/CourseUpdates.test.jsx index 8fdc223309..387d3b3c26 100644 --- a/src/course-updates/CourseUpdates.test.jsx +++ b/src/course-updates/CourseUpdates.test.jsx @@ -44,8 +44,9 @@ jest.mock('@tinymce/tinymce-react', () => { }; }); -jest.mock('@edx/frontend-lib-content-components', () => ({ - TinyMceWidget: () =>
Widget
, +jest.mock('../editors/sharedComponents/TinyMceWidget', () => ({ + __esModule: true, // Required to mock a default export + default: () =>
Widget
, prepareEditorRef: jest.fn(() => ({ refReady: true, setEditorRef: jest.fn().mockName('prepareEditorRef.setEditorRef'), diff --git a/src/course-updates/update-form/UpdateForm.test.jsx b/src/course-updates/update-form/UpdateForm.test.jsx index 65eb81a4ff..b48e07374d 100644 --- a/src/course-updates/update-form/UpdateForm.test.jsx +++ b/src/course-updates/update-form/UpdateForm.test.jsx @@ -32,8 +32,9 @@ jest.mock('@tinymce/tinymce-react', () => { }; }); -jest.mock('@edx/frontend-lib-content-components', () => ({ - TinyMceWidget: () =>
Widget
, +jest.mock('../../editors/sharedComponents/TinyMceWidget', () => ({ + __esModule: true, // Required to mock a default export + default: () =>
Widget
, prepareEditorRef: jest.fn(() => ({ refReady: true, setEditorRef: jest.fn().mockName('prepareEditorRef.setEditorRef'), diff --git a/src/custom-pages/CustomPages.jsx b/src/custom-pages/CustomPages.jsx index 5d16dbf713..f5392abf8b 100644 --- a/src/custom-pages/CustomPages.jsx +++ b/src/custom-pages/CustomPages.jsx @@ -18,11 +18,9 @@ import { Container, } from '@openedx/paragon'; import { Add, SpinnerSimple } from '@openedx/paragon/icons'; -import Placeholder, { - DraggableList, - SortableItem, - ErrorAlert, -} from '@edx/frontend-lib-content-components'; +import Placeholder from '../editors/Placeholder'; +import DraggableList, { SortableItem } from '../editors/sharedComponents/DraggableList'; +import ErrorAlert from '../editors/sharedComponents/ErrorAlerts/ErrorAlert'; import { RequestStatus } from '../data/constants'; import { useModels, useModel } from '../generic/model-store'; diff --git a/src/custom-pages/EditModal.jsx b/src/custom-pages/EditModal.jsx index 0f540c74ec..e89b39b827 100644 --- a/src/custom-pages/EditModal.jsx +++ b/src/custom-pages/EditModal.jsx @@ -2,7 +2,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { getConfig } from '@edx/frontend-platform'; -import { EditorPage } from '@edx/frontend-lib-content-components'; +import EditorPage from '../editors/EditorPage'; const EditModal = ({ pageId, diff --git a/src/editors/Editor.jsx b/src/editors/Editor.jsx index 18ead54a01..044c773f26 100644 --- a/src/editors/Editor.jsx +++ b/src/editors/Editor.jsx @@ -8,7 +8,7 @@ import * as hooks from './hooks'; import supportedEditors from './supportedEditors'; -export const Editor = ({ +const Editor = ({ learningContextId, blockType, blockId, diff --git a/src/editors/Editor.test.jsx b/src/editors/Editor.test.jsx index d2f09629b0..fa19e60689 100644 --- a/src/editors/Editor.test.jsx +++ b/src/editors/Editor.test.jsx @@ -1,7 +1,8 @@ +import 'CourseAuthoring/editors/setupEditorTest'; import React from 'react'; import { useDispatch } from 'react-redux'; import { shallow } from '@edx/react-unit-test-utils'; -import { Editor } from './Editor'; +import Editor from './Editor'; import supportedEditors from './supportedEditors'; import * as hooks from './hooks'; import { blockTypes } from './data/constants/app'; diff --git a/src/editors/EditorContainer.jsx b/src/editors/EditorContainer.jsx index c9e821daf7..34ca5ba631 100644 --- a/src/editors/EditorContainer.jsx +++ b/src/editors/EditorContainer.jsx @@ -1,9 +1,10 @@ import React from 'react'; import PropTypes from 'prop-types'; import { useParams } from 'react-router-dom'; -import { EditorPage } from '@edx/frontend-lib-content-components'; import { getConfig } from '@edx/frontend-platform'; +import EditorPage from './EditorPage'; + const EditorContainer = ({ courseId, }) => { diff --git a/src/editors/EditorContainer.test.jsx b/src/editors/EditorContainer.test.jsx index bee812eff5..a6186050ae 100644 --- a/src/editors/EditorContainer.test.jsx +++ b/src/editors/EditorContainer.test.jsx @@ -2,8 +2,6 @@ import React from 'react'; import { shallow } from '@edx/react-unit-test-utils'; import EditorContainer from './EditorContainer'; -jest.mock('@edx/frontend-lib-content-components', () => ({ EditorPage: () => 'HeaderTitle' })); - jest.mock('react-router', () => ({ ...jest.requireActual('react-router'), // use actual for all non-hook parts useParams: () => ({ diff --git a/src/editors/EditorPage.jsx b/src/editors/EditorPage.jsx index 2b717d4116..60de6e1cc6 100644 --- a/src/editors/EditorPage.jsx +++ b/src/editors/EditorPage.jsx @@ -6,7 +6,7 @@ import store from './data/store'; import Editor from './Editor'; import ErrorBoundary from './sharedComponents/ErrorBoundary'; -export const EditorPage = ({ +const EditorPage = ({ courseId, blockType, blockId, diff --git a/src/editors/Placeholder.test.jsx b/src/editors/Placeholder.test.jsx index 1025e1ad33..186bc9d781 100644 --- a/src/editors/Placeholder.test.jsx +++ b/src/editors/Placeholder.test.jsx @@ -4,7 +4,7 @@ import TestRenderer from 'react-test-renderer'; import { AppContext } from '@edx/frontend-platform/react'; import { Context as ResponsiveContext } from 'react-responsive'; -import Placeholder from '../index'; +import Placeholder from './Placeholder'; describe('', () => { it('renders correctly', () => { diff --git a/src/editors/VideoSelector.jsx b/src/editors/VideoSelector.jsx index a427eb1e6b..4711b76cfc 100644 --- a/src/editors/VideoSelector.jsx +++ b/src/editors/VideoSelector.jsx @@ -4,7 +4,7 @@ import PropTypes from 'prop-types'; import VideoGallery from './containers/VideoGallery'; import * as hooks from './hooks'; -export const VideoSelector = ({ +const VideoSelector = ({ blockId, learningContextId, lmsEndpointUrl, diff --git a/src/editors/VideoSelector.test.jsx b/src/editors/VideoSelector.test.jsx index aa55253f8c..f3d0e60ae3 100644 --- a/src/editors/VideoSelector.test.jsx +++ b/src/editors/VideoSelector.test.jsx @@ -1,3 +1,4 @@ +import 'CourseAuthoring/editors/setupEditorTest'; import React from 'react'; import { useDispatch } from 'react-redux'; import { shallow } from '@edx/react-unit-test-utils'; diff --git a/src/editors/__snapshots__/EditorContainer.test.jsx.snap b/src/editors/__snapshots__/EditorContainer.test.jsx.snap index e6c223bef8..c742c7a606 100644 --- a/src/editors/__snapshots__/EditorContainer.test.jsx.snap +++ b/src/editors/__snapshots__/EditorContainer.test.jsx.snap @@ -9,6 +9,8 @@ exports[`Editor Container snapshots rendering correctly with expected Input 1`] blockType="html" courseId="cOuRsEId" lmsEndpointUrl="http://localhost:18000" + onClose={null} + returnFunction={null} studioEndpointUrl="http://localhost:18010" /> diff --git a/src/editors/__snapshots__/Placeholder.test.jsx.snap b/src/editors/__snapshots__/Placeholder.test.jsx.snap new file mode 100644 index 0000000000..f504cf0632 --- /dev/null +++ b/src/editors/__snapshots__/Placeholder.test.jsx.snap @@ -0,0 +1,13 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[` renders correctly 1`] = ` +
+

+ Under Construction +
+ Coming Soon +

+
+`; diff --git a/src/editors/containers/EditorContainer/components/EditorFooter/index.jsx b/src/editors/containers/EditorContainer/components/EditorFooter/index.jsx index 4376ab38d8..20c2f2d560 100644 --- a/src/editors/containers/EditorContainer/components/EditorFooter/index.jsx +++ b/src/editors/containers/EditorContainer/components/EditorFooter/index.jsx @@ -12,7 +12,7 @@ import { FormattedMessage, injectIntl, intlShape } from '@edx/frontend-platform/ import messages from './messages'; -export const EditorFooter = ({ +const EditorFooter = ({ clearSaveFailed, disableSave, onCancel, @@ -60,4 +60,5 @@ EditorFooter.propTypes = { intl: intlShape.isRequired, }; +export const EditorFooterInternal = EditorFooter; // For testing only export default injectIntl(EditorFooter); diff --git a/src/editors/containers/EditorContainer/components/EditorFooter/index.test.jsx b/src/editors/containers/EditorContainer/components/EditorFooter/index.test.jsx index 97f3f72dfa..aaa78980a2 100644 --- a/src/editors/containers/EditorContainer/components/EditorFooter/index.test.jsx +++ b/src/editors/containers/EditorContainer/components/EditorFooter/index.test.jsx @@ -1,8 +1,9 @@ +import 'CourseAuthoring/editors/setupEditorTest'; import React from 'react'; import { shallow } from '@edx/react-unit-test-utils'; -import { formatMessage } from '../../../../../testUtils'; -import { EditorFooter } from '.'; +import { formatMessage } from '../../../../testUtils'; +import { EditorFooterInternal as EditorFooter } from '.'; jest.mock('../../hooks', () => ({ nullMethod: jest.fn().mockName('hooks.nullMethod'), diff --git a/src/editors/containers/EditorContainer/components/TitleHeader/EditConfirmationButtons.jsx b/src/editors/containers/EditorContainer/components/TitleHeader/EditConfirmationButtons.jsx index bf2ca571d0..32d64e52c4 100644 --- a/src/editors/containers/EditorContainer/components/TitleHeader/EditConfirmationButtons.jsx +++ b/src/editors/containers/EditorContainer/components/TitleHeader/EditConfirmationButtons.jsx @@ -7,7 +7,7 @@ import { injectIntl, intlShape } from '@edx/frontend-platform/i18n'; import messages from './messages'; -export const EditConfirmationButtons = ({ +const EditConfirmationButtons = ({ updateTitle, cancelEdit, // injected @@ -37,4 +37,5 @@ EditConfirmationButtons.propTypes = { // injected intl: intlShape.isRequired, }; +export const EditConfirmationButtonsInternal = EditConfirmationButtons; // For testing only export default injectIntl(EditConfirmationButtons); diff --git a/src/editors/containers/EditorContainer/components/TitleHeader/EditConfirmationButtons.test.jsx b/src/editors/containers/EditorContainer/components/TitleHeader/EditConfirmationButtons.test.jsx index 65619c46c1..99361275a9 100644 --- a/src/editors/containers/EditorContainer/components/TitleHeader/EditConfirmationButtons.test.jsx +++ b/src/editors/containers/EditorContainer/components/TitleHeader/EditConfirmationButtons.test.jsx @@ -1,7 +1,9 @@ +import 'CourseAuthoring/editors/setupEditorTest'; import React from 'react'; import { shallow } from '@edx/react-unit-test-utils'; -import { formatMessage } from '../../../../../testUtils'; -import * as module from './EditConfirmationButtons'; +import { formatMessage } from '../../../../testUtils'; + +import { EditConfirmationButtonsInternal as EditConfirmationButtons } from './EditConfirmationButtons'; describe('EditConfirmationButtons', () => { const props = { @@ -11,7 +13,7 @@ describe('EditConfirmationButtons', () => { }; describe('snapshot', () => { test('snapshot', () => { - expect(shallow().snapshot).toMatchSnapshot(); + expect(shallow().snapshot).toMatchSnapshot(); }); }); }); diff --git a/src/editors/containers/EditorContainer/components/TitleHeader/EditableHeader.jsx b/src/editors/containers/EditorContainer/components/TitleHeader/EditableHeader.jsx index bacd05f250..fe51bcd337 100644 --- a/src/editors/containers/EditorContainer/components/TitleHeader/EditableHeader.jsx +++ b/src/editors/containers/EditorContainer/components/TitleHeader/EditableHeader.jsx @@ -2,9 +2,9 @@ import React from 'react'; import PropTypes from 'prop-types'; import { Form } from '@openedx/paragon'; -import EditConfirmationButtons from './EditConfirmationButtons'; +import { EditConfirmationButtonsInternal as EditConfirmationButtons } from './EditConfirmationButtons'; -export const EditableHeader = ({ +const EditableHeader = ({ handleChange, updateTitle, handleKeyDown, @@ -41,4 +41,5 @@ EditableHeader.propTypes = { cancelEdit: PropTypes.func.isRequired, }; +export const EditableHeaderInternal = EditableHeader; // For testing only export default EditableHeader; diff --git a/src/editors/containers/EditorContainer/components/TitleHeader/EditableHeader.test.jsx b/src/editors/containers/EditorContainer/components/TitleHeader/EditableHeader.test.jsx index 929000c2b3..66c1705856 100644 --- a/src/editors/containers/EditorContainer/components/TitleHeader/EditableHeader.test.jsx +++ b/src/editors/containers/EditorContainer/components/TitleHeader/EditableHeader.test.jsx @@ -1,8 +1,9 @@ +import 'CourseAuthoring/editors/setupEditorTest'; import React from 'react'; import { shallow } from '@edx/react-unit-test-utils'; import { Form } from '@openedx/paragon'; -import * as module from './EditableHeader'; -import EditConfirmationButtons from './EditConfirmationButtons'; +import { EditableHeaderInternal as EditableHeader } from './EditableHeader'; +import { EditConfirmationButtonsInternal as EditConfirmationButtons } from './EditConfirmationButtons'; describe('EditableHeader', () => { const props = { @@ -15,7 +16,7 @@ describe('EditableHeader', () => { }; let el; beforeEach(() => { - el = shallow(); + el = shallow(); }); describe('snapshot', () => { diff --git a/src/editors/containers/EditorContainer/components/TitleHeader/__snapshots__/EditableHeader.test.jsx.snap b/src/editors/containers/EditorContainer/components/TitleHeader/__snapshots__/EditableHeader.test.jsx.snap index d47c195548..0d202841d6 100644 --- a/src/editors/containers/EditorContainer/components/TitleHeader/__snapshots__/EditableHeader.test.jsx.snap +++ b/src/editors/containers/EditorContainer/components/TitleHeader/__snapshots__/EditableHeader.test.jsx.snap @@ -15,7 +15,7 @@ exports[`EditableHeader snapshot snapshot 1`] = ` } } trailingElement={ - diff --git a/src/editors/containers/EditorContainer/components/TitleHeader/hooks.js b/src/editors/containers/EditorContainer/components/TitleHeader/hooks.js index 62ac756a36..d64801c071 100644 --- a/src/editors/containers/EditorContainer/components/TitleHeader/hooks.js +++ b/src/editors/containers/EditorContainer/components/TitleHeader/hooks.js @@ -3,6 +3,10 @@ import { useSelector } from 'react-redux'; import { actions, selectors } from '../../../../data/redux'; import * as textEditorHooks from '../../hooks'; +// This 'module' self-import hack enables mocking during tests. +// See src/editors/decisions/0005-internal-editor-testability-decisions.md. The whole approach to how hooks are tested +// should be re-thought and cleaned up to avoid this pattern. +// eslint-disable-next-line import/no-self-import import * as module from './hooks'; export const { navigateCallback } = textEditorHooks; diff --git a/src/editors/containers/EditorContainer/components/TitleHeader/hooks.test.js b/src/editors/containers/EditorContainer/components/TitleHeader/hooks.test.js index 7bc6869553..8698282021 100644 --- a/src/editors/containers/EditorContainer/components/TitleHeader/hooks.test.js +++ b/src/editors/containers/EditorContainer/components/TitleHeader/hooks.test.js @@ -1,8 +1,9 @@ +import 'CourseAuthoring/editors/setupEditorTest'; import React from 'react'; import { useSelector } from 'react-redux'; import { actions, selectors } from '../../../../data/redux'; -import { MockUseState } from '../../../../../testUtils'; +import { MockUseState } from '../../../../testUtils'; import * as hooks from './hooks'; jest.mock('react', () => { diff --git a/src/editors/containers/EditorContainer/components/TitleHeader/index.jsx b/src/editors/containers/EditorContainer/components/TitleHeader/index.jsx index 1e63ce80cf..8e3f017bce 100644 --- a/src/editors/containers/EditorContainer/components/TitleHeader/index.jsx +++ b/src/editors/containers/EditorContainer/components/TitleHeader/index.jsx @@ -11,7 +11,7 @@ import { localTitleHooks } from './hooks'; import messages from './messages'; import EditableHeader from './EditableHeader'; -export const TitleHeader = ({ +const TitleHeader = ({ isInitialized, // injected intl, @@ -70,4 +70,5 @@ TitleHeader.propTypes = { intl: intlShape.isRequired, }; +export const TitleHeaderInternal = TitleHeader; // For testing only export default injectIntl(TitleHeader); diff --git a/src/editors/containers/EditorContainer/components/TitleHeader/index.test.jsx b/src/editors/containers/EditorContainer/components/TitleHeader/index.test.jsx index 0672d5b278..6366e1c758 100644 --- a/src/editors/containers/EditorContainer/components/TitleHeader/index.test.jsx +++ b/src/editors/containers/EditorContainer/components/TitleHeader/index.test.jsx @@ -1,10 +1,11 @@ +import 'CourseAuthoring/editors/setupEditorTest'; import React from 'react'; import { useDispatch } from 'react-redux'; import { shallow } from '@edx/react-unit-test-utils'; -import { formatMessage } from '../../../../../testUtils'; +import { formatMessage } from '../../../../testUtils'; import { localTitleHooks } from './hooks'; -import * as module from '.'; +import { TitleHeaderInternal as TitleHeader } from '.'; jest.mock('./hooks', () => ({ localTitleHooks: jest.fn(), @@ -37,7 +38,7 @@ describe('TitleHeader', () => { describe('behavior', () => { it(' calls localTitleHooks with initialization args', () => { localTitleHooks.mockReturnValue(localTitleHooksProps); - shallow(); + shallow(); const dispatch = useDispatch(); expect(localTitleHooks).toHaveBeenCalledWith({ dispatch, @@ -47,15 +48,15 @@ describe('TitleHeader', () => { describe('snapshots', () => { test('not initialized', () => { - expect(shallow().snapshot).toMatchSnapshot(); + expect(shallow().snapshot).toMatchSnapshot(); }); test('initialized', () => { localTitleHooks.mockReturnValue(localTitleHooksProps); - expect(shallow().shallowWrapper).toMatchSnapshot(); + expect(shallow().shallowWrapper).toMatchSnapshot(); }); test('editing', () => { localTitleHooks.mockReturnValue({ ...localTitleHooksProps, isEditing: true }); - expect(shallow().snapshot).toMatchSnapshot(); + expect(shallow().snapshot).toMatchSnapshot(); }); }); }); diff --git a/src/editors/containers/EditorContainer/hooks.js b/src/editors/containers/EditorContainer/hooks.js index caae0263d6..72b8a0e20f 100644 --- a/src/editors/containers/EditorContainer/hooks.js +++ b/src/editors/containers/EditorContainer/hooks.js @@ -6,6 +6,10 @@ import { RequestKeys } from '../../data/constants/requests'; import { selectors } from '../../data/redux'; import { StrictDict } from '../../utils'; import * as appHooks from '../../hooks'; +// This 'module' self-import hack enables mocking during tests. +// See src/editors/decisions/0005-internal-editor-testability-decisions.md. The whole approach to how hooks are tested +// should be re-thought and cleaned up to avoid this pattern. +// eslint-disable-next-line import/no-self-import import * as module from './hooks'; export const { diff --git a/src/editors/containers/EditorContainer/hooks.test.jsx b/src/editors/containers/EditorContainer/hooks.test.jsx index c2e4d46499..e73534f5b3 100644 --- a/src/editors/containers/EditorContainer/hooks.test.jsx +++ b/src/editors/containers/EditorContainer/hooks.test.jsx @@ -1,5 +1,6 @@ +import 'CourseAuthoring/editors/setupEditorTest'; import * as reactRedux from 'react-redux'; -import { MockUseState } from '../../../testUtils'; +import { MockUseState } from '../../testUtils'; import { RequestKeys } from '../../data/constants/requests'; import { selectors } from '../../data/redux'; diff --git a/src/editors/containers/EditorContainer/index.jsx b/src/editors/containers/EditorContainer/index.jsx index f568257a61..5c80cbce79 100644 --- a/src/editors/containers/EditorContainer/index.jsx +++ b/src/editors/containers/EditorContainer/index.jsx @@ -15,7 +15,7 @@ import * as hooks from './hooks'; import messages from './messages'; import './index.scss'; -export const EditorContainer = ({ +const EditorContainer = ({ children, getContent, onClose, @@ -99,4 +99,5 @@ EditorContainer.propTypes = { intl: intlShape.isRequired, }; +export const EditorContainerInternal = EditorContainer; // For testing only export default injectIntl(EditorContainer); diff --git a/src/editors/containers/EditorContainer/index.test.jsx b/src/editors/containers/EditorContainer/index.test.jsx index c0068955aa..5360fdb7de 100644 --- a/src/editors/containers/EditorContainer/index.test.jsx +++ b/src/editors/containers/EditorContainer/index.test.jsx @@ -1,9 +1,10 @@ +import 'CourseAuthoring/editors/setupEditorTest'; import { shallow } from '@edx/react-unit-test-utils'; import { useDispatch } from 'react-redux'; -import { EditorContainer } from '.'; +import { EditorContainerInternal as EditorContainer } from '.'; import * as hooks from './hooks'; -import { formatMessage } from '../../../testUtils'; +import { formatMessage } from '../../testUtils'; const props = { getContent: jest.fn().mockName('props.getContent'), diff --git a/src/editors/containers/GameEditor/index.jsx b/src/editors/containers/GameEditor/index.jsx index b6e0f9ad77..e14c3bad0f 100644 --- a/src/editors/containers/GameEditor/index.jsx +++ b/src/editors/containers/GameEditor/index.jsx @@ -1,3 +1,5 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable no-unused-vars */ /* eslint-disable import/extensions */ /* eslint-disable import/no-unresolved */ /** @@ -6,8 +8,6 @@ * To use run npm run-script addXblock */ -/* eslint-disable no-unused-vars */ - import React from 'react'; import { connect } from 'react-redux'; import PropTypes from 'prop-types'; @@ -16,6 +16,10 @@ import { Spinner } from '@openedx/paragon'; import { injectIntl, intlShape } from '@edx/frontend-platform/i18n'; import EditorContainer from '../EditorContainer'; +// This 'module' self-import hack enables mocking during tests. +// See src/editors/decisions/0005-internal-editor-testability-decisions.md. The whole approach to how hooks are tested +// should be re-thought and cleaned up to avoid this pattern. +// eslint-disable-next-line import/no-self-import import * as module from '.'; import { actions, selectors } from '../../data/redux'; import { RequestKeys } from '../../data/constants/requests'; diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/AnswerOption.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/AnswerOption.jsx index bac86c98ae..8ac30a31c4 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/AnswerOption.jsx +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/AnswerOption.jsx @@ -18,7 +18,7 @@ import * as hooks from './hooks'; import { ProblemTypeKeys } from '../../../../../data/constants/problem'; import ExpandableTextArea from '../../../../../sharedComponents/ExpandableTextArea'; -export const AnswerOption = ({ +const AnswerOption = ({ answer, hasSingleAnswer, // injected @@ -142,4 +142,5 @@ export const mapStateToProps = (state) => ({ }); export const mapDispatchToProps = {}; +export const AnswerOptionInternal = AnswerOption; // For testing only export default injectIntl(connect(mapStateToProps, mapDispatchToProps)(memo(AnswerOption))); diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/AnswerOption.test.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/AnswerOption.test.jsx index 862b3b2140..7ef58f6d9a 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/AnswerOption.test.jsx +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/AnswerOption.test.jsx @@ -1,8 +1,9 @@ +import 'CourseAuthoring/editors/setupEditorTest'; import React from 'react'; import { shallow } from '@edx/react-unit-test-utils'; -import { formatMessage } from '../../../../../../testUtils'; +import { formatMessage } from '../../../../../testUtils'; import { selectors } from '../../../../../data/redux'; -import { AnswerOption, mapStateToProps } from './AnswerOption'; +import { AnswerOptionInternal as AnswerOption, mapStateToProps } from './AnswerOption'; jest.mock('../../../../../data/redux', () => ({ __esModule: true, diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/AnswersContainer.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/AnswersContainer.jsx index 2b1efbe968..1a5d547cb4 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/AnswersContainer.jsx +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/AnswersContainer.jsx @@ -13,7 +13,7 @@ import AnswerOption from './AnswerOption'; import Button from '../../../../../sharedComponents/Button'; import { ProblemTypeKeys } from '../../../../../data/constants/problem'; -export const AnswersContainer = ({ +const AnswersContainer = ({ problemType, // Redux answers, @@ -95,4 +95,5 @@ export const mapDispatchToProps = { updateField: actions.problem.updateField, }; +export const AnswersContainerInternal = AnswersContainer; // For testing only export default connect(mapStateToProps, mapDispatchToProps)(AnswersContainer); diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/AnswersContainer.test.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/AnswersContainer.test.jsx index 1445c0cdd4..c52933af2d 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/AnswersContainer.test.jsx +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/AnswersContainer.test.jsx @@ -1,13 +1,12 @@ /* eslint-disable react/prop-types */ +import 'CourseAuthoring/editors/setupEditorTest'; import React from 'react'; import { shallow } from '@edx/react-unit-test-utils'; import { act, render, waitFor } from '@testing-library/react'; import { actions, selectors } from '../../../../../data/redux'; -import * as module from './AnswersContainer'; - -import { AnswersContainer as AnswersContainerWithoutHOC } from './AnswersContainer'; +import { AnswersContainerInternal as AnswersContainer, mapStateToProps, mapDispatchToProps } from './AnswersContainer'; import { ProblemTypeKeys } from '../../../../../data/constants/problem'; jest.mock('@edx/frontend-platform/i18n', () => ({ @@ -44,13 +43,13 @@ describe('AnswersContainer', () => { describe('render', () => { test('snapshot: renders correct default', () => { act(() => { - expect(shallow().snapshot).toMatchSnapshot(); + expect(shallow().snapshot).toMatchSnapshot(); }); }); test('snapshot: renders correctly with answers', () => { act(() => { expect(shallow( - , @@ -67,7 +66,7 @@ describe('AnswersContainer', () => { addAnswerRange: jest.fn(), }; expect(shallow( - , ).snapshot).toMatchSnapshot(); @@ -90,7 +89,7 @@ describe('AnswersContainer', () => { addAnswerRange: jest.fn(), }; expect(shallow( - , ).snapshot).toMatchSnapshot(); @@ -122,7 +121,7 @@ describe('AnswersContainer', () => { addAnswerRange: jest.fn(), }; expect(shallow( - , ).snapshot).toMatchSnapshot(); @@ -133,7 +132,7 @@ describe('AnswersContainer', () => { let container = null; await act(async () => { const wrapper = render( - , @@ -142,7 +141,7 @@ describe('AnswersContainer', () => { }); await waitFor(() => expect(container.querySelector('button')).toBeTruthy()); - await new Promise(resolve => setTimeout(resolve, 500)); + await new Promise(resolve => { setTimeout(resolve, 500); }); expect(props.updateField).toHaveBeenCalledWith(expect.objectContaining({ correctAnswerCount: 2 })); }); @@ -151,16 +150,16 @@ describe('AnswersContainer', () => { const testState = { A: 'pple', B: 'anana', C: 'ucumber' }; test('answers from problem.answers', () => { expect( - module.mapStateToProps(testState).answers, + mapStateToProps(testState).answers, ).toEqual(selectors.problem.answers(testState)); }); }); describe('mapDispatchToProps', () => { test('updateField from actions.problem.updateField', () => { - expect(module.mapDispatchToProps.updateField).toEqual(actions.problem.updateField); + expect(mapDispatchToProps.updateField).toEqual(actions.problem.updateField); }); test('updateField from actions.problem.addAnswer', () => { - expect(module.mapDispatchToProps.addAnswer).toEqual(actions.problem.addAnswer); + expect(mapDispatchToProps.addAnswer).toEqual(actions.problem.addAnswer); }); }); }); diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/components/Checker/index.test.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/components/Checker/index.test.jsx index 46653ced11..1f99057ac2 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/components/Checker/index.test.jsx +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/components/Checker/index.test.jsx @@ -1,3 +1,4 @@ +import 'CourseAuthoring/editors/setupEditorTest'; import { shallow } from '@edx/react-unit-test-utils'; import Checker from '.'; diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/components/Feedback/FeedbackControl.test.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/components/Feedback/FeedbackControl.test.jsx index 13a850af02..7fb51bf491 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/components/Feedback/FeedbackControl.test.jsx +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/components/Feedback/FeedbackControl.test.jsx @@ -1,3 +1,4 @@ +import 'CourseAuthoring/editors/setupEditorTest'; import { shallow } from '@edx/react-unit-test-utils'; import FeedbackControl from './FeedbackControl'; diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/hooks.js b/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/hooks.js index 0d7b423f6b..b1e9585cf6 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/hooks.js +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/hooks.js @@ -1,5 +1,9 @@ import { useState, useEffect } from 'react'; import { StrictDict } from '../../../../../utils'; +// This 'module' self-import hack enables mocking during tests. +// See src/editors/decisions/0005-internal-editor-testability-decisions.md. The whole approach to how hooks are tested +// should be re-thought and cleaned up to avoid this pattern. +// eslint-disable-next-line import/no-self-import import * as module from './hooks'; import { actions } from '../../../../../data/redux'; import { ProblemTypeKeys } from '../../../../../data/constants/problem'; diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/hooks.test.js b/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/hooks.test.js index 6aa791c6a3..9614e635f3 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/hooks.test.js +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/hooks.test.js @@ -1,9 +1,14 @@ +import 'CourseAuthoring/editors/setupEditorTest'; import { useEffect } from 'react'; import { useDispatch } from 'react-redux'; import { actions } from '../../../../../data/redux'; -import { MockUseState } from '../../../../../../testUtils'; +import { MockUseState } from '../../../../../testUtils'; import { ProblemTypeKeys } from '../../../../../data/constants/problem'; +// This 'module' self-import hack enables mocking during tests. +// See src/editors/decisions/0005-internal-editor-testability-decisions.md. The whole approach to how hooks are tested +// should be re-thought and cleaned up to avoid this pattern. +// eslint-disable-next-line import/no-self-import import * as module from './hooks'; jest.mock('react', () => { diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/index.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/index.jsx index 4e3045262b..c7e865212e 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/index.jsx +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/AnswerWidget/index.jsx @@ -34,4 +34,5 @@ AnswerWidget.propTypes = { // injected intl: intlShape.isRequired, }; +export const AnswerWidgetInternal = AnswerWidget; // For testing only export default injectIntl(AnswerWidget); diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/ExplanationWidget/index.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/ExplanationWidget/index.jsx index 86298d5d95..16bdbab9a7 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/ExplanationWidget/index.jsx +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/ExplanationWidget/index.jsx @@ -8,7 +8,7 @@ import messages from './messages'; import TinyMceWidget from '../../../../../sharedComponents/TinyMceWidget'; import { prepareEditorRef, replaceStaticWithAsset } from '../../../../../sharedComponents/TinyMceWidget/hooks'; -export const ExplanationWidget = ({ +const ExplanationWidget = ({ // redux settings, learningContextId, @@ -57,4 +57,5 @@ export const mapStateToProps = (state) => ({ learningContextId: selectors.app.learningContextId(state), }); +export const ExplanationWidgetInternal = ExplanationWidget; // For testing only export default injectIntl(connect(mapStateToProps)(ExplanationWidget)); diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/ExplanationWidget/index.test.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/ExplanationWidget/index.test.jsx index 6e22712c2c..062330c190 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/ExplanationWidget/index.test.jsx +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/ExplanationWidget/index.test.jsx @@ -1,8 +1,9 @@ +import 'CourseAuthoring/editors/setupEditorTest'; import React from 'react'; import { shallow } from '@edx/react-unit-test-utils'; -import { formatMessage } from '../../../../../../testUtils'; +import { formatMessage } from '../../../../../testUtils'; import { selectors } from '../../../../../data/redux'; -import { ExplanationWidget, mapStateToProps } from '.'; +import { ExplanationWidgetInternal as ExplanationWidget, mapStateToProps } from '.'; jest.mock('../../../../../data/redux', () => ({ __esModule: true, diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/QuestionWidget/index.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/QuestionWidget/index.jsx index 5580975bf7..a0ecde82a1 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/QuestionWidget/index.jsx +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/QuestionWidget/index.jsx @@ -8,7 +8,7 @@ import messages from './messages'; import TinyMceWidget from '../../../../../sharedComponents/TinyMceWidget'; import { prepareEditorRef, replaceStaticWithAsset } from '../../../../../sharedComponents/TinyMceWidget/hooks'; -export const QuestionWidget = ({ +const QuestionWidget = ({ // redux question, learningContextId, @@ -53,4 +53,5 @@ export const mapStateToProps = (state) => ({ learningContextId: selectors.app.learningContextId(state), }); +export const QuestionWidgetInternal = QuestionWidget; // For testing only export default injectIntl(connect(mapStateToProps)(QuestionWidget)); diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/QuestionWidget/index.test.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/QuestionWidget/index.test.jsx index 6602eb1ae2..c867da970a 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/QuestionWidget/index.test.jsx +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/QuestionWidget/index.test.jsx @@ -1,8 +1,9 @@ +import 'CourseAuthoring/editors/setupEditorTest'; import React from 'react'; import { shallow } from '@edx/react-unit-test-utils'; -import { formatMessage } from '../../../../../../testUtils'; +import { formatMessage } from '../../../../../testUtils'; import { selectors } from '../../../../../data/redux'; -import { QuestionWidget, mapStateToProps } from '.'; +import { QuestionWidgetInternal as QuestionWidget, mapStateToProps } from '.'; jest.mock('../../../../../data/redux', () => ({ __esModule: true, diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/CardSection.test.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/CardSection.test.jsx index 2c5bc69604..a11f9ea276 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/CardSection.test.jsx +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/CardSection.test.jsx @@ -1,3 +1,4 @@ +import 'CourseAuthoring/editors/setupEditorTest'; import { shallow } from '@edx/react-unit-test-utils'; import CardSection from './CardSection'; diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/SettingsOption.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/SettingsOption.jsx index d04d94bec2..929bedb245 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/SettingsOption.jsx +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/SettingsOption.jsx @@ -11,7 +11,7 @@ import { import { showFullCard } from './hooks'; import CardSection from './CardSection'; -export const SettingsOption = ({ +const SettingsOption = ({ title, className, extraSections, children, summary, hasExpandableTextArea, ...passThroughProps }) => { const { isCardCollapsibleOpen, toggleCardCollapse } = showFullCard(hasExpandableTextArea); diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/SettingsOption.test.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/SettingsOption.test.jsx index e7c3074e78..5c9c1a17e8 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/SettingsOption.test.jsx +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/SettingsOption.test.jsx @@ -1,3 +1,4 @@ +import 'CourseAuthoring/editors/setupEditorTest'; import React from 'react'; import { shallow } from '@edx/react-unit-test-utils'; import SettingsOption from './SettingsOption'; diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/hooks.js b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/hooks.js index a76fef4221..1fb8fa6a8c 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/hooks.js +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/hooks.js @@ -1,6 +1,10 @@ import { useState, useEffect } from 'react'; -import _ from 'lodash-es'; +import _ from 'lodash'; +// This 'module' self-import hack enables mocking during tests. +// See src/editors/decisions/0005-internal-editor-testability-decisions.md. The whole approach to how hooks are tested +// should be re-thought and cleaned up to avoid this pattern. +// eslint-disable-next-line import/no-self-import import * as module from './hooks'; import messages from './messages'; import { @@ -129,7 +133,7 @@ export const scoringCardHooks = (scoring, updateSettings, defaultValue) => { const handleMaxAttemptChange = (event) => { let unlimitedAttempts = false; - let attemptNumber = parseInt(event.target.value); + let attemptNumber = parseInt(event.target.value, 10); if (!_.isFinite(attemptNumber) || attemptNumber === defaultValue) { attemptNumber = null; @@ -147,7 +151,7 @@ export const scoringCardHooks = (scoring, updateSettings, defaultValue) => { }; const handleOnChange = (event) => { - let newMaxAttempt = parseInt(event.target.value); + let newMaxAttempt = parseInt(event.target.value, 10); if (newMaxAttempt === defaultValue) { newMaxAttempt = `${defaultValue} (Default)`; } else if (_.isNaN(newMaxAttempt)) { @@ -193,7 +197,7 @@ export const useAnswerSettings = (showAnswer, updateSettings) => { }; const handleAttemptsChange = (event) => { - let attempts = parseInt(event.target.value); + let attempts = parseInt(event.target.value, 10); if (_.isNaN(attempts)) { attempts = 0; } @@ -209,7 +213,7 @@ export const useAnswerSettings = (showAnswer, updateSettings) => { export const timerCardHooks = (updateSettings) => ({ handleChange: (event) => { - let time = parseInt(event.target.value); + let time = parseInt(event.target.value, 10); if (_.isNaN(time)) { time = 0; } diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/hooks.test.js b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/hooks.test.js index 8b636c0a88..2576025a99 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/hooks.test.js +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/hooks.test.js @@ -1,5 +1,5 @@ import { useEffect } from 'react'; -import { MockUseState } from '../../../../../../testUtils'; +import { MockUseState } from '../../../../../testUtils'; import messages from './messages'; import { keyStore } from '../../../../../utils'; import * as hooks from './hooks'; @@ -286,7 +286,9 @@ describe('Problem settings hooks', () => { test('test handleAttemptsChange', () => { const value = 3; output.handleAttemptsChange({ target: { value } }); - expect(updateSettings).toHaveBeenCalledWith({ showAnswer: { ...showAnswer, afterAttempts: parseInt(value) } }); + expect(updateSettings).toHaveBeenCalledWith({ + showAnswer: { ...showAnswer, afterAttempts: parseInt(value, 10) }, + }); }); }); diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/index.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/index.jsx index 32be62ce1d..6c858b7361 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/index.jsx +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/index.jsx @@ -23,7 +23,7 @@ import { ProblemTypeKeys } from '../../../../../data/constants/problem'; import Randomization from './settingsComponents/Randomization'; // This widget should be connected, grab all settings from store, update them as needed. -export const SettingsWidget = ({ +const SettingsWidget = ({ problemType, // redux answers, @@ -192,4 +192,5 @@ export const mapDispatchToProps = { updateAnswer: actions.problem.updateAnswer, }; +export const SettingsWidgetInternal = SettingsWidget; // For testing only export default injectIntl(connect(mapStateToProps, mapDispatchToProps)(SettingsWidget)); diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/index.scss b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/index.scss index ec4cee4412..ad6d2705a5 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/index.scss +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/index.scss @@ -1,5 +1,5 @@ .settingsCardTitleSection { - padding-bottom: 0rem; + padding-bottom: 0; } .halfSpacedMessage { @@ -12,6 +12,7 @@ .settingsWidget { margin-top: 40px; + .pgn__form-text { font-size: small; } diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/index.test.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/index.test.jsx index 9a95851a58..a432a5730d 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/index.test.jsx +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/index.test.jsx @@ -1,7 +1,8 @@ +import 'CourseAuthoring/editors/setupEditorTest'; import React from 'react'; import { shallow } from '@edx/react-unit-test-utils'; import { showAdvancedSettingsCards } from './hooks'; -import { SettingsWidget, mapDispatchToProps } from '.'; +import { SettingsWidgetInternal as SettingsWidget, mapDispatchToProps } from '.'; import { ProblemTypeKeys } from '../../../../../data/constants/problem'; import { actions } from '../../../../../data/redux'; diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/GeneralFeedback/hooks.js b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/GeneralFeedback/hooks.js index cd4f8a77bb..c168392b37 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/GeneralFeedback/hooks.js +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/GeneralFeedback/hooks.js @@ -1,6 +1,10 @@ import { useState, useEffect } from 'react'; -import _ from 'lodash-es'; +import _ from 'lodash'; import messages from './messages'; +// This 'module' self-import hack enables mocking during tests. +// See src/editors/decisions/0005-internal-editor-testability-decisions.md. The whole approach to how hooks are tested +// should be re-thought and cleaned up to avoid this pattern. +// eslint-disable-next-line import/no-self-import import * as module from './hooks'; export const state = { diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/GeneralFeedback/hooks.test.js b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/GeneralFeedback/hooks.test.js index d866353bb4..5d5c98d528 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/GeneralFeedback/hooks.test.js +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/GeneralFeedback/hooks.test.js @@ -1,5 +1,5 @@ import { useEffect } from 'react'; -import { MockUseState } from '../../../../../../../../testUtils'; +import { MockUseState } from '../../../../../../../testUtils'; import messages from './messages'; import * as hooks from './hooks'; diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/GeneralFeedback/index.test.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/GeneralFeedback/index.test.jsx index af5befed9e..f24152c649 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/GeneralFeedback/index.test.jsx +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/GeneralFeedback/index.test.jsx @@ -1,6 +1,7 @@ +import 'CourseAuthoring/editors/setupEditorTest'; import React from 'react'; import { shallow } from '@edx/react-unit-test-utils'; -import { formatMessage } from '../../../../../../../../testUtils'; +import { formatMessage } from '../../../../../../../testUtils'; import { GeneralFeedbackCard } from './index'; import { generalFeedbackHooks } from './hooks'; diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/GroupFeedback/GroupFeedbackRow.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/GroupFeedback/GroupFeedbackRow.jsx index 9d1ebff4c9..53e232cbc9 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/GroupFeedback/GroupFeedbackRow.jsx +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/GroupFeedback/GroupFeedbackRow.jsx @@ -7,7 +7,7 @@ import { DeleteOutline } from '@openedx/paragon/icons'; import PropTypes from 'prop-types'; import messages from '../../messages'; -export const GroupFeedbackRow = ({ +const GroupFeedbackRow = ({ value, handleAnswersSelectedChange, handleFeedbackChange, @@ -76,4 +76,5 @@ GroupFeedbackRow.propTypes = { intl: intlShape.isRequired, }; +export const GroupFeedbackRowInternal = GroupFeedbackRow; // For testing only export default injectIntl(GroupFeedbackRow); diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/GroupFeedback/GroupFeedbackRow.test.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/GroupFeedback/GroupFeedbackRow.test.jsx index ea29dd2f40..93be2e2664 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/GroupFeedback/GroupFeedbackRow.test.jsx +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/GroupFeedback/GroupFeedbackRow.test.jsx @@ -1,7 +1,7 @@ import React from 'react'; import { shallow } from '@edx/react-unit-test-utils'; -import { formatMessage } from '../../../../../../../../testUtils'; -import { GroupFeedbackRow } from './GroupFeedbackRow'; +import { formatMessage } from '../../../../../../../testUtils'; +import { GroupFeedbackRowInternal as GroupFeedbackRow } from './GroupFeedbackRow'; jest.mock('@openedx/paragon', () => ({ ...jest.requireActual('@openedx/paragon'), diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/GroupFeedback/hooks.js b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/GroupFeedback/hooks.js index 71956ee541..4dbe89c00c 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/GroupFeedback/hooks.js +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/GroupFeedback/hooks.js @@ -1,6 +1,10 @@ import { useState, useEffect } from 'react'; -import _ from 'lodash-es'; +import { isEmpty } from 'lodash'; import messages from './messages'; +// This 'module' self-import hack enables mocking during tests. +// See src/editors/decisions/0005-internal-editor-testability-decisions.md. The whole approach to how hooks are tested +// should be re-thought and cleaned up to avoid this pattern. +// eslint-disable-next-line import/no-self-import import * as module from './hooks'; export const state = { @@ -30,7 +34,7 @@ export const groupFeedbackCardHooks = (groupFeedbacks, updateSettings, answersli const handleAdd = () => { let newId = 0; - if (!_.isEmpty(groupFeedbacks)) { + if (!isEmpty(groupFeedbacks)) { newId = Math.max(...groupFeedbacks.map(feedback => feedback.id)) + 1; } const groupFeedback = { id: newId, answers: [], feedback: '' }; diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/GroupFeedback/hooks.test.js b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/GroupFeedback/hooks.test.js index 91728e55aa..14adde6d3b 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/GroupFeedback/hooks.test.js +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/GroupFeedback/hooks.test.js @@ -1,5 +1,5 @@ import { useEffect } from 'react'; -import { MockUseState } from '../../../../../../../../testUtils'; +import { MockUseState } from '../../../../../../../testUtils'; import messages from './messages'; import * as hooks from './hooks'; diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/GroupFeedback/index.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/GroupFeedback/index.jsx index 3fe4a7ff4f..531be7bad4 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/GroupFeedback/index.jsx +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/GroupFeedback/index.jsx @@ -7,7 +7,7 @@ import { groupFeedbackCardHooks, groupFeedbackRowHooks } from './hooks'; import GroupFeedbackRow from './GroupFeedbackRow'; import Button from '../../../../../../../sharedComponents/Button'; -export const GroupFeedbackCard = ({ +const GroupFeedbackCard = ({ groupFeedbacks, updateSettings, answers, @@ -63,4 +63,5 @@ GroupFeedbackCard.propTypes = { updateSettings: PropTypes.func.isRequired, }; +export const GroupFeedbackCardInternal = GroupFeedbackCard; // For testing only export default injectIntl(GroupFeedbackCard); diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/GroupFeedback/index.test.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/GroupFeedback/index.test.jsx index b3c7c1d679..2c52195242 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/GroupFeedback/index.test.jsx +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/GroupFeedback/index.test.jsx @@ -1,7 +1,8 @@ +import 'CourseAuthoring/editors/setupEditorTest'; import React from 'react'; import { shallow } from '@edx/react-unit-test-utils'; -import { formatMessage } from '../../../../../../../../testUtils'; -import { GroupFeedbackCard } from './index'; +import { formatMessage } from '../../../../../../../testUtils'; +import { GroupFeedbackCardInternal as GroupFeedbackCard } from './index'; import { groupFeedbackRowHooks, groupFeedbackCardHooks } from './hooks'; import messages from './messages'; diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/HintRow.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/HintRow.jsx index 7f8e8b7333..55817d51a1 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/HintRow.jsx +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/HintRow.jsx @@ -11,7 +11,7 @@ import PropTypes from 'prop-types'; import messages from '../messages'; import ExpandableTextArea from '../../../../../../sharedComponents/ExpandableTextArea'; -export const HintRow = ({ +const HintRow = ({ value, handleChange, handleDelete, @@ -49,4 +49,5 @@ HintRow.propTypes = { intl: intlShape.isRequired, }; +export const HintRowInternal = HintRow; // For testing only export default injectIntl(HintRow); diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/HintRow.test.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/HintRow.test.jsx index e0d22b5878..3d7673ec9f 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/HintRow.test.jsx +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/HintRow.test.jsx @@ -1,7 +1,8 @@ +import 'CourseAuthoring/editors/setupEditorTest'; import React from 'react'; import { shallow } from '@edx/react-unit-test-utils'; -import { formatMessage } from '../../../../../../../testUtils'; -import { HintRow } from './HintRow'; +import { formatMessage } from '../../../../../../testUtils'; +import { HintRowInternal as HintRow } from './HintRow'; describe('HintRow', () => { const props = { diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/HintsCard.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/HintsCard.jsx index de29ba8ff1..cdb399d91f 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/HintsCard.jsx +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/HintsCard.jsx @@ -8,7 +8,7 @@ import { hintsCardHooks, hintsRowHooks } from '../hooks'; import HintRow from './HintRow'; import Button from '../../../../../../sharedComponents/Button'; -export const HintsCard = ({ +const HintsCard = ({ hints, problemType, updateSettings, @@ -56,4 +56,5 @@ HintsCard.propTypes = { updateSettings: PropTypes.func.isRequired, }; +export const HintsCardInternal = HintsCard; // For testing only export default injectIntl(HintsCard); diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/HintsCard.test.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/HintsCard.test.jsx index e1c3e8998b..3105445b6b 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/HintsCard.test.jsx +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/HintsCard.test.jsx @@ -1,7 +1,8 @@ +import 'CourseAuthoring/editors/setupEditorTest'; import React from 'react'; import { shallow } from '@edx/react-unit-test-utils'; -import { formatMessage } from '../../../../../../../testUtils'; -import { HintsCard } from './HintsCard'; +import { formatMessage } from '../../../../../../testUtils'; +import { HintsCardInternal as HintsCard } from './HintsCard'; import { hintsCardHooks, hintsRowHooks } from '../hooks'; import messages from '../messages'; diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/Randomization/hooks.js b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/Randomization/hooks.js index 28c5e8fef9..888488895c 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/Randomization/hooks.js +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/Randomization/hooks.js @@ -1,5 +1,9 @@ import { useState, useEffect } from 'react'; import { RandomizationTypes, RandomizationTypesKeys } from '../../../../../../../data/constants/problem'; +// This 'module' self-import hack enables mocking during tests. +// See src/editors/decisions/0005-internal-editor-testability-decisions.md. The whole approach to how hooks are tested +// should be re-thought and cleaned up to avoid this pattern. +// eslint-disable-next-line import/no-self-import import * as module from './hooks'; export const state = { diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/Randomization/hooks.test.js b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/Randomization/hooks.test.js index cd202f555b..dc4cf2cb1c 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/Randomization/hooks.test.js +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/Randomization/hooks.test.js @@ -1,5 +1,5 @@ import { useEffect } from 'react'; -import { MockUseState } from '../../../../../../../../testUtils'; +import { MockUseState } from '../../../../../../../testUtils'; import * as hooks from './hooks'; import { RandomizationTypes, RandomizationTypesKeys } from '../../../../../../../data/constants/problem'; diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/Randomization/index.test.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/Randomization/index.test.jsx index 8dac761f53..d3438f6196 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/Randomization/index.test.jsx +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/Randomization/index.test.jsx @@ -1,6 +1,7 @@ +import 'CourseAuthoring/editors/setupEditorTest'; import React from 'react'; import { shallow } from '@edx/react-unit-test-utils'; -import { formatMessage } from '../../../../../../../../testUtils'; +import { formatMessage } from '../../../../../../../testUtils'; import { RandomizationCard } from './index'; import { useRandomizationSettingStatus } from './hooks'; diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/ResetCard.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/ResetCard.jsx index ef132a2539..1413f092e9 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/ResetCard.jsx +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/ResetCard.jsx @@ -8,7 +8,7 @@ import messages from '../messages'; import { resetCardHooks } from '../hooks'; import { selectors } from '../../../../../../data/redux'; -export const ResetCard = ({ +const ResetCard = ({ showResetButton, defaultValue, updateSettings, @@ -58,4 +58,5 @@ ResetCard.propTypes = { intl: intlShape.isRequired, }; +export const ResetCardInternal = ResetCard; // For testing only export default injectIntl(ResetCard); diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/ResetCard.test.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/ResetCard.test.jsx index 5802d6d11d..d3c46314fb 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/ResetCard.test.jsx +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/ResetCard.test.jsx @@ -1,7 +1,9 @@ +import 'CourseAuthoring/editors/setupEditorTest'; import React from 'react'; import { shallow } from '@edx/react-unit-test-utils'; -import { formatMessage } from '../../../../../../../testUtils'; -import { ResetCard } from './ResetCard'; +import { useSelector } from 'react-redux'; +import { formatMessage } from '../../../../../../testUtils'; +import { ResetCardInternal as ResetCard } from './ResetCard'; import { resetCardHooks } from '../hooks'; jest.mock('../hooks', () => ({ @@ -17,9 +19,7 @@ jest.mock('../../../../../../data/redux', () => ({ }, })); -jest.mock('react-redux', () => ({ - useSelector: jest.fn().mockImplementation((args) => args), -})); +useSelector.mockImplementation((args) => args); describe('ResetCard', () => { const props = { diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/ScoringCard.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/ScoringCard.jsx index 60dc034476..d7710313c1 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/ScoringCard.jsx +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/ScoringCard.jsx @@ -1,5 +1,5 @@ import React from 'react'; -import _ from 'lodash-es'; +import _ from 'lodash'; import PropTypes from 'prop-types'; import { connect } from 'react-redux'; import { FormattedMessage, injectIntl, intlShape } from '@edx/frontend-platform/i18n'; @@ -9,7 +9,7 @@ import SettingsOption from '../SettingsOption'; import messages from '../messages'; import { scoringCardHooks } from '../hooks'; -export const ScoringCard = ({ +const ScoringCard = ({ scoring, defaultValue, updateSettings, @@ -113,4 +113,5 @@ export const mapStateToProps = (state) => ({ export const mapDispatchToProps = {}; +export const ScoringCardInternal = ScoringCard; // For testing only export default injectIntl(connect(mapStateToProps, mapDispatchToProps)(ScoringCard)); diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/ScoringCard.test.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/ScoringCard.test.jsx index e95fcbce7c..29d8e922cf 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/ScoringCard.test.jsx +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/ScoringCard.test.jsx @@ -1,8 +1,9 @@ +import 'CourseAuthoring/editors/setupEditorTest'; import React from 'react'; import { shallow } from '@edx/react-unit-test-utils'; -import { formatMessage } from '../../../../../../../testUtils'; +import { formatMessage } from '../../../../../../testUtils'; import { scoringCardHooks } from '../hooks'; -import { ScoringCard } from './ScoringCard'; +import { ScoringCardInternal as ScoringCard } from './ScoringCard'; jest.mock('../hooks', () => ({ scoringCardHooks: jest.fn(), diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/ShowAnswerCard.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/ShowAnswerCard.jsx index 0d06ab63b5..eb2690194f 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/ShowAnswerCard.jsx +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/ShowAnswerCard.jsx @@ -9,7 +9,7 @@ import { selectors } from '../../../../../../data/redux'; import messages from '../messages'; import { useAnswerSettings } from '../hooks'; -export const ShowAnswerCard = ({ +const ShowAnswerCard = ({ showAnswer, updateSettings, defaultValue, @@ -113,4 +113,5 @@ export const mapStateToProps = (state) => ({ export const mapDispatchToProps = {}; +export const ShowAnswerCardInternal = ShowAnswerCard; // For testing only export default injectIntl(connect(mapStateToProps, mapDispatchToProps)(ShowAnswerCard)); diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/ShowAnswerCard.test.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/ShowAnswerCard.test.jsx index ae1c850e11..dae004510b 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/ShowAnswerCard.test.jsx +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/ShowAnswerCard.test.jsx @@ -1,8 +1,9 @@ +import 'CourseAuthoring/editors/setupEditorTest'; import React from 'react'; import { shallow } from '@edx/react-unit-test-utils'; -import { formatMessage } from '../../../../../../../testUtils'; +import { formatMessage } from '../../../../../../testUtils'; import { selectors } from '../../../../../../data/redux'; -import { ShowAnswerCard, mapStateToProps, mapDispatchToProps } from './ShowAnswerCard'; +import { ShowAnswerCardInternal as ShowAnswerCard, mapStateToProps, mapDispatchToProps } from './ShowAnswerCard'; import { useAnswerSettings } from '../hooks'; jest.mock('../hooks', () => ({ diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/SwitchToAdvancedEditorCard.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/SwitchToAdvancedEditorCard.jsx index 2828b037c8..90454579ea 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/SwitchToAdvancedEditorCard.jsx +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/SwitchToAdvancedEditorCard.jsx @@ -10,7 +10,7 @@ import Button from '../../../../../../sharedComponents/Button'; import { confirmSwitchToAdvancedEditor } from '../hooks'; import { ProblemTypeKeys } from '../../../../../../data/constants/problem'; -export const SwitchToAdvancedEditorCard = ({ +const SwitchToAdvancedEditorCard = ({ problemType, switchToAdvancedEditor, }) => { @@ -59,4 +59,5 @@ export const mapDispatchToProps = { switchToAdvancedEditor: thunkActions.problem.switchToAdvancedEditor, }; +export const SwitchToAdvancedEditorCardInternal = SwitchToAdvancedEditorCard; // For testing only export default injectIntl(connect(mapStateToProps, mapDispatchToProps)(SwitchToAdvancedEditorCard)); diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/SwitchToAdvancedEditorCard.test.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/SwitchToAdvancedEditorCard.test.jsx index 9f16c8a8d5..b9d59f27f1 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/SwitchToAdvancedEditorCard.test.jsx +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/SwitchToAdvancedEditorCard.test.jsx @@ -1,6 +1,7 @@ +import 'CourseAuthoring/editors/setupEditorTest'; import React from 'react'; import { shallow } from '@edx/react-unit-test-utils'; -import { SwitchToAdvancedEditorCard, mapDispatchToProps } from './SwitchToAdvancedEditorCard'; +import { SwitchToAdvancedEditorCardInternal as SwitchToAdvancedEditorCard, mapDispatchToProps } from './SwitchToAdvancedEditorCard'; import { thunkActions } from '../../../../../../data/redux'; describe('SwitchToAdvancedEditorCard snapshot', () => { diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/TimerCard.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/TimerCard.jsx index 28e159b8c1..33032b529b 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/TimerCard.jsx +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/TimerCard.jsx @@ -6,7 +6,7 @@ import SettingsOption from '../SettingsOption'; import messages from '../messages'; import { timerCardHooks } from '../hooks'; -export const TimerCard = ({ +const TimerCard = ({ timeBetween, updateSettings, // inject @@ -42,4 +42,5 @@ TimerCard.propTypes = { intl: intlShape.isRequired, }; +export const TimerCardInternal = TimerCard; // For testing only export default injectIntl(TimerCard); diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/TimerCard.test.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/TimerCard.test.jsx index 46de822d58..614a73639b 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/TimerCard.test.jsx +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/TimerCard.test.jsx @@ -1,7 +1,8 @@ +import 'CourseAuthoring/editors/setupEditorTest'; import React from 'react'; import { shallow } from '@edx/react-unit-test-utils'; -import { formatMessage } from '../../../../../../../testUtils'; -import { TimerCard } from './TimerCard'; +import { formatMessage } from '../../../../../../testUtils'; +import { TimerCardInternal as TimerCard } from './TimerCard'; import { timerCardHooks } from '../hooks'; jest.mock('../hooks', () => ({ diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/Tolerance/index.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/Tolerance/index.jsx index cdb57f6511..150a2f39bd 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/Tolerance/index.jsx +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/Tolerance/index.jsx @@ -42,7 +42,7 @@ export const getSummary = ({ tolerance, intl }) => { } }; -export const ToleranceCard = ({ +const ToleranceCard = ({ tolerance, answers, updateSettings, @@ -119,4 +119,5 @@ ToleranceCard.propTypes = { intl: intlShape.isRequired, }; +export const ToleranceCardInternal = ToleranceCard; // For testing only export default injectIntl(ToleranceCard); diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/Tolerance/index.test.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/Tolerance/index.test.jsx index d0ba299ee8..d429128ccb 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/Tolerance/index.test.jsx +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/Tolerance/index.test.jsx @@ -4,8 +4,8 @@ import { import React from 'react'; import messages from './messages'; import { ToleranceTypes } from './constants'; -import { ToleranceCard } from './index'; -import { formatMessage } from '../../../../../../../../testUtils'; +import { ToleranceCardInternal as ToleranceCard } from './index'; +import { formatMessage } from '../../../../../../../testUtils'; jest.mock('@edx/frontend-platform/i18n', () => ({ __esmodule: true, diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/TypeCard.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/TypeCard.jsx index aa6808196b..75137b5050 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/TypeCard.jsx +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/TypeCard.jsx @@ -6,7 +6,7 @@ import { ProblemTypeKeys, ProblemTypes } from '../../../../../../data/constants/ import messages from '../messages'; import TypeRow from './TypeRow'; -export const TypeCard = ({ +const TypeCard = ({ answers, blockTitle, correctAnswerCount, @@ -64,4 +64,5 @@ TypeCard.propTypes = { intl: intlShape.isRequired, }; +export const TypeCardInternal = TypeCard; // For testing only export default injectIntl(TypeCard); diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/TypeCard.test.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/TypeCard.test.jsx index f64fa1b300..bd914dec9a 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/TypeCard.test.jsx +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/TypeCard.test.jsx @@ -1,7 +1,8 @@ +import 'CourseAuthoring/editors/setupEditorTest'; import React from 'react'; import { shallow } from '@edx/react-unit-test-utils'; -import { formatMessage } from '../../../../../../../testUtils'; -import { TypeCard } from './TypeCard'; +import { formatMessage } from '../../../../../../testUtils'; +import { TypeCardInternal as TypeCard } from './TypeCard'; import { ProblemTypeKeys } from '../../../../../../data/constants/problem'; describe('TypeCard', () => { diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/TypeRow.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/TypeRow.jsx index cad93083a0..ec8bc78ec9 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/TypeRow.jsx +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/TypeRow.jsx @@ -6,7 +6,7 @@ import { typeRowHooks } from '../hooks'; import Button from '../../../../../../sharedComponents/Button'; -export const TypeRow = ({ +const TypeRow = ({ answers, blockTitle, correctAnswerCount, diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/TypeRow.test.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/TypeRow.test.jsx index a34c14df06..3902fd9659 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/TypeRow.test.jsx +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/TypeRow.test.jsx @@ -1,6 +1,7 @@ +import 'CourseAuthoring/editors/setupEditorTest'; import React from 'react'; import { shallow } from '@edx/react-unit-test-utils'; -import { TypeRow } from './TypeRow'; +import TypeRow from './TypeRow'; import { typeRowHooks } from '../hooks'; jest.mock('../hooks', () => ({ diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/hooks.js b/src/editors/containers/ProblemEditor/components/EditProblemView/hooks.js index 6a11b3f711..642e149955 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/hooks.js +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/hooks.js @@ -24,7 +24,7 @@ export const fetchEditorContent = ({ format }) => { const editorObject = { hints: [] }; const EditorsArray = window.tinymce.editors; Object.entries(EditorsArray).forEach(([id, editor]) => { - if (Number.isNaN(parseInt(id))) { + if (Number.isNaN(parseInt(id, 10))) { if (id.startsWith('answer')) { const { answers } = editorObject; const answerId = id.substring(id.indexOf('-') + 1); diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/hooks.test.js b/src/editors/containers/ProblemEditor/components/EditProblemView/hooks.test.js index fe3a4d7b03..11f38473b4 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/hooks.test.js +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/hooks.test.js @@ -1,6 +1,6 @@ import { ProblemTypeKeys, ShowAnswerTypesKeys } from '../../../../data/constants/problem'; import * as hooks from './hooks'; -import { MockUseState } from '../../../../../testUtils'; +import { MockUseState } from '../../../../testUtils'; const mockRawOLX = 'rawOLX'; const mockBuiltOLX = 'builtOLX'; diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/index.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/index.jsx index e05a4ae4b2..64d9229061 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/index.jsx +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/index.jsx @@ -24,7 +24,7 @@ import messages from './messages'; import ExplanationWidget from './ExplanationWidget'; import { saveBlock } from '../../../../hooks'; -export const EditProblemView = ({ +const EditProblemView = ({ returnFunction, // redux problemType, @@ -139,4 +139,5 @@ export const mapStateToProps = (state) => ({ problemState: selectors.problem.completeState(state), }); +export const EditProblemViewInternal = EditProblemView; // For testing only export default injectIntl(connect(mapStateToProps)(EditProblemView)); diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/index.scss b/src/editors/containers/ProblemEditor/components/EditProblemView/index.scss index 95d58efef1..1c14750ebf 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/index.scss +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/index.scss @@ -4,9 +4,11 @@ flex-grow: 0; flex-shrink: 0; } + .advancedEditorTopMargin { margin-top: 40px; } + .answer-option { .pgn__form-checkbox, .pgn__form-radio { @@ -15,6 +17,7 @@ } } } + .settingsOption { .pgn__form-checkbox .pgn__form-label { min-width: .8rem; @@ -24,7 +27,7 @@ .tinyMceWidget { .tox-tinymce { - border-radius: 0.375rem; + border-radius: .375rem; } .tox { @@ -41,11 +44,11 @@ // TODO: Find a way to override the border without !important border-right: none !important; - &:after { + &::after { content: ''; position: relative; left: 5px; - border: 1px solid #eae6e5; + border: 1px solid #EAE6E5; height: 24px; } } diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/index.test.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/index.test.jsx index 6a40796cef..a55d025bad 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/index.test.jsx +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/index.test.jsx @@ -1,18 +1,11 @@ +import 'CourseAuthoring/editors/setupEditorTest'; import React from 'react'; import { shallow } from '@edx/react-unit-test-utils'; -import { EditProblemView } from '.'; -import AnswerWidget from './AnswerWidget'; +import { EditProblemViewInternal as EditProblemView } from '.'; +import { AnswerWidgetInternal as AnswerWidget } from './AnswerWidget'; import { ProblemTypeKeys } from '../../../../data/constants/problem'; import RawEditor from '../../../../sharedComponents/RawEditor'; -import { formatMessage } from '../../../../../testUtils'; - -jest.mock('@openedx/paragon', () => ({ - ...jest.requireActual('@openedx/paragon'), - Container: 'Container', - Button: 'Button', - AlertModal: 'AlertModal', - ActionRow: 'ActionRow', -})); +import { formatMessage } from '../../../../testUtils'; describe('EditorProblemView component', () => { test('renders simple view', () => { diff --git a/src/editors/containers/ProblemEditor/components/SelectTypeModal/SelectTypeWrapper/SelectTypeFooter.jsx b/src/editors/containers/ProblemEditor/components/SelectTypeModal/SelectTypeWrapper/SelectTypeFooter.jsx index 677dc4988e..733f87a1a2 100644 --- a/src/editors/containers/ProblemEditor/components/SelectTypeModal/SelectTypeWrapper/SelectTypeFooter.jsx +++ b/src/editors/containers/ProblemEditor/components/SelectTypeModal/SelectTypeWrapper/SelectTypeFooter.jsx @@ -9,11 +9,11 @@ import { } from '@openedx/paragon'; import { FormattedMessage, injectIntl, intlShape } from '@edx/frontend-platform/i18n'; import messages from './messages'; -import hooks from '../hooks'; +import * as hooks from '../hooks'; import { actions, selectors } from '../../../../../data/redux'; -export const SelectTypeFooter = ({ +const SelectTypeFooter = ({ onCancel, selected, // redux @@ -79,4 +79,5 @@ export const mapDispatchToProps = { setBlockTitle: actions.app.setBlockTitle, }; +export const SelectTypeFooterInternal = SelectTypeFooter; // For testing only export default injectIntl(connect(mapStateToProps, mapDispatchToProps)(SelectTypeFooter)); diff --git a/src/editors/containers/ProblemEditor/components/SelectTypeModal/SelectTypeWrapper/SelectTypeFooter.test.jsx b/src/editors/containers/ProblemEditor/components/SelectTypeModal/SelectTypeWrapper/SelectTypeFooter.test.jsx index 2a9e142362..c00cd02d48 100644 --- a/src/editors/containers/ProblemEditor/components/SelectTypeModal/SelectTypeWrapper/SelectTypeFooter.test.jsx +++ b/src/editors/containers/ProblemEditor/components/SelectTypeModal/SelectTypeWrapper/SelectTypeFooter.test.jsx @@ -1,12 +1,15 @@ +import 'CourseAuthoring/editors/setupEditorTest'; import React from 'react'; import { shallow } from '@edx/react-unit-test-utils'; import { Button } from '@openedx/paragon'; -import { formatMessage } from '../../../../../../testUtils'; +import { formatMessage } from '../../../../../testUtils'; import * as module from './SelectTypeFooter'; -import hooks from '../hooks'; +import * as hooks from '../hooks'; import { actions } from '../../../../../data/redux'; +const SelectTypeFooter = module.SelectTypeFooterInternal; + jest.mock('../hooks', () => ({ onSelect: jest.fn().mockName('onSelect'), })); @@ -23,13 +26,13 @@ describe('SelectTypeFooter', () => { }; test('snapshot', () => { - expect(shallow().snapshot).toMatchSnapshot(); + expect(shallow().snapshot).toMatchSnapshot(); }); describe('behavior', () => { let el; beforeEach(() => { - el = shallow(); + el = shallow(); }); test('close behavior is linked to modal onCancel', () => { const expected = props.onCancel; diff --git a/src/editors/containers/ProblemEditor/components/SelectTypeModal/SelectTypeWrapper/index.jsx b/src/editors/containers/ProblemEditor/components/SelectTypeModal/SelectTypeWrapper/index.jsx index 8f05c83328..f747783f5a 100644 --- a/src/editors/containers/ProblemEditor/components/SelectTypeModal/SelectTypeWrapper/index.jsx +++ b/src/editors/containers/ProblemEditor/components/SelectTypeModal/SelectTypeWrapper/index.jsx @@ -8,7 +8,7 @@ import SelectTypeFooter from './SelectTypeFooter'; import * as hooks from '../../../../EditorContainer/hooks'; import messages from './messages'; -export const SelectTypeWrapper = ({ +const SelectTypeWrapper = ({ children, onClose, selected, @@ -51,4 +51,5 @@ SelectTypeWrapper.propTypes = { onClose: PropTypes.func, }; +export const SelectTypeWrapperInternal = SelectTypeWrapper; // For testing only export default injectIntl(SelectTypeWrapper); diff --git a/src/editors/containers/ProblemEditor/components/SelectTypeModal/SelectTypeWrapper/index.test.jsx b/src/editors/containers/ProblemEditor/components/SelectTypeModal/SelectTypeWrapper/index.test.jsx index 3bdd2088ee..ec2e3a51a6 100644 --- a/src/editors/containers/ProblemEditor/components/SelectTypeModal/SelectTypeWrapper/index.test.jsx +++ b/src/editors/containers/ProblemEditor/components/SelectTypeModal/SelectTypeWrapper/index.test.jsx @@ -1,7 +1,8 @@ +import 'CourseAuthoring/editors/setupEditorTest'; import React from 'react'; import { shallow } from '@edx/react-unit-test-utils'; import { IconButton } from '@openedx/paragon'; -import * as module from '.'; +import { SelectTypeWrapperInternal as SelectTypeWrapper } from '.'; import { handleCancel } from '../../../../EditorContainer/hooks'; jest.mock('../../../../EditorContainer/hooks', () => ({ @@ -16,13 +17,13 @@ describe('SelectTypeWrapper', () => { }; test('snapshot', () => { - expect(shallow().snapshot).toMatchSnapshot(); + expect(shallow().snapshot).toMatchSnapshot(); }); describe('behavior', () => { let el; beforeEach(() => { - el = shallow(); + el = shallow(); }); test('close behavior is linked to modal onClose', () => { const expected = handleCancel({ onClose: props.onClose }); diff --git a/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/AdvanceTypeSelect.jsx b/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/AdvanceTypeSelect.jsx index 6dda14eaa6..d60061ddd1 100644 --- a/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/AdvanceTypeSelect.jsx +++ b/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/AdvanceTypeSelect.jsx @@ -16,7 +16,7 @@ import { FormattedMessage, injectIntl, intlShape } from '@edx/frontend-platform/ import { AdvanceProblems, ProblemTypeKeys } from '../../../../../data/constants/problem'; import messages from './messages'; -export const AdvanceTypeSelect = ({ +const AdvanceTypeSelect = ({ selected, setSelected, // injected @@ -97,4 +97,5 @@ AdvanceTypeSelect.propTypes = { intl: intlShape.isRequired, }; +export const AdvanceTypeSelectInternal = AdvanceTypeSelect; // For testing only export default injectIntl(AdvanceTypeSelect); diff --git a/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/AdvanceTypeSelect.test.jsx b/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/AdvanceTypeSelect.test.jsx index f385f1b6fe..978b35e7c5 100644 --- a/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/AdvanceTypeSelect.test.jsx +++ b/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/AdvanceTypeSelect.test.jsx @@ -1,9 +1,12 @@ +import 'CourseAuthoring/editors/setupEditorTest'; import React from 'react'; import { shallow } from '@edx/react-unit-test-utils'; -import { formatMessage } from '../../../../../../testUtils'; +import { formatMessage } from '../../../../../testUtils'; import * as module from './AdvanceTypeSelect'; +const AdvanceTypeSelect = module.AdvanceTypeSelectInternal; + describe('AdvanceTypeSelect', () => { const props = { intl: { formatMessage }, @@ -13,42 +16,42 @@ describe('AdvanceTypeSelect', () => { describe('snapshots', () => { test('snapshots: renders as expected with default props', () => { expect( - shallow().snapshot, + shallow().snapshot, ).toMatchSnapshot(); }); test('snapshots: renders as expected with problemType is circuitschematic', () => { expect( - shallow().snapshot, + shallow().snapshot, ).toMatchSnapshot(); }); test('snapshots: renders as expected with problemType is customgrader', () => { expect( - shallow().snapshot, + shallow().snapshot, ).toMatchSnapshot(); }); test('snapshots: renders as expected with problemType is drag_and_drop', () => { expect( - shallow().snapshot, + shallow().snapshot, ).toMatchSnapshot(); }); test('snapshots: renders as expected with problemType is formularesponse', () => { expect( - shallow().snapshot, + shallow().snapshot, ).toMatchSnapshot(); }); test('snapshots: renders as expected with problemType is imageresponse', () => { expect( - shallow().snapshot, + shallow().snapshot, ).toMatchSnapshot(); }); test('snapshots: renders as expected with problemType is jsinput_response', () => { expect( - shallow().snapshot, + shallow().snapshot, ).toMatchSnapshot(); }); test('snapshots: renders as expected with problemType is problem_with_hint', () => { expect( - shallow().snapshot, + shallow().snapshot, ).toMatchSnapshot(); }); }); diff --git a/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/Preview.jsx b/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/Preview.jsx index bca710bd25..7077c18a56 100644 --- a/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/Preview.jsx +++ b/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/Preview.jsx @@ -9,7 +9,7 @@ import { import messages from './messages'; import { ProblemTypes } from '../../../../../data/constants/problem'; -export const Preview = ({ +const Preview = ({ problemType, // injected intl, @@ -52,4 +52,5 @@ Preview.propTypes = { intl: intlShape.isRequired, }; +export const PreviewInternal = Preview; // For testing only export default injectIntl(Preview); diff --git a/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/Preview.test.jsx b/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/Preview.test.jsx index 6db9965d4f..36cf961fc8 100644 --- a/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/Preview.test.jsx +++ b/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/Preview.test.jsx @@ -1,8 +1,9 @@ +import 'CourseAuthoring/editors/setupEditorTest'; import React from 'react'; import { shallow } from '@edx/react-unit-test-utils'; -import { formatMessage } from '../../../../../../testUtils'; -import * as module from './Preview'; +import { formatMessage } from '../../../../../testUtils'; +import { PreviewInternal as Preview } from './Preview'; describe('Preview', () => { const props = { @@ -12,32 +13,32 @@ describe('Preview', () => { describe('snapshots', () => { test('snapshots: renders as expected with default props', () => { expect( - shallow().snapshot, + shallow().snapshot, ).toMatchSnapshot(); }); test('snapshots: renders as expected with problemType is stringresponse', () => { expect( - shallow().snapshot, + shallow().snapshot, ).toMatchSnapshot(); }); test('snapshots: renders as expected with problemType is numericalresponse', () => { expect( - shallow().snapshot, + shallow().snapshot, ).toMatchSnapshot(); }); test('snapshots: renders as expected with problemType is optionresponse', () => { expect( - shallow().snapshot, + shallow().snapshot, ).toMatchSnapshot(); }); test('snapshots: renders as expected with problemType is choiceresponse', () => { expect( - shallow().snapshot, + shallow().snapshot, ).toMatchSnapshot(); }); test('snapshots: renders as expected with problemType is multiplechoiceresponse', () => { expect( - shallow().snapshot, + shallow().snapshot, ).toMatchSnapshot(); }); }); diff --git a/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/ProblemTypeSelect.jsx b/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/ProblemTypeSelect.jsx index 8fcf860a2f..e58d731af2 100644 --- a/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/ProblemTypeSelect.jsx +++ b/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/ProblemTypeSelect.jsx @@ -8,7 +8,7 @@ import SelectableBox from '../../../../../sharedComponents/SelectableBox'; import { ProblemTypes, ProblemTypeKeys, AdvanceProblemKeys } from '../../../../../data/constants/problem'; import messages from './messages'; -export const ProblemTypeSelect = ({ +const ProblemTypeSelect = ({ selected, setSelected, }) => { @@ -50,4 +50,5 @@ ProblemTypeSelect.propTypes = { setSelected: PropTypes.func.isRequired, }; +export const ProblemTypeSelectInternal = ProblemTypeSelect; // For testing only export default injectIntl(ProblemTypeSelect); diff --git a/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/ProblemTypeSelect.test.jsx b/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/ProblemTypeSelect.test.jsx index e6463db987..7482f843ef 100644 --- a/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/ProblemTypeSelect.test.jsx +++ b/src/editors/containers/ProblemEditor/components/SelectTypeModal/content/ProblemTypeSelect.test.jsx @@ -1,7 +1,8 @@ +import 'CourseAuthoring/editors/setupEditorTest'; import React from 'react'; import { shallow } from '@edx/react-unit-test-utils'; import { ProblemTypeKeys } from '../../../../../data/constants/problem'; -import * as module from './ProblemTypeSelect'; +import { ProblemTypeSelectInternal as ProblemTypeSelect } from './ProblemTypeSelect'; describe('ProblemTypeSelect', () => { const props = { @@ -12,27 +13,27 @@ describe('ProblemTypeSelect', () => { describe('snapshot', () => { test('SINGLESELECT', () => { expect(shallow( - , + , ).snapshot).toMatchSnapshot(); }); test('MULTISELECT', () => { expect(shallow( - , + , ).snapshot).toMatchSnapshot(); }); test('DROPDOWN', () => { expect(shallow( - , + , ).snapshot).toMatchSnapshot(); }); test('NUMERIC', () => { expect(shallow( - , + , ).snapshot).toMatchSnapshot(); }); test('TEXTINPUT', () => { expect(shallow( - , + , ).snapshot).toMatchSnapshot(); }); }); diff --git a/src/editors/containers/ProblemEditor/components/SelectTypeModal/hooks.js b/src/editors/containers/ProblemEditor/components/SelectTypeModal/hooks.js index ef70a36133..d25c028aaa 100644 --- a/src/editors/containers/ProblemEditor/components/SelectTypeModal/hooks.js +++ b/src/editors/containers/ProblemEditor/components/SelectTypeModal/hooks.js @@ -3,6 +3,10 @@ import { AdvanceProblemKeys, AdvanceProblems, ProblemTypeKeys, ProblemTypes, } from '../../../../data/constants/problem'; import { StrictDict, snakeCaseKeys } from '../../../../utils'; +// This 'module' self-import hack enables mocking during tests. +// See src/editors/decisions/0005-internal-editor-testability-decisions.md. The whole approach to how hooks are tested +// should be re-thought and cleaned up to avoid this pattern. +// eslint-disable-next-line import/no-self-import import * as module from './hooks'; import { getDataFromOlx } from '../../../../data/redux/thunkActions/problem'; @@ -72,10 +76,3 @@ export const useArrowNav = (selected, setSelected) => { }; }, [selected, setSelected]); }; - -export default { - state, - selectHooks, - onSelect, - useArrowNav, -}; diff --git a/src/editors/containers/ProblemEditor/components/SelectTypeModal/hooks.test.js b/src/editors/containers/ProblemEditor/components/SelectTypeModal/hooks.test.js index 2970c87b39..e0582b5fb2 100644 --- a/src/editors/containers/ProblemEditor/components/SelectTypeModal/hooks.test.js +++ b/src/editors/containers/ProblemEditor/components/SelectTypeModal/hooks.test.js @@ -1,6 +1,10 @@ /* eslint-disable prefer-destructuring */ import React from 'react'; -import { MockUseState } from '../../../../../testUtils'; +import { MockUseState } from '../../../../testUtils'; +// This 'module' self-import hack enables mocking during tests. +// See src/editors/decisions/0005-internal-editor-testability-decisions.md. The whole approach to how hooks are tested +// should be re-thought and cleaned up to avoid this pattern. +// eslint-disable-next-line import/no-self-import import * as module from './hooks'; import { AdvanceProblems, ProblemTypeKeys, ProblemTypes } from '../../../../data/constants/problem'; import { getDataFromOlx } from '../../../../data/redux/thunkActions/problem'; diff --git a/src/editors/containers/ProblemEditor/components/SelectTypeModal/index.jsx b/src/editors/containers/ProblemEditor/components/SelectTypeModal/index.jsx index e39100edb7..828dc4be05 100644 --- a/src/editors/containers/ProblemEditor/components/SelectTypeModal/index.jsx +++ b/src/editors/containers/ProblemEditor/components/SelectTypeModal/index.jsx @@ -6,10 +6,10 @@ import ProblemTypeSelect from './content/ProblemTypeSelect'; import Preview from './content/Preview'; import AdvanceTypeSelect from './content/AdvanceTypeSelect'; import SelectTypeWrapper from './SelectTypeWrapper'; -import hooks from './hooks'; +import * as hooks from './hooks'; import { AdvanceProblemKeys } from '../../../../data/constants/problem'; -export const SelectTypeModal = ({ +const SelectTypeModal = ({ onClose, }) => { const { selected, setSelected } = hooks.selectHooks(); diff --git a/src/editors/containers/ProblemEditor/components/SelectTypeModal/index.test.jsx b/src/editors/containers/ProblemEditor/components/SelectTypeModal/index.test.jsx index 8347aff4e4..4574c1b799 100644 --- a/src/editors/containers/ProblemEditor/components/SelectTypeModal/index.test.jsx +++ b/src/editors/containers/ProblemEditor/components/SelectTypeModal/index.test.jsx @@ -1,6 +1,7 @@ +import 'CourseAuthoring/editors/setupEditorTest'; import React from 'react'; import { shallow } from '@edx/react-unit-test-utils'; -import * as module from '.'; +import SelectTypeModal from '.'; jest.mock('./hooks', () => ({ selectHooks: jest.fn(() => ({ @@ -16,6 +17,6 @@ describe('SelectTypeModal', () => { }; test('snapshot', () => { - expect(shallow().snapshot).toMatchSnapshot(); + expect(shallow().snapshot).toMatchSnapshot(); }); }); diff --git a/src/editors/containers/ProblemEditor/data/OLXParser.js b/src/editors/containers/ProblemEditor/data/OLXParser.js index 0a3d0ff6a3..e9a12348fb 100644 --- a/src/editors/containers/ProblemEditor/data/OLXParser.js +++ b/src/editors/containers/ProblemEditor/data/OLXParser.js @@ -2,7 +2,7 @@ /* eslint no-eval: 0 */ import { XMLParser, XMLBuilder } from 'fast-xml-parser'; -import _ from 'lodash-es'; +import _ from 'lodash'; import { ProblemTypeKeys, RichTextProblems, settingsOlxAttributes } from '../../../data/constants/problem'; export const indexToLetterMap = [...Array(26)].map((val, i) => String.fromCharCode(i + 65)); @@ -719,9 +719,9 @@ export class OLXParser { if (!toleranceValue || toleranceValue.length === 0) { settings.tolerance = { value: null, type: 'None' }; } else if (toleranceValue.includes('%')) { - settings.tolerance = { value: parseInt(toleranceValue.slice(0, -1)), type: 'Percent' }; + settings.tolerance = { value: parseInt(toleranceValue.slice(0, -1), 10), type: 'Percent' }; } else { - settings.tolerance = { value: parseInt(toleranceValue), type: 'Number' }; + settings.tolerance = { value: parseInt(toleranceValue, 10), type: 'Number' }; } } else { settings.tolerance = { value: null, type: 'None' }; diff --git a/src/editors/containers/ProblemEditor/data/ReactStateOLXParser.js b/src/editors/containers/ProblemEditor/data/ReactStateOLXParser.js index f320cdd827..673763de61 100644 --- a/src/editors/containers/ProblemEditor/data/ReactStateOLXParser.js +++ b/src/editors/containers/ProblemEditor/data/ReactStateOLXParser.js @@ -1,4 +1,4 @@ -import _ from 'lodash-es'; +import _ from 'lodash'; import { XMLParser, XMLBuilder } from 'fast-xml-parser'; import { ProblemTypeKeys } from '../../../data/constants/problem'; import { ToleranceTypes } from '../components/EditProblemView/SettingsWidget/settingsComponents/Tolerance/constants'; diff --git a/src/editors/containers/ProblemEditor/data/ReactStateSettingsParser.js b/src/editors/containers/ProblemEditor/data/ReactStateSettingsParser.js index 88fcb7f987..49ddac5324 100644 --- a/src/editors/containers/ProblemEditor/data/ReactStateSettingsParser.js +++ b/src/editors/containers/ProblemEditor/data/ReactStateSettingsParser.js @@ -1,5 +1,5 @@ import { XMLParser } from 'fast-xml-parser'; -import _ from 'lodash-es'; +import _ from 'lodash'; import { ShowAnswerTypesKeys, @@ -61,7 +61,7 @@ class ReactStateSettingsParser { const attributeKey = attribute.substring(2); if (SETTING_KEYS.includes(attributeKey)) { if (attributeKey === 'max_attempts' || attributeKey === 'weight') { - rawOlxSettings[attributeKey] = parseInt(olx.problem[attribute]); + rawOlxSettings[attributeKey] = parseInt(olx.problem[attribute], 10); } else { rawOlxSettings[attributeKey] = olx.problem[attribute]; } diff --git a/src/editors/containers/ProblemEditor/data/SettingsParser.js b/src/editors/containers/ProblemEditor/data/SettingsParser.js index 5836e2827b..1c90470675 100644 --- a/src/editors/containers/ProblemEditor/data/SettingsParser.js +++ b/src/editors/containers/ProblemEditor/data/SettingsParser.js @@ -1,4 +1,4 @@ -import _ from 'lodash-es'; +import _ from 'lodash'; import { ShowAnswerTypes, RandomizationTypesKeys } from '../../../data/constants/problem'; diff --git a/src/editors/containers/ProblemEditor/data/reactStateOLXHelpers.js b/src/editors/containers/ProblemEditor/data/reactStateOLXHelpers.js index 4a0502f336..2815228969 100644 --- a/src/editors/containers/ProblemEditor/data/reactStateOLXHelpers.js +++ b/src/editors/containers/ProblemEditor/data/reactStateOLXHelpers.js @@ -1,6 +1,6 @@ /* eslint-disable import/prefer-default-export */ -import flatten from 'lodash.flatten'; +import { flatten } from 'lodash'; /** * flattenSubNodes - appends a nodes children to a given array 'subNodes' in a flattened format. diff --git a/src/editors/containers/ProblemEditor/index.jsx b/src/editors/containers/ProblemEditor/index.jsx index fcb05d194c..5f342ad1e4 100644 --- a/src/editors/containers/ProblemEditor/index.jsx +++ b/src/editors/containers/ProblemEditor/index.jsx @@ -9,7 +9,7 @@ import { selectors, thunkActions } from '../../data/redux'; import { RequestKeys } from '../../data/constants/requests'; import messages from './messages'; -export const ProblemEditor = ({ +const ProblemEditor = ({ onClose, returnFunction, // Redux @@ -79,4 +79,5 @@ export const mapDispatchToProps = { initializeProblemEditor: thunkActions.problem.initializeProblem, }; +export const ProblemEditorInternal = ProblemEditor; // For testing only export default injectIntl(connect(mapStateToProps, mapDispatchToProps)(ProblemEditor)); diff --git a/src/editors/containers/ProblemEditor/index.test.jsx b/src/editors/containers/ProblemEditor/index.test.jsx index 6c171b22af..f6866b56a6 100644 --- a/src/editors/containers/ProblemEditor/index.test.jsx +++ b/src/editors/containers/ProblemEditor/index.test.jsx @@ -1,9 +1,10 @@ +import 'CourseAuthoring/editors/setupEditorTest'; import React from 'react'; import { shallow } from '@edx/react-unit-test-utils'; import { Spinner } from '@openedx/paragon'; import { thunkActions, selectors } from '../../data/redux'; import { RequestKeys } from '../../data/constants/requests'; -import { ProblemEditor, mapStateToProps, mapDispatchToProps } from '.'; +import { ProblemEditorInternal as ProblemEditor, mapStateToProps, mapDispatchToProps } from '.'; jest.mock('./components/EditProblemView', () => 'EditProblemView'); jest.mock('./components/SelectTypeModal', () => 'SelectTypeModal'); diff --git a/src/editors/containers/TextEditor/hooks.test.jsx b/src/editors/containers/TextEditor/hooks.test.jsx index 0580139faf..bac63ab334 100644 --- a/src/editors/containers/TextEditor/hooks.test.jsx +++ b/src/editors/containers/TextEditor/hooks.test.jsx @@ -1,5 +1,9 @@ import { keyStore } from '../../utils'; import * as appHooks from '../../hooks'; +// This 'module' self-import hack enables mocking during tests. +// See src/editors/decisions/0005-internal-editor-testability-decisions.md. The whole approach to how hooks are tested +// should be re-thought and cleaned up to avoid this pattern. +// eslint-disable-next-line import/no-self-import import * as module from './hooks'; import * as tinyMceHooks from '../../sharedComponents/TinyMceWidget/hooks'; diff --git a/src/editors/containers/TextEditor/index.jsx b/src/editors/containers/TextEditor/index.jsx index c349a593e8..830f3b510a 100644 --- a/src/editors/containers/TextEditor/index.jsx +++ b/src/editors/containers/TextEditor/index.jsx @@ -18,7 +18,7 @@ import messages from './messages'; import TinyMceWidget from '../../sharedComponents/TinyMceWidget'; import { prepareEditorRef, replaceStaticWithAsset } from '../../sharedComponents/TinyMceWidget/hooks'; -export const TextEditor = ({ +const TextEditor = ({ onClose, returnFunction, // redux @@ -121,4 +121,5 @@ export const mapDispatchToProps = { initializeEditor: actions.app.initializeEditor, }; +export const TextEditorInternal = TextEditor; // For testing only export default injectIntl(connect(mapStateToProps, mapDispatchToProps)(TextEditor)); diff --git a/src/editors/containers/TextEditor/index.test.jsx b/src/editors/containers/TextEditor/index.test.jsx index 17e813ac0a..ac337f68f7 100644 --- a/src/editors/containers/TextEditor/index.test.jsx +++ b/src/editors/containers/TextEditor/index.test.jsx @@ -1,10 +1,11 @@ +import 'CourseAuthoring/editors/setupEditorTest'; import React from 'react'; import { shallow } from '@edx/react-unit-test-utils'; -import { formatMessage } from '../../../testUtils'; +import { formatMessage } from '../../testUtils'; import { actions, selectors } from '../../data/redux'; import { RequestKeys } from '../../data/constants/requests'; -import { TextEditor, mapStateToProps, mapDispatchToProps } from '.'; +import { TextEditorInternal as TextEditor, mapStateToProps, mapDispatchToProps } from '.'; // Per https://github.com/tinymce/tinymce-react/issues/91 React unit testing in JSDOM is not supported by tinymce. // Consequently, mock the Editor out. diff --git a/src/editors/containers/VideoEditor/components/SelectVideoModal.jsx b/src/editors/containers/VideoEditor/components/SelectVideoModal.jsx index ade441c325..07167d65ca 100644 --- a/src/editors/containers/VideoEditor/components/SelectVideoModal.jsx +++ b/src/editors/containers/VideoEditor/components/SelectVideoModal.jsx @@ -6,6 +6,10 @@ import { Button } from '@openedx/paragon'; import { thunkActions } from '../../../data/redux'; import BaseModal from '../../../sharedComponents/BaseModal'; +// This 'module' self-import hack enables mocking during tests. +// See src/editors/decisions/0005-internal-editor-testability-decisions.md. The whole approach to how hooks are tested +// should be re-thought and cleaned up to avoid this pattern. +// eslint-disable-next-line import/no-self-import import * as module from './SelectVideoModal'; export const hooks = { diff --git a/src/editors/containers/VideoEditor/components/VideoSettingsModal/ErrorSummary.jsx b/src/editors/containers/VideoEditor/components/VideoSettingsModal/ErrorSummary.jsx index 08ea2acc6d..a7acc05a41 100644 --- a/src/editors/containers/VideoEditor/components/VideoSettingsModal/ErrorSummary.jsx +++ b/src/editors/containers/VideoEditor/components/VideoSettingsModal/ErrorSummary.jsx @@ -6,6 +6,10 @@ import { InfoOutline } from '@openedx/paragon/icons'; import messages from './components/messages'; import { ErrorContext } from '../../hooks'; +// This 'module' self-import hack enables mocking during tests. +// See src/editors/decisions/0005-internal-editor-testability-decisions.md. The whole approach to how hooks are tested +// should be re-thought and cleaned up to avoid this pattern. +// eslint-disable-next-line import/no-self-import import * as module from './ErrorSummary'; export const hasNoError = (error) => Object.keys(error[0]).length === 0; @@ -29,5 +33,3 @@ export const ErrorSummary = () => { ); }; - -export default ErrorSummary; diff --git a/src/editors/containers/VideoEditor/components/VideoSettingsModal/ErrorSummary.test.jsx b/src/editors/containers/VideoEditor/components/VideoSettingsModal/ErrorSummary.test.jsx index e644eac856..7a34297aef 100644 --- a/src/editors/containers/VideoEditor/components/VideoSettingsModal/ErrorSummary.test.jsx +++ b/src/editors/containers/VideoEditor/components/VideoSettingsModal/ErrorSummary.test.jsx @@ -1,8 +1,11 @@ +import 'CourseAuthoring/editors/setupEditorTest'; import React from 'react'; import { shallow } from '@edx/react-unit-test-utils'; import * as module from './ErrorSummary'; +const { ErrorSummary } = module; + describe('ErrorSummary', () => { const errors = { widgetWithError: [{ err1: 'mSg', err2: 'msG2' }, jest.fn()], @@ -17,11 +20,11 @@ describe('ErrorSummary', () => { }); test('snapshots: renders as expected when there are no errors', () => { jest.spyOn(module, 'showAlert').mockReturnValue(false); - expect(shallow().snapshot).toMatchSnapshot(); + expect(shallow().snapshot).toMatchSnapshot(); }); test('snapshots: renders as expected when there are errors', () => { jest.spyOn(module, 'showAlert').mockReturnValue(true); - expect(shallow().snapshot).toMatchSnapshot(); + expect(shallow().snapshot).toMatchSnapshot(); }); }); describe('hasNoError', () => { diff --git a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/CollapsibleFormWidget.jsx b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/CollapsibleFormWidget.jsx index 34117ea67c..9545195421 100644 --- a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/CollapsibleFormWidget.jsx +++ b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/CollapsibleFormWidget.jsx @@ -14,7 +14,7 @@ import messages from './messages'; *
My Widget
* */ -export const CollapsibleFormWidget = ({ +const CollapsibleFormWidget = ({ children, isError, subtitle, @@ -70,4 +70,5 @@ CollapsibleFormWidget.propTypes = { intl: intlShape.isRequired, }; +export const CollapsibleFormWidgetInternal = CollapsibleFormWidget; // For testing only export default injectIntl(CollapsibleFormWidget); diff --git a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/CollapsibleFormWidget.test.jsx b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/CollapsibleFormWidget.test.jsx index 91b9523cfe..6cbd4d5b41 100644 --- a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/CollapsibleFormWidget.test.jsx +++ b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/CollapsibleFormWidget.test.jsx @@ -1,8 +1,9 @@ +import 'CourseAuthoring/editors/setupEditorTest'; import React from 'react'; import { shallow } from '@edx/react-unit-test-utils'; -import { formatMessage } from '../../../../../../testUtils'; -import { CollapsibleFormWidget } from './CollapsibleFormWidget'; +import { formatMessage } from '../../../../../testUtils'; +import { CollapsibleFormWidgetInternal as CollapsibleFormWidget } from './CollapsibleFormWidget'; describe('CollapsibleFormWidget', () => { const props = { diff --git a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/DurationWidget/hooks.js b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/DurationWidget/hooks.js index c3fd932be5..ba84d31dfa 100644 --- a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/DurationWidget/hooks.js +++ b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/DurationWidget/hooks.js @@ -2,6 +2,10 @@ import { useEffect, useState } from 'react'; import messages from '../messages'; +// This 'module' self-import hack enables mocking during tests. +// See src/editors/decisions/0005-internal-editor-testability-decisions.md. The whole approach to how hooks are tested +// should be re-thought and cleaned up to avoid this pattern. +// eslint-disable-next-line import/no-self-import import * as module from './hooks'; const durationMatcher = /^(\d{0,2}):?(\d{0,2})?:?(\d{0,2})?$/i; @@ -228,13 +232,3 @@ export const valueFromDuration = (duration) => { const [hours, minutes, seconds] = matches.map(x => parseInt(x, 10) || 0); return ((hours * 60 + minutes) * 60 + seconds) * 1000; }; - -export default { - durationWidget, - durationString, - durationStringFromValue, - updateDuration, - onDurationChange, - onDurationKeyDown, - valueFromDuration, -}; diff --git a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/DurationWidget/index.jsx b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/DurationWidget/index.jsx index e2b692717c..5975edf7ae 100644 --- a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/DurationWidget/index.jsx +++ b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/DurationWidget/index.jsx @@ -8,7 +8,7 @@ import { injectIntl, intlShape, FormattedMessage } from '@edx/frontend-platform/ import { actions, selectors } from '../../../../../../data/redux'; import { keyStore } from '../../../../../../utils'; import CollapsibleFormWidget from '../CollapsibleFormWidget'; -import hooks from './hooks'; +import * as hooks from './hooks'; import messages from '../messages'; import './index.scss'; @@ -17,7 +17,7 @@ import './index.scss'; * Collapsible Form widget controlling video start and end times * Also displays the total run time of the video. */ -export const DurationWidget = ({ +const DurationWidget = ({ // redux duration, updateField, @@ -100,4 +100,5 @@ export const mapDispatchToProps = { updateField: actions.video.updateField, }; +export const DurationWidgetInternal = DurationWidget; // For testing only export default injectIntl(connect(mapStateToProps, mapDispatchToProps)(DurationWidget)); diff --git a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/DurationWidget/index.test.jsx b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/DurationWidget/index.test.jsx index f086f25514..4053431075 100644 --- a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/DurationWidget/index.test.jsx +++ b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/DurationWidget/index.test.jsx @@ -1,9 +1,10 @@ +import 'CourseAuthoring/editors/setupEditorTest'; import React from 'react'; import { shallow } from '@edx/react-unit-test-utils'; import { actions, selectors } from '../../../../../../data/redux'; -import { formatMessage } from '../../../../../../../testUtils'; -import { DurationWidget, mapStateToProps, mapDispatchToProps } from '.'; +import { formatMessage } from '../../../../../../testUtils'; +import { DurationWidgetInternal as DurationWidget, mapStateToProps, mapDispatchToProps } from '.'; jest.mock('../../../../../../data/redux', () => ({ actions: { diff --git a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/HandoutWidget/hooks.jsx b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/HandoutWidget/hooks.jsx index ec14fff5ad..5861715c49 100644 --- a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/HandoutWidget/hooks.jsx +++ b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/HandoutWidget/hooks.jsx @@ -1,6 +1,10 @@ import React from 'react'; import { useDispatch } from 'react-redux'; import { thunkActions } from '../../../../../../data/redux'; +// This 'module' self-import hack enables mocking during tests. +// See src/editors/decisions/0005-internal-editor-testability-decisions.md. The whole approach to how hooks are tested +// should be re-thought and cleaned up to avoid this pattern. +// eslint-disable-next-line import/no-self-import import * as module from './hooks'; export const state = { diff --git a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/HandoutWidget/hooks.test.jsx b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/HandoutWidget/hooks.test.jsx index a733ff5703..aead6fb96f 100644 --- a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/HandoutWidget/hooks.test.jsx +++ b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/HandoutWidget/hooks.test.jsx @@ -1,7 +1,7 @@ import React from 'react'; import { dispatch } from 'react-redux'; import { thunkActions } from '../../../../../../data/redux'; -import { MockUseState } from '../../../../../../../testUtils'; +import { MockUseState } from '../../../../../../testUtils'; import { keyStore } from '../../../../../../utils'; import * as hooks from './hooks'; diff --git a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/HandoutWidget/index.jsx b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/HandoutWidget/index.jsx index 35e8f41b77..799c06a0c1 100644 --- a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/HandoutWidget/index.jsx +++ b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/HandoutWidget/index.jsx @@ -21,9 +21,9 @@ import { actions, selectors } from '../../../../../../data/redux'; import * as hooks from './hooks'; import messages from './messages'; -import FileInput from '../../../../../../sharedComponents/FileInput'; -import { ErrorAlert } from '../../../../../../sharedComponents/ErrorAlerts/ErrorAlert'; -import { UploadErrorAlert } from '../../../../../../sharedComponents/ErrorAlerts/UploadErrorAlert'; +import { FileInput } from '../../../../../../sharedComponents/FileInput'; +import ErrorAlert from '../../../../../../sharedComponents/ErrorAlerts/ErrorAlert'; +import UploadErrorAlert from '../../../../../../sharedComponents/ErrorAlerts/UploadErrorAlert'; import CollapsibleFormWidget from '../CollapsibleFormWidget'; import { ErrorContext } from '../../../../hooks'; import { RequestKeys } from '../../../../../../data/constants/requests'; @@ -31,7 +31,7 @@ import { RequestKeys } from '../../../../../../data/constants/requests'; /** * Collapsible Form widget controlling video handouts */ -export const HandoutWidget = ({ +const HandoutWidget = ({ // injected intl, // redux @@ -134,4 +134,5 @@ export const mapDispatchToProps = (dispatch) => ({ updateField: (payload) => dispatch(actions.video.updateField(payload)), }); +export const HandoutWidgetInternal = HandoutWidget; // For testing only export default injectIntl(connect(mapStateToProps, mapDispatchToProps)(HandoutWidget)); diff --git a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/HandoutWidget/index.test.jsx b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/HandoutWidget/index.test.jsx index cad8e7e168..8ecb7e613f 100644 --- a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/HandoutWidget/index.test.jsx +++ b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/HandoutWidget/index.test.jsx @@ -1,9 +1,10 @@ +import 'CourseAuthoring/editors/setupEditorTest'; import React from 'react'; import { shallow } from '@edx/react-unit-test-utils'; -import { formatMessage } from '../../../../../../../testUtils'; +import { formatMessage } from '../../../../../../testUtils'; import { actions, selectors } from '../../../../../../data/redux'; -import { HandoutWidget, mapStateToProps, mapDispatchToProps } from '.'; +import { HandoutWidgetInternal as HandoutWidget, mapStateToProps, mapDispatchToProps } from '.'; jest.mock('react', () => ({ ...jest.requireActual('react'), diff --git a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/LicenseWidget/LicenseBlurb.jsx b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/LicenseWidget/LicenseBlurb.jsx index 1775df8dbd..236d496311 100644 --- a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/LicenseWidget/LicenseBlurb.jsx +++ b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/LicenseWidget/LicenseBlurb.jsx @@ -17,7 +17,7 @@ import { import messages from './messages'; import { LicenseTypes } from '../../../../../../data/constants/licenses'; -export const LicenseBlurb = ({ +const LicenseBlurb = ({ license, details, }) => ( @@ -48,4 +48,5 @@ LicenseBlurb.propTypes = { }).isRequired, }; +export const LicenseBlurbInternal = LicenseBlurb; // For testing only export default injectIntl(LicenseBlurb); diff --git a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/LicenseWidget/LicenseBlurb.test.jsx b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/LicenseWidget/LicenseBlurb.test.jsx index 7ae07a0012..f55458d859 100644 --- a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/LicenseWidget/LicenseBlurb.test.jsx +++ b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/LicenseWidget/LicenseBlurb.test.jsx @@ -1,7 +1,8 @@ +import 'CourseAuthoring/editors/setupEditorTest'; import React from 'react'; import { shallow } from '@edx/react-unit-test-utils'; -import { LicenseBlurb } from './LicenseBlurb'; +import { LicenseBlurbInternal as LicenseBlurb } from './LicenseBlurb'; describe('LicenseBlurb', () => { const props = { diff --git a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/LicenseWidget/LicenseDetails.jsx b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/LicenseWidget/LicenseDetails.jsx index cba3e3b3ad..45937efea6 100644 --- a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/LicenseWidget/LicenseDetails.jsx +++ b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/LicenseWidget/LicenseDetails.jsx @@ -23,7 +23,7 @@ import { actions } from '../../../../../../data/redux'; import { LicenseLevel, LicenseTypes } from '../../../../../../data/constants/licenses'; import messages from './messages'; -export const LicenseDetails = ({ +const LicenseDetails = ({ license, details, level, @@ -166,4 +166,5 @@ export const mapDispatchToProps = (dispatch) => ({ updateField: (stateUpdate) => dispatch(actions.video.updateField(stateUpdate)), }); +export const LicenseDetailsInternal = LicenseDetails; // For testing only export default injectIntl(connect(mapStateToProps, mapDispatchToProps)(LicenseDetails)); diff --git a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/LicenseWidget/LicenseDetails.test.jsx b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/LicenseWidget/LicenseDetails.test.jsx index 23a54c3fb3..48e4abdc38 100644 --- a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/LicenseWidget/LicenseDetails.test.jsx +++ b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/LicenseWidget/LicenseDetails.test.jsx @@ -1,8 +1,9 @@ +import 'CourseAuthoring/editors/setupEditorTest'; import React from 'react'; import { shallow } from '@edx/react-unit-test-utils'; import { actions } from '../../../../../../data/redux'; -import { LicenseDetails, mapStateToProps, mapDispatchToProps } from './LicenseDetails'; +import { LicenseDetailsInternal as LicenseDetails, mapStateToProps, mapDispatchToProps } from './LicenseDetails'; jest.mock('react', () => { const updateState = jest.fn(); @@ -21,18 +22,6 @@ jest.mock('../../../../../../data/redux', () => ({ }, })); -jest.mock('@openedx/paragon', () => ({ - ...jest.requireActual('@openedx/paragon'), - CheckboxControl: 'CheckboxControl', - Stack: 'Stack', - Icon: 'Icon', - Form: { - Group: 'Form.Group', - Label: 'Form.Label', - }, - ActionRow: { Spacer: 'ActionRow.Spacer' }, -})); - describe('LicenseDetails', () => { const props = { license: null, diff --git a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/LicenseWidget/LicenseDisplay.jsx b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/LicenseWidget/LicenseDisplay.jsx index 435d9fd421..a7799eb746 100644 --- a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/LicenseWidget/LicenseDisplay.jsx +++ b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/LicenseWidget/LicenseDisplay.jsx @@ -15,7 +15,7 @@ import { LicenseTypes } from '../../../../../../data/constants/licenses'; import LicenseBlurb from './LicenseBlurb'; import messages from './messages'; -export const LicenseDisplay = ({ +const LicenseDisplay = ({ license, details, licenseDescription, @@ -53,4 +53,5 @@ LicenseDisplay.propTypes = { licenseDescription: PropTypes.string.isRequired, }; +export const LicenseDisplayInternal = LicenseDisplay; // For testing only export default injectIntl(LicenseDisplay); diff --git a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/LicenseWidget/LicenseDisplay.test.jsx b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/LicenseWidget/LicenseDisplay.test.jsx index 21db0c559f..1111694998 100644 --- a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/LicenseWidget/LicenseDisplay.test.jsx +++ b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/LicenseWidget/LicenseDisplay.test.jsx @@ -1,7 +1,8 @@ +import 'CourseAuthoring/editors/setupEditorTest'; import React from 'react'; import { shallow } from '@edx/react-unit-test-utils'; -import { LicenseDisplay } from './LicenseDisplay'; +import { LicenseDisplayInternal as LicenseDisplay } from './LicenseDisplay'; jest.mock('react', () => ({ ...jest.requireActual('react'), diff --git a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/LicenseWidget/LicenseSelector.jsx b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/LicenseWidget/LicenseSelector.jsx index 30fc329d81..7e468982b9 100644 --- a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/LicenseWidget/LicenseSelector.jsx +++ b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/LicenseWidget/LicenseSelector.jsx @@ -15,11 +15,11 @@ import { import { DeleteOutline } from '@openedx/paragon/icons'; import { actions, selectors } from '../../../../../../data/redux'; -import hooks from './hooks'; +import * as hooks from './hooks'; import messages from './messages'; import { LicenseLevel, LicenseNames, LicenseTypes } from '../../../../../../data/constants/licenses'; -export const LicenseSelector = ({ +const LicenseSelector = ({ license, level, // injected @@ -89,4 +89,5 @@ export const mapDispatchToProps = (dispatch) => ({ updateField: (stateUpdate) => dispatch(actions.video.updateField(stateUpdate)), }); +export const LicenseSelectorInternal = LicenseSelector; // For testing only export default injectIntl(connect(mapStateToProps, mapDispatchToProps)(LicenseSelector)); diff --git a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/LicenseWidget/LicenseSelector.test.jsx b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/LicenseWidget/LicenseSelector.test.jsx index 7dffedf420..2b6ee9a0ed 100644 --- a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/LicenseWidget/LicenseSelector.test.jsx +++ b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/LicenseWidget/LicenseSelector.test.jsx @@ -1,9 +1,10 @@ +import 'CourseAuthoring/editors/setupEditorTest'; import React from 'react'; import { shallow } from '@edx/react-unit-test-utils'; -import { formatMessage } from '../../../../../../../testUtils'; +import { formatMessage } from '../../../../../../testUtils'; import { actions, selectors } from '../../../../../../data/redux'; -import { LicenseSelector, mapStateToProps, mapDispatchToProps } from './LicenseSelector'; +import { LicenseSelectorInternal as LicenseSelector, mapStateToProps, mapDispatchToProps } from './LicenseSelector'; jest.mock('react', () => { const updateState = jest.fn(); diff --git a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/LicenseWidget/__snapshots__/LicenseDetails.test.jsx.snap b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/LicenseWidget/__snapshots__/LicenseDetails.test.jsx.snap index b062d7fb84..046cc017d3 100644 --- a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/LicenseWidget/__snapshots__/LicenseDetails.test.jsx.snap +++ b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/LicenseWidget/__snapshots__/LicenseDetails.test.jsx.snap @@ -25,7 +25,7 @@ exports[`LicenseDetails snapshots snapshots: renders as expected with level set className="border-primary-100 border-bottom pb-4" > - <[object Object]> + @@ -44,7 +44,7 @@ exports[`LicenseDetails snapshots snapshots: renders as expected with level set checked={true} disabled={true} /> - +
- <[object Object]> + - +
- <[object Object]> + - +
- <[object Object]> + - +
({ updateField: (stateUpdate) => dispatch(actions.video.updateField(stateUpdate)), }); +export const LicenseWidgetInternal = LicenseWidget; // For testing only export default injectIntl(connect(mapStateToProps, mapDispatchToProps)(LicenseWidget)); diff --git a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/LicenseWidget/index.test.jsx b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/LicenseWidget/index.test.jsx index 823e651660..e7f1836fcc 100644 --- a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/LicenseWidget/index.test.jsx +++ b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/LicenseWidget/index.test.jsx @@ -1,9 +1,10 @@ +import 'CourseAuthoring/editors/setupEditorTest'; import React from 'react'; import { shallow } from '@edx/react-unit-test-utils'; -import { formatMessage } from '../../../../../../../testUtils'; +import { formatMessage } from '../../../../../../testUtils'; import { actions, selectors } from '../../../../../../data/redux'; -import { LicenseWidget, mapStateToProps, mapDispatchToProps } from '.'; +import { LicenseWidgetInternal as LicenseWidget, mapStateToProps, mapDispatchToProps } from '.'; jest.mock('react', () => { const updateState = jest.fn(); diff --git a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/SocialShareWidget/constants.js b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/SocialShareWidget/constants.js index 3b104e33bb..5c382f9bbe 100644 --- a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/SocialShareWidget/constants.js +++ b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/SocialShareWidget/constants.js @@ -1,5 +1,4 @@ +// eslint-disable-next-line import/prefer-default-export export const analyticsEvents = { socialSharingSettingChanged: 'edx.social.video_sharing_setting.changed', }; - -export default analyticsEvents; diff --git a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/SocialShareWidget/hooks.jsx b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/SocialShareWidget/hooks.jsx index f1d6db98ce..4b357af3b5 100644 --- a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/SocialShareWidget/hooks.jsx +++ b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/SocialShareWidget/hooks.jsx @@ -1,7 +1,7 @@ import { useSelector } from 'react-redux'; import { sendTrackEvent } from '@edx/frontend-platform/analytics'; import { selectors } from '../../../../../../data/redux'; -import analyticsEvents from './constants'; +import { analyticsEvents } from './constants'; export const useTrackSocialSharingChange = ({ updateField }) => { const analytics = useSelector(selectors.app.analytics); diff --git a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/SocialShareWidget/hooks.test.jsx b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/SocialShareWidget/hooks.test.jsx index 987b73f835..a70d8ea838 100644 --- a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/SocialShareWidget/hooks.test.jsx +++ b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/SocialShareWidget/hooks.test.jsx @@ -1,5 +1,6 @@ +import 'CourseAuthoring/editors/setupEditorTest'; import { sendTrackEvent } from '@edx/frontend-platform/analytics'; -import analyticsEvents from './constants'; +import { analyticsEvents } from './constants'; import * as hooks from './hooks'; jest.mock('../../../../../../data/redux', () => ({ diff --git a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/SocialShareWidget/index.jsx b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/SocialShareWidget/index.jsx index 862511642b..57b6e25ac2 100644 --- a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/SocialShareWidget/index.jsx +++ b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/SocialShareWidget/index.jsx @@ -19,7 +19,7 @@ import * as hooks from './hooks'; /** * Collapsible Form widget controlling video thumbnail */ -export const SocialShareWidget = ({ +const SocialShareWidget = ({ // injected intl, // redux @@ -116,4 +116,5 @@ export const mapDispatchToProps = (dispatch) => ({ updateField: (stateUpdate) => dispatch(actions.video.updateField(stateUpdate)), }); +export const SocialShareWidgetInternal = SocialShareWidget; // For testing only export default injectIntl(connect(mapStateToProps, mapDispatchToProps)(SocialShareWidget)); diff --git a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/SocialShareWidget/index.test.jsx b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/SocialShareWidget/index.test.jsx index 4335ab7984..2d7fccd76c 100644 --- a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/SocialShareWidget/index.test.jsx +++ b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/SocialShareWidget/index.test.jsx @@ -1,9 +1,10 @@ +import 'CourseAuthoring/editors/setupEditorTest'; import React from 'react'; import { shallow } from '@edx/react-unit-test-utils'; -import { formatMessage } from '../../../../../../../testUtils'; +import { formatMessage } from '../../../../../../testUtils'; import { actions, selectors } from '../../../../../../data/redux'; -import { SocialShareWidget, mapStateToProps, mapDispatchToProps } from '.'; +import { SocialShareWidgetInternal as SocialShareWidget, mapStateToProps, mapDispatchToProps } from '.'; import messages from './messages'; jest.mock('react', () => { diff --git a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/ThumbnailWidget/constants.js b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/ThumbnailWidget/constants.js index 8da5281e0f..17ee797924 100644 --- a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/ThumbnailWidget/constants.js +++ b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/ThumbnailWidget/constants.js @@ -14,7 +14,7 @@ export const MAX_WIDTH = 1280; export const MAX_HEIGHT = 720; export const MIN_WIDTH = 640; export const MIN_HEIGHT = 360; -// eslint-disable-next-line no-loss-of-precision +// eslint-disable-next-line no-loss-of-precision, @typescript-eslint/no-loss-of-precision export const ASPECT_RATIO = 1.7777777777777777777; export const ASPECT_RATIO_ERROR_MARGIN = 0.1; export default { diff --git a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/ThumbnailWidget/hooks.js b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/ThumbnailWidget/hooks.js index 1ece21af09..6277ac4bd5 100644 --- a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/ThumbnailWidget/hooks.js +++ b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/ThumbnailWidget/hooks.js @@ -2,6 +2,10 @@ import React from 'react'; import { useDispatch } from 'react-redux'; import { actions, thunkActions } from '../../../../../../data/redux'; import * as constants from './constants'; +// This 'module' self-import hack enables mocking during tests. +// See src/editors/decisions/0005-internal-editor-testability-decisions.md. The whole approach to how hooks are tested +// should be re-thought and cleaned up to avoid this pattern. +// eslint-disable-next-line import/no-self-import import * as module from './hooks'; export const state = { diff --git a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/ThumbnailWidget/hooks.test.jsx b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/ThumbnailWidget/hooks.test.jsx index ed4b65241e..d59b541fad 100644 --- a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/ThumbnailWidget/hooks.test.jsx +++ b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/ThumbnailWidget/hooks.test.jsx @@ -1,7 +1,8 @@ +import 'CourseAuthoring/editors/setupEditorTest'; import React from 'react'; -import { dispatch } from 'react-redux'; +import { useDispatch } from 'react-redux'; import { actions, thunkActions } from '../../../../../../data/redux'; -import { MockUseState } from '../../../../../../../testUtils'; +import { MockUseState } from '../../../../../../testUtils'; import { keyStore } from '../../../../../../utils'; import * as hooks from './hooks'; @@ -12,15 +13,6 @@ jest.mock('react', () => ({ useCallback: (cb, prereqs) => ({ cb, prereqs }), })); -jest.mock('react-redux', () => { - const dispatchFn = jest.fn(); - return { - ...jest.requireActual('react-redux'), - dispatch: dispatchFn, - useDispatch: jest.fn(() => dispatchFn), - }; -}); - jest.mock('../../../../../../data/redux', () => ({ actions: { video: { @@ -128,6 +120,7 @@ describe('fileInput', () => { expect(spies.checkValidSize).toHaveReturnedWith(false); }); it('dispatches updateField action with the first target file', () => { + const dispatch = useDispatch(); // Access the mock 'dispatch()' set up in setupEditorTest const checkValidSize = true; spies.checkValidSize = jest.spyOn(hooks, hookKeys.checkValidSize) .mockReturnValueOnce(checkValidSize); @@ -142,6 +135,7 @@ describe('fileInput', () => { }); }); describe('deleteThumbnail', () => { + const dispatch = useDispatch(); // Access the mock 'dispatch()' set up in setupEditorTest const testFile = new File([selectedFileSuccess], 'sOMEUrl.jpg'); hooks.deleteThumbnail({ dispatch })(); expect(dispatch).toHaveBeenNthCalledWith(1, actions.video.updateField({ thumbnail: null })); diff --git a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/ThumbnailWidget/index.jsx b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/ThumbnailWidget/index.jsx index a0bfa41904..72226c50cf 100644 --- a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/ThumbnailWidget/index.jsx +++ b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/ThumbnailWidget/index.jsx @@ -24,14 +24,14 @@ import * as hooks from './hooks'; import messages from './messages'; import CollapsibleFormWidget from '../CollapsibleFormWidget'; -import FileInput from '../../../../../../sharedComponents/FileInput'; +import { FileInput } from '../../../../../../sharedComponents/FileInput'; import ErrorAlert from '../../../../../../sharedComponents/ErrorAlerts/ErrorAlert'; import { ErrorContext } from '../../../../hooks'; /** * Collapsible Form widget controlling video thumbnail */ -export const ThumbnailWidget = ({ +const ThumbnailWidget = ({ // injected intl, // redux @@ -143,4 +143,5 @@ export const mapStateToProps = (state) => ({ export const mapDispatchToProps = {}; +export const ThumbnailWidgetInternal = ThumbnailWidget; // For testing only export default injectIntl(connect(mapStateToProps, mapDispatchToProps)(ThumbnailWidget)); diff --git a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/ThumbnailWidget/index.test.jsx b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/ThumbnailWidget/index.test.jsx index 66309718c1..0b761701c6 100644 --- a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/ThumbnailWidget/index.test.jsx +++ b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/ThumbnailWidget/index.test.jsx @@ -1,9 +1,10 @@ +import 'CourseAuthoring/editors/setupEditorTest'; import React from 'react'; import { shallow } from '@edx/react-unit-test-utils'; -import { formatMessage } from '../../../../../../../testUtils'; +import { formatMessage } from '../../../../../../testUtils'; import { selectors } from '../../../../../../data/redux'; -import { ThumbnailWidget, mapStateToProps, mapDispatchToProps } from '.'; +import { ThumbnailWidgetInternal as ThumbnailWidget, mapStateToProps, mapDispatchToProps } from '.'; jest.mock('react', () => ({ ...jest.requireActual('react'), diff --git a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/ImportTranscriptCard.jsx b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/ImportTranscriptCard.jsx index bbd3d361b2..21983b45f3 100644 --- a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/ImportTranscriptCard.jsx +++ b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/ImportTranscriptCard.jsx @@ -15,7 +15,7 @@ import { Close } from '@openedx/paragon/icons'; import messages from './messages'; import { thunkActions } from '../../../../../../data/redux'; -export const ImportTranscriptCard = ({ +const ImportTranscriptCard = ({ setOpen, // redux importTranscript, @@ -57,4 +57,5 @@ export const mapDispatchToProps = { importTranscript: thunkActions.video.importTranscript, }; +export const ImportTranscriptCardInternal = ImportTranscriptCard; // For testing only export default injectIntl(connect(mapStateToProps, mapDispatchToProps)(ImportTranscriptCard)); diff --git a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/ImportTranscriptCard.test.jsx b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/ImportTranscriptCard.test.jsx index 977c7571b4..1a55bc98ca 100644 --- a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/ImportTranscriptCard.test.jsx +++ b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/ImportTranscriptCard.test.jsx @@ -1,9 +1,10 @@ +import 'CourseAuthoring/editors/setupEditorTest'; import React from 'react'; import { shallow } from '@edx/react-unit-test-utils'; import { Button, IconButton } from '@openedx/paragon'; import { thunkActions } from '../../../../../../data/redux'; -import * as module from './ImportTranscriptCard'; +import { ImportTranscriptCardInternal as ImportTranscriptCard, mapDispatchToProps, mapStateToProps } from './ImportTranscriptCard'; jest.mock('react', () => ({ ...jest.requireActual('react'), @@ -27,13 +28,13 @@ describe('ImportTranscriptCard', () => { describe('snapshots', () => { test('snapshots: renders as expected with default props', () => { expect( - shallow().snapshot, + shallow().snapshot, ).toMatchSnapshot(); }); }); describe('behavior inspection', () => { beforeEach(() => { - el = shallow(); + el = shallow(); }); test('close behavior is linked to IconButton', () => { expect(el.instance.findByType(IconButton)[0] @@ -46,12 +47,12 @@ describe('ImportTranscriptCard', () => { }); describe('mapStateToProps', () => { it('returns an empty object', () => { - expect(module.mapStateToProps()).toEqual({}); + expect(mapStateToProps()).toEqual({}); }); }); describe('mapDispatchToProps', () => { test('updateField from thunkActions.video.importTranscript', () => { - expect(module.mapDispatchToProps.importTranscript).toEqual(thunkActions.video.importTranscript); + expect(mapDispatchToProps.importTranscript).toEqual(thunkActions.video.importTranscript); }); }); }); diff --git a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/LanguageSelector.jsx b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/LanguageSelector.jsx index 8c94d10fb1..90d3ec235b 100644 --- a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/LanguageSelector.jsx +++ b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/LanguageSelector.jsx @@ -15,6 +15,10 @@ import { thunkActions, selectors } from '../../../../../../data/redux'; import { videoTranscriptLanguages } from '../../../../../../data/constants/video'; import { FileInput, fileInput } from '../../../../../../sharedComponents/FileInput'; import messages from './messages'; +// This 'module' self-import hack enables mocking during tests. +// See src/editors/decisions/0005-internal-editor-testability-decisions.md. The whole approach to how hooks are tested +// should be re-thought and cleaned up to avoid this pattern. +// eslint-disable-next-line import/no-self-import import * as module from './LanguageSelector'; export const hooks = { @@ -45,7 +49,7 @@ export const hooks = { }; -export const LanguageSelector = ({ +const LanguageSelector = ({ index, // For a unique id for the form control language, // Redux @@ -129,4 +133,5 @@ export const mapStateToProps = (state) => ({ export const mapDispatchToProps = {}; +export const LanguageSelectorInternal = LanguageSelector; // For testing only export default injectIntl(connect(mapStateToProps, mapDispatchToProps)(LanguageSelector)); diff --git a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/LanguageSelector.test.jsx b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/LanguageSelector.test.jsx index f45665f35a..0eae896c6f 100644 --- a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/LanguageSelector.test.jsx +++ b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/LanguageSelector.test.jsx @@ -1,7 +1,8 @@ +import 'CourseAuthoring/editors/setupEditorTest'; import React from 'react'; import { shallow } from '@edx/react-unit-test-utils'; -import { LanguageSelector } from './LanguageSelector'; -import { formatMessage } from '../../../../../../../testUtils'; +import { LanguageSelectorInternal as LanguageSelector } from './LanguageSelector'; +import { formatMessage } from '../../../../../../testUtils'; const lang1 = 'kLinGon'; const lang1Code = 'kl'; diff --git a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/Transcript.jsx b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/Transcript.jsx index 85c1828a39..6300fee194 100644 --- a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/Transcript.jsx +++ b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/Transcript.jsx @@ -20,6 +20,10 @@ import { thunkActions } from '../../../../../../data/redux'; import TranscriptActionMenu from './TranscriptActionMenu'; import LanguageSelector from './LanguageSelector'; +// This 'module' self-import hack enables mocking during tests. +// See src/editors/decisions/0005-internal-editor-testability-decisions.md. The whole approach to how hooks are tested +// should be re-thought and cleaned up to avoid this pattern. +// eslint-disable-next-line import/no-self-import import * as module from './Transcript'; import messages from './messages'; @@ -38,7 +42,7 @@ export const hooks = { }, }; -export const Transcript = ({ +const Transcript = ({ index, language, transcriptUrl, @@ -120,4 +124,5 @@ export const mapDispatchToProps = { deleteTranscript: thunkActions.video.deleteTranscript, }; +export const TranscriptInternal = Transcript; // For testing only export default injectIntl(connect(mapStateToProps, mapDispatchToProps)(Transcript)); diff --git a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/Transcript.test.jsx b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/Transcript.test.jsx index 6216caefb2..ed16d2f530 100644 --- a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/Transcript.test.jsx +++ b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/Transcript.test.jsx @@ -1,9 +1,12 @@ +import 'CourseAuthoring/editors/setupEditorTest'; import React from 'react'; import { shallow } from '@edx/react-unit-test-utils'; import * as module from './Transcript'; -import { MockUseState } from '../../../../../../../testUtils'; +import { MockUseState } from '../../../../../../testUtils'; + +const Transcript = module.TranscriptInternal; jest.mock('./LanguageSelector', () => 'LanguageSelector'); jest.mock('./TranscriptActionMenu', () => 'TranscriptActionMenu'); @@ -57,7 +60,7 @@ describe('Transcript Component', () => { cancelDelete: jest.fn().mockName('cancelDelete'), })); expect( - shallow().snapshot, + shallow().snapshot, ).toMatchSnapshot(); }); test('snapshots: renders as expected with default props: dont show confirm delete, language is blank so delete is shown instead of action menu', () => { @@ -67,7 +70,7 @@ describe('Transcript Component', () => { cancelDelete: jest.fn().mockName('cancelDelete'), })); expect( - shallow().snapshot, + shallow().snapshot, ).toMatchSnapshot(); }); test('snapshots: renders as expected with default props: show confirm delete', () => { @@ -77,7 +80,7 @@ describe('Transcript Component', () => { cancelDelete: jest.fn().mockName('cancelDelete'), })); expect( - shallow().snapshot, + shallow().snapshot, ).toMatchSnapshot(); }); test('snapshots: renders as expected with transcriptUrl', () => { @@ -87,7 +90,7 @@ describe('Transcript Component', () => { cancelDelete: jest.fn().mockName('cancelDelete'), })); expect( - shallow().snapshot, + shallow().snapshot, ).toMatchSnapshot(); }); }); diff --git a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/TranscriptActionMenu.jsx b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/TranscriptActionMenu.jsx index 553e7f570c..46f51180bc 100644 --- a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/TranscriptActionMenu.jsx +++ b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/TranscriptActionMenu.jsx @@ -9,6 +9,10 @@ import { MoreHoriz } from '@openedx/paragon/icons'; import { thunkActions, selectors } from '../../../../../../data/redux'; import { FileInput, fileInput } from '../../../../../../sharedComponents/FileInput'; +// This 'module' self-import hack enables mocking during tests. +// See src/editors/decisions/0005-internal-editor-testability-decisions.md. The whole approach to how hooks are tested +// should be re-thought and cleaned up to avoid this pattern. +// eslint-disable-next-line import/no-self-import import * as module from './TranscriptActionMenu'; import messages from './messages'; @@ -22,7 +26,7 @@ export const hooks = { }, }; -export const TranscriptActionMenu = ({ +const TranscriptActionMenu = ({ index, language, transcriptUrl, @@ -85,4 +89,5 @@ export const mapDispatchToProps = { downloadTranscript: thunkActions.video.downloadTranscript, }; +export const TranscriptActionMenuInternal = TranscriptActionMenu; // For testing only export default injectIntl(connect(mapStateToProps, mapDispatchToProps)(TranscriptActionMenu)); diff --git a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/TranscriptActionMenu.test.jsx b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/TranscriptActionMenu.test.jsx index a22eda8a85..b23f5040e8 100644 --- a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/TranscriptActionMenu.test.jsx +++ b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/TranscriptActionMenu.test.jsx @@ -1,3 +1,4 @@ +import 'CourseAuthoring/editors/setupEditorTest'; import React from 'react'; import { shallow } from '@edx/react-unit-test-utils'; @@ -5,6 +6,8 @@ import { thunkActions, selectors } from '../../../../../../data/redux'; import * as module from './TranscriptActionMenu'; +const TranscriptActionMenu = module.TranscriptActionMenuInternal; + jest.mock('react-redux', () => { const dispatchFn = jest.fn().mockName('mockUseDispatch'); return { @@ -71,13 +74,13 @@ describe('TranscriptActionMenu', () => { test('snapshots: renders as expected with default props: dont show confirm delete', () => { jest.spyOn(module.hooks, 'replaceFileCallback').mockImplementationOnce(() => jest.fn().mockName('module.hooks.replaceFileCallback')); expect( - shallow().snapshot, + shallow().snapshot, ).toMatchSnapshot(); }); test('snapshots: renders as expected with transcriptUrl props: dont show confirm delete', () => { jest.spyOn(module.hooks, 'replaceFileCallback').mockImplementationOnce(() => jest.fn().mockName('module.hooks.replaceFileCallback')); expect( - shallow().snapshot, + shallow().snapshot, ).toMatchSnapshot(); }); }); diff --git a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/index.jsx b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/index.jsx index d2a56e723c..b734b31ff4 100644 --- a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/index.jsx +++ b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/index.jsx @@ -29,6 +29,10 @@ import CollapsibleFormWidget from '../CollapsibleFormWidget'; import ImportTranscriptCard from './ImportTranscriptCard'; import Transcript from './Transcript'; import { ErrorContext } from '../../../../hooks'; +// This 'module' self-import hack enables mocking during tests. +// See src/editors/decisions/0005-internal-editor-testability-decisions.md. The whole approach to how hooks are tested +// should be re-thought and cleaned up to avoid this pattern. +// eslint-disable-next-line import/no-self-import import * as module from './index'; export const hooks = { @@ -76,7 +80,7 @@ export const hooks = { /** * Collapsible Form widget controlling video transcripts */ -export const TranscriptWidget = ({ +const TranscriptWidget = ({ // redux transcripts, selectedVideoTranscriptUrls, @@ -209,4 +213,5 @@ export const mapDispatchToProps = (dispatch) => ({ updateField: (stateUpdate) => dispatch(actions.video.updateField(stateUpdate)), }); +export const TranscriptWidgetInternal = TranscriptWidget; // For testing only export default injectIntl(connect(mapStateToProps, mapDispatchToProps)(TranscriptWidget)); diff --git a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/index.test.jsx b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/index.test.jsx index 74353290c3..7e0c54effd 100644 --- a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/index.test.jsx +++ b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/index.test.jsx @@ -1,12 +1,19 @@ +import 'CourseAuthoring/editors/setupEditorTest'; import React from 'react'; import { shallow } from '@edx/react-unit-test-utils'; import { RequestKeys } from '../../../../../../data/constants/requests'; -import { formatMessage } from '../../../../../../../testUtils'; +import { formatMessage } from '../../../../../../testUtils'; import { actions, selectors } from '../../../../../../data/redux'; +// This 'module' self-import hack enables mocking during tests. +// See src/editors/decisions/0005-internal-editor-testability-decisions.md. The whole approach to how hooks are tested +// should be re-thought and cleaned up to avoid this pattern. +// eslint-disable-next-line import/no-self-import import * as module from './index'; +const TranscriptWidget = module.TranscriptWidgetInternal; + jest.mock('react', () => ({ ...jest.requireActual('react'), useContext: jest.fn(() => ({ transcripts: ['error.transcripts', jest.fn().mockName('error.setTranscripts')] })), @@ -101,47 +108,47 @@ describe('TranscriptWidget', () => { describe('snapshots', () => { test('snapshots: renders as expected with default props', () => { expect( - shallow().snapshot, + shallow().snapshot, ).toMatchSnapshot(); }); test('snapshots: renders as expected with allowTranscriptImport true', () => { expect( - shallow().snapshot, + shallow().snapshot, ).toMatchSnapshot(); }); test('snapshots: renders as expected with transcripts', () => { expect( - shallow().snapshot, + shallow().snapshot, ).toMatchSnapshot(); }); test('snapshots: renders as expected with transcript urls', () => { expect( - shallow().snapshot, + shallow().snapshot, ).toMatchSnapshot(); }); test('snapshots: renders as expected with transcripts and urls', () => { expect( - shallow().snapshot, + shallow().snapshot, ).toMatchSnapshot(); }); test('snapshots: renders as expected with allowTranscriptDownloads true', () => { expect( - shallow().snapshot, + shallow().snapshot, ).toMatchSnapshot(); }); test('snapshots: renders as expected with showTranscriptByDefault true', () => { expect( - shallow().snapshot, + shallow().snapshot, ).toMatchSnapshot(); }); test('snapshot: renders ErrorAlert with upload error message', () => { expect( - shallow().snapshot, + shallow().snapshot, ).toMatchSnapshot(); }); test('snapshot: renders ErrorAlert with delete error message', () => { expect( - shallow().snapshot, + shallow().snapshot, ).toMatchSnapshot(); }); }); diff --git a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/VideoPreviewWidget/LanguageNamesWidget.jsx b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/VideoPreviewWidget/LanguageNamesWidget.jsx index bbe8dd83ae..2647d07c87 100644 --- a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/VideoPreviewWidget/LanguageNamesWidget.jsx +++ b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/VideoPreviewWidget/LanguageNamesWidget.jsx @@ -6,7 +6,7 @@ import React from 'react'; import messages from '../messages'; import { hooks as transcriptHooks } from '../TranscriptWidget'; -export const LanguageNamesWidget = ({ transcripts, intl }) => { +const LanguageNamesWidget = ({ transcripts, intl }) => { let icon = ClosedCaptionOff; const hasTranscripts = transcriptHooks.hasTranscripts(transcripts); let message = intl.formatMessage(messages.noTranscriptsAdded); diff --git a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/VideoPreviewWidget/index.jsx b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/VideoPreviewWidget/index.jsx index 47aa9911bc..0c4a0f089a 100644 --- a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/VideoPreviewWidget/index.jsx +++ b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/VideoPreviewWidget/index.jsx @@ -11,7 +11,7 @@ import hooks from './hooks'; import LanguageNamesWidget from './LanguageNamesWidget'; import videoThumbnail from '../../../../../../data/images/videoThumbnail.svg'; -export const VideoPreviewWidget = ({ +const VideoPreviewWidget = ({ thumbnail, videoSource, transcripts, diff --git a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/VideoSourceWidget/hooks.test.jsx b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/VideoSourceWidget/hooks.test.jsx index 7bc8d6ad7f..096f98010b 100644 --- a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/VideoSourceWidget/hooks.test.jsx +++ b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/VideoSourceWidget/hooks.test.jsx @@ -1,6 +1,6 @@ import { dispatch } from 'react-redux'; import { actions } from '../../../../../../data/redux'; -import { MockUseState } from '../../../../../../../testUtils'; +import { MockUseState } from '../../../../../../testUtils'; import * as requests from '../../../../../../data/redux/thunkActions/requests'; import * as hooks from './hooks'; diff --git a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/VideoSourceWidget/index.jsx b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/VideoSourceWidget/index.jsx index 9726266fad..85045f0810 100644 --- a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/VideoSourceWidget/index.jsx +++ b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/VideoSourceWidget/index.jsx @@ -21,13 +21,13 @@ import * as widgetHooks from '../hooks'; import * as hooks from './hooks'; import messages from './messages'; -import { ErrorAlert } from '../../../../../../sharedComponents/ErrorAlerts/ErrorAlert'; +import ErrorAlert from '../../../../../../sharedComponents/ErrorAlerts/ErrorAlert'; import CollapsibleFormWidget from '../CollapsibleFormWidget'; /** * Collapsible Form widget controlling video source as well as fallback sources */ -export const VideoSourceWidget = ({ +const VideoSourceWidget = ({ // injected intl, }) => { @@ -157,4 +157,5 @@ VideoSourceWidget.propTypes = { intl: intlShape.isRequired, }; +export const VideoSourceWidgetInternal = VideoSourceWidget; // For testing only export default injectIntl(VideoSourceWidget); diff --git a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/VideoSourceWidget/index.test.jsx b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/VideoSourceWidget/index.test.jsx index a88f9a3fc1..c472e65129 100644 --- a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/VideoSourceWidget/index.test.jsx +++ b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/VideoSourceWidget/index.test.jsx @@ -1,9 +1,10 @@ +import 'CourseAuthoring/editors/setupEditorTest'; import React from 'react'; import { dispatch } from 'react-redux'; import { shallow } from '@edx/react-unit-test-utils'; -import { formatMessage } from '../../../../../../../testUtils'; -import { VideoSourceWidget } from '.'; +import { formatMessage } from '../../../../../../testUtils'; +import { VideoSourceWidgetInternal as VideoSourceWidget } from '.'; import * as hooks from './hooks'; jest.mock('react-redux', () => { diff --git a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/hooks.js b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/hooks.js index a51ffd377b..d0b8503e2b 100644 --- a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/hooks.js +++ b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/hooks.js @@ -13,6 +13,10 @@ import { onValue, onChecked, } from './handlers'; +// This 'module' self-import hack enables mocking during tests. +// See src/editors/decisions/0005-internal-editor-testability-decisions.md. The whole approach to how hooks are tested +// should be re-thought and cleaned up to avoid this pattern. +// eslint-disable-next-line import/no-self-import import * as module from './hooks'; export const selectorKeys = keyStore(selectors.video); @@ -254,11 +258,3 @@ export const widgetValues = ({ fields, dispatch }) => Object.keys(fields).reduce }), {}, ); - -export default { - arrayWidget, - genericWidget, - objectWidget, - selectorKeys, - widgetValues, -}; diff --git a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/hooks.test.js b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/hooks.test.js index c5acb0c646..ebfa493729 100644 --- a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/hooks.test.js +++ b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/hooks.test.js @@ -1,9 +1,11 @@ +import 'CourseAuthoring/editors/setupEditorTest'; + import { useEffect } from 'react'; import { useSelector } from 'react-redux'; import { keyStore } from '../../../../../utils'; import { actions, selectors } from '../../../../../data/redux'; -import { MockUseState } from '../../../../../../testUtils'; +import { MockUseState } from '../../../../../testUtils'; import * as handlers from './handlers'; import * as hooks from './hooks'; diff --git a/src/editors/containers/VideoEditor/components/VideoSettingsModal/index.jsx b/src/editors/containers/VideoEditor/components/VideoSettingsModal/index.jsx index 138ba0ab3a..5f01ec92cb 100644 --- a/src/editors/containers/VideoEditor/components/VideoSettingsModal/index.jsx +++ b/src/editors/containers/VideoEditor/components/VideoSettingsModal/index.jsx @@ -8,7 +8,7 @@ import { } from '@edx/frontend-platform/i18n'; // import VideoPreview from './components/VideoPreview'; -import ErrorSummary from './ErrorSummary'; +import { ErrorSummary } from './ErrorSummary'; import DurationWidget from './components/DurationWidget'; import HandoutWidget from './components/HandoutWidget'; import LicenseWidget from './components/LicenseWidget'; @@ -20,7 +20,7 @@ import './index.scss'; import SocialShareWidget from './components/SocialShareWidget'; import messages from '../../messages'; -export const VideoSettingsModal = ({ +const VideoSettingsModal = ({ onReturn, isLibrary, }) => ( diff --git a/src/editors/containers/VideoEditor/components/VideoSettingsModal/index.scss b/src/editors/containers/VideoEditor/components/VideoSettingsModal/index.scss index 28185d48c6..119b04dfba 100644 --- a/src/editors/containers/VideoEditor/components/VideoSettingsModal/index.scss +++ b/src/editors/containers/VideoEditor/components/VideoSettingsModal/index.scss @@ -1,6 +1,6 @@ -.video-settings-modal { - .video-preview { - } - .video-controls { - } -} +// .video-settings-modal { +// .video-preview { +// } +// .video-controls { +// } +// } diff --git a/src/editors/containers/VideoEditor/hooks.js b/src/editors/containers/VideoEditor/hooks.js index 392f47d9be..c046888879 100644 --- a/src/editors/containers/VideoEditor/hooks.js +++ b/src/editors/containers/VideoEditor/hooks.js @@ -2,6 +2,10 @@ import { useState, createContext } from 'react'; import { thunkActions } from '../../data/redux'; import { StrictDict } from '../../utils'; +// This 'module' self-import hack enables mocking during tests. +// See src/editors/decisions/0005-internal-editor-testability-decisions.md. The whole approach to how hooks are tested +// should be re-thought and cleaned up to avoid this pattern. +// eslint-disable-next-line import/no-self-import import * as module from './hooks'; export const ErrorContext = createContext(); diff --git a/src/editors/containers/VideoEditor/hooks.test.js b/src/editors/containers/VideoEditor/hooks.test.js index 5ee6652102..32a8df46d8 100644 --- a/src/editors/containers/VideoEditor/hooks.test.js +++ b/src/editors/containers/VideoEditor/hooks.test.js @@ -1,7 +1,11 @@ import { dispatch } from 'react-redux'; import { thunkActions } from '../../data/redux'; -import { MockUseState } from '../../../testUtils'; +import { MockUseState } from '../../testUtils'; +// This 'module' self-import hack enables mocking during tests. +// See src/editors/decisions/0005-internal-editor-testability-decisions.md. The whole approach to how hooks are tested +// should be re-thought and cleaned up to avoid this pattern. +// eslint-disable-next-line import/no-self-import import * as module from './hooks'; jest.mock('react', () => ({ diff --git a/src/editors/containers/VideoEditor/index.jsx b/src/editors/containers/VideoEditor/index.jsx index 6bd0236459..633e51f9f2 100644 --- a/src/editors/containers/VideoEditor/index.jsx +++ b/src/editors/containers/VideoEditor/index.jsx @@ -15,7 +15,7 @@ import VideoEditorModal from './components/VideoEditorModal'; import { ErrorContext, errorsHook, fetchVideoContent } from './hooks'; import messages from './messages'; -export const VideoEditor = ({ +const VideoEditor = ({ onClose, returnFunction, // injected @@ -81,4 +81,5 @@ export const mapStateToProps = (state) => ({ export const mapDispatchToProps = {}; +export const VideoEditorInternal = VideoEditor; // For testing only export default injectIntl(connect(mapStateToProps, mapDispatchToProps)(VideoEditor)); diff --git a/src/editors/containers/VideoEditor/index.test.jsx b/src/editors/containers/VideoEditor/index.test.jsx index fd59b97eb5..99c1f157a4 100644 --- a/src/editors/containers/VideoEditor/index.test.jsx +++ b/src/editors/containers/VideoEditor/index.test.jsx @@ -1,10 +1,11 @@ +import 'CourseAuthoring/editors/setupEditorTest'; import React from 'react'; import { shallow } from '@edx/react-unit-test-utils'; -import { formatMessage } from '../../../testUtils'; +import { formatMessage } from '../../testUtils'; import { selectors } from '../../data/redux'; import { RequestKeys } from '../../data/constants/requests'; -import { VideoEditor, mapStateToProps, mapDispatchToProps } from '.'; +import { VideoEditorInternal as VideoEditor, mapStateToProps, mapDispatchToProps } from '.'; jest.mock('../EditorContainer', () => 'EditorContainer'); jest.mock('./components/VideoEditorModal', () => 'VideoEditorModal'); diff --git a/src/editors/containers/VideoGallery/hooks.js b/src/editors/containers/VideoGallery/hooks.js index d52a1cf316..648db7a641 100644 --- a/src/editors/containers/VideoGallery/hooks.js +++ b/src/editors/containers/VideoGallery/hooks.js @@ -1,6 +1,9 @@ import React from 'react'; import { useSelector } from 'react-redux'; - +// This 'module' self-import hack enables mocking during tests. +// See src/editors/decisions/0005-internal-editor-testability-decisions.md. The whole approach to how hooks are tested +// should be re-thought and cleaned up to avoid this pattern. +// eslint-disable-next-line import/no-self-import import * as module from './hooks'; import messages from './messages'; import * as appHooks from '../../hooks'; @@ -211,10 +214,3 @@ export const useVideoProps = ({ videos }) => { selectBtnProps, }; }; - -export default { - useVideoProps, - buildVideos, - useCancelHandler, - useVideoUploadHandler, -}; diff --git a/src/editors/containers/VideoGallery/index.jsx b/src/editors/containers/VideoGallery/index.jsx index 8973a09521..5f5a8a8ca8 100644 --- a/src/editors/containers/VideoGallery/index.jsx +++ b/src/editors/containers/VideoGallery/index.jsx @@ -2,14 +2,14 @@ import React, { useEffect } from 'react'; import { Image } from '@openedx/paragon'; import { useSelector } from 'react-redux'; import { selectors } from '../../data/redux'; -import hooks from './hooks'; +import * as hooks from './hooks'; import SelectionModal from '../../sharedComponents/SelectionModal'; import { acceptedImgKeys } from './utils'; import messages from './messages'; import { RequestKeys } from '../../data/constants/requests'; import videoThumbnail from '../../data/images/videoThumbnail.svg'; -export const VideoGallery = () => { +const VideoGallery = () => { const rawVideos = useSelector(selectors.app.videos); const isLoaded = useSelector( (state) => selectors.requests.isFinished(state, { requestKey: RequestKeys.fetchVideos }), diff --git a/src/editors/containers/VideoGallery/index.test.jsx b/src/editors/containers/VideoGallery/index.test.jsx index 3abae625ed..79771c90d8 100644 --- a/src/editors/containers/VideoGallery/index.test.jsx +++ b/src/editors/containers/VideoGallery/index.test.jsx @@ -7,7 +7,7 @@ import { act, fireEvent, render, screen, } from '@testing-library/react'; -import { VideoGallery } from './index'; +import VideoGallery from './index'; jest.unmock('react-redux'); jest.unmock('@edx/frontend-platform/i18n'); diff --git a/src/editors/containers/VideoGallery/messages.js b/src/editors/containers/VideoGallery/messages.js index c0c582c359..e26dd63db3 100644 --- a/src/editors/containers/VideoGallery/messages.js +++ b/src/editors/containers/VideoGallery/messages.js @@ -1,4 +1,4 @@ -export const messages = { +const messages = { // Gallery emptyGalleryLabel: { id: 'authoring.selectvideomodal.emptyGalleryLabel', diff --git a/src/editors/containers/VideoUploadEditor/hooks.js b/src/editors/containers/VideoUploadEditor/hooks.js index e0f3f38475..a2774d9c60 100644 --- a/src/editors/containers/VideoUploadEditor/hooks.js +++ b/src/editors/containers/VideoUploadEditor/hooks.js @@ -1,3 +1,7 @@ +// This 'module' self-import hack enables mocking during tests. +// See src/editors/decisions/0005-internal-editor-testability-decisions.md. The whole approach to how hooks are tested +// should be re-thought and cleaned up to avoid this pattern. +// eslint-disable-next-line import/no-self-import import * as module from './hooks'; import { selectors, thunkActions } from '../../data/redux'; import store from '../../data/store'; diff --git a/src/editors/containers/VideoUploadEditor/index.jsx b/src/editors/containers/VideoUploadEditor/index.jsx index 8aabeb5430..ae2be7b5fb 100644 --- a/src/editors/containers/VideoUploadEditor/index.jsx +++ b/src/editors/containers/VideoUploadEditor/index.jsx @@ -5,7 +5,7 @@ import './index.scss'; import messages from './messages'; import { VideoUploader } from './VideoUploader'; -export const VideoUploadEditor = () => { +const VideoUploadEditor = () => { const [loading, setLoading] = React.useState(false); const intl = useIntl(); diff --git a/src/editors/containers/VideoUploadEditor/index.scss b/src/editors/containers/VideoUploadEditor/index.scss index 2c0565c6c3..4e27f44d12 100644 --- a/src/editors/containers/VideoUploadEditor/index.scss +++ b/src/editors/containers/VideoUploadEditor/index.scss @@ -1,5 +1,5 @@ .dropzone-middle { - border: 2px dashed #ccc; + border: 2px dashed #CCCCCC; &.active { border: 2px solid #262626; /* change color when active */ @@ -12,7 +12,6 @@ } .video-id-prompt { - input::placeholder { color: #454545; // color: #5E35B1; @@ -34,6 +33,6 @@ } .prompt-button { - background: rgba(239, 234, 247, 0.70); + background: rgba(239 234 247 / .70); } } diff --git a/src/editors/data/constants/advancedOlxTemplates/circuitschematic.js b/src/editors/data/constants/advancedOlxTemplates/circuitschematic.js index 032f0b0727..e3d5eb6ada 100644 --- a/src/editors/data/constants/advancedOlxTemplates/circuitschematic.js +++ b/src/editors/data/constants/advancedOlxTemplates/circuitschematic.js @@ -4,7 +4,7 @@ // display_name: Circuit Schematic Builder // markdown: !!null // data: | -export const circuitSchematic = ` +const circuitSchematic = `

Circuit schematic problems allow students to create virtual circuits by arranging elements such as voltage sources, capacitors, resistors, and diff --git a/src/editors/data/constants/advancedOlxTemplates/customgrader.js b/src/editors/data/constants/advancedOlxTemplates/customgrader.js index 227d02c084..f24c59263b 100644 --- a/src/editors/data/constants/advancedOlxTemplates/customgrader.js +++ b/src/editors/data/constants/advancedOlxTemplates/customgrader.js @@ -4,7 +4,7 @@ // display_name: Custom Python-Evaluated Input // markdown: !!null // data: | -export const customGrader = ` +const customGrader = `

In custom Python-evaluated input (also called "write-your-own-grader" problems), the grader uses a Python script that you create and embed in diff --git a/src/editors/data/constants/advancedOlxTemplates/formularesponse.js b/src/editors/data/constants/advancedOlxTemplates/formularesponse.js index b77a166ae8..72aaa62c65 100644 --- a/src/editors/data/constants/advancedOlxTemplates/formularesponse.js +++ b/src/editors/data/constants/advancedOlxTemplates/formularesponse.js @@ -4,7 +4,7 @@ // display_name: Math Expression Input // markdown: !!null // data: | -export const formulaResponse = ` +const formulaResponse = `

You can use this template as a guide to the OLX markup to use for math expression problems. Edit this component to replace the example with your own assessment.

diff --git a/src/editors/data/constants/advancedOlxTemplates/imageresponse.js b/src/editors/data/constants/advancedOlxTemplates/imageresponse.js index 36ac9e2c65..1980d40ead 100644 --- a/src/editors/data/constants/advancedOlxTemplates/imageresponse.js +++ b/src/editors/data/constants/advancedOlxTemplates/imageresponse.js @@ -4,7 +4,7 @@ // display_name: Image Mapped Input // markdown: !!null // data: | -export const imageResponse = ` +const imageResponse = `

In an image mapped input problem, also known as a "pointing on a picture" problem, students click inside a defined region in an image. You define this region by including coordinates in the body of the problem. You can define one rectangular region, multiple rectangular regions, or one non-rectangular region. For more information, see diff --git a/src/editors/data/constants/advancedOlxTemplates/jsinput_response.js b/src/editors/data/constants/advancedOlxTemplates/jsinput_response.js index 6ebaa1565a..34e6f6e55c 100644 --- a/src/editors/data/constants/advancedOlxTemplates/jsinput_response.js +++ b/src/editors/data/constants/advancedOlxTemplates/jsinput_response.js @@ -5,7 +5,7 @@ // markdown: !!null // showanswer: never // data: | -export const jsInputResponse = ` +const jsInputResponse = `

In these problems (also called custom JavaScript problems or JS Input problems), you add a problem or tool that uses JavaScript in Studio. diff --git a/src/editors/data/constants/advancedOlxTemplates/problem_with_hint.js b/src/editors/data/constants/advancedOlxTemplates/problem_with_hint.js index 8ee6c867a9..442b0cc3d7 100644 --- a/src/editors/data/constants/advancedOlxTemplates/problem_with_hint.js +++ b/src/editors/data/constants/advancedOlxTemplates/problem_with_hint.js @@ -4,7 +4,7 @@ // display_name: Problem with Adaptive Hint // markdown: !!null // data: | -export const problemWithHint = ` +const problemWithHint = `

Problem With Adaptive Hint

This problem demonstrates a question with hints, based on using the hintfn method.

diff --git a/src/editors/data/constants/analyticsEvt.js b/src/editors/data/constants/analyticsEvt.js index 1b201956d3..1fef2fa7d1 100644 --- a/src/editors/data/constants/analyticsEvt.js +++ b/src/editors/data/constants/analyticsEvt.js @@ -1,4 +1,4 @@ -export const analyticsEvt = { +const analyticsEvt = { editorSaveClick: 'edx.ui.authoring.editor.save', editorCancelClick: 'edx.ui.authoring.editor.cancel', videoGalleryCancelClick: 'edx.ui.authoring.videogallery.cancel', diff --git a/src/editors/data/constants/basicOlxTemplates/dropdown.js b/src/editors/data/constants/basicOlxTemplates/dropdown.js index 4988b43e2d..e66e0d1f0d 100644 --- a/src/editors/data/constants/basicOlxTemplates/dropdown.js +++ b/src/editors/data/constants/basicOlxTemplates/dropdown.js @@ -1,5 +1,5 @@ /* eslint-disable */ -export const dropdown = ` +const dropdown = ` diff --git a/src/editors/data/constants/basicOlxTemplates/numeric.js b/src/editors/data/constants/basicOlxTemplates/numeric.js index dfaae48a0d..5b7a915c9c 100644 --- a/src/editors/data/constants/basicOlxTemplates/numeric.js +++ b/src/editors/data/constants/basicOlxTemplates/numeric.js @@ -1,5 +1,5 @@ /* eslint-disable */ -export const numeric = ` +const numeric = ` diff --git a/src/editors/data/constants/basicOlxTemplates/singleSelect.js b/src/editors/data/constants/basicOlxTemplates/singleSelect.js index 08daf32d03..ea0e370366 100644 --- a/src/editors/data/constants/basicOlxTemplates/singleSelect.js +++ b/src/editors/data/constants/basicOlxTemplates/singleSelect.js @@ -1,5 +1,5 @@ /* eslint-disable */ -export const singleSelect = ` +const singleSelect = ` diff --git a/src/editors/data/constants/tinyMCE.js b/src/editors/data/constants/tinyMCE.js index f7edbe273e..a6e40203a5 100644 --- a/src/editors/data/constants/tinyMCE.js +++ b/src/editors/data/constants/tinyMCE.js @@ -74,10 +74,3 @@ export const plugins = listKeyStore([ ]); export const textToSpeechIcon = ''; - -export default StrictDict({ - buttons, - commands, - plugins, - textToSpeechIcon, -}); diff --git a/src/editors/data/redux/app/selectors.js b/src/editors/data/redux/app/selectors.js index c9d23c2e07..9976eee19f 100644 --- a/src/editors/data/redux/app/selectors.js +++ b/src/editors/data/redux/app/selectors.js @@ -1,6 +1,10 @@ import { createSelector } from 'reselect'; import { blockTypes } from '../../constants/app'; import * as urls from '../../services/cms/urls'; +// This 'module' self-import hack enables mocking during tests. +// See src/editors/decisions/0005-internal-editor-testability-decisions.md. The whole approach to how hooks are tested +// should be re-thought and cleaned up to avoid this pattern. +// eslint-disable-next-line import/no-self-import import * as module from './selectors'; export const appSelector = (state) => state.app; diff --git a/src/editors/data/redux/game/selectors.js b/src/editors/data/redux/game/selectors.js index 6cba1a7c3d..736d49f93b 100644 --- a/src/editors/data/redux/game/selectors.js +++ b/src/editors/data/redux/game/selectors.js @@ -1,4 +1,8 @@ import { createSelector } from 'reselect'; +// This 'module' self-import hack enables mocking during tests. +// See src/editors/decisions/0005-internal-editor-testability-decisions.md. The whole approach to how hooks are tested +// should be re-thought and cleaned up to avoid this pattern. +// eslint-disable-next-line import/no-self-import import * as module from './selectors'; export const gameState = (state) => state.game; diff --git a/src/editors/data/redux/index.js b/src/editors/data/redux/index.js index 05811bbf24..84e5648bda 100644 --- a/src/editors/data/redux/index.js +++ b/src/editors/data/redux/index.js @@ -8,7 +8,6 @@ import * as video from './video'; import * as problem from './problem'; import * as game from './game'; -/* eslint-disable import/no-cycle */ export { default as thunkActions } from './thunkActions'; const modules = { diff --git a/src/editors/data/redux/problem/reducers.js b/src/editors/data/redux/problem/reducers.js index c61dfb240f..42e1f7c57c 100644 --- a/src/editors/data/redux/problem/reducers.js +++ b/src/editors/data/redux/problem/reducers.js @@ -1,4 +1,4 @@ -import _ from 'lodash-es'; +import _ from 'lodash'; import { createSlice } from '@reduxjs/toolkit'; import { indexToLetterMap } from '../../../containers/ProblemEditor/data/OLXParser'; import { StrictDict } from '../../../utils'; diff --git a/src/editors/data/redux/problem/selectors.js b/src/editors/data/redux/problem/selectors.js index efd327ee48..1ba3c3ea0b 100644 --- a/src/editors/data/redux/problem/selectors.js +++ b/src/editors/data/redux/problem/selectors.js @@ -1,4 +1,8 @@ import { createSelector } from 'reselect'; +// This 'module' self-import hack enables mocking during tests. +// See src/editors/decisions/0005-internal-editor-testability-decisions.md. The whole approach to how hooks are tested +// should be re-thought and cleaned up to avoid this pattern. +// eslint-disable-next-line import/no-self-import import * as module from './selectors'; export const problemState = (state) => state.problem; diff --git a/src/editors/data/redux/requests/selectors.js b/src/editors/data/redux/requests/selectors.js index d66cf1384e..c9b318bcbe 100644 --- a/src/editors/data/redux/requests/selectors.js +++ b/src/editors/data/redux/requests/selectors.js @@ -1,5 +1,9 @@ import { StrictDict } from '../../../utils'; import { RequestStates } from '../../constants/requests'; +// This 'module' self-import hack enables mocking during tests. +// See src/editors/decisions/0005-internal-editor-testability-decisions.md. The whole approach to how hooks are tested +// should be re-thought and cleaned up to avoid this pattern. +// eslint-disable-next-line import/no-self-import import * as module from './selectors'; export const requestStatus = (state, { requestKey }) => state.requests[requestKey]; diff --git a/src/editors/data/redux/thunkActions/app.js b/src/editors/data/redux/thunkActions/app.js index a248279d21..fa50c91a06 100644 --- a/src/editors/data/redux/thunkActions/app.js +++ b/src/editors/data/redux/thunkActions/app.js @@ -1,10 +1,20 @@ import { StrictDict, camelizeKeys } from '../../../utils'; -/* eslint-disable import/no-cycle */ -import { actions } from '..'; import * as requests from './requests'; +// This 'module' self-import hack enables mocking during tests. +// See src/editors/decisions/0005-internal-editor-testability-decisions.md. The whole approach to how hooks are tested +// should be re-thought and cleaned up to avoid this pattern. +// eslint-disable-next-line import/no-self-import import * as module from './app'; +import { actions as appActions } from '../app'; +import { actions as requestsActions } from '../requests'; import { RequestKeys } from '../../constants/requests'; +// Similar to `import { actions } from '..';` but avoid circular imports: +const actions = { + app: appActions, + requests: requestsActions, +}; + export const fetchBlock = () => (dispatch) => { dispatch(requests.fetchBlock({ onSuccess: (response) => { diff --git a/src/editors/data/redux/thunkActions/index.js b/src/editors/data/redux/thunkActions/index.js index ec9542fd74..b787e23cff 100644 --- a/src/editors/data/redux/thunkActions/index.js +++ b/src/editors/data/redux/thunkActions/index.js @@ -1,6 +1,5 @@ import { StrictDict } from '../../../utils'; -/* eslint-disable import/no-cycle */ import app from './app'; import video from './video'; import problem from './problem'; diff --git a/src/editors/data/redux/thunkActions/problem.js b/src/editors/data/redux/thunkActions/problem.js index 16bebedf62..62de4f44a9 100644 --- a/src/editors/data/redux/thunkActions/problem.js +++ b/src/editors/data/redux/thunkActions/problem.js @@ -1,6 +1,5 @@ -import _ from 'lodash-es'; -/* eslint-disable import/no-cycle */ -import { actions } from '..'; +import _ from 'lodash'; +import { actions as problemActions } from '../problem'; import * as requests from './requests'; import { OLXParser } from '../../../containers/ProblemEditor/data/OLXParser'; import { parseSettings } from '../../../containers/ProblemEditor/data/SettingsParser'; @@ -10,6 +9,9 @@ import { blankProblemOLX } from '../../../containers/ProblemEditor/data/mockData import { camelizeKeys } from '../../../utils'; import { fetchEditorContent } from '../../../containers/ProblemEditor/components/EditProblemView/hooks'; +// Similar to `import { actions, selectors } from '..';` but avoid circular imports: +const actions = { problem: problemActions }; + export const switchToAdvancedEditor = () => (dispatch, getState) => { const state = getState(); const editorObject = fetchEditorContent({ format: '' }); diff --git a/src/editors/data/redux/thunkActions/problem.test.js b/src/editors/data/redux/thunkActions/problem.test.js index da1f0ad477..828547b5b4 100644 --- a/src/editors/data/redux/thunkActions/problem.test.js +++ b/src/editors/data/redux/thunkActions/problem.test.js @@ -1,5 +1,11 @@ +import 'CourseAuthoring/editors/setupEditorTest'; import { actions } from '..'; -import * as module from './problem'; +import { + initializeProblem, + switchToAdvancedEditor, + fetchAdvancedSettings, + loadProblem, +} from './problem'; import { checkboxesOLXWithFeedbackAndHintsOLX, advancedProblemOlX, blankProblemOLX } from '../../../containers/ProblemEditor/data/mockData/olxTestData'; import { ProblemTypeKeys } from '../../constants/problem'; @@ -7,13 +13,11 @@ const mockOlx = 'SOmEVALue'; const mockBuildOlx = jest.fn(() => mockOlx); jest.mock('../../../containers/ProblemEditor/data/ReactStateOLXParser', () => jest.fn().mockImplementation(() => ({ buildOLX: mockBuildOlx }))); -jest.mock('..', () => ({ +jest.mock('../problem', () => ({ actions: { - problem: { - load: () => {}, - setEnableTypeSelection: () => {}, - updateField: (args) => args, - }, + load: () => {}, + setEnableTypeSelection: () => {}, + updateField: (args) => args, }, })); @@ -48,31 +52,31 @@ describe('problem thunkActions', () => { jest.restoreAllMocks(); }); test('initializeProblem visual Problem :', () => { - module.initializeProblem(blockValue)(dispatch); + initializeProblem(blockValue)(dispatch); expect(dispatch).toHaveBeenCalled(); }); test('switchToAdvancedEditor visual Problem', () => { - module.switchToAdvancedEditor()(dispatch, getState); + switchToAdvancedEditor()(dispatch, getState); expect(dispatch).toHaveBeenCalledWith( actions.problem.updateField({ problemType: ProblemTypeKeys.ADVANCED, rawOLX: mockOlx }), ); }); describe('fetchAdvanceSettings', () => { it('dispatches fetchAdvanceSettings action', () => { - module.fetchAdvancedSettings({ rawOLX, rawSettings })(dispatch); + fetchAdvancedSettings({ rawOLX, rawSettings })(dispatch); [[dispatchedAction]] = dispatch.mock.calls; expect(dispatchedAction.fetchAdvanceSettings).not.toEqual(undefined); }); it('dispatches actions.problem.updateField and loadProblem on success', () => { dispatch.mockClear(); - module.fetchAdvancedSettings({ rawOLX, rawSettings })(dispatch); + fetchAdvancedSettings({ rawOLX, rawSettings })(dispatch); [[dispatchedAction]] = dispatch.mock.calls; dispatchedAction.fetchAdvanceSettings.onSuccess({ data: { key: 'test', max_attempts: 1 } }); expect(dispatch).toHaveBeenCalledWith(actions.problem.load()); }); it('calls loadProblem on failure', () => { dispatch.mockClear(); - module.fetchAdvancedSettings({ rawOLX, rawSettings })(dispatch); + fetchAdvancedSettings({ rawOLX, rawSettings })(dispatch); [[dispatchedAction]] = dispatch.mock.calls; dispatchedAction.fetchAdvanceSettings.onFailure(); expect(dispatch).toHaveBeenCalledWith(actions.problem.load()); @@ -81,12 +85,12 @@ describe('problem thunkActions', () => { describe('loadProblem', () => { test('initializeProblem advanced Problem', () => { rawOLX = advancedProblemOlX.rawOLX; - module.loadProblem({ rawOLX, rawSettings, defaultSettings })(dispatch); + loadProblem({ rawOLX, rawSettings, defaultSettings })(dispatch); expect(dispatch).toHaveBeenCalledWith(actions.problem.load()); }); test('initializeProblem blank Problem', () => { rawOLX = blankProblemOLX.rawOLX; - module.loadProblem({ rawOLX, rawSettings, defaultSettings })(dispatch); + loadProblem({ rawOLX, rawSettings, defaultSettings })(dispatch); expect(dispatch).toHaveBeenCalledWith(actions.problem.setEnableTypeSelection()); }); }); diff --git a/src/editors/data/redux/thunkActions/requests.js b/src/editors/data/redux/thunkActions/requests.js index f419c9a4bb..46f9d1a03a 100644 --- a/src/editors/data/redux/thunkActions/requests.js +++ b/src/editors/data/redux/thunkActions/requests.js @@ -1,12 +1,20 @@ import { StrictDict } from '../../../utils'; import { RequestKeys } from '../../constants/requests'; -/* eslint-disable import/no-cycle */ -import { actions, selectors } from '..'; import api, { loadImages } from '../../services/cms/api'; +import { actions as requestsActions } from '../requests'; +import { selectors as appSelectors } from '../app'; +// This 'module' self-import hack enables mocking during tests. +// See src/editors/decisions/0005-internal-editor-testability-decisions.md. The whole approach to how hooks are tested +// should be re-thought and cleaned up to avoid this pattern. +// eslint-disable-next-line import/no-self-import import * as module from './requests'; +// Similar to `import { actions, selectors } from '..';` but avoid circular imports: +const actions = { requests: requestsActions }; +const selectors = { app: appSelectors }; + /** * Wrapper around a network request promise, that sends actions to the redux store to * track the state of that promise. diff --git a/src/editors/data/redux/thunkActions/video.js b/src/editors/data/redux/thunkActions/video.js index 8a54b68179..352d989737 100644 --- a/src/editors/data/redux/thunkActions/video.js +++ b/src/editors/data/redux/thunkActions/video.js @@ -1,11 +1,19 @@ -/* eslint-disable import/no-cycle */ -import _, { isEmpty } from 'lodash-es'; -import { actions, selectors } from '..'; +import _, { isEmpty } from 'lodash'; import { removeItemOnce } from '../../../utils'; import * as requests from './requests'; +// This 'module' self-import hack enables mocking during tests. +// See src/editors/decisions/0005-internal-editor-testability-decisions.md. The whole approach to how hooks are tested +// should be re-thought and cleaned up to avoid this pattern. +// eslint-disable-next-line import/no-self-import import * as module from './video'; import { valueFromDuration } from '../../../containers/VideoEditor/components/VideoSettingsModal/components/DurationWidget/hooks'; import { parseYoutubeId } from '../../services/cms/api'; +import { selectors as appSelectors } from '../app'; +import { actions as videoActions, selectors as videoSelectors } from '../video'; + +// Similar to `import { actions, selectors } from '..';` but avoid circular imports: +const actions = { video: videoActions }; +const selectors = { app: appSelectors, video: videoSelectors }; export const loadVideoData = (selectedVideoId, selectedVideoUrl) => (dispatch, getState) => { const state = getState(); diff --git a/src/editors/data/redux/thunkActions/video.test.js b/src/editors/data/redux/thunkActions/video.test.js index 6e53c87d90..75a668d0bc 100644 --- a/src/editors/data/redux/thunkActions/video.test.js +++ b/src/editors/data/redux/thunkActions/video.test.js @@ -2,23 +2,23 @@ import { actions } from '..'; import keyStore from '../../../utils/keyStore'; import * as thunkActions from './video'; -jest.mock('..', () => ({ +jest.mock('../video', () => ({ + ...jest.requireActual('../video'), actions: { - video: { - load: (args) => ({ load: args }), - updateField: (args) => ({ updateField: args }), - }, + load: (args) => ({ load: args }), + updateField: (args) => ({ updateField: args }), }, selectors: { - app: { - courseDetails: (state) => ({ courseDetails: state }), - videos: (state) => ({ videos: state.app.videos }), - }, - video: { - videoId: (state) => ({ videoId: state }), - videoSettings: (state) => ({ videoSettings: state }), - getTranscriptDownloadUrl: (state) => ({ getTranscriptDownloadUrl: state }), - }, + videoId: (state) => ({ videoId: state }), + videoSettings: (state) => ({ videoSettings: state }), + getTranscriptDownloadUrl: (state) => ({ getTranscriptDownloadUrl: state }), + }, +})); +jest.mock('../app', () => ({ + ...jest.requireActual('../app'), + selectors: { + courseDetails: (state) => ({ courseDetails: state }), + videos: (state) => ({ videos: state.app.videos }), }, })); jest.mock('./requests', () => ({ diff --git a/src/editors/data/redux/video/selectors.js b/src/editors/data/redux/video/selectors.js index 77a740ca1e..f71c014cf7 100644 --- a/src/editors/data/redux/video/selectors.js +++ b/src/editors/data/redux/video/selectors.js @@ -4,6 +4,10 @@ import { keyStore } from '../../../utils'; import { videoTranscriptLanguages } from '../../constants/video'; import { initialState } from './reducer'; +// This 'module' self-import hack enables mocking during tests. +// See src/editors/decisions/0005-internal-editor-testability-decisions.md. The whole approach to how hooks are tested +// should be re-thought and cleaned up to avoid this pattern. +// eslint-disable-next-line import/no-self-import import * as module from './selectors'; import * as AppSelectors from '../app/selectors'; import { downloadVideoTranscriptURL, downloadVideoHandoutUrl, mediaTranscriptURL } from '../../services/cms/urls'; diff --git a/src/editors/data/services/cms/api.js b/src/editors/data/services/cms/api.js index 38abad7154..7d732c15d4 100644 --- a/src/editors/data/services/cms/api.js +++ b/src/editors/data/services/cms/api.js @@ -1,6 +1,10 @@ import { camelizeKeys } from '../../../utils'; import * as urls from './urls'; import { get, post, deleteObject } from './utils'; +// This 'module' self-import hack enables mocking during tests. +// See src/editors/decisions/0005-internal-editor-testability-decisions.md. The whole approach to how hooks are tested +// should be re-thought and cleaned up to avoid this pattern. +// eslint-disable-next-line import/no-self-import import * as module from './api'; import * as mockApi from './mockApi'; import { durationStringFromValue } from '../../../containers/VideoEditor/components/VideoSettingsModal/components/DurationWidget/hooks'; diff --git a/src/editors/data/services/cms/api.test.js b/src/editors/data/services/cms/api.test.js index 116efbf627..7abec953cc 100644 --- a/src/editors/data/services/cms/api.test.js +++ b/src/editors/data/services/cms/api.test.js @@ -84,9 +84,9 @@ describe('cms api', () => { it('should call get with normal accept header for prod', async () => { process.env.NODE_ENV = 'production'; process.env.MFE_NAME = 'frontend-app-library-authoring'; - // eslint-disable-next-line no-shadow + // eslint-disable-next-line no-shadow, @typescript-eslint/no-shadow const { apiMethods } = await import('./api'); - // eslint-disable-next-line no-shadow + // eslint-disable-next-line no-shadow, @typescript-eslint/no-shadow const utils = await import('./utils'); const getSpy = jest.spyOn(utils, 'get'); apiMethods.fetchByUnitId({ blockId, studioEndpointUrl }); @@ -96,9 +96,9 @@ describe('cms api', () => { it('should call get with normal accept header for course-authoring', async () => { process.env.NODE_ENV = 'development'; process.env.MFE_NAME = 'frontend-app-course-authoring'; - // eslint-disable-next-line no-shadow + // eslint-disable-next-line no-shadow, @typescript-eslint/no-shadow const { apiMethods } = await import('./api'); - // eslint-disable-next-line no-shadow + // eslint-disable-next-line no-shadow, @typescript-eslint/no-shadow const utils = await import('./utils'); const getSpy = jest.spyOn(utils, 'get'); apiMethods.fetchByUnitId({ blockId, studioEndpointUrl }); @@ -108,9 +108,9 @@ describe('cms api', () => { it('should call get with special accept header "*/*" for course-authoring', async () => { process.env.NODE_ENV = 'development'; process.env.MFE_NAME = 'frontend-app-library-authoring'; - // eslint-disable-next-line no-shadow + // eslint-disable-next-line no-shadow, @typescript-eslint/no-shadow const { apiMethods } = await import('./api'); - // eslint-disable-next-line no-shadow + // eslint-disable-next-line no-shadow, @typescript-eslint/no-shadow const utils = await import('./utils'); const getSpy = jest.spyOn(utils, 'get'); apiMethods.fetchByUnitId({ blockId, studioEndpointUrl }); diff --git a/src/editors/data/services/cms/mockApi.js b/src/editors/data/services/cms/mockApi.js index 5c4440b3ec..ca50e0c0a6 100644 --- a/src/editors/data/services/cms/mockApi.js +++ b/src/editors/data/services/cms/mockApi.js @@ -1,7 +1,7 @@ /* istanbul ignore file */ import * as urls from './urls'; -const mockPromise = (returnValue) => new Promise(resolve => resolve(returnValue)); +const mockPromise = (returnValue) => new Promise(resolve => { resolve(returnValue); }); // TODO: update to return block data appropriate per block ID, which will equal block type // eslint-disable-next-line diff --git a/src/editors/data/store.js b/src/editors/data/store.js index 4e73ca33ad..a10b1ba28f 100755 --- a/src/editors/data/store.js +++ b/src/editors/data/store.js @@ -1,6 +1,6 @@ import * as redux from 'redux'; import thunkMiddleware from 'redux-thunk'; -import { composeWithDevTools } from 'redux-devtools-extension/logOnlyInProduction'; +import { composeWithDevToolsLogOnlyInProduction } from '@redux-devtools/extension'; import { createLogger } from 'redux-logger'; import reducer, { actions, selectors } from './redux'; @@ -12,7 +12,7 @@ export const createStore = () => { const store = redux.createStore( reducer, - composeWithDevTools(redux.applyMiddleware(...middleware)), + composeWithDevToolsLogOnlyInProduction(redux.applyMiddleware(...middleware)), ); /** diff --git a/src/editors/data/store.test.js b/src/editors/data/store.test.js index 0a221d6799..6b8c851d94 100644 --- a/src/editors/data/store.test.js +++ b/src/editors/data/store.test.js @@ -1,6 +1,6 @@ import { applyMiddleware } from 'redux'; import thunkMiddleware from 'redux-thunk'; -import { composeWithDevTools } from 'redux-devtools-extension/logOnlyInProduction'; +import { composeWithDevToolsLogOnlyInProduction } from '@redux-devtools/extension'; import { createLogger } from 'redux-logger'; import rootReducer, { actions, selectors } from './redux'; @@ -22,8 +22,8 @@ jest.mock('redux', () => ({ applyMiddleware: (...middleware) => ({ applied: middleware }), createStore: (reducer, middleware) => ({ reducer, middleware }), })); -jest.mock('redux-devtools-extension/logOnlyInProduction', () => ({ - composeWithDevTools: (middleware) => ({ withDevTools: middleware }), +jest.mock('@redux-devtools/extension', () => ({ + composeWithDevToolsLogOnlyInProduction: (middleware) => ({ withDevTools: middleware }), })); describe('store aggregator module', () => { @@ -37,7 +37,7 @@ describe('store aggregator module', () => { describe('middleware', () => { it('exports thunk and logger middleware, composed and applied with dev tools', () => { expect(createStore().middleware).toEqual( - composeWithDevTools(applyMiddleware(thunkMiddleware, createLogger())), + composeWithDevToolsLogOnlyInProduction(applyMiddleware(thunkMiddleware, createLogger())), ); }); }); diff --git a/src/editors/example.jsx b/src/editors/example.jsx index f0c65a6e47..98d238f688 100644 --- a/src/editors/example.jsx +++ b/src/editors/example.jsx @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ /* eslint-disable import/extensions */ /* eslint-disable import/no-unresolved */ /** @@ -16,6 +17,10 @@ import { Spinner } from '@openedx/paragon'; import { injectIntl, intlShape } from '@edx/frontend-platform/i18n'; import EditorContainer from '../EditorContainer'; +// This 'module' self-import hack enables mocking during tests. +// See src/editors/decisions/0005-internal-editor-testability-decisions.md. The whole approach to how hooks are tested +// should be re-thought and cleaned up to avoid this pattern. +// eslint-disable-next-line import/no-self-import import * as module from '..'; import { actions, selectors } from '../../data/redux'; import { RequestKeys } from '../../data/constants/requests'; diff --git a/src/editors/hooks.js b/src/editors/hooks.js index d2488ad757..c0d1b70a87 100644 --- a/src/editors/hooks.js +++ b/src/editors/hooks.js @@ -4,6 +4,10 @@ import { sendTrackEvent } from '@edx/frontend-platform/analytics'; import analyticsEvt from './data/constants/analyticsEvt'; import { actions, thunkActions } from './data/redux'; +// This 'module' self-import hack enables mocking during tests. +// See src/editors/decisions/0005-internal-editor-testability-decisions.md. The whole approach to how hooks are tested +// should be re-thought and cleaned up to avoid this pattern. +// eslint-disable-next-line import/no-self-import import * as module from './hooks'; import { RequestKeys } from './data/constants/requests'; diff --git a/src/editors/setupEditorTest.js b/src/editors/setupEditorTest.js new file mode 100644 index 0000000000..54b23ca0ab --- /dev/null +++ b/src/editors/setupEditorTest.js @@ -0,0 +1,122 @@ +// These additional mocks and setup are required for some tests in src/editors/ +// and are imported on an as-needed basis. +// eslint-disable-next-line import/no-extraneous-dependencies +import 'jest-canvas-mock'; + +jest.mock('@edx/frontend-platform/i18n', () => { + const i18n = jest.requireActual('@edx/frontend-platform/i18n'); + const PropTypes = jest.requireActual('prop-types'); + return { + ...i18n, + intlShape: PropTypes.shape({ + formatMessage: PropTypes.func, + }), + defineMessages: m => m, + getLocale: () => 'getLocale', + FormattedDate: () => 'FormattedDate', + FormattedMessage: () => 'FormattedMessage', + FormattedTime: () => 'FormattedTime', + }; +}); + +jest.mock('@openedx/paragon', () => jest.requireActual('./testUtils').mockNestedComponents({ + Alert: { + Heading: 'Alert.Heading', + }, + AlertModal: 'AlertModal', + ActionRow: { + Spacer: 'ActionRow.Spacer', + }, + Badge: 'Badge', + Button: 'Button', + ButtonGroup: 'ButtonGroup', + Collapsible: { + Advanced: 'Advanced', + Body: 'Body', + Trigger: 'Trigger', + Visible: 'Visible', + }, + Card: { + Header: 'Card.Header', + Section: 'Card.Section', + Footer: 'Card.Footer', + Body: 'Card.Body', + }, + CheckboxControl: 'CheckboxControl', + Col: 'Col', + Container: 'Container', + Dropdown: { + Item: 'Dropdown.Item', + Menu: 'Dropdown.Menu', + Toggle: 'Dropdown.Toggle', + }, + ErrorContext: { + Provider: 'ErrorContext.Provider', + }, + Hyperlink: 'Hyperlink', + Icon: 'Icon', + IconButton: 'IconButton', + IconButtonWithTooltip: 'IconButtonWithTooltip', + Image: 'Image', + MailtoLink: 'MailtoLink', + ModalDialog: { + Footer: 'ModalDialog.Footer', + Header: 'ModalDialog.Header', + Title: 'ModalDialog.Title', + Body: 'ModalDialog.Body', + CloseButton: 'ModalDialog.CloseButton', + }, + Form: { + Checkbox: 'Form.Checkbox', + Control: { + Feedback: 'Form.Control.Feedback', + }, + Group: 'Form.Group', + Label: 'Form.Label', + Text: 'Form.Text', + Row: 'Form.Row', + Radio: 'Radio', + RadioSet: 'RadioSet', + }, + OverlayTrigger: 'OverlayTrigger', + Tooltip: 'Tooltip', + FullscreenModal: 'FullscreenModal', + Row: 'Row', + Scrollable: 'Scrollable', + SelectableBox: { + Set: 'SelectableBox.Set', + }, + + Spinner: 'Spinner', + Stack: 'Stack', + Toast: 'Toast', + Truncate: 'Truncate', + useWindowSize: { height: '500px' }, +})); + +jest.mock('@openedx/paragon/icons', () => ({ + Close: jest.fn().mockName('icons.Close'), + Edit: jest.fn().mockName('icons.Edit'), + Locked: jest.fn().mockName('icons.Locked'), + Unlocked: jest.fn().mockName('icons.Unlocked'), +})); + +// Mock react-redux hooks +// unmock for integration tests +jest.mock('react-redux', () => { + const dispatch = jest.fn((...args) => ({ dispatch: args })).mockName('react-redux.dispatch'); + return { + connect: (mapStateToProps, mapDispatchToProps) => (component) => ({ + mapStateToProps, + mapDispatchToProps, + component, + }), + useDispatch: jest.fn(() => dispatch), + useSelector: jest.fn((selector) => ({ useSelector: selector })), + }; +}); + +// Mock the plugins repo so jest will stop complaining about ES6 syntax +jest.mock('frontend-components-tinymce-advanced-plugins', () => ({ + a11ycheckerCss: '', +})); diff --git a/src/editors/sharedComponents/BaseModal/index.jsx b/src/editors/sharedComponents/BaseModal/index.jsx index dd1bef7484..74639dd54d 100644 --- a/src/editors/sharedComponents/BaseModal/index.jsx +++ b/src/editors/sharedComponents/BaseModal/index.jsx @@ -10,7 +10,7 @@ import { FormattedMessage } from '@edx/frontend-platform/i18n'; import messages from './messages'; -export const BaseModal = ({ +const BaseModal = ({ isOpen, close, title, diff --git a/src/editors/sharedComponents/BaseModal/index.test.jsx b/src/editors/sharedComponents/BaseModal/index.test.jsx index 1afd1662c2..cca9a4b57d 100644 --- a/src/editors/sharedComponents/BaseModal/index.test.jsx +++ b/src/editors/sharedComponents/BaseModal/index.test.jsx @@ -1,3 +1,4 @@ +import 'CourseAuthoring/editors/setupEditorTest'; import React from 'react'; import { shallow } from '@edx/react-unit-test-utils'; diff --git a/src/editors/sharedComponents/CodeEditor/index.jsx b/src/editors/sharedComponents/CodeEditor/index.jsx index eeeacafd37..29a4ed2daf 100644 --- a/src/editors/sharedComponents/CodeEditor/index.jsx +++ b/src/editors/sharedComponents/CodeEditor/index.jsx @@ -11,7 +11,7 @@ import './index.scss'; import * as hooks from './hooks'; -export const CodeEditor = ({ +const CodeEditor = ({ innerRef, value, lang, @@ -53,4 +53,5 @@ CodeEditor.propTypes = { lang: PropTypes.string.isRequired, }; +export const CodeEditorInternal = CodeEditor; // For testing only export default injectIntl(CodeEditor); diff --git a/src/editors/sharedComponents/CodeEditor/index.test.jsx b/src/editors/sharedComponents/CodeEditor/index.test.jsx index d0827754ca..0760f8e0d1 100644 --- a/src/editors/sharedComponents/CodeEditor/index.test.jsx +++ b/src/editors/sharedComponents/CodeEditor/index.test.jsx @@ -1,14 +1,17 @@ +import 'CourseAuthoring/editors/setupEditorTest'; import React from 'react'; import { shallow } from '@edx/react-unit-test-utils'; import { EditorState } from '@codemirror/state'; import { EditorView } from '@codemirror/view'; import { html } from '@codemirror/lang-html'; -import { formatMessage, MockUseState } from '../../../testUtils'; +import { formatMessage, MockUseState } from '../../testUtils'; import alphanumericMap from './constants'; import * as module from './index'; import * as hooks from './hooks'; +const CodeEditor = module.CodeEditorInternal; + jest.mock('@codemirror/view'); jest.mock('react', () => ({ @@ -171,7 +174,7 @@ describe('CodeEditor', () => { test('Renders and calls Hooks ', () => { jest.spyOn(hooks, 'prepareShowBtnEscapeHTML').mockImplementation(() => ({ showBtnEscapeHTML: true, hideBtn: mockHideBtn })); // Note: ref won't show up as it is not acutaly a DOM attribute. - expect(shallow().snapshot).toMatchSnapshot(); + expect(shallow().snapshot).toMatchSnapshot(); expect(hooks.createCodeMirrorDomNode).toHaveBeenCalled(); }); }); diff --git a/src/editors/sharedComponents/ErrorAlerts/ErrorAlert.jsx b/src/editors/sharedComponents/ErrorAlerts/ErrorAlert.jsx index 9b5bdadc65..3e3ef935aa 100644 --- a/src/editors/sharedComponents/ErrorAlerts/ErrorAlert.jsx +++ b/src/editors/sharedComponents/ErrorAlerts/ErrorAlert.jsx @@ -33,7 +33,7 @@ export const hooks = { }, }; -export const ErrorAlert = ({ +const ErrorAlert = ({ dismissError, hideHeading, isError, diff --git a/src/editors/sharedComponents/ErrorAlerts/ErrorAlert.test.jsx b/src/editors/sharedComponents/ErrorAlerts/ErrorAlert.test.jsx index f8d8f7b3ce..a8b1f0504b 100644 --- a/src/editors/sharedComponents/ErrorAlerts/ErrorAlert.test.jsx +++ b/src/editors/sharedComponents/ErrorAlerts/ErrorAlert.test.jsx @@ -1,9 +1,8 @@ +import 'CourseAuthoring/editors/setupEditorTest'; import React from 'react'; import { shallow } from '@edx/react-unit-test-utils'; -import * as module from './ErrorAlert'; -import { MockUseState } from '../../../testUtils'; - -const { ErrorAlert } = module; +import ErrorAlert, { hooks } from './ErrorAlert'; +import { MockUseState } from '../../testUtils'; jest.mock('react', () => ({ ...jest.requireActual('react'), @@ -12,7 +11,7 @@ jest.mock('react', () => ({ useCallback: (cb, prereqs) => ({ cb, prereqs }), })); -const state = new MockUseState(module.hooks); +const state = new MockUseState(hooks); let hook; const testValue = 'testVALUE'; @@ -33,7 +32,7 @@ describe('ErrorAlert component', () => { isError: testValue, }; beforeEach(() => { - hook = module.hooks.dismissalHooks(props); + hook = hooks.dismissalHooks(props); }); it('returns isDismissed value, initialized to false', () => { expect(state.stateVals.isDismissed).toEqual(hook.isDismissed); @@ -61,7 +60,7 @@ describe('ErrorAlert component', () => { props = { dismissError: jest.fn(), }; - jest.spyOn(module.hooks, 'dismissalHooks').mockImplementation(() => ({ + jest.spyOn(hooks, 'dismissalHooks').mockImplementation(() => ({ isDismissed: false, dismissAlert: jest.fn().mockName('dismissAlert'), })); diff --git a/src/editors/sharedComponents/ErrorAlerts/FetchErrorAlert.jsx b/src/editors/sharedComponents/ErrorAlerts/FetchErrorAlert.jsx index d098c76780..e8a8167174 100644 --- a/src/editors/sharedComponents/ErrorAlerts/FetchErrorAlert.jsx +++ b/src/editors/sharedComponents/ErrorAlerts/FetchErrorAlert.jsx @@ -4,7 +4,7 @@ import { FormattedMessage } from '@edx/frontend-platform/i18n'; import ErrorAlert from './ErrorAlert'; -export const FetchErrorAlert = ({ +const FetchErrorAlert = ({ message, isFetchError, }) => ( diff --git a/src/editors/sharedComponents/ErrorAlerts/FetchErrorAlert.test.jsx b/src/editors/sharedComponents/ErrorAlerts/FetchErrorAlert.test.jsx index c7f365b5ac..2a849ba417 100644 --- a/src/editors/sharedComponents/ErrorAlerts/FetchErrorAlert.test.jsx +++ b/src/editors/sharedComponents/ErrorAlerts/FetchErrorAlert.test.jsx @@ -1,6 +1,7 @@ +import 'CourseAuthoring/editors/setupEditorTest'; import React from 'react'; import { shallow } from '@edx/react-unit-test-utils'; -import { FetchErrorAlert } from './FetchErrorAlert'; +import FetchErrorAlert from './FetchErrorAlert'; jest.mock('../../data/redux', () => ({ selectors: { diff --git a/src/editors/sharedComponents/ErrorAlerts/UploadErrorAlert.jsx b/src/editors/sharedComponents/ErrorAlerts/UploadErrorAlert.jsx index 9119c4627a..3348515635 100644 --- a/src/editors/sharedComponents/ErrorAlerts/UploadErrorAlert.jsx +++ b/src/editors/sharedComponents/ErrorAlerts/UploadErrorAlert.jsx @@ -4,7 +4,7 @@ import { FormattedMessage } from '@edx/frontend-platform/i18n'; import ErrorAlert from './ErrorAlert'; -export const UploadErrorAlert = ({ +const UploadErrorAlert = ({ message, isUploadError, }) => ( diff --git a/src/editors/sharedComponents/ErrorAlerts/UploadErrorAlert.test.jsx b/src/editors/sharedComponents/ErrorAlerts/UploadErrorAlert.test.jsx index d3afb4b7aa..09c8a7df57 100644 --- a/src/editors/sharedComponents/ErrorAlerts/UploadErrorAlert.test.jsx +++ b/src/editors/sharedComponents/ErrorAlerts/UploadErrorAlert.test.jsx @@ -1,6 +1,7 @@ +import 'CourseAuthoring/editors/setupEditorTest'; import React from 'react'; import { shallow } from '@edx/react-unit-test-utils'; -import { UploadErrorAlert } from './UploadErrorAlert'; +import UploadErrorAlert from './UploadErrorAlert'; jest.mock('../../data/redux', () => ({ selectors: { diff --git a/src/editors/sharedComponents/ErrorBoundary/ErrorPage.jsx b/src/editors/sharedComponents/ErrorBoundary/ErrorPage.jsx index d09a6a5cb5..e00552b1aa 100644 --- a/src/editors/sharedComponents/ErrorBoundary/ErrorPage.jsx +++ b/src/editors/sharedComponents/ErrorBoundary/ErrorPage.jsx @@ -14,7 +14,7 @@ import { selectors } from '../../data/redux'; * An error page that displays a generic message for unexpected errors. Also contains a "Try * Again" button to refresh the page. */ -export const ErrorPage = ({ +const ErrorPage = ({ message, studioEndpointUrl, learningContextId, @@ -86,4 +86,5 @@ export const mapStateToProps = (state) => ({ unitData: selectors.app.unitUrl(state), }); +export const ErrorPageInternal = ErrorPage; // For testing only export default injectIntl(connect(mapStateToProps)(ErrorPage)); diff --git a/src/editors/sharedComponents/ErrorBoundary/ErrorPage.test.jsx b/src/editors/sharedComponents/ErrorBoundary/ErrorPage.test.jsx index 4c7d6e0857..139f6c98e1 100644 --- a/src/editors/sharedComponents/ErrorBoundary/ErrorPage.test.jsx +++ b/src/editors/sharedComponents/ErrorBoundary/ErrorPage.test.jsx @@ -1,8 +1,9 @@ +import 'CourseAuthoring/editors/setupEditorTest'; import React from 'react'; import { shallow } from '@edx/react-unit-test-utils'; import { selectors } from '../../data/redux'; -import { formatMessage } from '../../../testUtils'; -import { ErrorPage, mapStateToProps } from './ErrorPage'; +import { formatMessage } from '../../testUtils'; +import { ErrorPageInternal as ErrorPage, mapStateToProps } from './ErrorPage'; jest.mock('../../data/redux', () => ({ selectors: { diff --git a/src/editors/sharedComponents/ExpandableTextArea/index.jsx b/src/editors/sharedComponents/ExpandableTextArea/index.jsx index cf9025c5f1..a2f30d66d8 100644 --- a/src/editors/sharedComponents/ExpandableTextArea/index.jsx +++ b/src/editors/sharedComponents/ExpandableTextArea/index.jsx @@ -4,7 +4,7 @@ import TinyMceWidget from '../TinyMceWidget'; import { prepareEditorRef } from '../TinyMceWidget/hooks'; import './index.scss'; -export const ExpandableTextArea = ({ +const ExpandableTextArea = ({ value, setContent, error, diff --git a/src/editors/sharedComponents/ExpandableTextArea/index.scss b/src/editors/sharedComponents/ExpandableTextArea/index.scss index 0a5c5820dc..39659be1f5 100644 --- a/src/editors/sharedComponents/ExpandableTextArea/index.scss +++ b/src/editors/sharedComponents/ExpandableTextArea/index.scss @@ -1,25 +1,29 @@ .expandable-mce { - .error { outline: 2px solid #CA3A2F; } + .mce-content-body { padding: 10px; + p { margin: 0; } + blockquote { margin: 16px 40px; } } - *[contentEditable=false] { + *[contentEditable="false"] { outline: 1px solid #D7D3D1; } - *[contentEditable=true] { + + *[contentEditable="true"] { outline: 1px solid #707070; + &:focus, &:active { - outline: 2px solid #000; + outline: 2px solid #000000; } } } diff --git a/src/editors/sharedComponents/FileInput/index.jsx b/src/editors/sharedComponents/FileInput/index.jsx index e926e857e3..f265db4d44 100644 --- a/src/editors/sharedComponents/FileInput/index.jsx +++ b/src/editors/sharedComponents/FileInput/index.jsx @@ -40,5 +40,3 @@ FileInput.propTypes = { ]), }).isRequired, }; - -export default FileInput; diff --git a/src/editors/sharedComponents/ImageUploadModal/ImageSettingsModal/AltTextControls.jsx b/src/editors/sharedComponents/ImageUploadModal/ImageSettingsModal/AltTextControls.jsx index 75d4f35f0f..5b06c6f8f2 100644 --- a/src/editors/sharedComponents/ImageUploadModal/ImageSettingsModal/AltTextControls.jsx +++ b/src/editors/sharedComponents/ImageUploadModal/ImageSettingsModal/AltTextControls.jsx @@ -16,7 +16,7 @@ import messages from './messages'; * @param {func} setValue - update alt-text value * @param {string} value - current alt-text value */ -export const AltTextControls = ({ +const AltTextControls = ({ isDecorative, setIsDecorative, setValue, @@ -70,4 +70,5 @@ AltTextControls.propTypes = { intl: intlShape.isRequired, }; +export const AltTextControlsInternal = AltTextControls; // For testing only export default injectIntl(AltTextControls); diff --git a/src/editors/sharedComponents/ImageUploadModal/ImageSettingsModal/AltTextControls.test.jsx b/src/editors/sharedComponents/ImageUploadModal/ImageSettingsModal/AltTextControls.test.jsx index 9c2163feff..36780669d5 100644 --- a/src/editors/sharedComponents/ImageUploadModal/ImageSettingsModal/AltTextControls.test.jsx +++ b/src/editors/sharedComponents/ImageUploadModal/ImageSettingsModal/AltTextControls.test.jsx @@ -1,8 +1,9 @@ +import 'CourseAuthoring/editors/setupEditorTest'; import React from 'react'; import { shallow } from '@edx/react-unit-test-utils'; -import { formatMessage } from '../../../../testUtils'; -import { AltTextControls } from './AltTextControls'; +import { formatMessage } from '../../../testUtils'; +import { AltTextControlsInternal as AltTextControls } from './AltTextControls'; jest.mock('./hooks', () => ({ onInputChange: (handler) => ({ 'hooks.onInputChange': handler }), diff --git a/src/editors/sharedComponents/ImageUploadModal/ImageSettingsModal/DimensionControls.jsx b/src/editors/sharedComponents/ImageUploadModal/ImageSettingsModal/DimensionControls.jsx index e6dc0f85b0..c05ec4faeb 100644 --- a/src/editors/sharedComponents/ImageUploadModal/ImageSettingsModal/DimensionControls.jsx +++ b/src/editors/sharedComponents/ImageUploadModal/ImageSettingsModal/DimensionControls.jsx @@ -11,7 +11,7 @@ import { } from '@openedx/paragon/icons'; import { FormattedMessage, injectIntl, intlShape } from '@edx/frontend-platform/i18n'; -import hooks from './hooks'; +import * as hooks from './hooks'; import messages from './messages'; /** @@ -24,7 +24,7 @@ import messages from './messages'; * @param {func} updateDimensions - update dimensions callback * @param {obj} value - local dimension values { height, width } */ -export const DimensionControls = ({ +const DimensionControls = ({ isLocked, lock, setHeight, @@ -89,4 +89,5 @@ DimensionControls.propTypes = ({ intl: intlShape.isRequired, }); +export const DimensionControlsInternal = DimensionControls; // For testing only export default injectIntl(DimensionControls); diff --git a/src/editors/sharedComponents/ImageUploadModal/ImageSettingsModal/DimensionControls.test.jsx b/src/editors/sharedComponents/ImageUploadModal/ImageSettingsModal/DimensionControls.test.jsx index 9525c8fe54..0cf9a6d279 100644 --- a/src/editors/sharedComponents/ImageUploadModal/ImageSettingsModal/DimensionControls.test.jsx +++ b/src/editors/sharedComponents/ImageUploadModal/ImageSettingsModal/DimensionControls.test.jsx @@ -1,3 +1,4 @@ +import 'CourseAuthoring/editors/setupEditorTest'; import React, { useEffect } from 'react'; import { shallow } from '@edx/react-unit-test-utils'; import * as paragon from '@openedx/paragon'; @@ -6,12 +7,12 @@ import * as icons from '@openedx/paragon/icons'; import { fireEvent, render, screen, waitFor, } from '@testing-library/react'; -import { formatMessage } from '../../../../testUtils'; -import { DimensionControls } from './DimensionControls'; -import hooks from './hooks'; +import { formatMessage } from '../../../testUtils'; +import { DimensionControlsInternal as DimensionControls } from './DimensionControls'; +import * as hooks from './hooks'; const WrappedDimensionControls = () => { - const dimensions = hooks.dimensions('altText'); + const dimensions = hooks.dimensionHooks('altText'); useEffect(() => { dimensions.onImgLoad({ })({ target: { naturalWidth: 1517, naturalHeight: 803 } }); @@ -21,7 +22,7 @@ const WrappedDimensionControls = () => { }; const UnlockedDimensionControls = () => { - const dimensions = hooks.dimensions('altText'); + const dimensions = hooks.dimensionHooks('altText'); useEffect(() => { dimensions.onImgLoad({ })({ target: { naturalWidth: 1517, naturalHeight: 803 } }); diff --git a/src/editors/sharedComponents/ImageUploadModal/ImageSettingsModal/hooks.js b/src/editors/sharedComponents/ImageUploadModal/ImageSettingsModal/hooks.js index 25ca32e865..c67d63f404 100644 --- a/src/editors/sharedComponents/ImageUploadModal/ImageSettingsModal/hooks.js +++ b/src/editors/sharedComponents/ImageUploadModal/ImageSettingsModal/hooks.js @@ -1,6 +1,10 @@ import React from 'react'; import { StrictDict } from '../../../utils'; +// This 'module' self-import hack enables mocking during tests. +// See src/editors/decisions/0005-internal-editor-testability-decisions.md. The whole approach to how hooks are tested +// should be re-thought and cleaned up to avoid this pattern. +// eslint-disable-next-line import/no-self-import import * as module from './hooks'; // Simple wrappers for useState to allow easy mocking for tests. @@ -334,12 +338,3 @@ export const onSaveClick = ({ }); } }; - -export default { - altText: altTextHooks, - dimensions: dimensionHooks, - onCheckboxChange, - onInputChange, - onSaveClick, - checkFormValidation, -}; diff --git a/src/editors/sharedComponents/ImageUploadModal/ImageSettingsModal/hooks.test.js b/src/editors/sharedComponents/ImageUploadModal/ImageSettingsModal/hooks.test.js index 932577cc4c..bcac2ef658 100644 --- a/src/editors/sharedComponents/ImageUploadModal/ImageSettingsModal/hooks.test.js +++ b/src/editors/sharedComponents/ImageUploadModal/ImageSettingsModal/hooks.test.js @@ -1,6 +1,6 @@ import React from 'react'; import { StrictDict } from '../../../utils'; -import { MockUseState } from '../../../../testUtils'; +import { MockUseState } from '../../../testUtils'; import * as hooks from './hooks'; jest.mock('react', () => ({ diff --git a/src/editors/sharedComponents/ImageUploadModal/ImageSettingsModal/index.jsx b/src/editors/sharedComponents/ImageUploadModal/ImageSettingsModal/index.jsx index a4257f1f0f..da0073f514 100644 --- a/src/editors/sharedComponents/ImageUploadModal/ImageSettingsModal/index.jsx +++ b/src/editors/sharedComponents/ImageUploadModal/ImageSettingsModal/index.jsx @@ -6,7 +6,7 @@ import { ArrowBackIos } from '@openedx/paragon/icons'; import { FormattedMessage, injectIntl, intlShape } from '@edx/frontend-platform/i18n'; import './index.scss'; -import hooks from './hooks'; +import * as hooks from './hooks'; import messages from './messages'; import BaseModal from '../../BaseModal'; import AltTextControls from './AltTextControls'; @@ -23,7 +23,7 @@ import ErrorAlert from '../../ErrorAlerts/ErrorAlert'; * @param {func} saveToEditor - save the current settings to the editor * @param {func} returnToSelection - return to image selection */ -export const ImageSettingsModal = ({ +const ImageSettingsModal = ({ close, isOpen, returnToSelection, @@ -32,8 +32,8 @@ export const ImageSettingsModal = ({ // inject intl, }) => { - const altText = hooks.altText(selection.altText); - const dimensions = hooks.dimensions(altText); + const altText = hooks.altTextHooks(selection.altText); + const dimensions = hooks.dimensionHooks(altText); const onSaveClick = hooks.onSaveClick({ altText, dimensions: dimensions.value, @@ -102,4 +102,5 @@ ImageSettingsModal.propTypes = { // inject intl: intlShape.isRequired, }; +export const ImageSettingsModalInternal = ImageSettingsModal; // For testing only export default injectIntl(ImageSettingsModal); diff --git a/src/editors/sharedComponents/ImageUploadModal/ImageSettingsModal/index.scss b/src/editors/sharedComponents/ImageUploadModal/ImageSettingsModal/index.scss index 9ccb573a62..d50b5e81d1 100644 --- a/src/editors/sharedComponents/ImageUploadModal/ImageSettingsModal/index.scss +++ b/src/editors/sharedComponents/ImageUploadModal/ImageSettingsModal/index.scss @@ -1,6 +1,7 @@ .img-settings-form-container { .img-settings-thumbnail-container { width: 282px; + .img-settings-thumbnail { margin-left: 32px; max-height: 250px; @@ -11,17 +12,21 @@ hr { width: 1px; } + .img-settings-form-controls { width: 375px; margin: 0 24px; + .dimension-input { width: 145px; margin-right: 15px; display: inline-block; } + .img-settings-control-label { font-size: 1rem; } + .decorative-control-label label { font-size: .75rem; } diff --git a/src/editors/sharedComponents/ImageUploadModal/ImageSettingsModal/index.test.jsx b/src/editors/sharedComponents/ImageUploadModal/ImageSettingsModal/index.test.jsx index 56a94ebc73..f6d378ccfa 100644 --- a/src/editors/sharedComponents/ImageUploadModal/ImageSettingsModal/index.test.jsx +++ b/src/editors/sharedComponents/ImageUploadModal/ImageSettingsModal/index.test.jsx @@ -1,14 +1,15 @@ +import 'CourseAuthoring/editors/setupEditorTest'; import React from 'react'; import { shallow } from '@edx/react-unit-test-utils'; -import { formatMessage } from '../../../../testUtils'; -import { ImageSettingsModal } from '.'; +import { formatMessage } from '../../../testUtils'; +import { ImageSettingsModalInternal as ImageSettingsModal } from '.'; jest.mock('./AltTextControls', () => 'AltTextControls'); jest.mock('./DimensionControls', () => 'DimensionControls'); jest.mock('./hooks', () => ({ - altText: () => ({ + altTextHooks: () => ({ error: { show: true, dismiss: jest.fn(), @@ -16,7 +17,7 @@ jest.mock('./hooks', () => ({ isDecorative: false, value: 'alternative Taxes', }), - dimensions: () => ({ + dimensionHooks: () => ({ onImgLoad: jest.fn( (selection) => ({ 'hooks.dimensions.onImgLoad.callback': { selection } }), ).mockName('hooks.dimensions.onImgLoad'), diff --git a/src/editors/sharedComponents/ImageUploadModal/SelectImageModal/hooks.js b/src/editors/sharedComponents/ImageUploadModal/SelectImageModal/hooks.js index 801f9c0dc3..92a1f513ef 100644 --- a/src/editors/sharedComponents/ImageUploadModal/SelectImageModal/hooks.js +++ b/src/editors/sharedComponents/ImageUploadModal/SelectImageModal/hooks.js @@ -2,6 +2,10 @@ import React from 'react'; import { useDispatch } from 'react-redux'; import { thunkActions } from '../../../data/redux'; +// This 'module' self-import hack enables mocking during tests. +// See src/editors/decisions/0005-internal-editor-testability-decisions.md. The whole approach to how hooks are tested +// should be re-thought and cleaned up to avoid this pattern. +// eslint-disable-next-line import/no-self-import import * as module from './hooks'; import { sortFunctions, sortKeys, sortMessages } from './utils'; import messages from './messages'; @@ -177,7 +181,3 @@ export const imgHooks = ({ selectBtnProps, }; }; - -export default { - imgHooks, -}; diff --git a/src/editors/sharedComponents/ImageUploadModal/SelectImageModal/hooks.test.js b/src/editors/sharedComponents/ImageUploadModal/SelectImageModal/hooks.test.js index 2f560e994c..4029bd9f0d 100644 --- a/src/editors/sharedComponents/ImageUploadModal/SelectImageModal/hooks.test.js +++ b/src/editors/sharedComponents/ImageUploadModal/SelectImageModal/hooks.test.js @@ -1,7 +1,7 @@ import React from 'react'; import { dispatch } from 'react-redux'; -import { MockUseState } from '../../../../testUtils'; +import { MockUseState } from '../../../testUtils'; import { keyStore } from '../../../utils'; import { thunkActions } from '../../../data/redux'; diff --git a/src/editors/sharedComponents/ImageUploadModal/SelectImageModal/index.jsx b/src/editors/sharedComponents/ImageUploadModal/SelectImageModal/index.jsx index 2624331004..81e4802cb7 100644 --- a/src/editors/sharedComponents/ImageUploadModal/SelectImageModal/index.jsx +++ b/src/editors/sharedComponents/ImageUploadModal/SelectImageModal/index.jsx @@ -1,13 +1,13 @@ import PropTypes from 'prop-types'; import { connect } from 'react-redux'; -import hooks from './hooks'; +import * as hooks from './hooks'; import { acceptedImgKeys } from './utils'; import SelectionModal from '../../SelectionModal'; import messages from './messages'; import { RequestKeys } from '../../../data/constants/requests'; import { selectors } from '../../../data/redux'; -export const SelectImageModal = ({ +const SelectImageModal = ({ isOpen, close, setSelection, @@ -84,4 +84,5 @@ export const mapStateToProps = (state) => ({ export const mapDispatchToProps = {}; +export const SelectImageModalInternal = SelectImageModal; // For testing only export default connect(mapStateToProps, mapDispatchToProps)(SelectImageModal); diff --git a/src/editors/sharedComponents/ImageUploadModal/SelectImageModal/index.test.jsx b/src/editors/sharedComponents/ImageUploadModal/SelectImageModal/index.test.jsx index 4b52b3afaa..0bd810d280 100644 --- a/src/editors/sharedComponents/ImageUploadModal/SelectImageModal/index.test.jsx +++ b/src/editors/sharedComponents/ImageUploadModal/SelectImageModal/index.test.jsx @@ -1,10 +1,10 @@ import React from 'react'; import { shallow } from '@edx/react-unit-test-utils'; -import { formatMessage } from '../../../../testUtils'; +import { formatMessage } from '../../../testUtils'; import SelectionModal from '../../SelectionModal'; -import hooks from './hooks'; -import { SelectImageModal } from '.'; +import * as hooks from './hooks'; +import { SelectImageModalInternal as SelectImageModal } from '.'; const mockImage = { displayName: 'DALLĀ·E 2023-03-10.png', diff --git a/src/editors/sharedComponents/ImageUploadModal/index.jsx b/src/editors/sharedComponents/ImageUploadModal/index.jsx index 80af029a70..b657265ea5 100644 --- a/src/editors/sharedComponents/ImageUploadModal/index.jsx +++ b/src/editors/sharedComponents/ImageUploadModal/index.jsx @@ -1,10 +1,15 @@ +import 'CourseAuthoring/editors/setupEditorTest'; import React from 'react'; import PropTypes from 'prop-types'; import { injectIntl } from '@edx/frontend-platform/i18n'; -import tinyMCEKeys from '../../data/constants/tinyMCE'; +import * as tinyMCEKeys from '../../data/constants/tinyMCE'; import ImageSettingsModal from './ImageSettingsModal'; import SelectImageModal from './SelectImageModal'; +// This 'module' self-import hack enables mocking during tests. +// See src/editors/decisions/0005-internal-editor-testability-decisions.md. The whole approach to how hooks are tested +// should be re-thought and cleaned up to avoid this pattern. +// eslint-disable-next-line import/no-self-import import * as module from '.'; import { updateImageDimensions } from '../TinyMceWidget/hooks'; @@ -55,6 +60,7 @@ export const updateImagesRef = ({ images: images.current, url: selection.externalUrl, height, width, }); + // eslint-disable-next-line no-param-reassign images.current = imageAlreadyExists ? mappedImages : [...images.current, newImage]; }; @@ -114,7 +120,7 @@ export const hooks = { propsString, }; -export const ImageUploadModal = ({ +const ImageUploadModal = ({ // eslint-disable-next-line editorRef, isOpen, @@ -187,4 +193,5 @@ ImageUploadModal.propTypes = { editorType: PropTypes.string, }; +export const ImageUploadModalInternal = ImageUploadModal; // For testing only export default injectIntl(ImageUploadModal); diff --git a/src/editors/sharedComponents/ImageUploadModal/index.test.jsx b/src/editors/sharedComponents/ImageUploadModal/index.test.jsx index d0e3f860b3..b9b8fbc2bd 100644 --- a/src/editors/sharedComponents/ImageUploadModal/index.test.jsx +++ b/src/editors/sharedComponents/ImageUploadModal/index.test.jsx @@ -3,15 +3,18 @@ import React from 'react'; import { shallow } from '@edx/react-unit-test-utils'; import { keyStore } from '../../utils'; -import tinyMCEKeys from '../../data/constants/tinyMCE'; - +import * as tinyMCEKeys from '../../data/constants/tinyMCE'; +// This 'module' self-import hack enables mocking during tests. +// See src/editors/decisions/0005-internal-editor-testability-decisions.md. The whole approach to how hooks are tested +// should be re-thought and cleaned up to avoid this pattern. +// eslint-disable-next-line import/no-self-import import * as module from '.'; import * as tinyMceHooks from '../TinyMceWidget/hooks'; jest.mock('./ImageSettingsModal', () => 'ImageSettingsModal'); jest.mock('./SelectImageModal', () => 'SelectImageModal'); -const { ImageUploadModal } = module; +const { ImageUploadModalInternal: ImageUploadModal } = module; const hookKeys = keyStore(module.hooks); const settings = { diff --git a/src/editors/sharedComponents/RawEditor/index.jsx b/src/editors/sharedComponents/RawEditor/index.jsx index 1f507f3ab1..d6cccca65c 100644 --- a/src/editors/sharedComponents/RawEditor/index.jsx +++ b/src/editors/sharedComponents/RawEditor/index.jsx @@ -11,7 +11,7 @@ function getValue(content) { return content.data?.data; } -export const RawEditor = ({ +const RawEditor = ({ editorRef, content, lang, diff --git a/src/editors/sharedComponents/RawEditor/index.test.jsx b/src/editors/sharedComponents/RawEditor/index.test.jsx index 848e5070e1..8b68afaf30 100644 --- a/src/editors/sharedComponents/RawEditor/index.test.jsx +++ b/src/editors/sharedComponents/RawEditor/index.test.jsx @@ -3,7 +3,7 @@ import { render, screen } from '@testing-library/react'; import { IntlProvider } from '@edx/frontend-platform/i18n'; import '@testing-library/jest-dom/extend-expect'; -import { RawEditor } from '.'; +import RawEditor from '.'; jest.unmock('@openedx/paragon'); diff --git a/src/editors/sharedComponents/SelectionModal/Gallery.jsx b/src/editors/sharedComponents/SelectionModal/Gallery.jsx index 9124ce80d1..1e01a109fb 100644 --- a/src/editors/sharedComponents/SelectionModal/Gallery.jsx +++ b/src/editors/sharedComponents/SelectionModal/Gallery.jsx @@ -13,7 +13,7 @@ import messages from './messages'; import GalleryCard from './GalleryCard'; import GalleryLoadMoreButton from './GalleryLoadMoreButton'; -export const Gallery = ({ +const Gallery = ({ galleryIsEmpty, searchIsEmpty, displayList, diff --git a/src/editors/sharedComponents/SelectionModal/Gallery.test.jsx b/src/editors/sharedComponents/SelectionModal/Gallery.test.jsx index bddbe37768..e4ba05be25 100644 --- a/src/editors/sharedComponents/SelectionModal/Gallery.test.jsx +++ b/src/editors/sharedComponents/SelectionModal/Gallery.test.jsx @@ -3,7 +3,7 @@ import { shallow } from '@edx/react-unit-test-utils'; import { IntlProvider } from '@edx/frontend-platform/i18n'; -import { Gallery } from './Gallery'; +import Gallery from './Gallery'; jest.mock('../../data/redux', () => ({ selectors: { diff --git a/src/editors/sharedComponents/SelectionModal/GalleryCard.jsx b/src/editors/sharedComponents/SelectionModal/GalleryCard.jsx index 6e73c9fb2d..e1864b3991 100644 --- a/src/editors/sharedComponents/SelectionModal/GalleryCard.jsx +++ b/src/editors/sharedComponents/SelectionModal/GalleryCard.jsx @@ -14,7 +14,7 @@ import messages from './messages'; import { formatDuration } from '../../utils'; import LanguageNamesWidget from '../../containers/VideoEditor/components/VideoSettingsModal/components/VideoPreviewWidget/LanguageNamesWidget'; -export const GalleryCard = ({ +const GalleryCard = ({ asset, thumbnailFallback, }) => { diff --git a/src/editors/sharedComponents/SelectionModal/GalleryCard.test.jsx b/src/editors/sharedComponents/SelectionModal/GalleryCard.test.jsx index b98ae6eeee..6cd930edf3 100644 --- a/src/editors/sharedComponents/SelectionModal/GalleryCard.test.jsx +++ b/src/editors/sharedComponents/SelectionModal/GalleryCard.test.jsx @@ -1,15 +1,9 @@ +import 'CourseAuthoring/editors/setupEditorTest'; import React from 'react'; import { shallow } from '@edx/react-unit-test-utils'; import { Image } from '@openedx/paragon'; -import { GalleryCard } from './GalleryCard'; - -jest.mock('@openedx/paragon', () => ({ - ...jest.requireActual('@openedx/paragon'), - Badge: 'Badge', - SelectableBox: 'SelectableBox', - Image: 'Image', -})); +import GalleryCard from './GalleryCard'; describe('GalleryCard component', () => { const asset = { diff --git a/src/editors/sharedComponents/SelectionModal/SearchSort.jsx b/src/editors/sharedComponents/SelectionModal/SearchSort.jsx index ac9576faa5..a2470edc57 100644 --- a/src/editors/sharedComponents/SelectionModal/SearchSort.jsx +++ b/src/editors/sharedComponents/SelectionModal/SearchSort.jsx @@ -14,7 +14,7 @@ import messages from './messages'; import './index.scss'; import { sortKeys, sortMessages } from '../../containers/VideoGallery/utils'; -export const SearchSort = ({ +const SearchSort = ({ searchString, onSearchChange, clearSearchString, diff --git a/src/editors/sharedComponents/SelectionModal/SearchSort.test.jsx b/src/editors/sharedComponents/SelectionModal/SearchSort.test.jsx index 17c1d39f2e..8757d74e02 100644 --- a/src/editors/sharedComponents/SelectionModal/SearchSort.test.jsx +++ b/src/editors/sharedComponents/SelectionModal/SearchSort.test.jsx @@ -9,7 +9,7 @@ import { IntlProvider } from '@edx/frontend-platform/i18n'; import { filterKeys, filterMessages, sortKeys, sortMessages, } from '../../containers/VideoGallery/utils'; -import { SearchSort } from './SearchSort'; +import SearchSort from './SearchSort'; import messages from './messages'; jest.unmock('react-redux'); diff --git a/src/editors/sharedComponents/SelectionModal/__snapshots__/GalleryCard.test.jsx.snap b/src/editors/sharedComponents/SelectionModal/__snapshots__/GalleryCard.test.jsx.snap index 0dbc28b4d2..f14a235f3e 100644 --- a/src/editors/sharedComponents/SelectionModal/__snapshots__/GalleryCard.test.jsx.snap +++ b/src/editors/sharedComponents/SelectionModal/__snapshots__/GalleryCard.test.jsx.snap @@ -59,12 +59,7 @@ exports[`GalleryCard component snapshot with duration badge 1`] = `

- + props.img.displayName

@@ -143,12 +138,7 @@ exports[`GalleryCard component snapshot with duration transcripts 1`] = `

- + props.img.displayName

@@ -242,12 +232,7 @@ exports[`GalleryCard component snapshot with status badge 1`] = `

- + props.img.displayName

@@ -327,12 +312,7 @@ exports[`GalleryCard component snapshot with thumbnail fallback and load error 1

- + props.img.displayName

@@ -412,12 +392,7 @@ exports[`GalleryCard component snapshot with thumbnail fallback and no error 1`]

- + props.img.displayName

@@ -496,12 +471,7 @@ exports[`GalleryCard component snapshot: dateAdded=12345 1`] = `

- + props.img.displayName

diff --git a/src/editors/sharedComponents/SelectionModal/index.jsx b/src/editors/sharedComponents/SelectionModal/index.jsx index 3e68eae1b1..f96a29c832 100644 --- a/src/editors/sharedComponents/SelectionModal/index.jsx +++ b/src/editors/sharedComponents/SelectionModal/index.jsx @@ -11,14 +11,14 @@ import { import BaseModal from '../BaseModal'; import SearchSort from './SearchSort'; import Gallery from './Gallery'; -import FileInput from '../FileInput'; +import { FileInput } from '../FileInput'; import ErrorAlert from '../ErrorAlerts/ErrorAlert'; import FetchErrorAlert from '../ErrorAlerts/FetchErrorAlert'; import UploadErrorAlert from '../ErrorAlerts/UploadErrorAlert'; import './index.scss'; -export const SelectionModal = ({ +const SelectionModal = ({ isOpen, close, size, diff --git a/src/editors/sharedComponents/SelectionModal/index.scss b/src/editors/sharedComponents/SelectionModal/index.scss index 50fbd29c51..89e5978168 100644 --- a/src/editors/sharedComponents/SelectionModal/index.scss +++ b/src/editors/sharedComponents/SelectionModal/index.scss @@ -8,7 +8,7 @@ /* Sort options come in pairs of ascending and descending. */ .search-sort-menu .pgn__menu > div:nth-child(even) { - border-bottom: 1px solid #f4f3f6; + border-bottom: 1px solid #F4F3F6; } .search-sort-menu .pgn__menu > div:last-child { @@ -27,6 +27,6 @@ .selection-modal .pgn__vstack > .alert > .alert-icon { /* Vertical margin equal to the vertical padding of the "Dismiss" button. */ - margin-bottom: 0.4375rem; - margin-top: 0.4375rem; + margin-bottom: .4375rem; + margin-top: .4375rem; } diff --git a/src/editors/sharedComponents/SelectionModal/index.test.jsx b/src/editors/sharedComponents/SelectionModal/index.test.jsx index 6f716c3ee0..91186e8d69 100644 --- a/src/editors/sharedComponents/SelectionModal/index.test.jsx +++ b/src/editors/sharedComponents/SelectionModal/index.test.jsx @@ -89,10 +89,12 @@ jest.mock('./Gallery', () => function mockGallery(componentProps) { mockGalleryFn(componentProps); return (
Gallery
); }); -jest.mock('../FileInput', () => function mockFileInput(componentProps) { - mockFileInputFn(componentProps); - return (
FileInput
); -}); +jest.mock('../FileInput', () => ({ + FileInput: function mockFileInput(componentProps) { + mockFileInputFn(componentProps); + return (
FileInput
); + }, +})); jest.mock('../ErrorAlerts/ErrorAlert', () => function mockErrorAlert() { return
ErrorAlert
; }); diff --git a/src/editors/sharedComponents/SelectionModal/messages.js b/src/editors/sharedComponents/SelectionModal/messages.js index 9e865930b1..0fcf9b4214 100644 --- a/src/editors/sharedComponents/SelectionModal/messages.js +++ b/src/editors/sharedComponents/SelectionModal/messages.js @@ -1,4 +1,4 @@ -export const messages = { +const messages = { searchPlaceholder: { id: 'authoring.selectionmodal.search.placeholder', defaultMessage: 'Search', diff --git a/src/editors/sharedComponents/SourceCodeModal/hooks.js b/src/editors/sharedComponents/SourceCodeModal/hooks.js index 04c40112fe..0c35f1b4c3 100644 --- a/src/editors/sharedComponents/SourceCodeModal/hooks.js +++ b/src/editors/sharedComponents/SourceCodeModal/hooks.js @@ -1,4 +1,8 @@ import { useRef } from 'react'; +// This 'module' self-import hack enables mocking during tests. +// See src/editors/decisions/0005-internal-editor-testability-decisions.md. The whole approach to how hooks are tested +// should be re-thought and cleaned up to avoid this pattern. +// eslint-disable-next-line import/no-self-import import * as module from './hooks'; export const getSaveBtnProps = ({ editorRef, ref, close }) => ({ @@ -22,7 +26,3 @@ export const prepareSourceCodeModal = ({ editorRef, close }) => { } return { saveBtnProps, value: null, ref }; }; - -export default { - prepareSourceCodeModal, -}; diff --git a/src/editors/sharedComponents/SourceCodeModal/index.jsx b/src/editors/sharedComponents/SourceCodeModal/index.jsx index 6c00c374ea..88f7ff9f8e 100644 --- a/src/editors/sharedComponents/SourceCodeModal/index.jsx +++ b/src/editors/sharedComponents/SourceCodeModal/index.jsx @@ -9,12 +9,12 @@ import { import { Button, useWindowSize } from '@openedx/paragon'; import messages from './messages'; -import hooks from './hooks'; +import * as hooks from './hooks'; import BaseModal from '../BaseModal'; import CodeEditor from '../CodeEditor'; -export const SourceCodeModal = ({ +const SourceCodeModal = ({ isOpen, close, editorRef, @@ -59,4 +59,5 @@ SourceCodeModal.propTypes = { intl: intlShape.isRequired, }; +export const SourceCodeModalInternal = SourceCodeModal; // For testing only export default injectIntl(SourceCodeModal); diff --git a/src/editors/sharedComponents/SourceCodeModal/index.test.jsx b/src/editors/sharedComponents/SourceCodeModal/index.test.jsx index 95468064de..a95b40e98a 100644 --- a/src/editors/sharedComponents/SourceCodeModal/index.test.jsx +++ b/src/editors/sharedComponents/SourceCodeModal/index.test.jsx @@ -1,9 +1,10 @@ +import 'CourseAuthoring/editors/setupEditorTest'; import React from 'react'; import { shallow } from '@edx/react-unit-test-utils'; -import hooks from './hooks'; -import { formatMessage } from '../../../testUtils'; +import * as hooks from './hooks'; +import { formatMessage } from '../../testUtils'; -import { SourceCodeModal } from '.'; +import { SourceCodeModalInternal as SourceCodeModal } from '.'; jest.mock('./hooks', () => ({ prepareSourceCodeModal: jest.fn(() => { diff --git a/src/editors/sharedComponents/TinyMceWidget/hooks.js b/src/editors/sharedComponents/TinyMceWidget/hooks.js index a2fe25717d..2353a158ed 100644 --- a/src/editors/sharedComponents/TinyMceWidget/hooks.js +++ b/src/editors/sharedComponents/TinyMceWidget/hooks.js @@ -6,12 +6,16 @@ import { } from 'react'; import { getLocale, isRtl } from '@edx/frontend-platform/i18n'; import { a11ycheckerCss } from 'frontend-components-tinymce-advanced-plugins'; -import { isEmpty } from 'lodash-es'; +import { isEmpty } from 'lodash'; import tinyMCEStyles from '../../data/constants/tinyMCEStyles'; import { StrictDict } from '../../utils'; import pluginConfig from './pluginConfig'; +// This 'module' self-import hack enables mocking during tests. +// See src/editors/decisions/0005-internal-editor-testability-decisions.md. The whole approach to how hooks are tested +// should be re-thought and cleaned up to avoid this pattern. +// eslint-disable-next-line import/no-self-import import * as module from './hooks'; -import tinyMCE from '../../data/constants/tinyMCE'; +import * as tinyMCE from '../../data/constants/tinyMCE'; import { getRelativeUrl, getStaticUrl, parseAssetName } from './utils'; export const state = StrictDict({ @@ -30,6 +34,7 @@ export const addImagesAndDimensionsToRef = ({ imagesRef, images, editorContentHt const imageFragment = module.getImageFromHtmlString(editorContentHtml, image.url); return { ...image, width: imageFragment?.width, height: imageFragment?.height }; }); + // eslint-disable-next-line no-param-reassign imagesRef.current = imagesWithDimensions; }; @@ -119,6 +124,7 @@ export const getImageResizeHandler = ({ editor, imagesRef, setImage }) => () => src, alt, width, height, } = editor.selection.getNode(); + // eslint-disable-next-line no-param-reassign imagesRef.current = module.updateImageDimensions({ images: imagesRef.current, url: src, width, height, }).result; diff --git a/src/editors/sharedComponents/TinyMceWidget/hooks.test.js b/src/editors/sharedComponents/TinyMceWidget/hooks.test.js index 36f46515e0..7f9ee94bcb 100644 --- a/src/editors/sharedComponents/TinyMceWidget/hooks.test.js +++ b/src/editors/sharedComponents/TinyMceWidget/hooks.test.js @@ -1,8 +1,13 @@ -import { MockUseState } from '../../../testUtils'; +import 'CourseAuthoring/editors/setupEditorTest'; +import { MockUseState } from '../../testUtils'; -import tinyMCE from '../../data/constants/tinyMCE'; +import * as tinyMCE from '../../data/constants/tinyMCE'; import { keyStore } from '../../utils'; import pluginConfig from './pluginConfig'; +// This 'module' self-import hack enables mocking during tests. +// See src/editors/decisions/0005-internal-editor-testability-decisions.md. The whole approach to how hooks are tested +// should be re-thought and cleaned up to avoid this pattern. +// eslint-disable-next-line import/no-self-import import * as module from './hooks'; jest.mock('react', () => ({ diff --git a/src/editors/sharedComponents/TinyMceWidget/index.jsx b/src/editors/sharedComponents/TinyMceWidget/index.jsx index 151d08cba7..601af1bf87 100644 --- a/src/editors/sharedComponents/TinyMceWidget/index.jsx +++ b/src/editors/sharedComponents/TinyMceWidget/index.jsx @@ -16,6 +16,8 @@ import SourceCodeModal from '../SourceCodeModal'; import * as hooks from './hooks'; import './customTinyMcePlugins/embedIframePlugin'; +export { prepareEditorRef } from './hooks'; + const editorConfigDefaultProps = { setEditorRef: undefined, placeholder: undefined, @@ -34,7 +36,7 @@ const editorConfigPropTypes = { minHeight: PropTypes.any, }; -export const TinyMceWidget = ({ +const TinyMceWidget = ({ editorType, editorRef, disabled, @@ -137,4 +139,5 @@ export const mapStateToProps = (state) => ({ learningContextId: selectors.app.learningContextId(state), }); +export const TinyMceWidgetInternal = TinyMceWidget; // For testing only export default (connect(mapStateToProps)(TinyMceWidget)); diff --git a/src/editors/sharedComponents/TinyMceWidget/index.test.jsx b/src/editors/sharedComponents/TinyMceWidget/index.test.jsx index 1b2a51b55e..c0064c2d2b 100644 --- a/src/editors/sharedComponents/TinyMceWidget/index.test.jsx +++ b/src/editors/sharedComponents/TinyMceWidget/index.test.jsx @@ -4,7 +4,7 @@ import { selectors } from '../../data/redux'; import SourceCodeModal from '../SourceCodeModal'; import ImageUploadModal from '../ImageUploadModal'; import { imgModalToggle, sourceCodeModalToggle } from './hooks'; -import { TinyMceWidget, mapStateToProps } from '.'; +import { TinyMceWidgetInternal as TinyMceWidget, mapStateToProps } from '.'; const staticUrl = '/assets/sOmEaSsET'; diff --git a/src/editors/sharedComponents/TypeaheadDropdown/FormGroup.jsx b/src/editors/sharedComponents/TypeaheadDropdown/FormGroup.jsx index 1c728aa4fe..f0739e1e4b 100644 --- a/src/editors/sharedComponents/TypeaheadDropdown/FormGroup.jsx +++ b/src/editors/sharedComponents/TypeaheadDropdown/FormGroup.jsx @@ -1,6 +1,6 @@ import React from 'react'; import PropTypes from 'prop-types'; -import _ from 'lodash-es'; +import _ from 'lodash'; import { Form } from '@openedx/paragon'; const FormGroup = (props) => { diff --git a/src/editors/sharedComponents/TypeaheadDropdown/index.jsx b/src/editors/sharedComponents/TypeaheadDropdown/index.jsx index 0938a00a58..23e0ea1a77 100644 --- a/src/editors/sharedComponents/TypeaheadDropdown/index.jsx +++ b/src/editors/sharedComponents/TypeaheadDropdown/index.jsx @@ -7,7 +7,7 @@ import { } from '@openedx/paragon'; import { Add, ExpandLess, ExpandMore } from '@openedx/paragon/icons'; import PropTypes from 'prop-types'; -import { sortBy } from 'lodash-es'; +import { sortBy } from 'lodash'; // eslint-disable-next-line import/no-unresolved import onClickOutside from 'react-onclickoutside'; import FormGroup from './FormGroup'; diff --git a/src/editors/utils/camelizeKeys.js b/src/editors/utils/camelizeKeys.js index 99457a5ab8..9a69870baa 100644 --- a/src/editors/utils/camelizeKeys.js +++ b/src/editors/utils/camelizeKeys.js @@ -1,4 +1,4 @@ -import { camelCase } from 'lodash-es'; +import { camelCase } from 'lodash'; const camelizeKeys = (obj) => { if (Array.isArray(obj)) { diff --git a/src/editors/utils/snakeCaseKeys.js b/src/editors/utils/snakeCaseKeys.js index d07acfba17..f522ce44bd 100644 --- a/src/editors/utils/snakeCaseKeys.js +++ b/src/editors/utils/snakeCaseKeys.js @@ -1,4 +1,4 @@ -import { snakeCase } from 'lodash-es'; +import { snakeCase } from 'lodash'; const snakeCaseKeys = (obj) => { if (Array.isArray(obj)) { diff --git a/src/files-and-videos/files-page/FilesPage.jsx b/src/files-and-videos/files-page/FilesPage.jsx index 6c3ef01b43..be100bd55b 100644 --- a/src/files-and-videos/files-page/FilesPage.jsx +++ b/src/files-and-videos/files-page/FilesPage.jsx @@ -3,7 +3,7 @@ import PropTypes from 'prop-types'; import { useDispatch, useSelector } from 'react-redux'; import { injectIntl, FormattedMessage, intlShape } from '@edx/frontend-platform/i18n'; import { CheckboxFilter, Container } from '@openedx/paragon'; -import Placeholder from '@edx/frontend-lib-content-components'; +import Placeholder from '../../editors/Placeholder'; import { RequestStatus } from '../../data/constants'; import { useModels, useModel } from '../../generic/model-store'; diff --git a/src/files-and-videos/generic/EditFileErrors.jsx b/src/files-and-videos/generic/EditFileErrors.jsx index 97b5c32ce4..a964fbc9da 100644 --- a/src/files-and-videos/generic/EditFileErrors.jsx +++ b/src/files-and-videos/generic/EditFileErrors.jsx @@ -2,7 +2,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { injectIntl, intlShape } from '@edx/frontend-platform/i18n'; import { Alert } from '@openedx/paragon'; -import { ErrorAlert } from '@edx/frontend-lib-content-components'; +import ErrorAlert from '../../editors/sharedComponents/ErrorAlerts/ErrorAlert'; import { RequestStatus } from '../../data/constants'; import messages from './messages'; diff --git a/src/files-and-videos/generic/table-components/sort-and-filter-modal/SortAndFilterModal.jsx b/src/files-and-videos/generic/table-components/sort-and-filter-modal/SortAndFilterModal.jsx index fef4b8c01c..b45c29e80b 100644 --- a/src/files-and-videos/generic/table-components/sort-and-filter-modal/SortAndFilterModal.jsx +++ b/src/files-and-videos/generic/table-components/sort-and-filter-modal/SortAndFilterModal.jsx @@ -1,8 +1,6 @@ import React, { useContext, useEffect, useState } from 'react'; import PropTypes from 'prop-types'; import { injectIntl, intlShape, FormattedMessage } from '@edx/frontend-platform/i18n'; -// SelectableBox in paragon has a bug only visible on stage where you can't change selection. So we override it -import { SelectableBox } from '@edx/frontend-lib-content-components'; import { ActionRow, Button, @@ -12,6 +10,8 @@ import { useCheckboxSetValues, } from '@openedx/paragon'; import messages from './messages'; +// SelectableBox in paragon has a bug only visible on stage where you can't change selection. So we override it +import SelectableBox from '../../../../editors/sharedComponents/SelectableBox'; import { getCheckedFilters, getFilterOptions, processFilters } from './utils'; const SortAndFilterModal = ({ diff --git a/src/files-and-videos/videos-page/VideosPage.jsx b/src/files-and-videos/videos-page/VideosPage.jsx index 4f006572b5..bf1f78528c 100644 --- a/src/files-and-videos/videos-page/VideosPage.jsx +++ b/src/files-and-videos/videos-page/VideosPage.jsx @@ -14,8 +14,8 @@ import { Container, useToggle, } from '@openedx/paragon'; -import Placeholder from '@edx/frontend-lib-content-components'; +import Placeholder from '../../editors/Placeholder'; import { RequestStatus } from '../../data/constants'; import { useModels, useModel } from '../../generic/model-store'; import { diff --git a/src/files-and-videos/videos-page/info-sidebar/TranscriptTab.jsx b/src/files-and-videos/videos-page/info-sidebar/TranscriptTab.jsx index 57bebc1367..6c08860b0d 100644 --- a/src/files-and-videos/videos-page/info-sidebar/TranscriptTab.jsx +++ b/src/files-and-videos/videos-page/info-sidebar/TranscriptTab.jsx @@ -2,10 +2,11 @@ import React, { useEffect, useState, useRef } from 'react'; import PropTypes from 'prop-types'; import { useDispatch, useSelector } from 'react-redux'; import { isEmpty } from 'lodash'; -import { ErrorAlert } from '@edx/frontend-lib-content-components'; import { Button, Stack } from '@openedx/paragon'; import { Add } from '@openedx/paragon/icons'; import { injectIntl, intlShape } from '@edx/frontend-platform/i18n'; + +import ErrorAlert from '../../../editors/sharedComponents/ErrorAlerts/ErrorAlert'; import { getLanguages, getSortedTranscripts } from '../data/utils'; import Transcript from './transcript-item'; import { diff --git a/src/files-and-videos/videos-page/transcript-settings/OrderTranscriptForm.jsx b/src/files-and-videos/videos-page/transcript-settings/OrderTranscriptForm.jsx index 76e66f1c7c..28e344e36c 100644 --- a/src/files-and-videos/videos-page/transcript-settings/OrderTranscriptForm.jsx +++ b/src/files-and-videos/videos-page/transcript-settings/OrderTranscriptForm.jsx @@ -7,7 +7,8 @@ import { intlShape, } from '@edx/frontend-platform/i18n'; import { Button, Stack } from '@openedx/paragon'; -import { ErrorAlert, SelectableBox } from '@edx/frontend-lib-content-components'; +import ErrorAlert from '../../../editors/sharedComponents/ErrorAlerts/ErrorAlert'; +import SelectableBox from '../../../editors/sharedComponents/SelectableBox'; import Cielo24Form from './Cielo24Form'; import ThreePlayMediaForm from './ThreePlayMediaForm'; import { RequestStatus } from '../../../data/constants'; diff --git a/src/generic/WysiwygEditor.jsx b/src/generic/WysiwygEditor.jsx index b506b74ba5..dd326181cf 100644 --- a/src/generic/WysiwygEditor.jsx +++ b/src/generic/WysiwygEditor.jsx @@ -3,10 +3,7 @@ import PropTypes from 'prop-types'; import { connect, Provider, useSelector } from 'react-redux'; import { createStore } from 'redux'; import { getConfig } from '@edx/frontend-platform'; -import { - prepareEditorRef, - TinyMceWidget, -} from '@edx/frontend-lib-content-components'; +import TinyMceWidget, { prepareEditorRef } from '../editors/sharedComponents/TinyMceWidget'; import { DEFAULT_EMPTY_WYSIWYG_VALUE } from '../constants'; diff --git a/src/generic/create-or-rerun-course/CreateOrRerunCourseForm.jsx b/src/generic/create-or-rerun-course/CreateOrRerunCourseForm.jsx index fc4f48666e..cffbf53f24 100644 --- a/src/generic/create-or-rerun-course/CreateOrRerunCourseForm.jsx +++ b/src/generic/create-or-rerun-course/CreateOrRerunCourseForm.jsx @@ -12,7 +12,7 @@ import { TransitionReplace, } from '@openedx/paragon'; import { Info as InfoIcon } from '@openedx/paragon/icons'; -import { TypeaheadDropdown } from '@edx/frontend-lib-content-components'; +import TypeaheadDropdown from '../../editors/sharedComponents/TypeaheadDropdown'; import AlertMessage from '../alert-message'; import { STATEFUL_BUTTON_STATES } from '../../constants'; diff --git a/src/pages-and-resources/SettingsComponent.jsx b/src/pages-and-resources/SettingsComponent.jsx index a45d64db10..bf6d295300 100644 --- a/src/pages-and-resources/SettingsComponent.jsx +++ b/src/pages-and-resources/SettingsComponent.jsx @@ -2,8 +2,8 @@ import React from 'react'; import PropTypes from 'prop-types'; import { useParams, useNavigate } from 'react-router-dom'; import { useIntl } from '@edx/frontend-platform/i18n'; -import { ErrorAlert } from '@edx/frontend-lib-content-components'; +import ErrorAlert from '../editors/sharedComponents/ErrorAlerts/ErrorAlert'; import messages from './messages'; const PluginLoadFailedError = () => { diff --git a/src/schedule-and-details/ScheduleAndDetails.test.jsx b/src/schedule-and-details/ScheduleAndDetails.test.jsx index 72891fda46..0afee52fee 100644 --- a/src/schedule-and-details/ScheduleAndDetails.test.jsx +++ b/src/schedule-and-details/ScheduleAndDetails.test.jsx @@ -36,9 +36,9 @@ jest.mock('@tinymce/tinymce-react', () => { }; }); -// Mock the TinyMceWidget from frontend-lib-content-components -jest.mock('@edx/frontend-lib-content-components', () => ({ - TinyMceWidget: () =>
Widget
, +jest.mock('../editors/sharedComponents/TinyMceWidget', () => ({ + __esModule: true, // Required to mock a default export + default: () =>
Widget
, prepareEditorRef: jest.fn(() => ({ refReady: true, setEditorRef: jest.fn().mockName('prepareEditorRef.setEditorRef'), diff --git a/src/schedule-and-details/index.jsx b/src/schedule-and-details/index.jsx index 40fdf1c019..f8d114f44d 100644 --- a/src/schedule-and-details/index.jsx +++ b/src/schedule-and-details/index.jsx @@ -10,8 +10,8 @@ import { Warning as WarningIcon, } from '@openedx/paragon/icons'; import { injectIntl, intlShape } from '@edx/frontend-platform/i18n'; -import Placeholder from '@edx/frontend-lib-content-components'; +import Placeholder from '../editors/Placeholder'; import { RequestStatus } from '../data/constants'; import { useModel } from '../generic/model-store'; import AlertMessage from '../generic/alert-message'; diff --git a/src/schedule-and-details/introducing-section/IntroducingSection.test.jsx b/src/schedule-and-details/introducing-section/IntroducingSection.test.jsx index f0b3feb93e..9b7dd765f2 100644 --- a/src/schedule-and-details/introducing-section/IntroducingSection.test.jsx +++ b/src/schedule-and-details/introducing-section/IntroducingSection.test.jsx @@ -19,9 +19,10 @@ jest.mock('@tinymce/tinymce-react', () => { }; }); -// Mock the TinyMceWidget from frontend-lib-content-components -jest.mock('@edx/frontend-lib-content-components', () => ({ - TinyMceWidget: () =>
Widget
, +// Mock the TinyMceWidget +jest.mock('../../editors/sharedComponents/TinyMceWidget', () => ({ + __esModule: true, // Required to mock a default export + default: () =>
Widget
, prepareEditorRef: jest.fn(() => ({ refReady: true, setEditorRef: jest.fn().mockName('prepareEditorRef.setEditorRef'), diff --git a/src/selectors/VideoSelectorContainer.jsx b/src/selectors/VideoSelectorContainer.jsx index e37ef6ba70..ea83315d5a 100644 --- a/src/selectors/VideoSelectorContainer.jsx +++ b/src/selectors/VideoSelectorContainer.jsx @@ -1,8 +1,8 @@ import React from 'react'; import PropTypes from 'prop-types'; import { useParams } from 'react-router-dom'; -import { VideoSelectorPage } from '@edx/frontend-lib-content-components'; import { getConfig } from '@edx/frontend-platform'; +import VideoSelectorPage from '../editors/VideoSelectorPage'; const VideoSelectorContainer = ({ courseId, diff --git a/src/selectors/VideoSelectorContainer.test.jsx b/src/selectors/VideoSelectorContainer.test.jsx index c4064b3e23..f32053f40f 100644 --- a/src/selectors/VideoSelectorContainer.test.jsx +++ b/src/selectors/VideoSelectorContainer.test.jsx @@ -2,7 +2,10 @@ import React from 'react'; import { shallow } from '@edx/react-unit-test-utils'; import VideoSelectorContainer from './VideoSelectorContainer'; -jest.mock('@edx/frontend-lib-content-components', () => ({ VideoSelectorPage: () => 'HeaderTitle' })); +jest.mock('../editors/VideoSelectorPage', () => ({ + default: function VideoSelectorPage() { return 'HeaderTitle'; }, + __esModule: true, // Required to mock a default export +})); jest.mock('react-router', () => ({ ...jest.requireActual('react-router'), // use actual for all non-hook parts diff --git a/src/studio-home/organization-section/index.jsx b/src/studio-home/organization-section/index.jsx index f64a7205cc..c7cd0688cf 100644 --- a/src/studio-home/organization-section/index.jsx +++ b/src/studio-home/organization-section/index.jsx @@ -4,8 +4,8 @@ import { useDispatch, useSelector } from 'react-redux'; import { isEmpty } from 'lodash'; import { Button, Form, FormLabel } from '@openedx/paragon'; import { injectIntl, intlShape } from '@edx/frontend-platform/i18n'; -import { TypeaheadDropdown } from '@edx/frontend-lib-content-components'; +import TypeaheadDropdown from '../../editors/sharedComponents/TypeaheadDropdown'; import { getOrganizations } from '../../generic/data/selectors'; import { fetchOrganizationsQuery } from '../../generic/data/thunks'; import messages from '../messages';