From 666dec5246ef5e78213fd49e865558da5b016240 Mon Sep 17 00:00:00 2001 From: Nick Strayer Date: Fri, 24 Mar 2023 17:21:24 -0400 Subject: [PATCH] Add playwright test of error boundaries --- .../{index-115a400a.js => index-760ede59.js} | 2 +- inst/editor/build/index.html | 2 +- .../playwright/error-boundaries.spec.ts | 96 +++++++++++++++++++ .../ErrorCatcher/GeneralErrorView.tsx | 5 +- 4 files changed, 102 insertions(+), 3 deletions(-) rename inst/editor/build/assets/{index-115a400a.js => index-760ede59.js} (99%) create mode 100644 inst/editor/playwright/error-boundaries.spec.ts diff --git a/inst/editor/build/assets/index-115a400a.js b/inst/editor/build/assets/index-760ede59.js similarity index 99% rename from inst/editor/build/assets/index-115a400a.js rename to inst/editor/build/assets/index-760ede59.js index 712c4ee09..8ab6526d1 100644 --- a/inst/editor/build/assets/index-115a400a.js +++ b/inst/editor/build/assets/index-760ede59.js @@ -107,7 +107,7 @@ Error generating stack: `+o.message+` * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */var ig=Symbol.for("react.element"),og=Symbol.for("react.portal"),af=Symbol.for("react.fragment"),sf=Symbol.for("react.strict_mode"),uf=Symbol.for("react.profiler"),cf=Symbol.for("react.provider"),ff=Symbol.for("react.context"),GN=Symbol.for("react.server_context"),df=Symbol.for("react.forward_ref"),pf=Symbol.for("react.suspense"),hf=Symbol.for("react.suspense_list"),mf=Symbol.for("react.memo"),gf=Symbol.for("react.lazy"),JN=Symbol.for("react.offscreen"),zS;zS=Symbol.for("react.module.reference");function wn(e){if(typeof e=="object"&&e!==null){var t=e.$$typeof;switch(t){case ig:switch(e=e.type,e){case af:case uf:case sf:case pf:case hf:return e;default:switch(e=e&&e.$$typeof,e){case GN:case ff:case df:case gf:case mf:case cf:return e;default:return t}}case og:return t}}}Ee.ContextConsumer=ff;Ee.ContextProvider=cf;Ee.Element=ig;Ee.ForwardRef=df;Ee.Fragment=af;Ee.Lazy=gf;Ee.Memo=mf;Ee.Portal=og;Ee.Profiler=uf;Ee.StrictMode=sf;Ee.Suspense=pf;Ee.SuspenseList=hf;Ee.isAsyncMode=function(){return!1};Ee.isConcurrentMode=function(){return!1};Ee.isContextConsumer=function(e){return wn(e)===ff};Ee.isContextProvider=function(e){return wn(e)===cf};Ee.isElement=function(e){return typeof e=="object"&&e!==null&&e.$$typeof===ig};Ee.isForwardRef=function(e){return wn(e)===df};Ee.isFragment=function(e){return wn(e)===af};Ee.isLazy=function(e){return wn(e)===gf};Ee.isMemo=function(e){return wn(e)===mf};Ee.isPortal=function(e){return wn(e)===og};Ee.isProfiler=function(e){return wn(e)===uf};Ee.isStrictMode=function(e){return wn(e)===sf};Ee.isSuspense=function(e){return wn(e)===pf};Ee.isSuspenseList=function(e){return wn(e)===hf};Ee.isValidElementType=function(e){return typeof e=="string"||typeof e=="function"||e===af||e===uf||e===sf||e===pf||e===hf||e===JN||typeof e=="object"&&e!==null&&(e.$$typeof===gf||e.$$typeof===mf||e.$$typeof===cf||e.$$typeof===ff||e.$$typeof===df||e.$$typeof===zS||e.getModuleId!==void 0)};Ee.typeOf=wn;(function(e){e.exports=Ee})(HN);const QN=Hh(fh);function KN(e){const t=e&&typeof e=="object"&&e.type==="text"?e.value||"":e;return typeof t=="string"&&t.replace(/[ \t\n\f\r]/g,"")===""}function qN(e){return e.join(" ").trim()}function XN(e,t){const n=t||{};return(e[e.length-1]===""?[...e,""]:e).join((n.padRight?" ":"")+","+(n.padLeft===!1?"":" ")).trim()}var $u={},ZN={get exports(){return $u},set exports(e){$u=e}},f1=/\/\*[^*]*\*+([^/*][^*]*\*+)*\//g,e3=/\n/g,t3=/^\s*/,n3=/^(\*?[-#/*\\\w]+(\[[0-9a-z_-]+\])?)\s*/,r3=/^:\s*/,i3=/^((?:'(?:\\'|.)*?'|"(?:\\"|.)*?"|\([^)]*?\)|[^};])+)/,o3=/^[;\s]*/,l3=/^\s+|\s+$/g,a3=` -`,d1="/",p1="*",Pi="",s3="comment",u3="declaration",c3=function(e,t){if(typeof e!="string")throw new TypeError("First argument must be a string");if(!e)return[];t=t||{};var n=1,r=1;function i(h){var m=h.match(e3);m&&(n+=m.length);var E=h.lastIndexOf(a3);r=~E?h.length-E:r+h.length}function o(){var h={line:n,column:r};return function(m){return m.position=new l(h),u(),m}}function l(h){this.start=h,this.end={line:n,column:r},this.source=t.source}l.prototype.content=e;function a(h){var m=new Error(t.source+":"+n+":"+r+": "+h);if(m.reason=h,m.filename=t.source,m.line=n,m.column=r,m.source=e,!t.silent)throw m}function s(h){var m=h.exec(e);if(m){var E=m[0];return i(E),e=e.slice(E.length),m}}function u(){s(t3)}function c(h){var m;for(h=h||[];m=f();)m!==!1&&h.push(m);return h}function f(){var h=o();if(!(d1!=e.charAt(0)||p1!=e.charAt(1))){for(var m=2;Pi!=e.charAt(m)&&(p1!=e.charAt(m)||d1!=e.charAt(m+1));)++m;if(m+=2,Pi===e.charAt(m-1))return a("End of comment missing");var E=e.slice(2,m-2);return r+=2,i(E),e=e.slice(m),r+=2,h({type:s3,comment:E})}}function d(){var h=o(),m=s(n3);if(m){if(f(),!s(r3))return a("property missing ':'");var E=s(i3),g=h({type:u3,property:h1(m[0].replace(f1,Pi)),value:E?h1(E[0].replace(f1,Pi)):Pi});return s(o3),g}}function p(){var h=[];c(h);for(var m;m=d();)m!==!1&&(h.push(m),c(h));return h}return u(),p()};function h1(e){return e?e.replace(l3,Pi):Pi}var f3=c3;function jS(e,t){var n=null;if(!e||typeof e!="string")return n;for(var r,i=f3(e),o=typeof t=="function",l,a,s=0,u=i.length;s0?k.createElement(p,s,f):k.createElement(p,s)}function m3(e){let t=-1;for(;++t for more info)`),delete Ls[o]}const t=SO().use(_P).use(e.remarkPlugins||[]).use(yN,W(P({},e.remarkRehypeOptions),{allowDangerousHtml:!0})).use(e.rehypePlugins||[]).use($N,e),n=new iS;typeof e.children=="string"?n.value=e.children:e.children!==void 0&&e.children!==null&&console.warn(`[react-markdown] Warning: please pass a string as \`children\` (not: \`${e.children}\`)`);const r=t.runSync(t.parse(n),n);if(r.type!=="root")throw new TypeError("Expected a `root` node");let i=k.createElement(k.Fragment,{},WS({options:e,schema:DN,listDepth:0},r));return e.className&&(i=k.createElement("div",{className:e.className},i)),i}YS.propTypes={children:M.string,className:M.string,allowElement:M.func,allowedElements:M.arrayOf(M.string),disallowedElements:M.arrayOf(M.string),unwrapDisallowed:M.bool,remarkPlugins:M.arrayOf(M.oneOfType([M.object,M.func,M.arrayOf(M.oneOfType([M.bool,M.string,M.object,M.func,M.arrayOf(M.any)]))])),rehypePlugins:M.arrayOf(M.oneOfType([M.object,M.func,M.arrayOf(M.oneOfType([M.bool,M.string,M.object,M.func,M.arrayOf(M.any)]))])),sourcePos:M.bool,rawSourcePos:M.bool,skipHtml:M.bool,includeElementIndex:M.bool,transformLinkUri:M.oneOfType([M.func,M.bool]),linkTarget:M.oneOfType([M.func,M.string]),transformImageUri:M.func,components:M.object};const E3="_tooltip_base_w63jb_1",S3="_popoverMarkdown_w63jb_10",VS={tooltip_base:E3,popoverMarkdown:S3};function A3({initialOpen:e=!1,placement:t="top",open:n,onOpenChange:r}={}){const[i,o]=O.useState(e),l=O.useRef(null),a=n!=null?n:i,s=r!=null?r:o,u=O.useCallback(()=>{s(g=>!g)},[s]),c=lO({placement:t,open:a,onOpenChange:s,whileElementsMounted:sI,middleware:[rI(7),nI({fallbackAxisSideDirection:"start"}),cI({element:l}),oI({padding:5})]}),f=c.context,d=WI(f,{move:!1,enabled:n==null}),p=iO(f,{enabled:n==null}),h=rO(f),m=oO(f,{role:"tooltip"}),E=aO([d,p,h,m]);return O.useMemo(()=>P(P({open:a,setOpen:s,toggleOpen:u,arrowRef:l},E),c),[a,s,u,E,c])}const $S=O.createContext(null),HS=()=>{const e=O.useContext($S);if(e==null)throw new Error("Tooltip components must be wrapped in ");return e};function yf(n){var r=n,{children:e}=r,t=ye(r,["children"]);const i=A3(t);return v($S.Provider,{value:i,children:e})}const vf=O.forwardRef(function(o,i){var l=o,{children:t,asChild:n=!1}=l,r=ye(l,["children","asChild"]);const a=HS(),s=t.ref,u=nS([a.refs.setReference,i,s]),c=O.useMemo(()=>({onMouseDown:a.toggleOpen,onMouseUp:a.toggleOpen}),[a.toggleOpen]);return n&&O.isValidElement(t)?O.cloneElement(t,a.getReferenceProps(W(P(P(P({ref:u},r),t.props),c),{"data-state":a.open?"open":"closed"}))):v("button",W(P(P({ref:u,"data-state":a.open?"open":"closed"},a.getReferenceProps(r)),c),{children:t}))}),GS=O.forwardRef(function({content:t},n){return v(lg,{ref:n,children:v(YS,{className:VS.popoverMarkdown,children:t})})}),lg=O.forwardRef(function(i,r){var o=i,{children:t}=o,n=ye(o,["children"]);var s,u;const l=HS(),a=nS([l.refs.setFloating,r]);return v(KI,{children:l.open&&v(Me,{children:U("div",W(P({ref:a,className:VS.tooltip_base,style:P({position:l.strategy,zIndex:10,top:(s=l.y)!=null?s:0,left:(u=l.x)!=null?u:0,visibility:l.x==null?"hidden":"visible"},n.style)},l.getFloatingProps(n)),{children:[t,v(NI,{ref:l.arrowRef,context:l.context,fill:"var(--tooltip-bg-color, pink)"})]}))})})});function Je(...e){return e.filter(t=>t).join(" ")}const x3="_button_1y00r_1",C3="_regular_1y00r_26",_3="_icon_1y00r_34",k3="_transparent_1y00r_42",Td={button:x3,regular:C3,delete:"_delete_1y00r_30",icon:_3,transparent:k3},rt=k.forwardRef((o,i)=>{var l=o,{children:e,variant:t="regular",className:n}=l,r=ye(l,["children","variant","className"]);const a=t?Array.isArray(t)?t.map(s=>Td[s]).join(" "):Td[t]:"";return v("button",W(P({ref:i,className:Je(Td.button,a,n)},r),{children:e}))}),lu=i=>{var o=i,{placement:e="right",popoverContent:t,tooltipClass:n}=o,r=ye(o,["placement","popoverContent","tooltipClass"]);return U(yf,{placement:e,children:[v(vf,{asChild:!0,children:v(rt,P({},r))}),v(lg,{children:v("div",{className:n,children:t})})]})},T3="_container_valbi_1",I3="_header_valbi_14",O3="_information_valbi_19",P3="_error_msg_valbi_24",N3="_actions_valbi_32",kl={container:T3,header:I3,information:O3,error_msg:P3,actions:N3};function ag({header:e,error:t,generateIssueLink:n,resetErrorBoundary:r}){const i=Gm(),{goBackward:o,canGoBackward:l}=JT();return U("div",{className:kl.container,children:[v("h3",{className:kl.header,children:e}),v("p",{className:kl.information,children:"Error message:"}),v("code",{className:kl.error_msg,children:t.message}),U("div",{className:kl.actions,children:[v(lu,{placement:"top",popoverContent:"Try rendering again to see if it fixes the error",onClick:()=>r(),children:"Reset"}),l?v(lu,{variant:"regular",placement:"top",popoverContent:"Undo the last state change to see if that fixes issue",onClick:()=>{o(),r()},children:"Undo last change"}):null,v(lu,{role:"link",popoverContent:"Generate a bug report for github",variant:"regular",placement:"top",onClick:()=>{window.open(n(i.getState()),"_blank")},children:"Submit bug report"})]})]})}var JS=R3;function R3(e,t,n){var r=null,i=null,o=function(){r&&(clearTimeout(r),i=null,r=null)},l=function(){var s=i;o(),s&&s()},a=function(){if(!t)return e.apply(this,arguments);var s=this,u=arguments,c=n&&!r;if(o(),i=function(){e.apply(s,u)},r=setTimeout(function(){if(r=null,!c){var f=i;return i=null,f()}},t),c)return i()};return a.cancel=o,a.flush=l,a}const Zn={ui:"",libraries:""};function xe(){return function(o){var l=o,{name:t,library:n,category:r}=l,i=ye(l,["name","library","category"]);return P({uiName:n?`${n}::${t}`:t,name:t,library:n,category:r!=null?r:"Uncategorized"},i)}}const D3="_container_1og9v_1",L3={container:D3},F3=xe()({library:"TESTING",name:"error_node",title:"Error Throwing Node",takesChildren:!1,UiComponent:({uiArguments:e,path:t,wrapperProps:n})=>{const{showBoundary:r}=EE();return U("div",W(P({className:L3.container},n),{children:[v("h3",{children:"Error Node! I throw errors"}),v(rt,{variant:"delete","aria-label":"Throw an error",onClick:()=>{r(new Error(`Ui Node error: ${e.error_msg}`))},children:"💣"})]}))},settingsInfo:{error_msg:{label:"Message for error",inputType:"string",defaultValue:"Uh oh, an error!"}},settingsFormRender:({inputs:e,settings:t})=>{if(t.error_msg==="Trigger settings error")throw new Error(`Settings panel render error: +`,d1="/",p1="*",Pi="",s3="comment",u3="declaration",c3=function(e,t){if(typeof e!="string")throw new TypeError("First argument must be a string");if(!e)return[];t=t||{};var n=1,r=1;function i(h){var m=h.match(e3);m&&(n+=m.length);var E=h.lastIndexOf(a3);r=~E?h.length-E:r+h.length}function o(){var h={line:n,column:r};return function(m){return m.position=new l(h),u(),m}}function l(h){this.start=h,this.end={line:n,column:r},this.source=t.source}l.prototype.content=e;function a(h){var m=new Error(t.source+":"+n+":"+r+": "+h);if(m.reason=h,m.filename=t.source,m.line=n,m.column=r,m.source=e,!t.silent)throw m}function s(h){var m=h.exec(e);if(m){var E=m[0];return i(E),e=e.slice(E.length),m}}function u(){s(t3)}function c(h){var m;for(h=h||[];m=f();)m!==!1&&h.push(m);return h}function f(){var h=o();if(!(d1!=e.charAt(0)||p1!=e.charAt(1))){for(var m=2;Pi!=e.charAt(m)&&(p1!=e.charAt(m)||d1!=e.charAt(m+1));)++m;if(m+=2,Pi===e.charAt(m-1))return a("End of comment missing");var E=e.slice(2,m-2);return r+=2,i(E),e=e.slice(m),r+=2,h({type:s3,comment:E})}}function d(){var h=o(),m=s(n3);if(m){if(f(),!s(r3))return a("property missing ':'");var E=s(i3),g=h({type:u3,property:h1(m[0].replace(f1,Pi)),value:E?h1(E[0].replace(f1,Pi)):Pi});return s(o3),g}}function p(){var h=[];c(h);for(var m;m=d();)m!==!1&&(h.push(m),c(h));return h}return u(),p()};function h1(e){return e?e.replace(l3,Pi):Pi}var f3=c3;function jS(e,t){var n=null;if(!e||typeof e!="string")return n;for(var r,i=f3(e),o=typeof t=="function",l,a,s=0,u=i.length;s0?k.createElement(p,s,f):k.createElement(p,s)}function m3(e){let t=-1;for(;++t for more info)`),delete Ls[o]}const t=SO().use(_P).use(e.remarkPlugins||[]).use(yN,W(P({},e.remarkRehypeOptions),{allowDangerousHtml:!0})).use(e.rehypePlugins||[]).use($N,e),n=new iS;typeof e.children=="string"?n.value=e.children:e.children!==void 0&&e.children!==null&&console.warn(`[react-markdown] Warning: please pass a string as \`children\` (not: \`${e.children}\`)`);const r=t.runSync(t.parse(n),n);if(r.type!=="root")throw new TypeError("Expected a `root` node");let i=k.createElement(k.Fragment,{},WS({options:e,schema:DN,listDepth:0},r));return e.className&&(i=k.createElement("div",{className:e.className},i)),i}YS.propTypes={children:M.string,className:M.string,allowElement:M.func,allowedElements:M.arrayOf(M.string),disallowedElements:M.arrayOf(M.string),unwrapDisallowed:M.bool,remarkPlugins:M.arrayOf(M.oneOfType([M.object,M.func,M.arrayOf(M.oneOfType([M.bool,M.string,M.object,M.func,M.arrayOf(M.any)]))])),rehypePlugins:M.arrayOf(M.oneOfType([M.object,M.func,M.arrayOf(M.oneOfType([M.bool,M.string,M.object,M.func,M.arrayOf(M.any)]))])),sourcePos:M.bool,rawSourcePos:M.bool,skipHtml:M.bool,includeElementIndex:M.bool,transformLinkUri:M.oneOfType([M.func,M.bool]),linkTarget:M.oneOfType([M.func,M.string]),transformImageUri:M.func,components:M.object};const E3="_tooltip_base_w63jb_1",S3="_popoverMarkdown_w63jb_10",VS={tooltip_base:E3,popoverMarkdown:S3};function A3({initialOpen:e=!1,placement:t="top",open:n,onOpenChange:r}={}){const[i,o]=O.useState(e),l=O.useRef(null),a=n!=null?n:i,s=r!=null?r:o,u=O.useCallback(()=>{s(g=>!g)},[s]),c=lO({placement:t,open:a,onOpenChange:s,whileElementsMounted:sI,middleware:[rI(7),nI({fallbackAxisSideDirection:"start"}),cI({element:l}),oI({padding:5})]}),f=c.context,d=WI(f,{move:!1,enabled:n==null}),p=iO(f,{enabled:n==null}),h=rO(f),m=oO(f,{role:"tooltip"}),E=aO([d,p,h,m]);return O.useMemo(()=>P(P({open:a,setOpen:s,toggleOpen:u,arrowRef:l},E),c),[a,s,u,E,c])}const $S=O.createContext(null),HS=()=>{const e=O.useContext($S);if(e==null)throw new Error("Tooltip components must be wrapped in ");return e};function yf(n){var r=n,{children:e}=r,t=ye(r,["children"]);const i=A3(t);return v($S.Provider,{value:i,children:e})}const vf=O.forwardRef(function(o,i){var l=o,{children:t,asChild:n=!1}=l,r=ye(l,["children","asChild"]);const a=HS(),s=t.ref,u=nS([a.refs.setReference,i,s]),c=O.useMemo(()=>({onMouseDown:a.toggleOpen,onMouseUp:a.toggleOpen}),[a.toggleOpen]);return n&&O.isValidElement(t)?O.cloneElement(t,a.getReferenceProps(W(P(P(P({ref:u},r),t.props),c),{"data-state":a.open?"open":"closed"}))):v("button",W(P(P({ref:u,"data-state":a.open?"open":"closed"},a.getReferenceProps(r)),c),{children:t}))}),GS=O.forwardRef(function({content:t},n){return v(lg,{ref:n,children:v(YS,{className:VS.popoverMarkdown,children:t})})}),lg=O.forwardRef(function(i,r){var o=i,{children:t}=o,n=ye(o,["children"]);var s,u;const l=HS(),a=nS([l.refs.setFloating,r]);return v(KI,{children:l.open&&v(Me,{children:U("div",W(P({ref:a,className:VS.tooltip_base,style:P({position:l.strategy,zIndex:10,top:(s=l.y)!=null?s:0,left:(u=l.x)!=null?u:0,visibility:l.x==null?"hidden":"visible"},n.style)},l.getFloatingProps(n)),{children:[t,v(NI,{ref:l.arrowRef,context:l.context,fill:"var(--tooltip-bg-color, pink)"})]}))})})});function Je(...e){return e.filter(t=>t).join(" ")}const x3="_button_1y00r_1",C3="_regular_1y00r_26",_3="_icon_1y00r_34",k3="_transparent_1y00r_42",Td={button:x3,regular:C3,delete:"_delete_1y00r_30",icon:_3,transparent:k3},rt=k.forwardRef((o,i)=>{var l=o,{children:e,variant:t="regular",className:n}=l,r=ye(l,["children","variant","className"]);const a=t?Array.isArray(t)?t.map(s=>Td[s]).join(" "):Td[t]:"";return v("button",W(P({ref:i,className:Je(Td.button,a,n)},r),{children:e}))}),lu=i=>{var o=i,{placement:e="right",popoverContent:t,tooltipClass:n}=o,r=ye(o,["placement","popoverContent","tooltipClass"]);return U(yf,{placement:e,children:[v(vf,{asChild:!0,children:v(rt,P({},r))}),v(lg,{children:v("div",{className:n,children:t})})]})},T3="_container_valbi_1",I3="_header_valbi_14",O3="_information_valbi_19",P3="_error_msg_valbi_24",N3="_actions_valbi_32",kl={container:T3,header:I3,information:O3,error_msg:P3,actions:N3};function ag({header:e,error:t,generateIssueLink:n,resetErrorBoundary:r}){const i=Gm(),{goBackward:o,canGoBackward:l}=JT();return U("div",{className:kl.container,children:[v("h3",{className:kl.header,children:e}),v("p",{className:kl.information,children:"Error message:"}),v("code",{className:kl.error_msg,children:t.message}),U("div",{className:kl.actions,children:[v(lu,{placement:"top",popoverContent:"Try rendering again to see if it fixes the error",onClick:()=>r(),children:"Reset"}),l?v(lu,{variant:"regular",placement:"top",popoverContent:"Undo the last state change to see if that fixes issue",onClick:()=>{o(),setTimeout(r,5)},children:"Undo last change"}):null,v(lu,{role:"link",popoverContent:"Generate a bug report for github",variant:"regular",placement:"top",onClick:()=>{window.open(n(i.getState()),"_blank")},children:"Submit bug report"})]})]})}var JS=R3;function R3(e,t,n){var r=null,i=null,o=function(){r&&(clearTimeout(r),i=null,r=null)},l=function(){var s=i;o(),s&&s()},a=function(){if(!t)return e.apply(this,arguments);var s=this,u=arguments,c=n&&!r;if(o(),i=function(){e.apply(s,u)},r=setTimeout(function(){if(r=null,!c){var f=i;return i=null,f()}},t),c)return i()};return a.cancel=o,a.flush=l,a}const Zn={ui:"",libraries:""};function xe(){return function(o){var l=o,{name:t,library:n,category:r}=l,i=ye(l,["name","library","category"]);return P({uiName:n?`${n}::${t}`:t,name:t,library:n,category:r!=null?r:"Uncategorized"},i)}}const D3="_container_1og9v_1",L3={container:D3},F3=xe()({library:"TESTING",name:"error_node",title:"Error Throwing Node",takesChildren:!1,UiComponent:({uiArguments:e,path:t,wrapperProps:n})=>{const{showBoundary:r}=EE();return U("div",W(P({className:L3.container},n),{children:[v("h3",{children:"Error Node! I throw errors"}),v(rt,{variant:"delete","aria-label":"Throw an error",onClick:()=>{r(new Error(`Ui Node error: ${e.error_msg}`))},children:"💣"})]}))},settingsInfo:{error_msg:{label:"Message for error",inputType:"string",defaultValue:"Uh oh, an error!"}},settingsFormRender:({inputs:e,settings:t})=>{if(t.error_msg==="Trigger settings error")throw new Error(`Settings panel render error: ${t.error_msg}`);return U(Me,{children:[v("p",{children:'Set the error message to "Trigger settings error" to cause the settings panel to throw an error'}),e.error_msg]})},category:"TESTING",description:"Node that throws an error when a button is clicked in editor or settings panel"}),M3="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAJYAAACWCAYAAAA8AXHiAAAACXBIWXMAABYlAAAWJQFJUiTwAAAEO0lEQVR4nO3dsYqcVRiH8WeNrkXMDRgLixRWRjSiXoMWG0iUXIGNsii4wRsQTApD0EIvQBCJ2RD0GqIoRjthC4vsHaRxRcbi7MDk28kMgv+c92SfH2zxfbPFmZcnZ06+LWZjNpsh/d+e6L0APZ4MSxGGpQjDUoRhKcKwFGFYijAsRRiWIgxLEYalCMNShGEpwrAUYViKMCxFGJYiDEsRhqUIw1KEYSnCsBRhWIowLEUYliIMSxGGpQjDUoRhKcKwFGFYijAsRRiWIgxLEYalCMNShGEpwrAUYViKMCxFGJYiDEsRhqWIJ3svYJ2db/aW3d4Etg5/3gCePby3Mfm96RcFjfj6feAe8CtwE7gFHEx+jyvvnJne6qp8WEucB64AtSaZ8wzwwuHPJWAPuAx813NR64z0UXgC+JQ20OMS1TJngBu0WZzovJaHGmnH+gTY6b2IQuazuNx1FQ8xyo51gaNRHQDXaWesUxw9n3B4b/FnxNdP0d7jdY6erXZosylnhLA2gc8m9/aB14Bt4A7tgPu4uk97j9u097w/ef0abUaljBDWReC5hesD4C3gbpfV9HUXeBP4a+HeaeDtLqtZYYSwtibXX3I8o5r7Dfhqcm+rwzpWGiGsVyfXX3dZRS3TGZzrsooVRvhf4fOT63LniQ7usPywX8YIO9bUkafOqmfEsDQAw1LECGesdX+oPa5Kz8UdSxGGpQjDUsQIZ6xSZ4dCSs/FHUsRhqUIw1LECGes0s9rOio9F3csRRiWIgxLESOcsUqdHQopPRd3LEUYliIMSxEjnLFKP6/pqPRc3LEUYViKMCxFjHDGKnV2KKT0XNyxFGFYijAsRYxwxir9vKaj0nNxx1KEYSnCsBQxwhmr1NmhkNJzccdShGEpwrAUMcIZq/Tzmo5Kz8UdSxGGpQjDUsQIZ6xSZ4dCSs/FHUsRhqWIEcPyK08GMEJYf9Ce2cx/Xu67nBJe58GZ/Nl1NUuMENbvk+tLXVZRy3QGP3dZxQojhLU7uX4XONthHVW8SJvBot0O61hphLC+Be4tXD8NfA+81GU1fZ0FfqDNYG6fNqNSRgjrAPhwcu808CPt+5DPAScf8ZoepZO093gN+In23hd9wINf5VvCCA9Iof2LvAp8tHBvk/YF3NsL96YPDdf9oXa016euUnC3gjF2rLmPgc97L6KQL2gzKWmksP4B3gcuAHud19LTHnAReI82k5JG+ShcdAO4TRvueeAV2rnjqZ6LCvqbdkD/BbhJ++gr//XFG7PZuo9x6b8b6aNQAzEsRRiWIgxLEYalCMNShGEpwrAUYViKMCxFGJYiDEsRhqUIw1KEYSnCsBRhWIowLEUYliIMSxGGpQjDUoRhKcKwFGFYijAsRRiWIgxLEYalCMNShGEpwrAUYViKMCxFGJYiDEsRhqUIw1KEYSnCsBRhWIowLEUYliL+BXaHdHGUC5uqAAAAAElFTkSuQmCC";function B3({text:e,position:t="down",size:n,children:r}){return v("span",{"aria-label":e,"data-balloon-pos":t,"data-balloon-length":n,children:r})}function Ir(l){var a=l,{text:e,position:t="down",size:n,children:r,variant:i="icon"}=a,o=ye(a,["text","position","size","children","variant"]);return v(rt,W(P({"aria-label":e,"data-balloon-pos":t,"data-balloon-length":n,variant:i},o),{children:r}))}const QS=({children:e,el:t="div"})=>{const[n]=O.useState(document.createElement(t));return O.useEffect(()=>(document.body.appendChild(n),()=>{document.body.removeChild(n)}),[n]),br.createPortal(e,n)},KS=236,U3=174,z3=31,qS={"--elements-palette-width":`${U3}px`,"--header-height":`${z3}px`,"--properties-panel-width":`${KS}px`},j3="_container_1w66f_1",W3="_full_screen_mode_1w66f_13",Y3="_full_screen_button_container_1w66f_25",V3="_card_holder_1w66f_69",$3="_card_contents_holder_1w66f_76",au={container:j3,full_screen_mode:W3,full_screen_button_container:Y3,card_holder:V3,card_contents_holder:$3},XS=k.forwardRef((o,i)=>{var l=o,{children:e,style:t,card_args:{full_screen:n=!1}}=l,r=ye(l,["children","style","card_args"]);const[a,s]=k.useState(!1),u=()=>s(f=>!f),c=U("div",{style:qS,className:Je("card",au.container,a?au.full_screen_mode:null),children:[n?v(H3,{isFullScreen:a,onClick:u}):null,e]});return a?v(QS,{children:c}):v("div",W(P({ref:i,style:t,className:au.card_holder},r),{children:c}))});function H3({isFullScreen:e,onClick:t}){return v("div",{className:au.full_screen_button_container,"data-is-full-screen":e,children:v(Ir,{text:e?"Reset full screen":"Expand to full screen",position:"left",onClick:n=>{n.preventDefault(),t()},variant:e?"regular":"icon",children:e?"Close":v("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 16 16","aria-hidden":"true",role:"img",children:v("path",{"fill-rule":"evenodd",d:"M5.828 10.172a.5.5 0 0 0-.707 0l-4.096 4.096V11.5a.5.5 0 0 0-1 0v3.975a.5.5 0 0 0 .5.5H4.5a.5.5 0 0 0 0-1H1.732l4.096-4.096a.5.5 0 0 0 0-.707zm4.344 0a.5.5 0 0 1 .707 0l4.096 4.096V11.5a.5.5 0 1 1 1 0v3.975a.5.5 0 0 1-.5.5H11.5a.5.5 0 0 1 0-1h2.768l-4.096-4.096a.5.5 0 0 1 0-.707zm0-4.344a.5.5 0 0 0 .707 0l4.096-4.096V4.5a.5.5 0 1 0 1 0V.525a.5.5 0 0 0-.5-.5H11.5a.5.5 0 0 0 0 1h2.768l-4.096 4.096a.5.5 0 0 0 0 .707zm-4.344 0a.5.5 0 0 1-.707 0L1.025 1.732V4.5a.5.5 0 0 1-1 0V.525a.5.5 0 0 1 .5-.5H4.5a.5.5 0 0 1 0 1H1.732l4.096 4.096a.5.5 0 0 1 0 .707z"})})})})}function g1(...e){let t=0;for(const n of e)n&&(t+=1);return t}function ph(...e){return e.filter(n=>n!==void 0).reduce((n,r,i)=>(i===0?"":n+` `)+r,"")}function G3(e,t){const n=" ".repeat(t);return e.replaceAll(/\n/g,` ${n}`)}function ZS(e,t="multi"){const n=t==="single"?"`":"\n```\n";return`${n}${e}${n}`}function J3(e){return ZS(e,"single")}function Id(e){return ZS(e,"multi")}function sg(e){var t,n;switch(e.app_info.mode){case"MAIN":{const r=JSON.stringify(e.app_info.ui_tree,null,2),i=(n=((t=e.selected_path)!=null?t:[]).join(" > "))!=null?n:"null";return ph(`## Ui-Tree at error: diff --git a/inst/editor/build/index.html b/inst/editor/build/index.html index f0cc1bca0..c13090a7a 100644 --- a/inst/editor/build/index.html +++ b/inst/editor/build/index.html @@ -26,7 +26,7 @@ Learn how to configure a non-root public URL by running `npm run build`. --> Shiny UI Editor - + diff --git a/inst/editor/playwright/error-boundaries.spec.ts b/inst/editor/playwright/error-boundaries.spec.ts new file mode 100644 index 000000000..7994a128e --- /dev/null +++ b/inst/editor/playwright/error-boundaries.spec.ts @@ -0,0 +1,96 @@ +import { expect, test } from "@playwright/test"; + +import { errorTestingTree } from "../src/state/sample_ui_trees/errorTesting"; + +import { mockBackendState } from "./utils/mockBackend"; + +test("Errors are caught and not allowed to propigate up beyond their local position in app", async ({ + page, +}) => { + // Mock the window.open() api so we can check to see if the bug report link + // generated proper url + const linksOpened: string[] = []; + let logLinkOpening: (msg: string) => void; + await page.exposeFunction("logLinkOpening", (msg: string) => + linksOpened.push(msg) + ); + await page.addInitScript(() => { + // Override the method to always return mock battery info. + window.open = ( + url?: string | URL | undefined, + target?: string | undefined, + features?: string | undefined + ) => { + logLinkOpening(url as string); + return null; + }; + }); + + await mockBackendState(page, errorTestingTree); + + await page.goto("/"); + + const non_errored_node_selector = page.getByRole("heading", { + name: "Error Node! I throw errors", + }); + const undo_state_change_button_selector = page.getByText("Undo last change"); + + const node_error_trigger = page.getByRole("button", { + name: "Throw an error", + }); + + // Page should load without error + await expect(non_errored_node_selector).toBeVisible(); + + // Trigger error + await node_error_trigger.click(); + + // Error message should be visible now + await expect( + page.getByRole("heading", { + name: "Something went wrong rendering TESTING::error_node()", + }) + ).toBeVisible(); + + // The original node should now be gone + await expect(non_errored_node_selector).not.toBeVisible(); + + // Since this is a fresh app with no changes the undo button should not be shown + await expect(undo_state_change_button_selector).not.toBeVisible(); + + // Can try rerendering the app with reset button. + await page.getByRole("button", { name: "Reset" }).click(); + + // This will work here and the error message should be gone + await expect(non_errored_node_selector).toBeVisible(); + + // Now we will trigger an error in the settings panel + // First we need to select the node to make sure it's settings panel is visible + await non_errored_node_selector.click(); + await page.getByRole("textbox", { name: "Message for error:" }).click(); + await page + .getByRole("textbox", { name: "Message for error:" }) + .fill("Trigger settings error"); + + const settings_panel_error_header = page.getByRole("heading", { + name: "Error rendering settings panel", + }); + + // Error message should be visible now in the settings panel + await expect(settings_panel_error_header).toBeVisible(); + + // Because we've made a state change we can try undoing that state change which should fix the error + await undo_state_change_button_selector.click(); + await expect(settings_panel_error_header).not.toBeVisible(); + + // Now we can trigger another error in the main editor to test out the generation of an automatic bug report + await node_error_trigger.click(); + + // Click bug report button + await page.getByRole("link", { name: "Submit bug report" }).click(); + + // Check to make sure our mocked function was called with a new issue url + expect(linksOpened[0]).toMatch( + /https:\/\/github.com\/rstudio\/shinyuieditor\/issues\/new/ + ); +}); diff --git a/inst/editor/src/components/ErrorCatcher/GeneralErrorView.tsx b/inst/editor/src/components/ErrorCatcher/GeneralErrorView.tsx index 9e249b5af..ead0cfbdf 100644 --- a/inst/editor/src/components/ErrorCatcher/GeneralErrorView.tsx +++ b/inst/editor/src/components/ErrorCatcher/GeneralErrorView.tsx @@ -43,7 +43,10 @@ export function GeneralErrorView({ popoverContent="Undo the last state change to see if that fixes issue" onClick={() => { goBackward(); - resetErrorBoundary(); + // Call resetErrorBoundary after a 10ms delay + // This is to give the state time to update + // before the error boundary is reset + setTimeout(resetErrorBoundary, 5); }} > Undo last change