expect-sdk 0.0.24

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.
Files changed (324) hide show
  1. package/.claude/settings.local.json +48 -0
  2. package/.expect/replays/08a6cacb-cdfb-47ba-9cf7-41a13ac07a36.html +286 -0
  3. package/.expect/replays/08a6cacb-cdfb-47ba-9cf7-41a13ac07a36.ndjson +2 -0
  4. package/.expect/replays/08a6cacb-cdfb-47ba-9cf7-41a13ac07a36.ndjson.js +2 -0
  5. package/.expect/replays/0abc2d0c-30d4-405b-8f6a-ad55eab10797.html +286 -0
  6. package/.expect/replays/0abc2d0c-30d4-405b-8f6a-ad55eab10797.ndjson +2 -0
  7. package/.expect/replays/0abc2d0c-30d4-405b-8f6a-ad55eab10797.ndjson.js +2 -0
  8. package/.expect/replays/0be009ad-fc3d-4aa8-81e2-2473dddf876a.html +286 -0
  9. package/.expect/replays/0be009ad-fc3d-4aa8-81e2-2473dddf876a.ndjson +7 -0
  10. package/.expect/replays/0be009ad-fc3d-4aa8-81e2-2473dddf876a.ndjson.js +2 -0
  11. package/.expect/replays/0bf0f4f1-9c7d-4dac-ae8f-6990ec08b82b.html +286 -0
  12. package/.expect/replays/0bf0f4f1-9c7d-4dac-ae8f-6990ec08b82b.ndjson +10 -0
  13. package/.expect/replays/0bf0f4f1-9c7d-4dac-ae8f-6990ec08b82b.ndjson.js +2 -0
  14. package/.expect/replays/0cdb0236-3fdf-4c48-8ef1-fbc8539a8d32.html +286 -0
  15. package/.expect/replays/0cdb0236-3fdf-4c48-8ef1-fbc8539a8d32.ndjson +49 -0
  16. package/.expect/replays/0cdb0236-3fdf-4c48-8ef1-fbc8539a8d32.ndjson.js +2 -0
  17. package/.expect/replays/0dbbf37b-6749-4973-ae20-b37b9b734ce6.html +286 -0
  18. package/.expect/replays/0dbbf37b-6749-4973-ae20-b37b9b734ce6.ndjson +44 -0
  19. package/.expect/replays/0dbbf37b-6749-4973-ae20-b37b9b734ce6.ndjson.js +2 -0
  20. package/.expect/replays/15009a95-16f4-4f14-9f52-a2a650b6de23.html +286 -0
  21. package/.expect/replays/15009a95-16f4-4f14-9f52-a2a650b6de23.ndjson +23 -0
  22. package/.expect/replays/15009a95-16f4-4f14-9f52-a2a650b6de23.ndjson.js +2 -0
  23. package/.expect/replays/2095af47-e1dc-444b-ab84-f614d755fd04.html +286 -0
  24. package/.expect/replays/2095af47-e1dc-444b-ab84-f614d755fd04.ndjson +10 -0
  25. package/.expect/replays/2095af47-e1dc-444b-ab84-f614d755fd04.ndjson.js +2 -0
  26. package/.expect/replays/26962ef6-a3ac-4033-aee6-d09414c2232d.html +286 -0
  27. package/.expect/replays/26962ef6-a3ac-4033-aee6-d09414c2232d.ndjson +8 -0
  28. package/.expect/replays/26962ef6-a3ac-4033-aee6-d09414c2232d.ndjson.js +2 -0
  29. package/.expect/replays/28d1ed69-05c0-44dc-a369-77b0d37b64c8.html +286 -0
  30. package/.expect/replays/28d1ed69-05c0-44dc-a369-77b0d37b64c8.ndjson +12 -0
  31. package/.expect/replays/28d1ed69-05c0-44dc-a369-77b0d37b64c8.ndjson.js +2 -0
  32. package/.expect/replays/2ece9734-85bf-4d45-9738-7e3c7a4b6c9e.html +286 -0
  33. package/.expect/replays/2ece9734-85bf-4d45-9738-7e3c7a4b6c9e.ndjson +20 -0
  34. package/.expect/replays/2ece9734-85bf-4d45-9738-7e3c7a4b6c9e.ndjson.js +2 -0
  35. package/.expect/replays/301ecd28-499b-4367-80c8-8f5c740a4011.html +286 -0
  36. package/.expect/replays/301ecd28-499b-4367-80c8-8f5c740a4011.ndjson +6 -0
  37. package/.expect/replays/301ecd28-499b-4367-80c8-8f5c740a4011.ndjson.js +2 -0
  38. package/.expect/replays/323fdb67-102e-45c2-a6ae-2ed78e590928.html +286 -0
  39. package/.expect/replays/323fdb67-102e-45c2-a6ae-2ed78e590928.ndjson +7 -0
  40. package/.expect/replays/323fdb67-102e-45c2-a6ae-2ed78e590928.ndjson.js +2 -0
  41. package/.expect/replays/338b9a53-a211-448a-99cd-a95873edef79.html +286 -0
  42. package/.expect/replays/338b9a53-a211-448a-99cd-a95873edef79.ndjson +7 -0
  43. package/.expect/replays/338b9a53-a211-448a-99cd-a95873edef79.ndjson.js +2 -0
  44. package/.expect/replays/33bfd732-9116-4200-b732-41faba42ae75.html +286 -0
  45. package/.expect/replays/33bfd732-9116-4200-b732-41faba42ae75.ndjson +10 -0
  46. package/.expect/replays/33bfd732-9116-4200-b732-41faba42ae75.ndjson.js +2 -0
  47. package/.expect/replays/33eb7162-e0c8-40ae-8e72-8ad273b0fca7.html +286 -0
  48. package/.expect/replays/33eb7162-e0c8-40ae-8e72-8ad273b0fca7.ndjson +7 -0
  49. package/.expect/replays/33eb7162-e0c8-40ae-8e72-8ad273b0fca7.ndjson.js +2 -0
  50. package/.expect/replays/365939fb-9693-4219-b5f5-0bafef524617.html +286 -0
  51. package/.expect/replays/365939fb-9693-4219-b5f5-0bafef524617.ndjson +2 -0
  52. package/.expect/replays/365939fb-9693-4219-b5f5-0bafef524617.ndjson.js +2 -0
  53. package/.expect/replays/383ea7a8-0ff8-4958-a148-3032f0a52eab.html +286 -0
  54. package/.expect/replays/383ea7a8-0ff8-4958-a148-3032f0a52eab.ndjson +54 -0
  55. package/.expect/replays/383ea7a8-0ff8-4958-a148-3032f0a52eab.ndjson.js +2 -0
  56. package/.expect/replays/397a5665-4344-441b-a424-2ac53df712a1.html +286 -0
  57. package/.expect/replays/397a5665-4344-441b-a424-2ac53df712a1.ndjson +14 -0
  58. package/.expect/replays/397a5665-4344-441b-a424-2ac53df712a1.ndjson.js +2 -0
  59. package/.expect/replays/39a5d9cc-72d0-483d-bfcc-506a6fc85f13.html +286 -0
  60. package/.expect/replays/39a5d9cc-72d0-483d-bfcc-506a6fc85f13.ndjson +10 -0
  61. package/.expect/replays/39a5d9cc-72d0-483d-bfcc-506a6fc85f13.ndjson.js +2 -0
  62. package/.expect/replays/39ccd62e-2f4c-4a4a-9b43-40524b7a945b.html +286 -0
  63. package/.expect/replays/39ccd62e-2f4c-4a4a-9b43-40524b7a945b.ndjson +14 -0
  64. package/.expect/replays/39ccd62e-2f4c-4a4a-9b43-40524b7a945b.ndjson.js +2 -0
  65. package/.expect/replays/3eff073f-7e14-49d8-a2e2-1b7634a117cd.html +286 -0
  66. package/.expect/replays/3eff073f-7e14-49d8-a2e2-1b7634a117cd.ndjson +45 -0
  67. package/.expect/replays/3eff073f-7e14-49d8-a2e2-1b7634a117cd.ndjson.js +2 -0
  68. package/.expect/replays/3f144b2f-78ce-4d23-bd96-99994bf07edd.html +286 -0
  69. package/.expect/replays/3f144b2f-78ce-4d23-bd96-99994bf07edd.ndjson +34 -0
  70. package/.expect/replays/3f144b2f-78ce-4d23-bd96-99994bf07edd.ndjson.js +2 -0
  71. package/.expect/replays/436e0182-8590-4520-97b5-d62b08ebe822.html +286 -0
  72. package/.expect/replays/436e0182-8590-4520-97b5-d62b08ebe822.ndjson +15 -0
  73. package/.expect/replays/436e0182-8590-4520-97b5-d62b08ebe822.ndjson.js +2 -0
  74. package/.expect/replays/43e9a6e6-ce7d-4ed4-b593-f07ce6d53009.html +286 -0
  75. package/.expect/replays/43e9a6e6-ce7d-4ed4-b593-f07ce6d53009.ndjson +85 -0
  76. package/.expect/replays/43e9a6e6-ce7d-4ed4-b593-f07ce6d53009.ndjson.js +2 -0
  77. package/.expect/replays/4568fc07-ea40-4740-be6f-0f6a8e632175.html +286 -0
  78. package/.expect/replays/4568fc07-ea40-4740-be6f-0f6a8e632175.ndjson +53 -0
  79. package/.expect/replays/4568fc07-ea40-4740-be6f-0f6a8e632175.ndjson.js +2 -0
  80. package/.expect/replays/47b43574-9b41-44be-af9c-fb7c0a81196f.html +286 -0
  81. package/.expect/replays/47b43574-9b41-44be-af9c-fb7c0a81196f.ndjson +2 -0
  82. package/.expect/replays/47b43574-9b41-44be-af9c-fb7c0a81196f.ndjson.js +2 -0
  83. package/.expect/replays/47d846d6-f86e-46e4-94d7-f0abff232b20.html +286 -0
  84. package/.expect/replays/47d846d6-f86e-46e4-94d7-f0abff232b20.ndjson +10 -0
  85. package/.expect/replays/47d846d6-f86e-46e4-94d7-f0abff232b20.ndjson.js +2 -0
  86. package/.expect/replays/486cd227-9d22-49cf-b050-cb546d374206.html +286 -0
  87. package/.expect/replays/486cd227-9d22-49cf-b050-cb546d374206.ndjson +2 -0
  88. package/.expect/replays/486cd227-9d22-49cf-b050-cb546d374206.ndjson.js +2 -0
  89. package/.expect/replays/4c53e4c2-ece4-4767-87fe-394fe0ab4300.html +286 -0
  90. package/.expect/replays/4c53e4c2-ece4-4767-87fe-394fe0ab4300.ndjson +33 -0
  91. package/.expect/replays/4c53e4c2-ece4-4767-87fe-394fe0ab4300.ndjson.js +2 -0
  92. package/.expect/replays/4d1c0166-77e7-49ee-9bdc-9e3382e7f60c.html +286 -0
  93. package/.expect/replays/4d1c0166-77e7-49ee-9bdc-9e3382e7f60c.ndjson +58 -0
  94. package/.expect/replays/4d1c0166-77e7-49ee-9bdc-9e3382e7f60c.ndjson.js +2 -0
  95. package/.expect/replays/4ee7ecf6-20ce-42c3-8ed6-9476e74498eb.html +286 -0
  96. package/.expect/replays/4ee7ecf6-20ce-42c3-8ed6-9476e74498eb.ndjson +10 -0
  97. package/.expect/replays/4ee7ecf6-20ce-42c3-8ed6-9476e74498eb.ndjson.js +2 -0
  98. package/.expect/replays/5438a8a6-ef42-42e2-8764-fde7c6529d95.html +286 -0
  99. package/.expect/replays/5438a8a6-ef42-42e2-8764-fde7c6529d95.ndjson +33 -0
  100. package/.expect/replays/5438a8a6-ef42-42e2-8764-fde7c6529d95.ndjson.js +2 -0
  101. package/.expect/replays/5709193a-1153-46b2-b19c-736e4bda525d.html +286 -0
  102. package/.expect/replays/5709193a-1153-46b2-b19c-736e4bda525d.ndjson +16 -0
  103. package/.expect/replays/5709193a-1153-46b2-b19c-736e4bda525d.ndjson.js +2 -0
  104. package/.expect/replays/5eee229c-5e60-4cb2-a1c2-f7720a94ea1c.html +286 -0
  105. package/.expect/replays/5eee229c-5e60-4cb2-a1c2-f7720a94ea1c.ndjson +2 -0
  106. package/.expect/replays/5eee229c-5e60-4cb2-a1c2-f7720a94ea1c.ndjson.js +2 -0
  107. package/.expect/replays/63657ec6-87d8-4250-a0db-05d12fa2983e.html +286 -0
  108. package/.expect/replays/63657ec6-87d8-4250-a0db-05d12fa2983e.ndjson +48 -0
  109. package/.expect/replays/63657ec6-87d8-4250-a0db-05d12fa2983e.ndjson.js +2 -0
  110. package/.expect/replays/6623c0d4-cfcf-461a-8029-0f157ff21080.html +286 -0
  111. package/.expect/replays/6623c0d4-cfcf-461a-8029-0f157ff21080.ndjson +8 -0
  112. package/.expect/replays/6623c0d4-cfcf-461a-8029-0f157ff21080.ndjson.js +2 -0
  113. package/.expect/replays/69a4dab6-06ad-4d54-99c9-1113d6f5a033.html +286 -0
  114. package/.expect/replays/69a4dab6-06ad-4d54-99c9-1113d6f5a033.ndjson +7 -0
  115. package/.expect/replays/69a4dab6-06ad-4d54-99c9-1113d6f5a033.ndjson.js +2 -0
  116. package/.expect/replays/6b3fae01-5e61-48e3-b675-334572bdaf67.html +286 -0
  117. package/.expect/replays/6b3fae01-5e61-48e3-b675-334572bdaf67.ndjson +14 -0
  118. package/.expect/replays/6b3fae01-5e61-48e3-b675-334572bdaf67.ndjson.js +2 -0
  119. package/.expect/replays/709859dd-cd9d-4f4a-93f3-0185631feaf5.html +286 -0
  120. package/.expect/replays/709859dd-cd9d-4f4a-93f3-0185631feaf5.ndjson +2 -0
  121. package/.expect/replays/709859dd-cd9d-4f4a-93f3-0185631feaf5.ndjson.js +2 -0
  122. package/.expect/replays/76b454d4-ba48-47a0-9336-486a7b106322.html +286 -0
  123. package/.expect/replays/76b454d4-ba48-47a0-9336-486a7b106322.ndjson +37 -0
  124. package/.expect/replays/76b454d4-ba48-47a0-9336-486a7b106322.ndjson.js +2 -0
  125. package/.expect/replays/76c75bfa-d266-487e-a4d2-54d6f64760b5.html +286 -0
  126. package/.expect/replays/76c75bfa-d266-487e-a4d2-54d6f64760b5.ndjson +18 -0
  127. package/.expect/replays/76c75bfa-d266-487e-a4d2-54d6f64760b5.ndjson.js +2 -0
  128. package/.expect/replays/78333bb7-5172-4839-98a0-745372a1032b.html +286 -0
  129. package/.expect/replays/78333bb7-5172-4839-98a0-745372a1032b.ndjson +65 -0
  130. package/.expect/replays/78333bb7-5172-4839-98a0-745372a1032b.ndjson.js +2 -0
  131. package/.expect/replays/7be24ce2-8544-492e-9b66-b56c0c1e027b.html +286 -0
  132. package/.expect/replays/7be24ce2-8544-492e-9b66-b56c0c1e027b.ndjson +10 -0
  133. package/.expect/replays/7be24ce2-8544-492e-9b66-b56c0c1e027b.ndjson.js +2 -0
  134. package/.expect/replays/825423b3-7e83-4037-b0b7-c4eafe6282b8.html +286 -0
  135. package/.expect/replays/825423b3-7e83-4037-b0b7-c4eafe6282b8.ndjson +54 -0
  136. package/.expect/replays/825423b3-7e83-4037-b0b7-c4eafe6282b8.ndjson.js +2 -0
  137. package/.expect/replays/83641ba6-ccfa-4400-88ee-b8e4a1775c12.html +286 -0
  138. package/.expect/replays/83641ba6-ccfa-4400-88ee-b8e4a1775c12.ndjson +18 -0
  139. package/.expect/replays/83641ba6-ccfa-4400-88ee-b8e4a1775c12.ndjson.js +2 -0
  140. package/.expect/replays/87af45a5-dd8f-433a-8e5d-5ab136d653b9.html +286 -0
  141. package/.expect/replays/87af45a5-dd8f-433a-8e5d-5ab136d653b9.ndjson +3 -0
  142. package/.expect/replays/87af45a5-dd8f-433a-8e5d-5ab136d653b9.ndjson.js +2 -0
  143. package/.expect/replays/882a48e3-15b5-47fb-9f96-3e63c282557c.html +286 -0
  144. package/.expect/replays/882a48e3-15b5-47fb-9f96-3e63c282557c.ndjson +44 -0
  145. package/.expect/replays/882a48e3-15b5-47fb-9f96-3e63c282557c.ndjson.js +2 -0
  146. package/.expect/replays/8efca11f-3649-4433-a61c-f3b844aaa0b9.html +286 -0
  147. package/.expect/replays/8efca11f-3649-4433-a61c-f3b844aaa0b9.ndjson +33 -0
  148. package/.expect/replays/8efca11f-3649-4433-a61c-f3b844aaa0b9.ndjson.js +2 -0
  149. package/.expect/replays/8f563116-c926-4de3-9d16-3dfe33dd52b6.html +286 -0
  150. package/.expect/replays/8f563116-c926-4de3-9d16-3dfe33dd52b6.ndjson +12 -0
  151. package/.expect/replays/8f563116-c926-4de3-9d16-3dfe33dd52b6.ndjson.js +2 -0
  152. package/.expect/replays/9321eb59-9587-4819-80b0-b387c50aaaf4.html +286 -0
  153. package/.expect/replays/9321eb59-9587-4819-80b0-b387c50aaaf4.ndjson +10 -0
  154. package/.expect/replays/9321eb59-9587-4819-80b0-b387c50aaaf4.ndjson.js +2 -0
  155. package/.expect/replays/94cb0431-e2a6-4a8d-800a-6918adb25660.html +286 -0
  156. package/.expect/replays/94cb0431-e2a6-4a8d-800a-6918adb25660.ndjson +10 -0
  157. package/.expect/replays/94cb0431-e2a6-4a8d-800a-6918adb25660.ndjson.js +2 -0
  158. package/.expect/replays/94d30b76-3fb0-476a-93c5-c9acb88eb45a.html +286 -0
  159. package/.expect/replays/94d30b76-3fb0-476a-93c5-c9acb88eb45a.ndjson +43 -0
  160. package/.expect/replays/94d30b76-3fb0-476a-93c5-c9acb88eb45a.ndjson.js +2 -0
  161. package/.expect/replays/94da15c7-217f-4167-aad9-0ed7d69fb1a9.html +286 -0
  162. package/.expect/replays/94da15c7-217f-4167-aad9-0ed7d69fb1a9.ndjson +2 -0
  163. package/.expect/replays/94da15c7-217f-4167-aad9-0ed7d69fb1a9.ndjson.js +2 -0
  164. package/.expect/replays/9998db1c-b008-497f-8a13-1fc0eb6a8845.html +286 -0
  165. package/.expect/replays/9998db1c-b008-497f-8a13-1fc0eb6a8845.ndjson +31 -0
  166. package/.expect/replays/9998db1c-b008-497f-8a13-1fc0eb6a8845.ndjson.js +2 -0
  167. package/.expect/replays/9c54b7fe-d113-4d99-9df9-42af0779a176.html +286 -0
  168. package/.expect/replays/9c54b7fe-d113-4d99-9df9-42af0779a176.ndjson +52 -0
  169. package/.expect/replays/9c54b7fe-d113-4d99-9df9-42af0779a176.ndjson.js +2 -0
  170. package/.expect/replays/9d8b9d7f-2fad-44c8-92bc-5377adb4ca1b.html +286 -0
  171. package/.expect/replays/9d8b9d7f-2fad-44c8-92bc-5377adb4ca1b.ndjson +24 -0
  172. package/.expect/replays/9d8b9d7f-2fad-44c8-92bc-5377adb4ca1b.ndjson.js +2 -0
  173. package/.expect/replays/9f6d215a-17bd-44f8-be75-4f21c36cb7a5.html +286 -0
  174. package/.expect/replays/9f6d215a-17bd-44f8-be75-4f21c36cb7a5.ndjson +14 -0
  175. package/.expect/replays/9f6d215a-17bd-44f8-be75-4f21c36cb7a5.ndjson.js +2 -0
  176. package/.expect/replays/9fddc5ad-02e8-4f8e-96cf-c249444bf123.html +286 -0
  177. package/.expect/replays/9fddc5ad-02e8-4f8e-96cf-c249444bf123.ndjson +2 -0
  178. package/.expect/replays/9fddc5ad-02e8-4f8e-96cf-c249444bf123.ndjson.js +2 -0
  179. package/.expect/replays/a1e5b091-888f-43c5-95c3-c25f91f8925a.html +286 -0
  180. package/.expect/replays/a1e5b091-888f-43c5-95c3-c25f91f8925a.ndjson +12 -0
  181. package/.expect/replays/a1e5b091-888f-43c5-95c3-c25f91f8925a.ndjson.js +2 -0
  182. package/.expect/replays/a2e5372c-e459-4c47-8133-921303e7c74d.html +286 -0
  183. package/.expect/replays/a2e5372c-e459-4c47-8133-921303e7c74d.ndjson +2 -0
  184. package/.expect/replays/a2e5372c-e459-4c47-8133-921303e7c74d.ndjson.js +2 -0
  185. package/.expect/replays/a41a8fe3-4abc-4bde-bea0-526ef9d5c16f.html +286 -0
  186. package/.expect/replays/a41a8fe3-4abc-4bde-bea0-526ef9d5c16f.ndjson +16 -0
  187. package/.expect/replays/a41a8fe3-4abc-4bde-bea0-526ef9d5c16f.ndjson.js +2 -0
  188. package/.expect/replays/a726e14d-080c-4b30-b413-fcf6d14d0daf.html +286 -0
  189. package/.expect/replays/a726e14d-080c-4b30-b413-fcf6d14d0daf.ndjson +8 -0
  190. package/.expect/replays/a726e14d-080c-4b30-b413-fcf6d14d0daf.ndjson.js +2 -0
  191. package/.expect/replays/ab5651bb-3528-4b4b-809d-1e6d1fef2ade.html +286 -0
  192. package/.expect/replays/ab5651bb-3528-4b4b-809d-1e6d1fef2ade.ndjson +52 -0
  193. package/.expect/replays/ab5651bb-3528-4b4b-809d-1e6d1fef2ade.ndjson.js +2 -0
  194. package/.expect/replays/ac83d5ac-73c5-48f0-8d73-53f50151e4bf.html +286 -0
  195. package/.expect/replays/ac83d5ac-73c5-48f0-8d73-53f50151e4bf.ndjson +2 -0
  196. package/.expect/replays/ac83d5ac-73c5-48f0-8d73-53f50151e4bf.ndjson.js +2 -0
  197. package/.expect/replays/afeb4664-90ba-4525-9f4e-54349c5254f4.html +286 -0
  198. package/.expect/replays/afeb4664-90ba-4525-9f4e-54349c5254f4.ndjson +8 -0
  199. package/.expect/replays/afeb4664-90ba-4525-9f4e-54349c5254f4.ndjson.js +2 -0
  200. package/.expect/replays/b205a14f-ea04-41f7-b3f6-6a24881a4907.html +286 -0
  201. package/.expect/replays/b205a14f-ea04-41f7-b3f6-6a24881a4907.ndjson +44 -0
  202. package/.expect/replays/b205a14f-ea04-41f7-b3f6-6a24881a4907.ndjson.js +2 -0
  203. package/.expect/replays/b23eafd9-c876-4a66-a5e6-8d560672d5a8.html +286 -0
  204. package/.expect/replays/b23eafd9-c876-4a66-a5e6-8d560672d5a8.ndjson +84 -0
  205. package/.expect/replays/b23eafd9-c876-4a66-a5e6-8d560672d5a8.ndjson.js +2 -0
  206. package/.expect/replays/b4116451-32d0-4b85-a404-503bb4123815.html +286 -0
  207. package/.expect/replays/b4116451-32d0-4b85-a404-503bb4123815.ndjson +10 -0
  208. package/.expect/replays/b4116451-32d0-4b85-a404-503bb4123815.ndjson.js +2 -0
  209. package/.expect/replays/b649a6ef-45e3-49c2-bd02-284deb7ea9b1.html +286 -0
  210. package/.expect/replays/b649a6ef-45e3-49c2-bd02-284deb7ea9b1.ndjson +32 -0
  211. package/.expect/replays/b649a6ef-45e3-49c2-bd02-284deb7ea9b1.ndjson.js +2 -0
  212. package/.expect/replays/b8487d6d-87db-4453-9a87-f3ef57b9596a.html +286 -0
  213. package/.expect/replays/b8487d6d-87db-4453-9a87-f3ef57b9596a.ndjson +12 -0
  214. package/.expect/replays/b8487d6d-87db-4453-9a87-f3ef57b9596a.ndjson.js +2 -0
  215. package/.expect/replays/b9126d9e-93e9-44d1-901b-f4e4700a2ac7.html +286 -0
  216. package/.expect/replays/b9126d9e-93e9-44d1-901b-f4e4700a2ac7.ndjson +16 -0
  217. package/.expect/replays/b9126d9e-93e9-44d1-901b-f4e4700a2ac7.ndjson.js +2 -0
  218. package/.expect/replays/bc9ac26e-6915-466d-a250-61679c7d8785.html +286 -0
  219. package/.expect/replays/bc9ac26e-6915-466d-a250-61679c7d8785.ndjson +58 -0
  220. package/.expect/replays/bc9ac26e-6915-466d-a250-61679c7d8785.ndjson.js +2 -0
  221. package/.expect/replays/bf71f292-d0fa-4b38-88d2-838d631b4fd3.html +286 -0
  222. package/.expect/replays/bf71f292-d0fa-4b38-88d2-838d631b4fd3.ndjson +18 -0
  223. package/.expect/replays/bf71f292-d0fa-4b38-88d2-838d631b4fd3.ndjson.js +2 -0
  224. package/.expect/replays/c064a436-aa74-411c-b4ff-228e6016748c.html +286 -0
  225. package/.expect/replays/c064a436-aa74-411c-b4ff-228e6016748c.ndjson +14 -0
  226. package/.expect/replays/c064a436-aa74-411c-b4ff-228e6016748c.ndjson.js +2 -0
  227. package/.expect/replays/c293319a-56fc-47c2-be0a-efebab7e5547.html +286 -0
  228. package/.expect/replays/c293319a-56fc-47c2-be0a-efebab7e5547.ndjson +10 -0
  229. package/.expect/replays/c293319a-56fc-47c2-be0a-efebab7e5547.ndjson.js +2 -0
  230. package/.expect/replays/c48e7ccb-f40a-4d86-9ada-f9ac0f174809.html +286 -0
  231. package/.expect/replays/c48e7ccb-f40a-4d86-9ada-f9ac0f174809.ndjson +18 -0
  232. package/.expect/replays/c48e7ccb-f40a-4d86-9ada-f9ac0f174809.ndjson.js +2 -0
  233. package/.expect/replays/c4ec751f-4c2f-4ac4-83e1-9d53ebd275b0.html +286 -0
  234. package/.expect/replays/c4ec751f-4c2f-4ac4-83e1-9d53ebd275b0.ndjson +12 -0
  235. package/.expect/replays/c4ec751f-4c2f-4ac4-83e1-9d53ebd275b0.ndjson.js +2 -0
  236. package/.expect/replays/c5f8f2de-415c-4605-90e9-8a31286c1d33.html +286 -0
  237. package/.expect/replays/c5f8f2de-415c-4605-90e9-8a31286c1d33.ndjson +36 -0
  238. package/.expect/replays/c5f8f2de-415c-4605-90e9-8a31286c1d33.ndjson.js +2 -0
  239. package/.expect/replays/c92defec-c6f7-4334-89f4-d3b1010a592b.html +286 -0
  240. package/.expect/replays/c92defec-c6f7-4334-89f4-d3b1010a592b.ndjson +97 -0
  241. package/.expect/replays/c92defec-c6f7-4334-89f4-d3b1010a592b.ndjson.js +2 -0
  242. package/.expect/replays/ce024389-749b-4d9f-9cbe-d2add9b5d506.html +286 -0
  243. package/.expect/replays/ce024389-749b-4d9f-9cbe-d2add9b5d506.ndjson +8 -0
  244. package/.expect/replays/ce024389-749b-4d9f-9cbe-d2add9b5d506.ndjson.js +2 -0
  245. package/.expect/replays/d0106187-b170-4208-b6e2-1652906fb952.html +286 -0
  246. package/.expect/replays/d0106187-b170-4208-b6e2-1652906fb952.ndjson +52 -0
  247. package/.expect/replays/d0106187-b170-4208-b6e2-1652906fb952.ndjson.js +2 -0
  248. package/.expect/replays/d4aa55a2-9b5f-4eb5-b795-5324e5c5d7db.html +286 -0
  249. package/.expect/replays/d4aa55a2-9b5f-4eb5-b795-5324e5c5d7db.ndjson +84 -0
  250. package/.expect/replays/d4aa55a2-9b5f-4eb5-b795-5324e5c5d7db.ndjson.js +2 -0
  251. package/.expect/replays/d4fc939c-c97d-4125-95dc-a8745709e879.html +286 -0
  252. package/.expect/replays/d4fc939c-c97d-4125-95dc-a8745709e879.ndjson +2 -0
  253. package/.expect/replays/d4fc939c-c97d-4125-95dc-a8745709e879.ndjson.js +2 -0
  254. package/.expect/replays/d59474f3-6eee-4145-9252-ac56d7d634d1.html +286 -0
  255. package/.expect/replays/d59474f3-6eee-4145-9252-ac56d7d634d1.ndjson +46 -0
  256. package/.expect/replays/d59474f3-6eee-4145-9252-ac56d7d634d1.ndjson.js +2 -0
  257. package/.expect/replays/d842aee0-a2e0-45a4-84bb-02571ea704ef.html +286 -0
  258. package/.expect/replays/d842aee0-a2e0-45a4-84bb-02571ea704ef.ndjson +10 -0
  259. package/.expect/replays/d842aee0-a2e0-45a4-84bb-02571ea704ef.ndjson.js +2 -0
  260. package/.expect/replays/dbacab2e-6e47-4c2e-861b-881a3439cc61.html +286 -0
  261. package/.expect/replays/dbacab2e-6e47-4c2e-861b-881a3439cc61.ndjson +40 -0
  262. package/.expect/replays/dbacab2e-6e47-4c2e-861b-881a3439cc61.ndjson.js +2 -0
  263. package/.expect/replays/e47ad94c-1326-4e5f-9e36-9187b2475dc4.html +286 -0
  264. package/.expect/replays/e47ad94c-1326-4e5f-9e36-9187b2475dc4.ndjson +10 -0
  265. package/.expect/replays/e47ad94c-1326-4e5f-9e36-9187b2475dc4.ndjson.js +2 -0
  266. package/.expect/replays/e4e7c05e-0def-4eb4-80de-5c0abc6e9707.html +286 -0
  267. package/.expect/replays/e4e7c05e-0def-4eb4-80de-5c0abc6e9707.ndjson +32 -0
  268. package/.expect/replays/e4e7c05e-0def-4eb4-80de-5c0abc6e9707.ndjson.js +2 -0
  269. package/.expect/replays/e7fbb2f5-f5be-430f-b9d8-e9b5980c4e25.html +286 -0
  270. package/.expect/replays/e7fbb2f5-f5be-430f-b9d8-e9b5980c4e25.ndjson +30 -0
  271. package/.expect/replays/e7fbb2f5-f5be-430f-b9d8-e9b5980c4e25.ndjson.js +2 -0
  272. package/.expect/replays/ef25484d-6e02-4cde-bf19-7251a9ada2b3.html +286 -0
  273. package/.expect/replays/ef25484d-6e02-4cde-bf19-7251a9ada2b3.ndjson +37 -0
  274. package/.expect/replays/ef25484d-6e02-4cde-bf19-7251a9ada2b3.ndjson.js +2 -0
  275. package/.expect/replays/f114ddaa-f891-4be1-b626-eaeeba692e0f.html +286 -0
  276. package/.expect/replays/f114ddaa-f891-4be1-b626-eaeeba692e0f.ndjson +12 -0
  277. package/.expect/replays/f114ddaa-f891-4be1-b626-eaeeba692e0f.ndjson.js +2 -0
  278. package/.expect/replays/f39c87c6-34c7-4b81-8350-a644e309430f.html +286 -0
  279. package/.expect/replays/f39c87c6-34c7-4b81-8350-a644e309430f.ndjson +8 -0
  280. package/.expect/replays/f39c87c6-34c7-4b81-8350-a644e309430f.ndjson.js +2 -0
  281. package/.expect/replays/f6484a4f-c647-4f78-9a2e-f41f00683c15.html +286 -0
  282. package/.expect/replays/f6484a4f-c647-4f78-9a2e-f41f00683c15.ndjson +14 -0
  283. package/.expect/replays/f6484a4f-c647-4f78-9a2e-f41f00683c15.ndjson.js +2 -0
  284. package/.turbo/turbo-build.log +22 -0
  285. package/.turbo/turbo-check.log +6 -0
  286. package/.turbo/turbo-typecheck.log +4 -0
  287. package/CHANGELOG.md +12 -0
  288. package/LICENSE +110 -0
  289. package/README.md +536 -0
  290. package/dist/constants-DFJAD4-F.mjs +152 -0
  291. package/dist/constants-DFJAD4-F.mjs.map +1 -0
  292. package/dist/effect.d.mts +30 -0
  293. package/dist/effect.d.mts.map +1 -0
  294. package/dist/effect.mjs +2 -0
  295. package/dist/index.d.mts +20 -0
  296. package/dist/index.d.mts.map +1 -0
  297. package/dist/index.mjs +279 -0
  298. package/dist/index.mjs.map +1 -0
  299. package/dist/types-0J0EwTM4.d.mts +124 -0
  300. package/dist/types-0J0EwTM4.d.mts.map +1 -0
  301. package/package.json +46 -0
  302. package/src/build-instruction.ts +35 -0
  303. package/src/config.ts +15 -0
  304. package/src/constants.ts +2 -0
  305. package/src/effect.ts +14 -0
  306. package/src/errors.ts +17 -0
  307. package/src/expect.ts +432 -0
  308. package/src/index.ts +23 -0
  309. package/src/layers.ts +14 -0
  310. package/src/result-builder.ts +184 -0
  311. package/src/test-run.ts +19 -0
  312. package/src/tool.ts +26 -0
  313. package/src/types.ts +98 -0
  314. package/tests/build-instruction.test.ts +62 -0
  315. package/tests/config.test.ts +44 -0
  316. package/tests/e2e.ts +81 -0
  317. package/tests/examples.test.ts +44 -0
  318. package/tests/expect.test.ts +143 -0
  319. package/tests/fixtures/fixture-server.ts +111 -0
  320. package/tests/result-builder.test.ts +292 -0
  321. package/tests/test-run.test.ts +95 -0
  322. package/tests/tool.test.ts +60 -0
  323. package/tsconfig.json +9 -0
  324. package/vite.config.ts +13 -0
