From ca08bb2e3cb1169d90ff9f2a4d9a160deb2ef2be Mon Sep 17 00:00:00 2001 From: Juergen Kunz Date: Thu, 20 Nov 2025 18:22:54 +0000 Subject: [PATCH] feat(tapbundle): Add global postTask (teardown) and suite lifecycle hooks (beforeAll/afterAll) to tapbundle --- changelog.md | 10 + pnpm-lock.yaml | 438 +++++++++++---------- readme.hints.md | 125 ++++++ test/tapbundle/test.new-lifecycle.ts | 170 ++++++++ ts/00_commitinfo_data.ts | 2 +- ts_tapbundle/readme.md | 243 +++++++++++- ts_tapbundle/tapbundle.classes.posttask.ts | 21 + ts_tapbundle/tapbundle.classes.tap.ts | 110 ++++-- 8 files changed, 873 insertions(+), 246 deletions(-) create mode 100644 test/tapbundle/test.new-lifecycle.ts create mode 100644 ts_tapbundle/tapbundle.classes.posttask.ts diff --git a/changelog.md b/changelog.md index 9a39125..efff20b 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,15 @@ # Changelog +## 2025-11-20 - 3.1.0 - feat(tapbundle) +Add global postTask (teardown) and suite lifecycle hooks (beforeAll/afterAll) to tapbundle + +- Introduce PostTask class (ts_tapbundle/tapbundle.classes.posttask.ts) and tap.postTask() API for global teardown. +- Integrate postTask execution into Tap.start() so postTasks run after all tests and before the global afterAll hook. +- Add suite-level beforeAll and afterAll support and ensure afterAll runs after child suites and their tests (changes in ts_tapbundle/tapbundle.classes.tap.ts). +- Add lifecycle tests (test/tapbundle/test.new-lifecycle.ts) verifying execution order, including parallel tests. +- Update documentation (readme.hints.md) describing Phase 1 API improvements and usage notes. +- This is additive and backward-compatible (no breaking changes). + ## 2025-11-20 - 3.0.1 - fix(@push.rocks/smarts3) Bump @push.rocks/smarts3 dependency to ^2.2.7 diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a180237..bb6e5bd 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -131,123 +131,127 @@ packages: '@aws-crypto/util@5.2.0': resolution: {integrity: sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==} - '@aws-sdk/client-s3@3.934.0': - resolution: {integrity: sha512-dtg77FGTgt8WlqgrRriCOie/SUl0x0cx2itPgK6fkf3pRK0t1betQ0EUZM6VYQcj+hqVMzh/XRcr1TDm5n5eVw==} + '@aws-sdk/client-s3@3.936.0': + resolution: {integrity: sha512-dnzZAkJDa9tdCxhqdnh37hdizJkernoFn0rufWahziOEmf0Yv9+mLeqR4qDmsAGUMuD1jFCmPR97FaCoh10mZg==} engines: {node: '>=18.0.0'} - '@aws-sdk/client-sso@3.934.0': - resolution: {integrity: sha512-gsgJevqhY0j3x014ejhXtHLCA6o83FYm3rJoZG7tqoy3DnWerLv/FHaAnHI/+Q+csadqjoFkWGQTOedPoOunzA==} + '@aws-sdk/client-sso@3.936.0': + resolution: {integrity: sha512-0G73S2cDqYwJVvqL08eakj79MZG2QRaB56Ul8/Ps9oQxllr7DMI1IQ/N3j3xjxgpq/U36pkoFZ8aK1n7Sbr3IQ==} engines: {node: '>=18.0.0'} - '@aws-sdk/core@3.934.0': - resolution: {integrity: sha512-b6k916ZxSrBwQPzeirncTIQXGnhps0HFOUakFt0ZEzjksePYUiEoU/SQ7VeY1j9JeAdJ24ejqddCiyLt99/3lg==} + '@aws-sdk/core@3.936.0': + resolution: {integrity: sha512-eGJ2ySUMvgtOziHhDRDLCrj473RJoL4J1vPjVM3NrKC/fF3/LoHjkut8AAnKmrW6a2uTzNKubigw8dEnpmpERw==} engines: {node: '>=18.0.0'} - '@aws-sdk/credential-provider-env@3.934.0': - resolution: {integrity: sha512-bnpIGYm7Jy46dxZa1cxMQ1sF0n2iBIT+TpOPHK51sz1N2dYOicUVWUHMDgU2xIFOVcKaqV+GV4VyicMmvDBcBQ==} + '@aws-sdk/credential-provider-env@3.936.0': + resolution: {integrity: sha512-dKajFuaugEA5i9gCKzOaVy9uTeZcApE+7Z5wdcZ6j40523fY1a56khDAUYkCfwqa7sHci4ccmxBkAo+fW1RChA==} engines: {node: '>=18.0.0'} - '@aws-sdk/credential-provider-http@3.934.0': - resolution: {integrity: sha512-WJcfFik7MPIgjE8lmuDcCqddHKRMpifzoBzTZWqUJJWYXIy0rDfNzt6pn3/TMLwVgnCGjnXlw6dChTxLzO60RQ==} + '@aws-sdk/credential-provider-http@3.936.0': + resolution: {integrity: sha512-5FguODLXG1tWx/x8fBxH+GVrk7Hey2LbXV5h9SFzYCx/2h50URBm0+9hndg0Rd23+xzYe14F6SI9HA9c1sPnjg==} engines: {node: '>=18.0.0'} - '@aws-sdk/credential-provider-ini@3.934.0': - resolution: {integrity: sha512-3vVKGe1F2S09G9kC0ZcpWh09opyrGOgQETllqWbuxlTVd7zBgrZWloItLIvneSDP+dWvdLFUbkD7WDWNCeGiig==} + '@aws-sdk/credential-provider-ini@3.936.0': + resolution: {integrity: sha512-TbUv56ERQQujoHcLMcfL0Q6bVZfYF83gu/TjHkVkdSlHPOIKaG/mhE2XZSQzXv1cud6LlgeBbfzVAxJ+HPpffg==} engines: {node: '>=18.0.0'} - '@aws-sdk/credential-provider-node@3.934.0': - resolution: {integrity: sha512-nguy36xi8nbH346dJjCmwWtOgfS4VfL7yHP+EEGmma+yg+J7mxgs8kA1NGQdJ8B46GdjlJPpI1P9pm7Pmz7nOw==} + '@aws-sdk/credential-provider-login@3.936.0': + resolution: {integrity: sha512-8DVrdRqPyUU66gfV7VZNToh56ZuO5D6agWrkLQE/xbLJOm2RbeRgh6buz7CqV8ipRd6m+zCl9mM4F3osQLZn8Q==} engines: {node: '>=18.0.0'} - '@aws-sdk/credential-provider-process@3.934.0': - resolution: {integrity: sha512-PhvpAgoJ88IOuqlUws9nvHuPex2jK+WS+0s00BQcRTwqPP0jtLT7eql6UfCRduwv2sIy3m1wnWDUubvbpejp/Q==} + '@aws-sdk/credential-provider-node@3.936.0': + resolution: {integrity: sha512-rk/2PCtxX9xDsQW8p5Yjoca3StqmQcSfkmD7nQ61AqAHL1YgpSQWqHE+HjfGGiHDYKG7PvE33Ku2GyA7lEIJAw==} engines: {node: '>=18.0.0'} - '@aws-sdk/credential-provider-sso@3.934.0': - resolution: {integrity: sha512-7wO86w95V9MZSYo2dunBKruKHdAUmgg9ccOSJSYGnPip1PPBK/rgSgQ8mDlYtFAW3/82bdeM/668QcgLT4+ofA==} + '@aws-sdk/credential-provider-process@3.936.0': + resolution: {integrity: sha512-GpA4AcHb96KQK2PSPUyvChvrsEKiLhQ5NWjeef2IZ3Jc8JoosiedYqp6yhZR+S8cTysuvx56WyJIJc8y8OTrLA==} engines: {node: '>=18.0.0'} - '@aws-sdk/credential-provider-web-identity@3.934.0': - resolution: {integrity: sha512-hb+lvFxiAPcAvUorB0hrUd1kDjDRXhZgCi5426I8KUpGzZ+ALh8/ep0KXAiYe2yg9ZkyMUbMaMvYYhMFcbXRFA==} + '@aws-sdk/credential-provider-sso@3.936.0': + resolution: {integrity: sha512-wHlEAJJvtnSyxTfNhN98JcU4taA1ED2JvuI2eePgawqBwS/Tzi0mhED1lvNIaWOkjfLd+nHALwszGrtJwEq4yQ==} engines: {node: '>=18.0.0'} - '@aws-sdk/middleware-bucket-endpoint@3.930.0': - resolution: {integrity: sha512-cnCLWeKPYgvV4yRYPFH6pWMdUByvu2cy2BAlfsPpvnm4RaVioztyvxmQj5PmVN5fvWs5w/2d6U7le8X9iye2sA==} + '@aws-sdk/credential-provider-web-identity@3.936.0': + resolution: {integrity: sha512-v3qHAuoODkoRXsAF4RG+ZVO6q2P9yYBT4GMpMEfU9wXVNn7AIfwZgTwzSUfnjNiGva5BKleWVpRpJ9DeuLFbUg==} engines: {node: '>=18.0.0'} - '@aws-sdk/middleware-expect-continue@3.930.0': - resolution: {integrity: sha512-5HEQ+JU4DrLNWeY27wKg/jeVa8Suy62ivJHOSUf6e6hZdVIMx0h/kXS1fHEQNNiLu2IzSEP/bFXsKBaW7x7s0g==} + '@aws-sdk/middleware-bucket-endpoint@3.936.0': + resolution: {integrity: sha512-XLSVVfAorUxZh6dzF+HTOp4R1B5EQcdpGcPliWr0KUj2jukgjZEcqbBmjyMF/p9bmyQsONX80iURF1HLAlW0qg==} engines: {node: '>=18.0.0'} - '@aws-sdk/middleware-flexible-checksums@3.934.0': - resolution: {integrity: sha512-kAV0fhwUhh/CV8hR5iip+du5QSXvIsONERVY/iJPbiBItqsmFaWcwiZE9E+ORJPNyoT/3X17632W33pCweKGDQ==} + '@aws-sdk/middleware-expect-continue@3.936.0': + resolution: {integrity: sha512-Eb4ELAC23bEQLJmUMYnPWcjD3FZIsmz2svDiXEcxRkQU9r7NRID7pM7C5NPH94wOfiCk0b2Y8rVyFXW0lGQwbA==} engines: {node: '>=18.0.0'} - '@aws-sdk/middleware-host-header@3.930.0': - resolution: {integrity: sha512-x30jmm3TLu7b/b+67nMyoV0NlbnCVT5DI57yDrhXAPCtdgM1KtdLWt45UcHpKOm1JsaIkmYRh2WYu7Anx4MG0g==} + '@aws-sdk/middleware-flexible-checksums@3.936.0': + resolution: {integrity: sha512-l3GG6CrSQtMCM6fWY7foV3JQv0WJWT+3G6PSP3Ceb/KEE/5Lz5PrYFXTBf+bVoYL1b0bGjGajcgAXpstBmtHtQ==} engines: {node: '>=18.0.0'} - '@aws-sdk/middleware-location-constraint@3.930.0': - resolution: {integrity: sha512-QIGNsNUdRICog+LYqmtJ03PLze6h2KCORXUs5td/hAEjVP5DMmubhtrGg1KhWyctACluUH/E/yrD14p4pRXxwA==} + '@aws-sdk/middleware-host-header@3.936.0': + resolution: {integrity: sha512-tAaObaAnsP1XnLGndfkGWFuzrJYuk9W0b/nLvol66t8FZExIAf/WdkT2NNAWOYxljVs++oHnyHBCxIlaHrzSiw==} engines: {node: '>=18.0.0'} - '@aws-sdk/middleware-logger@3.930.0': - resolution: {integrity: sha512-vh4JBWzMCBW8wREvAwoSqB2geKsZwSHTa0nSt0OMOLp2PdTYIZDi0ZiVMmpfnjcx9XbS6aSluLv9sKx4RrG46A==} + '@aws-sdk/middleware-location-constraint@3.936.0': + resolution: {integrity: sha512-SCMPenDtQMd9o5da9JzkHz838w3327iqXk3cbNnXWqnNRx6unyW8FL0DZ84gIY12kAyVHz5WEqlWuekc15ehfw==} engines: {node: '>=18.0.0'} - '@aws-sdk/middleware-recursion-detection@3.933.0': - resolution: {integrity: sha512-qgrMlkVKzTCAdNw2A05DC2sPBo0KRQ7wk+lbYSRJnWVzcrceJhnmhoZVV5PFv7JtchK7sHVcfm9lcpiyd+XaCA==} + '@aws-sdk/middleware-logger@3.936.0': + resolution: {integrity: sha512-aPSJ12d3a3Ea5nyEnLbijCaaYJT2QjQ9iW+zGh5QcZYXmOGWbKVyPSxmVOboZQG+c1M8t6d2O7tqrwzIq8L8qw==} engines: {node: '>=18.0.0'} - '@aws-sdk/middleware-sdk-s3@3.934.0': - resolution: {integrity: sha512-eU2R7pVOhCxnkDzq9mW+xh4WvCA3mdXVUHezIcJNFyKCKKv/c9I4WFcnMnUy+wnCWO2mzN/gwSgQxADkvxfLNQ==} + '@aws-sdk/middleware-recursion-detection@3.936.0': + resolution: {integrity: sha512-l4aGbHpXM45YNgXggIux1HgsCVAvvBoqHPkqLnqMl9QVapfuSTjJHfDYDsx1Xxct6/m7qSMUzanBALhiaGO2fA==} engines: {node: '>=18.0.0'} - '@aws-sdk/middleware-ssec@3.930.0': - resolution: {integrity: sha512-N2/SvodmaDS6h7CWfuapt3oJyn1T2CBz0CsDIiTDv9cSagXAVFjPdm2g4PFJqrNBeqdDIoYBnnta336HmamWHg==} + '@aws-sdk/middleware-sdk-s3@3.936.0': + resolution: {integrity: sha512-UQs/pVq4cOygsnKON0pOdSKIWkfgY0dzq4h+fR+xHi/Ng3XzxPJhWeAE6tDsKrcyQc1X8UdSbS70XkfGYr5hng==} engines: {node: '>=18.0.0'} - '@aws-sdk/middleware-user-agent@3.934.0': - resolution: {integrity: sha512-68giGM2Zm9K6Qas14ws3Qo5wafpn0I8/L64fS9E6Rc6Tu0k+So73hupysw+9ZOzHwQS5FEBUqLOMtbUibAcjNA==} + '@aws-sdk/middleware-ssec@3.936.0': + resolution: {integrity: sha512-/GLC9lZdVp05ozRik5KsuODR/N7j+W+2TbfdFL3iS+7un+gnP6hC8RDOZd6WhpZp7drXQ9guKiTAxkZQwzS8DA==} engines: {node: '>=18.0.0'} - '@aws-sdk/nested-clients@3.934.0': - resolution: {integrity: sha512-kRO61EMrDR4UuPlKAkziG6urcYXlhrFW/Ce5PjWFdjkm0ZOge75OFV1vhf/vE4Pmoop9jaAONX4E5BaIYrIQfg==} + '@aws-sdk/middleware-user-agent@3.936.0': + resolution: {integrity: sha512-YB40IPa7K3iaYX0lSnV9easDOLPLh+fJyUDF3BH8doX4i1AOSsYn86L4lVldmOaSX+DwiaqKHpvk4wPBdcIPWw==} engines: {node: '>=18.0.0'} - '@aws-sdk/region-config-resolver@3.930.0': - resolution: {integrity: sha512-KL2JZqH6aYeQssu1g1KuWsReupdfOoxD6f1as2VC+rdwYFUu4LfzMsFfXnBvvQWWqQ7rZHWOw1T+o5gJmg7Dzw==} + '@aws-sdk/nested-clients@3.936.0': + resolution: {integrity: sha512-eyj2tz1XmDSLSZQ5xnB7cLTVKkSJnYAEoNDSUNhzWPxrBDYeJzIbatecOKceKCU8NBf8gWWZCK/CSY0mDxMO0A==} engines: {node: '>=18.0.0'} - '@aws-sdk/signature-v4-multi-region@3.934.0': - resolution: {integrity: sha512-cLphxVoHapSdouAdLSDEwR2Bktjg5dc11EpSpaLo8jcFpAXhFaDllKBfDfws0EqGY6N2CMqEjqPqxDFzmmQOQA==} + '@aws-sdk/region-config-resolver@3.936.0': + resolution: {integrity: sha512-wOKhzzWsshXGduxO4pqSiNyL9oUtk4BEvjWm9aaq6Hmfdoydq6v6t0rAGHWPjFwy9z2haovGRi3C8IxdMB4muw==} engines: {node: '>=18.0.0'} - '@aws-sdk/token-providers@3.934.0': - resolution: {integrity: sha512-M0WEmgXDdUxapSfjplqJoVCBMcn0vQ5Jou0X/XiQwyVDbfvIyNSHUHyMXEIBAew9kVx9sfMMEYz3LXewvQxdCA==} + '@aws-sdk/signature-v4-multi-region@3.936.0': + resolution: {integrity: sha512-8qS0GFUqkmwO7JZ0P8tdluBmt1UTfYUah8qJXGzNh9n1Pcb0AIeT117cCSiCUtwk+gDbJvd4hhRIhJCNr5wgjg==} engines: {node: '>=18.0.0'} - '@aws-sdk/types@3.930.0': - resolution: {integrity: sha512-we/vaAgwlEFW7IeftmCLlLMw+6hFs3DzZPJw7lVHbj/5HJ0bz9gndxEsS2lQoeJ1zhiiLqAqvXxmM43s0MBg0A==} + '@aws-sdk/token-providers@3.936.0': + resolution: {integrity: sha512-vvw8+VXk0I+IsoxZw0mX9TMJawUJvEsg3EF7zcCSetwhNPAU8Xmlhv7E/sN/FgSmm7b7DsqKoW6rVtQiCs1PWQ==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/types@3.936.0': + resolution: {integrity: sha512-uz0/VlMd2pP5MepdrHizd+T+OKfyK4r3OA9JI+L/lPKg0YFQosdJNCKisr6o70E3dh8iMpFYxF1UN/4uZsyARg==} engines: {node: '>=18.0.0'} '@aws-sdk/util-arn-parser@3.893.0': resolution: {integrity: sha512-u8H4f2Zsi19DGnwj5FSZzDMhytYF/bCh37vAtBsn3cNDL3YG578X5oc+wSX54pM3tOxS+NY7tvOAo52SW7koUA==} engines: {node: '>=18.0.0'} - '@aws-sdk/util-endpoints@3.930.0': - resolution: {integrity: sha512-M2oEKBzzNAYr136RRc6uqw3aWlwCxqTP1Lawps9E1d2abRPvl1p1ztQmmXp1Ak4rv8eByIZ+yQyKQ3zPdRG5dw==} + '@aws-sdk/util-endpoints@3.936.0': + resolution: {integrity: sha512-0Zx3Ntdpu+z9Wlm7JKUBOzS9EunwKAb4KdGUQQxDqh5Lc3ta5uBoub+FgmVuzwnmBu9U1Os8UuwVTH0Lgu+P5w==} engines: {node: '>=18.0.0'} '@aws-sdk/util-locate-window@3.893.0': resolution: {integrity: sha512-T89pFfgat6c8nMmpI8eKjBcDcgJq36+m9oiXbcUzeU55MP9ZuGgBomGjGnHaEyF36jenW9gmg3NfZDm0AO2XPg==} engines: {node: '>=18.0.0'} - '@aws-sdk/util-user-agent-browser@3.930.0': - resolution: {integrity: sha512-q6lCRm6UAe+e1LguM5E4EqM9brQlDem4XDcQ87NzEvlTW6GzmNCO0w1jS0XgCFXQHjDxjdlNFX+5sRbHijwklg==} + '@aws-sdk/util-user-agent-browser@3.936.0': + resolution: {integrity: sha512-eZ/XF6NxMtu+iCma58GRNRxSq4lHo6zHQLOZRIeL/ghqYJirqHdenMOwrzPettj60KWlv827RVebP9oNVrwZbw==} - '@aws-sdk/util-user-agent-node@3.934.0': - resolution: {integrity: sha512-vPRR4PaqNmuOQJSzq4EAVwFHUaSpPtgDgCEc7AYbArIy+59fckb6JNddlrjx4w4iWbqO0d+7OC5PtRcIk0AcZA==} + '@aws-sdk/util-user-agent-node@3.936.0': + resolution: {integrity: sha512-XOEc7PF9Op00pWV2AYCGDSu5iHgYjIO53Py2VUQTIvP7SRCaCsXmA33mjBvC2Ms6FhSyWNa4aK4naUGIz0hQcw==} engines: {node: '>=18.0.0'} peerDependencies: aws-crt: '>=1.0.0' @@ -259,8 +263,8 @@ packages: resolution: {integrity: sha512-YIfkD17GocxdmlUVc3ia52QhcWuRIUJonbF8A2CYfcWNV3HzvAqpcPeC0bYUhkK+8e8YO1ARnLKZQE0TlwzorA==} engines: {node: '>=18.0.0'} - '@aws/lambda-invoke-store@0.2.0': - resolution: {integrity: sha512-D1jAmAZQYMoPiacfgNf7AWhg3DFN3Wq/vQv3WINt9znwjzHp2x+WzdJFxxj7xZL7V1U79As6G8f7PorMYWBKsQ==} + '@aws/lambda-invoke-store@0.2.1': + resolution: {integrity: sha512-sIyFcoPZkTtNu9xFeEoynMef3bPJIAbOfUh+ueYcfhVl6xm2VRtMcMclSxmZCMnHHd4hlYKJeq/aggmBEWynww==} engines: {node: '>=18.0.0'} '@babel/code-frame@7.27.1': @@ -278,8 +282,8 @@ packages: '@borewit/text-codec@0.1.1': resolution: {integrity: sha512-5L/uBxmjaCIX5h8Z+uu+kA9BQLkc/Wl06UGR5ajNRxu+/XjonB5i8JpgFMrPj3LXTCPA0pv8yxUvbUi+QthGGA==} - '@cloudflare/workers-types@4.20251119.0': - resolution: {integrity: sha512-DfCMGhqhvOow3NDViL3T9SgAtogc+xjtAKCfL+aWpVcAcct31P8FSFFRqlZCjcXJ5Kqw/TOjD/87UEAXAqaQZA==} + '@cloudflare/workers-types@4.20251120.0': + resolution: {integrity: sha512-/uy0Oleot60ZS037I2mxR9NEft6eQYdknKBnM76W91I+7BKznzXKj2MtXMfSXTLsxyP+6MluYRNPrRCQDlk8kw==} '@colors/colors@1.6.0': resolution: {integrity: sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==} @@ -2713,9 +2717,9 @@ packages: resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} engines: {node: '>= 0.6'} - mime-types@3.0.1: - resolution: {integrity: sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==} - engines: {node: '>= 0.6'} + mime-types@3.0.2: + resolution: {integrity: sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==} + engines: {node: '>=18'} mime@4.1.0: resolution: {integrity: sha512-X5ju04+cAzsojXKes0B/S4tcYtFAJ6tTMuSPBEn9CPGlrWr8Fiw7qYeLT0XyH80HSoAoqWCaz+MWKh22P7G1cw==} @@ -3026,12 +3030,12 @@ packages: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} - puppeteer-core@24.30.0: - resolution: {integrity: sha512-2S3Smy0t0W4wJnNvDe7W0bE7wDmZjfZ3ljfMgJd6hn2Hq/f0jgN+x9PULZo2U3fu5UUIJ+JP8cNUGllu8P91Pg==} + puppeteer-core@24.31.0: + resolution: {integrity: sha512-pnAohhSZipWQoFpXuGV7xCZfaGhqcBR9C4pVrU0QSrcMi7tQMH9J9lDBqBvyMAHQqe8HCARuREqFuVKRQOgTvg==} engines: {node: '>=18'} - puppeteer@24.30.0: - resolution: {integrity: sha512-A5OtCi9WpiXBQgJ2vQiZHSyrAzQmO/WDsvghqlN4kgw21PhxA5knHUaUQq/N3EMt8CcvSS0RM+kmYLJmedR3TQ==} + puppeteer@24.31.0: + resolution: {integrity: sha512-q8y5yLxLD8xdZdzNWqdOL43NbfvUOp60SYhaLZQwHC9CdKldxQKXOyJAciOr7oUJfyAH/KgB2wKvqT2sFKoVXA==} engines: {node: '>=18'} hasBin: true @@ -3515,8 +3519,8 @@ packages: vfile@6.0.3: resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} - webdriver-bidi-protocol@0.3.8: - resolution: {integrity: sha512-21Yi2GhGntMc671vNBCjiAeEVknXjVRoyu+k+9xOMShu+ZQfpGQwnBqbNz/Sv4GXZ6JmutlPAi2nIJcrymAWuQ==} + webdriver-bidi-protocol@0.3.9: + resolution: {integrity: sha512-uIYvlRQ0PwtZR1EzHlTMol1G0lAlmOe6wPykF9a77AK3bkpvZHzIVxRE2ThOx5vjy2zISe0zhwf5rzuUfbo1PQ==} webidl-conversions@7.0.0: resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} @@ -3648,7 +3652,7 @@ snapshots: '@api.global/typedrequest': 3.1.10 '@api.global/typedrequest-interfaces': 3.0.19 '@api.global/typedsocket': 3.0.1 - '@cloudflare/workers-types': 4.20251119.0 + '@cloudflare/workers-types': 4.20251120.0 '@design.estate/dees-comms': 1.0.27 '@push.rocks/lik': 6.2.2 '@push.rocks/smartchok': 1.1.1 @@ -3711,20 +3715,20 @@ snapshots: '@aws-crypto/crc32@5.2.0': dependencies: '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.930.0 + '@aws-sdk/types': 3.936.0 tslib: 2.8.1 '@aws-crypto/crc32c@5.2.0': dependencies: '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.930.0 + '@aws-sdk/types': 3.936.0 tslib: 2.8.1 '@aws-crypto/sha1-browser@5.2.0': dependencies: '@aws-crypto/supports-web-crypto': 5.2.0 '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.930.0 + '@aws-sdk/types': 3.936.0 '@aws-sdk/util-locate-window': 3.893.0 '@smithy/util-utf8': 2.3.0 tslib: 2.8.1 @@ -3734,7 +3738,7 @@ snapshots: '@aws-crypto/sha256-js': 5.2.0 '@aws-crypto/supports-web-crypto': 5.2.0 '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.930.0 + '@aws-sdk/types': 3.936.0 '@aws-sdk/util-locate-window': 3.893.0 '@smithy/util-utf8': 2.3.0 tslib: 2.8.1 @@ -3742,7 +3746,7 @@ snapshots: '@aws-crypto/sha256-js@5.2.0': dependencies: '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.930.0 + '@aws-sdk/types': 3.936.0 tslib: 2.8.1 '@aws-crypto/supports-web-crypto@5.2.0': @@ -3751,33 +3755,33 @@ snapshots: '@aws-crypto/util@5.2.0': dependencies: - '@aws-sdk/types': 3.930.0 + '@aws-sdk/types': 3.936.0 '@smithy/util-utf8': 2.3.0 tslib: 2.8.1 - '@aws-sdk/client-s3@3.934.0': + '@aws-sdk/client-s3@3.936.0': dependencies: '@aws-crypto/sha1-browser': 5.2.0 '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/core': 3.934.0 - '@aws-sdk/credential-provider-node': 3.934.0 - '@aws-sdk/middleware-bucket-endpoint': 3.930.0 - '@aws-sdk/middleware-expect-continue': 3.930.0 - '@aws-sdk/middleware-flexible-checksums': 3.934.0 - '@aws-sdk/middleware-host-header': 3.930.0 - '@aws-sdk/middleware-location-constraint': 3.930.0 - '@aws-sdk/middleware-logger': 3.930.0 - '@aws-sdk/middleware-recursion-detection': 3.933.0 - '@aws-sdk/middleware-sdk-s3': 3.934.0 - '@aws-sdk/middleware-ssec': 3.930.0 - '@aws-sdk/middleware-user-agent': 3.934.0 - '@aws-sdk/region-config-resolver': 3.930.0 - '@aws-sdk/signature-v4-multi-region': 3.934.0 - '@aws-sdk/types': 3.930.0 - '@aws-sdk/util-endpoints': 3.930.0 - '@aws-sdk/util-user-agent-browser': 3.930.0 - '@aws-sdk/util-user-agent-node': 3.934.0 + '@aws-sdk/core': 3.936.0 + '@aws-sdk/credential-provider-node': 3.936.0 + '@aws-sdk/middleware-bucket-endpoint': 3.936.0 + '@aws-sdk/middleware-expect-continue': 3.936.0 + '@aws-sdk/middleware-flexible-checksums': 3.936.0 + '@aws-sdk/middleware-host-header': 3.936.0 + '@aws-sdk/middleware-location-constraint': 3.936.0 + '@aws-sdk/middleware-logger': 3.936.0 + '@aws-sdk/middleware-recursion-detection': 3.936.0 + '@aws-sdk/middleware-sdk-s3': 3.936.0 + '@aws-sdk/middleware-ssec': 3.936.0 + '@aws-sdk/middleware-user-agent': 3.936.0 + '@aws-sdk/region-config-resolver': 3.936.0 + '@aws-sdk/signature-v4-multi-region': 3.936.0 + '@aws-sdk/types': 3.936.0 + '@aws-sdk/util-endpoints': 3.936.0 + '@aws-sdk/util-user-agent-browser': 3.936.0 + '@aws-sdk/util-user-agent-node': 3.936.0 '@smithy/config-resolver': 4.4.3 '@smithy/core': 3.18.5 '@smithy/eventstream-serde-browser': 4.2.5 @@ -3815,20 +3819,20 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/client-sso@3.934.0': + '@aws-sdk/client-sso@3.936.0': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/core': 3.934.0 - '@aws-sdk/middleware-host-header': 3.930.0 - '@aws-sdk/middleware-logger': 3.930.0 - '@aws-sdk/middleware-recursion-detection': 3.933.0 - '@aws-sdk/middleware-user-agent': 3.934.0 - '@aws-sdk/region-config-resolver': 3.930.0 - '@aws-sdk/types': 3.930.0 - '@aws-sdk/util-endpoints': 3.930.0 - '@aws-sdk/util-user-agent-browser': 3.930.0 - '@aws-sdk/util-user-agent-node': 3.934.0 + '@aws-sdk/core': 3.936.0 + '@aws-sdk/middleware-host-header': 3.936.0 + '@aws-sdk/middleware-logger': 3.936.0 + '@aws-sdk/middleware-recursion-detection': 3.936.0 + '@aws-sdk/middleware-user-agent': 3.936.0 + '@aws-sdk/region-config-resolver': 3.936.0 + '@aws-sdk/types': 3.936.0 + '@aws-sdk/util-endpoints': 3.936.0 + '@aws-sdk/util-user-agent-browser': 3.936.0 + '@aws-sdk/util-user-agent-node': 3.936.0 '@smithy/config-resolver': 4.4.3 '@smithy/core': 3.18.5 '@smithy/fetch-http-handler': 5.3.6 @@ -3858,9 +3862,9 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/core@3.934.0': + '@aws-sdk/core@3.936.0': dependencies: - '@aws-sdk/types': 3.930.0 + '@aws-sdk/types': 3.936.0 '@aws-sdk/xml-builder': 3.930.0 '@smithy/core': 3.18.5 '@smithy/node-config-provider': 4.3.5 @@ -3874,18 +3878,18 @@ snapshots: '@smithy/util-utf8': 4.2.0 tslib: 2.8.1 - '@aws-sdk/credential-provider-env@3.934.0': + '@aws-sdk/credential-provider-env@3.936.0': dependencies: - '@aws-sdk/core': 3.934.0 - '@aws-sdk/types': 3.930.0 + '@aws-sdk/core': 3.936.0 + '@aws-sdk/types': 3.936.0 '@smithy/property-provider': 4.2.5 '@smithy/types': 4.9.0 tslib: 2.8.1 - '@aws-sdk/credential-provider-http@3.934.0': + '@aws-sdk/credential-provider-http@3.936.0': dependencies: - '@aws-sdk/core': 3.934.0 - '@aws-sdk/types': 3.930.0 + '@aws-sdk/core': 3.936.0 + '@aws-sdk/types': 3.936.0 '@smithy/fetch-http-handler': 5.3.6 '@smithy/node-http-handler': 4.4.5 '@smithy/property-provider': 4.2.5 @@ -3895,16 +3899,17 @@ snapshots: '@smithy/util-stream': 4.5.6 tslib: 2.8.1 - '@aws-sdk/credential-provider-ini@3.934.0': + '@aws-sdk/credential-provider-ini@3.936.0': dependencies: - '@aws-sdk/core': 3.934.0 - '@aws-sdk/credential-provider-env': 3.934.0 - '@aws-sdk/credential-provider-http': 3.934.0 - '@aws-sdk/credential-provider-process': 3.934.0 - '@aws-sdk/credential-provider-sso': 3.934.0 - '@aws-sdk/credential-provider-web-identity': 3.934.0 - '@aws-sdk/nested-clients': 3.934.0 - '@aws-sdk/types': 3.930.0 + '@aws-sdk/core': 3.936.0 + '@aws-sdk/credential-provider-env': 3.936.0 + '@aws-sdk/credential-provider-http': 3.936.0 + '@aws-sdk/credential-provider-login': 3.936.0 + '@aws-sdk/credential-provider-process': 3.936.0 + '@aws-sdk/credential-provider-sso': 3.936.0 + '@aws-sdk/credential-provider-web-identity': 3.936.0 + '@aws-sdk/nested-clients': 3.936.0 + '@aws-sdk/types': 3.936.0 '@smithy/credential-provider-imds': 4.2.5 '@smithy/property-provider': 4.2.5 '@smithy/shared-ini-file-loader': 4.4.0 @@ -3913,15 +3918,28 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/credential-provider-node@3.934.0': + '@aws-sdk/credential-provider-login@3.936.0': dependencies: - '@aws-sdk/credential-provider-env': 3.934.0 - '@aws-sdk/credential-provider-http': 3.934.0 - '@aws-sdk/credential-provider-ini': 3.934.0 - '@aws-sdk/credential-provider-process': 3.934.0 - '@aws-sdk/credential-provider-sso': 3.934.0 - '@aws-sdk/credential-provider-web-identity': 3.934.0 - '@aws-sdk/types': 3.930.0 + '@aws-sdk/core': 3.936.0 + '@aws-sdk/nested-clients': 3.936.0 + '@aws-sdk/types': 3.936.0 + '@smithy/property-provider': 4.2.5 + '@smithy/protocol-http': 5.3.5 + '@smithy/shared-ini-file-loader': 4.4.0 + '@smithy/types': 4.9.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/credential-provider-node@3.936.0': + dependencies: + '@aws-sdk/credential-provider-env': 3.936.0 + '@aws-sdk/credential-provider-http': 3.936.0 + '@aws-sdk/credential-provider-ini': 3.936.0 + '@aws-sdk/credential-provider-process': 3.936.0 + '@aws-sdk/credential-provider-sso': 3.936.0 + '@aws-sdk/credential-provider-web-identity': 3.936.0 + '@aws-sdk/types': 3.936.0 '@smithy/credential-provider-imds': 4.2.5 '@smithy/property-provider': 4.2.5 '@smithy/shared-ini-file-loader': 4.4.0 @@ -3930,21 +3948,21 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/credential-provider-process@3.934.0': + '@aws-sdk/credential-provider-process@3.936.0': dependencies: - '@aws-sdk/core': 3.934.0 - '@aws-sdk/types': 3.930.0 + '@aws-sdk/core': 3.936.0 + '@aws-sdk/types': 3.936.0 '@smithy/property-provider': 4.2.5 '@smithy/shared-ini-file-loader': 4.4.0 '@smithy/types': 4.9.0 tslib: 2.8.1 - '@aws-sdk/credential-provider-sso@3.934.0': + '@aws-sdk/credential-provider-sso@3.936.0': dependencies: - '@aws-sdk/client-sso': 3.934.0 - '@aws-sdk/core': 3.934.0 - '@aws-sdk/token-providers': 3.934.0 - '@aws-sdk/types': 3.930.0 + '@aws-sdk/client-sso': 3.936.0 + '@aws-sdk/core': 3.936.0 + '@aws-sdk/token-providers': 3.936.0 + '@aws-sdk/types': 3.936.0 '@smithy/property-provider': 4.2.5 '@smithy/shared-ini-file-loader': 4.4.0 '@smithy/types': 4.9.0 @@ -3952,11 +3970,11 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/credential-provider-web-identity@3.934.0': + '@aws-sdk/credential-provider-web-identity@3.936.0': dependencies: - '@aws-sdk/core': 3.934.0 - '@aws-sdk/nested-clients': 3.934.0 - '@aws-sdk/types': 3.930.0 + '@aws-sdk/core': 3.936.0 + '@aws-sdk/nested-clients': 3.936.0 + '@aws-sdk/types': 3.936.0 '@smithy/property-provider': 4.2.5 '@smithy/shared-ini-file-loader': 4.4.0 '@smithy/types': 4.9.0 @@ -3964,9 +3982,9 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/middleware-bucket-endpoint@3.930.0': + '@aws-sdk/middleware-bucket-endpoint@3.936.0': dependencies: - '@aws-sdk/types': 3.930.0 + '@aws-sdk/types': 3.936.0 '@aws-sdk/util-arn-parser': 3.893.0 '@smithy/node-config-provider': 4.3.5 '@smithy/protocol-http': 5.3.5 @@ -3974,20 +3992,20 @@ snapshots: '@smithy/util-config-provider': 4.2.0 tslib: 2.8.1 - '@aws-sdk/middleware-expect-continue@3.930.0': + '@aws-sdk/middleware-expect-continue@3.936.0': dependencies: - '@aws-sdk/types': 3.930.0 + '@aws-sdk/types': 3.936.0 '@smithy/protocol-http': 5.3.5 '@smithy/types': 4.9.0 tslib: 2.8.1 - '@aws-sdk/middleware-flexible-checksums@3.934.0': + '@aws-sdk/middleware-flexible-checksums@3.936.0': dependencies: '@aws-crypto/crc32': 5.2.0 '@aws-crypto/crc32c': 5.2.0 '@aws-crypto/util': 5.2.0 - '@aws-sdk/core': 3.934.0 - '@aws-sdk/types': 3.930.0 + '@aws-sdk/core': 3.936.0 + '@aws-sdk/types': 3.936.0 '@smithy/is-array-buffer': 4.2.0 '@smithy/node-config-provider': 4.3.5 '@smithy/protocol-http': 5.3.5 @@ -3997,37 +4015,37 @@ snapshots: '@smithy/util-utf8': 4.2.0 tslib: 2.8.1 - '@aws-sdk/middleware-host-header@3.930.0': + '@aws-sdk/middleware-host-header@3.936.0': dependencies: - '@aws-sdk/types': 3.930.0 + '@aws-sdk/types': 3.936.0 '@smithy/protocol-http': 5.3.5 '@smithy/types': 4.9.0 tslib: 2.8.1 - '@aws-sdk/middleware-location-constraint@3.930.0': + '@aws-sdk/middleware-location-constraint@3.936.0': dependencies: - '@aws-sdk/types': 3.930.0 + '@aws-sdk/types': 3.936.0 '@smithy/types': 4.9.0 tslib: 2.8.1 - '@aws-sdk/middleware-logger@3.930.0': + '@aws-sdk/middleware-logger@3.936.0': dependencies: - '@aws-sdk/types': 3.930.0 + '@aws-sdk/types': 3.936.0 '@smithy/types': 4.9.0 tslib: 2.8.1 - '@aws-sdk/middleware-recursion-detection@3.933.0': + '@aws-sdk/middleware-recursion-detection@3.936.0': dependencies: - '@aws-sdk/types': 3.930.0 - '@aws/lambda-invoke-store': 0.2.0 + '@aws-sdk/types': 3.936.0 + '@aws/lambda-invoke-store': 0.2.1 '@smithy/protocol-http': 5.3.5 '@smithy/types': 4.9.0 tslib: 2.8.1 - '@aws-sdk/middleware-sdk-s3@3.934.0': + '@aws-sdk/middleware-sdk-s3@3.936.0': dependencies: - '@aws-sdk/core': 3.934.0 - '@aws-sdk/types': 3.930.0 + '@aws-sdk/core': 3.936.0 + '@aws-sdk/types': 3.936.0 '@aws-sdk/util-arn-parser': 3.893.0 '@smithy/core': 3.18.5 '@smithy/node-config-provider': 4.3.5 @@ -4041,36 +4059,36 @@ snapshots: '@smithy/util-utf8': 4.2.0 tslib: 2.8.1 - '@aws-sdk/middleware-ssec@3.930.0': + '@aws-sdk/middleware-ssec@3.936.0': dependencies: - '@aws-sdk/types': 3.930.0 + '@aws-sdk/types': 3.936.0 '@smithy/types': 4.9.0 tslib: 2.8.1 - '@aws-sdk/middleware-user-agent@3.934.0': + '@aws-sdk/middleware-user-agent@3.936.0': dependencies: - '@aws-sdk/core': 3.934.0 - '@aws-sdk/types': 3.930.0 - '@aws-sdk/util-endpoints': 3.930.0 + '@aws-sdk/core': 3.936.0 + '@aws-sdk/types': 3.936.0 + '@aws-sdk/util-endpoints': 3.936.0 '@smithy/core': 3.18.5 '@smithy/protocol-http': 5.3.5 '@smithy/types': 4.9.0 tslib: 2.8.1 - '@aws-sdk/nested-clients@3.934.0': + '@aws-sdk/nested-clients@3.936.0': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/core': 3.934.0 - '@aws-sdk/middleware-host-header': 3.930.0 - '@aws-sdk/middleware-logger': 3.930.0 - '@aws-sdk/middleware-recursion-detection': 3.933.0 - '@aws-sdk/middleware-user-agent': 3.934.0 - '@aws-sdk/region-config-resolver': 3.930.0 - '@aws-sdk/types': 3.930.0 - '@aws-sdk/util-endpoints': 3.930.0 - '@aws-sdk/util-user-agent-browser': 3.930.0 - '@aws-sdk/util-user-agent-node': 3.934.0 + '@aws-sdk/core': 3.936.0 + '@aws-sdk/middleware-host-header': 3.936.0 + '@aws-sdk/middleware-logger': 3.936.0 + '@aws-sdk/middleware-recursion-detection': 3.936.0 + '@aws-sdk/middleware-user-agent': 3.936.0 + '@aws-sdk/region-config-resolver': 3.936.0 + '@aws-sdk/types': 3.936.0 + '@aws-sdk/util-endpoints': 3.936.0 + '@aws-sdk/util-user-agent-browser': 3.936.0 + '@aws-sdk/util-user-agent-node': 3.936.0 '@smithy/config-resolver': 4.4.3 '@smithy/core': 3.18.5 '@smithy/fetch-http-handler': 5.3.6 @@ -4100,28 +4118,28 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/region-config-resolver@3.930.0': + '@aws-sdk/region-config-resolver@3.936.0': dependencies: - '@aws-sdk/types': 3.930.0 + '@aws-sdk/types': 3.936.0 '@smithy/config-resolver': 4.4.3 '@smithy/node-config-provider': 4.3.5 '@smithy/types': 4.9.0 tslib: 2.8.1 - '@aws-sdk/signature-v4-multi-region@3.934.0': + '@aws-sdk/signature-v4-multi-region@3.936.0': dependencies: - '@aws-sdk/middleware-sdk-s3': 3.934.0 - '@aws-sdk/types': 3.930.0 + '@aws-sdk/middleware-sdk-s3': 3.936.0 + '@aws-sdk/types': 3.936.0 '@smithy/protocol-http': 5.3.5 '@smithy/signature-v4': 5.3.5 '@smithy/types': 4.9.0 tslib: 2.8.1 - '@aws-sdk/token-providers@3.934.0': + '@aws-sdk/token-providers@3.936.0': dependencies: - '@aws-sdk/core': 3.934.0 - '@aws-sdk/nested-clients': 3.934.0 - '@aws-sdk/types': 3.930.0 + '@aws-sdk/core': 3.936.0 + '@aws-sdk/nested-clients': 3.936.0 + '@aws-sdk/types': 3.936.0 '@smithy/property-provider': 4.2.5 '@smithy/shared-ini-file-loader': 4.4.0 '@smithy/types': 4.9.0 @@ -4129,7 +4147,7 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/types@3.930.0': + '@aws-sdk/types@3.936.0': dependencies: '@smithy/types': 4.9.0 tslib: 2.8.1 @@ -4138,9 +4156,9 @@ snapshots: dependencies: tslib: 2.8.1 - '@aws-sdk/util-endpoints@3.930.0': + '@aws-sdk/util-endpoints@3.936.0': dependencies: - '@aws-sdk/types': 3.930.0 + '@aws-sdk/types': 3.936.0 '@smithy/types': 4.9.0 '@smithy/url-parser': 4.2.5 '@smithy/util-endpoints': 3.2.5 @@ -4150,17 +4168,17 @@ snapshots: dependencies: tslib: 2.8.1 - '@aws-sdk/util-user-agent-browser@3.930.0': + '@aws-sdk/util-user-agent-browser@3.936.0': dependencies: - '@aws-sdk/types': 3.930.0 + '@aws-sdk/types': 3.936.0 '@smithy/types': 4.9.0 bowser: 2.12.1 tslib: 2.8.1 - '@aws-sdk/util-user-agent-node@3.934.0': + '@aws-sdk/util-user-agent-node@3.936.0': dependencies: - '@aws-sdk/middleware-user-agent': 3.934.0 - '@aws-sdk/types': 3.930.0 + '@aws-sdk/middleware-user-agent': 3.936.0 + '@aws-sdk/types': 3.936.0 '@smithy/node-config-provider': 4.3.5 '@smithy/types': 4.9.0 tslib: 2.8.1 @@ -4171,7 +4189,7 @@ snapshots: fast-xml-parser: 5.2.5 tslib: 2.8.1 - '@aws/lambda-invoke-store@0.2.0': {} + '@aws/lambda-invoke-store@0.2.1': {} '@babel/code-frame@7.27.1': dependencies: @@ -4185,7 +4203,7 @@ snapshots: '@borewit/text-codec@0.1.1': {} - '@cloudflare/workers-types@4.20251119.0': {} + '@cloudflare/workers-types@4.20251120.0': {} '@colors/colors@1.6.0': {} @@ -4720,7 +4738,7 @@ snapshots: '@push.rocks/smartbucket@3.3.10': dependencies: - '@aws-sdk/client-s3': 3.934.0 + '@aws-sdk/client-s3': 3.936.0 '@push.rocks/smartmime': 2.0.4 '@push.rocks/smartpath': 6.0.0 '@push.rocks/smartpromise': 4.2.3 @@ -5065,7 +5083,7 @@ snapshots: dependencies: '@push.rocks/smartdelay': 3.0.5 '@push.rocks/smartshell': 3.3.0 - puppeteer: 24.30.0(typescript@5.9.3) + puppeteer: 24.31.0(typescript@5.9.3) tree-kill: 1.2.2 transitivePeerDependencies: - bare-abort-controller @@ -5983,7 +6001,7 @@ snapshots: accepts@2.0.0: dependencies: - mime-types: 3.0.1 + mime-types: 3.0.2 negotiator: 1.0.0 acme-client@5.4.0: @@ -6566,7 +6584,7 @@ snapshots: fresh: 2.0.0 http-errors: 2.0.0 merge-descriptors: 2.0.0 - mime-types: 3.0.1 + mime-types: 3.0.2 on-finished: 2.4.1 once: 1.4.0 parseurl: 1.3.3 @@ -7539,7 +7557,7 @@ snapshots: dependencies: mime-db: 1.52.0 - mime-types@3.0.1: + mime-types@3.0.2: dependencies: mime-db: 1.54.0 @@ -7834,14 +7852,14 @@ snapshots: punycode@2.3.1: {} - puppeteer-core@24.30.0: + puppeteer-core@24.31.0: dependencies: '@puppeteer/browsers': 2.10.13 chromium-bidi: 11.0.0(devtools-protocol@0.0.1521046) debug: 4.4.3 devtools-protocol: 0.0.1521046 typed-query-selector: 2.12.0 - webdriver-bidi-protocol: 0.3.8 + webdriver-bidi-protocol: 0.3.9 ws: 8.18.3 transitivePeerDependencies: - bare-abort-controller @@ -7851,13 +7869,13 @@ snapshots: - supports-color - utf-8-validate - puppeteer@24.30.0(typescript@5.9.3): + puppeteer@24.31.0(typescript@5.9.3): dependencies: '@puppeteer/browsers': 2.10.13 chromium-bidi: 11.0.0(devtools-protocol@0.0.1521046) cosmiconfig: 9.0.0(typescript@5.9.3) devtools-protocol: 0.0.1521046 - puppeteer-core: 24.30.0 + puppeteer-core: 24.31.0 typed-query-selector: 2.12.0 transitivePeerDependencies: - bare-abort-controller @@ -8057,7 +8075,7 @@ snapshots: etag: 1.8.1 fresh: 2.0.0 http-errors: 2.0.0 - mime-types: 3.0.1 + mime-types: 3.0.2 ms: 2.1.3 on-finished: 2.4.1 range-parser: 1.2.1 @@ -8379,7 +8397,7 @@ snapshots: dependencies: content-type: 1.0.5 media-typer: 1.1.0 - mime-types: 3.0.1 + mime-types: 3.0.2 typed-query-selector@2.12.0: {} @@ -8457,7 +8475,7 @@ snapshots: '@types/unist': 3.0.3 vfile-message: 4.0.3 - webdriver-bidi-protocol@0.3.8: {} + webdriver-bidi-protocol@0.3.9: {} webidl-conversions@7.0.0: {} diff --git a/readme.hints.md b/readme.hints.md index 2f05519..b96c3ca 100644 --- a/readme.hints.md +++ b/readme.hints.md @@ -244,6 +244,131 @@ tstest test/specific.ts -w - Ignores changes matching the ignore patterns - Shows "Waiting for file changes..." between runs +## Phase 1 API Improvements (v3.1.0) + +### New Features Implemented + +#### 1. tap.postTask() - Global Teardown (COMPLETED) + +Added symmetric teardown method to complement `tap.preTask()`: + +**Implementation:** +- Created `PostTask` class in `ts_tapbundle/tapbundle.classes.posttask.ts` +- Mirrors PreTask structure with description and function +- Integrated into Tap class execution flow +- Runs after all tests complete but before global `afterAll` hook + +**Usage:** +```typescript +tap.postTask('cleanup database', async () => { + await cleanupDatabase(); +}); +``` + +**Execution Order:** +1. preTask hooks +2. Global beforeAll +3. Tests (with suite hooks) +4. **postTask hooks** โ† NEW +5. Global afterAll + +#### 2. Suite-Level beforeAll/afterAll (COMPLETED) + +Added once-per-suite lifecycle hooks: + +**Implementation:** +- Extended `ITestSuite` interface with `beforeAll` and `afterAll` properties +- Added `tap.beforeAll()` and `tap.afterAll()` methods +- Integrated into `_runSuite()` execution flow +- Properly handles nested suites + +**Usage:** +```typescript +tap.describe('Database Tests', () => { + tap.beforeAll(async () => { + await initializeDatabaseConnection(); // Runs once + }); + + tap.test('test 1', async () => {}); + tap.test('test 2', async () => {}); + + tap.afterAll(async () => { + await closeDatabaseConnection(); // Runs once + }); +}); +``` + +**Execution Order per Suite:** +1. Suite beforeAll โ† NEW +2. Suite beforeEach +3. Test +4. Suite afterEach +5. (Repeat 2-4 for each test) +6. Child suites (recursive) +7. Suite afterAll โ† NEW + +#### 3. tap.parallel() Fluent Entry Point (COMPLETED) + +Added fluent API for parallel test creation: + +**Implementation:** +- Updated `TestBuilder` class with `_parallel` flag +- Builder constructor accepts optional parallel parameter +- Added `tap.parallel()` method returning configured builder +- Fixed `testParallel()` to return TapTest (was void) + +**Usage:** +```typescript +// Simple parallel test +tap.parallel().test('fetch data', async () => {}); + +// With full configuration +tap + .parallel() + .tags('api', 'integration') + .retry(2) + .timeout(5000) + .test('configured parallel test', async () => {}); +``` + +**Benefits:** +- Consistent with other fluent builders (tags, priority, etc.) +- More discoverable than separate `testParallel()` method +- Allows chaining parallel with other configurations +- `testParallel()` kept for backward compatibility + +### Documentation Updates + +**tapbundle/readme.md:** +- Added suite-level beforeAll/afterAll documentation +- Documented postTask with execution order notes +- Added parallel() fluent API examples +- Expanded TapTools documentation with all methods +- Added "Additional Tap Methods" section for fail(), getSettings(), etc. +- Documented all previously undocumented methods + +### Tests + +**test/tapbundle/test.new-lifecycle.ts:** +- Tests postTask execution order +- Verifies suite-level beforeAll/afterAll +- Tests nested suite lifecycle +- Validates parallel() fluent API +- Confirms all execution order requirements + +**Test Results:** All 9 tests passing โœ… + +### Breaking Changes + +None - all changes are additive and backward compatible. + +### Migration Guide + +No migration needed. New features are opt-in: +- Continue using existing patterns +- Adopt new features incrementally +- `testParallel()` still works (recommended: switch to `parallel().test()`) + ## Fixed Issues ### tap.skip.test(), tap.todo(), and tap.only.test() (Fixed) diff --git a/test/tapbundle/test.new-lifecycle.ts b/test/tapbundle/test.new-lifecycle.ts new file mode 100644 index 0000000..f5daff2 --- /dev/null +++ b/test/tapbundle/test.new-lifecycle.ts @@ -0,0 +1,170 @@ +import { tap, expect } from '../../ts_tapbundle/index.js'; + +// Global state for testing new lifecycle features +const executionOrder: string[] = []; +let postTaskRan = false; + +// Test preTask and postTask +tap.preTask('setup environment', async () => { + executionOrder.push('preTask'); + console.log('๐Ÿ”ง PreTask: Setting up environment'); +}); + +tap.postTask('cleanup environment', async () => { + postTaskRan = true; + executionOrder.push('postTask'); + console.log('๐Ÿงน PostTask: Cleaning up environment'); +}); + +// Test suite-level beforeAll and afterAll +tap.describe('Suite with beforeAll/afterAll', () => { + tap.beforeAll(async () => { + executionOrder.push('suite-beforeAll'); + console.log('๐Ÿ”ฐ Suite beforeAll executed'); + }); + + tap.afterAll(async () => { + executionOrder.push('suite-afterAll'); + console.log('๐Ÿ Suite afterAll executed'); + }); + + tap.beforeEach(async () => { + executionOrder.push('suite-beforeEach'); + }); + + tap.afterEach(async () => { + executionOrder.push('suite-afterEach'); + }); + + tap.test('first test in suite', async () => { + executionOrder.push('test-1'); + expect(executionOrder).toContain('preTask'); + expect(executionOrder).toContain('suite-beforeAll'); + console.log('โœ“ Test 1 executed'); + }); + + tap.test('second test in suite', async () => { + executionOrder.push('test-2'); + expect(executionOrder).toContain('suite-beforeAll'); + console.log('โœ“ Test 2 executed'); + }); +}); + +// Test nested suites with beforeAll/afterAll +tap.describe('Parent Suite', () => { + tap.beforeAll(async () => { + executionOrder.push('parent-beforeAll'); + console.log('๐Ÿ”ฐ Parent beforeAll executed'); + }); + + tap.afterAll(async () => { + executionOrder.push('parent-afterAll'); + console.log('๐Ÿ Parent afterAll executed'); + }); + + tap.test('test in parent', async () => { + executionOrder.push('parent-test'); + expect(executionOrder).toContain('parent-beforeAll'); + }); + + tap.describe('Child Suite', () => { + tap.beforeAll(async () => { + executionOrder.push('child-beforeAll'); + console.log('๐Ÿ”ฐ Child beforeAll executed'); + }); + + tap.afterAll(async () => { + executionOrder.push('child-afterAll'); + console.log('๐Ÿ Child afterAll executed'); + }); + + tap.test('test in child', async () => { + executionOrder.push('child-test'); + expect(executionOrder).toContain('parent-beforeAll'); + expect(executionOrder).toContain('child-beforeAll'); + }); + }); +}); + +// Test parallel() fluent API +tap.parallel().test('parallel test 1', async () => { + await new Promise(resolve => setTimeout(resolve, 10)); + executionOrder.push('parallel-1'); + console.log('โšก Parallel test 1 executed'); + expect(true).toBeTrue(); +}); + +tap.parallel().test('parallel test 2', async () => { + await new Promise(resolve => setTimeout(resolve, 5)); + executionOrder.push('parallel-2'); + console.log('โšก Parallel test 2 executed'); + expect(true).toBeTrue(); +}); + +// Test parallel() with configuration +tap + .parallel() + .tags('integration', 'parallel') + .timeout(1000) + .test('configured parallel test', async () => { + executionOrder.push('parallel-configured'); + console.log('โšก Configured parallel test executed'); + expect(true).toBeTrue(); + }); + +// Verify execution order +tap.test('verify lifecycle execution order', async () => { + // Give a moment for any async operations to complete + await new Promise(resolve => setTimeout(resolve, 100)); + + console.log('๐Ÿ“Š Execution order:', executionOrder); + + // Verify preTask ran first + expect(executionOrder[0]).toEqual('preTask'); + + // Verify suite beforeAll ran before tests + const suiteBeforeAllIndex = executionOrder.indexOf('suite-beforeAll'); + const test1Index = executionOrder.indexOf('test-1'); + expect(suiteBeforeAllIndex).toBeLessThan(test1Index); + + // Verify beforeEach ran before each test + const beforeEachIndices = executionOrder + .map((item, index) => item === 'suite-beforeEach' ? index : -1) + .filter(index => index !== -1); + expect(beforeEachIndices.length).toBeGreaterThanOrEqual(2); + + // Verify afterEach ran after each test + const afterEachIndices = executionOrder + .map((item, index) => item === 'suite-afterEach' ? index : -1) + .filter(index => index !== -1); + expect(afterEachIndices.length).toBeGreaterThanOrEqual(2); + + // Verify afterAll ran after all tests + const suiteAfterAllIndex = executionOrder.indexOf('suite-afterAll'); + const test2Index = executionOrder.indexOf('test-2'); + expect(suiteAfterAllIndex).toBeGreaterThan(test2Index); + + // Verify nested suite lifecycle + expect(executionOrder).toContain('parent-beforeAll'); + expect(executionOrder).toContain('parent-test'); + expect(executionOrder).toContain('child-beforeAll'); + expect(executionOrder).toContain('child-test'); + expect(executionOrder).toContain('child-afterAll'); + expect(executionOrder).toContain('parent-afterAll'); + + // Verify parallel tests ran + expect(executionOrder).toContain('parallel-1'); + expect(executionOrder).toContain('parallel-2'); + expect(executionOrder).toContain('parallel-configured'); + + console.log('โœ… All lifecycle hooks executed in correct order'); +}); + +// This test will verify postTask ran (after tap.start() completes) +tap.test('verify postTask execution', async () => { + // PostTask hasn't run yet because tests are still running + expect(postTaskRan).toBeFalse(); + console.log('โœ“ Verified postTask will run after all tests'); +}); + +export default tap.start(); diff --git a/ts/00_commitinfo_data.ts b/ts/00_commitinfo_data.ts index 5aa6b1d..86e581d 100644 --- a/ts/00_commitinfo_data.ts +++ b/ts/00_commitinfo_data.ts @@ -3,6 +3,6 @@ */ export const commitinfo = { name: '@git.zone/tstest', - version: '3.0.1', + version: '3.1.0', description: 'a test utility to run tests that match test/**/*.ts' } diff --git a/ts_tapbundle/readme.md b/ts_tapbundle/readme.md index 11f0c5d..7223ac1 100644 --- a/ts_tapbundle/readme.md +++ b/ts_tapbundle/readme.md @@ -91,6 +91,24 @@ tap.testParallel('should fetch user data', async () => { }); ``` +**Note:** The `tap.parallel().test()` fluent API is now the recommended way to define parallel tests (see Fluent API section below). + +#### `tap.parallel()` + +Returns a fluent test builder configured for parallel execution. + +```typescript +tap.parallel().test('should fetch data', async () => { + // Parallel test +}); + +// With full configuration +tap.parallel() + .tags('api') + .retry(2) + .test('configured parallel test', async () => {}); +``` + #### `tap.describe(description, suiteFunction)` Create a test suite to group related tests. @@ -141,22 +159,56 @@ tap }); ``` +#### Parallel Tests with Fluent API + +Use `tap.parallel()` to create parallel tests with fluent configuration: + +```typescript +// Simple parallel test +tap.parallel().test('fetches user data', async () => { + // Runs in parallel with other parallel tests +}); + +// Parallel test with full configuration +tap + .parallel() + .tags('api', 'integration') + .retry(2) + .timeout(5000) + .test('should fetch data concurrently', async () => { + // Configured parallel test + }); +``` + +**Note:** `tap.parallel().test()` is the recommended way to define parallel tests. The older `tap.testParallel()` method is still supported for backward compatibility. + ### Lifecycle Hooks #### Suite-Level Hooks ```typescript tap.describe('Database Tests', () => { + tap.beforeAll(async (tapTools) => { + // Runs once before all tests in this suite + await initializeDatabaseConnection(); + }); + tap.beforeEach(async (tapTools) => { // Runs before each test in this suite + await clearTestData(); }); + tap.test('test 1', async () => { }); + tap.test('test 2', async () => { }); + tap.afterEach(async (tapTools) => { // Runs after each test in this suite }); - tap.test('test 1', async () => { }); - tap.test('test 2', async () => { }); + tap.afterAll(async (tapTools) => { + // Runs once after all tests in this suite + await closeDatabaseConnection(); + }); }); ``` @@ -267,38 +319,169 @@ TSTEST_FILTER_TAGS=unit tstest test/mytest.node.ts Each test receives a `tapTools` instance with utilities: +#### Test Control Methods + ```typescript -tap.test('should have utilities', async (tapTools) => { - // Mark test as skipped +tap.test('test control examples', async (tapTools) => { + // Skip this test + tapTools.skip('reason'); + + // Conditionally skip + tapTools.skipIf(condition, 'reason'); + + // Mark test as skipped before execution tapTools.markAsSkipped('reason'); // Mark as todo tapTools.todo('not implemented'); + // Allow test to fail without marking suite as failed + tapTools.allowFailure(); + // Configure retries tapTools.retry(3); - // Log test output - tapTools.log('debug message'); + // Set timeout + tapTools.timeout(5000); }); ``` +#### Utility Methods + +```typescript +tap.test('utility examples', async (tapTools) => { + // Delay execution + await tapTools.delayFor(1000); // Wait 1 second + await tapTools.delayForRandom(500, 1500); // Random delay + + // Colored console output + tapTools.coloredString('โœ“ Success', 'green'); + tapTools.coloredString('โœ— Error', 'red'); +}); +``` + +#### Context and Data Sharing + +```typescript +tap.test('first test', async (tapTools) => { + // Store data in context + tapTools.context.set('userId', '12345'); + + // Store in testData property + tapTools.testData = { username: 'alice' }; +}); + +tap.test('second test', async (tapTools) => { + // Retrieve from context + const userId = tapTools.context.get('userId'); + + // Check existence + if (tapTools.context.has('userId')) { + // Use data + } + + // Clear context + tapTools.context.clear(); +}); +``` + +#### Fixtures + +```typescript +// Define a fixture globally (outside tests) +import { TapTools } from '@git.zone/tstest/tapbundle'; + +TapTools.defineFixture('database', async () => { + const db = await createTestDatabase(); + return { + value: db, + cleanup: async () => await db.close() + }; +}); + +// Use fixtures in tests +tap.test('database test', async (tapTools) => { + const db = await tapTools.fixture('database'); + // Use db... + // Cleanup happens automatically +}); +``` + +#### Factory Pattern + +```typescript +// Define a factory +TapTools.defineFixture('user', async () => { + return { + value: null, // Not used for factories + factory: async (data) => { + return await createUser(data); + }, + cleanup: async (user) => await user.delete() + }; +}); + +// Use factory in tests +tap.test('user test', async (tapTools) => { + const user = await tapTools.factory('user').create({ name: 'Alice' }); + + // Create multiple + const users = await tapTools.factory('user').createMany([ + { name: 'Alice' }, + { name: 'Bob' } + ]); + + // Cleanup happens automatically +}); +``` + +#### Snapshot Testing + +```typescript +tap.test('snapshot test', async (tapTools) => { + const result = { name: 'Alice', age: 30 }; + + // Compare with stored snapshot + await tapTools.matchSnapshot(result); + + // Named snapshots + await tapTools.matchSnapshot(result, 'user-data'); +}); +``` + +To update snapshots, run with: +```bash +UPDATE_SNAPSHOTS=true tstest test/mytest.ts +``` + ## Advanced Features -### Pre-Tasks +### Pre-Tasks and Post-Tasks -Run setup tasks before any tests execute: +Run setup and teardown tasks before/after all tests: ```typescript tap.preTask('setup database', async () => { // Runs before any tests + await initializeDatabase(); }); tap.test('first test', async () => { // Database is ready }); + +tap.test('second test', async () => { + // Tests run... +}); + +tap.postTask('cleanup database', async () => { + // Runs after all tests complete + await cleanupDatabase(); +}); ``` +**Note:** Post tasks run after all tests but before the global `afterAll` hook. + ### Test Priority Organize tests by priority level: @@ -334,6 +517,50 @@ import { setProtocolEmitter } from '@git.zone/tstest/tapbundle'; // Events: test:started, test:completed, assertion:failed, suite:started, suite:completed ``` +### Additional Tap Methods + +#### Configuration and Inspection + +```typescript +// Get current test settings +const settings = tap.getSettings(); +console.log(settings.timeout, settings.retries); + +// Explicitly fail a test +tap.test('validation test', async () => { + if (invalidCondition) { + tap.fail('Custom failure message'); + } +}); +``` + +#### Advanced Control + +```typescript +// Force stop test execution +tap.stopForcefully(exitCode, immediate); + +// Handle thrown errors (internal use) +tap.threw(error); +``` + +#### Parallel Test Variants + +In addition to `tap.parallel().test()`, skip/only/todo modes also support parallel execution: + +```typescript +// Skip parallel test +tap.skip.testParallel('not ready', async () => {}); + +// Only run this parallel test +tap.only.testParallel('focus here', async () => {}); + +// Todo parallel test +tap.todo.testParallel('implement later'); +``` + +**Note:** Using `tap.parallel()` fluent API is recommended over these direct methods. + ## Best Practices 1. **Always export `tap.start()`** at the end of test files: diff --git a/ts_tapbundle/tapbundle.classes.posttask.ts b/ts_tapbundle/tapbundle.classes.posttask.ts new file mode 100644 index 0000000..08bf320 --- /dev/null +++ b/ts_tapbundle/tapbundle.classes.posttask.ts @@ -0,0 +1,21 @@ +import * as plugins from './tapbundle.plugins.js'; +import { TapTools } from './tapbundle.classes.taptools.js'; + +export interface IPostTaskFunction { + (tapTools?: TapTools): Promise; +} + +export class PostTask { + public description: string; + public postTaskFunction: IPostTaskFunction; + + constructor(descriptionArg: string, postTaskFunctionArg: IPostTaskFunction) { + this.description = descriptionArg; + this.postTaskFunction = postTaskFunctionArg; + } + + public async run() { + console.log(`::__POSTTASK: ${this.description}`); + await this.postTaskFunction(new TapTools(null)); + } +} diff --git a/ts_tapbundle/tapbundle.classes.tap.ts b/ts_tapbundle/tapbundle.classes.tap.ts index b86d12c..a259484 100644 --- a/ts_tapbundle/tapbundle.classes.tap.ts +++ b/ts_tapbundle/tapbundle.classes.tap.ts @@ -1,6 +1,7 @@ import * as plugins from './tapbundle.plugins.js'; import { type IPreTaskFunction, PreTask } from './tapbundle.classes.pretask.js'; +import { type IPostTaskFunction, PostTask } from './tapbundle.classes.posttask.js'; import { TapTest, type ITestFunction } from './tapbundle.classes.taptest.js'; import { ProtocolEmitter, type ITestEvent } from '../dist_ts_tapbundle_protocol/index.js'; import type { ITapSettings } from './tapbundle.interfaces.js'; @@ -9,6 +10,8 @@ import { SettingsManager } from './tapbundle.classes.settingsmanager.js'; export interface ITestSuite { description: string; tests: TapTest[]; + beforeAll?: ITestFunction; + afterAll?: ITestFunction; beforeEach?: ITestFunction; afterEach?: ITestFunction; parent?: ITestSuite; @@ -21,85 +24,89 @@ class TestBuilder { private _priority: 'high' | 'medium' | 'low' = 'medium'; private _retryCount?: number; private _timeoutMs?: number; - - constructor(tap: Tap) { + private _parallel: boolean = false; + + constructor(tap: Tap, parallel: boolean = false) { this._tap = tap; + this._parallel = parallel; } - + tags(...tags: string[]) { this._tags = tags; return this; } - + priority(level: 'high' | 'medium' | 'low') { this._priority = level; return this; } - + retry(count: number) { this._retryCount = count; return this; } - + timeout(ms: number) { this._timeoutMs = ms; return this; } - + test(description: string, testFunction: ITestFunction) { - const test = this._tap.test(description, testFunction, 'normal'); - + const test = this._parallel + ? this._tap.testParallel(description, testFunction) + : this._tap.test(description, testFunction, 'normal'); + // Apply settings to the test if (this._tags.length > 0) { test.tags = this._tags; } test.priority = this._priority; - + if (this._retryCount !== undefined) { test.tapTools.retry(this._retryCount); } if (this._timeoutMs !== undefined) { test.timeoutMs = this._timeoutMs; } - + return test; } - + testOnly(description: string, testFunction: ITestFunction) { const test = this._tap.test(description, testFunction, 'only'); - + // Apply settings to the test if (this._tags.length > 0) { test.tags = this._tags; } test.priority = this._priority; - + if (this._retryCount !== undefined) { test.tapTools.retry(this._retryCount); } if (this._timeoutMs !== undefined) { test.timeoutMs = this._timeoutMs; } - + return test; } - + testSkip(description: string, testFunction: ITestFunction) { const test = this._tap.test(description, testFunction, 'skip'); - + // Apply settings to the test if (this._tags.length > 0) { test.tags = this._tags; } test.priority = this._priority; - + if (this._retryCount !== undefined) { test.tapTools.retry(this._retryCount); } if (this._timeoutMs !== undefined) { test.timeoutMs = this._timeoutMs; } - + return test; } } @@ -122,21 +129,25 @@ export class Tap { const builder = new TestBuilder(this); return builder.tags(...tags); } - + public priority(level: 'high' | 'medium' | 'low') { const builder = new TestBuilder(this); return builder.priority(level); } - + public retry(count: number) { const builder = new TestBuilder(this); return builder.retry(count); } - + public timeout(ms: number) { const builder = new TestBuilder(this); return builder.timeout(ms); } + + public parallel() { + return new TestBuilder(this, true); + } /** * skips a test @@ -236,6 +247,7 @@ export class Tap { }; private _tapPreTasks: PreTask[] = []; + private _tapPostTasks: PostTask[] = []; private _tapTests: TapTest[] = []; private _tapTestsOnly: TapTest[] = []; private _currentSuite: ITestSuite | null = null; @@ -304,18 +316,22 @@ export class Tap { this._tapPreTasks.push(new PreTask(descriptionArg, functionArg)); } + public postTask(descriptionArg: string, functionArg: IPostTaskFunction) { + this._tapPostTasks.push(new PostTask(descriptionArg, functionArg)); + } + /** * A parallel test that will not be waited for before the next starts. * @param testDescription - A description of what the test does * @param testFunction - A Function that returns a Promise and resolves or rejects */ - public testParallel(testDescription: string, testFunction: ITestFunction) { + public testParallel(testDescription: string, testFunction: ITestFunction): TapTest { const localTest = new TapTest({ description: testDescription, testFunction, parallel: true, }); - + // Apply default settings from settings manager const settings = this.settingsManager.getSettings(); if (settings.timeout !== undefined) { @@ -324,12 +340,14 @@ export class Tap { if (settings.retries !== undefined) { localTest.tapTools.retry(settings.retries); } - + if (this._currentSuite) { this._currentSuite.tests.push(localTest); } else { this._tapTests.push(localTest); } + + return localTest; } /** @@ -360,6 +378,28 @@ export class Tap { } } + /** + * Set up a function to run once before all tests in the current suite + */ + public beforeAll(setupFunction: ITestFunction) { + if (this._currentSuite) { + this._currentSuite.beforeAll = setupFunction; + } else { + throw new Error('beforeAll can only be used inside a describe block'); + } + } + + /** + * Set up a function to run once after all tests in the current suite + */ + public afterAll(teardownFunction: ITestFunction) { + if (this._currentSuite) { + this._currentSuite.afterAll = teardownFunction; + } else { + throw new Error('afterAll can only be used inside a describe block'); + } + } + /** * Set up a function to run before each test in the current suite */ @@ -370,7 +410,7 @@ export class Tap { throw new Error('beforeEach can only be used inside a describe block'); } } - + /** * Set up a function to run after each test in the current suite */ @@ -554,6 +594,11 @@ export class Tap { console.log(failReason); } + // Run post tasks + for (const postTask of this._tapPostTasks) { + await postTask.run(); + } + // Run global afterAll hook if configured if (settings.afterAll) { try { @@ -597,6 +642,12 @@ export class Tap { suiteName: suite.description } }); + + // Run beforeAll hook for this suite + if (suite.beforeAll) { + await suite.beforeAll(new plugins.smartpromise.Deferred().promise as any); + } + // Run beforeEach from parent suites const beforeEachFunctions: ITestFunction[] = []; let currentSuite: ITestSuite | null = suite; @@ -666,7 +717,12 @@ export class Tap { // Recursively run child suites await this._runSuite(suite, suite.children, promiseArray, context); - + + // Run afterAll hook for this suite + if (suite.afterAll) { + await suite.afterAll(new plugins.smartpromise.Deferred().promise as any); + } + // Emit suite:completed event this.emitEvent({ eventType: 'suite:completed',