diff --git a/package-lock.json b/package-lock.json index 8fed87ed..85689f29 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13780,6 +13780,14 @@ "react-transition-group": "^4.4.1" } }, + "react-toggle": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/react-toggle/-/react-toggle-4.1.1.tgz", + "integrity": "sha512-+wXlMcSpg8SmnIXauMaZiKpR+r2wp2gMUteroejp2UTSqGTVvZLN+m9EhMzFARBKEw7KpQOwzCyfzeHeAndQGw==", + "requires": { + "classnames": "^2.2.5" + } + }, "react-transition-group": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.1.tgz", diff --git a/package.json b/package.json index 6e96ac30..0d8e545a 100644 --- a/package.json +++ b/package.json @@ -55,6 +55,7 @@ "react-dom": "^16.13.1", "react-router-dom": "^5.2.0", "react-toastify": "^6.0.5", + "react-toggle": "^4.1.1", "react-virtualized-auto-sizer": "^1.0.2", "react-window": "^1.8.5" }, @@ -96,7 +97,7 @@ "jest": { "verbose": true, "moduleNameMapper": { - "\\.(jpg|jpeg|png|svg)$": "/src/__mocks__/fileMock.js" + "\\.(jpg|jpeg|png|svg|css)$": "/src/__mocks__/fileMock.js" }, "setupFilesAfterEnv": [ "./tests/setupTests.js" diff --git a/src/components/Playground.js b/src/components/Playground.js index c3553cad..2df10f9d 100644 --- a/src/components/Playground.js +++ b/src/components/Playground.js @@ -15,7 +15,7 @@ function onStateChange({ markup, query, result }) { const initialValues = state.load() || {}; function Playground() { - const [{ markup, query, result }, dispatch] = usePlayground({ + const [{ markup, query, result, eventExecuted }, dispatch] = usePlayground({ onChange: onStateChange, ...initialValues, }); @@ -29,7 +29,7 @@ function Playground() {
- +
diff --git a/src/components/Result.js b/src/components/Result.js index f771ef83..ba243fbf 100644 --- a/src/components/Result.js +++ b/src/components/Result.js @@ -4,14 +4,19 @@ import ResultQueries from './ResultQueries'; import ResultSuggestion from './ResultSuggestion'; import Scrollable from './Scrollable'; import { emptyResult } from '../lib'; +import UserEventResult from './UserEventResult'; -function Result({ result, dispatch }) { +function Result({ result, eventExecuted, dispatch }) { if (result.error) { return ( ); } - + if (result.expression && result.expression.userEvent) { + return ( + + ); + } if ( !result.expression || !Array.isArray(result.elements) || diff --git a/src/components/UserEventResult.js b/src/components/UserEventResult.js new file mode 100644 index 00000000..359198c6 --- /dev/null +++ b/src/components/UserEventResult.js @@ -0,0 +1,47 @@ +import React from 'react'; +import Toggle from 'react-toggle'; +import 'react-toggle/style.css'; + +const UserEventResult = ({ eventExecuted, dispatch }) => { + const handleToggleChange = () => + dispatch({ + type: 'TOGGLE_EXECUTION', + }); + + const label = eventExecuted + ? 'showing the preview after user-event action is applied' + : 'showing the preview according to the original markup'; + + return ( +
+
+

+ + @testing-library/user-event + {' '} + method detected! +

+
+
+ +
+
+ ); +}; + +export default UserEventResult; diff --git a/src/hooks/usePlayground.js b/src/hooks/usePlayground.js index 62209cda..be54862a 100644 --- a/src/hooks/usePlayground.js +++ b/src/hooks/usePlayground.js @@ -47,6 +47,10 @@ function reducer(state, action) { }; } + case 'TOGGLE_EXECUTION': { + return { ...state, eventExecuted: !state.eventExecuted }; + } + default: { throw new Error('Unknown action type: ' + action.type); } @@ -65,6 +69,7 @@ function usePlayground(props) { const [state, dispatch] = useReducer(withLogging(reducer), { rootNode, markup, + eventExecuted: result ? result.markup !== markup : false, query, result, }); @@ -74,7 +79,6 @@ function usePlayground(props) { onChange(state); } }, [state.result]); - return [state, dispatch]; } diff --git a/src/parser.js b/src/parser.js index e23d4cef..60bbc184 100644 --- a/src/parser.js +++ b/src/parser.js @@ -11,6 +11,8 @@ import { logDOM, } from '@testing-library/dom'; +import userEvent from '@testing-library/user-event'; + const debug = (element, maxLength, options) => Array.isArray(element) ? element.map((el) => logDOM(el, maxLength, options)).join('\n') @@ -81,6 +83,7 @@ function getLastExpression(code) { level, args, call, + userEvent: minified.trim().startsWith('userEvent.'), }; } @@ -88,6 +91,7 @@ function createEvaluator({ rootNode }) { const context = Object.assign({}, queries, { screen: getScreen(rootNode), container: rootNode, + userEvent, }); const evaluator = Function.apply(null, [ @@ -109,6 +113,7 @@ function createEvaluator({ rootNode }) { details: error.slice(1).join('\n').trim(), }; } + result.markup = rootNode.innerHTML; result.elements = ensureArray(result.data) .filter((x) => x?.nodeType === Node.ELEMENT_NODE) @@ -140,6 +145,19 @@ function createEvaluator({ rootNode }) { return { context, evaluator, exec, wrap }; } +function parseScripts(markup) { + const container = document.createElement('div'); + container.innerHTML = markup; + const scriptsCollections = container.getElementsByTagName('script'); + const jsScripts = Array.from(scriptsCollections).filter( + (script) => script.type === 'text/javascript' || script.type === '', + ); + return jsScripts.map((script) => ({ + scriptCode: script.innerHTML, + evaluated: false, + })); +} + function createSandbox({ markup }) { // render the frame in a container, so we can set "display: none". If the // hiding would be done in the frame itself, testing-library would mark the @@ -194,6 +212,17 @@ function createSandbox({ markup }) { body = html; } }, + evalScripts: () => + parseScripts(markup) + .filter((script) => !script.evaluated) + .forEach((script) => { + try { + frame.contentWindow.exec(context, script.scriptCode); + script.evaluated = true; + } catch (e) { + console.log(e); + } + }), eval: (query) => wrap(() => frame.contentWindow.exec(context, query), { markup, query }), destroy: () => document.body.removeChild(container), @@ -217,6 +246,7 @@ function runInSandbox({ markup, query, cacheId }) { const sandbox = sandboxes[cacheId] || createSandbox({ markup }); sandbox.ensureMarkup(markup); + sandbox.evalScripts(); const result = sandbox.eval(query); if (cacheId && !sandboxes[cacheId]) {