@flowriki/agent 0.1.0-beta.20 → 0.1.0-beta.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/accname.global.js +1 -1
- package/dist/{chunk-ZVX26LV7.js → chunk-U3QYIAXF.js} +414 -36
- package/dist/cli.js +1 -1
- package/dist/engine.js +1 -1
- package/package.json +1 -1
package/dist/accname.global.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";(()=>{function U(e,t,i){function u(r){let o=e.__flAccName;if(o){let a=o(r);return a||t(r.placeholder)||t(r.title)||""}return b(r)}function b(r){let o=r.getAttribute&&r.getAttribute("aria-labelledby");if(o){let d=o.split(/\s+/).map(g=>document.getElementById(g)).filter(Boolean).map(g=>t(g.textContent)).filter(Boolean).join(" ");if(d)return d}let a=t(r.getAttribute&&r.getAttribute("aria-label"));if(a)return a;if(r.id){let d=document.querySelector('label[for="'+CSS.escape(r.id)+'"]');if(d&&t(d.textContent))return t(d.textContent)}let l=r.closest&&r.closest("label");if(l&&t(l.textContent))return t(l.textContent);let s=r.tagName.toLowerCase(),p=(r.type||"").toLowerCase();return(["button","a","summary"].includes(s)||r.getAttribute&&r.getAttribute("role")==="button")&&t(r.textContent)?t(r.textContent):s==="input"&&["submit","button","reset"].includes(p)&&r.value?t(r.value):t(r.placeholder)||t(r.title)||""}let S="input,select,textarea,button,meter,output,progress,[contenteditable]";function y(r){let o=r.parentElement;if(!o||!r.matches?.(S))return"";for(let a of Array.from(o.querySelectorAll("label"))){let l=a.getAttribute("for");if(l&&document.getElementById(l))continue;let s=t(a.textContent);if(s)return s}return""}let E=new WeakMap,P=crypto.randomUUID(),_=0;function T(r){let o=E.get(r);o||(o=`${P}:${++_}`,E.set(r,o));let a={__targetId:o},l=r.getRootNode(),s=r.getAttribute("data-testid");if(s&&l.querySelectorAll(`[data-testid=${JSON.stringify(s)}]`).length===1)return{...a,name:u(r),locator:{strategy:"testId",value:s}};if(r.type==="password"&&u(r))return{...a,name:u(r),locator:{strategy:"label",value:u(r)}};let p=r.getAttribute("title");if(p&&l.querySelectorAll(`[title=${JSON.stringify(p)}]`).length===1)return{...a,name:u(r),locator:{strategy:"title",value:p}};let d=u(r);if(d)return{...a,name:d};let g=y(r);if(g)return{...a,name:g,nameFrom:"adjacent-label"};let v=t(Array.from(r.childNodes||[]).filter(A=>A.nodeType===3).map(A=>A.textContent).join(" "));return v&&v.length<=80?{...a,name:v,nameFrom:"text"}:r.id&&!/:r|[a-f0-9]{12}|\d{6}/i.test(r.id)&&l.querySelectorAll(`#${CSS.escape(r.id)}`).length===1?{...a,name:"",locator:{strategy:"css",value:`#${CSS.escape(r.id)}`}}:{...a,name:""}}function N(r){let o=r.getAttribute&&r.getAttribute("role");if(o)return o;let a=r.tagName.toLowerCase(),l=(r.type||"").toLowerCase();return a==="button"?"button":a==="a"&&r.href?"link":a==="select"?r.multiple||r.size>1?"listbox":"combobox":a==="textarea"?"textbox":a==="input"?["submit","button","reset","file"].includes(l)?"button":l==="checkbox"||l==="radio"?l:l==="number"?"spinbutton":l==="search"?"searchbox":l==="range"?"slider":"textbox":r.isContentEditable?"textbox":H[a]??""}let H={li:"listitem",ul:"list",ol:"list",tr:"row",td:"cell",th:"columnheader",table:"table",nav:"navigation",main:"main",header:"banner",footer:"contentinfo",aside:"complementary",form:"form",dialog:"dialog",details:"group",fieldset:"group",img:"img",h1:"heading",h2:"heading",h3:"heading",h4:"heading",h5:"heading",h6:"heading"};function j(r){let o=r,a=null;for(let l=0;o&&l<6&&!(o===document.body||o===document.documentElement);l++){let s="";try{s=getComputedStyle(o).cursor}catch{break}if(s==="pointer")a=o;else if(a)break;o=o.parentElement}return a}let C="tr, [role=row], li, [role=listitem], [role=article], [role=gridcell]";function L(r){try{let o=r.cloneNode(!0);for(let a of Array.from(o.querySelectorAll(i+", input, select, textarea")))a.remove();for(let a of Array.from(o.querySelectorAll("td, th, li, p, div, span, tr")))a.append(" ");return t(o.textContent).slice(0,120)}catch{return""}}function D(r,o,a){if(!a)return!1;let l;try{l=Array.from(document.querySelectorAll(i+", input, select, textarea"))}catch{return!1}if(l.length>400)return!0;let s=0;for(let p of l)if(p!==r&&N(p)===o&&T(p).name===a&&++s>0)return!0;return!1}function V(r,o,a){if(!D(r,o,a))return null;let l=r.closest&&r.closest(C);if(!l)return null;let s=L(l);if(!s)return null;let p=l.getAttribute&&l.getAttribute("role"),d=l.tagName.toLowerCase();return{scope:s,scopeRole:p||(d==="tr"?"row":d==="li"?"listitem":d)}}function x(r,o,a){let l=V(r,o,a);return l||{scope:n(r)}}function n(r){let o=r.closest&&r.closest("form, fieldset, section, [role=group], [role=region], [role=dialog], [role=alertdialog], dialog");return o?u(o):""}function f(r){if(r.id)return"#"+CSS.escape(r.id);let o=[],a=r;for(;a&&a.nodeType===1&&a!==document.body;){let l=Array.from(a.parentNode?a.parentNode.children:[]).filter(s=>s.tagName===a.tagName);o.unshift(l.length>1?`${a.tagName.toLowerCase()}:nth-of-type(${l.indexOf(a)+1})`:a.tagName.toLowerCase()),a=a.parentElement}return"body > "+o.join(" > ")}return{accName:u,identify:T,roleOf:N,pointerTarget:j,scoping:x,cssPath:f}}var ge=Object.prototype.toString;function Q(e){return typeof e=="function"||ge.call(e)==="[object Function]"}function ye(e){var t=Number(e);return isNaN(t)?0:t===0||!isFinite(t)?t:(t>0?1:-1)*Math.floor(Math.abs(t))}var he=Math.pow(2,53)-1;function ve(e){var t=ye(e);return Math.min(Math.max(t,0),he)}function h(e,t){var i=Array,u=Object(e);if(e==null)throw new TypeError("Array.from requires an array-like object - not null or undefined");if(typeof t<"u"&&!Q(t))throw new TypeError("Array.from: when provided, the second argument must be a function");for(var b=ve(u.length),S=Q(i)?Object(new i(b)):new Array(b),y=0,E;y<b;)E=u[y],t?S[y]=t(E,y):S[y]=E,y+=1;return S.length=b,S}function I(e){"@babel/helpers - typeof";return I=typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?function(t){return typeof t}:function(t){return t&&typeof Symbol=="function"&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},I(e)}function we(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function X(e,t){for(var i=0;i<t.length;i++){var u=t[i];u.enumerable=u.enumerable||!1,u.configurable=!0,"value"in u&&(u.writable=!0),Object.defineProperty(e,Y(u.key),u)}}function Se(e,t,i){return t&&X(e.prototype,t),i&&X(e,i),Object.defineProperty(e,"prototype",{writable:!1}),e}function Ee(e,t,i){return t=Y(t),t in e?Object.defineProperty(e,t,{value:i,enumerable:!0,configurable:!0,writable:!0}):e[t]=i,e}function Y(e){var t=xe(e,"string");return I(t)=="symbol"?t:t+""}function xe(e,t){if(I(e)!="object"||!e)return e;var i=e[Symbol.toPrimitive];if(i!==void 0){var u=i.call(e,t||"default");if(I(u)!="object")return u;throw new TypeError("@@toPrimitive must return a primitive value.")}return(t==="string"?String:Number)(e)}var Ae=(function(){function e(){var t=arguments.length>0&&arguments[0]!==void 0?arguments[0]:[];we(this,e),Ee(this,"items",void 0),this.items=t}return Se(e,[{key:"add",value:function(i){return this.has(i)===!1&&this.items.push(i),this}},{key:"clear",value:function(){this.items=[]}},{key:"delete",value:function(i){var u=this.items.length;return this.items=this.items.filter(function(b){return b!==i}),u!==this.items.length}},{key:"forEach",value:function(i){var u=this;this.items.forEach(function(b){i(b,b,u)})}},{key:"has",value:function(i){return this.items.indexOf(i)!==-1}},{key:"size",get:function(){return this.items.length}}])})(),Z=typeof Set>"u"?Set:Ae;function m(e){var t;return(t=e.localName)!==null&&t!==void 0?t:e.tagName.toLowerCase()}var Le={article:"article",aside:"complementary",button:"button",datalist:"listbox",dd:"definition",details:"group",dialog:"dialog",dt:"term",fieldset:"group",figure:"figure",form:"form",footer:"contentinfo",h1:"heading",h2:"heading",h3:"heading",h4:"heading",h5:"heading",h6:"heading",header:"banner",hr:"separator",html:"document",legend:"legend",li:"listitem",math:"math",main:"main",menu:"list",nav:"navigation",ol:"list",optgroup:"group",option:"option",output:"status",progress:"progressbar",section:"region",summary:"button",table:"table",tbody:"rowgroup",textarea:"textbox",tfoot:"rowgroup",td:"cell",th:"columnheader",thead:"rowgroup",tr:"row",ul:"list"},Te={caption:new Set(["aria-label","aria-labelledby"]),code:new Set(["aria-label","aria-labelledby"]),deletion:new Set(["aria-label","aria-labelledby"]),emphasis:new Set(["aria-label","aria-labelledby"]),generic:new Set(["aria-label","aria-labelledby","aria-roledescription"]),insertion:new Set(["aria-label","aria-labelledby"]),none:new Set(["aria-label","aria-labelledby"]),paragraph:new Set(["aria-label","aria-labelledby"]),presentation:new Set(["aria-label","aria-labelledby"]),strong:new Set(["aria-label","aria-labelledby"]),subscript:new Set(["aria-label","aria-labelledby"]),superscript:new Set(["aria-label","aria-labelledby"])};function Ne(e,t){return["aria-atomic","aria-busy","aria-controls","aria-current","aria-description","aria-describedby","aria-details","aria-dropeffect","aria-flowto","aria-grabbed","aria-hidden","aria-keyshortcuts","aria-label","aria-labelledby","aria-live","aria-owns","aria-relevant","aria-roledescription"].some(function(i){var u;return e.hasAttribute(i)&&!((u=Te[t])!==null&&u!==void 0&&u.has(i))})}function ee(e,t){return Ne(e,t)}function k(e){var t=Ie(e);if(t===null||O.indexOf(t)!==-1){var i=Ce(e);if(O.indexOf(t||"")===-1||ee(e,i||""))return i}return t}function Ce(e){var t=Le[m(e)];if(t!==void 0)return t;switch(m(e)){case"a":case"area":case"link":if(e.hasAttribute("href"))return"link";break;case"img":return e.getAttribute("alt")===""&&!ee(e,"img")?"presentation":"img";case"input":{var i=e,u=i.type;switch(u){case"button":case"image":case"reset":case"submit":return"button";case"checkbox":case"radio":return u;case"range":return"slider";case"email":case"tel":case"text":case"url":return e.hasAttribute("list")?"combobox":"textbox";case"search":return e.hasAttribute("list")?"combobox":"searchbox";case"number":return"spinbutton";default:return null}}case"select":return e.hasAttribute("multiple")||e.size>1?"listbox":"combobox"}return null}function Ie(e){var t=e.getAttribute("role");if(t!==null){var i=t.trim().split(" ")[0];if(i.length>0)return i}return null}var O=["presentation","none"];function c(e){return e!==null&&e.nodeType===e.ELEMENT_NODE}function G(e){return c(e)&&m(e)==="caption"}function R(e){return c(e)&&m(e)==="input"}function te(e){return c(e)&&m(e)==="optgroup"}function re(e){return c(e)&&m(e)==="select"}function ne(e){return c(e)&&m(e)==="table"}function ie(e){return c(e)&&m(e)==="textarea"}function ae(e){var t=e.ownerDocument===null?e:e.ownerDocument,i=t.defaultView;if(i===null)throw new TypeError("no window available");return i}function oe(e){return c(e)&&m(e)==="fieldset"}function ue(e){return c(e)&&m(e)==="legend"}function le(e){return c(e)&&m(e)==="slot"}function Oe(e){return c(e)&&e.ownerSVGElement!==void 0}function se(e){return c(e)&&m(e)==="svg"}function fe(e){return Oe(e)&&m(e)==="title"}function M(e,t){if(c(e)&&e.hasAttribute(t)){var i=e.getAttribute(t).split(" "),u=e.getRootNode?e.getRootNode():e.ownerDocument;return i.map(function(b){return u.getElementById(b)}).filter(function(b){return b!==null})}return[]}function w(e,t){return c(e)?t.indexOf(k(e))!==-1:!1}function Re(e){return e.trim().replace(/\s\s+/g," ")}function Me(e,t){if(!c(e))return!1;if(e.hasAttribute("hidden")||e.getAttribute("aria-hidden")==="true")return!0;var i=t(e);return i.getPropertyValue("display")==="none"||i.getPropertyValue("visibility")==="hidden"}function Pe(e){return w(e,["button","combobox","listbox","textbox"])||me(e,"range")}function me(e,t){if(!c(e))return!1;if(t==="range")return w(e,["meter","progressbar","scrollbar","slider","spinbutton"]);throw new TypeError("No knowledge about abstract role '".concat(t,"'. This is likely a bug :("))}function ce(e,t){var i=h(e.querySelectorAll(t));return M(e,"aria-owns").forEach(function(u){i.push.apply(i,h(u.querySelectorAll(t)))}),i}function _e(e){return re(e)?e.selectedOptions||ce(e,"[selected]"):ce(e,'[aria-selected="true"]')}function je(e){return w(e,O)}function ke(e){return G(e)}function He(e){return w(e,["button","cell","checkbox","columnheader","gridcell","heading","label","legend","link","menuitem","menuitemcheckbox","menuitemradio","option","radio","row","rowheader","switch","tab","tooltip","treeitem"])}function De(e){return!1}function Ve(e){return R(e)||ie(e)?e.value:e.textContent||""}function de(e){var t=e.getPropertyValue("content");return/^["'].*["']$/.test(t)?t.slice(1,-1):""}function pe(e){var t=m(e);return t==="button"||t==="input"&&e.getAttribute("type")!=="hidden"||t==="meter"||t==="output"||t==="progress"||t==="select"||t==="textarea"}function be(e){if(pe(e))return e;var t=null;return e.childNodes.forEach(function(i){if(t===null&&c(i)){var u=be(i);u!==null&&(t=u)}}),t}function $e(e){if(e.control!==void 0)return e.control;var t=e.getAttribute("for");return t!==null?e.ownerDocument.getElementById(t):be(e)}function qe(e){var t=e.labels;if(t===null)return t;if(t!==void 0)return h(t);if(!pe(e))return null;var i=e.ownerDocument;return h(i.querySelectorAll("label")).filter(function(u){return $e(u)===e})}function Ge(e){var t=e.assignedNodes();return t.length===0?h(e.childNodes):t}function B(e){var t=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{},i=new Z,u=typeof Map>"u"?void 0:new Map,b=ae(e),S=t.compute,y=S===void 0?"name":S,E=t.computedStyleSupportsPseudoElements,P=E===void 0?t.getComputedStyle!==void 0:E,_=t.getComputedStyle,T=_===void 0?b.getComputedStyle.bind(b):_,N=t.hidden,H=N===void 0?!1:N,j=function(f,r){if(r!==void 0)throw new Error("use uncachedGetComputedStyle directly for pseudo elements");if(u===void 0)return T(f);var o=u.get(f);if(o)return o;var a=T(f,r);return u.set(f,a),a};function C(n,f){var r="";if(c(n)&&P){var o=T(n,"::before"),a=de(o);r="".concat(a," ").concat(r)}var l=le(n)?Ge(n):h(n.childNodes).concat(M(n,"aria-owns"));if(l.forEach(function(d){var g=x(d,{isEmbeddedInLabel:f.isEmbeddedInLabel,isReferenced:!1,recursion:!0}),v=c(d)?j(d).getPropertyValue("display"):"inline",A=v!=="inline"?" ":"";r+="".concat(A).concat(g).concat(A)}),c(n)&&P){var s=T(n,"::after"),p=de(s);r="".concat(r," ").concat(p)}return r.trim()}function L(n,f){var r=n.getAttributeNode(f);return r!==null&&!i.has(r)&&r.value.trim()!==""?(i.add(r),r.value):null}function D(n){return c(n)?L(n,"title"):null}function V(n){if(!c(n))return null;if(oe(n)){i.add(n);for(var f=h(n.childNodes),r=0;r<f.length;r+=1){var o=f[r];if(ue(o))return x(o,{isEmbeddedInLabel:!1,isReferenced:!1,recursion:!1})}}else if(ne(n)){i.add(n);for(var a=h(n.childNodes),l=0;l<a.length;l+=1){var s=a[l];if(G(s))return x(s,{isEmbeddedInLabel:!1,isReferenced:!1,recursion:!1})}}else if(se(n)){i.add(n);for(var p=h(n.childNodes),d=0;d<p.length;d+=1){var g=p[d];if(fe(g))return g.textContent}return null}else if(m(n)==="img"||m(n)==="area"){var v=L(n,"alt");if(v!==null)return v}else if(te(n)){var A=L(n,"label");if(A!==null)return A}if(R(n)&&(n.type==="button"||n.type==="submit"||n.type==="reset")){var z=L(n,"value");if(z!==null)return z;if(n.type==="submit")return"Submit";if(n.type==="reset")return"Reset"}var $=qe(n);if($!==null&&$.length!==0)return i.add(n),h($).map(function(q){return x(q,{isEmbeddedInLabel:!0,isReferenced:!1,recursion:!0})}).filter(function(q){return q.length>0}).join(" ");if(R(n)&&n.type==="image"){var F=L(n,"alt");if(F!==null)return F;var J=L(n,"title");return J!==null?J:"Submit Query"}if(w(n,["button"])){var K=C(n,{isEmbeddedInLabel:!1,isReferenced:!1});if(K!=="")return K}return null}function x(n,f){if(i.has(n))return"";if(!H&&Me(n,j)&&!f.isReferenced)return i.add(n),"";var r=c(n)?n.getAttributeNode("aria-labelledby"):null,o=r!==null&&!i.has(r)?M(n,"aria-labelledby"):[];if(y==="name"&&!f.isReferenced&&o.length>0)return i.add(r),o.map(function(v){return x(v,{isEmbeddedInLabel:f.isEmbeddedInLabel,isReferenced:!0,recursion:!1})}).join(" ");var a=f.recursion&&Pe(n)&&y==="name";if(!a){var l=(c(n)&&n.getAttribute("aria-label")||"").trim();if(l!==""&&y==="name")return i.add(n),l;if(!je(n)){var s=V(n);if(s!==null)return i.add(n),s}}if(w(n,["menu"]))return i.add(n),"";if(a||f.isEmbeddedInLabel||f.isReferenced){if(w(n,["combobox","listbox"])){i.add(n);var p=_e(n);return p.length===0?R(n)?n.value:"":h(p).map(function(v){return x(v,{isEmbeddedInLabel:f.isEmbeddedInLabel,isReferenced:!1,recursion:!0})}).join(" ")}if(me(n,"range"))return i.add(n),n.hasAttribute("aria-valuetext")?n.getAttribute("aria-valuetext"):n.hasAttribute("aria-valuenow")?n.getAttribute("aria-valuenow"):n.getAttribute("value")||"";if(w(n,["textbox"]))return i.add(n),Ve(n)}if(He(n)||c(n)&&f.isReferenced||ke(n)||De(n)){var d=C(n,{isEmbeddedInLabel:f.isEmbeddedInLabel,isReferenced:!1});if(d!=="")return i.add(n),d}if(n.nodeType===n.TEXT_NODE)return i.add(n),n.textContent||"";if(f.recursion)return i.add(n),C(n,{isEmbeddedInLabel:f.isEmbeddedInLabel,isReferenced:!1});var g=D(n);return g!==null?(i.add(n),g):(i.add(n),"")}return Re(x(e,{isEmbeddedInLabel:!1,isReferenced:y==="description",recursion:!1}))}function Be(e){return w(e,["caption","code","deletion","emphasis","generic","insertion","none","paragraph","presentation","strong","subscript","superscript"])}function W(e){var t=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{};return Be(e)?"":B(e,t)}window.__flAccName=e=>{try{return W(e).replace(/\s+/g," ").trim()}catch{return""}};window.__flCreateTargetIdentity=U;})();
|
|
1
|
+
"use strict";(()=>{function U(e,t,i){function l(n){let u=e.__flAccName;if(u){let a=u(n);return a||t(n.placeholder)||t(n.title)||""}return y(n)}function y(n){let u=n.getAttribute&&n.getAttribute("aria-labelledby");if(u){let m=u.split(/\s+/).map(b=>document.getElementById(b)).filter(Boolean).map(b=>t(b.textContent)).filter(Boolean).join(" ");if(m)return m}let a=t(n.getAttribute&&n.getAttribute("aria-label"));if(a)return a;if(n.id){let m=document.querySelector('label[for="'+CSS.escape(n.id)+'"]');if(m&&t(m.textContent))return t(m.textContent)}let o=n.closest&&n.closest("label");if(o&&t(o.textContent))return t(o.textContent);let s=n.tagName.toLowerCase(),p=(n.type||"").toLowerCase();return(["button","a","summary"].includes(s)||n.getAttribute&&n.getAttribute("role")==="button")&&t(n.textContent)?t(n.textContent):s==="input"&&["submit","button","reset"].includes(p)&&n.value?t(n.value):t(n.placeholder)||t(n.title)||""}let E="input,select,textarea,button,meter,output,progress,[contenteditable]";function v(n){let u=n.parentElement;if(!u||!n.matches?.(E))return"";for(let a of Array.from(u.querySelectorAll("label"))){let o=a.getAttribute("for");if(o&&document.getElementById(o))continue;let s=t(a.textContent);if(s)return s}return""}let x=new WeakMap,_=crypto.randomUUID(),j=0;function L(n){let u=n.getRootNode(),a=h=>u.querySelectorAll(h).length===1,o=[],s=n.getAttribute?.("data-testid");s&&a(`[data-testid=${JSON.stringify(s)}]`)&&o.push({strategy:"testId",value:s});let p=t(n.placeholder);p&&a(`[placeholder=${JSON.stringify(n.placeholder)}]`)&&o.push({strategy:"placeholder",value:p});let m=t(n.getAttribute?.("alt"));m&&a(`[alt=${JSON.stringify(n.getAttribute("alt"))}]`)&&o.push({strategy:"altText",value:m});let b=n.getAttribute?.("title");return b&&a(`[title=${JSON.stringify(b)}]`)&&o.push({strategy:"title",value:t(b)}),n.id&&!/:r|[a-f0-9]{12}|\d{6}/i.test(n.id)&&a(`#${CSS.escape(n.id)}`)&&o.push({strategy:"css",value:`#${CSS.escape(n.id)}`}),o.slice(0,4)}function C(n){let u=x.get(n);u||(u=`${_}:${++j}`,x.set(n,u));let a=L(n),o={__targetId:u,...a.length?{candidates:a}:{}},s=n.getRootNode(),p=l(n);if(p)return{...o,name:p};let m=v(n);if(m)return{...o,name:m,nameFrom:"adjacent-label"};let b=t(Array.from(n.childNodes||[]).filter(N=>N.nodeType===3).map(N=>N.textContent).join(" "));if(b&&b.length<=80)return{...o,name:b,nameFrom:"text"};let h=a[0];return h?{...o,name:"",locator:h.strategy==="placeholder"||h.strategy==="altText"?{strategy:"css",value:`[${h.strategy==="altText"?"alt":"placeholder"}=${JSON.stringify(h.value)}]`}:h}:{...o,name:""}}function k(n){let u=n.getAttribute&&n.getAttribute("role");if(u)return u;let a=n.tagName.toLowerCase(),o=(n.type||"").toLowerCase();return a==="button"?"button":a==="a"&&n.href?"link":a==="select"?n.multiple||n.size>1?"listbox":"combobox":a==="textarea"?"textbox":a==="input"?["submit","button","reset","file"].includes(o)?"button":o==="checkbox"||o==="radio"?o:o==="number"?"spinbutton":o==="search"?"searchbox":o==="range"?"slider":"textbox":n.isContentEditable?"textbox":H[a]??""}let H={li:"listitem",ul:"list",ol:"list",tr:"row",td:"cell",th:"columnheader",table:"table",nav:"navigation",main:"main",header:"banner",footer:"contentinfo",aside:"complementary",form:"form",dialog:"dialog",details:"group",fieldset:"group",img:"img",h1:"heading",h2:"heading",h3:"heading",h4:"heading",h5:"heading",h6:"heading"};function I(n){let u=n,a=null;for(let o=0;u&&o<6&&!(u===document.body||u===document.documentElement);o++){let s="";try{s=getComputedStyle(u).cursor}catch{break}if(s==="pointer")a=u;else if(a)break;u=u.parentElement}return a}let T="tr, [role=row], li, [role=listitem], [role=article], [role=gridcell]";function $(n){try{let u=n.cloneNode(!0);for(let a of Array.from(u.querySelectorAll(i+", input, select, textarea")))a.remove();for(let a of Array.from(u.querySelectorAll("td, th, li, p, div, span, tr")))a.append(" ");return t(u.textContent).slice(0,120)}catch{return""}}function V(n,u,a){if(!a)return!1;let o;try{o=Array.from(document.querySelectorAll(i+", input, select, textarea"))}catch{return!1}if(o.length>400)return!0;let s=0;for(let p of o)if(p!==n&&k(p)===u&&C(p).name===a&&++s>0)return!0;return!1}function A(n,u,a){if(!V(n,u,a))return null;let o=n.closest&&n.closest(T);if(!o)return null;let s=$(o);if(!s)return null;let p=o.getAttribute&&o.getAttribute("role"),m=o.tagName.toLowerCase();return{scope:s,scopeRole:p||(m==="tr"?"row":m==="li"?"listitem":m)}}function r(n,u,a){let o=A(n,u,a);return o||{scope:f(n)}}function f(n){let u=n.closest&&n.closest("form, fieldset, section, [role=group], [role=region], [role=dialog], [role=alertdialog], dialog");return u?l(u):""}function c(n){if(n.id)return"#"+CSS.escape(n.id);let u=[],a=n;for(;a&&a.nodeType===1&&a!==document.body;){let o=Array.from(a.parentNode?a.parentNode.children:[]).filter(s=>s.tagName===a.tagName);u.unshift(o.length>1?`${a.tagName.toLowerCase()}:nth-of-type(${o.indexOf(a)+1})`:a.tagName.toLowerCase()),a=a.parentElement}return"body > "+u.join(" > ")}return{accName:l,identify:C,roleOf:k,pointerTarget:I,scoping:r,cssPath:c}}var ge=Object.prototype.toString;function Q(e){return typeof e=="function"||ge.call(e)==="[object Function]"}function ye(e){var t=Number(e);return isNaN(t)?0:t===0||!isFinite(t)?t:(t>0?1:-1)*Math.floor(Math.abs(t))}var he=Math.pow(2,53)-1;function ve(e){var t=ye(e);return Math.min(Math.max(t,0),he)}function w(e,t){var i=Array,l=Object(e);if(e==null)throw new TypeError("Array.from requires an array-like object - not null or undefined");if(typeof t<"u"&&!Q(t))throw new TypeError("Array.from: when provided, the second argument must be a function");for(var y=ve(l.length),E=Q(i)?Object(new i(y)):new Array(y),v=0,x;v<y;)x=l[v],t?E[v]=t(x,v):E[v]=x,v+=1;return E.length=y,E}function O(e){"@babel/helpers - typeof";return O=typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?function(t){return typeof t}:function(t){return t&&typeof Symbol=="function"&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},O(e)}function we(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function X(e,t){for(var i=0;i<t.length;i++){var l=t[i];l.enumerable=l.enumerable||!1,l.configurable=!0,"value"in l&&(l.writable=!0),Object.defineProperty(e,Y(l.key),l)}}function Se(e,t,i){return t&&X(e.prototype,t),i&&X(e,i),Object.defineProperty(e,"prototype",{writable:!1}),e}function Ee(e,t,i){return t=Y(t),t in e?Object.defineProperty(e,t,{value:i,enumerable:!0,configurable:!0,writable:!0}):e[t]=i,e}function Y(e){var t=xe(e,"string");return O(t)=="symbol"?t:t+""}function xe(e,t){if(O(e)!="object"||!e)return e;var i=e[Symbol.toPrimitive];if(i!==void 0){var l=i.call(e,t||"default");if(O(l)!="object")return l;throw new TypeError("@@toPrimitive must return a primitive value.")}return(t==="string"?String:Number)(e)}var Ae=(function(){function e(){var t=arguments.length>0&&arguments[0]!==void 0?arguments[0]:[];we(this,e),Ee(this,"items",void 0),this.items=t}return Se(e,[{key:"add",value:function(i){return this.has(i)===!1&&this.items.push(i),this}},{key:"clear",value:function(){this.items=[]}},{key:"delete",value:function(i){var l=this.items.length;return this.items=this.items.filter(function(y){return y!==i}),l!==this.items.length}},{key:"forEach",value:function(i){var l=this;this.items.forEach(function(y){i(y,y,l)})}},{key:"has",value:function(i){return this.items.indexOf(i)!==-1}},{key:"size",get:function(){return this.items.length}}])})(),Z=typeof Set>"u"?Set:Ae;function g(e){var t;return(t=e.localName)!==null&&t!==void 0?t:e.tagName.toLowerCase()}var Te={article:"article",aside:"complementary",button:"button",datalist:"listbox",dd:"definition",details:"group",dialog:"dialog",dt:"term",fieldset:"group",figure:"figure",form:"form",footer:"contentinfo",h1:"heading",h2:"heading",h3:"heading",h4:"heading",h5:"heading",h6:"heading",header:"banner",hr:"separator",html:"document",legend:"legend",li:"listitem",math:"math",main:"main",menu:"list",nav:"navigation",ol:"list",optgroup:"group",option:"option",output:"status",progress:"progressbar",section:"region",summary:"button",table:"table",tbody:"rowgroup",textarea:"textbox",tfoot:"rowgroup",td:"cell",th:"columnheader",thead:"rowgroup",tr:"row",ul:"list"},Le={caption:new Set(["aria-label","aria-labelledby"]),code:new Set(["aria-label","aria-labelledby"]),deletion:new Set(["aria-label","aria-labelledby"]),emphasis:new Set(["aria-label","aria-labelledby"]),generic:new Set(["aria-label","aria-labelledby","aria-roledescription"]),insertion:new Set(["aria-label","aria-labelledby"]),none:new Set(["aria-label","aria-labelledby"]),paragraph:new Set(["aria-label","aria-labelledby"]),presentation:new Set(["aria-label","aria-labelledby"]),strong:new Set(["aria-label","aria-labelledby"]),subscript:new Set(["aria-label","aria-labelledby"]),superscript:new Set(["aria-label","aria-labelledby"])};function Ne(e,t){return["aria-atomic","aria-busy","aria-controls","aria-current","aria-description","aria-describedby","aria-details","aria-dropeffect","aria-flowto","aria-grabbed","aria-hidden","aria-keyshortcuts","aria-label","aria-labelledby","aria-live","aria-owns","aria-relevant","aria-roledescription"].some(function(i){var l;return e.hasAttribute(i)&&!((l=Le[t])!==null&&l!==void 0&&l.has(i))})}function ee(e,t){return Ne(e,t)}function D(e){var t=Ie(e);if(t===null||R.indexOf(t)!==-1){var i=Ce(e);if(R.indexOf(t||"")===-1||ee(e,i||""))return i}return t}function Ce(e){var t=Te[g(e)];if(t!==void 0)return t;switch(g(e)){case"a":case"area":case"link":if(e.hasAttribute("href"))return"link";break;case"img":return e.getAttribute("alt")===""&&!ee(e,"img")?"presentation":"img";case"input":{var i=e,l=i.type;switch(l){case"button":case"image":case"reset":case"submit":return"button";case"checkbox":case"radio":return l;case"range":return"slider";case"email":case"tel":case"text":case"url":return e.hasAttribute("list")?"combobox":"textbox";case"search":return e.hasAttribute("list")?"combobox":"searchbox";case"number":return"spinbutton";default:return null}}case"select":return e.hasAttribute("multiple")||e.size>1?"listbox":"combobox"}return null}function Ie(e){var t=e.getAttribute("role");if(t!==null){var i=t.trim().split(" ")[0];if(i.length>0)return i}return null}var R=["presentation","none"];function d(e){return e!==null&&e.nodeType===e.ELEMENT_NODE}function B(e){return d(e)&&g(e)==="caption"}function M(e){return d(e)&&g(e)==="input"}function te(e){return d(e)&&g(e)==="optgroup"}function re(e){return d(e)&&g(e)==="select"}function ne(e){return d(e)&&g(e)==="table"}function ie(e){return d(e)&&g(e)==="textarea"}function ae(e){var t=e.ownerDocument===null?e:e.ownerDocument,i=t.defaultView;if(i===null)throw new TypeError("no window available");return i}function oe(e){return d(e)&&g(e)==="fieldset"}function ue(e){return d(e)&&g(e)==="legend"}function le(e){return d(e)&&g(e)==="slot"}function Oe(e){return d(e)&&e.ownerSVGElement!==void 0}function se(e){return d(e)&&g(e)==="svg"}function fe(e){return Oe(e)&&g(e)==="title"}function P(e,t){if(d(e)&&e.hasAttribute(t)){var i=e.getAttribute(t).split(" "),l=e.getRootNode?e.getRootNode():e.ownerDocument;return i.map(function(y){return l.getElementById(y)}).filter(function(y){return y!==null})}return[]}function S(e,t){return d(e)?t.indexOf(D(e))!==-1:!1}function Re(e){return e.trim().replace(/\s\s+/g," ")}function Me(e,t){if(!d(e))return!1;if(e.hasAttribute("hidden")||e.getAttribute("aria-hidden")==="true")return!0;var i=t(e);return i.getPropertyValue("display")==="none"||i.getPropertyValue("visibility")==="hidden"}function Pe(e){return S(e,["button","combobox","listbox","textbox"])||pe(e,"range")}function pe(e,t){if(!d(e))return!1;if(t==="range")return S(e,["meter","progressbar","scrollbar","slider","spinbutton"]);throw new TypeError("No knowledge about abstract role '".concat(t,"'. This is likely a bug :("))}function ce(e,t){var i=w(e.querySelectorAll(t));return P(e,"aria-owns").forEach(function(l){i.push.apply(i,w(l.querySelectorAll(t)))}),i}function _e(e){return re(e)?e.selectedOptions||ce(e,"[selected]"):ce(e,'[aria-selected="true"]')}function je(e){return S(e,R)}function ke(e){return B(e)}function He(e){return S(e,["button","cell","checkbox","columnheader","gridcell","heading","label","legend","link","menuitem","menuitemcheckbox","menuitemradio","option","radio","row","rowheader","switch","tab","tooltip","treeitem"])}function De(e){return!1}function $e(e){return M(e)||ie(e)?e.value:e.textContent||""}function de(e){var t=e.getPropertyValue("content");return/^["'].*["']$/.test(t)?t.slice(1,-1):""}function me(e){var t=g(e);return t==="button"||t==="input"&&e.getAttribute("type")!=="hidden"||t==="meter"||t==="output"||t==="progress"||t==="select"||t==="textarea"}function be(e){if(me(e))return e;var t=null;return e.childNodes.forEach(function(i){if(t===null&&d(i)){var l=be(i);l!==null&&(t=l)}}),t}function Ve(e){if(e.control!==void 0)return e.control;var t=e.getAttribute("for");return t!==null?e.ownerDocument.getElementById(t):be(e)}function qe(e){var t=e.labels;if(t===null)return t;if(t!==void 0)return w(t);if(!me(e))return null;var i=e.ownerDocument;return w(i.querySelectorAll("label")).filter(function(l){return Ve(l)===e})}function Ge(e){var t=e.assignedNodes();return t.length===0?w(e.childNodes):t}function J(e){var t=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{},i=new Z,l=typeof Map>"u"?void 0:new Map,y=ae(e),E=t.compute,v=E===void 0?"name":E,x=t.computedStyleSupportsPseudoElements,_=x===void 0?t.getComputedStyle!==void 0:x,j=t.getComputedStyle,L=j===void 0?y.getComputedStyle.bind(y):j,C=t.hidden,k=C===void 0?!1:C,H=function(f,c){if(c!==void 0)throw new Error("use uncachedGetComputedStyle directly for pseudo elements");if(l===void 0)return L(f);var n=l.get(f);if(n)return n;var u=L(f,c);return l.set(f,u),u};function I(r,f){var c="";if(d(r)&&_){var n=L(r,"::before"),u=de(n);c="".concat(u," ").concat(c)}var a=le(r)?Ge(r):w(r.childNodes).concat(P(r,"aria-owns"));if(a.forEach(function(p){var m=A(p,{isEmbeddedInLabel:f.isEmbeddedInLabel,isReferenced:!1,recursion:!0}),b=d(p)?H(p).getPropertyValue("display"):"inline",h=b!=="inline"?" ":"";c+="".concat(h).concat(m).concat(h)}),d(r)&&_){var o=L(r,"::after"),s=de(o);c="".concat(c," ").concat(s)}return c.trim()}function T(r,f){var c=r.getAttributeNode(f);return c!==null&&!i.has(c)&&c.value.trim()!==""?(i.add(c),c.value):null}function $(r){return d(r)?T(r,"title"):null}function V(r){if(!d(r))return null;if(oe(r)){i.add(r);for(var f=w(r.childNodes),c=0;c<f.length;c+=1){var n=f[c];if(ue(n))return A(n,{isEmbeddedInLabel:!1,isReferenced:!1,recursion:!1})}}else if(ne(r)){i.add(r);for(var u=w(r.childNodes),a=0;a<u.length;a+=1){var o=u[a];if(B(o))return A(o,{isEmbeddedInLabel:!1,isReferenced:!1,recursion:!1})}}else if(se(r)){i.add(r);for(var s=w(r.childNodes),p=0;p<s.length;p+=1){var m=s[p];if(fe(m))return m.textContent}return null}else if(g(r)==="img"||g(r)==="area"){var b=T(r,"alt");if(b!==null)return b}else if(te(r)){var h=T(r,"label");if(h!==null)return h}if(M(r)&&(r.type==="button"||r.type==="submit"||r.type==="reset")){var N=T(r,"value");if(N!==null)return N;if(r.type==="submit")return"Submit";if(r.type==="reset")return"Reset"}var q=qe(r);if(q!==null&&q.length!==0)return i.add(r),w(q).map(function(G){return A(G,{isEmbeddedInLabel:!0,isReferenced:!1,recursion:!0})}).filter(function(G){return G.length>0}).join(" ");if(M(r)&&r.type==="image"){var z=T(r,"alt");if(z!==null)return z;var F=T(r,"title");return F!==null?F:"Submit Query"}if(S(r,["button"])){var K=I(r,{isEmbeddedInLabel:!1,isReferenced:!1});if(K!=="")return K}return null}function A(r,f){if(i.has(r))return"";if(!k&&Me(r,H)&&!f.isReferenced)return i.add(r),"";var c=d(r)?r.getAttributeNode("aria-labelledby"):null,n=c!==null&&!i.has(c)?P(r,"aria-labelledby"):[];if(v==="name"&&!f.isReferenced&&n.length>0)return i.add(c),n.map(function(b){return A(b,{isEmbeddedInLabel:f.isEmbeddedInLabel,isReferenced:!0,recursion:!1})}).join(" ");var u=f.recursion&&Pe(r)&&v==="name";if(!u){var a=(d(r)&&r.getAttribute("aria-label")||"").trim();if(a!==""&&v==="name")return i.add(r),a;if(!je(r)){var o=V(r);if(o!==null)return i.add(r),o}}if(S(r,["menu"]))return i.add(r),"";if(u||f.isEmbeddedInLabel||f.isReferenced){if(S(r,["combobox","listbox"])){i.add(r);var s=_e(r);return s.length===0?M(r)?r.value:"":w(s).map(function(b){return A(b,{isEmbeddedInLabel:f.isEmbeddedInLabel,isReferenced:!1,recursion:!0})}).join(" ")}if(pe(r,"range"))return i.add(r),r.hasAttribute("aria-valuetext")?r.getAttribute("aria-valuetext"):r.hasAttribute("aria-valuenow")?r.getAttribute("aria-valuenow"):r.getAttribute("value")||"";if(S(r,["textbox"]))return i.add(r),$e(r)}if(He(r)||d(r)&&f.isReferenced||ke(r)||De(r)){var p=I(r,{isEmbeddedInLabel:f.isEmbeddedInLabel,isReferenced:!1});if(p!=="")return i.add(r),p}if(r.nodeType===r.TEXT_NODE)return i.add(r),r.textContent||"";if(f.recursion)return i.add(r),I(r,{isEmbeddedInLabel:f.isEmbeddedInLabel,isReferenced:!1});var m=$(r);return m!==null?(i.add(r),m):(i.add(r),"")}return Re(A(e,{isEmbeddedInLabel:!1,isReferenced:v==="description",recursion:!1}))}function Be(e){return S(e,["caption","code","deletion","emphasis","generic","insertion","none","paragraph","presentation","strong","subscript","superscript"])}function W(e){var t=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{};return Be(e)?"":J(e,t)}window.__flAccName=e=>{try{return W(e).replace(/\s+/g," ").trim()}catch{return""}};window.__flCreateTargetIdentity=U;})();
|
|
@@ -6,7 +6,7 @@ import os from "os";
|
|
|
6
6
|
import path from "path";
|
|
7
7
|
import { createRequire } from "module";
|
|
8
8
|
import { spawnSync } from "child_process";
|
|
9
|
-
var AGENT_VERSION = "0.1.0-beta.
|
|
9
|
+
var AGENT_VERSION = "0.1.0-beta.21";
|
|
10
10
|
async function prepareBrowsers() {
|
|
11
11
|
if (browsersReady()) {
|
|
12
12
|
return { ok: true, already: true, message: "chromium is already installed" };
|
|
@@ -458,7 +458,7 @@ import path7 from "path";
|
|
|
458
458
|
// ../engine-playwright/dist/index.js
|
|
459
459
|
import path3 from "path";
|
|
460
460
|
|
|
461
|
-
// ../test-model/dist/chunk-
|
|
461
|
+
// ../test-model/dist/chunk-RQ5E3H3R.js
|
|
462
462
|
import { z as z3 } from "zod";
|
|
463
463
|
var LocatorOverrideSchema = z3.object({
|
|
464
464
|
strategy: z3.enum(["css", "xpath", "id", "testId", "text", "label", "placeholder", "altText", "title"]),
|
|
@@ -524,7 +524,30 @@ var RecordedTargetSchema = z3.object({
|
|
|
524
524
|
frame: z3.array(z3.string()).optional(),
|
|
525
525
|
pageId: z3.string().optional(),
|
|
526
526
|
nameFrom: z3.enum(["adjacent-label", "text"]).optional(),
|
|
527
|
+
/** SET BY A PERSON, and exclusive.
|
|
528
|
+
*
|
|
529
|
+
* Somebody opened the editor and said "find it this way instead", which is
|
|
530
|
+
* a decision about semantics that were wrong for this element. Falling back
|
|
531
|
+
* to those semantics would undo it, so this replaces resolution entirely.
|
|
532
|
+
*
|
|
533
|
+
* Steps recorded before `candidates` existed also carry the recorder's own
|
|
534
|
+
* pick here, and are treated the same way: nothing in the document says
|
|
535
|
+
* which of the two put it there, and guessing wrong in the other direction
|
|
536
|
+
* would change what somebody's test does. */
|
|
527
537
|
locator: LocatorOverrideSchema.optional(),
|
|
538
|
+
/** WHAT THE RECORDER SAW, best first — preferences, not assertions.
|
|
539
|
+
*
|
|
540
|
+
* One locator is one way to be wrong. A test id is removed, a placeholder
|
|
541
|
+
* is reworded, a wrapper is added, and a step that had exactly one way to
|
|
542
|
+
* find its element has none — although the field is sitting in plain sight
|
|
543
|
+
* and two other things about it are unchanged.
|
|
544
|
+
*
|
|
545
|
+
* Tried in order, and ONLY where the one before found nothing. Two matches
|
|
546
|
+
* is a question the test has to answer, never one to guess at, so ambiguity
|
|
547
|
+
* stops the walk rather than passing it along. A step that resolves by
|
|
548
|
+
* anything other than its first choice says so in the result — a repair
|
|
549
|
+
* nobody can see is a repair nobody can trust. */
|
|
550
|
+
candidates: z3.array(LocatorOverrideSchema).max(4).optional(),
|
|
528
551
|
nth: z3.number().int().nonnegative().optional()
|
|
529
552
|
});
|
|
530
553
|
var RecordedAssertionKindSchema = z3.enum([
|
|
@@ -918,11 +941,57 @@ var FlowOccurrenceSchema = z22.object({
|
|
|
918
941
|
startPath: z22.string().optional(),
|
|
919
942
|
baseUrl: z22.string().url().optional(),
|
|
920
943
|
/** GO SOMEWHERE FIRST, or carry on where the last row ended.
|
|
944
|
+
*
|
|
945
|
+
* SUPERSEDED BY `start`, and still read so that every case written before
|
|
946
|
+
* it keeps running unchanged. `startOf` answers for both shapes; nothing
|
|
947
|
+
* should consult this field directly.
|
|
921
948
|
*
|
|
922
949
|
* Off by default, because a case is a sequence in one browser: re-entering
|
|
923
950
|
* each flow's original starting URL would sign the tester out between rows
|
|
924
951
|
* and is exactly the behaviour composing flows is meant to avoid. */
|
|
925
952
|
navigateFirst: z22.boolean().default(false),
|
|
953
|
+
/** WHICH BROWSER THIS ROW RUNS IN.
|
|
954
|
+
*
|
|
955
|
+
* Two answers, because there are two: carry on in the browser this
|
|
956
|
+
* participant already has, or open them a new one. Where the page goes is a
|
|
957
|
+
* different question with its own field — folding them together produced a
|
|
958
|
+
* three-way choice in which two of the answers differed by something most
|
|
959
|
+
* people never set.
|
|
960
|
+
*
|
|
961
|
+
* A case is a sequence of flows performed by participants, and until now
|
|
962
|
+
* the only question a row could answer was whether to navigate. Whether it
|
|
963
|
+
* ran in the participant's EXISTING browser or a fresh one could not be
|
|
964
|
+
* said at all — it was implied (always the existing one) and could only be
|
|
965
|
+
* overridden by burying a `resetActor` step inside the flow.
|
|
966
|
+
*
|
|
967
|
+
* continue — the participant's browser, exactly where they left it
|
|
968
|
+
* navigate — the participant's browser, back at this flow's start path
|
|
969
|
+
* fresh — a new browser for them, signed in again if they start signed
|
|
970
|
+
* in, then the start path
|
|
971
|
+
*
|
|
972
|
+
* A participant's FIRST row is always fresh in effect: there is nothing to
|
|
973
|
+
* continue from. That is not stored, because it is a fact about the order
|
|
974
|
+
* rather than about the row — moving the row changes the answer. */
|
|
975
|
+
start: z22.enum(["continue", "navigate", "fresh"]).optional(),
|
|
976
|
+
/** WHETHER THIS COPY DOES ITS OWN SIGNING IN.
|
|
977
|
+
*
|
|
978
|
+
* A flow recorded with "sign in before recording" keeps that sign-in as its
|
|
979
|
+
* first step, because that is how it readies ITSELF when it runs alone.
|
|
980
|
+
* Inside a case the identity belongs to the person, so the same step is
|
|
981
|
+
* usually a second answer to a question already answered.
|
|
982
|
+
*
|
|
983
|
+
* skip — the person's account is the one used; this copy's own sign-ins
|
|
984
|
+
* lie dormant, and the run confirms the session rather than
|
|
985
|
+
* assuming it
|
|
986
|
+
* run — perform them anyway, whoever they name. Signing in as somebody
|
|
987
|
+
* else on this person's browser is a real thing to want, and
|
|
988
|
+
* before this there was no way to say it.
|
|
989
|
+
*
|
|
990
|
+
* ABSENT IS NOT "run". It is "nobody has said", which behaves as skip where
|
|
991
|
+
* the two agree on the account and is refused where they do not — so a case
|
|
992
|
+
* written before this keeps its meaning, and a contradiction stays a
|
|
993
|
+
* contradiction until somebody actually resolves it. */
|
|
994
|
+
ownSignIn: z22.enum(["skip", "run"]).optional(),
|
|
926
995
|
steps: z22.array(RecordedStepSchema),
|
|
927
996
|
environment: ExecutionEnvironmentSchema.optional(),
|
|
928
997
|
/** Where this copy came from, so the editor can say whether the source has
|
|
@@ -954,8 +1023,18 @@ var TestCaseSchema = z22.object({
|
|
|
954
1023
|
slug: z22.string().regex(/^[a-z0-9][a-z0-9-]*$/),
|
|
955
1024
|
name: z22.string().min(1),
|
|
956
1025
|
description: z22.string().default(""),
|
|
957
|
-
/**
|
|
958
|
-
*
|
|
1026
|
+
/** NO LONGER USED, and kept only so that a case written while they were
|
|
1027
|
+
* parses.
|
|
1028
|
+
*
|
|
1029
|
+
* These overrode the FIRST row's start path — whichever row that happened
|
|
1030
|
+
* to be. A case is reordered by dragging, so the override silently moved to
|
|
1031
|
+
* whatever was dragged to the top, carrying a path chosen for a different
|
|
1032
|
+
* flow. Nothing said so, and the row it landed on looked like every other
|
|
1033
|
+
* row. A positional override of a list people reorder is a setting that
|
|
1034
|
+
* cannot stay correct.
|
|
1035
|
+
*
|
|
1036
|
+
* Every row now starts where its own flow says, which is the same answer
|
|
1037
|
+
* whatever the order. */
|
|
959
1038
|
startPath: z22.string().default("/"),
|
|
960
1039
|
useStartPath: z22.boolean().default(false),
|
|
961
1040
|
baseUrl: z22.string().url().optional(),
|
|
@@ -984,6 +1063,11 @@ var TestCaseSchema = z22.object({
|
|
|
984
1063
|
steps.add(step.id);
|
|
985
1064
|
});
|
|
986
1065
|
});
|
|
1066
|
+
}).transform((testCase) => {
|
|
1067
|
+
if (!testCase.defaultActorId || testCase.defaultActorId === "default") return testCase;
|
|
1068
|
+
if (testCase.actors?.some((actor) => actor.id === testCase.defaultActorId)) return testCase;
|
|
1069
|
+
const { defaultActorId: _gone, ...rest } = testCase;
|
|
1070
|
+
return rest;
|
|
987
1071
|
});
|
|
988
1072
|
var CaseFolderSchema = z22.object({
|
|
989
1073
|
schemaVersion: z22.literal(1),
|
|
@@ -1035,13 +1119,66 @@ var SuiteSchema = z22.object({
|
|
|
1035
1119
|
seen.add(entry.entryId);
|
|
1036
1120
|
});
|
|
1037
1121
|
});
|
|
1038
|
-
function
|
|
1039
|
-
return
|
|
1122
|
+
function startOf(occurrence) {
|
|
1123
|
+
return occurrence.start === "fresh" ? "fresh" : "continue";
|
|
1124
|
+
}
|
|
1125
|
+
function navigatesFirst(occurrence) {
|
|
1126
|
+
return occurrence.start === "navigate" || occurrence.navigateFirst;
|
|
1127
|
+
}
|
|
1128
|
+
function sessionLanes(occurrences, defaultActorId) {
|
|
1129
|
+
const mine = /* @__PURE__ */ new Map();
|
|
1130
|
+
const owner = /* @__PURE__ */ new Map();
|
|
1131
|
+
const incarnations = /* @__PURE__ */ new Map();
|
|
1132
|
+
const lanes = /* @__PURE__ */ new Map();
|
|
1133
|
+
const last = /* @__PURE__ */ new Map();
|
|
1134
|
+
const rows = [];
|
|
1135
|
+
let previous = null;
|
|
1136
|
+
const open = (actorId) => {
|
|
1137
|
+
const next = (incarnations.get(actorId) ?? -1) + 1;
|
|
1138
|
+
incarnations.set(actorId, next);
|
|
1139
|
+
const sessionId = `${actorId}#${next}`;
|
|
1140
|
+
mine.set(actorId, sessionId);
|
|
1141
|
+
owner.set(sessionId, actorId);
|
|
1142
|
+
if (!lanes.has(sessionId)) lanes.set(sessionId, lanes.size);
|
|
1143
|
+
return sessionId;
|
|
1144
|
+
};
|
|
1145
|
+
for (const occurrence of occurrences) {
|
|
1146
|
+
const actorId = occurrence.actorId || defaultActorId || "default";
|
|
1147
|
+
const continues = startOf(occurrence) === "continue";
|
|
1148
|
+
const asked = occurrence.start === "continue";
|
|
1149
|
+
const theirs = mine.get(actorId);
|
|
1150
|
+
const adoptable = previous !== null && owner.get(previous) === "default" && actorId !== "default";
|
|
1151
|
+
let sessionId;
|
|
1152
|
+
if (continues && theirs) sessionId = theirs;
|
|
1153
|
+
else if (asked && adoptable) {
|
|
1154
|
+
sessionId = previous;
|
|
1155
|
+
owner.set(sessionId, actorId);
|
|
1156
|
+
mine.set(actorId, sessionId);
|
|
1157
|
+
mine.delete("default");
|
|
1158
|
+
} else sessionId = open(actorId);
|
|
1159
|
+
const continuesFrom = last.get(sessionId) ?? null;
|
|
1160
|
+
rows.push({
|
|
1161
|
+
occurrenceId: occurrence.occurrenceId,
|
|
1162
|
+
actorId,
|
|
1163
|
+
incarnation: Number(sessionId.split("#")[1]),
|
|
1164
|
+
sessionId,
|
|
1165
|
+
lane: lanes.get(sessionId),
|
|
1166
|
+
continuesFrom,
|
|
1167
|
+
opens: continuesFrom === null,
|
|
1168
|
+
// WHETHER THERE IS A CHOICE TO MAKE. A row with nothing to continue —
|
|
1169
|
+
// no browser of their own and no guest's to take over — is told so
|
|
1170
|
+
// rather than offered an answer that cannot mean anything.
|
|
1171
|
+
mayContinue: Boolean(theirs) || adoptable
|
|
1172
|
+
});
|
|
1173
|
+
last.set(sessionId, occurrence.occurrenceId);
|
|
1174
|
+
previous = sessionId;
|
|
1175
|
+
}
|
|
1176
|
+
return rows;
|
|
1040
1177
|
}
|
|
1041
1178
|
function caseDebugPlan(testCase, baseUrl) {
|
|
1042
1179
|
const owners = {};
|
|
1043
1180
|
const steps = [];
|
|
1044
|
-
const occurrences =
|
|
1181
|
+
const occurrences = testCase.occurrences;
|
|
1045
1182
|
for (const [index, item] of occurrences.entries()) {
|
|
1046
1183
|
if (testCase.actors?.length) {
|
|
1047
1184
|
const id = `case-actor:${encodeURIComponent(item.occurrenceId)}`;
|
|
@@ -1244,14 +1381,19 @@ function validateActors(actors = [], defaultId = "default", used = []) {
|
|
|
1244
1381
|
ids.add(actor.id);
|
|
1245
1382
|
}
|
|
1246
1383
|
for (const id of [defaultId, ...used]) {
|
|
1247
|
-
if (id === UNSET_ACTOR) throw new Error("A
|
|
1248
|
-
if (id && !ids.has(id)) throw new Error(`
|
|
1384
|
+
if (id === UNSET_ACTOR) throw new Error("A step does not say who it is for \u2014 choose somebody before running");
|
|
1385
|
+
if (id && !ids.has(id)) throw new Error(`This test names somebody it does not have (${id}) \u2014 choose who runs that row before running`);
|
|
1249
1386
|
}
|
|
1250
1387
|
}
|
|
1251
1388
|
var UNSET_ACTOR = "\0unset";
|
|
1252
1389
|
function actorIdsUsed(steps) {
|
|
1253
1390
|
return steps.flatMap((step) => step.disabled ? [] : step.type === "useActor" ? [step.actorId ?? UNSET_ACTOR] : step.type === "resetActor" && step.actorId ? [step.actorId] : []);
|
|
1254
1391
|
}
|
|
1392
|
+
function sameIdentity(a, b2) {
|
|
1393
|
+
if (a.profileId !== b2.profileId) return false;
|
|
1394
|
+
if (a.accountId && b2.accountId) return a.accountId === b2.accountId;
|
|
1395
|
+
return true;
|
|
1396
|
+
}
|
|
1255
1397
|
|
|
1256
1398
|
// ../engine-playwright/dist/index.js
|
|
1257
1399
|
import { createHmac, randomBytes } from "crypto";
|
|
@@ -1328,34 +1470,36 @@ function resolveStartUrl(baseUrl, startPath) {
|
|
|
1328
1470
|
}
|
|
1329
1471
|
var INPUT_ROLES = /* @__PURE__ */ new Set(["textbox", "combobox", "checkbox", "radio", "spinbutton", "searchbox"]);
|
|
1330
1472
|
var NAVIGATIONS = /* @__PURE__ */ new Set(["back", "forward"]);
|
|
1331
|
-
function
|
|
1332
|
-
const o = step.locator;
|
|
1473
|
+
function planForOverride(o, overridden) {
|
|
1333
1474
|
if (o && o.value.trim()) {
|
|
1334
1475
|
const value = o.value.trim();
|
|
1335
1476
|
switch (o.strategy) {
|
|
1336
1477
|
// Playwright reads a leading `xpath=` prefix, and also bare paths that
|
|
1337
1478
|
// start with `/` or `//`. Normalising here means the QA can paste either.
|
|
1338
1479
|
case "xpath":
|
|
1339
|
-
return { method: "locator", arg: value.startsWith("xpath=") ? value : `xpath=${value}`, overridden
|
|
1480
|
+
return { method: "locator", arg: value.startsWith("xpath=") ? value : `xpath=${value}`, overridden };
|
|
1340
1481
|
// Typed as a bare id, so nobody has to remember the `#`.
|
|
1341
1482
|
case "id":
|
|
1342
|
-
return { method: "locator", arg: /^[a-zA-Z_][a-zA-Z0-9_-]*$/.test(value.replace(/^#/, "")) ? `#${value.replace(/^#/, "")}` : `[id=${JSON.stringify(value.replace(/^#/, ""))}]`, overridden
|
|
1483
|
+
return { method: "locator", arg: /^[a-zA-Z_][a-zA-Z0-9_-]*$/.test(value.replace(/^#/, "")) ? `#${value.replace(/^#/, "")}` : `[id=${JSON.stringify(value.replace(/^#/, ""))}]`, overridden };
|
|
1343
1484
|
case "css":
|
|
1344
|
-
return { method: "locator", arg: value, overridden
|
|
1485
|
+
return { method: "locator", arg: value, overridden };
|
|
1345
1486
|
case "testId":
|
|
1346
|
-
return { method: "getByTestId", arg: value, overridden
|
|
1487
|
+
return { method: "getByTestId", arg: value, overridden };
|
|
1347
1488
|
case "text":
|
|
1348
|
-
return { method: "getByText", arg: value, exact: true, overridden
|
|
1489
|
+
return { method: "getByText", arg: value, exact: true, overridden };
|
|
1349
1490
|
case "label":
|
|
1350
|
-
return { method: "getByLabel", arg: value, exact: true, overridden
|
|
1491
|
+
return { method: "getByLabel", arg: value, exact: true, overridden };
|
|
1351
1492
|
case "placeholder":
|
|
1352
|
-
return { method: "getByPlaceholder", arg: value, exact: true, overridden
|
|
1493
|
+
return { method: "getByPlaceholder", arg: value, exact: true, overridden };
|
|
1353
1494
|
case "altText":
|
|
1354
|
-
return { method: "getByAltText", arg: value, exact: true, overridden
|
|
1495
|
+
return { method: "getByAltText", arg: value, exact: true, overridden };
|
|
1355
1496
|
case "title":
|
|
1356
|
-
return { method: "getByTitle", arg: value, exact: true, overridden
|
|
1497
|
+
return { method: "getByTitle", arg: value, exact: true, overridden };
|
|
1357
1498
|
}
|
|
1358
1499
|
}
|
|
1500
|
+
return null;
|
|
1501
|
+
}
|
|
1502
|
+
function semanticPlan(step) {
|
|
1359
1503
|
if (NAVIGATIONS.has(step.type)) {
|
|
1360
1504
|
return { method: step.type === "back" ? "goBack" : "goForward", arg: "", overridden: false };
|
|
1361
1505
|
}
|
|
@@ -1380,6 +1524,49 @@ function locatorPlan(step) {
|
|
|
1380
1524
|
}
|
|
1381
1525
|
return INPUT_ROLES.has(step.role) ? { method: "getByLabel", arg: step.name, exact: true, overridden: false, inputRole: step.role } : step.role ? { method: "getByRole", arg: step.role, name: step.name, exact: true, overridden: false } : { method: "getByText", arg: step.name, exact: step.type !== "hover", overridden: false, visible: true };
|
|
1382
1526
|
}
|
|
1527
|
+
function locatorPlans(step) {
|
|
1528
|
+
const chosen = planForOverride(step.locator, true);
|
|
1529
|
+
if (chosen) return [chosen];
|
|
1530
|
+
const candidates = step.candidates ?? [];
|
|
1531
|
+
const plans = [
|
|
1532
|
+
...candidates.filter((each) => each.strategy === "testId"),
|
|
1533
|
+
...candidates.filter((each) => each.strategy !== "testId")
|
|
1534
|
+
];
|
|
1535
|
+
const first = plans.findIndex((each) => each.strategy !== "testId");
|
|
1536
|
+
const before = first === -1 ? plans : plans.slice(0, first);
|
|
1537
|
+
const after = first === -1 ? [] : plans.slice(first);
|
|
1538
|
+
const semantic = semanticPlan(step);
|
|
1539
|
+
const ordered = [
|
|
1540
|
+
...before.map((each) => planForOverride(each, false)),
|
|
1541
|
+
// Below a test id and above everything else. A name is what somebody
|
|
1542
|
+
// reading the test understands the step to be about, so a step that
|
|
1543
|
+
// resolves by it is the step still meaning what it says.
|
|
1544
|
+
semantic.method === "none" ? null : semantic,
|
|
1545
|
+
...after.map((each) => planForOverride(each, false))
|
|
1546
|
+
].filter((plan) => plan !== null);
|
|
1547
|
+
return ordered.length ? ordered : [semantic];
|
|
1548
|
+
}
|
|
1549
|
+
var quote = (s2) => `'${s2.replace(/\\/g, "\\\\").replace(/'/g, "\\'")}'`;
|
|
1550
|
+
function locatorPlan(step) {
|
|
1551
|
+
return locatorPlans(step)[0];
|
|
1552
|
+
}
|
|
1553
|
+
function describeLocators(step) {
|
|
1554
|
+
return locatorPlans(step).map((plan) => describePlan(step, plan));
|
|
1555
|
+
}
|
|
1556
|
+
function describePlan(step, p) {
|
|
1557
|
+
if (p.method === "none") return "(nothing to resolve \u2014 no accessible name)";
|
|
1558
|
+
if (p.method === "goBack") return "page.goBack()";
|
|
1559
|
+
if (p.method === "goForward") return "page.goForward()";
|
|
1560
|
+
let root = "page";
|
|
1561
|
+
for (const frame of step.frame ?? []) root += `.frameLocator(${quote(frameSelector(frame))})`;
|
|
1562
|
+
if (!p.overridden && step.scope) root = scopeExpression(root, step.scope, step.scopeRole);
|
|
1563
|
+
let base = p.viaAdjacentLabel ? `${root}.getByText(${quote(p.arg)}, { exact: true }).locator('xpath=..').getByRole(${quote(step.role || "textbox")})` : p.method === "getByRole" ? `${root}.getByRole(${quote(p.arg)}, { name: ${quote(p.name ?? "")}, exact: true })` : `${root}.${p.method}(${quote(p.arg)}${p.exact !== void 0 ? `, { exact: ${p.exact} }` : ""})`;
|
|
1564
|
+
if (p.inputRole) base = `${root}.getByRole(${quote(p.inputRole)}, { name: ${quote(p.arg)}, exact: true }).or(${base}.and(${root}.locator(${quote(inputSelector(p.inputRole))})))`;
|
|
1565
|
+
if (p.visible) base += ".filter({ visible: true })";
|
|
1566
|
+
if (step.nth !== void 0) base += `.nth(${step.nth})`;
|
|
1567
|
+
if (step.pageId && step.pageId !== "main") base += ` // page ${quote(step.pageId)}`;
|
|
1568
|
+
return base;
|
|
1569
|
+
}
|
|
1383
1570
|
function frameSelector(name) {
|
|
1384
1571
|
const positional = /^nth:(\d+)$/.exec(name);
|
|
1385
1572
|
if (positional) return `iframe, frame >> nth=${positional[1]}`;
|
|
@@ -1390,6 +1577,10 @@ function inputSelector(role) {
|
|
|
1390
1577
|
return role === "textbox" ? 'input, textarea, [contenteditable="true"], [role="textbox"]' : `input, select, [role="${role}"]`;
|
|
1391
1578
|
}
|
|
1392
1579
|
var SCOPE_ROLES = ["form", "group", "region", "dialog", "alertdialog"];
|
|
1580
|
+
function scopeExpression(root, name, role) {
|
|
1581
|
+
const parts = (role ? [role] : SCOPE_ROLES).map((r) => `${root}.getByRole(${quote(r)}, { name: ${quote(name)}, exact: ${!role} })`);
|
|
1582
|
+
return parts.reduce((a, b2) => `${a}.or(${b2})`);
|
|
1583
|
+
}
|
|
1393
1584
|
var CHECKS = [
|
|
1394
1585
|
{ id: "visible", label: "is visible", kind: "visible", group: "state" },
|
|
1395
1586
|
{ id: "not-visible", label: "is not visible", kind: "visible", not: true, group: "state" },
|
|
@@ -3105,6 +3296,30 @@ var RunActors = class {
|
|
|
3105
3296
|
if (!id || id === "default") return this.opts.actors?.find((a) => a.id === (id ?? "default"))?.name ?? "the main participant";
|
|
3106
3297
|
return this.opts.actors?.find((a) => a.id === id)?.name ?? id;
|
|
3107
3298
|
}
|
|
3299
|
+
/** WHAT THIS PARTICIPANT STARTS AS, if anything.
|
|
3300
|
+
*
|
|
3301
|
+
* Asked by a `signIn` step before it runs: a flow carries its own sign-in
|
|
3302
|
+
* because that is how it gets itself ready when it runs ALONE, and in a
|
|
3303
|
+
* case the person is already that identity. Running it again logs in on top
|
|
3304
|
+
* of a session that is already signed in, which most applications answer by
|
|
3305
|
+
* redirecting away from the login page — so the form is not there and a
|
|
3306
|
+
* correctly signed-in run goes red. */
|
|
3307
|
+
identityOf(id) {
|
|
3308
|
+
return this.opts.actors?.find((a) => a.id === (id ?? "default"))?.startSignedIn;
|
|
3309
|
+
}
|
|
3310
|
+
/** SIGN THIS PERSON IN AGAIN, in the browser they already have.
|
|
3311
|
+
*
|
|
3312
|
+
* Not `freshGuest`, which is the opposite: that throws the context away to
|
|
3313
|
+
* start somebody over as a stranger, and leaves them one. This is for a
|
|
3314
|
+
* session that has simply gone — expired, or dropped by the application —
|
|
3315
|
+
* where the browser is the right browser and only the sign-in is missing.
|
|
3316
|
+
*
|
|
3317
|
+
* Un-initialising is what makes `select` do the preparation again rather
|
|
3318
|
+
* than assume it already happened. */
|
|
3319
|
+
async prepareAgain(id) {
|
|
3320
|
+
this.initialized.delete(id);
|
|
3321
|
+
await this.select(id);
|
|
3322
|
+
}
|
|
3108
3323
|
async select(id, startUrl = this.opts.startUrl) {
|
|
3109
3324
|
const actor = this.opts.actors?.find((a) => a.id === id);
|
|
3110
3325
|
if (id !== "default" && !actor) throw new Error(`Unknown actor: ${id}`);
|
|
@@ -3170,6 +3385,42 @@ var RunActors = class {
|
|
|
3170
3385
|
}
|
|
3171
3386
|
this.initialized.add(id);
|
|
3172
3387
|
}
|
|
3388
|
+
/** START THIS PARTICIPANT AGAIN, as themselves.
|
|
3389
|
+
*
|
|
3390
|
+
* `freshGuest` gives a browser that has never been anywhere, which is the
|
|
3391
|
+
* right answer for "start over as a stranger" and the wrong one for "Alice
|
|
3392
|
+
* on a second device": it would leave Alice signed out for the rest of the
|
|
3393
|
+
* case. So the context is replaced AND the participant is un-initialised,
|
|
3394
|
+
* which is what makes `select` navigate and run their sign-in again.
|
|
3395
|
+
*
|
|
3396
|
+
* The distinction is the whole difference between a row that says `fresh`
|
|
3397
|
+
* and a `resetActor` step, and both are worth having. */
|
|
3398
|
+
async restart(id, startUrl) {
|
|
3399
|
+
await this.freshGuest(id);
|
|
3400
|
+
this.initialized.delete(id);
|
|
3401
|
+
await this.select(id, startUrl);
|
|
3402
|
+
}
|
|
3403
|
+
/** HAND A GUEST'S BROWSER TO SOMEBODY, and sign them in there.
|
|
3404
|
+
*
|
|
3405
|
+
* Browsing as a guest and then signing in is one browser, not two: the
|
|
3406
|
+
* basket filled before signing in is the whole point of testing it, and a
|
|
3407
|
+
* participant given a browser of their own would never see it.
|
|
3408
|
+
*
|
|
3409
|
+
* The context is moved rather than copied — there is one of it — and the
|
|
3410
|
+
* participant is left un-initialised so `select` navigates and runs their
|
|
3411
|
+
* sign-in inside it. The guest keeps no claim on it; a later guest row
|
|
3412
|
+
* opens a browser of its own, because this one is signed in now. */
|
|
3413
|
+
async adopt(from, to, startUrl) {
|
|
3414
|
+
const session = this.sessions.get(from);
|
|
3415
|
+
if (!session) return this.select(to, startUrl);
|
|
3416
|
+
this.sessions.set(to, session);
|
|
3417
|
+
this.sessions.delete(from);
|
|
3418
|
+
this.initialized.delete(from);
|
|
3419
|
+
this.initialized.delete(to);
|
|
3420
|
+
this.current = session;
|
|
3421
|
+
this.currentId = to;
|
|
3422
|
+
await this.select(to, startUrl);
|
|
3423
|
+
}
|
|
3173
3424
|
/** Replace a participant's context without disturbing anyone else's. */
|
|
3174
3425
|
async replaceWithGuest(id, previous) {
|
|
3175
3426
|
const session = await openRunSession({ ...this.opts, browser: this.main.browser, ...this.evidenceIn(`actor-${id}`) });
|
|
@@ -3189,6 +3440,10 @@ var RunActors = class {
|
|
|
3189
3440
|
preparedHow() {
|
|
3190
3441
|
return Object.fromEntries(this.preparations);
|
|
3191
3442
|
}
|
|
3443
|
+
/** Whether this participant already has a browser open. */
|
|
3444
|
+
has(id) {
|
|
3445
|
+
return this.sessions.has(id);
|
|
3446
|
+
}
|
|
3192
3447
|
/** Throw this participant's session away and give them a new signed-out one.
|
|
3193
3448
|
*
|
|
3194
3449
|
* NOT SIGNING OUT. An application can be signed out of while the browser
|
|
@@ -3553,8 +3808,15 @@ async function locate(page, step, opts = {}) {
|
|
|
3553
3808
|
const loc = await locateAll(page, step, opts);
|
|
3554
3809
|
return step.nth === void 0 ? loc : loc.nth(step.nth);
|
|
3555
3810
|
}
|
|
3811
|
+
async function locateCandidates(page, step, opts = {}) {
|
|
3812
|
+
const built = [];
|
|
3813
|
+
for (const plan of locatorPlans(step)) built.push(await fromPlan(page, step, plan, opts));
|
|
3814
|
+
return step.nth === void 0 ? built : built.map((each) => each.nth(step.nth));
|
|
3815
|
+
}
|
|
3556
3816
|
async function locateAll(page, step, opts = {}) {
|
|
3557
|
-
|
|
3817
|
+
return fromPlan(page, step, locatorPlan(step), opts);
|
|
3818
|
+
}
|
|
3819
|
+
async function fromPlan(page, step, plan, opts = {}) {
|
|
3558
3820
|
let root = rootFor(page, step);
|
|
3559
3821
|
if (step.scope && !plan.overridden) {
|
|
3560
3822
|
const roles = step.scopeRole ? [step.scopeRole] : [...SCOPE_ROLES];
|
|
@@ -4036,6 +4298,7 @@ async function performSignIn(session, step, opts) {
|
|
|
4036
4298
|
}
|
|
4037
4299
|
return { ok: true, label, detail: null, kind: null, checks: checked?.checks ?? [] };
|
|
4038
4300
|
}
|
|
4301
|
+
var SIGNED_IN_CHECK_MS = 3e3;
|
|
4039
4302
|
async function confirmSignedIn(page, auth, timeoutMs) {
|
|
4040
4303
|
for (const check of auth.success) {
|
|
4041
4304
|
if (check.enabled === false) continue;
|
|
@@ -4150,6 +4413,67 @@ async function runStepsIn(session, steps, opts) {
|
|
|
4150
4413
|
error = "the run was cancelled";
|
|
4151
4414
|
break;
|
|
4152
4415
|
}
|
|
4416
|
+
if (step.type === "signIn" && !step.disabled && step.auth && opts.actors && opts.ownSignIn !== "run") {
|
|
4417
|
+
const identity = opts.actors.identityOf(opts.actors.currentId);
|
|
4418
|
+
const auth = opts.resolveAuth?.(step.auth);
|
|
4419
|
+
const dormant = opts.ownSignIn === "skip" || identity !== void 0 && sameIdentity(identity, step.auth);
|
|
4420
|
+
const expected = identity && !sameIdentity(identity, step.auth) ? opts.resolveAuth?.(identity) : auth;
|
|
4421
|
+
if (identity && auth && expected && hasSuccessCheck(expected) && dormant) {
|
|
4422
|
+
const started = Date.now();
|
|
4423
|
+
if ((await confirmSignedIn(session.page, expected, SIGNED_IN_CHECK_MS)).ok) {
|
|
4424
|
+
const provided = {
|
|
4425
|
+
stepId: step.id,
|
|
4426
|
+
name: `Sign in as ${expected.accountName}`,
|
|
4427
|
+
type: step.type,
|
|
4428
|
+
value: null,
|
|
4429
|
+
secret: false,
|
|
4430
|
+
status: "skipped",
|
|
4431
|
+
detail: `${opts.actors.nameOf(opts.actors.currentId)} already starts signed in as ${expected.accountName}`,
|
|
4432
|
+
failureKind: null,
|
|
4433
|
+
durationMs: Date.now() - started,
|
|
4434
|
+
checks: []
|
|
4435
|
+
};
|
|
4436
|
+
results.push(provided);
|
|
4437
|
+
onStepResult?.(provided);
|
|
4438
|
+
continue;
|
|
4439
|
+
}
|
|
4440
|
+
if (!sameIdentity(identity, step.auth)) {
|
|
4441
|
+
const started2 = Date.now();
|
|
4442
|
+
const who = opts.actors.currentId;
|
|
4443
|
+
let recovered = null;
|
|
4444
|
+
try {
|
|
4445
|
+
await opts.actors.prepareAgain(who);
|
|
4446
|
+
session = opts.actors.current;
|
|
4447
|
+
({ page, pages, config, artifacts } = session);
|
|
4448
|
+
if (!(await confirmSignedIn(session.page, expected, SIGNED_IN_CHECK_MS)).ok) {
|
|
4449
|
+
recovered = "signing them in again did not take";
|
|
4450
|
+
}
|
|
4451
|
+
} catch (failure) {
|
|
4452
|
+
recovered = firstLine2(failure);
|
|
4453
|
+
}
|
|
4454
|
+
const outcome = {
|
|
4455
|
+
stepId: step.id,
|
|
4456
|
+
name: `Sign in as ${expected.accountName}`,
|
|
4457
|
+
type: step.type,
|
|
4458
|
+
value: null,
|
|
4459
|
+
secret: false,
|
|
4460
|
+
status: recovered ? "failed" : "skipped",
|
|
4461
|
+
detail: recovered ? `this row uses ${opts.actors.nameOf(who)}'s own sign-in rather than its own, and ${recovered}` : `${opts.actors.nameOf(who)} was signed in again as ${expected.accountName}`,
|
|
4462
|
+
failureKind: recovered ? "unknown" : null,
|
|
4463
|
+
durationMs: Date.now() - started2,
|
|
4464
|
+
checks: []
|
|
4465
|
+
};
|
|
4466
|
+
results.push(outcome);
|
|
4467
|
+
onStepResult?.(outcome);
|
|
4468
|
+
if (recovered) {
|
|
4469
|
+
status2 = "failed";
|
|
4470
|
+
error = `${outcome.name}: ${outcome.detail}`;
|
|
4471
|
+
break;
|
|
4472
|
+
}
|
|
4473
|
+
continue;
|
|
4474
|
+
}
|
|
4475
|
+
}
|
|
4476
|
+
}
|
|
4153
4477
|
if ((step.type === "signIn" || step.type === "signOut") && !step.disabled) {
|
|
4154
4478
|
const started = Date.now();
|
|
4155
4479
|
const outcome = await performSignIn(session, step, opts);
|
|
@@ -4217,7 +4541,13 @@ async function runStepsIn(session, steps, opts) {
|
|
|
4217
4541
|
baseUrl: opts.baseUrl,
|
|
4218
4542
|
...opts.actors ? { actors: opts.actors } : {},
|
|
4219
4543
|
timeoutMs: config.timeoutMs,
|
|
4220
|
-
navigationTimeoutMs: config.navigationTimeoutMs
|
|
4544
|
+
navigationTimeoutMs: config.navigationTimeoutMs,
|
|
4545
|
+
// SAID OUT LOUD, on a step that passed. The element was found, but
|
|
4546
|
+
// not the way this step says it should be — something has moved,
|
|
4547
|
+
// and the run is the only place anybody would learn it.
|
|
4548
|
+
onFallback: (_step, foundBy) => {
|
|
4549
|
+
result.foundBy = foundBy;
|
|
4550
|
+
}
|
|
4221
4551
|
});
|
|
4222
4552
|
for (const a of step.proposals ?? []) {
|
|
4223
4553
|
if (a.enabled === false) continue;
|
|
@@ -4424,8 +4754,9 @@ async function executeStep(main, input, options = {}) {
|
|
|
4424
4754
|
if (step.scroll && step.type !== "scroll") {
|
|
4425
4755
|
await applyScroll(page, step, Math.min(2e3, remaining()));
|
|
4426
4756
|
}
|
|
4427
|
-
const
|
|
4428
|
-
|
|
4757
|
+
const target = await ensureTarget(page, step, remaining());
|
|
4758
|
+
const loc = target.loc;
|
|
4759
|
+
if (target.foundBy) options.onFallback?.(step, target.foundBy);
|
|
4429
4760
|
page.setDefaultTimeout(remaining());
|
|
4430
4761
|
await applyAction(loc, step, value, { resolveFile: options.resolveFile, dropTarget: await locateDrop(page, step) });
|
|
4431
4762
|
};
|
|
@@ -4477,21 +4808,36 @@ async function applyScroll(page, step, timeout) {
|
|
|
4477
4808
|
} catch {
|
|
4478
4809
|
}
|
|
4479
4810
|
}
|
|
4480
|
-
async function ensureTarget(page,
|
|
4811
|
+
async function ensureTarget(page, step, timeout) {
|
|
4812
|
+
const candidates = await locateCandidates(page, step);
|
|
4813
|
+
const described = describeLocators(step);
|
|
4814
|
+
const primary = candidates[0];
|
|
4481
4815
|
const firstWait = Math.max(500, Math.floor(timeout * 0.45));
|
|
4482
4816
|
const recovery = Math.max(1e3, Math.floor(timeout * 0.35));
|
|
4483
4817
|
try {
|
|
4484
|
-
await
|
|
4485
|
-
return;
|
|
4818
|
+
await primary.waitFor({ state: "attached", timeout: firstWait });
|
|
4819
|
+
return { loc: primary };
|
|
4486
4820
|
} catch (error) {
|
|
4487
4821
|
if (/strict mode violation/.test(String(error))) {
|
|
4488
4822
|
throw Object.assign(error, { kind: "ambiguous" });
|
|
4489
4823
|
}
|
|
4490
4824
|
}
|
|
4491
|
-
|
|
4825
|
+
for (let at = 1; at < candidates.length; at += 1) {
|
|
4826
|
+
const found = await candidates[at].count().catch(() => 0);
|
|
4827
|
+
if (found === 1) return { loc: candidates[at], foundBy: described[at] ?? "another locator" };
|
|
4828
|
+
if (found > 1) {
|
|
4829
|
+
throw Object.assign(
|
|
4830
|
+
new Error(
|
|
4831
|
+
`"${step.name}" could not be found the way this step records it, and ${found} elements match ${described[at]} \u2014 narrow it in the editor`
|
|
4832
|
+
),
|
|
4833
|
+
{ kind: "ambiguous" }
|
|
4834
|
+
);
|
|
4835
|
+
}
|
|
4836
|
+
}
|
|
4837
|
+
if (await scrollUntilFound(page, primary, recovery)) return { loc: primary };
|
|
4492
4838
|
throw Object.assign(
|
|
4493
4839
|
new Error(
|
|
4494
|
-
`no element named "${step.name}" \u2014 nothing on this page matches the target or its recorded scope`
|
|
4840
|
+
candidates.length > 1 ? `no element named "${step.name}" \u2014 none of the ${candidates.length} ways this step records it match anything on the page` : `no element named "${step.name}" \u2014 nothing on this page matches the target or its recorded scope`
|
|
4495
4841
|
),
|
|
4496
4842
|
{ kind: "not_found" }
|
|
4497
4843
|
);
|
|
@@ -4533,6 +4879,7 @@ async function replayCase(opts) {
|
|
|
4533
4879
|
const outcomes = [];
|
|
4534
4880
|
let status2 = "passed";
|
|
4535
4881
|
let error = null;
|
|
4882
|
+
const lanes = sessionLanes(occurrences, opts.defaultActorId);
|
|
4536
4883
|
try {
|
|
4537
4884
|
for (const [index, occurrence] of occurrences.entries()) {
|
|
4538
4885
|
if (status2 !== "passed" || opts.signal?.aborted) {
|
|
@@ -4555,9 +4902,15 @@ async function replayCase(opts) {
|
|
|
4555
4902
|
opts.onOccurrenceResult?.(skipped);
|
|
4556
4903
|
continue;
|
|
4557
4904
|
}
|
|
4558
|
-
const navigates = occurrence
|
|
4905
|
+
const navigates = navigatesFirst(occurrence);
|
|
4906
|
+
const who = occurrence.actorId ?? opts.defaultActorId ?? "default";
|
|
4907
|
+
const entry = resolveStartUrl(occurrence.baseUrl ?? baseUrl, occurrence.startPath ?? "/");
|
|
4908
|
+
const plan = lanes[index];
|
|
4559
4909
|
try {
|
|
4560
|
-
|
|
4910
|
+
if (plan.opens && startOf(occurrence) === "fresh") await actors.restart(who, entry);
|
|
4911
|
+
else if (plan.opens) await actors.select(who, entry);
|
|
4912
|
+
else if (actors.currentId !== who && !actors.has(who)) await actors.adopt("default", who, entry);
|
|
4913
|
+
else await actors.select(who, entry);
|
|
4561
4914
|
} catch (failure) {
|
|
4562
4915
|
status2 = "failed";
|
|
4563
4916
|
error = failure.message;
|
|
@@ -4570,6 +4923,7 @@ async function replayCase(opts) {
|
|
|
4570
4923
|
baseUrl: occurrence.baseUrl ?? baseUrl,
|
|
4571
4924
|
...opts.resolveAuth ? { resolveAuth: opts.resolveAuth } : {},
|
|
4572
4925
|
stepOffset: outcomes.reduce((count, previous) => count + previous.stepsTotal, 0),
|
|
4926
|
+
...occurrence.ownSignIn ? { ownSignIn: occurrence.ownSignIn } : {},
|
|
4573
4927
|
...opts.resolveSecret === void 0 ? {} : { resolveSecret: opts.resolveSecret },
|
|
4574
4928
|
...opts.resolveFile === void 0 ? {} : { resolveFile: opts.resolveFile },
|
|
4575
4929
|
...opts.evidenceDir === void 0 ? {} : { evidenceDir: opts.evidenceDir },
|
|
@@ -4833,6 +5187,7 @@ async function startRecordingSession(opts) {
|
|
|
4833
5187
|
...payload.frame?.length ? { frame: payload.frame } : {},
|
|
4834
5188
|
...payload.to ? { to: payload.to } : {},
|
|
4835
5189
|
...payload.locator ? { locator: payload.locator } : {},
|
|
5190
|
+
...payload.candidates?.length ? { candidates: payload.candidates } : {},
|
|
4836
5191
|
...payload.values ? { values: payload.values } : {},
|
|
4837
5192
|
...payload.warning ? { warning: payload.warning } : {},
|
|
4838
5193
|
...payload.pageId ? { pageId: payload.pageId } : {},
|
|
@@ -5045,6 +5400,7 @@ async function startRecordingSession(opts) {
|
|
|
5045
5400
|
...parsed.target?.scopeRole ? { scopeRole: parsed.target.scopeRole } : {},
|
|
5046
5401
|
...parsed.target?.frame?.length ? { frame: parsed.target.frame } : {},
|
|
5047
5402
|
...parsed.target?.locator ? { locator: parsed.target.locator } : {},
|
|
5403
|
+
...parsed.target?.candidates?.length ? { candidates: parsed.target.candidates } : {},
|
|
5048
5404
|
...parsed.target?.pageId ? { pageId: parsed.target.pageId } : {},
|
|
5049
5405
|
value: "",
|
|
5050
5406
|
secret: false,
|
|
@@ -5676,7 +6032,7 @@ async function startDebugSession(opts) {
|
|
|
5676
6032
|
return session;
|
|
5677
6033
|
}
|
|
5678
6034
|
|
|
5679
|
-
// ../results/dist/chunk-
|
|
6035
|
+
// ../results/dist/chunk-LYCTVHPR.js
|
|
5680
6036
|
import { z as z5 } from "zod";
|
|
5681
6037
|
var RunStatusSchema = z5.enum([
|
|
5682
6038
|
"queued",
|
|
@@ -5744,6 +6100,13 @@ var RunStepSchema = z5.object({
|
|
|
5744
6100
|
status: z5.enum(["passed", "failed", "skipped"]),
|
|
5745
6101
|
failureKind: FailureKindSchema.nullable(),
|
|
5746
6102
|
detail: z5.string().nullable(),
|
|
6103
|
+
/** WHICH WAY FOUND IT, when the first way did not.
|
|
6104
|
+
*
|
|
6105
|
+
* A step carries several routes to its element and takes the first that
|
|
6106
|
+
* matches. Passing on a later one is not a failure, but it does mean the
|
|
6107
|
+
* page has moved under the step — and a suite that repairs itself quietly
|
|
6108
|
+
* drifts away from the application it tests. Null is the ordinary case. */
|
|
6109
|
+
foundBy: z5.string().nullable().default(null),
|
|
5747
6110
|
durationMs: z5.number().int().nonnegative().nullable(),
|
|
5748
6111
|
/** Where the browser was when this step ran. Recorded on failure, and
|
|
5749
6112
|
* frequently the whole answer — the element is missing because a step
|
|
@@ -6004,6 +6367,16 @@ var MIGRATIONS2 = [
|
|
|
6004
6367
|
`
|
|
6005
6368
|
ALTER TABLE run_step ADD COLUMN value TEXT;
|
|
6006
6369
|
ALTER TABLE run_step ADD COLUMN secret INTEGER NOT NULL DEFAULT 0;
|
|
6370
|
+
`,
|
|
6371
|
+
// 5 — a step that found its element some way other than the first it tried.
|
|
6372
|
+
//
|
|
6373
|
+
// Nullable, and NULL is the ordinary case: the step was found the way it
|
|
6374
|
+
// says it should be. It is recorded because the alternative is a suite that
|
|
6375
|
+
// repairs itself quietly and drifts away from the application it tests —
|
|
6376
|
+
// and because "which steps are limping?" is a question about history, not
|
|
6377
|
+
// about the run somebody happens to be watching.
|
|
6378
|
+
`
|
|
6379
|
+
ALTER TABLE run_step ADD COLUMN found_by TEXT;
|
|
6007
6380
|
`
|
|
6008
6381
|
];
|
|
6009
6382
|
var SCHEMA_VERSION = MIGRATIONS2.length;
|
|
@@ -6216,8 +6589,8 @@ var SqliteStorage = class {
|
|
|
6216
6589
|
this.handle.prepare(
|
|
6217
6590
|
`INSERT INTO run_step (id, run_task_id, ordinal, step_id, action, target_role,
|
|
6218
6591
|
target_name, target_scope, status, failure_kind, detail, duration_ms,
|
|
6219
|
-
page_url, page_title, value, secret)
|
|
6220
|
-
VALUES (
|
|
6592
|
+
page_url, page_title, value, secret, found_by)
|
|
6593
|
+
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)`
|
|
6221
6594
|
).run(
|
|
6222
6595
|
id,
|
|
6223
6596
|
taskId,
|
|
@@ -6235,7 +6608,8 @@ var SqliteStorage = class {
|
|
|
6235
6608
|
input.pageTitle ?? null,
|
|
6236
6609
|
// Never the plaintext. A secret arrives here already withheld.
|
|
6237
6610
|
input.secret ? null : input.value ?? null,
|
|
6238
|
-
b(input.secret ?? false)
|
|
6611
|
+
b(input.secret ?? false),
|
|
6612
|
+
input.foundBy ?? null
|
|
6239
6613
|
);
|
|
6240
6614
|
const stmt = this.handle.prepare(
|
|
6241
6615
|
`INSERT INTO run_check (id, run_step_id, assertion_id, kind, expected, actual, ok, soft, detail)
|
|
@@ -6379,6 +6753,7 @@ var SqliteStorage = class {
|
|
|
6379
6753
|
durationMs: n(st.duration_ms),
|
|
6380
6754
|
pageUrl: s(st.page_url),
|
|
6381
6755
|
pageTitle: s(st.page_title),
|
|
6756
|
+
foundBy: s(st.found_by),
|
|
6382
6757
|
checks: checksByStep.get(String(st.id)) ?? []
|
|
6383
6758
|
});
|
|
6384
6759
|
stepsByTask.set(String(st.run_task_id), list);
|
|
@@ -7519,6 +7894,9 @@ function toStepRow(ordinal, result, step) {
|
|
|
7519
7894
|
durationMs: result.durationMs,
|
|
7520
7895
|
pageUrl: result.evidence?.url ?? null,
|
|
7521
7896
|
pageTitle: result.evidence?.title ?? null,
|
|
7897
|
+
// Kept, because "which steps are limping?" is a question about history
|
|
7898
|
+
// rather than about the run somebody happens to be watching.
|
|
7899
|
+
foundBy: result.foundBy ?? null,
|
|
7522
7900
|
checks
|
|
7523
7901
|
};
|
|
7524
7902
|
}
|
|
@@ -7585,7 +7963,7 @@ async function executeCase(opts) {
|
|
|
7585
7963
|
let outcome;
|
|
7586
7964
|
try {
|
|
7587
7965
|
outcome = await replayCase({
|
|
7588
|
-
occurrences:
|
|
7966
|
+
occurrences: testCase.occurrences,
|
|
7589
7967
|
...testCase.actors ? { actors: testCase.actors } : {},
|
|
7590
7968
|
...testCase.defaultActorId ? { defaultActorId: testCase.defaultActorId } : {},
|
|
7591
7969
|
resolveAuth: authenticationResolver(opts.authentications),
|
package/dist/cli.js
CHANGED
package/dist/engine.js
CHANGED
package/package.json
CHANGED