@@ -0,0 +1,2 @@
1
+ export const DEFAULT_TIMEOUT_MS = 5 * 60 * 1000;
2
+ export const DEFAULT_AGENT_BACKEND = "claude" as const;
package/src/effect.ts ADDED
@@ -0,0 +1,14 @@
1
+ export { ExpectConfigError, ExpectTimeoutError } from "./errors";
2
+ export { resolveUrl, buildInstruction } from "./build-instruction";
3
+ export { layerSdk } from "./layers";
4
+ export { DEFAULT_TIMEOUT_MS, DEFAULT_AGENT_BACKEND } from "./constants";
5
+ export { buildTestResult, buildStepResult, diffEvents, extractArtifacts } from "./result-builder";
6
+ export type {
7
+ Action,
8
+ BrowserName,
9
+ CookieInput,
10
+ Cookie,
11
+ StepResult,
12
+ TestResult,
13
+ TestEvent,
14
+ } from "./types";
package/src/errors.ts ADDED
@@ -0,0 +1,17 @@
1
+ import { Schema } from "effect";
2
+
3
+ export class ExpectTimeoutError extends Schema.ErrorClass<ExpectTimeoutError>("ExpectTimeoutError")(
4
+ {
5
+ _tag: Schema.tag("ExpectTimeoutError"),
6
+ timeoutMs: Schema.Number,
7
+ },
8
+ ) {
9
+ message = `expect execution timed out after ${this.timeoutMs}ms`;
10
+ }
11
+
12
+ export class ExpectConfigError extends Error {
13
+ constructor(message: string, fix: string) {
14
+ super(`${message}\n\nFix: ${fix}`);
15
+ this.name = "ExpectConfigError";
16
+ }
17
+ }
package/src/expect.ts ADDED
@@ -0,0 +1,432 @@
1
+ import { Effect, Option, Stream } from "effect";
2
+ import * as NodeServices from "@effect/platform-node/NodeServices";
3
+ import { Executor, type ExecuteOptions } from "@expect/supervisor";
4
+ import { type ExecutedTestPlan, type ExecutionEvent, ChangesFor } from "@expect/shared/models";
5
+ import {
6
+ Cookies as CookiesService,
7
+ Browsers,
8
+ layerLive as cookiesLayerLive,
9
+ } from "@expect/cookies";
10
+ import { ExpectConfigError, ExpectTimeoutError } from "./errors";
11
+ import { resolveUrl, buildInstruction } from "./build-instruction";
12
+ import { getGlobalConfig } from "./config";
13
+ import { layerSdk } from "./layers";
14
+ import { createTestRun } from "./test-run";
15
+ import { buildTestResult, diffEvents, extractArtifacts } from "./result-builder";
16
+ import { DEFAULT_TIMEOUT_MS, DEFAULT_AGENT_BACKEND } from "./constants";
17
+ import type { Page } from "playwright";
18
+ import type {
19
+ TestInput,
20
+ TestRun,
21
+ TestResult,
22
+ TestEvent,
23
+ SessionConfig,
24
+ SessionTestInput,
25
+ ExpectSession,
26
+ Cookie,
27
+ BrowserName,
28
+ Test,
29
+ } from "./types";
30
+
31
+ const normalizeTestPrompts = (tests: readonly Test[]): readonly string[] =>
32
+ tests.map((test) => (typeof test === "string" ? test : test.prompt));
33
+
34
+ const resolveInputUrl = (input: { url?: string; page?: Page }): string => {
35
+ const config = getGlobalConfig();
36
+
37
+ if (input.page) {
38
+ return input.page.url();
39
+ }
40
+
41
+ if (input.url !== undefined) {
42
+ return resolveUrl(input.url, config.baseUrl);
43
+ }
44
+
45
+ if (config.baseUrl) {
46
+ return config.baseUrl;
47
+ }
48
+
49
+ throw new ExpectConfigError(
50
+ "No URL provided and no baseUrl configured.",
51
+ `Expect.test({ url: "http://localhost:3000", tests: [...] })\nOr: configure({ baseUrl: "http://localhost:3000" })`,
52
+ );
53
+ };
54
+
55
+ const validateTests = (tests: readonly Test[]): void => {
56
+ if (tests.length === 0) {
57
+ throw new ExpectConfigError(
58
+ "tests array is empty.",
59
+ `Expect.test({ url: "...", tests: ["at least one test"] })`,
60
+ );
61
+ }
62
+ };
63
+
64
+ const validateTestInput = (input: TestInput): void => {
65
+ validateTests(input.tests);
66
+ if (input.tools && input.tools.length > 0) {
67
+ throw new ExpectConfigError(
68
+ "Custom tools are not yet supported.",
69
+ `Remove the tools field for now. Tool support is coming in a future release.`,
70
+ );
71
+ }
72
+ if (typeof input.before === "function" && !input.page) {
73
+ throw new ExpectConfigError(
74
+ "Function before requires a page.",
75
+ `Pass a Playwright Page: Expect.test({ page, before: async (page) => { ... }, tests: [...] })`,
76
+ );
77
+ }
78
+ if (typeof input.after === "function" && !input.page) {
79
+ throw new ExpectConfigError(
80
+ "Function after requires a page.",
81
+ `Pass a Playwright Page: Expect.test({ page, after: async (page) => { ... }, tests: [...] })`,
82
+ );
83
+ }
84
+ };
85
+
86
+ const validateSessionConfig = (config: SessionConfig): void => {
87
+ if (config.browserContext) {
88
+ throw new ExpectConfigError(
89
+ "External browserContext is not yet supported.",
90
+ `Remove the browserContext field. The SDK manages browser lifecycle internally.`,
91
+ );
92
+ }
93
+ if (config.tools && config.tools.length > 0) {
94
+ throw new ExpectConfigError(
95
+ "Custom tools are not yet supported.",
96
+ `Remove the tools field for now. Tool support is coming in a future release.`,
97
+ );
98
+ }
99
+ };
100
+
101
+ interface ResolvedCookies {
102
+ readonly browserKeys: readonly string[];
103
+ readonly explicitCookies: readonly Cookie[];
104
+ }
105
+
106
+ const isBrowserNameArray = (cookies: readonly unknown[]): cookies is readonly string[] =>
107
+ cookies.length > 0 && typeof cookies[0] === "string";
108
+
109
+ const resolveCookies = (cookies: TestInput["cookies"]): ResolvedCookies => {
110
+ if (cookies === undefined) return { browserKeys: [], explicitCookies: [] };
111
+ if (cookies === true) return { browserKeys: ["chrome"], explicitCookies: [] };
112
+ if (typeof cookies === "string") return { browserKeys: [cookies], explicitCookies: [] };
113
+ if (Array.isArray(cookies)) {
114
+ if (isBrowserNameArray(cookies)) {
115
+ return { browserKeys: cookies, explicitCookies: [] };
116
+ }
117
+ return { browserKeys: [], explicitCookies: cookies };
118
+ }
119
+ return { browserKeys: [], explicitCookies: [] };
120
+ };
121
+
122
+ const buildInstructionWithActions = (
123
+ url: string,
124
+ tests: readonly Test[],
125
+ before: TestInput["before"],
126
+ beforeContext: string | undefined,
127
+ after: TestInput["after"],
128
+ ): string => {
129
+ const prompts = normalizeTestPrompts(tests);
130
+ let instruction = buildInstruction(url, prompts);
131
+
132
+ if (typeof before === "string") {
133
+ instruction = `Before: ${before}\n\n${instruction}`;
134
+ }
135
+ if (beforeContext) {
136
+ instruction = `Context from before: ${beforeContext}\n\n${instruction}`;
137
+ }
138
+ if (typeof after === "string") {
139
+ instruction = `${instruction}\n\nAfter: ${after}`;
140
+ }
141
+
142
+ return instruction;
143
+ };
144
+
145
+ interface ExecutionContext {
146
+ readonly url: string;
147
+ readonly startedAt: number;
148
+ readonly eventBuffer: TestEvent[];
149
+ readonly resolveWaiter: { current: (() => void) | undefined };
150
+ }
151
+
152
+ const executeTests = Effect.fn("Sdk.executeTests")(function* (
153
+ executeOptions: ExecuteOptions,
154
+ context: ExecutionContext,
155
+ ) {
156
+ yield* Effect.annotateCurrentSpan({
157
+ url: context.url,
158
+ isHeadless: executeOptions.isHeadless,
159
+ });
160
+
161
+ const executor = yield* Executor;
162
+ let previousEvents: readonly ExecutionEvent[] = [];
163
+
164
+ const finalExecuted = yield* executor.execute(executeOptions).pipe(
165
+ Stream.tap((executed: ExecutedTestPlan) =>
166
+ Effect.sync(() => {
167
+ const newEvents = diffEvents(
168
+ previousEvents,
169
+ executed.events,
170
+ executed,
171
+ context.url,
172
+ context.startedAt,
173
+ );
174
+ previousEvents = executed.events;
175
+ for (const event of newEvents) {
176
+ context.eventBuffer.push(event);
177
+ }
178
+ context.resolveWaiter.current?.();
179
+ }),
180
+ ),
181
+ Stream.runLast,
182
+ Effect.flatMap((option) =>
183
+ Option.match(option, {
184
+ onNone: () => Effect.fail(new ExpectTimeoutError({ timeoutMs: 0 })),
185
+ onSome: (executed) => Effect.succeed(executed.finalizeTextBlock().synthesizeRunFinished()),
186
+ }),
187
+ ),
188
+ );
189
+
190
+ const artifacts = extractArtifacts(finalExecuted.events);
191
+ const result = buildTestResult(finalExecuted, context.url, context.startedAt, artifacts);
192
+
193
+ if (!context.eventBuffer.some((event) => event.type === "completed")) {
194
+ context.eventBuffer.push({ type: "completed", result });
195
+ }
196
+
197
+ context.resolveWaiter.current?.();
198
+ return result;
199
+ });
200
+
201
+ const extractCookies = Effect.fn("Sdk.extractCookies")(function* (keys: readonly string[]) {
202
+ yield* Effect.annotateCurrentSpan({ browserKeys: keys.join(",") });
203
+
204
+ const cookiesService = yield* CookiesService;
205
+ const browsers = yield* Browsers;
206
+ const allBrowsers = yield* browsers.list;
207
+
208
+ const matchingBrowsers = allBrowsers.filter((browser) =>
209
+ keys.some((key) => {
210
+ if (browser._tag === "ChromiumBrowser") return browser.key === key;
211
+ if (browser._tag === "FirefoxBrowser") return key === "firefox";
212
+ if (browser._tag === "SafariBrowser") return key === "safari";
213
+ return false;
214
+ }),
215
+ );
216
+
217
+ const results = yield* Effect.forEach(
218
+ matchingBrowsers,
219
+ (browser) => cookiesService.extract(browser),
220
+ { concurrency: "unbounded" },
221
+ );
222
+
223
+ return results.flat().map((cookie): Cookie => {
224
+ const formatted = cookie.playwrightFormat;
225
+ return { ...formatted, sameSite: formatted.sameSite ?? "Lax" };
226
+ });
227
+ });
228
+
229
+ const createAsyncIterator = (
230
+ eventBuffer: TestEvent[],
231
+ resolveWaiter: { current: (() => void) | undefined },
232
+ getFinished: () => boolean,
233
+ getError: () => unknown,
234
+ ): (() => AsyncIterableIterator<TestEvent>) => {
235
+ return () => {
236
+ let cursor = 0;
237
+
238
+ return {
239
+ async next(): Promise<IteratorResult<TestEvent>> {
240
+ while (true) {
241
+ if (cursor < eventBuffer.length) {
242
+ const event = eventBuffer[cursor];
243
+ cursor++;
244
+ if (event.type === "completed") {
245
+ return { done: true, value: undefined };
246
+ }
247
+ return { done: false, value: event };
248
+ }
249
+
250
+ if (getFinished()) {
251
+ const error = getError();
252
+ if (error) throw error;
253
+ return { done: true, value: undefined };
254
+ }
255
+
256
+ await new Promise<void>((resolve) => {
257
+ resolveWaiter.current = resolve;
258
+ });
259
+ }
260
+ },
261
+
262
+ [Symbol.asyncIterator]() {
263
+ return this;
264
+ },
265
+ };
266
+ };
267
+ };
268
+
269
+ const runExecution = (
270
+ url: string,
271
+ tests: readonly Test[],
272
+ input: {
273
+ cookies?: TestInput["cookies"];
274
+ mode?: "headed" | "headless";
275
+ timeout?: number;
276
+ isRecording?: boolean;
277
+ before?: TestInput["before"];
278
+ after?: TestInput["after"];
279
+ page?: Page;
280
+ },
281
+ ): { promise: Promise<TestResult>; subscribe: () => AsyncIterableIterator<TestEvent> } => {
282
+ const config = getGlobalConfig();
283
+ const timeoutMs = input.timeout ?? config.timeout ?? DEFAULT_TIMEOUT_MS;
284
+ const isHeadless = (input.mode ?? config.mode ?? "headless") === "headless";
285
+ const resolved = resolveCookies(input.cookies ?? config.cookies);
286
+ const rootDir = config.rootDir ?? process.cwd();
287
+
288
+ const eventBuffer: TestEvent[] = [];
289
+ const resolveWaiter: { current: (() => void) | undefined } = { current: undefined };
290
+ let finished = false;
291
+ let executionError: unknown;
292
+
293
+ const startExecution = async (): Promise<TestResult> => {
294
+ let beforeContext: string | undefined;
295
+ if (typeof input.before === "function" && input.page) {
296
+ const beforeResult = await input.before(input.page);
297
+ if (typeof beforeResult === "string") {
298
+ beforeContext = beforeResult;
299
+ }
300
+ }
301
+
302
+ const instruction = buildInstructionWithActions(
303
+ url,
304
+ tests,
305
+ input.before,
306
+ beforeContext,
307
+ input.after,
308
+ );
309
+
310
+ const executeOptions: ExecuteOptions = {
311
+ changesFor: ChangesFor.makeUnsafe({ _tag: "WorkingTree" }),
312
+ instruction,
313
+ isHeadless,
314
+ cookieBrowserKeys: [...resolved.browserKeys],
315
+ baseUrl: url,
316
+ };
317
+
318
+ const context: ExecutionContext = {
319
+ url,
320
+ startedAt: Date.now(),
321
+ eventBuffer,
322
+ resolveWaiter,
323
+ };
324
+
325
+ const program = executeTests(executeOptions, context).pipe(
326
+ Effect.timeoutOrElse({
327
+ duration: `${timeoutMs} millis`,
328
+ onTimeout: () => Effect.fail(new ExpectTimeoutError({ timeoutMs })),
329
+ }),
330
+ Effect.provide(layerSdk(DEFAULT_AGENT_BACKEND, rootDir)),
331
+ Effect.provide(NodeServices.layer),
332
+ );
333
+
334
+ const result = await Effect.runPromise(program);
335
+
336
+ if (typeof input.after === "function" && input.page) {
337
+ await input.after(input.page);
338
+ }
339
+
340
+ return result;
341
+ };
342
+
343
+ const promise = startExecution().then(
344
+ (result) => {
345
+ finished = true;
346
+ resolveWaiter.current?.();
347
+ return result;
348
+ },
349
+ (error) => {
350
+ executionError = error;
351
+ finished = true;
352
+ resolveWaiter.current?.();
353
+ throw error;
354
+ },
355
+ );
356
+
357
+ const subscribe = createAsyncIterator(
358
+ eventBuffer,
359
+ resolveWaiter,
360
+ () => finished,
361
+ () => executionError,
362
+ );
363
+
364
+ return { promise, subscribe };
365
+ };
366
+
367
+ const test = (input: TestInput): TestRun => {
368
+ validateTestInput(input);
369
+ const url = resolveInputUrl(input);
370
+ const { promise, subscribe } = runExecution(url, input.tests, input);
371
+ return createTestRun({ promise, subscribe });
372
+ };
373
+
374
+ const session = (config: SessionConfig): ExpectSession => {
375
+ validateSessionConfig(config);
376
+
377
+ const sessionTest = (input: SessionTestInput): TestRun => {
378
+ validateTests(input.tests);
379
+ const testUrl = input.url ?? config.url;
380
+ const globalCfg = getGlobalConfig();
381
+
382
+ let resolvedUrl: string;
383
+ if (testUrl !== undefined) {
384
+ resolvedUrl = resolveUrl(testUrl, globalCfg.baseUrl ?? config.url);
385
+ } else if (config.url) {
386
+ resolvedUrl = config.url;
387
+ } else if (globalCfg.baseUrl) {
388
+ resolvedUrl = globalCfg.baseUrl;
389
+ } else {
390
+ throw new ExpectConfigError(
391
+ "No URL provided for session test and no baseUrl configured.",
392
+ `session.test({ url: "/page", tests: [...] })`,
393
+ );
394
+ }
395
+
396
+ const { promise, subscribe } = runExecution(resolvedUrl, input.tests, {
397
+ cookies: config.cookies,
398
+ mode: input.mode ?? config.mode,
399
+ timeout: input.timeout ?? config.timeout,
400
+ isRecording: input.isRecording ?? config.isRecording,
401
+ before: input.before,
402
+ after: input.after,
403
+ });
404
+
405
+ return createTestRun({ promise, subscribe });
406
+ };
407
+
408
+ const close = async (): Promise<void> => {
409
+ // HACK: browser lifecycle is managed by the executor internally for now
410
+ };
411
+
412
+ return {
413
+ test: sessionTest,
414
+ close,
415
+ [Symbol.asyncDispose]: close,
416
+ };
417
+ };
418
+
419
+ const cookies = (browser: true | BrowserName | BrowserName[]): Promise<Cookie[]> => {
420
+ const keys: string[] =
421
+ browser === true ? ["chrome"] : typeof browser === "string" ? [browser] : browser;
422
+
423
+ return Effect.runPromise(
424
+ Effect.scoped(extractCookies(keys)).pipe(
425
+ Effect.provide(CookiesService.layer),
426
+ Effect.provide(cookiesLayerLive),
427
+ Effect.provide(NodeServices.layer),
428
+ ),
429
+ );
430
+ };
431
+
432
+ export const Expect = { test, session, cookies } as const;
package/src/index.ts ADDED
@@ -0,0 +1,23 @@
1
+ export { Expect, Expect as default } from "./expect";
2
+ export { tool } from "./tool";
3
+ export { defineConfig, configure } from "./config";
4
+ export { ExpectConfigError } from "./errors";
5
+ export { DEFAULT_TIMEOUT_MS } from "./constants";
6
+ export type {
7
+ Action,
8
+ BrowserName,
9
+ Cookie,
10
+ CookieInput,
11
+ ExpectConfig,
12
+ ExpectSession,
13
+ SessionConfig,
14
+ SessionTestInput,
15
+ Status,
16
+ StepResult,
17
+ Test,
18
+ TestEvent,
19
+ TestInput,
20
+ TestResult,
21
+ TestRun,
22
+ Tool,
23
+ } from "./types";
package/src/layers.ts ADDED
@@ -0,0 +1,14 @@
1
+ import { Layer, References } from "effect";
2
+ import { Executor, Git } from "@expect/supervisor";
3
+ import { Agent, type AgentBackend } from "@expect/agent";
4
+
5
+ export const layerSdk = (agentBackend: AgentBackend, rootDir: string) => {
6
+ const gitLayer = Git.withRepoRoot(rootDir);
7
+ const agentLayer = Agent.layerFor(agentBackend);
8
+ const executorLayer = Executor.layer.pipe(Layer.provide(gitLayer));
9
+
10
+ return Layer.mergeAll(executorLayer, gitLayer).pipe(
11
+ Layer.provideMerge(agentLayer),
12
+ Layer.provideMerge(Layer.succeed(References.MinimumLogLevel, "Error")),
13
+ );
14
+ };
@@ -0,0 +1,184 @@
1
+ import { DateTime, Option } from "effect";
2
+ import type { ExecutedTestPlan, TestPlanStep, ExecutionEvent } from "@expect/shared/models";
3
+ import type { Status, StepResult, TestResult, TestEvent } from "./types";
4
+
5
+ const REPLAY_SESSION_PREFIX = "rrweb replay:";
6
+ const SCREENSHOT_PREFIX = "Screenshot:";
7
+
8
+ const stepDurationMs = (step: TestPlanStep): number => {
9
+ if (Option.isNone(step.startedAt)) return 0;
10
+ if (Option.isNone(step.endedAt))
11
+ return Date.now() - Number(DateTime.toEpochMillis(step.startedAt.value));
12
+ return (
13
+ Number(DateTime.toEpochMillis(step.endedAt.value)) -
14
+ Number(DateTime.toEpochMillis(step.startedAt.value))
15
+ );
16
+ };
17
+
18
+ const stepStatusToResultStatus = (status: string): Status => {
19
+ if (status === "passed") return "passed";
20
+ if (status === "failed" || status === "skipped") return "failed";
21
+ return "pending";
22
+ };
23
+
24
+ export interface ExecutionArtifacts {
25
+ readonly recordingPath: string | undefined;
26
+ readonly screenshotPaths: readonly string[];
27
+ }
28
+
29
+ export const extractArtifacts = (events: readonly ExecutionEvent[]): ExecutionArtifacts => {
30
+ const closeResult = events
31
+ .slice()
32
+ .reverse()
33
+ .find(
34
+ (event) =>
35
+ event._tag === "ToolResult" &&
36
+ event.toolName === "close" &&
37
+ !event.isError &&
38
+ event.result.length > 0,
39
+ );
40
+
41
+ if (!closeResult || closeResult._tag !== "ToolResult") {
42
+ return { recordingPath: undefined, screenshotPaths: [] };
43
+ }
44
+
45
+ const lines = closeResult.result
46
+ .split("\n")
47
+ .map((line) => line.trim())
48
+ .filter((line) => line.length > 0);
49
+
50
+ const replayLine = lines.find((line) => line.startsWith(REPLAY_SESSION_PREFIX));
51
+ const recordingPath = replayLine?.replace(REPLAY_SESSION_PREFIX, "").trim() || undefined;
52
+
53
+ const screenshotPaths = lines
54
+ .filter((line) => line.startsWith(SCREENSHOT_PREFIX))
55
+ .map((line) => line.replace(SCREENSHOT_PREFIX, "").trim())
56
+ .filter((value) => value.length > 0);
57
+
58
+ return { recordingPath, screenshotPaths };
59
+ };
60
+
61
+ export const buildStepResult = (
62
+ step: TestPlanStep,
63
+ screenshotPaths: readonly string[],
64
+ stepIndex: number,
65
+ ): StepResult => ({
66
+ title: step.title,
67
+ status: stepStatusToResultStatus(step.status),
68
+ summary: Option.getOrElse(step.summary, () => ""),
69
+ screenshotPath: screenshotPaths[stepIndex],
70
+ duration: stepDurationMs(step),
71
+ });
72
+
73
+ export const buildTestResult = (
74
+ executed: ExecutedTestPlan,
75
+ url: string,
76
+ startedAt: number,
77
+ artifacts: ExecutionArtifacts,
78
+ ): TestResult => {
79
+ const steps = executed.steps.map((step, index) =>
80
+ buildStepResult(step, artifacts.screenshotPaths, index),
81
+ );
82
+ const errors = steps.filter((step) => step.status === "failed");
83
+ const hasFailure = errors.length > 0;
84
+ const hasPending = steps.some((step) => step.status === "pending");
85
+
86
+ let status: Status = "passed";
87
+ if (hasFailure) status = "failed";
88
+ else if (hasPending || steps.length === 0) status = "pending";
89
+
90
+ return {
91
+ status,
92
+ url,
93
+ duration: Date.now() - startedAt,
94
+ recordingPath: artifacts.recordingPath,
95
+ steps,
96
+ errors,
97
+ };
98
+ };
99
+
100
+ interface DiffContext {
101
+ readonly stepMap: ReadonlyMap<string, TestPlanStep>;
102
+ readonly stepIndexMap: ReadonlyMap<string, number>;
103
+ readonly artifacts: ExecutionArtifacts;
104
+ readonly executed: ExecutedTestPlan;
105
+ readonly url: string;
106
+ readonly startedAt: number;
107
+ }
108
+
109
+ const mapExecutionEvent = (event: ExecutionEvent, context: DiffContext): TestEvent | undefined => {
110
+ switch (event._tag) {
111
+ case "RunStarted":
112
+ return {
113
+ type: "run:started",
114
+ title: event.plan.title,
115
+ baseUrl: Option.getOrUndefined(event.plan.baseUrl),
116
+ };
117
+ case "StepStarted":
118
+ return { type: "step:started", title: event.title };
119
+ case "StepCompleted": {
120
+ const step = context.stepMap.get(event.stepId);
121
+ const index = context.stepIndexMap.get(event.stepId) ?? -1;
122
+ return step
123
+ ? {
124
+ type: "step:passed",
125
+ step: buildStepResult(step, context.artifacts.screenshotPaths, index),
126
+ }
127
+ : undefined;
128
+ }
129
+ case "StepFailed": {
130
+ const step = context.stepMap.get(event.stepId);
131
+ const index = context.stepIndexMap.get(event.stepId) ?? -1;
132
+ return step
133
+ ? {
134
+ type: "step:failed",
135
+ step: buildStepResult(step, context.artifacts.screenshotPaths, index),
136
+ }
137
+ : undefined;
138
+ }
139
+ case "StepSkipped":
140
+ return {
141
+ type: "step:skipped",
142
+ title: context.stepMap.get(event.stepId)?.title ?? event.stepId,
143
+ reason: event.reason,
144
+ };
145
+ case "ToolResult":
146
+ return event.toolName.endsWith("__screenshot") && !event.isError
147
+ ? { type: "screenshot", title: event.toolName, path: event.result }
148
+ : undefined;
149
+ case "RunFinished":
150
+ return {
151
+ type: "completed",
152
+ result: buildTestResult(
153
+ context.executed,
154
+ context.url,
155
+ context.startedAt,
156
+ context.artifacts,
157
+ ),
158
+ };
159
+ default:
160
+ return undefined;
161
+ }
162
+ };
163
+
164
+ export const diffEvents = (
165
+ previous: readonly ExecutionEvent[],
166
+ current: readonly ExecutionEvent[],
167
+ executed: ExecutedTestPlan,
168
+ url: string,
169
+ startedAt: number,
170
+ ): TestEvent[] => {
171
+ const context: DiffContext = {
172
+ stepMap: new Map(executed.steps.map((step) => [step.id, step])),
173
+ stepIndexMap: new Map(executed.steps.map((step, index) => [step.id, index])),
174
+ artifacts: extractArtifacts(executed.events),
175
+ executed,
176
+ url,
177
+ startedAt,
178
+ };
179
+
180
+ return current
181
+ .slice(previous.length)
182
+ .map((event) => mapExecutionEvent(event, context))
183
+ .filter((event): event is TestEvent => event !== undefined);
184
+ };