twd-js 1.6.3 → 1.6.4
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/bundled.es.js +1 -1
- package/dist/index.cjs.js +1 -1
- package/dist/index.d.ts +624 -0
- package/dist/index.es.js +1 -1
- package/dist/mock-sw.js +1 -1
- package/dist/runner-ci.d.ts +27 -0
- package/dist/runner.d.ts +62 -0
- package/dist/ui.d.ts +9 -0
- package/dist/vite-plugin.d.ts +66 -0
- package/package.json +2 -2
- /package/dist/{src/bundled.d.ts → bundled.d.ts} +0 -0
package/dist/bundled.es.js
CHANGED
|
@@ -1134,7 +1134,7 @@ var Pt = (e) => {
|
|
|
1134
1134
|
}
|
|
1135
1135
|
};
|
|
1136
1136
|
window.__testRunner = Ze;
|
|
1137
|
-
var me = "1.6.
|
|
1137
|
+
var me = "1.6.4", Ot = () => typeof window < "u" ? (window.__TWD_MOCK_STATE__ || (window.__TWD_MOCK_STATE__ = {
|
|
1138
1138
|
rules: [],
|
|
1139
1139
|
counts: {}
|
|
1140
1140
|
}), window.__TWD_MOCK_STATE__) : {
|
package/dist/index.cjs.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});var ql=Object.create,lr=Object.defineProperty,Rl=Object.getOwnPropertyDescriptor,Tl=Object.getOwnPropertyNames,Sl=Object.getPrototypeOf,xl=Object.prototype.hasOwnProperty,P=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports),Ol=(e,t)=>{let r={};for(var n in e)lr(r,n,{get:e[n],enumerable:!0});return t||lr(r,Symbol.toStringTag,{value:"Module"}),r},Ml=(e,t,r,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(var a=Tl(t),o=0,i=a.length,s;o<i;o++)s=a[o],!xl.call(e,s)&&s!==r&&lr(e,s,{get:(u=>t[u]).bind(null,s),enumerable:!(n=Rl(t,s))||n.enumerable});return e},In=(e,t,r)=>(r=e!=null?ql(Sl(e)):{},Ml(t||!e||!e.__esModule?lr(r,"default",{value:e,enumerable:!0}):r,e));const Mt=require("./runner-Dr_mUWED.cjs"),tn=require("./componentMocks-CwMyWCu5.cjs");let re=require("react"),j=require("react/jsx-runtime");var Al=(e,t,r=2e3,n=50)=>new Promise((a,o)=>{const i=Date.now(),s=()=>{const u=t();if(u)return a(u);if(Date.now()-i>r)return o(new Error(`Timeout waiting for element ${e}`));setTimeout(s,n)};s()}),Il=(e,t,r=2e3,n=50)=>new Promise((a,o)=>{const i=Date.now(),s=()=>{const u=t();if(u&&u.length>0)return a(Array.from(u));if(Date.now()-i>r)return o(new Error(`Timeout waiting for elements ${e}`));setTimeout(s,n)};s()}),ur=e=>new Promise(t=>setTimeout(t,e)),rn=(e,t,r,n)=>{if(!e&&!t)throw new Error(n);if(e&&t)throw new Error(n.replace("to be","to not be").replace("to have","to not have").replace("to contain","to not contain"));return r};function Nl(e){if(!e.isConnected)return!1;let t=e;for(;t;){const r=getComputedStyle(t);if(r.display==="none"||r.visibility==="hidden"||r.visibility==="collapse")return!1;t=t.parentElement}return!0}var kl={"have.text":{positive:{pass:e=>`Assertion passed: Text is exactly "${e[0]}"`,fail:(e,t)=>`Assertion failed: Expected text to be "${e[0]}", but got "${t}"`},negative:{pass:e=>`Assertion passed: Text is not exactly "${e[0]}"`,fail:(e,t)=>`Assertion failed: Expected text to not be "${e[0]}", but got "${t}"`}},"contain.text":{positive:{pass:e=>`Assertion passed: Text contains "${e[0]}"`,fail:(e,t)=>`Assertion failed: Expected text to contain "${e[0]}", but got "${t}"`},negative:{pass:e=>`Assertion passed: Text does not contain "${e[0]}"`,fail:(e,t)=>`Assertion failed: Expected text to not contain "${e[0]}", but got "${t}"`}},"be.empty":{positive:{pass:()=>"Assertion passed: Text is empty",fail:(e,t)=>`Assertion failed: Expected text to be empty, but got "${t}"`},negative:{pass:()=>"Assertion passed: Text is not empty",fail:(e,t)=>`Assertion failed: Expected text to not be empty, but got "${t}"`}},"have.attr":{positive:{pass:e=>`Assertion passed: Attribute "${e[0]}" is "${e[1]}"`,fail:(e,t)=>`Assertion failed: Expected attribute "${e[0]}" to be "${e[1]}", but got "${t}"`},negative:{pass:e=>`Assertion passed: Attribute "${e[0]}" is not "${e[1]}"`,fail:(e,t)=>`Assertion failed: Expected attribute "${e[0]}" to not be "${e[1]}", but got "${t}"`}},"have.value":{positive:{pass:e=>`Assertion passed: Value is "${e[0]}"`,fail:(e,t)=>`Assertion failed: Expected value to be "${e[0]}", but got "${t}"`},negative:{pass:e=>`Assertion passed: Value is not "${e[0]}"`,fail:(e,t)=>`Assertion failed: Expected value to not be "${e[0]}", but got "${t}"`}},"be.disabled":{positive:{pass:()=>"Assertion passed: Element is disabled",fail:()=>"Assertion failed: Expected element to be disabled"},negative:{pass:()=>"Assertion passed: Element is not disabled",fail:()=>"Assertion failed: Expected element to not be disabled"}},"be.enabled":{positive:{pass:()=>"Assertion passed: Element is enabled",fail:()=>"Assertion failed: Expected element to be enabled"},negative:{pass:()=>"Assertion passed: Element is not enabled",fail:()=>"Assertion failed: Expected element to not be enabled"}},"be.checked":{positive:{pass:()=>"Assertion passed: Element is checked",fail:()=>"Assertion failed: Expected element to be checked"},negative:{pass:()=>"Assertion passed: Element is not checked",fail:()=>"Assertion failed: Expected element to not be checked"}},"be.selected":{positive:{pass:()=>"Assertion passed: Element is selected",fail:()=>"Assertion failed: Expected element to be selected"},negative:{pass:()=>"Assertion passed: Element is not selected",fail:()=>"Assertion failed: Expected element to not be selected"}},"be.focused":{positive:{pass:()=>"Assertion passed: Element is focused",fail:()=>"Assertion failed: Expected element to be focused"},negative:{pass:()=>"Assertion passed: Element is not focused",fail:()=>"Assertion failed: Expected element to not be focused"}},"be.visible":{positive:{pass:()=>"Assertion passed: Element is visible",fail:()=>"Assertion failed: Expected element to be visible"},negative:{pass:()=>"Assertion passed: Element is not visible",fail:()=>"Assertion failed: Expected element to not be visible"}},"have.class":{positive:{pass:e=>`Assertion passed: Element has class "${e[0]}"`,fail:e=>`Assertion failed: Expected element to have class "${e[0]}"`},negative:{pass:e=>`Assertion passed: Element does not have class "${e[0]}"`,fail:e=>`Assertion failed: Expected element to not have class "${e[0]}"`}}},zr=(e,t,...r)=>{const n=t.startsWith("not."),a=n?t.slice(4):t,o=(e.textContent||"").trim(),i=kl[a];if(!i)throw new Error(`Unknown assertion: ${a}`);const s=n?"negative":"positive";let u,l;switch(a){case"have.text":u=o===r[0],l=o;break;case"contain.text":u=o.includes(r[0]),l=o;break;case"be.empty":u=o.length===0,l=o;break;case"have.attr":l=e.getAttribute(r[0]),u=l===r[1];break;case"have.value":l=e.value,u=l===r[0];break;case"be.disabled":u=e.disabled===!0;break;case"be.enabled":u=e.disabled===!1;break;case"be.checked":u=e.checked===!0;break;case"be.selected":u=e.selected===!0;break;case"be.focused":u=document.activeElement===e;break;case"be.visible":u=Nl(e);break;case"have.class":u=e.classList.contains(r[0]);break;default:throw new Error(`Unknown assertion: ${a}`)}return rn(u,n,i[s].pass(r),i[s].fail(r,l))},ce=e=>{const t=Mt.handlers.size?Array.from(Mt.handlers.values()).find(r=>r.status==="running"):null;t&&t.logs?.push(e)},Rr="1.6.
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});var ql=Object.create,lr=Object.defineProperty,Rl=Object.getOwnPropertyDescriptor,Tl=Object.getOwnPropertyNames,Sl=Object.getPrototypeOf,xl=Object.prototype.hasOwnProperty,P=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports),Ol=(e,t)=>{let r={};for(var n in e)lr(r,n,{get:e[n],enumerable:!0});return t||lr(r,Symbol.toStringTag,{value:"Module"}),r},Ml=(e,t,r,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(var a=Tl(t),o=0,i=a.length,s;o<i;o++)s=a[o],!xl.call(e,s)&&s!==r&&lr(e,s,{get:(u=>t[u]).bind(null,s),enumerable:!(n=Rl(t,s))||n.enumerable});return e},In=(e,t,r)=>(r=e!=null?ql(Sl(e)):{},Ml(t||!e||!e.__esModule?lr(r,"default",{value:e,enumerable:!0}):r,e));const Mt=require("./runner-Dr_mUWED.cjs"),tn=require("./componentMocks-CwMyWCu5.cjs");let re=require("react"),j=require("react/jsx-runtime");var Al=(e,t,r=2e3,n=50)=>new Promise((a,o)=>{const i=Date.now(),s=()=>{const u=t();if(u)return a(u);if(Date.now()-i>r)return o(new Error(`Timeout waiting for element ${e}`));setTimeout(s,n)};s()}),Il=(e,t,r=2e3,n=50)=>new Promise((a,o)=>{const i=Date.now(),s=()=>{const u=t();if(u&&u.length>0)return a(Array.from(u));if(Date.now()-i>r)return o(new Error(`Timeout waiting for elements ${e}`));setTimeout(s,n)};s()}),ur=e=>new Promise(t=>setTimeout(t,e)),rn=(e,t,r,n)=>{if(!e&&!t)throw new Error(n);if(e&&t)throw new Error(n.replace("to be","to not be").replace("to have","to not have").replace("to contain","to not contain"));return r};function Nl(e){if(!e.isConnected)return!1;let t=e;for(;t;){const r=getComputedStyle(t);if(r.display==="none"||r.visibility==="hidden"||r.visibility==="collapse")return!1;t=t.parentElement}return!0}var kl={"have.text":{positive:{pass:e=>`Assertion passed: Text is exactly "${e[0]}"`,fail:(e,t)=>`Assertion failed: Expected text to be "${e[0]}", but got "${t}"`},negative:{pass:e=>`Assertion passed: Text is not exactly "${e[0]}"`,fail:(e,t)=>`Assertion failed: Expected text to not be "${e[0]}", but got "${t}"`}},"contain.text":{positive:{pass:e=>`Assertion passed: Text contains "${e[0]}"`,fail:(e,t)=>`Assertion failed: Expected text to contain "${e[0]}", but got "${t}"`},negative:{pass:e=>`Assertion passed: Text does not contain "${e[0]}"`,fail:(e,t)=>`Assertion failed: Expected text to not contain "${e[0]}", but got "${t}"`}},"be.empty":{positive:{pass:()=>"Assertion passed: Text is empty",fail:(e,t)=>`Assertion failed: Expected text to be empty, but got "${t}"`},negative:{pass:()=>"Assertion passed: Text is not empty",fail:(e,t)=>`Assertion failed: Expected text to not be empty, but got "${t}"`}},"have.attr":{positive:{pass:e=>`Assertion passed: Attribute "${e[0]}" is "${e[1]}"`,fail:(e,t)=>`Assertion failed: Expected attribute "${e[0]}" to be "${e[1]}", but got "${t}"`},negative:{pass:e=>`Assertion passed: Attribute "${e[0]}" is not "${e[1]}"`,fail:(e,t)=>`Assertion failed: Expected attribute "${e[0]}" to not be "${e[1]}", but got "${t}"`}},"have.value":{positive:{pass:e=>`Assertion passed: Value is "${e[0]}"`,fail:(e,t)=>`Assertion failed: Expected value to be "${e[0]}", but got "${t}"`},negative:{pass:e=>`Assertion passed: Value is not "${e[0]}"`,fail:(e,t)=>`Assertion failed: Expected value to not be "${e[0]}", but got "${t}"`}},"be.disabled":{positive:{pass:()=>"Assertion passed: Element is disabled",fail:()=>"Assertion failed: Expected element to be disabled"},negative:{pass:()=>"Assertion passed: Element is not disabled",fail:()=>"Assertion failed: Expected element to not be disabled"}},"be.enabled":{positive:{pass:()=>"Assertion passed: Element is enabled",fail:()=>"Assertion failed: Expected element to be enabled"},negative:{pass:()=>"Assertion passed: Element is not enabled",fail:()=>"Assertion failed: Expected element to not be enabled"}},"be.checked":{positive:{pass:()=>"Assertion passed: Element is checked",fail:()=>"Assertion failed: Expected element to be checked"},negative:{pass:()=>"Assertion passed: Element is not checked",fail:()=>"Assertion failed: Expected element to not be checked"}},"be.selected":{positive:{pass:()=>"Assertion passed: Element is selected",fail:()=>"Assertion failed: Expected element to be selected"},negative:{pass:()=>"Assertion passed: Element is not selected",fail:()=>"Assertion failed: Expected element to not be selected"}},"be.focused":{positive:{pass:()=>"Assertion passed: Element is focused",fail:()=>"Assertion failed: Expected element to be focused"},negative:{pass:()=>"Assertion passed: Element is not focused",fail:()=>"Assertion failed: Expected element to not be focused"}},"be.visible":{positive:{pass:()=>"Assertion passed: Element is visible",fail:()=>"Assertion failed: Expected element to be visible"},negative:{pass:()=>"Assertion passed: Element is not visible",fail:()=>"Assertion failed: Expected element to not be visible"}},"have.class":{positive:{pass:e=>`Assertion passed: Element has class "${e[0]}"`,fail:e=>`Assertion failed: Expected element to have class "${e[0]}"`},negative:{pass:e=>`Assertion passed: Element does not have class "${e[0]}"`,fail:e=>`Assertion failed: Expected element to not have class "${e[0]}"`}}},zr=(e,t,...r)=>{const n=t.startsWith("not."),a=n?t.slice(4):t,o=(e.textContent||"").trim(),i=kl[a];if(!i)throw new Error(`Unknown assertion: ${a}`);const s=n?"negative":"positive";let u,l;switch(a){case"have.text":u=o===r[0],l=o;break;case"contain.text":u=o.includes(r[0]),l=o;break;case"be.empty":u=o.length===0,l=o;break;case"have.attr":l=e.getAttribute(r[0]),u=l===r[1];break;case"have.value":l=e.value,u=l===r[0];break;case"be.disabled":u=e.disabled===!0;break;case"be.enabled":u=e.disabled===!1;break;case"be.checked":u=e.checked===!0;break;case"be.selected":u=e.selected===!0;break;case"be.focused":u=document.activeElement===e;break;case"be.visible":u=Nl(e);break;case"have.class":u=e.classList.contains(r[0]);break;default:throw new Error(`Unknown assertion: ${a}`)}return rn(u,n,i[s].pass(r),i[s].fail(r,l))},ce=e=>{const t=Mt.handlers.size?Array.from(Mt.handlers.values()).find(r=>r.status==="running"):null;t&&t.logs?.push(e)},Rr="1.6.4",jl=()=>typeof window<"u"?(window.__TWD_MOCK_STATE__||(window.__TWD_MOCK_STATE__={rules:[],counts:{}}),window.__TWD_MOCK_STATE__):{rules:[],counts:{}},et=jl(),Ce=et.rules,Dl=100,ka=!1,Ll=async e=>{if(ka){console.warn("[TWD] Request mocking already initialized");return}if("serviceWorker"in navigator){ka=!0;const t=e??"/mock-sw.js";await navigator.serviceWorker.register(`${t}?v=${Rr}`),navigator.serviceWorker.controller||await new Promise(r=>{navigator.serviceWorker.addEventListener("controllerchange",r,{once:!0})}),navigator.serviceWorker.addEventListener("message",r=>{if(r.data?.type==="EXECUTED"){const{alias:n,request:a,hitCount:o}=r.data,i=Ce.find(s=>s.alias===n);i&&(i.executed=!0,i.request=a,i.hitCount=o),et.counts[n]=o??(et.counts[n]??0)+1}})}},Bl=async(e,t)=>{const r={alias:e,...t,executed:!1},n=Ce.findIndex(a=>a.alias===e);n!==-1?Ce[n]=r:Ce.push(r),typeof window.__twdCollectMock=="function"&&window.__twdCollectMock({alias:e,url:String(t.url),method:t.method,status:t.status||200,response:t.response,urlRegex:t.urlRegex||!1}),navigator.serviceWorker.controller?.postMessage({type:"ADD_RULE",rule:r,version:Rr}),await ur(Dl),await Promise.resolve()},$l=async e=>await Promise.all(e.map(t=>To(t))),To=async(e,t=10,r=100)=>{const n=Ce.find(u=>u.alias===e);if(!n)throw new Error(`Rule ${e} not found`);for(let u=0;u<t;u++){const l=Ce.find(c=>c.alias===e&&c.executed);if(l)return Promise.resolve(l);u<t-1&&await new Promise(c=>setTimeout(c,r))}const a=t*r,o=Ce.filter(u=>u.executed).map(u=>`${u.alias} (${u.method} ${u.url})`).join(", "),i=Ce.filter(u=>!u.executed).map(u=>`${u.alias} (${u.method} ${u.url})`).join(", "),s=[`Rule "${e}" was not executed within ${a}ms.`,` Expected: ${n.method} ${n.url}`,` Executed rules: ${o||"none"}`,` Not executed rules: ${i||"none"}`].join(`
|
|
2
2
|
`);throw console.log(s),new Error(s)},So=()=>Ce,xo=()=>{navigator.serviceWorker.controller?.postMessage({type:"CLEAR_RULES",version:Rr}),Ce.length=0;for(const e in et.counts)delete et.counts[e]},Fl=e=>et.counts[e]??0,Ul=()=>({...et.counts}),zl=(e,t)=>{const r=e.startsWith("not.");switch(r?e.slice(4):e){case"eq":return rn(window.location.href===t,r,`Assertion passed: URL is ${t}`,`Assertion failed: Expected URL to be ${t}, but got ${window.location.href}`);case"contain.url":return rn(window.location.href.includes(t),r,`Assertion passed: URL contains ${t}`,`Assertion failed: Expected URL to contain ${t}, but got ${window.location.href}`);default:throw new Error(`Unknown assertion: ${e}`)}},Vl=()=>({location:window.location,should:async(e,t,r=5)=>{let n="",a;for(let o=0;o<r;o++)try{n=zl(e,t),ce(n);break}catch(i){await new Promise(s=>setTimeout(s,100)),a=i}if(a)throw a;return n}}),Hl=100,Wl=async(e,t)=>{if(ce(`visit("${e}")`),window.location.pathname===e||t){const r=`/__dummy_${Math.random().toString(36).slice(2)}`;window.history.pushState({},"",r),window.dispatchEvent(new PopStateEvent("popstate")),await ur(10)}window.history.pushState({},"",e),window.dispatchEvent(new PopStateEvent("popstate")),await ur(Hl)},nn="twd-viewport-styles",an="twd-viewport-iframe",fe=null,lt=null,Kl=()=>{if(fe)return;const{style:e}=document.body;fe={maxWidth:e.maxWidth,maxHeight:e.maxHeight,minHeight:e.minHeight,overflow:e.overflow,margin:e.margin,boxSizing:e.boxSizing,boxShadow:e.boxShadow}},Gl=(e,t)=>{let r=document.getElementById(nn);r||(r=document.createElement("style"),r.id=nn,document.head.appendChild(r));const n=t?`${e} × ${t}`:`${e}`;r.textContent=`
|
|
3
3
|
#twd-viewport-badge {
|
|
4
4
|
position: fixed;
|
package/dist/index.d.ts
CHANGED
|
@@ -1 +1,625 @@
|
|
|
1
|
+
import { configure } from '@testing-library/dom';
|
|
2
|
+
import { default as default_2 } from '@testing-library/user-event';
|
|
3
|
+
import { expect } from 'chai';
|
|
4
|
+
import { JSX } from 'react/jsx-runtime';
|
|
5
|
+
import { screen as screen_2 } from '@testing-library/dom';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* All assertion names, including negated ones.
|
|
9
|
+
*/
|
|
10
|
+
declare type AnyAssertion = Negatable_2<AssertionName>;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* All assertion names, including negated ones.
|
|
14
|
+
*/
|
|
15
|
+
declare type AnyURLAssertion = Negatable<URLAssertionName>;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Maps assertion name to its argument tuple.
|
|
19
|
+
*/
|
|
20
|
+
declare type ArgsFor<A extends AnyAssertion> = A extends `not.${infer Base extends AssertionName}` ? AssertionArgs[Base] : A extends AssertionName ? AssertionArgs[A] : never;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Argument types for each assertion.
|
|
24
|
+
*/
|
|
25
|
+
declare type AssertionArgs = {
|
|
26
|
+
"have.text": [expected: string];
|
|
27
|
+
"contain.text": [expected: string];
|
|
28
|
+
"be.empty": [];
|
|
29
|
+
"have.attr": [attr: string, value: string];
|
|
30
|
+
"have.value": [value: string];
|
|
31
|
+
"be.disabled": [];
|
|
32
|
+
"be.enabled": [];
|
|
33
|
+
"be.checked": [];
|
|
34
|
+
"be.selected": [];
|
|
35
|
+
"be.focused": [];
|
|
36
|
+
"be.visible": [];
|
|
37
|
+
"have.class": [className: string];
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Types and interfaces for the TWD testing library.
|
|
42
|
+
*
|
|
43
|
+
* @module twd-types
|
|
44
|
+
*/
|
|
45
|
+
/**
|
|
46
|
+
* All supported assertion names for the `should` function.
|
|
47
|
+
*
|
|
48
|
+
* @example
|
|
49
|
+
* api.should("have.text", "Hello");
|
|
50
|
+
* api.should("be.empty");
|
|
51
|
+
*/
|
|
52
|
+
declare type AssertionName = "have.text" | "contain.text" | "be.empty" | "have.attr" | "have.value" | "be.disabled" | "be.enabled" | "be.checked" | "be.selected" | "be.focused" | "be.visible" | "have.class";
|
|
53
|
+
|
|
54
|
+
export declare const configureScreenDom: typeof configure;
|
|
55
|
+
|
|
56
|
+
export declare const defaultTheme: TWDTheme;
|
|
57
|
+
|
|
58
|
+
export { expect }
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Initialize Vite test loading.
|
|
62
|
+
* @param testModules - The test modules to load.
|
|
63
|
+
* @param component - The React component to render the sidebar.
|
|
64
|
+
* @param createRoot - Function to create a React root.
|
|
65
|
+
* @param theme - Optional theme customization
|
|
66
|
+
* @example
|
|
67
|
+
* ```ts
|
|
68
|
+
* if (import.meta.env.DEV) {
|
|
69
|
+
* const testModules = import.meta.glob("./example.twd.test.ts");
|
|
70
|
+
* const { initTests, TWDSidebar } = await import('twd-js');
|
|
71
|
+
* await initTests(testModules, <TWDSidebar open={true} position="left" />, createRoot, {
|
|
72
|
+
* primary: '#ff0000',
|
|
73
|
+
* background: '#ffffff'
|
|
74
|
+
* });
|
|
75
|
+
* }
|
|
76
|
+
* ```
|
|
77
|
+
*/
|
|
78
|
+
export declare const initTests: (testModules: TestModule, Component: React.ReactNode, createRoot: (el: HTMLElement) => {
|
|
79
|
+
render: (el: React.ReactNode) => void;
|
|
80
|
+
}, theme?: Partial<TWDTheme>) => Promise<void>;
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Injects theme CSS variables into the document
|
|
84
|
+
* This should be called once when the sidebar is initialized
|
|
85
|
+
*/
|
|
86
|
+
export declare function injectTheme(theme?: Partial<TWDTheme>): void;
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Negatable assertion names (e.g., 'not.have.text').
|
|
90
|
+
*/
|
|
91
|
+
declare type Negatable<T extends string> = T | `not.${T}`;
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Negatable assertion names (e.g., 'not.have.text').
|
|
95
|
+
*/
|
|
96
|
+
declare type Negatable_2<T extends string> = T | `not.${T}`;
|
|
97
|
+
|
|
98
|
+
declare interface Options {
|
|
99
|
+
/** HTTP method to match (e.g. `"GET"`, `"POST"`). */
|
|
100
|
+
method: string;
|
|
101
|
+
/** URL string or RegExp to match against the request URL. */
|
|
102
|
+
url: string | RegExp;
|
|
103
|
+
/** The mocked response body returned to the client. */
|
|
104
|
+
response: unknown;
|
|
105
|
+
/** HTTP status code for the mocked response (default: `200`). */
|
|
106
|
+
status?: number;
|
|
107
|
+
/** Headers to include in the mocked response. */
|
|
108
|
+
responseHeaders?: Record<string, string>;
|
|
109
|
+
/** Whether the `url` field should be treated as a regex pattern. */
|
|
110
|
+
urlRegex?: boolean;
|
|
111
|
+
/** Delay in milliseconds before returning the mocked response. */
|
|
112
|
+
delay?: number;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
declare type Rule = {
|
|
116
|
+
/** HTTP method to match (e.g. `"GET"`, `"POST"`). */
|
|
117
|
+
method: string;
|
|
118
|
+
/** URL string or RegExp to match against the request URL. */
|
|
119
|
+
url: string | RegExp;
|
|
120
|
+
/** The mocked response body returned to the client. */
|
|
121
|
+
response: unknown;
|
|
122
|
+
/** Unique identifier for this mock rule, used with `waitForRequest`. */
|
|
123
|
+
alias: string;
|
|
124
|
+
/** Whether this rule has been matched by an actual request. Set automatically by the service worker. */
|
|
125
|
+
executed?: boolean;
|
|
126
|
+
/**
|
|
127
|
+
* The parsed request body sent by the client.
|
|
128
|
+
* For JSON requests this is the parsed object, for form data an object of key/value pairs, for text a string.
|
|
129
|
+
* Access fields directly: `rule.request.email`, **not** `rule.request.body.email`.
|
|
130
|
+
*/
|
|
131
|
+
request?: any;
|
|
132
|
+
/** HTTP status code for the mocked response (default: `200`). */
|
|
133
|
+
status?: number;
|
|
134
|
+
/** Headers to include in the mocked response. */
|
|
135
|
+
responseHeaders?: Record<string, string>;
|
|
136
|
+
/** Whether the `url` field should be treated as a regex pattern. */
|
|
137
|
+
urlRegex?: boolean;
|
|
138
|
+
/** Delay in milliseconds before returning the mocked response. */
|
|
139
|
+
delay?: number;
|
|
140
|
+
/** Number of times this rule has been matched. Updated automatically by the service worker. */
|
|
141
|
+
hitCount?: number;
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
declare type ScreenDom = typeof screen_2;
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* screenDom - Scoped queries that exclude the TWD sidebar
|
|
148
|
+
*
|
|
149
|
+
* Searches only within the main app container (typically #root).
|
|
150
|
+
* Use this for most queries to avoid matching elements in the sidebar.
|
|
151
|
+
*
|
|
152
|
+
* Note: This will NOT find portal-rendered elements (modals, dialogs) that are
|
|
153
|
+
* rendered outside the root container. For portals, use `screenDomGlobal` instead.
|
|
154
|
+
*/
|
|
155
|
+
export declare const screenDom: ScreenDom;
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* screenDomGlobal - Global queries that search the entire document.body
|
|
159
|
+
*
|
|
160
|
+
* Searches all elements in document.body, including portal-rendered elements
|
|
161
|
+
* (modals, dialogs, tooltips, etc.).
|
|
162
|
+
*
|
|
163
|
+
* ⚠️ WARNING: This may also match elements inside the TWD sidebar if your selectors
|
|
164
|
+
* are not specific enough. Use more specific queries (e.g., getByRole with name)
|
|
165
|
+
* to avoid matching sidebar elements.
|
|
166
|
+
*
|
|
167
|
+
* Use this when:
|
|
168
|
+
* - Querying portal-rendered elements (modals, dialogs)
|
|
169
|
+
* - You need to search outside the root container
|
|
170
|
+
*
|
|
171
|
+
* Example:
|
|
172
|
+
* ```ts
|
|
173
|
+
* // For a modal rendered via portal
|
|
174
|
+
* const modal = screenDomGlobal.getByRole('dialog', { name: 'Confirm' });
|
|
175
|
+
* ```
|
|
176
|
+
*/
|
|
177
|
+
export declare const screenDomGlobal: ScreenDom;
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Overloads for the `should` function, for best IntelliSense.
|
|
181
|
+
*
|
|
182
|
+
* @example
|
|
183
|
+
* twd.should("have.text", "Hello");
|
|
184
|
+
* twd.should("be.empty");
|
|
185
|
+
* twd.should("have.class", "active");
|
|
186
|
+
*/
|
|
187
|
+
declare type ShouldFn = {
|
|
188
|
+
(name: "have.text", expected: string): TWDElemAPI;
|
|
189
|
+
(name: "not.have.text", expected: string): TWDElemAPI;
|
|
190
|
+
(name: "contain.text", expected: string): TWDElemAPI;
|
|
191
|
+
(name: "not.contain.text", expected: string): TWDElemAPI;
|
|
192
|
+
(name: "be.empty"): TWDElemAPI;
|
|
193
|
+
(name: "not.be.empty"): TWDElemAPI;
|
|
194
|
+
(name: "have.attr", attr: string, value: string): TWDElemAPI;
|
|
195
|
+
(name: "not.have.attr", attr: string, value: string): TWDElemAPI;
|
|
196
|
+
(name: "have.value", value: string): TWDElemAPI;
|
|
197
|
+
(name: "not.have.value", value: string): TWDElemAPI;
|
|
198
|
+
(name: "be.disabled"): TWDElemAPI;
|
|
199
|
+
(name: "not.be.disabled"): TWDElemAPI;
|
|
200
|
+
(name: "be.enabled"): TWDElemAPI;
|
|
201
|
+
(name: "not.be.enabled"): TWDElemAPI;
|
|
202
|
+
(name: "be.checked"): TWDElemAPI;
|
|
203
|
+
(name: "not.be.checked"): TWDElemAPI;
|
|
204
|
+
(name: "be.selected"): TWDElemAPI;
|
|
205
|
+
(name: "not.be.selected"): TWDElemAPI;
|
|
206
|
+
(name: "be.focused"): TWDElemAPI;
|
|
207
|
+
(name: "not.be.focused"): TWDElemAPI;
|
|
208
|
+
(name: "be.visible"): TWDElemAPI;
|
|
209
|
+
(name: "not.be.visible"): TWDElemAPI;
|
|
210
|
+
(name: "have.class", className: string): TWDElemAPI;
|
|
211
|
+
(name: "not.have.class", className: string): TWDElemAPI;
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* A record of test module paths to their loader functions.
|
|
216
|
+
* Each function returns a promise that resolves when the module is loaded.
|
|
217
|
+
* This is typically used with Vite's `import.meta.glob` to dynamically import test modules.
|
|
218
|
+
* @example
|
|
219
|
+
* ```ts
|
|
220
|
+
* const testModules = {
|
|
221
|
+
* './test1.twd.test.ts': () => import('./test1.twd.test.ts'),
|
|
222
|
+
* './test2.twd.test.ts': () => import('./test2.twd.test.ts'),
|
|
223
|
+
* };
|
|
224
|
+
* ```
|
|
225
|
+
*/
|
|
226
|
+
declare type TestModule = Record<string, () => Promise<unknown>>;
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Mini Cypress-style helpers for DOM testing.
|
|
230
|
+
* @namespace twd
|
|
231
|
+
*/
|
|
232
|
+
export declare const twd: TWDAPI;
|
|
233
|
+
|
|
234
|
+
declare interface TWDAPI {
|
|
235
|
+
/**
|
|
236
|
+
* Finds an element by selector and returns the TWD API for it.
|
|
237
|
+
* @param selector CSS selector
|
|
238
|
+
* @returns {Promise<TWDElemAPI>} The TWD API for the element
|
|
239
|
+
*
|
|
240
|
+
* @example
|
|
241
|
+
* ```ts
|
|
242
|
+
* const btn = await twd.get("button");
|
|
243
|
+
*
|
|
244
|
+
* ```
|
|
245
|
+
*
|
|
246
|
+
*/
|
|
247
|
+
get: (selector: string) => Promise<TWDElemAPI>;
|
|
248
|
+
/**
|
|
249
|
+
* Sets the value of an input element and dispatches an input event. We recommend using this only for range, color, time inputs.
|
|
250
|
+
* @param el The input element
|
|
251
|
+
* @param value The value to set
|
|
252
|
+
*
|
|
253
|
+
* @example
|
|
254
|
+
* ```ts
|
|
255
|
+
* const input = await twd.get("input[type='time']");
|
|
256
|
+
* twd.setInputValue(input.el, "13:30");
|
|
257
|
+
*
|
|
258
|
+
* ```
|
|
259
|
+
*/
|
|
260
|
+
setInputValue: (el: Element, value: string) => void;
|
|
261
|
+
/**
|
|
262
|
+
* Finds multiple elements by selector and returns an array of TWD APIs for them.
|
|
263
|
+
* @param selector CSS selector
|
|
264
|
+
* @returns {Promise<TWDElemAPI[]>} Array of TWD APIs for the elements
|
|
265
|
+
*
|
|
266
|
+
* @example
|
|
267
|
+
* ```ts
|
|
268
|
+
* const items = await twd.getAll(".item");
|
|
269
|
+
* items.at(0).should("be.visible");
|
|
270
|
+
* items.at(1).should("contain.text", "Hello");
|
|
271
|
+
* expect(items).to.have.length(3);
|
|
272
|
+
* ```
|
|
273
|
+
*/
|
|
274
|
+
getAll: (selector: string) => Promise<TWDElemAPI[]>;
|
|
275
|
+
/**
|
|
276
|
+
* Simulates visiting a URL (SPA navigation).
|
|
277
|
+
* @param url The URL to visit
|
|
278
|
+
* @param [reload] Whether to force a reload even if already on the URL (optional)
|
|
279
|
+
*
|
|
280
|
+
* @example
|
|
281
|
+
* ```ts
|
|
282
|
+
* twd.visit("/contact");
|
|
283
|
+
* // visit with reload
|
|
284
|
+
* twd.visit("/contact", true);
|
|
285
|
+
* ```
|
|
286
|
+
*/
|
|
287
|
+
visit: (url: string, reload?: boolean) => Promise<void>;
|
|
288
|
+
/**
|
|
289
|
+
* Mock a network request.
|
|
290
|
+
*
|
|
291
|
+
* @param alias Identifier for the mock rule. Useful for `waitFor()`.
|
|
292
|
+
* @param options Options to configure the mock:
|
|
293
|
+
* - `method`: HTTP method ("GET", "POST", …)
|
|
294
|
+
* - `url`: URL string or RegExp to match
|
|
295
|
+
* - `response`: Body of the mocked response
|
|
296
|
+
* - `status`: (optional) HTTP status code (default: 200)
|
|
297
|
+
* - `responseHeaders`: (optional) Response headers
|
|
298
|
+
* - `delay`: (optional) Delay in ms before returning the response
|
|
299
|
+
*
|
|
300
|
+
* @example
|
|
301
|
+
* ```ts
|
|
302
|
+
* mockRequest("getUser", {
|
|
303
|
+
* method: "GET",
|
|
304
|
+
* url: /\/api\/user\/\d+/,
|
|
305
|
+
* response: { id: 1, name: "Kevin" },
|
|
306
|
+
* status: 200,
|
|
307
|
+
* responseHeaders: { "x-mock": "true" }
|
|
308
|
+
* });
|
|
309
|
+
* ```
|
|
310
|
+
*/
|
|
311
|
+
mockRequest: (alias: string, options: Options) => Promise<void>;
|
|
312
|
+
/**
|
|
313
|
+
* Wait for a mocked request to be made.
|
|
314
|
+
* @param alias The alias of the mock rule to wait for
|
|
315
|
+
* @param retries The number of retries to make
|
|
316
|
+
* @param retryDelay The delay between retries
|
|
317
|
+
* @return The matched rule (with body if applicable)
|
|
318
|
+
*
|
|
319
|
+
* @example
|
|
320
|
+
* ```ts
|
|
321
|
+
* const rule = await twd.waitFor("aliasId");
|
|
322
|
+
* console.log(rule.body);
|
|
323
|
+
* const rule = await twd.waitFor("aliasId", 5, 100);
|
|
324
|
+
* console.log(rule.body);
|
|
325
|
+
*
|
|
326
|
+
* ```
|
|
327
|
+
*/
|
|
328
|
+
waitForRequest: (alias: string, retries?: number, retryDelay?: number) => Promise<Rule>;
|
|
329
|
+
/**
|
|
330
|
+
* wait for a list of mocked requests to be made.
|
|
331
|
+
* @param aliases The aliases of the mock rules to wait for
|
|
332
|
+
* @returns The matched rules (with body if applicable)
|
|
333
|
+
* @example
|
|
334
|
+
* ```ts
|
|
335
|
+
* const rules = await waitForRequests(["getUser", "postComment"]);
|
|
336
|
+
* ```
|
|
337
|
+
*/
|
|
338
|
+
waitForRequests: (aliases: string[]) => Promise<Rule[]>;
|
|
339
|
+
/**
|
|
340
|
+
* URL-related assertions.
|
|
341
|
+
*
|
|
342
|
+
* @example
|
|
343
|
+
* ```ts
|
|
344
|
+
* twd.url().should("eq", "http://localhost:3000/contact");
|
|
345
|
+
* twd.url().should("contain.url", "/contact");
|
|
346
|
+
*
|
|
347
|
+
* ```
|
|
348
|
+
*/
|
|
349
|
+
url: () => URLCommandAPI;
|
|
350
|
+
/**
|
|
351
|
+
* Initializes request mocking (registers the service worker).
|
|
352
|
+
* @param [path] service worker absolute path (optional)
|
|
353
|
+
*
|
|
354
|
+
* @example
|
|
355
|
+
* ```ts
|
|
356
|
+
* await twd.initRequestMocking();
|
|
357
|
+
* // init with custom service worker path
|
|
358
|
+
* await twd.initRequestMocking('/test-path/mock-sw.js');
|
|
359
|
+
* ```
|
|
360
|
+
*/
|
|
361
|
+
initRequestMocking: (path?: string) => Promise<void>;
|
|
362
|
+
/**
|
|
363
|
+
* Clears all request mock rules.
|
|
364
|
+
*
|
|
365
|
+
* @example
|
|
366
|
+
* ```ts
|
|
367
|
+
* twd.clearRequestMockRules();
|
|
368
|
+
*
|
|
369
|
+
* ```
|
|
370
|
+
*/
|
|
371
|
+
clearRequestMockRules: () => void;
|
|
372
|
+
/**
|
|
373
|
+
* Gets all current request mock rules.
|
|
374
|
+
*
|
|
375
|
+
* @example
|
|
376
|
+
* ```ts
|
|
377
|
+
* const rules = twd.getRequestMockRules();
|
|
378
|
+
* console.log(rules);
|
|
379
|
+
* ```
|
|
380
|
+
*/
|
|
381
|
+
getRequestMockRules: () => Rule[];
|
|
382
|
+
/**
|
|
383
|
+
* Gets the number of times a specific mock rule was hit.
|
|
384
|
+
* @param alias The alias of the mock rule
|
|
385
|
+
* @returns The number of times the rule was matched
|
|
386
|
+
*
|
|
387
|
+
* @example
|
|
388
|
+
* ```ts
|
|
389
|
+
* const count = twd.getRequestCount("getUser");
|
|
390
|
+
* expect(count).to.equal(2);
|
|
391
|
+
* ```
|
|
392
|
+
*/
|
|
393
|
+
getRequestCount: (alias: string) => number;
|
|
394
|
+
/**
|
|
395
|
+
* Gets a snapshot of all mock rule hit counts.
|
|
396
|
+
* @returns An object mapping rule aliases to their hit counts
|
|
397
|
+
*
|
|
398
|
+
* @example
|
|
399
|
+
* ```ts
|
|
400
|
+
* const counts = twd.getRequestCounts();
|
|
401
|
+
* expect(counts).to.deep.equal({ getUser: 2, listPosts: 1 });
|
|
402
|
+
* ```
|
|
403
|
+
*/
|
|
404
|
+
getRequestCounts: () => Record<string, number>;
|
|
405
|
+
/**
|
|
406
|
+
* Waits for a specified time.
|
|
407
|
+
* @param time Time in milliseconds to wait
|
|
408
|
+
* @returns A promise that resolves after the specified time
|
|
409
|
+
* @example
|
|
410
|
+
* ```ts
|
|
411
|
+
* await twd.wait(500); // wait for 500ms
|
|
412
|
+
* ```
|
|
413
|
+
*/
|
|
414
|
+
wait: (time: number) => Promise<void>;
|
|
415
|
+
/**
|
|
416
|
+
* Asserts something about the element.
|
|
417
|
+
* @param el The element to assert on
|
|
418
|
+
* @param name The name of the assertion.
|
|
419
|
+
* @param args Arguments for the assertion.
|
|
420
|
+
* @returns The same API for chaining.
|
|
421
|
+
* @example
|
|
422
|
+
* ```ts
|
|
423
|
+
* const button = await twd.get("button");
|
|
424
|
+
* const text = screenDom.getByText("Hello");
|
|
425
|
+
* twd.should(button.el, "have.text", "Hello");
|
|
426
|
+
* twd.should(text, "be.empty");
|
|
427
|
+
* twd.should(button.el, "have.class", "active");
|
|
428
|
+
* ```
|
|
429
|
+
*/
|
|
430
|
+
should: (el: Element, name: AnyAssertion, ...args: ArgsFor<AnyAssertion>) => void;
|
|
431
|
+
/**
|
|
432
|
+
* Mock a component.
|
|
433
|
+
* @param name The name of the component to mock
|
|
434
|
+
* @param component The component to mock
|
|
435
|
+
* @returns The mocked component
|
|
436
|
+
* @example
|
|
437
|
+
* ```ts
|
|
438
|
+
* twd.mockComponent("Button", Button);
|
|
439
|
+
* ```
|
|
440
|
+
*/
|
|
441
|
+
mockComponent: (name: string, component: React.ComponentType<any>) => void;
|
|
442
|
+
/**
|
|
443
|
+
* Clears all component mocks.
|
|
444
|
+
*
|
|
445
|
+
* @example
|
|
446
|
+
* ```ts
|
|
447
|
+
* twd.clearComponentMocks();
|
|
448
|
+
* ```
|
|
449
|
+
*/
|
|
450
|
+
clearComponentMocks: () => void;
|
|
451
|
+
/**
|
|
452
|
+
* Asserts that an element does not exist in the DOM.
|
|
453
|
+
* @param selector CSS selector of the element to check
|
|
454
|
+
* @returns A promise that resolves if the element does not exist, or rejects if it does
|
|
455
|
+
*
|
|
456
|
+
* @example
|
|
457
|
+
* ```ts
|
|
458
|
+
* await twd.notExists(".non-existent");
|
|
459
|
+
* ```
|
|
460
|
+
*/
|
|
461
|
+
notExists: (selector: string) => Promise<void>;
|
|
462
|
+
/**
|
|
463
|
+
* Simulates a viewport size by constraining body dimensions, overriding
|
|
464
|
+
* `window.innerWidth`/`window.innerHeight` and `window.matchMedia()`, and
|
|
465
|
+
* rewriting CSS `@media` rules to match the simulated dimensions.
|
|
466
|
+
* Call with no arguments to reset to the original viewport.
|
|
467
|
+
*
|
|
468
|
+
* @param width Viewport width in pixels
|
|
469
|
+
* @param height Viewport height in pixels (optional — omit to leave height unconstrained)
|
|
470
|
+
*
|
|
471
|
+
* @example
|
|
472
|
+
* ```ts
|
|
473
|
+
* twd.viewport(375, 667); // mobile
|
|
474
|
+
* twd.viewport(768); // tablet width, height unconstrained
|
|
475
|
+
* twd.viewport(); // reset
|
|
476
|
+
* ```
|
|
477
|
+
*/
|
|
478
|
+
viewport: (width?: number, height?: number) => void;
|
|
479
|
+
/**
|
|
480
|
+
* Resets the viewport to its original size (undoes a previous `twd.viewport()` call).
|
|
481
|
+
*
|
|
482
|
+
* @example
|
|
483
|
+
* ```ts
|
|
484
|
+
* twd.resetViewport();
|
|
485
|
+
* ```
|
|
486
|
+
*/
|
|
487
|
+
resetViewport: () => void;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
/**
|
|
491
|
+
* The main API returned by `twd.get()`.
|
|
492
|
+
*
|
|
493
|
+
* @example
|
|
494
|
+
* ```ts
|
|
495
|
+
* const btn = await twd.get("button");
|
|
496
|
+
* btn.should("have.text", "Clicked").click();
|
|
497
|
+
*
|
|
498
|
+
* ```
|
|
499
|
+
*
|
|
500
|
+
*/
|
|
501
|
+
declare interface TWDElemAPI {
|
|
502
|
+
/** The underlying DOM element. */
|
|
503
|
+
el: Element;
|
|
504
|
+
/**
|
|
505
|
+
* Asserts something about the element.
|
|
506
|
+
* @param name The name of the assertion.
|
|
507
|
+
* @param args Arguments for the assertion.
|
|
508
|
+
* @returns The same API for chaining.
|
|
509
|
+
*
|
|
510
|
+
* @example
|
|
511
|
+
* ```ts
|
|
512
|
+
* const btn = await twd.get("button");
|
|
513
|
+
* btn.should("have.text", "Click me").should("not.be.disabled");
|
|
514
|
+
*
|
|
515
|
+
* ```
|
|
516
|
+
*
|
|
517
|
+
*/
|
|
518
|
+
should: ShouldFn;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
export declare const TWDSidebar: ({ open, position, search }: TWDSidebarProps) => JSX.Element;
|
|
522
|
+
|
|
523
|
+
declare interface TWDSidebarProps {
|
|
524
|
+
/**
|
|
525
|
+
* Whether the sidebar is open by default
|
|
526
|
+
*/
|
|
527
|
+
open: boolean;
|
|
528
|
+
/**
|
|
529
|
+
* Sidebar position
|
|
530
|
+
* - left: Sidebar on the left side (default)
|
|
531
|
+
* - right: Sidebar on the right side
|
|
532
|
+
*
|
|
533
|
+
* @default "left"
|
|
534
|
+
*/
|
|
535
|
+
position?: "left" | "right";
|
|
536
|
+
/**
|
|
537
|
+
* Whether to show the search/filter input
|
|
538
|
+
*/
|
|
539
|
+
search?: boolean;
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
/**
|
|
543
|
+
* TWD Theme Configuration
|
|
544
|
+
*
|
|
545
|
+
* This file defines CSS variables that can be customized by users
|
|
546
|
+
* to personalize their TWD UI experience.
|
|
547
|
+
*
|
|
548
|
+
* Users can override these variables by setting them in their CSS:
|
|
549
|
+
*
|
|
550
|
+
* ```css
|
|
551
|
+
* :root {
|
|
552
|
+
* --twd-primary: #2563eb;
|
|
553
|
+
* --twd-background: #1e293b;
|
|
554
|
+
* ... other variables
|
|
555
|
+
* }
|
|
556
|
+
*
|
|
557
|
+
*/
|
|
558
|
+
export declare interface TWDTheme {
|
|
559
|
+
primary: string;
|
|
560
|
+
background: string;
|
|
561
|
+
backgroundSecondary: string;
|
|
562
|
+
border: string;
|
|
563
|
+
borderLight: string;
|
|
564
|
+
text: string;
|
|
565
|
+
textSecondary: string;
|
|
566
|
+
textMuted: string;
|
|
567
|
+
describeBg: string;
|
|
568
|
+
describeText: string;
|
|
569
|
+
describeBorder: string;
|
|
570
|
+
success: string;
|
|
571
|
+
successBg: string;
|
|
572
|
+
error: string;
|
|
573
|
+
errorBg: string;
|
|
574
|
+
warning: string;
|
|
575
|
+
warningBg: string;
|
|
576
|
+
skip: string;
|
|
577
|
+
skipBg: string;
|
|
578
|
+
buttonPrimary: string;
|
|
579
|
+
buttonPrimaryText: string;
|
|
580
|
+
buttonSecondary: string;
|
|
581
|
+
buttonSecondaryText: string;
|
|
582
|
+
buttonBorder: string;
|
|
583
|
+
spacingXs: string;
|
|
584
|
+
spacingSm: string;
|
|
585
|
+
spacingMd: string;
|
|
586
|
+
spacingLg: string;
|
|
587
|
+
spacingXl: string;
|
|
588
|
+
fontSizeXs: string;
|
|
589
|
+
fontSizeSm: string;
|
|
590
|
+
fontSizeMd: string;
|
|
591
|
+
fontSizeLg: string;
|
|
592
|
+
fontWeightNormal: string;
|
|
593
|
+
fontWeightMedium: string;
|
|
594
|
+
fontWeightBold: string;
|
|
595
|
+
sidebarWidth: string;
|
|
596
|
+
borderRadius: string;
|
|
597
|
+
borderRadiusLg: string;
|
|
598
|
+
shadow: string;
|
|
599
|
+
shadowSm: string;
|
|
600
|
+
zIndexSidebar: string;
|
|
601
|
+
zIndexSticky: string;
|
|
602
|
+
animationDuration: string;
|
|
603
|
+
iconColor: string;
|
|
604
|
+
iconColorSecondary: string;
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
/**
|
|
608
|
+
* All supported assertion names for the `should` function in url command
|
|
609
|
+
*
|
|
610
|
+
* @example
|
|
611
|
+
* twd.url().should("contain.url", "/new-page");
|
|
612
|
+
* twd.url().should("eq", "http://localhost:3000/new-page");
|
|
613
|
+
*/
|
|
614
|
+
declare type URLAssertionName = "eq" | "contain.url";
|
|
615
|
+
|
|
616
|
+
declare type URLCommandAPI = {
|
|
617
|
+
location: Location;
|
|
618
|
+
should: (name: AnyURLAssertion, value: string, retries?: number) => Promise<string>;
|
|
619
|
+
};
|
|
620
|
+
|
|
621
|
+
declare type UserEvent = typeof default_2;
|
|
622
|
+
|
|
623
|
+
export declare const userEvent: UserEvent;
|
|
624
|
+
|
|
1
625
|
export { }
|
package/dist/index.es.js
CHANGED
|
@@ -222,7 +222,7 @@ var $l = {
|
|
|
222
222
|
}, ce = (e) => {
|
|
223
223
|
const t = ur.size ? Array.from(ur.values()).find((r) => r.status === "running") : null;
|
|
224
224
|
t && t.logs?.push(e);
|
|
225
|
-
}, Mr = "1.6.
|
|
225
|
+
}, Mr = "1.6.4", Fl = () => typeof window < "u" ? (window.__TWD_MOCK_STATE__ || (window.__TWD_MOCK_STATE__ = {
|
|
226
226
|
rules: [],
|
|
227
227
|
counts: {}
|
|
228
228
|
}), window.__TWD_MOCK_STATE__) : {
|
package/dist/mock-sw.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
(function(){var d=t=>{try{return new RegExp(t),!0}catch{return!1}},f=new Set(["js","mjs","cjs","ts","tsx","jsx","mts","cts","css","scss","sass","less","styl","html","htm","xml","xhtml","vue","svelte","json","yaml","yml","toml","csv","txt","md","mdx","pdf","doc","docx","png","jpg","jpeg","gif","svg","webp","ico","bmp","avif","woff","woff2","ttf","eot","otf","mp3","mp4","webm","ogg","wav","zip","tar","gz","rar","map"]),c=t=>{const s=t.split("?")[0].match(/\.([a-zA-Z0-9]+)$/);return s?f.has(s[1].toLowerCase()):!1},h=(t,s)=>{const i=t.indexOf(s);if(i===-1)return!1;const e=i+s.length,a=t[e];if(a===void 0)return!0;const n=t.indexOf("?");return n!==-1&&e>n?!0:"?#&".includes(a)};function m(t,s,i){return i.find(e=>{const a=e.method.toLowerCase()===t.toLowerCase();if(e.urlRegex&&d(e.url)){const l=new RegExp(e.url);return a&&l.test(s)}if(c(e.url))return a&&s.includes(e.url);const n=e.url===s||h(s,e.url);return a&&n&&!c(s)})}function p(t,s,i,e){t.forEach(a=>a.postMessage({type:"EXECUTED",alias:s.alias,request:i,hitCount:e}))}var y=(t,s,i)=>{const e=![204,205,304].includes(s),a=e?JSON.stringify(t):null;return new Response(a,{status:s,headers:e?i||{"Content-Type":"application/json"}:i||{}})},u="1.6.
|
|
1
|
+
(function(){var d=t=>{try{return new RegExp(t),!0}catch{return!1}},f=new Set(["js","mjs","cjs","ts","tsx","jsx","mts","cts","css","scss","sass","less","styl","html","htm","xml","xhtml","vue","svelte","json","yaml","yml","toml","csv","txt","md","mdx","pdf","doc","docx","png","jpg","jpeg","gif","svg","webp","ico","bmp","avif","woff","woff2","ttf","eot","otf","mp3","mp4","webm","ogg","wav","zip","tar","gz","rar","map"]),c=t=>{const s=t.split("?")[0].match(/\.([a-zA-Z0-9]+)$/);return s?f.has(s[1].toLowerCase()):!1},h=(t,s)=>{const i=t.indexOf(s);if(i===-1)return!1;const e=i+s.length,a=t[e];if(a===void 0)return!0;const n=t.indexOf("?");return n!==-1&&e>n?!0:"?#&".includes(a)};function m(t,s,i){return i.find(e=>{const a=e.method.toLowerCase()===t.toLowerCase();if(e.urlRegex&&d(e.url)){const l=new RegExp(e.url);return a&&l.test(s)}if(c(e.url))return a&&s.includes(e.url);const n=e.url===s||h(s,e.url);return a&&n&&!c(s)})}function p(t,s,i,e){t.forEach(a=>a.postMessage({type:"EXECUTED",alias:s.alias,request:i,hitCount:e}))}var y=(t,s,i)=>{const e=![204,205,304].includes(s),a=e?JSON.stringify(t):null;return new Response(a,{status:s,headers:e?i||{"Content-Type":"application/json"}:i||{}})},u="1.6.4",r=[],o={},g=async t=>{const{method:s}=t.request,i=t.request.url,e=m(s,i,r);e&&(console.log("[TWD] Mock hit:",e.alias,s,i),t.respondWith((async()=>{let a=null;const n=t.request.headers.get("content-type")||"application/json";if(n.includes("application/json"))try{a=await t.request.clone().json()}catch{}else if(n.includes("form"))try{const l=await t.request.clone().formData();a={},l.forEach((x,E)=>{a[E]=x})}catch{}else if(n.includes("text"))try{a=await t.request.clone().text()}catch{}else if(n.includes("octet-stream"))try{a=await t.request.clone().arrayBuffer()}catch{}else if(n.includes("image"))try{a=await t.request.clone().blob()}catch{}else try{a=await t.request.clone().text()}catch{}return o[e.alias]||(o[e.alias]=0),o[e.alias]++,p(await self.clients.matchAll(),e,a,o[e.alias]),e.delay&&e.delay>0&&await new Promise(l=>setTimeout(l,e.delay)),y(e.response,e.status??200,e.responseHeaders)})()))},w=t=>{t!=="1.6.4"&&console.warn(`[TWD] ⚠️ Version mismatch detected:
|
|
2
2
|
Client version: ${t}
|
|
3
3
|
Service Worker version: ${u}
|
|
4
4
|
|
package/dist/runner-ci.d.ts
CHANGED
|
@@ -1 +1,28 @@
|
|
|
1
|
+
export declare const executeTests: () => Promise<{
|
|
2
|
+
handlers: Handler[];
|
|
3
|
+
testStatus: TestResult[];
|
|
4
|
+
}>;
|
|
5
|
+
|
|
6
|
+
declare interface Handler {
|
|
7
|
+
id: string;
|
|
8
|
+
name: string;
|
|
9
|
+
parent?: string;
|
|
10
|
+
handler: () => void | Promise<void>;
|
|
11
|
+
children?: string[];
|
|
12
|
+
type: 'suite' | 'test';
|
|
13
|
+
status?: 'idle' | 'pass' | 'fail' | 'skip' | 'running';
|
|
14
|
+
logs: string[];
|
|
15
|
+
depth: number;
|
|
16
|
+
only?: boolean;
|
|
17
|
+
skip?: boolean;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export declare const reportResults: (handlers: Handler[], testResults: TestResult[]) => void;
|
|
21
|
+
|
|
22
|
+
declare interface TestResult {
|
|
23
|
+
id: string;
|
|
24
|
+
status: 'pass' | 'fail' | 'skip';
|
|
25
|
+
error?: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
1
28
|
export { }
|
package/dist/runner.d.ts
CHANGED
|
@@ -1 +1,63 @@
|
|
|
1
|
+
export declare const afterEach: (fn: HookFn) => void;
|
|
2
|
+
|
|
3
|
+
export declare const beforeEach: (fn: HookFn) => void;
|
|
4
|
+
|
|
5
|
+
export declare const clearTests: () => void;
|
|
6
|
+
|
|
7
|
+
export declare const describe: {
|
|
8
|
+
(name: string, handler: () => void): void;
|
|
9
|
+
only(name: string, handler: () => void): void;
|
|
10
|
+
skip(name: string, handler: () => void): void;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export declare interface Handler {
|
|
14
|
+
id: string;
|
|
15
|
+
name: string;
|
|
16
|
+
parent?: string;
|
|
17
|
+
handler: () => void | Promise<void>;
|
|
18
|
+
children?: string[];
|
|
19
|
+
type: 'suite' | 'test';
|
|
20
|
+
status?: 'idle' | 'pass' | 'fail' | 'skip' | 'running';
|
|
21
|
+
logs: string[];
|
|
22
|
+
depth: number;
|
|
23
|
+
only?: boolean;
|
|
24
|
+
skip?: boolean;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export declare const handlers: Map<string, Handler>;
|
|
28
|
+
|
|
29
|
+
declare type HookFn = () => void | Promise<void>;
|
|
30
|
+
|
|
31
|
+
export declare const it: {
|
|
32
|
+
(name: string, handler: () => void | Promise<void>): void;
|
|
33
|
+
only(name: string, handler: () => void | Promise<void>): void;
|
|
34
|
+
skip(name: string, handler?: () => void | Promise<void>): void;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export declare interface RunnerEvents {
|
|
38
|
+
onStart: (test: Handler) => void;
|
|
39
|
+
onPass: (test: Handler, retryAttempt?: number) => void;
|
|
40
|
+
onFail: (test: Handler, error: Error) => void;
|
|
41
|
+
onSkip: (test: Handler) => void;
|
|
42
|
+
onSuiteStart?: (suite: Handler) => void;
|
|
43
|
+
onSuiteEnd?: (suite: Handler) => void;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export declare class TestRunner {
|
|
47
|
+
private events;
|
|
48
|
+
private retryCount;
|
|
49
|
+
constructor(events: RunnerEvents, options?: TestRunnerOptions);
|
|
50
|
+
runAll(): Promise<Map<string, Handler>>;
|
|
51
|
+
runSingle(id: string): Promise<void>;
|
|
52
|
+
runByIds(ids: string[]): Promise<Map<string, Handler>>;
|
|
53
|
+
private runSuiteByIds;
|
|
54
|
+
private hasDescendantInSet;
|
|
55
|
+
private runSuite;
|
|
56
|
+
private runTest;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export declare interface TestRunnerOptions {
|
|
60
|
+
retryCount?: number;
|
|
61
|
+
}
|
|
62
|
+
|
|
1
63
|
export { }
|
package/dist/ui.d.ts
CHANGED
|
@@ -1 +1,10 @@
|
|
|
1
|
+
import { JSX } from 'react/jsx-runtime';
|
|
2
|
+
|
|
3
|
+
export declare function MockedComponent<TProps extends Record<string, any>>({ name, children, }: MockedComponentProps<TProps>): JSX.Element;
|
|
4
|
+
|
|
5
|
+
declare interface MockedComponentProps<TProps = any> {
|
|
6
|
+
name: string;
|
|
7
|
+
children: React.ReactElement<TProps>;
|
|
8
|
+
}
|
|
9
|
+
|
|
1
10
|
export { }
|
package/dist/vite-plugin.d.ts
CHANGED
|
@@ -1 +1,67 @@
|
|
|
1
|
+
import { Plugin as Plugin_2 } from 'vite';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Vite plugin to remove the mock service worker file from the build output.
|
|
5
|
+
* This is useful for production builds where you don't want the mock service worker to be included.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```ts
|
|
9
|
+
* import { removeMockServiceWorker } from 'twd-js';
|
|
10
|
+
*
|
|
11
|
+
* export default defineConfig({
|
|
12
|
+
* plugins: [
|
|
13
|
+
* // ... other plugins
|
|
14
|
+
* removeMockServiceWorker()
|
|
15
|
+
* ]
|
|
16
|
+
* });
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
export declare function removeMockServiceWorker(): Plugin_2;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Vite plugin to handle HMR for TWD test files.
|
|
23
|
+
* When a TWD test file is updated, it forces a full page reload to prevent
|
|
24
|
+
* duplicate test entries that occur when HMR reloads test modules.
|
|
25
|
+
*
|
|
26
|
+
* This plugin only runs in development mode (serve) and does not affect Vitest test runs.
|
|
27
|
+
*
|
|
28
|
+
* @param options - Configuration options for the plugin
|
|
29
|
+
* @example
|
|
30
|
+
* ```ts
|
|
31
|
+
* import { twdHmr } from 'twd-js/vite-plugin';
|
|
32
|
+
*
|
|
33
|
+
* export default defineConfig({
|
|
34
|
+
* plugins: [
|
|
35
|
+
* // ... other plugins
|
|
36
|
+
* twdHmr() // Uses default pattern: .twd.test.ts
|
|
37
|
+
* ]
|
|
38
|
+
* });
|
|
39
|
+
* ```
|
|
40
|
+
* @example
|
|
41
|
+
* ```ts
|
|
42
|
+
* // Custom pattern
|
|
43
|
+
* twdHmr({ testFilePattern: '.twd.test.tsx' })
|
|
44
|
+
* ```
|
|
45
|
+
* @example
|
|
46
|
+
* ```ts
|
|
47
|
+
* // Custom function
|
|
48
|
+
* twdHmr({
|
|
49
|
+
* testFilePattern: (file) => file.includes('.twd.test.')
|
|
50
|
+
* })
|
|
51
|
+
* ```
|
|
52
|
+
*/
|
|
53
|
+
export declare function twdHmr(options?: TwdHmrOptions): Plugin_2;
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Options for the TWD HMR plugin.
|
|
57
|
+
*/
|
|
58
|
+
export declare interface TwdHmrOptions {
|
|
59
|
+
/**
|
|
60
|
+
* Pattern to match test files. Defaults to `.twd.test.ts`.
|
|
61
|
+
* Can be a string (checked with `endsWith`) or a function that returns a boolean.
|
|
62
|
+
* @default ".twd.test.ts"
|
|
63
|
+
*/
|
|
64
|
+
testFilePattern?: string | ((file: string) => boolean);
|
|
65
|
+
}
|
|
66
|
+
|
|
1
67
|
export { }
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "twd-js",
|
|
3
|
-
"version": "1.6.
|
|
3
|
+
"version": "1.6.4",
|
|
4
4
|
"description": "Test While Developing (TWD) - in-browser testing",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -88,7 +88,7 @@
|
|
|
88
88
|
"@vitest/coverage-v8": "^4.1.2",
|
|
89
89
|
"conventional-changelog": "^7.2.0",
|
|
90
90
|
"jsdom": "^29.0.1",
|
|
91
|
-
"typescript": "
|
|
91
|
+
"typescript": "~5.9.3",
|
|
92
92
|
"vite": "^8.0.3",
|
|
93
93
|
"vite-plugin-dts": "^4.5.4",
|
|
94
94
|
"vitepress": "^2.0.0-alpha.12",
|
|
File without changes
|