mirror of
https://github.com/pestphp/pest.git
synced 2026-03-06 15:57:21 +01:00
Compare commits
1563 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 45f16484d5 | |||
| b16e8650da | |||
| c2f30e0148 | |||
| 47ce45de56 | |||
| 32881774d2 | |||
| ea72461f1b | |||
| 49f15521e0 | |||
| 95c5394b66 | |||
| 8de30cc8b7 | |||
| b17feef3f4 | |||
| d8e4a405ad | |||
| 04af21183a | |||
| 09edaa9c2d | |||
| fa41a67be9 | |||
| 1a8f7025fa | |||
| 6afd2ec9df | |||
| d772069db2 | |||
| bb1a0b5e79 | |||
| 3c333ebbb8 | |||
| 868ac1840f | |||
| f857b4889c | |||
| c6b81e6e12 | |||
| c78d33b69e | |||
| bfd351783e | |||
| 526af2a75e | |||
| bf9d011045 | |||
| aaee0e420b | |||
| 772448db80 | |||
| e22fb2e6c0 | |||
| 49aa44c470 | |||
| 1cae035887 | |||
| 15183c4145 | |||
| ae288d1123 | |||
| 2d80ff19ec | |||
| c82f77ea75 | |||
| 5050ae304f | |||
| 98e947e0cc | |||
| 68785986a0 | |||
| 8c078087ff | |||
| 65f74f620c | |||
| dd20323ca7 | |||
| a7ca7afe4e | |||
| baf764f286 | |||
| 3a907c886b | |||
| e6823679dd | |||
| a021b5b8c3 | |||
| e2d360b1b5 | |||
| 8920b850e1 | |||
| 509074b3fa | |||
| 6f9ea14c68 | |||
| 60dcfb36a8 | |||
| ca25d5b13f | |||
| 5cba63e2ba | |||
| dd45a5c655 | |||
| dde943b993 | |||
| bb8677549a | |||
| 5ae5ac9a54 | |||
| cc6f1b43f6 | |||
| 88197fe1d5 | |||
| f53f855e9c | |||
| df69b0b791 | |||
| ecdbe7a472 | |||
| 2c6c3119d2 | |||
| 9ceb0834ae | |||
| 86d2191cae | |||
| 748beb17d5 | |||
| 7ba235f61a | |||
| 700bd517f4 | |||
| cbcfa2c5e2 | |||
| 243e45a551 | |||
| 9b11857ae6 | |||
| bb29e97200 | |||
| 8fe2698c28 | |||
| 823c3d4b17 | |||
| 39c9b15bc0 | |||
| b13acb630d | |||
| 172d94c0ca | |||
| f72d6f2278 | |||
| 71811d6e3a | |||
| dfdbd357e9 | |||
| 4d9ed8768c | |||
| 6638d279e1 | |||
| b5cd0ffb65 | |||
| 7ef40760c2 | |||
| ce4495b093 | |||
| 868547114f | |||
| 9c07dd9990 | |||
| 09beb812d4 | |||
| 4e98dfe3c3 | |||
| ad6dca94fa | |||
| 86f46c2efd | |||
| ccfd4fd77a | |||
| e4d2dac354 | |||
| 7e4c51e13d | |||
| aacd874ebe | |||
| 1c236aab26 | |||
| b6bf01148f | |||
| 347bcfd8a8 | |||
| 0ced3171b0 | |||
| 38638e865f | |||
| adbc6b4a89 | |||
| 9353015691 | |||
| 17058d1709 | |||
| 8ffa66dc7c | |||
| 0697555dc2 | |||
| af680ca8aa | |||
| 651aab560c | |||
| 41e50cac05 | |||
| 6fb1133d52 | |||
| 63ba117b33 | |||
| 33d36d77cb | |||
| 4e7db91ee8 | |||
| d0ff2c8ec2 | |||
| 5e41e546a0 | |||
| 45cce6ce93 | |||
| 6a8a4f3243 | |||
| 101e26749a | |||
| b3c8c24aea | |||
| ef29b4f091 | |||
| a7553b7593 | |||
| f2691623cf | |||
| 99107544ff | |||
| 2e411893d2 | |||
| 135c8a0d46 | |||
| 1cdd7d6744 | |||
| fca0c3a10c | |||
| 0331a87be1 | |||
| ef120125e0 | |||
| 8a9a416133 | |||
| 4783334f15 | |||
| d3be6b72dd | |||
| 7d3118db65 | |||
| eac7abebcb | |||
| 6896dd486a | |||
| 1e5b399603 | |||
| ccdf43726d | |||
| 67dbce2d42 | |||
| ee32f25485 | |||
| 09ca7a1fd5 | |||
| dade84e6b6 | |||
| 1c4bc8b1dc | |||
| 0d2f3eb60e | |||
| 29787d1ff1 | |||
| 474b9b7e17 | |||
| 5c3bf469d5 | |||
| d9252e85d6 | |||
| 0289466ce8 | |||
| 57ef989df8 | |||
| 9d02b649e2 | |||
| 00643312b7 | |||
| eac6585a2e | |||
| 04c39bae2e | |||
| c65755725d | |||
| ec58040f6e | |||
| 3fa73e40cc | |||
| c07513c6a0 | |||
| 85d91d5652 | |||
| 02bae3b649 | |||
| 3ba2b68afc | |||
| ed3ec79aab | |||
| 894dca83f7 | |||
| b873b89b62 | |||
| 1bee283d15 | |||
| 7b4dd410f6 | |||
| 4396ee2e03 | |||
| e4550c8d51 | |||
| a25cfb435c | |||
| fe4fe12df1 | |||
| 3bcc99a372 | |||
| e8f122bf47 | |||
| 9fc607a2b8 | |||
| 3ad788dddb | |||
| 2108d18be5 | |||
| aa4a5fcd15 | |||
| 1688888f15 | |||
| 40539ca720 | |||
| 7144d6dfbd | |||
| 7240250a15 | |||
| 508e42a2ff | |||
| d8156fee53 | |||
| abc245bf85 | |||
| 65dacd5647 | |||
| 917f7a64a0 | |||
| e8b09d6f8c | |||
| 0c4e6de823 | |||
| 52282cc590 | |||
| a46142d8c7 | |||
| 241dcf8f34 | |||
| 927cee609e | |||
| 98e4ebb8fd | |||
| c173e3e86b | |||
| c73655f4f9 | |||
| 4ac1c6efc6 | |||
| 2e5a308b0d | |||
| 7b8e4aec08 | |||
| 13fb66f15c | |||
| dd1bd92910 | |||
| d665b53b22 | |||
| c54b7e400e | |||
| c1e1fff0d0 | |||
| 2e4a8329a6 | |||
| 878988a02d | |||
| ceb7244b43 | |||
| 84256aa8b9 | |||
| d6b59e4e96 | |||
| 087d09120a | |||
| cc41a7f81d | |||
| bd16769b93 | |||
| 60b1e63c23 | |||
| c7bcb6eb7b | |||
| d25ec50384 | |||
| 9e27813897 | |||
| b33af71036 | |||
| 3c6c89a6ad | |||
| 55f6b5696e | |||
| ba914fa2fb | |||
| c919bb5bc4 | |||
| 8169382362 | |||
| 04b099e87c | |||
| fecdb7f572 | |||
| b611d0d444 | |||
| ac7199c96d | |||
| 7756457dc4 | |||
| 10da81eee4 | |||
| 8bbee3c1e5 | |||
| 16125df77b | |||
| 80530cb1e0 | |||
| 2070538fd3 | |||
| a2cb78710d | |||
| 335bfdb79d | |||
| cfa00da885 | |||
| f49d1e0e18 | |||
| 303f4c0113 | |||
| 35a1fcd0cf | |||
| adb2fb51df | |||
| a7a3e4240e | |||
| e4af33867b | |||
| 0c51b159a7 | |||
| c6984323c3 | |||
| 831d9bf49a | |||
| 12f6aa604c | |||
| 265f0c7da9 | |||
| 680111fb1e | |||
| aa6ff95ea4 | |||
| 863a0cc837 | |||
| 126a84a63e | |||
| 0ccbe5c8f0 | |||
| a4f8ae1a12 | |||
| 6094682158 | |||
| d519e40b95 | |||
| 6a1161ead8 | |||
| a1b3547dd6 | |||
| b9e3146a47 | |||
| ce1607cba9 | |||
| ac07bc1770 | |||
| 521a41dd10 | |||
| 1b68b340e8 | |||
| 853f6efce6 | |||
| 62a9a78ee2 | |||
| 78d9fd31d0 | |||
| 2c3234fb3d | |||
| 1b64fef7ba | |||
| a136231503 | |||
| 602b696348 | |||
| 5b0f88c227 | |||
| f31a2c3220 | |||
| e8fa98c810 | |||
| 07e314fbf5 | |||
| 4baf27911e | |||
| 12e48a14d1 | |||
| 1bc0f79508 | |||
| cb0f256791 | |||
| 7b9bae0415 | |||
| 3dffdf7cb8 | |||
| 923970a117 | |||
| b3db7dfd4c | |||
| b303f9f818 | |||
| d29997d5b0 | |||
| 13f340a742 | |||
| eeade88ad2 | |||
| 06280ef75d | |||
| aa46f73888 | |||
| 3660865e5e | |||
| 13695d597b | |||
| fab2de833f | |||
| 5b630bcdff | |||
| e70edbfa38 | |||
| b1558ddde5 | |||
| 582529377b | |||
| 88714598b6 | |||
| 5136267bbe | |||
| 19e748f0d4 | |||
| a53a9d03cf | |||
| edaa045283 | |||
| c5ce355f3c | |||
| 62d8459627 | |||
| a5bf6a3fcb | |||
| 7a46514df8 | |||
| cb1735f4d8 | |||
| 607a4906ac | |||
| 317ea0356e | |||
| 1153531104 | |||
| cfb724cd77 | |||
| 0060b6f955 | |||
| 95cd550524 | |||
| 815ae3c644 | |||
| 887bed3d45 | |||
| 79da02c500 | |||
| 0aecd5d5d7 | |||
| e95c4ee636 | |||
| 2e7fec6be5 | |||
| 4be7082de5 | |||
| fb90f778b9 | |||
| 9d58e1a77e | |||
| 9c077ed352 | |||
| 2562d36518 | |||
| 1d2fe2de2d | |||
| 2d82ee2837 | |||
| 1eee9df679 | |||
| 8c57cc1731 | |||
| 4febd8a11b | |||
| 880b003bee | |||
| e0f9d0bccf | |||
| d4853feecd | |||
| 86e812284d | |||
| 4e31973040 | |||
| f75063c420 | |||
| 6a48e9d44b | |||
| 1f8e6e4e9f | |||
| bb593846e5 | |||
| 108d181a05 | |||
| ac5d6c1f67 | |||
| 5aa3b91d56 | |||
| 9a01504b76 | |||
| 0ab636e436 | |||
| b9d2be87a2 | |||
| fef02594db | |||
| e135e2671f | |||
| 6d74965727 | |||
| 146e141b2a | |||
| 6fed7545c0 | |||
| be407ac904 | |||
| 9ce52ee7ce | |||
| 3ff41bcb68 | |||
| 5332858782 | |||
| 2b094b4188 | |||
| 3457841a9b | |||
| 5258e569c1 | |||
| abb416c2ff | |||
| dd4d5bbd4e | |||
| ab64912c70 | |||
| 1506d8bb27 | |||
| 5aa13b8e97 | |||
| b143ed7aac | |||
| 26dd5f298f | |||
| d939ee938e | |||
| 515de3972f | |||
| bf573b3cac | |||
| 53dc9ffa06 | |||
| 04d2fa5ce8 | |||
| 7764a7a162 | |||
| 727a427837 | |||
| b1c59ec2e6 | |||
| f69a3cf832 | |||
| ed0bf1786f | |||
| 2d1d8a81e1 | |||
| d515cf965e | |||
| dc1e4f040d | |||
| 5e1e701ce5 | |||
| f004591c5a | |||
| 86a96dd157 | |||
| 97dc32f9d2 | |||
| a3ab065343 | |||
| c390721ac3 | |||
| f83d758d4b | |||
| e00aba539a | |||
| 7799500d06 | |||
| c099991cd9 | |||
| e27d2e7394 | |||
| 14fb992ef2 | |||
| 4550a344d3 | |||
| 8efd25ef65 | |||
| 117694f210 | |||
| e5dc6f0ae2 | |||
| 8f738f5d49 | |||
| 1e2ca40c5b | |||
| 4522cb5dcb | |||
| 9ee4191020 | |||
| cc65009d0a | |||
| 453133d382 | |||
| dd0dddffd4 | |||
| 9a8f6e6414 | |||
| 4ece95a040 | |||
| 0cc09380bc | |||
| 809fb855de | |||
| aa14f2e200 | |||
| e319bdb6d3 | |||
| fb7340b556 | |||
| 0528fec083 | |||
| 1cbaaf6e12 | |||
| dc862f60b2 | |||
| ff04d54247 | |||
| 330cf05177 | |||
| 42b5fa914c | |||
| 3b1026b7d7 | |||
| b6151e0d01 | |||
| d6db2c13c1 | |||
| 07b6ff6c04 | |||
| ac5da9e3f7 | |||
| 90fb8c602c | |||
| 3974a65a18 | |||
| 2a54b5819d | |||
| 8be46b57a0 | |||
| 7177791f1e | |||
| c743b10a87 | |||
| 83f8de17c8 | |||
| da20a62e49 | |||
| c8d3e1a9fa | |||
| f7705fe1c1 | |||
| 4f35dbc607 | |||
| 2e01776272 | |||
| cf23dfa477 | |||
| ab4787c667 | |||
| bd6b166a62 | |||
| 17340947b3 | |||
| f235d84d95 | |||
| 3c0d780696 | |||
| 16768fca9f | |||
| 95ec0a82b2 | |||
| 15cd7187e9 | |||
| 0a680dd06e | |||
| 152892cc38 | |||
| 9aad417fb2 | |||
| b58e0cba66 | |||
| 74864c60e1 | |||
| fd4f161edd | |||
| e0939e3e99 | |||
| 2cbecd10e6 | |||
| 2cdd5e3ba0 | |||
| 811ef27ee4 | |||
| 22a7fd0656 | |||
| 698c276cbe | |||
| 6340656ece | |||
| 2d5840f947 | |||
| b8bb3684a3 | |||
| b8cd563569 | |||
| 9fb64599de | |||
| 502f37d280 | |||
| 29cfa8ec35 | |||
| 86c107ae5e | |||
| a63cd2e4f5 | |||
| 7249b59e52 | |||
| 5c94d9994e | |||
| bb0a5d8323 | |||
| b126e8e6e4 | |||
| 677129d23d | |||
| cef5c36885 | |||
| a343ba4a29 | |||
| 21b30b22a7 | |||
| 449c4b6c5e | |||
| 6513ad6ced | |||
| 12421c846e | |||
| a312cecede | |||
| 4be97ed314 | |||
| 5101b9dce3 | |||
| 67e452e9ed | |||
| ecff90da1c | |||
| 2ffafd445d | |||
| 6068ef6150 | |||
| 3ee5c29a00 | |||
| 8c0b933fcd | |||
| 991e02649a | |||
| 79f5973e5a | |||
| 37c40cb735 | |||
| 28ee2917f1 | |||
| a8b785f69e | |||
| 56610d886d | |||
| be0d9e964b | |||
| 6bc9da3fe1 | |||
| 6f54462070 | |||
| 876629b744 | |||
| 5e74e5a19d | |||
| 0d114e21fd | |||
| 95b65fe72b | |||
| bc08f2cb55 | |||
| 6c73a3d90b | |||
| c08f33638a | |||
| 6c93390c9c | |||
| b53e396aac | |||
| 8b327aa8b4 | |||
| d0c6f9bc60 | |||
| b5e066939b | |||
| 7892237408 | |||
| 74df53c72b | |||
| ee26457705 | |||
| 09e6a0944a | |||
| bdee46043a | |||
| 3e25168777 | |||
| 21b8507252 | |||
| d8e283777e | |||
| 2b0aa4b9c9 | |||
| 040eb8142d | |||
| d1aeabc9da | |||
| e4ec2b3efa | |||
| dedcc6b887 | |||
| 2b0ed2bc45 | |||
| 9c859ae7c4 | |||
| ae0a230046 | |||
| 644fade478 | |||
| c9e919dd40 | |||
| 42323e27b1 | |||
| 3927177b23 | |||
| 038fd80428 | |||
| cc6c5bf199 | |||
| b88d9e8ff2 | |||
| 0fc232bbc7 | |||
| 7dcd42d113 | |||
| e79ffc6bad | |||
| 8ea425b266 | |||
| 3a0f6a1d09 | |||
| b9b90295fa | |||
| 9dabecacbf | |||
| 04fa6b6372 | |||
| a0d2856f51 | |||
| 55b9266648 | |||
| 4313a1ef20 | |||
| 005ef03845 | |||
| bbac28c9f4 | |||
| eb56483ba2 | |||
| 5d6b717c9a | |||
| e888f3613b | |||
| e69899559d | |||
| e6fe968d44 | |||
| 678898efe7 | |||
| 6c3d8829ce | |||
| 14859a4c89 | |||
| 8a44d3f136 | |||
| be71d6918d | |||
| afb3dd459a | |||
| b6e3ffafa7 | |||
| 6c95f3d8cf | |||
| 2192373bec | |||
| dfcdaa3f8e | |||
| 79bc9e677f | |||
| 60b615ea6a | |||
| 8787481e40 | |||
| c24406259f | |||
| cbd6a65057 | |||
| 175004baf3 | |||
| 6d9c0483a6 | |||
| 2dc413cba0 | |||
| 206548af2b | |||
| af6de422e9 | |||
| 1c7b254395 | |||
| de1c721cd9 | |||
| f8dd286213 | |||
| e11337df2d | |||
| 2f90d4ccd7 | |||
| 2db15af24a | |||
| 8ea7b2b802 | |||
| c9e3932637 | |||
| d218afaf77 | |||
| 19739ff814 | |||
| 478144fb35 | |||
| 5d81cf0d4c | |||
| 0b115230f9 | |||
| 0b246f7a76 | |||
| 7914224ff7 | |||
| 997b0e9368 | |||
| a76414aeee | |||
| d2096df82a | |||
| 4951b1b0f9 | |||
| f2e31452f2 | |||
| c2985ffb31 | |||
| 492f797dd5 | |||
| 0b261ef97b | |||
| f19692a72f | |||
| 0787b37f2c | |||
| f0223b50d0 | |||
| 0263fcb2ac | |||
| c0a234317b | |||
| 72100075d2 | |||
| a7aa923241 | |||
| e012517b16 | |||
| b1dd18af8a | |||
| 398e3ff3b5 | |||
| 03648f580c | |||
| df2212055b | |||
| b1a137c513 | |||
| 62267dfd3e | |||
| f996a48dfa | |||
| 54e00dd4dc | |||
| f1414a0beb | |||
| 47f2ae32c1 | |||
| 306b7eb2a6 | |||
| 02f72aabb2 | |||
| e3a21384e6 | |||
| 331381eed5 | |||
| 75a7d77a80 | |||
| cc242a50d1 | |||
| 704acbf6de | |||
| 7baa48e068 | |||
| 3742e74adf | |||
| cbcea04768 | |||
| f0581f87c6 | |||
| 60763f2223 | |||
| 8a589022d9 | |||
| 00109e9976 | |||
| 43107c1743 | |||
| 546253d591 | |||
| d94a6580f5 | |||
| fb75b712d3 | |||
| 6ead2a4e8b | |||
| 9afe2296a6 | |||
| 0221c2a643 | |||
| 0518971d2f | |||
| fe3747f850 | |||
| 844d175981 | |||
| 4e719214c6 | |||
| 2f6b99885e | |||
| 4b24da1a58 | |||
| 72d482de28 | |||
| 049da041b2 | |||
| 4d7aa2b98f | |||
| 2e352c0084 | |||
| 3f854713e6 | |||
| 011bd3ba82 | |||
| 4de70da0a0 | |||
| 6820d8b7aa | |||
| 6886558ed1 | |||
| b795a92840 | |||
| 2e622f6fd4 | |||
| 5f7a1663dd | |||
| f3f35a2ed1 | |||
| 86a6b32715 | |||
| 1efb9de043 | |||
| b60d21dfe2 | |||
| 39e0d61dec | |||
| be41181b43 | |||
| 632ffc2f8e | |||
| 705f19dd87 | |||
| 5637dfa75d | |||
| cf5275293f | |||
| 81efe5953b | |||
| a37a3b9f99 | |||
| 9100913184 | |||
| 8fdb0b3d32 | |||
| 8322ff0f5e | |||
| c8287567eb | |||
| b00bc4d5ea | |||
| 8abc0d1920 | |||
| ea967b439f | |||
| 23d7191990 | |||
| c7e6df7c95 | |||
| 805b81edc0 | |||
| c42541a3d9 | |||
| 3f352605ca | |||
| 21a04fefcf | |||
| aa4fb3bba2 | |||
| 4d0dffafd3 | |||
| 19e75d1070 | |||
| cee5b9feb9 | |||
| 355a2349af | |||
| 7e815cc985 | |||
| fb443e0fa0 | |||
| 7f1135eeac | |||
| 25e15e76e0 | |||
| 9426881cf6 | |||
| 1f6970a5b3 | |||
| e541ee86fc | |||
| b1c6f247e0 | |||
| 36b585835d | |||
| 17db4bd616 | |||
| c98d8ca26a | |||
| d5334f96a4 | |||
| 54f4ee57ad | |||
| 4f3796ed2e | |||
| ac13a288fb | |||
| e2ccc9deac | |||
| 80129f2e23 | |||
| 5802bbc1dd | |||
| ee2f4eedbd | |||
| 0de1ce053a | |||
| be9056f978 | |||
| 26a6e7d712 | |||
| a90b90ad29 | |||
| bc951787d3 | |||
| 0ae0887665 | |||
| 551fa01415 | |||
| 68ea2c7d7e | |||
| 3e8616ec64 | |||
| 465c65243d | |||
| 9c0e5ddfc6 | |||
| 8442b9a6e4 | |||
| a1208b5876 | |||
| b5f89d1ff8 | |||
| cd823193cc | |||
| eb7bb34825 | |||
| 19e3d929b1 | |||
| 25729f6262 | |||
| cbb6a58c8a | |||
| 167c96965e | |||
| 8db3238a1f | |||
| cbd8cae83e | |||
| 3e03a87e02 | |||
| 193dd107d7 | |||
| 0cea8fe922 | |||
| abbbc9fdbb | |||
| 10b210d2bb | |||
| 9385a3dcea | |||
| c0af671ca2 | |||
| eb9f31edeb | |||
| 0e5470b192 | |||
| 2122e57990 | |||
| e42d224db2 | |||
| 070139eda7 | |||
| a0d0182031 | |||
| 1885d4e110 | |||
| 5959890125 | |||
| 7460bd6c1f | |||
| 801346b894 | |||
| 40fd06c0d0 | |||
| aa9fe351a6 | |||
| e00efb1b6d | |||
| 86a765b06b | |||
| 3c20e8114e | |||
| 73a859ee56 | |||
| c9180e590e | |||
| 4196579a3d | |||
| 436b20857d | |||
| 82b0adf5ae | |||
| a0041f139c | |||
| b25bc7ee05 | |||
| faafedd55c | |||
| c99325275a | |||
| 37a7583755 | |||
| a851b5ed70 | |||
| 983659f8e8 | |||
| 3e1cef296f | |||
| cada5c5136 | |||
| 477492fdd2 | |||
| 963b7f43ab | |||
| a34767fa15 | |||
| fe3c7d72bd | |||
| 97898a0a8e | |||
| cddddc3ec1 | |||
| f8930d20ae | |||
| 2e25eb59b8 | |||
| a5471fd0f4 | |||
| 9842e1051d | |||
| d39e4f15fe | |||
| 1a05df14d0 | |||
| 5c0df87f52 | |||
| ec6a81735a | |||
| 2ea37f3424 | |||
| 2e4206cb97 | |||
| 72b3cdaab4 | |||
| 9744b9848e | |||
| e1143d2cfc | |||
| 5861b0498a | |||
| f6d9aa51bd | |||
| 9070b12377 | |||
| dab68d6d85 | |||
| 40de54ecd5 | |||
| 6d8b926df3 | |||
| d6c3f3522b | |||
| 41cdb5f01b | |||
| 0064f3fdff | |||
| 518035514e | |||
| 81cdb7a300 | |||
| 97dbda92e6 | |||
| 4520fe918a | |||
| f73e6aebcb | |||
| 3b9ca8853f | |||
| cbc26faeb9 | |||
| 12c75524a2 | |||
| 6edbd69dc4 | |||
| 0cfb1237e5 | |||
| cab6aa6e57 | |||
| 9ff2f3b46c | |||
| ead0237fb3 | |||
| 31726b51ad | |||
| 99d3ee448c | |||
| d21ae255f1 | |||
| 0823e5da88 | |||
| 1f913f6fc1 | |||
| a6dec31b9d | |||
| c1979f735f | |||
| 1c1cb1e591 | |||
| 49ddcbd66b | |||
| 23c8adf497 | |||
| 8e3d7d85e8 | |||
| 3cee6a499d | |||
| 3f38f19df8 | |||
| 4df6ab4a84 | |||
| 90b6771451 | |||
| 681723cbc3 | |||
| d9595ec122 | |||
| ec35010cf4 | |||
| d14f2728d8 | |||
| 152b7a9fc4 | |||
| 56ec3b9ee3 | |||
| ca69e4fd94 | |||
| 1965763cd0 | |||
| cd8d94780f | |||
| ba87e1fde8 | |||
| e48bd31ae4 | |||
| 5ae061d208 | |||
| 3d7e621b7d | |||
| bf14c4262a | |||
| b186d7a4ee | |||
| e109cd1da2 | |||
| 473f295b77 | |||
| a05684026e | |||
| 5df46d03c3 | |||
| 19424ae06d | |||
| 6c8970e0a3 | |||
| 2f2b51ce3d | |||
| 33f596bcce | |||
| 50a96dcb8f | |||
| d9a4fa33b9 | |||
| cc6bd59df9 | |||
| 3ce6408195 | |||
| 1c673fcff9 | |||
| ff82596158 | |||
| 0539d2ba62 | |||
| 221ac62f03 | |||
| 4b6c949032 | |||
| 1915ad368a | |||
| 1408cffc02 | |||
| 95b5379945 | |||
| a4833bbfe4 | |||
| cb1c777b9b | |||
| 7433cc5565 | |||
| 4c769fac66 | |||
| 176d3efbc6 | |||
| d635665c1b | |||
| 22467d05c8 | |||
| 7a699e16db | |||
| 341ba56bb9 | |||
| a320cc3e2b | |||
| 8b428357b2 | |||
| bb6d6b0951 | |||
| b94b8c6a4f | |||
| 43894afa18 | |||
| 28de31a8b9 | |||
| 974e70d7d1 | |||
| f914f1ad87 | |||
| 14dd5cb57b | |||
| 077ed287b7 | |||
| 9a41f2ff82 | |||
| 88f29e4180 | |||
| c34f649724 | |||
| e1e4f8d884 | |||
| 2d6d8b810b | |||
| bcd1503cad | |||
| e0f2919f62 | |||
| a8bd353ba6 | |||
| ed3bb2634d | |||
| 48ae4bfc18 | |||
| 26bb0b6eec | |||
| 236a9bd7ce | |||
| f4d19c90d3 | |||
| ecbaff503e | |||
| 9d0cd32e3f | |||
| 8782e9c34e | |||
| a4932e41de | |||
| 522ac55d5f | |||
| b3a8aef6ac | |||
| 8068bebebd | |||
| b887116e5c | |||
| 6071d86ac6 | |||
| 5723da1043 | |||
| 17e242a5f6 | |||
| c9a8007811 | |||
| c64c41a4d9 | |||
| da4bf7f5c3 | |||
| bb5dbc878e | |||
| e3ab27e2ec | |||
| 8f91f40e8e | |||
| 2973b600f5 | |||
| 37d4434000 | |||
| 627b673380 | |||
| bc1b11054c | |||
| 8350d74020 | |||
| 1431a2a897 | |||
| 2906a2de2d | |||
| 540c2a56bd | |||
| 2fb8690320 | |||
| 88e047bd27 | |||
| 8f630c064f | |||
| 54fd188299 | |||
| 4db2318a66 | |||
| 7e1c769d1c | |||
| 7a57f9f9b8 | |||
| 961e0aec66 | |||
| ccfcd336fe | |||
| 7287d65865 | |||
| d96ddaeaac | |||
| 085d3436c8 | |||
| 2da899a2b1 | |||
| 48ea48981b | |||
| 084f7c596f | |||
| aafdf6f39c | |||
| d4c66d73a0 | |||
| 7d89d3546e | |||
| 840364891c | |||
| 1f3e5115c7 | |||
| 9de85175db | |||
| 0bf051610d | |||
| 42fc80b76d | |||
| ff1249b5cf | |||
| 331585a0ba | |||
| 0c808736b8 | |||
| 9f0dc108fa | |||
| cf5c6f9ffd | |||
| f6d6a4db78 | |||
| ef3305ec23 | |||
| f6ce6cece7 | |||
| 32923f32a8 | |||
| 02f3202a61 | |||
| e6af0c8a77 | |||
| 52a8183fcb | |||
| c2f57811e1 | |||
| 639029349d | |||
| 595172aba6 | |||
| e7af5a4cf9 | |||
| ef7d1527da | |||
| 2a370a2a76 | |||
| b828843974 | |||
| 0b99c72937 | |||
| ec4f15132e | |||
| 0e4cc94471 | |||
| 9e89fb5e23 | |||
| 26cf278103 | |||
| 050fe38a90 | |||
| f07c3a2480 | |||
| ae365324a8 | |||
| b4b212a426 | |||
| f52d4392a6 | |||
| c662c59daf | |||
| ef4a487322 | |||
| 30b151f927 | |||
| b93bf82be6 | |||
| ea3827fe7f | |||
| 7cd6b4ff40 | |||
| 544af117bb | |||
| d71af91360 | |||
| fccb90c8ea | |||
| 4b1cad2493 | |||
| 57cd294f0f | |||
| 837552b11d | |||
| 966c382d1a | |||
| 43e17741eb | |||
| d6e3906da6 | |||
| dbef162fa6 | |||
| 1bd9c9e60d | |||
| d1adc65037 | |||
| ae15fa668d | |||
| 88e576c3a3 | |||
| 60950c624f | |||
| 3d1e0c3f9f | |||
| d6c8149a3a | |||
| aedf2e3727 | |||
| 6316828fbd | |||
| 24a9ab6761 | |||
| 0a76032b1b | |||
| 730bc7d3b3 | |||
| 99c7bb705e | |||
| fc747c306d | |||
| b6b8a72a6a | |||
| 36f005981b | |||
| eadc6f4307 | |||
| 2876ac590d | |||
| 99436f94f9 | |||
| 60f82a21db | |||
| f68e6cefd4 | |||
| b939d94cda | |||
| 37d71adb4e | |||
| 813f63360b | |||
| ad97b202c4 | |||
| 1e61034e86 | |||
| 3f6b2e856e | |||
| 3df68b288a | |||
| a75e899d98 | |||
| f25a9f5558 | |||
| 69a0c3ba99 | |||
| e1bb1d8c2d | |||
| 62238b2714 | |||
| e9f83dc020 | |||
| b15dc03d16 | |||
| 3af60b874e | |||
| 1da1eeb384 | |||
| 8abad572ec | |||
| 20012b65fb | |||
| 244087db27 | |||
| b54c24a589 | |||
| c664094f35 | |||
| f5d71b9282 | |||
| a478798cfe | |||
| a6e133a194 | |||
| 46b785f29f | |||
| 69b1c08558 | |||
| 2a1db41880 | |||
| 64dbcf0a26 | |||
| 683910bff4 | |||
| 7492b331a0 | |||
| ae8df3f51d | |||
| 658c428b71 | |||
| d7ecef80e9 | |||
| 09644640bf | |||
| ba1f2df40b | |||
| 0348037638 | |||
| 23b42730ba | |||
| 9ded563e4b | |||
| 8a48a3da7f | |||
| 61a35d65c4 | |||
| aafbd04753 | |||
| 56a2f1b733 | |||
| aa3369757c | |||
| f309e06292 | |||
| 05989c35a7 | |||
| 813a74759b | |||
| c9fb8e6f52 | |||
| d374a46c4d | |||
| 0ce70a0180 | |||
| 7fc12613a8 | |||
| d0e949bf19 | |||
| 957dabc3f0 | |||
| b4b4bf3685 | |||
| 49619ff2b5 | |||
| efca71f1e7 | |||
| 86fdfb75cf | |||
| f865e93050 | |||
| 4fccddebb0 | |||
| 15ef5f12b8 | |||
| c87aabf5d4 | |||
| 0d9c11c99a | |||
| 7fdc7d6997 | |||
| 7fe8399d48 | |||
| 9be89c4042 | |||
| a61db76c24 | |||
| ea6af719e0 | |||
| 9f5506364b | |||
| 2d70f18a93 | |||
| 427ee89ae6 | |||
| dd4247e150 | |||
| cac777bce1 | |||
| 3c0390b1ac | |||
| 2df49947df | |||
| 26369a19b4 | |||
| 2b3e146d5a | |||
| aff8e33eca | |||
| 104db3f6a5 | |||
| ec8fb202b3 | |||
| e22f6e1e4d | |||
| d57437ff02 | |||
| eb64113a3d | |||
| 3b5f9ec59d | |||
| 666b2f3fd0 | |||
| 2bc33c7cd5 | |||
| 8ee7a4deef | |||
| 69afb31bb9 | |||
| 5ca4c5bca9 | |||
| b6fb81e506 | |||
| 735f131222 | |||
| 8a58e984fe | |||
| 0e9c1bc0f7 | |||
| e749af6d91 | |||
| 052b9e051b | |||
| 6ddc5c8572 | |||
| 8eaf4859ff | |||
| e1406554fc | |||
| 504fd04705 | |||
| 7fe7a01d43 | |||
| b7ec3c59b8 | |||
| 757a98230e | |||
| c319a8e84c | |||
| 8d33c9dc89 | |||
| 2ae06a0e2d | |||
| f107fdfa08 | |||
| 2561d47bb5 | |||
| aff11486b2 | |||
| 17cda168e1 | |||
| d5495a7e3a | |||
| 87ee5ef36b | |||
| a34001faf0 | |||
| dd840f8861 | |||
| f94ea9ba0d | |||
| 6338d762fa | |||
| f48ae48677 | |||
| 1658176fe1 | |||
| 2f519261f5 | |||
| 7466667c08 | |||
| d03302db7b | |||
| 951b54e7cd | |||
| 2929af4715 | |||
| 48309931ef | |||
| d69f61c8d3 | |||
| a8f0b96338 | |||
| c1663191ee | |||
| ded14c5425 | |||
| 80bea0b4b4 | |||
| 2cdcf7f3e3 | |||
| 8d34d0743f | |||
| fe763c1dc7 | |||
| 8a2aeff9a0 | |||
| b1545f270f | |||
| 975f1bd9fa | |||
| fed9776f9b | |||
| 803ba535c3 | |||
| 60358461c4 | |||
| bdff30b511 | |||
| 6e3d940c8c | |||
| b702e0c084 | |||
| 48caaed58c | |||
| c45a303451 | |||
| 2bffd6a51e | |||
| 179e6a9db1 | |||
| 94cef989d3 | |||
| 6982b02d48 | |||
| 3a4a57a262 | |||
| 67d26388de | |||
| a26e3946bd | |||
| 026d4920cf | |||
| d305f4dca0 | |||
| 217fae0967 | |||
| 349e2f45df | |||
| 0675529320 | |||
| 0839c7e127 | |||
| 15931e2418 | |||
| f6676118ac | |||
| 78673ceeb1 | |||
| e228d565af | |||
| 51bcf6a2be | |||
| feedeab7e3 | |||
| 76d1a8ffed | |||
| 5436ff8c49 | |||
| e8b10fcc91 | |||
| 4083e6e26e | |||
| 3ecf351432 | |||
| 6595e6960b | |||
| 330b4f0171 | |||
| 43a7df2cc1 | |||
| 0e98c5c5c4 | |||
| 39ee5b9b08 | |||
| 227d32a1fd | |||
| b7e2cd758f | |||
| c9a02b964d | |||
| 0fd5b2efe1 | |||
| 476f56b617 | |||
| 174a9ca60b | |||
| 406fcf72ae | |||
| 37b1367d25 | |||
| a55953a8e0 | |||
| f120b32791 | |||
| ca113127cc | |||
| 138bdf599b | |||
| 522504916b | |||
| b04207d9ea | |||
| 9596274b14 | |||
| 8d018ea3f1 | |||
| 0f0af9eefb | |||
| 8aece3981d | |||
| 1c9c408cf3 | |||
| 3911cfec6d | |||
| 6d7fd66f82 | |||
| e1e926076a | |||
| 68bf8a2d26 | |||
| c0d9f739b3 | |||
| d33cc19778 | |||
| f940b89284 | |||
| 21990ccd8b | |||
| c01654efcc | |||
| 1ad631c528 | |||
| ecb5d9c83e | |||
| 0039dbde38 | |||
| 70f447a8bc | |||
| d29d68a2c2 | |||
| c5f6923e5a | |||
| 34878bf432 | |||
| fab08f0e20 | |||
| 850955d7dd | |||
| 606d627f1d | |||
| dfe8a3deeb | |||
| 3bc356ceec | |||
| 7ad045d6b7 | |||
| 3324455e0a | |||
| 7ffc5602b4 | |||
| 8944fdd96f | |||
| 3aaa93931a | |||
| d77715b0fe | |||
| 17cc194ad1 | |||
| f9397924fa | |||
| 9281060ab5 | |||
| 6a3cc48d6b | |||
| 7194a87d0a | |||
| 3e325e3364 | |||
| 348bd4b923 | |||
| f6cfd425c6 | |||
| 7683d791f4 | |||
| 649047f087 | |||
| 3aab10774e | |||
| e37f3d3d45 | |||
| e105afce83 | |||
| 3d16183a93 | |||
| 129325db8e | |||
| f214a78c75 | |||
| 42c10f9a62 | |||
| 8d4fa06ba3 | |||
| a73744c081 | |||
| e2ab53ed53 | |||
| 5c4e98cf0c | |||
| 6bad9d302b | |||
| db00fc8c09 | |||
| 98b04632ce | |||
| cbd4cefc1a | |||
| 84b8c389b2 | |||
| e34364d8b1 | |||
| 80854d5f87 | |||
| cbee6e76b0 | |||
| 12618ff8b3 | |||
| 115bb551df | |||
| 3799dc7a2a | |||
| a8b8adafdf | |||
| 961e44b5ad | |||
| 20e99af194 | |||
| 1c888ce055 | |||
| f3a748fee3 | |||
| 2a68234bd3 | |||
| 8275d7e08d | |||
| e9630ff9a6 | |||
| 97431f2622 | |||
| df8a64b017 | |||
| accfe86eaa | |||
| 667918905f | |||
| c1231c9bde | |||
| bac941715a | |||
| 8a3caa5e7f | |||
| 7bc513bc2b | |||
| eecc598471 | |||
| d8d1baf945 | |||
| c5cb1fc325 | |||
| 93a118a532 | |||
| d6ddd7326a | |||
| fa1145dced | |||
| 06ef57060f | |||
| 921d580d47 | |||
| 7c7c8358e9 | |||
| 9b54b61e2e | |||
| b93e3524f5 | |||
| b6598af59f | |||
| 4de7284657 | |||
| 0e0e2adfbe | |||
| 08b62f6633 | |||
| b0b83505af | |||
| 3d5271f512 | |||
| 462982bb28 | |||
| 245f636fa8 | |||
| 9fd8610390 | |||
| 7a41a540f2 | |||
| fb588711ef | |||
| 1e7e164d84 | |||
| 036f2de795 | |||
| 45011ebd14 | |||
| e9564febaf | |||
| 579b975318 | |||
| d8f3e9c313 | |||
| 01ccbfe254 | |||
| af82c1005a | |||
| 02e3b5aa77 | |||
| fb378bed56 | |||
| 1454bcf165 | |||
| f20a7cc9e4 | |||
| b21bfe3666 | |||
| 0669423138 | |||
| 3f111708e3 | |||
| a1f47f1a90 | |||
| 24a17cfb7c | |||
| bee4eda3ef | |||
| 797ebb2986 | |||
| ddb75441e7 | |||
| c61d70abf1 | |||
| 0953ae431e | |||
| b3a2e6026f | |||
| 46ffdf9c7a | |||
| 99e607dcfb | |||
| 1006b9cb7b | |||
| 6769231b00 | |||
| 3ff95faaaa | |||
| eab944023c | |||
| 00d3c735fe | |||
| 173a72e69d | |||
| 38a82cd142 | |||
| 42ceddf374 | |||
| 6252e288b9 | |||
| 8594980dae | |||
| c22fed89f3 | |||
| 429660fe57 | |||
| a4ec4b2841 | |||
| c0d66b7dc7 | |||
| 787e7f6ce5 | |||
| 2c338468bc | |||
| d8b456d89a | |||
| bff18ab381 | |||
| 4e95a65af1 | |||
| 1706e1d2e0 | |||
| 324823fef0 | |||
| afedf83f5d | |||
| 402995bf29 | |||
| c0fc52f719 | |||
| 41dbc947fa | |||
| fe09184c9c | |||
| 4821bd4423 | |||
| 1461c9ba88 | |||
| 452d4b26b9 | |||
| 3dd7b677bd | |||
| 1e86dcecd0 | |||
| 949ba1f298 | |||
| 77b7181b08 | |||
| 6e120d60f6 | |||
| 10ff36480a | |||
| ffb20c9956 | |||
| 751a532124 | |||
| 80c411be44 | |||
| db5c11d96e | |||
| f3ed9bdf8e | |||
| 53c20d9cd2 | |||
| fca0c8fc0c | |||
| c8aa204ee0 | |||
| a3889110f1 | |||
| cda4665979 | |||
| 09e2a26b7d | |||
| 3dc451cf44 | |||
| 3795870150 | |||
| 24204adc09 | |||
| a027e24e3c | |||
| 15e2e1711b | |||
| d363321db5 | |||
| 3ffed844a6 | |||
| 9cf1005183 | |||
| 3601d01bd5 | |||
| d0136b63d4 | |||
| 00029c15ef | |||
| a5cbdea868 | |||
| edd1d890ca | |||
| 985bbf4ea5 | |||
| 443f848386 | |||
| 32dbac87c8 | |||
| 1dc33070fe | |||
| 1079793ccf | |||
| 21364779f9 | |||
| 50d8688b79 | |||
| 27baad82d0 | |||
| a894386b49 | |||
| 2465b88462 | |||
| 7660517f7c | |||
| 74470ec96d | |||
| 03ccea8978 | |||
| ed89689425 | |||
| 62d7652376 | |||
| 0e85921964 | |||
| 51ec80f11f | |||
| e08d6f2803 | |||
| 22030bffd0 | |||
| bc105bc818 | |||
| 2c3a296040 | |||
| 10b204e19d | |||
| 36130eb7a0 | |||
| 6d5a8a9235 | |||
| 04663e0c8e | |||
| 6f9ebe04b0 | |||
| 8ca4caaffa | |||
| 8baaf80691 | |||
| e91c85496f | |||
| 6bf92d20ad | |||
| 35f607c46c | |||
| b99f65d936 | |||
| ead2dfd0a9 | |||
| d2ca6e630d | |||
| 12b48a6cf6 | |||
| 635a71ce66 | |||
| b99c4d611b | |||
| af8886c062 | |||
| 30b1f6cd0a | |||
| d24091d224 | |||
| b3d3b4485d | |||
| 108fe45164 | |||
| e1a30e3c92 | |||
| d969eaac2c | |||
| dd081c59b7 | |||
| b0264886c9 | |||
| 6dcdfdb82f | |||
| e7d75365fd | |||
| 266447bcc0 | |||
| b74a688677 | |||
| b1f9ce2283 | |||
| e64b6fe924 | |||
| c8697e0310 | |||
| 98db677646 | |||
| 6c3a8be049 | |||
| 129a733888 | |||
| 5aeda553a4 | |||
| 0146186ddb | |||
| 106b279ed0 | |||
| 05f44ed84a | |||
| 6e7890c206 | |||
| 2d2760e15c | |||
| b2eb69cbc1 | |||
| 0fc90ec181 | |||
| 337d55b9ab | |||
| 9f1e3dadf4 | |||
| 24943e5fbb | |||
| 33d1579660 | |||
| 8047ae570d | |||
| e236bf3821 | |||
| 32c2df0444 | |||
| 7a0e841a0d | |||
| ce2dcc3128 | |||
| 52314a4928 | |||
| d65cc9be84 | |||
| beb14ce5f4 | |||
| c49700dd47 | |||
| 3d0f267a5c | |||
| 729d7c4bef | |||
| d10b8f5d2c | |||
| 8494d4566a | |||
| 7fd9cfa2e9 | |||
| 3df78f2edf | |||
| 5f0752e874 | |||
| cdd67a6900 | |||
| fce24ef01f | |||
| 47264416b1 | |||
| 86dca12c09 | |||
| 0b5cea6df1 | |||
| 9258dcc988 | |||
| 24edab45b1 | |||
| f3371e51fe | |||
| 83b9f86972 | |||
| ca30677c53 | |||
| b205b8e748 | |||
| b9b9de1945 | |||
| 22895ce682 | |||
| 3829623984 | |||
| 3f3bc525bc | |||
| ece0930319 | |||
| 94585789dc | |||
| 7ea6d8a35d | |||
| 9dd40e4610 | |||
| 8cdca8d012 | |||
| 7bcd3ebaee | |||
| d4a8a3ec37 | |||
| f460cceeba | |||
| f3f58c7f52 | |||
| f2a9b73b83 | |||
| ffd4e6d577 | |||
| 5287eff507 | |||
| f6004e07c1 | |||
| b2cd60395f | |||
| 183f975166 | |||
| 8ace01b6f1 | |||
| 4b213d63bd | |||
| da5c21de8f | |||
| 408ae4cad8 | |||
| fc2484a28a | |||
| be58d5517a | |||
| 602403eb59 | |||
| 5f1776829b | |||
| 4a22c5d673 | |||
| 3943919709 | |||
| 8174f2d973 | |||
| 22d16d54c6 | |||
| cd34f0ba81 | |||
| 648c6c5a27 | |||
| 2b687a7269 | |||
| e8c2fe6e35 | |||
| 88dfabc633 | |||
| a35bf249a0 | |||
| cf47b45262 | |||
| eed3ed5513 | |||
| de46ee0f64 | |||
| 1e011c7b40 | |||
| 04dcebf3aa | |||
| 0346450a51 | |||
| fc53f08e37 | |||
| bc4e5b9b4e | |||
| c3a445534b | |||
| 70877bfad4 | |||
| 55376d32e5 | |||
| 8835502074 | |||
| ba9b06adf3 | |||
| e92d9bfaae | |||
| d802e88148 | |||
| e853792a59 | |||
| b0fbe54181 | |||
| 2a649bdfc0 | |||
| e042bf7d3a | |||
| 3ff71a4563 | |||
| 205238fcbf | |||
| ba06c5a76d | |||
| 78ffc491e9 | |||
| 7f38de11b7 | |||
| a6e34d204c | |||
| 66d47e4922 | |||
| 7d70b6e95a | |||
| 076dcab4c5 | |||
| 6f42e336c9 | |||
| 0d72b5197c | |||
| 7691e3c602 | |||
| b43a59868d | |||
| 457972716f | |||
| ae029660e3 | |||
| dc12419078 | |||
| f0ddd10a54 | |||
| 4daf7ee4ab | |||
| d60f320382 | |||
| 3c3e6b160b | |||
| c99f8f196e | |||
| 9cc4ecd5ab | |||
| 8d96f975e0 | |||
| 7f214f9e12 | |||
| da258fa89f | |||
| f23f857903 | |||
| fec11928cf | |||
| f6131d042b | |||
| 543b9542ae | |||
| 1681c1f4f8 | |||
| f41c3ce9ba | |||
| 847b06e558 | |||
| 601c4b01fc | |||
| 05c1c82ae2 | |||
| 1bde49b3c4 | |||
| b22f5e0c85 | |||
| dd643faa5c | |||
| facbf05016 | |||
| 58cff003d8 | |||
| ae997e6eee | |||
| e6c7d68def | |||
| 0b19672963 | |||
| a16a19e121 | |||
| 3dd10b3c7c | |||
| 12e63c7376 | |||
| 2f0cd7a4e3 | |||
| 447af55e7c | |||
| 253e9d10c8 | |||
| 536ce1eca0 | |||
| 4331b2aaf6 | |||
| 8d99cacc95 | |||
| 3c38facc8a | |||
| ed389d35d0 | |||
| 60c0636523 | |||
| 16b6f96b47 | |||
| 042f2ec3f3 | |||
| 851ce36010 | |||
| 4f386894bd | |||
| 2289adade2 | |||
| 29e21e3814 | |||
| 8367af22e7 | |||
| e3d678dc04 | |||
| 4ae482c707 | |||
| 1d4c1a5359 | |||
| 8e32b88fc8 | |||
| 1a7baad338 |
16
.gitattributes
vendored
16
.gitattributes
vendored
@ -1,14 +1,18 @@
|
||||
/art export-ignore
|
||||
/docker export-ignore
|
||||
/docs export-ignore
|
||||
/tests export-ignore
|
||||
/scripts export-ignore
|
||||
/.github export-ignore
|
||||
/.php-cs-fixer.dist.php export-ignore
|
||||
.editorconfig export-ignore
|
||||
.gitattributes export-ignore
|
||||
.gitignore export-ignore
|
||||
phpstan.neon export-ignore
|
||||
/phpstan.neon export-ignore
|
||||
/phpunit.xml export-ignore
|
||||
CHANGELOG.md export-ignore
|
||||
CONTRIBUTING.md export-ignore
|
||||
README.md export-ignore
|
||||
/CHANGELOG.md export-ignore
|
||||
/CONTRIBUTING.md export-ignore
|
||||
/docker-compose.yml export-ignore
|
||||
/Makefile export-ignore
|
||||
/rector.php export-ignore
|
||||
/README.md export-ignore
|
||||
/RELEASE.md export-ignore
|
||||
|
||||
|
||||
4
.github/FUNDING.yml
vendored
4
.github/FUNDING.yml
vendored
@ -1,4 +1,4 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: [nunomaduro,owenvoke,olivernybroe,octoper,lukeraymonddowning]
|
||||
patreon: nunomaduro
|
||||
github: [nunomaduro]
|
||||
custom: https://www.paypal.com/paypalme/enunomaduro
|
||||
|
||||
66
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
66
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
@ -0,0 +1,66 @@
|
||||
name: Bug Report
|
||||
description: Report an Issue or Bug with the Pest
|
||||
title: "[Bug]: "
|
||||
labels: ["bug"]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
We're sorry to hear you have a problem. Can you help us solve it by providing the following details.
|
||||
- type: textarea
|
||||
id: what-happened
|
||||
attributes:
|
||||
label: What Happened
|
||||
description: What did you expect to happen?
|
||||
placeholder: When I use expect()->toBeTrue() in my tests, I get an error
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: how-to-reproduce
|
||||
attributes:
|
||||
label: How to Reproduce
|
||||
description: How did this occur, please add any config values used and provide a set of reliable steps if possible.
|
||||
placeholder: Install a fresh Laravel app, add Pest, add a test that uses expect()->toBeTrue()
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: repository-sample
|
||||
attributes:
|
||||
label: Sample Repository
|
||||
description: If possible, please provide a sample repository that reproduces the issue.
|
||||
placeholder: https://github.com.br/your-username/your-repository
|
||||
- type: input
|
||||
id: pest-version
|
||||
attributes:
|
||||
label: Pest Version
|
||||
description: What version of our Package are you running? Please be as specific as possible
|
||||
placeholder: 2.14.1
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: php-version
|
||||
attributes:
|
||||
label: PHP Version
|
||||
description: What version of PHP are you running? Please be as specific as possible
|
||||
placeholder: 8.1.20
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
id: operating-systems
|
||||
attributes:
|
||||
label: Operation System
|
||||
description: On which operating systems does the problem occur? You can select more than one.
|
||||
multiple: true
|
||||
options:
|
||||
- macOS
|
||||
- Windows
|
||||
- Linux
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: notes
|
||||
attributes:
|
||||
label: Notes
|
||||
description: Use this field to provide any other notes that you feel might be relevant to the issue.
|
||||
validations:
|
||||
required: false
|
||||
20
.github/PULL_REQUEST_TEMPLATE.md
vendored
20
.github/PULL_REQUEST_TEMPLATE.md
vendored
@ -1,10 +1,16 @@
|
||||
| Q | A
|
||||
| ------------- | ---
|
||||
| Bug fix? | yes/no
|
||||
| New feature? | yes/no
|
||||
| Fixed tickets | #... <!-- #-prefixed issue number(s), if any -->
|
||||
|
||||
<!--
|
||||
- Replace this comment by a description of what your PR is solving.
|
||||
- Fill in the form below correctly. This will help the Pest team to understand the PR and also work on it.
|
||||
-->
|
||||
|
||||
### What:
|
||||
|
||||
- [ ] Bug Fix
|
||||
- [ ] New Feature
|
||||
|
||||
### Description:
|
||||
|
||||
<!-- describe what your PR is solving -->
|
||||
|
||||
### Related:
|
||||
|
||||
<!-- link to the issue(s) your PR is solving. If it doesn't exist, remove the "Related" section. -->
|
||||
|
||||
63
.github/workflows/changelog.yml
vendored
63
.github/workflows/changelog.yml
vendored
@ -1,63 +0,0 @@
|
||||
name: Changelog
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
paths:
|
||||
- CHANGELOG.md
|
||||
- .github/workflows/changelog.yml
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
paths:
|
||||
- CHANGELOG.md
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
if: github.repository == 'pestphp/pest'
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Checkout website repository
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
token: ${{ secrets.CHANGELOG_KEY }}
|
||||
repository: pestphp/docs
|
||||
path: pestphp-docs
|
||||
ref: master
|
||||
|
||||
- name: Read CHANGELOG.md
|
||||
id: package
|
||||
uses: juliangruber/read-file-action@v1
|
||||
with:
|
||||
path: ./CHANGELOG.md
|
||||
|
||||
- name: Add file headers
|
||||
uses: DamianReeves/write-file-action@v1.0
|
||||
with:
|
||||
path: ./CHANGELOG.md
|
||||
contents: |
|
||||
---
|
||||
title: Changelog
|
||||
description: Changelog
|
||||
---
|
||||
${{ steps.package.outputs.content }}
|
||||
|
||||
----
|
||||
|
||||
Next section: [Upgrade Guide →](/docs/upgrade-guide)
|
||||
write-mode: overwrite
|
||||
|
||||
- name: Copy CHANGELOG to website repository
|
||||
run: cp CHANGELOG.md pestphp-docs/changelog.md
|
||||
|
||||
- name: Create Pull Request
|
||||
uses: peter-evans/create-pull-request@v3
|
||||
with:
|
||||
token: ${{ secrets.CHANGELOG_KEY }}
|
||||
commit-message: Update changelog.md
|
||||
committer: GitHub Action <noreply@github.com>
|
||||
author: ${{ github.actor }} <${{ github.actor }}@users.noreply.github.com>
|
||||
title: 'Update changelog.md'
|
||||
path: ./pestphp-docs
|
||||
53
.github/workflows/static.yml
vendored
53
.github/workflows/static.yml
vendored
@ -1,51 +1,44 @@
|
||||
name: Static Analysis
|
||||
|
||||
on: ['push', 'pull_request']
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
schedule:
|
||||
- cron: '0 0 * * *'
|
||||
|
||||
jobs:
|
||||
cs:
|
||||
runs-on: ubuntu-latest
|
||||
static:
|
||||
if: github.event_name != 'schedule' || github.repository == 'pestphp/pest'
|
||||
name: Static Tests
|
||||
|
||||
name: Code Style
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Setup PHP
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: 8.0
|
||||
tools: composer:v2
|
||||
coverage: none
|
||||
|
||||
- name: Install Dependencies
|
||||
run: composer update --no-interaction --no-progress
|
||||
|
||||
- name: Run PHP-CS-Fixer
|
||||
run: vendor/bin/php-cs-fixer fix -v --allow-risky=yes --dry-run
|
||||
|
||||
phpstan:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: true
|
||||
matrix:
|
||||
dependency-version: [prefer-lowest, prefer-stable]
|
||||
|
||||
name: PHPStan ${{ matrix.dependency-version }}
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup PHP
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: 8.0
|
||||
php-version: 8.2
|
||||
tools: composer:v2
|
||||
coverage: none
|
||||
|
||||
- name: Install Dependencies
|
||||
run: composer update --prefer-stable --no-interaction --no-progress
|
||||
run: composer update --prefer-stable --no-interaction --no-progress --ansi
|
||||
|
||||
- name: Run PHPStan
|
||||
run: vendor/bin/phpstan analyse --no-progress
|
||||
# - name: Type Check
|
||||
# run: composer test:type:check
|
||||
|
||||
- name: Type Coverage
|
||||
run: composer test:type:coverage
|
||||
|
||||
- name: Refacto
|
||||
run: composer test:refacto
|
||||
|
||||
- name: Style
|
||||
run: composer test:lint
|
||||
|
||||
35
.github/workflows/tests.yml
vendored
35
.github/workflows/tests.yml
vendored
@ -1,22 +1,27 @@
|
||||
name: Tests
|
||||
|
||||
on: ['push', 'pull_request']
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
ci:
|
||||
tests:
|
||||
if: github.event_name != 'schedule' || github.repository == 'pestphp/pest'
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: true
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||
php: ['7.3', '7.4', '8.0']
|
||||
dependency-version: [prefer-lowest, prefer-stable]
|
||||
parallel: ['', '--parallel']
|
||||
symfony: ['7.0']
|
||||
php: ['8.2', '8.3']
|
||||
dependency_version: [prefer-lowest, prefer-stable]
|
||||
|
||||
name: PHP ${{ matrix.php }} - ${{ matrix.os }} - ${{ matrix.dependency-version }} - ${{ matrix.parallel }}
|
||||
name: PHP ${{ matrix.php }} - Symfony ^${{ matrix.symfony }} - ${{ matrix.os }} - ${{ matrix.dependency_version }}
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup PHP
|
||||
uses: shivammathur/setup-php@v2
|
||||
@ -30,16 +35,14 @@ jobs:
|
||||
echo "::add-matcher::${{ runner.tool_cache }}/php.json"
|
||||
echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json"
|
||||
|
||||
- name: Install PHP 7 dependencies
|
||||
run: composer update --${{ matrix.dependency-version }} --no-interaction --no-progress
|
||||
if: "matrix.php < 8"
|
||||
|
||||
- name: Install PHP 8 dependencies
|
||||
run: composer update --${{ matrix.dependency-version }} --ignore-platform-req=php --no-interaction --no-progress
|
||||
if: "matrix.php >= 8"
|
||||
- name: Install PHP dependencies
|
||||
run: composer update --${{ matrix.dependency_version }} --no-interaction --no-progress --ansi --with="symfony/console:~${{ matrix.symfony }}"
|
||||
|
||||
- name: Unit Tests
|
||||
run: php bin/pest --colors=always --exclude-group=integration ${{ matrix.parallel }}
|
||||
run: composer test:unit
|
||||
|
||||
- name: Parallel Tests
|
||||
run: composer test:parallel
|
||||
|
||||
- name: Integration Tests
|
||||
run: php bin/pest --colors=always --group=integration
|
||||
run: composer test:integration
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,9 +1,11 @@
|
||||
.idea/*
|
||||
.idea/codeStyleSettings.xml
|
||||
.temp/*
|
||||
composer.lock
|
||||
/vendor/
|
||||
coverage.xml
|
||||
.phpunit.result.cache
|
||||
.phpunit.cache
|
||||
/.php-cs-fixer.php
|
||||
.php-cs-fixer.cache
|
||||
.temp/coverage.php
|
||||
|
||||
@ -1,30 +0,0 @@
|
||||
<?php
|
||||
|
||||
$finder = PhpCsFixer\Finder::create()
|
||||
->in(__DIR__ . DIRECTORY_SEPARATOR . 'tests')
|
||||
->in(__DIR__ . DIRECTORY_SEPARATOR . 'bin')
|
||||
->in(__DIR__ . DIRECTORY_SEPARATOR . 'stubs')
|
||||
->in(__DIR__ . DIRECTORY_SEPARATOR . 'src')
|
||||
->append(['.php-cs-fixer.dist.php']);
|
||||
|
||||
$rules = [
|
||||
'@Symfony' => true,
|
||||
'phpdoc_no_empty_return' => false,
|
||||
'array_syntax' => ['syntax' => 'short'],
|
||||
'yoda_style' => false,
|
||||
'binary_operator_spaces' => [
|
||||
'operators' => [
|
||||
'=>' => 'align',
|
||||
'=' => 'align',
|
||||
],
|
||||
],
|
||||
'concat_space' => ['spacing' => 'one'],
|
||||
'not_operator_with_space' => false,
|
||||
];
|
||||
|
||||
$rules['increment_style'] = ['style' => 'post'];
|
||||
|
||||
return (new PhpCsFixer\Config())
|
||||
->setUsingCache(true)
|
||||
->setRules($rules)
|
||||
->setFinder($finder);
|
||||
317
CHANGELOG.md
317
CHANGELOG.md
@ -1,317 +0,0 @@
|
||||
# Changelog
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](http://keepachangelog.com/)
|
||||
and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
## [v1.17.0 (2021-08-26)](https://github.com/pestphp/pest/compare/v1.16.0...v1.17.0)
|
||||
### Added
|
||||
- `toThrow` expectation ([#361](https://github.com/pestphp/pest/pull/361))
|
||||
|
||||
## [v1.16.0 (2021-08-19)](https://github.com/pestphp/pest/compare/v1.15.0...v1.16.0)
|
||||
### Added
|
||||
- Support for new parallel options ([#369](https://github.com/pestphp/pest/pull/369))
|
||||
|
||||
## [v1.15.0 (2021-08-04)](https://github.com/pestphp/pest/compare/v1.14.0...v1.15.0)
|
||||
### Added
|
||||
- `toBeTruthy` and `toBeFalsy` ([#367](https://github.com/pestphp/pest/pull/367))
|
||||
|
||||
## [v1.14.0 (2021-08-03)](https://github.com/pestphp/pest/compare/v1.13.0...v1.14.0)
|
||||
### Added
|
||||
- A new bound closure that allows you to access the test case in Datasets ([#364](https://github.com/pestphp/pest/pull/364))
|
||||
|
||||
## [v1.13.0 (2021-08-02)](https://github.com/pestphp/pest/compare/v1.12.0...v1.13.0)
|
||||
### Added
|
||||
- A cleaner output when running the Pest runner in PhpStorm ([#350](https://github.com/pestphp/pest/pull/350))
|
||||
- `toBeIn` expectation ([#363](https://github.com/pestphp/pest/pull/363))
|
||||
|
||||
### Fixed
|
||||
- `skip` with false condition marking test as skipped ([22b822c](https://github.com/pestphp/pest/commit/22b822ce87a3d19d84960fa5c93eb286820b525d))
|
||||
|
||||
## [v1.12.0 (2021-07-26)](https://github.com/pestphp/pest/compare/v1.11.0...v1.12.0)
|
||||
### Added
|
||||
- `--force` option to override tests in `pest:test` artisan command ([#353](https://github.com/pestphp/pest/pull/353))
|
||||
- Support for PHPUnit `^9.3.7` ([ca9d783](https://github.com/pestphp/pest/commit/ca9d783cf942a2caabc85ff7a728c7f28350c67a))
|
||||
|
||||
### Fixed
|
||||
- `beforeAll` and `afterAll` behind called multiple times per test ([#357](https://github.com/pestphp/pest/pull/357))
|
||||
|
||||
## [v1.11.0 (2021-07-21)](https://github.com/pestphp/pest/compare/v1.10.0...v1.11.0)
|
||||
### Added
|
||||
- Support for interacting with datasets in higher order tests ([#352](https://github.com/pestphp/pest/pull/352))
|
||||
|
||||
### Changed
|
||||
- The unit test stub now uses the expectation API ([#348](https://github.com/pestphp/pest/pull/348))
|
||||
|
||||
### Fixed
|
||||
- PhpStorm will no longer show 0 assertions in the output ([#349](https://github.com/pestphp/pest/pull/349))
|
||||
|
||||
## [v1.10.0 (2021-07-12)](https://github.com/pestphp/pest/compare/v1.9.1...v1.10.0)
|
||||
### Added
|
||||
- The ability to use higher order expectations inside higher order tests ([#341](https://github.com/pestphp/pest/pull/341))
|
||||
|
||||
## [v1.9.1 (2021-07-11)](https://github.com/pestphp/pest/compare/v1.9.0...v1.9.1)
|
||||
### Fixed
|
||||
- Callable `expect` values in higher order tests failing if the value was an existing method name ([#334](https://github.com/pestphp/pest/pull/344))
|
||||
|
||||
## [v1.9.0 (2021-07-09)](https://github.com/pestphp/pest/compare/v1.8.0...v1.9.0)
|
||||
### Changed
|
||||
- You may now pass just an exception message when using the `throws` method ([#339](https://github.com/pestphp/pest/pull/339))
|
||||
|
||||
## [v1.8.0 (2021-07-08)](https://github.com/pestphp/pest/compare/v1.7.1...v1.8.0)
|
||||
### Added
|
||||
- A new `tap` and test case aware `expect` methods for higher order tests ([#331](https://github.com/pestphp/pest/pull/331))
|
||||
- Access to test case methods and properties when using `skip` ([#338](https://github.com/pestphp/pest/pull/338))
|
||||
|
||||
## [v1.7.1 (2021-06-24)](https://github.com/pestphp/pest/compare/v1.7.0...v1.7.1)
|
||||
### Fixed
|
||||
- The `and` method not being usable in Higher Order expectations ([#330](https://github.com/pestphp/pest/pull/330))
|
||||
|
||||
## [v1.7.0 (2021-06-19)](https://github.com/pestphp/pest/compare/v1.6.0...v1.7.0)
|
||||
### Added
|
||||
- Support for non-callable values in the sequence method, which will be passed as `toEqual` ([#323](https://github.com/pestphp/pest/pull/323))
|
||||
- Support for nested Higher Order Expectations ([#324](https://github.com/pestphp/pest/pull/324))
|
||||
|
||||
## [v1.6.0 (2021-06-18)](https://github.com/pestphp/pest/compare/v1.5.0...v1.6.0)
|
||||
### Added
|
||||
- Adds a new `json` expectation method to improve testing with JSON strings ([#325](https://github.com/pestphp/pest/pull/325))
|
||||
- Adds dot notation support to the `toHaveKey` and `toHaveKeys` expectations ([#322](https://github.com/pestphp/pest/pull/322))
|
||||
|
||||
## [v1.5.0 (2021-06-15)](https://github.com/pestphp/pest/compare/v1.4.0...v1.5.0)
|
||||
### Changed
|
||||
- Moves plugins from the `require` section to the core itself ([#317](https://github.com/pestphp/pest/pull/317)), ([#318](https://github.com/pestphp/pest/pull/318)), ([#320](https://github.com/pestphp/pest/pull/320))
|
||||
|
||||
## [v1.4.0 (2021-06-10)](https://github.com/pestphp/pest/compare/v1.3.2...v1.4.0)
|
||||
### Added
|
||||
- Support for multiple datasets (Matrix) on the `with` method ([#303](https://github.com/pestphp/pest/pull/303))
|
||||
- Support for incompleted tests ([49de462](https://github.com/pestphp/pest/commit/49de462250cf9f65f09e13eaf6dcc0e06865b930))
|
||||
|
||||
## [v1.3.2 (2021-06-07)](https://github.com/pestphp/pest/compare/v1.3.1...v1.3.2)
|
||||
### Fixed
|
||||
- Test cases with the @ symbol in the directory fail ([#308](https://github.com/pestphp/pest/pull/308))
|
||||
|
||||
## [v1.3.1 (2021-06-06)](https://github.com/pestphp/pest/compare/v1.3.0...v1.3.1)
|
||||
### Added
|
||||
- Added for PHPUnit 9.5.5 ([#310](https://github.com/pestphp/pest/pull/310))
|
||||
|
||||
### Changed
|
||||
- Lock minimum Pest plugin versions ([#306](https://github.com/pestphp/pest/pull/306))
|
||||
|
||||
## [v1.3.0 (2021-05-23)](https://github.com/pestphp/pest/compare/v1.2.1...v1.3.0)
|
||||
### Added
|
||||
- Named datasets no longer show the arguments ([#302](https://github.com/pestphp/pest/pull/302))
|
||||
|
||||
### Fixed
|
||||
- Wraps global functions within `function_exists` ([#300](https://github.com/pestphp/pest/pull/300))
|
||||
|
||||
## [v1.2.1 (2021-05-14)](https://github.com/pestphp/pest/compare/v1.2.0...v1.2.1)
|
||||
### Fixed
|
||||
- Laravel commands failing with new `--test-directory` option ([#297](https://github.com/pestphp/pest/pull/297))
|
||||
|
||||
## [v1.2.0 (2021-05-13)](https://github.com/pestphp/pest/compare/v1.1.0...v1.2.0)
|
||||
### Added
|
||||
- Adds JUnit / Infection support ([#291](https://github.com/pestphp/pest/pull/291))
|
||||
- `--test-directory` command line option ([#283](https://github.com/pestphp/pest/pull/283))
|
||||
|
||||
## [v1.1.0 (2021-05-02)](https://github.com/pestphp/pest/compare/v1.0.5...v1.1.0)
|
||||
### Added
|
||||
- Possibility of "hooks" being added using the "uses" function ([#282](https://github.com/pestphp/pest/pull/282))
|
||||
|
||||
## [v1.0.5 (2021-03-31)](https://github.com/pestphp/pest/compare/v1.0.4...v1.0.5)
|
||||
### Added
|
||||
- Add `--browse` option to `pest:dusk` command ([#280](https://github.com/pestphp/pest/pull/280))
|
||||
- Support for PHPUnit 9.5.4 ([#284](https://github.com/pestphp/pest/pull/284))
|
||||
|
||||
## [v1.0.4 (2021-03-17)](https://github.com/pestphp/pest/compare/v1.0.3...v1.0.4)
|
||||
### Added
|
||||
- Support for PHPUnit 9.5.3 ([#278](https://github.com/pestphp/pest/pull/278))
|
||||
|
||||
## [v1.0.3 (2021-03-13)](https://github.com/pestphp/pest/compare/v1.0.2...v1.0.3)
|
||||
### Added
|
||||
- Support for test extensions ([#269](https://github.com/pestphp/pest/pull/269))
|
||||
|
||||
## [v1.0.2 (2021-02-04)](https://github.com/pestphp/pest/compare/v1.0.1...v1.0.2)
|
||||
### Added
|
||||
- Support for PHPUnit 9.5.2 ([#267](https://github.com/pestphp/pest/pull/267))
|
||||
|
||||
## [v1.0.1 (2021-01-18)](https://github.com/pestphp/pest/compare/v1.0.0...v1.0.1)
|
||||
### Added
|
||||
- Support for PHPUnit 9.5.1 ([#261](https://github.com/pestphp/pest/pull/261))
|
||||
|
||||
### Fixed
|
||||
- Fix `TestCase@expect` PHPDoc tag ([#251](https://github.com/pestphp/pest/pull/251))
|
||||
|
||||
## [v1.0.0 (2021-01-03)](https://github.com/pestphp/pest/compare/v0.3.19...v1.0.0)
|
||||
### Added
|
||||
- `pest:test --dusk` option ([#245](https://github.com/pestphp/pest/pull/245))
|
||||
|
||||
### Changed
|
||||
- Stable version
|
||||
- Updates init structure ([#240](https://github.com/pestphp/pest/pull/240))
|
||||
|
||||
## [v0.3.19 (2020-12-27)](https://github.com/pestphp/pest/compare/v0.3.18...v0.3.19)
|
||||
### Fixed
|
||||
- Fix binary path in `pest:dusk` command ([#239](https://github.com/pestphp/pest/pull/239))
|
||||
|
||||
## [v0.3.18 (2020-12-26)](https://github.com/pestphp/pest/compare/v0.3.17...v0.3.18)
|
||||
### Added
|
||||
- `toBeJson()` expectation ([plugin-expectations#2](https://github.com/pestphp/pest-plugin-expectations/pull/2))
|
||||
|
||||
## [v0.3.17 (2020-12-20)](https://github.com/pestphp/pest/compare/v0.3.16...v0.3.17)
|
||||
### Fixed
|
||||
- Class inheritance with `depends()` ([#236](https://github.com/pestphp/pest/pull/236))
|
||||
|
||||
## [v0.3.16 (2020-12-13)](https://github.com/pestphp/pest/compare/v0.3.15...v0.3.16)
|
||||
### Changed
|
||||
- Moves expectation API for external plugin ([5d7f262](https://github.com/pestphp/pest/commit/5d7f262f4ab280a660a85900f402eebb23abfda8))
|
||||
|
||||
## [v0.3.15 (2020-12-04)](https://github.com/pestphp/pest/compare/v0.3.14...v0.3.15)
|
||||
### Added
|
||||
- Support for PHPUnit 9.5.0 ([#234](https://github.com/pestphp/pest/pull/234))
|
||||
- Support for extending expectation API ([#232](https://github.com/pestphp/pest/pull/232))
|
||||
|
||||
### Fixed
|
||||
- Static analysis while using string as key for datasets ([#233](https://github.com/pestphp/pest/pull/233))
|
||||
|
||||
## [v0.3.14 (2020-11-28)](https://github.com/pestphp/pest/compare/v0.3.13...v0.3.14)
|
||||
### Added
|
||||
- `pest:dusk` command ([#223](https://github.com/pestphp/pest/pull/223))
|
||||
- Better feedback on errors in `toMatchArray` and `toMatchObject` ([#231](https://github.com/pestphp/pest/pull/231))
|
||||
|
||||
## [v0.3.13 (2020-11-23)](https://github.com/pestphp/pest/compare/v0.3.12...v0.3.13)
|
||||
### Added
|
||||
- `toMatchArray` expectation ([7bea51f](https://github.com/pestphp/pest/commit/7bea51fe09dd2eca7093e4c34cf2dab2e8d39fa5), [3fd24d9](https://github.com/pestphp/pest/commit/3fd24d96d3145dcebdb0aab40aa8b76faa8b6979))
|
||||
- Add Pest options to `--help` output ([#217](https://github.com/pestphp/pest/pull/217))
|
||||
|
||||
### Fixed
|
||||
- Resolve issue with name resolution in `depends()` ([#216](https://github.com/pestphp/pest/pull/216))
|
||||
|
||||
## [v0.3.12 (2020-11-11)](https://github.com/pestphp/pest/compare/v0.3.11...v0.3.12)
|
||||
### Added
|
||||
- Add support for PHPUnit 9.4.3 ([#219](https://github.com/pestphp/pest/pull/219))
|
||||
|
||||
## [v0.3.11 (2020-11-09)](https://github.com/pestphp/pest/compare/v0.3.10...v0.3.11)
|
||||
### Changed
|
||||
- Improved the exception output for the TeamCity printer (usage with phpstorm plugin) ([#215](https://github.com/pestphp/pest/pull/215))
|
||||
|
||||
## [v0.3.10 (2020-11-01)](https://github.com/pestphp/pest/compare/v0.3.9...v0.3.10)
|
||||
### Added
|
||||
- Add support for PHPUnit 9.4.2 ([d177ab5](https://github.com/pestphp/pest/commit/d177ab5ec2030c5bb8e418d10834c370c94c433d))
|
||||
|
||||
## [v0.3.9 (2020-10-13)](https://github.com/pestphp/pest/compare/v0.3.8...v0.3.9)
|
||||
### Added
|
||||
- Add support for named datasets in description output ([#134](https://github.com/pestphp/pest/pull/134))
|
||||
- Add Pest version to `--help` output ([#203](https://github.com/pestphp/pest/pull/203))
|
||||
- Add support for PHPUnit 9.4.1 ([#207](https://github.com/pestphp/pest/pull/207))
|
||||
|
||||
## [v0.3.8 (2020-10-03)](https://github.com/pestphp/pest/compare/v0.3.7...v0.3.8)
|
||||
### Added
|
||||
- Add support for PHPUnit 9.4.0 ([#199](https://github.com/pestphp/pest/pull/199))
|
||||
|
||||
### Fixed
|
||||
- Fix chained higher order assertions returning void ([#196](https://github.com/pestphp/pest/pull/196))
|
||||
|
||||
## [v0.3.7 (2020-09-30)](https://github.com/pestphp/pest/compare/v0.3.6...v0.3.7)
|
||||
### Added
|
||||
- Add support for PHPUnit 9.3.11 ([#193](https://github.com/pestphp/pest/pull/193))
|
||||
|
||||
## [v0.3.6 (2020-09-21)](https://github.com/pestphp/pest/compare/v0.3.5...v0.3.6)
|
||||
### Added
|
||||
- `toMatch` expectation ([#191](https://github.com/pestphp/pest/pull/191))
|
||||
- `toMatchConstraint` expectation ([#190](https://github.com/pestphp/pest/pull/190))
|
||||
|
||||
## [v0.3.5 (2020-09-16)](https://github.com/pestphp/pest/compare/v0.3.4...v0.3.5)
|
||||
### Added
|
||||
- `toStartWith` and `toEndWith` expectations ([#187](https://github.com/pestphp/pest/pull/187))
|
||||
|
||||
## [v0.3.4 (2020-09-15)](https://github.com/pestphp/pest/compare/v0.3.3...v0.3.4)
|
||||
### Added
|
||||
- `toMatchObject` expectation ([4e184b2](https://github.com/pestphp/pest/commit/4e184b2f906c318a5e9cd38fe693cdab5c48d8a2))
|
||||
|
||||
## [v0.3.3 (2020-09-13)](https://github.com/pestphp/pest/compare/v0.3.2...v0.3.3)
|
||||
### Added
|
||||
- `toHaveKeys` expectation ([204f343](https://github.com/pestphp/pest/commit/204f343831adc17bb3734553c24fac92d02f27c7))
|
||||
|
||||
## [v0.3.2 (2020-09-12)](https://github.com/pestphp/pest/compare/v0.3.1...v0.3.2)
|
||||
### Added
|
||||
- Support to PHPUnit 9.3.9, and 9.3.10 ([1318bf9](https://github.com/pestphp/pest/commit/97f98569bc86e8b87f8cde963fe7b4bf5399623b))
|
||||
|
||||
## [v0.3.1 (2020-08-29)](https://github.com/pestphp/pest/compare/v0.3.0...v0.3.1)
|
||||
### Added
|
||||
- Support to PHPUnit 9.3.8 ([#174](https://github.com/pestphp/pest/pull/174))
|
||||
|
||||
## [v0.3.0 (2020-08-27)](https://github.com/pestphp/pest/compare/v0.2.3...v0.3.0)
|
||||
### Added
|
||||
- Expectation API (TODO)
|
||||
- PHPUnit 9.3 and PHP 8 support ([#128](https://github.com/pestphp/pest/pull/128))
|
||||
- Forwards `$this` calls to globals ([#169](https://github.com/pestphp/pest/pull/169))
|
||||
|
||||
### Fixed
|
||||
- don't decorate output if --colors=never is set ([36b879f](https://github.com/pestphp/pest/commit/36b879f97d7b187c87a94eb60af5b7d3b7253d56))
|
||||
|
||||
## [v0.2.3 (2020-07-01)](https://github.com/pestphp/pest/compare/v0.2.2...v0.2.3)
|
||||
### Added
|
||||
- `--init` and `pest:install` artisan command output changes ([#118](https://github.com/pestphp/pest/pull/118), [db7c4b1](https://github.com/pestphp/pest/commit/db7c4b174f0974969450dda71dcd649ef0c073a3))
|
||||
- `--version` option to view the current version of Pest ([9ea51ca](https://github.com/pestphp/pest/commit/9ea51caf3f74569debb1e465992e9ea916cb80fe))
|
||||
|
||||
## [v0.2.2 (2020-06-21)](https://github.com/pestphp/pest/compare/v0.2.1...v0.2.2)
|
||||
### Added
|
||||
- `depends` phpunit feature ([#103](https://github.com/pestphp/pest/pull/103))
|
||||
|
||||
### Fixes
|
||||
- datasets name conflict ([#101](https://github.com/pestphp/pest/pull/101))
|
||||
|
||||
## [v0.2.1 (2020-06-17)](https://github.com/pestphp/pest/compare/v0.2.0...v0.2.1)
|
||||
### Fixes
|
||||
- Multiple `uses` in the same path override previous `uses` ([#97](https://github.com/pestphp/pest/pull/97))
|
||||
|
||||
## [v0.2.0 (2020-06-14)](https://github.com/pestphp/pest/compare/v0.1.5...v0.2.0)
|
||||
### Adds
|
||||
- `--init` option to install Pest on a new blank project ([70b3c7e](https://github.com/pestphp/pest/commit/70b3c7ea1ddb031f3bbfaabdc28d56270608ebbd))
|
||||
- pending higher orders tests aka tests without description ([aa1917c](https://github.com/pestphp/pest/commit/aa1917c28d9b69c2bd1d51f986c4f61318ee7e16))
|
||||
|
||||
### Fixed
|
||||
- `--verbose` and `--colors` options not being used by printers ([#51](https://github.com/pestphp/pest/pull/51))
|
||||
- missing support on windows ([#61](https://github.com/pestphp/pest/pull/61))
|
||||
|
||||
### Changed
|
||||
- `helpers.php` stub provides now namespaced functions
|
||||
- functions provided by plugins are now namespaced functions:
|
||||
|
||||
```php
|
||||
use function Pest\Faker\faker;
|
||||
|
||||
it('foo', function () {
|
||||
$name = faker()->name;
|
||||
});
|
||||
```
|
||||
|
||||
## [v0.1.5 (2020-05-24)](https://github.com/pestphp/pest/compare/v0.1.4...v0.1.5)
|
||||
### Fixed
|
||||
- Missing default decorated output on coverage ([88d2391](https://github.com/pestphp/pest/commit/88d2391d2e6fe9c9416462734b9b523cb418f469))
|
||||
|
||||
## [v0.1.4 (2020-05-24)](https://github.com/pestphp/pest/compare/v0.1.3...v0.1.4)
|
||||
### Added
|
||||
- Support to Lumen on artisan commands ([#18](https://github.com/pestphp/pest/pull/18))
|
||||
|
||||
### Fixed
|
||||
- Mockery tests without assertions being considered risky ([415f571](https://github.com/pestphp/pest/commit/415f5719101b30c11d87f74810a71686ef2786c6))
|
||||
|
||||
## [v0.1.3 (2020-05-21)](https://github.com/pestphp/pest/compare/v0.1.2...v0.1.3)
|
||||
### Added
|
||||
- `Plugin::uses()` method for making traits globally available ([6c4be01](https://github.com/pestphp/pest/commit/6c4be0190e9493702a976b996bbbf5150cc6bb53))
|
||||
|
||||
## [v0.1.2 (2020-05-15)](https://github.com/pestphp/pest/compare/v0.1.1...v0.1.2)
|
||||
### Added
|
||||
- Support to custom helpers ([#7](https://github.com/pestphp/pest/pull/7))
|
||||
|
||||
## [v0.1.1 (2020-05-14)](https://github.com/pestphp/pest/compare/v0.1.0...v0.1.1)
|
||||
### Added
|
||||
- `test` function without any arguments returns the current test case ([6fc55be](https://github.com/pestphp/pest/commit/6fc55becc8aecff685a958617015be1a4c118b01))
|
||||
|
||||
### Fixed
|
||||
- "No coverage driver error" now returns proper error on Laravel ([28d8822](https://github.com/pestphp/pest/commit/28d8822de01f4fa92c62d8b8e019313f382b97e9))
|
||||
|
||||
## [v0.1.0 (2020-05-09)](https://github.com/pestphp/pest/commit/de2929077b344a099ef9c2ddc2f48abce14e248f)
|
||||
### Added
|
||||
- First version
|
||||
@ -31,6 +31,10 @@ composer lint
|
||||
```
|
||||
## Tests
|
||||
|
||||
Update the snapshots:
|
||||
```bash
|
||||
composer update:snapshots
|
||||
```
|
||||
Run all tests:
|
||||
```bash
|
||||
composer test
|
||||
@ -50,3 +54,22 @@ Integration tests:
|
||||
```bash
|
||||
composer test:integration
|
||||
```
|
||||
|
||||
## Simplified setup using Docker
|
||||
|
||||
If you have Docker installed, you can quickly get all dependencies for Pest in place using
|
||||
our Docker files. Assuming you have the repository cloned, you may run the following
|
||||
commands:
|
||||
|
||||
1. `make build` to build the Docker image
|
||||
2. `make install` to install Composer dependencies
|
||||
3. `make test` to run the project tests and analysis tools
|
||||
|
||||
If you want to check things work against a specific version of PHP, you may include
|
||||
the `PHP` build argument when building the image:
|
||||
|
||||
```bash
|
||||
make build ARGS="--build-arg PHP=8.3"
|
||||
```
|
||||
|
||||
The default PHP version will always be the lowest version of PHP supported by Pest.
|
||||
|
||||
14
Makefile
Normal file
14
Makefile
Normal file
@ -0,0 +1,14 @@
|
||||
# Well documented Makefiles
|
||||
DEFAULT_GOAL := help
|
||||
help:
|
||||
@awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m<target>\033[0m\n"} /^[a-zA-Z0-9_-]+:.*?##/ { printf " \033[36m%-40s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST)
|
||||
|
||||
build: ## Build all docker images. Specify the command e.g. via make build ARGS="--build-arg PHP=8.2"
|
||||
docker compose build $(ARGS)
|
||||
|
||||
##@ [Application]
|
||||
install: ## Install the composer dependencies
|
||||
docker compose run --rm composer install
|
||||
|
||||
test: ## Run the tests
|
||||
docker compose run --rm composer test
|
||||
39
README.md
39
README.md
@ -1,7 +1,7 @@
|
||||
<p align="center">
|
||||
<img src="https://raw.githubusercontent.com/pestphp/art/master/readme.png" width="600" alt="PEST">
|
||||
<img src="https://raw.githubusercontent.com/pestphp/art/master/v3/banner.png" width="600" alt="PEST">
|
||||
<p align="center">
|
||||
<a href="https://github.com/pestphp/pest/actions"><img alt="GitHub Workflow Status (master)" src="https://img.shields.io/github/workflow/status/pestphp/pest/Tests/master"></a>
|
||||
<a href="https://github.com/pestphp/pest/actions"><img alt="GitHub Workflow Status (master)" src="https://img.shields.io/github/actions/workflow/status/pestphp/pest/tests.yml?branch=2.x&label=Tests%202.x"></a>
|
||||
<a href="https://packagist.org/packages/pestphp/pest"><img alt="Total Downloads" src="https://img.shields.io/packagist/dt/pestphp/pest"></a>
|
||||
<a href="https://packagist.org/packages/pestphp/pest"><img alt="Latest Version" src="https://img.shields.io/packagist/v/pestphp/pest"></a>
|
||||
<a href="https://packagist.org/packages/pestphp/pest"><img alt="License" src="https://img.shields.io/packagist/l/pestphp/pest"></a>
|
||||
@ -9,22 +9,35 @@
|
||||
</p>
|
||||
|
||||
------
|
||||
**Pest** is an elegant PHP Testing Framework with a focus on simplicity. It was carefully crafted to bring the joy of testing to PHP.
|
||||
|
||||
- Explore the docs: **[pestphp.com »](https://pestphp.com)**
|
||||
- Follow us on Twitter: **[@pestphp »](https://twitter.com/pestphp)**
|
||||
- Join us on the Discord Server: **[discord.gg/bMAJv82 »](https://discord.gg/bMAJv82)**
|
||||
> Pest v3 Now Available: **[Read the announcement »](https://pestphp.com/docs/pest3-now-available)**.
|
||||
|
||||
## Pest Sponsors
|
||||
**Pest** is an elegant PHP testing Framework with a focus on simplicity, meticulously designed to bring back the joy of testing in PHP.
|
||||
|
||||
We would like to extend our thanks to the following sponsors for funding Pest development. If you are interested in becoming a sponsor, please visit the Nuno Maduro's [Sponsors page](https://github.com/sponsors/nunomaduro).
|
||||
- Explore our docs at **[pestphp.com »](https://pestphp.com)**
|
||||
- Follow us on Twitter at **[@pestphp »](https://twitter.com/pestphp)**
|
||||
- Join us at **[discord.gg/kaHY6p54JH »](https://discord.gg/kaHY6p54JH)** or **[t.me/+kYH5G4d5MV83ODk0 »](https://t.me/+kYH5G4d5MV83ODk0)**
|
||||
|
||||
## Sponsors
|
||||
|
||||
We cannot thank our sponsors enough for their incredible support in funding Pest's development. Their contributions have been instrumental in making Pest the best it can be. For those who are interested in becoming a sponsor, please visit Nuno Maduro's Sponsor page at **[github.com/sponsors/nunomaduro](https://github.com/sponsors/nunomaduro)**.
|
||||
|
||||
### Platinum Sponsors
|
||||
|
||||
- **[LaraJobs](https://larajobs.com)**
|
||||
- **[Brokerchooser](https://brokerchooser.com)**
|
||||
- **[Forge](https://forge.laravel.com)**
|
||||
- **[Spatie](https://spatie.be)**
|
||||
- **[Worksome](https://www.worksome.com/)**
|
||||
|
||||
### Premium Sponsors
|
||||
|
||||
- **[Akaunting](https://akaunting.com)**
|
||||
- **[Codecourse](https://codecourse.com/)**
|
||||
- **[Meema](https://meema.io/)**
|
||||
- **[Scout APM](https://scoutapm.com)**
|
||||
- **[Spatie](https://spatie.be/)**
|
||||
- [Akaunting](https://akaunting.com/?ref=pestphp)
|
||||
- [Codecourse](https://codecourse.com/?ref=pestphp)
|
||||
- [DocuWriter.ai](https://www.docuwriter.ai/?ref=pestphp)
|
||||
- [Laracasts](https://laracasts.com/?ref=pestphp)
|
||||
- [Localazy](https://localazy.com/?ref=pestphp)
|
||||
- [Route4Me](https://www.route4me.com/?ref=pestphp)
|
||||
- [Zapiet](https://www.zapiet.com/?ref=pestphp)
|
||||
|
||||
Pest is an open-sourced software licensed under the **[MIT license](https://opensource.org/licenses/MIT)**.
|
||||
|
||||
@ -2,14 +2,17 @@
|
||||
|
||||
When releasing a new version of Pest there are some checks and updates that need to be done:
|
||||
|
||||
- Clear your local repository with: `git add . && git reset --hard && git checkout master`
|
||||
- On the GitHub repository, check the contents of [github.com/pestphp/pest/compare/{latest_version}...master](https://github.com/pestphp/pest/compare/{latest_version}...master) and update the [changelog](CHANGELOG.md) file with the main changes for this release
|
||||
> **For Pest v2 you should use the `2.x` branch instead.**
|
||||
|
||||
- Clear your local repository with: `git add . && git reset --hard && git checkout 3.x`
|
||||
- On the GitHub repository, check the contents of [github.com/pestphp/pest/compare/{latest_version}...3.x](https://github.com/pestphp/pest/compare/{latest_version}...3.x)
|
||||
- Update the version number in [src/Pest.php](src/Pest.php)
|
||||
- Run the tests locally using: `composer test`
|
||||
- Commit the CHANGELOG and Pest file with the message: `git commit -m "release: vX.X.X"`
|
||||
- Commit the Pest file with the message: `git commit -m "release: vX.X.X"`
|
||||
- Push the changes to GitHub
|
||||
- Check that the CI is passing as expected: [github.com/pestphp/pest/actions](https://github.com/pestphp/pest/actions)
|
||||
- Tag and push the tag with `git tag vX.X.X && git push --tags`
|
||||
- Publish release here: [github.com/pestphp/pest/releases/new](https://github.com/pestphp/pest/releases/new).
|
||||
|
||||
### Plugins
|
||||
|
||||
|
||||
188
bin/pest
188
bin/pest
@ -1,16 +1,123 @@
|
||||
#!/usr/bin/env php
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
use NunoMaduro\Collision\Provider;
|
||||
use Pest\Actions\ValidatesEnvironment;
|
||||
use Pest\Support\Container;
|
||||
use Pest\Kernel;
|
||||
use Pest\Panic;
|
||||
use Pest\TestCaseFilters\GitDirtyTestCaseFilter;
|
||||
use Pest\TestCaseMethodFilters\AssigneeTestCaseFilter;
|
||||
use Pest\TestCaseMethodFilters\IssueTestCaseFilter;
|
||||
use Pest\TestCaseMethodFilters\NotesTestCaseFilter;
|
||||
use Pest\TestCaseMethodFilters\PrTestCaseFilter;
|
||||
use Pest\TestCaseMethodFilters\TodoTestCaseFilter;
|
||||
use Pest\TestSuite;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\ArgvInput;
|
||||
use Symfony\Component\Console\Output\ConsoleOutput;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
(static function () {
|
||||
// Ensures Collision's Printer is registered.
|
||||
$_SERVER['COLLISION_PRINTER'] = 'DefaultPrinter';
|
||||
|
||||
$arguments = $originalArguments = $_SERVER['argv'];
|
||||
|
||||
$dirty = false;
|
||||
$todo = false;
|
||||
$notes = false;
|
||||
|
||||
foreach ($arguments as $key => $value) {
|
||||
|
||||
if ($value === '--compact') {
|
||||
$_SERVER['COLLISION_PRINTER_COMPACT'] = 'true';
|
||||
unset($arguments[$key]);
|
||||
}
|
||||
|
||||
if ($value === '--profile') {
|
||||
$_SERVER['COLLISION_PRINTER_PROFILE'] = 'true';
|
||||
unset($arguments[$key]);
|
||||
}
|
||||
|
||||
if (str_contains($value, '--test-directory=')) {
|
||||
unset($arguments[$key]);
|
||||
} else if ($value === '--test-directory') {
|
||||
unset($arguments[$key]);
|
||||
|
||||
if (isset($arguments[$key + 1])) {
|
||||
unset($arguments[$key + 1]);
|
||||
}
|
||||
}
|
||||
|
||||
if ($value === '--dirty') {
|
||||
$dirty = true;
|
||||
unset($arguments[$key]);
|
||||
}
|
||||
|
||||
if (in_array($value, ['--todo', '--todos'], true)) {
|
||||
$todo = true;
|
||||
unset($arguments[$key]);
|
||||
}
|
||||
|
||||
if ($value === '--notes') {
|
||||
$notes = true;
|
||||
unset($arguments[$key]);
|
||||
}
|
||||
|
||||
if (str_contains($value, '--assignee=')) {
|
||||
unset($arguments[$key]);
|
||||
} else if ($value === '--assignee') {
|
||||
unset($arguments[$key]);
|
||||
|
||||
if (isset($arguments[$key + 1])) {
|
||||
unset($arguments[$key + 1]);
|
||||
}
|
||||
}
|
||||
|
||||
if (str_contains($value, '--issue=')) {
|
||||
unset($arguments[$key]);
|
||||
} else if ($value === '--issue') {
|
||||
unset($arguments[$key]);
|
||||
|
||||
if (isset($arguments[$key + 1])) {
|
||||
unset($arguments[$key + 1]);
|
||||
}
|
||||
}
|
||||
|
||||
if (str_contains($value, '--ticket=')) {
|
||||
unset($arguments[$key]);
|
||||
} else if ($value === '--ticket') {
|
||||
unset($arguments[$key]);
|
||||
|
||||
if (isset($arguments[$key + 1])) {
|
||||
unset($arguments[$key + 1]);
|
||||
}
|
||||
}
|
||||
|
||||
if (str_contains($value, '--pr=')) {
|
||||
unset($arguments[$key]);
|
||||
} else if ($value === '--pr') {
|
||||
unset($arguments[$key]);
|
||||
|
||||
if (isset($arguments[$key + 1])) {
|
||||
unset($arguments[$key + 1]);
|
||||
}
|
||||
}
|
||||
|
||||
if (str_contains($value, '--pull-request=')) {
|
||||
unset($arguments[$key]);
|
||||
} else if ($value === '--pull-request') {
|
||||
unset($arguments[$key]);
|
||||
|
||||
if (isset($arguments[$key + 1])) {
|
||||
unset($arguments[$key + 1]);
|
||||
}
|
||||
}
|
||||
|
||||
if (str_contains($value, '--teamcity')) {
|
||||
unset($arguments[$key]);
|
||||
$arguments[] = '--no-output';
|
||||
unset($_SERVER['COLLISION_PRINTER']);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Used when Pest is required using composer.
|
||||
$vendorPath = dirname(__DIR__, 4).'/vendor/autoload.php';
|
||||
|
||||
@ -25,39 +132,60 @@ use Symfony\Component\Console\Output\OutputInterface;
|
||||
$autoloadPath = $localPath;
|
||||
}
|
||||
|
||||
(new Provider())->register();
|
||||
|
||||
// Get $rootPath based on $autoloadPath
|
||||
$rootPath = dirname($autoloadPath, 2);
|
||||
$argv = new ArgvInput();
|
||||
$input = new ArgvInput();
|
||||
|
||||
$testSuite = TestSuite::getInstance($rootPath, $argv->getParameterOption('--test-directory', 'tests'));
|
||||
$testSuite = TestSuite::getInstance(
|
||||
$rootPath,
|
||||
$input->getParameterOption('--test-directory', 'tests'),
|
||||
);
|
||||
|
||||
if ($dirty) {
|
||||
$testSuite->tests->addTestCaseFilter(new GitDirtyTestCaseFilter($rootPath));
|
||||
}
|
||||
|
||||
if ($todo) {
|
||||
$testSuite->tests->addTestCaseMethodFilter(new TodoTestCaseFilter());
|
||||
}
|
||||
|
||||
if ($notes) {
|
||||
$testSuite->tests->addTestCaseMethodFilter(new NotesTestCaseFilter());
|
||||
}
|
||||
|
||||
if ($assignee = $input->getParameterOption('--assignee')) {
|
||||
$testSuite->tests->addTestCaseMethodFilter(new AssigneeTestCaseFilter((string) $assignee));
|
||||
}
|
||||
|
||||
if ($issue = $input->getParameterOption('--issue')) {
|
||||
$testSuite->tests->addTestCaseMethodFilter(new IssueTestCaseFilter((int) $issue));
|
||||
}
|
||||
|
||||
if ($issue = $input->getParameterOption('--ticket')) {
|
||||
$testSuite->tests->addTestCaseMethodFilter(new IssueTestCaseFilter((int) $issue));
|
||||
}
|
||||
|
||||
if ($pr = $input->getParameterOption('--pr')) {
|
||||
$testSuite->tests->addTestCaseMethodFilter(new PrTestCaseFilter((int) $pr));
|
||||
}
|
||||
|
||||
if ($pr = $input->getParameterOption('--pull-request')) {
|
||||
$testSuite->tests->addTestCaseMethodFilter(new PrTestCaseFilter((int) $pr));
|
||||
}
|
||||
|
||||
$isDecorated = $input->getParameterOption('--colors', 'always') !== 'never';
|
||||
|
||||
$isDecorated = $argv->getParameterOption('--colors', 'always') !== 'never';
|
||||
$output = new ConsoleOutput(ConsoleOutput::VERBOSITY_NORMAL, $isDecorated);
|
||||
|
||||
$container = Container::getInstance();
|
||||
$container->add(TestSuite::class, $testSuite);
|
||||
$container->add(OutputInterface::class, $output);
|
||||
try {
|
||||
$kernel = Kernel::boot($testSuite, $input, $output);
|
||||
|
||||
ValidatesEnvironment::in($testSuite);
|
||||
$result = $kernel->handle($originalArguments, $arguments);
|
||||
|
||||
$args = $_SERVER['argv'];
|
||||
|
||||
// Let's remove any arguments that PHPUnit does not understand
|
||||
if ($argv->hasParameterOption('--test-directory')) {
|
||||
foreach ($args as $key => $value) {
|
||||
if (strpos($value, '--test-directory') !== false) {
|
||||
unset($args[$key]);
|
||||
}
|
||||
}
|
||||
$kernel->terminate();
|
||||
} catch (Throwable|Error $e) {
|
||||
Panic::with($e);
|
||||
}
|
||||
|
||||
if (($runInParallel = $argv->hasParameterOption(['--parallel', '-p'])) && !class_exists(\Pest\Parallel\Command::class)) {
|
||||
$output->writeln("Parallel support requires the Pest Parallel plugin. Run <fg=yellow;options=bold>`composer require --dev pestphp/pest-plugin-parallel`</> first.");
|
||||
exit(Command::FAILURE);
|
||||
}
|
||||
|
||||
$command = $runInParallel ? \Pest\Parallel\Command::class : \Pest\Console\Command::class;
|
||||
exit($container->get($command)->run($args));
|
||||
exit($result);
|
||||
})();
|
||||
|
||||
105
bin/worker.php
Normal file
105
bin/worker.php
Normal file
@ -0,0 +1,105 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use ParaTest\WrapperRunner\ApplicationForWrapperWorker;
|
||||
use ParaTest\WrapperRunner\WrapperWorker;
|
||||
use Pest\Kernel;
|
||||
use Pest\Plugins\Actions\CallsHandleArguments;
|
||||
use Pest\TestSuite;
|
||||
use Symfony\Component\Console\Input\ArgvInput;
|
||||
use Symfony\Component\Console\Output\ConsoleOutput;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
$bootPest = (static function (): void {
|
||||
$workerArgv = new ArgvInput;
|
||||
|
||||
$rootPath = dirname(PHPUNIT_COMPOSER_INSTALL, 2);
|
||||
$testSuite = TestSuite::getInstance($rootPath, $workerArgv->getParameterOption(
|
||||
'--test-directory',
|
||||
'tests'
|
||||
));
|
||||
|
||||
$input = new ArgvInput;
|
||||
|
||||
$output = new ConsoleOutput(OutputInterface::VERBOSITY_NORMAL, true);
|
||||
|
||||
Kernel::boot($testSuite, $input, $output);
|
||||
});
|
||||
|
||||
(static function () use ($bootPest): void {
|
||||
$getopt = getopt('', [
|
||||
'status-file:',
|
||||
'progress-file:',
|
||||
'unexpected-output-file:',
|
||||
'testresult-file:',
|
||||
'teamcity-file:',
|
||||
'testdox-file:',
|
||||
'testdox-color',
|
||||
'phpunit-argv:',
|
||||
]);
|
||||
|
||||
$composerAutoloadFiles = [
|
||||
dirname(__DIR__, 3).DIRECTORY_SEPARATOR.'autoload.php',
|
||||
dirname(__DIR__, 2).DIRECTORY_SEPARATOR.'vendor'.DIRECTORY_SEPARATOR.'autoload.php',
|
||||
dirname(__DIR__).DIRECTORY_SEPARATOR.'vendor'.DIRECTORY_SEPARATOR.'autoload.php',
|
||||
];
|
||||
|
||||
foreach ($composerAutoloadFiles as $file) {
|
||||
|
||||
if (file_exists($file)) {
|
||||
require_once $file;
|
||||
define('PHPUNIT_COMPOSER_INSTALL', $file);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
assert(isset($getopt['status-file']) && is_string($getopt['status-file']));
|
||||
$statusFile = fopen($getopt['status-file'], 'wb');
|
||||
assert(is_resource($statusFile));
|
||||
|
||||
assert(isset($getopt['progress-file']) && is_string($getopt['progress-file']));
|
||||
assert(isset($getopt['unexpected-output-file']) && is_string($getopt['unexpected-output-file']));
|
||||
assert(isset($getopt['testresult-file']) && is_string($getopt['testresult-file']));
|
||||
assert(! isset($getopt['teamcity-file']) || is_string($getopt['teamcity-file']));
|
||||
assert(! isset($getopt['testdox-file']) || is_string($getopt['testdox-file']));
|
||||
|
||||
assert(isset($getopt['phpunit-argv']) && is_string($getopt['phpunit-argv']));
|
||||
$phpunitArgv = unserialize($getopt['phpunit-argv'], ['allowed_classes' => false]);
|
||||
assert(is_array($phpunitArgv));
|
||||
|
||||
$bootPest();
|
||||
|
||||
$phpunitArgv = CallsHandleArguments::execute($phpunitArgv);
|
||||
|
||||
$application = new ApplicationForWrapperWorker(
|
||||
$phpunitArgv,
|
||||
$getopt['progress-file'],
|
||||
$getopt['unexpected-output-file'],
|
||||
$getopt['testresult-file'],
|
||||
$getopt['teamcity-file'] ?? null,
|
||||
$getopt['testdox-file'] ?? null,
|
||||
isset($getopt['testdox-color']),
|
||||
$getopt['testdox-columns'] ?? null,
|
||||
);
|
||||
|
||||
while (true) {
|
||||
if (feof(STDIN)) {
|
||||
$application->end();
|
||||
exit;
|
||||
}
|
||||
|
||||
$testPath = fgets(STDIN);
|
||||
if ($testPath === false || $testPath === WrapperWorker::COMMAND_EXIT) {
|
||||
$application->end();
|
||||
exit;
|
||||
}
|
||||
|
||||
// It must be a 1 byte string to ensure filesize() is equal to the number of tests executed
|
||||
$exitCode = $application->runTest(realpath(trim($testPath)));
|
||||
|
||||
fwrite($statusFile, (string) $exitCode);
|
||||
fflush($statusFile);
|
||||
}
|
||||
})();
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "pestphp/pest",
|
||||
"description": "An elegant PHP Testing Framework.",
|
||||
"description": "The elegant PHP Testing Framework.",
|
||||
"keywords": [
|
||||
"php",
|
||||
"framework",
|
||||
@ -17,10 +17,19 @@
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": "^7.3 || ^8.0",
|
||||
"nunomaduro/collision": "^5.4.0",
|
||||
"pestphp/pest-plugin": "^1.0.0",
|
||||
"phpunit/phpunit": "^9.5.5"
|
||||
"php": "^8.2.0",
|
||||
"brianium/paratest": "^7.5.4",
|
||||
"nunomaduro/collision": "^8.4.0",
|
||||
"nunomaduro/termwind": "^2.1.0",
|
||||
"pestphp/pest-plugin": "^3.0.0",
|
||||
"pestphp/pest-plugin-arch": "^3.0.0",
|
||||
"pestphp/pest-plugin-mutate": "^3.0.3",
|
||||
"phpunit/phpunit": "^11.3.4"
|
||||
},
|
||||
"conflict": {
|
||||
"phpunit/phpunit": ">11.3.4",
|
||||
"sebastian/exporter": "<6.0.0",
|
||||
"webmozart/assert": "<1.11.0"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
@ -33,6 +42,9 @@
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"Tests\\Fixtures\\Covers\\": "tests/Fixtures/Covers",
|
||||
"Tests\\Fixtures\\Inheritance\\": "tests/Fixtures/Inheritance",
|
||||
"Tests\\Fixtures\\Arch\\": "tests/Fixtures/Arch",
|
||||
"Tests\\": "tests/PHPUnit/"
|
||||
},
|
||||
"files": [
|
||||
@ -40,51 +52,70 @@
|
||||
]
|
||||
},
|
||||
"require-dev": {
|
||||
"illuminate/console": "^8.47.0",
|
||||
"illuminate/support": "^8.47.0",
|
||||
"laravel/dusk": "^6.15.0",
|
||||
"pestphp/pest-dev-tools": "dev-master",
|
||||
"pestphp/pest-plugin-parallel": "^1.0"
|
||||
"pestphp/pest-dev-tools": "^3.0.0",
|
||||
"pestphp/pest-plugin-type-coverage": "^3.0.0",
|
||||
"symfony/process": "^7.1.3"
|
||||
},
|
||||
"minimum-stability": "dev",
|
||||
"prefer-stable": true,
|
||||
"config": {
|
||||
"sort-packages": true,
|
||||
"preferred-install": "dist"
|
||||
"preferred-install": "dist",
|
||||
"allow-plugins": {
|
||||
"pestphp/pest-plugin": true
|
||||
}
|
||||
},
|
||||
"bin": [
|
||||
"bin/pest"
|
||||
],
|
||||
"scripts": {
|
||||
"lint": "php-cs-fixer fix -v",
|
||||
"test:lint": "php-cs-fixer fix -v --dry-run",
|
||||
"test:types": "phpstan analyse --ansi --memory-limit=-1",
|
||||
"test:unit": "php bin/pest --colors=always --exclude-group=integration",
|
||||
"test:parallel": "php bin/pest -p --colors=always --exclude-group=integration",
|
||||
"test:integration": "php bin/pest --colors=always --group=integration",
|
||||
"update:snapshots": "REBUILD_SNAPSHOTS=true php bin/pest --colors=always",
|
||||
"refacto": "rector",
|
||||
"lint": "pint",
|
||||
"test:refacto": "rector --dry-run",
|
||||
"test:lint": "pint --test",
|
||||
"test:type:check": "phpstan analyse --ansi --memory-limit=-1 --debug",
|
||||
"test:type:coverage": "php -d memory_limit=-1 bin/pest --type-coverage --min=100",
|
||||
"test:unit": "php bin/pest --colors=always --exclude-group=integration --compact",
|
||||
"test:inline": "php bin/pest --colors=always --configuration=phpunit.inline.xml",
|
||||
"test:parallel": "php bin/pest --colors=always --exclude-group=integration --parallel --processes=3",
|
||||
"test:integration": "php bin/pest --colors=always --group=integration -v",
|
||||
"update:snapshots": "REBUILD_SNAPSHOTS=true php bin/pest --colors=always --update-snapshots",
|
||||
"test": [
|
||||
"@test:refacto",
|
||||
"@test:lint",
|
||||
"@test:types",
|
||||
"@test:type:check",
|
||||
"@test:type:coverage",
|
||||
"@test:unit",
|
||||
"@test:parallel",
|
||||
"@test:integration"
|
||||
]
|
||||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.x-dev"
|
||||
},
|
||||
"pest": {
|
||||
"plugins": [
|
||||
"Pest\\Mutate\\Plugins\\Mutate",
|
||||
"Pest\\Plugins\\Configuration",
|
||||
"Pest\\Plugins\\Bail",
|
||||
"Pest\\Plugins\\Cache",
|
||||
"Pest\\Plugins\\Coverage",
|
||||
"Pest\\Plugins\\Init",
|
||||
"Pest\\Plugins\\Version"
|
||||
"Pest\\Plugins\\Environment",
|
||||
"Pest\\Plugins\\Help",
|
||||
"Pest\\Plugins\\Memory",
|
||||
"Pest\\Plugins\\Only",
|
||||
"Pest\\Plugins\\Printer",
|
||||
"Pest\\Plugins\\ProcessIsolation",
|
||||
"Pest\\Plugins\\Profile",
|
||||
"Pest\\Plugins\\Retry",
|
||||
"Pest\\Plugins\\Snapshot",
|
||||
"Pest\\Plugins\\Verbose",
|
||||
"Pest\\Plugins\\Version",
|
||||
"Pest\\Plugins\\Parallel"
|
||||
]
|
||||
},
|
||||
"laravel": {
|
||||
"providers": [
|
||||
"Pest\\Laravel\\PestServiceProvider"
|
||||
"phpstan": {
|
||||
"includes": [
|
||||
"extension.neon"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
14
docker-compose.yml
Normal file
14
docker-compose.yml
Normal file
@ -0,0 +1,14 @@
|
||||
version: "3.8"
|
||||
|
||||
services:
|
||||
php:
|
||||
build:
|
||||
context: ./docker
|
||||
volumes:
|
||||
- .:/var/www/html
|
||||
composer:
|
||||
build:
|
||||
context: ./docker
|
||||
volumes:
|
||||
- .:/var/www/html
|
||||
entrypoint: ["composer"]
|
||||
16
docker/Dockerfile
Normal file
16
docker/Dockerfile
Normal file
@ -0,0 +1,16 @@
|
||||
ARG PHP=8.1
|
||||
FROM php:${PHP}-cli-alpine
|
||||
|
||||
RUN apk update && apk add \
|
||||
zip libzip-dev icu-dev git
|
||||
|
||||
RUN docker-php-ext-install zip intl
|
||||
|
||||
RUN apk add --no-cache linux-headers autoconf build-base
|
||||
RUN pecl install xdebug
|
||||
RUN docker-php-ext-enable xdebug
|
||||
COPY --from=composer:2 /usr/bin/composer /usr/bin/composer
|
||||
|
||||
WORKDIR /var/www/html
|
||||
|
||||
ENTRYPOINT ["php"]
|
||||
4
extension.neon
Normal file
4
extension.neon
Normal file
@ -0,0 +1,4 @@
|
||||
parameters:
|
||||
universalObjectCratesClasses:
|
||||
- Pest\Support\HigherOrderTapProxy
|
||||
- Pest\Expectation
|
||||
88
overrides/Event/Value/ThrowableBuilder.php
Normal file
88
overrides/Event/Value/ThrowableBuilder.php
Normal file
@ -0,0 +1,88 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* BSD 3-Clause License
|
||||
*
|
||||
* Copyright (c) 2001-2023, Sebastian Bergmann
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of PHPUnit.
|
||||
*
|
||||
* (c) Sebastian Bergmann <sebastian@phpunit.de>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace PHPUnit\Event\Code;
|
||||
|
||||
use NunoMaduro\Collision\Contracts\RenderableOnCollisionEditor;
|
||||
use PHPUnit\Event\NoPreviousThrowableException;
|
||||
use PHPUnit\Framework\Exception;
|
||||
use PHPUnit\Util\Filter;
|
||||
use PHPUnit\Util\ThrowableToStringMapper;
|
||||
|
||||
/**
|
||||
* @internal This class is not covered by the backward compatibility promise for PHPUnit
|
||||
*/
|
||||
final readonly class ThrowableBuilder
|
||||
{
|
||||
/**
|
||||
* @throws Exception
|
||||
* @throws NoPreviousThrowableException
|
||||
*/
|
||||
public static function from(\Throwable $t): Throwable
|
||||
{
|
||||
$previous = $t->getPrevious();
|
||||
|
||||
if ($previous !== null) {
|
||||
$previous = self::from($previous);
|
||||
}
|
||||
|
||||
$trace = Filter::getFilteredStacktrace($t);
|
||||
|
||||
if ($t instanceof RenderableOnCollisionEditor && $frame = $t->toCollisionEditor()) {
|
||||
$file = $frame->getFile();
|
||||
$line = $frame->getLine();
|
||||
|
||||
$trace = "$file:$line\n$trace";
|
||||
}
|
||||
|
||||
return new Throwable(
|
||||
$t::class,
|
||||
$t->getMessage(),
|
||||
ThrowableToStringMapper::map($t),
|
||||
$trace,
|
||||
$previous
|
||||
);
|
||||
}
|
||||
}
|
||||
458
overrides/Logging/JUnit/JunitXmlLogger.php
Normal file
458
overrides/Logging/JUnit/JunitXmlLogger.php
Normal file
@ -0,0 +1,458 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/*
|
||||
* This file is part of PHPUnit.
|
||||
*
|
||||
* (c) Sebastian Bergmann <sebastian@phpunit.de>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace PHPUnit\Logging\JUnit;
|
||||
|
||||
use DOMDocument;
|
||||
use DOMElement;
|
||||
use PHPUnit\Event\Code\Test;
|
||||
use PHPUnit\Event\Code\TestMethod;
|
||||
use PHPUnit\Event\EventFacadeIsSealedException;
|
||||
use PHPUnit\Event\Facade;
|
||||
use PHPUnit\Event\InvalidArgumentException;
|
||||
use PHPUnit\Event\Telemetry\HRTime;
|
||||
use PHPUnit\Event\Telemetry\Info;
|
||||
use PHPUnit\Event\Test\Errored;
|
||||
use PHPUnit\Event\Test\Failed;
|
||||
use PHPUnit\Event\Test\Finished;
|
||||
use PHPUnit\Event\Test\MarkedIncomplete;
|
||||
use PHPUnit\Event\Test\PreparationStarted;
|
||||
use PHPUnit\Event\Test\Prepared;
|
||||
use PHPUnit\Event\Test\Skipped;
|
||||
use PHPUnit\Event\TestSuite\Started;
|
||||
use PHPUnit\Event\UnknownSubscriberTypeException;
|
||||
use PHPUnit\TextUI\Output\Printer;
|
||||
use PHPUnit\Util\Xml;
|
||||
|
||||
use function assert;
|
||||
use function basename;
|
||||
use function is_int;
|
||||
use function sprintf;
|
||||
use function str_replace;
|
||||
use function trim;
|
||||
|
||||
/**
|
||||
* @internal This class is not covered by the backward compatibility promise for PHPUnit
|
||||
*/
|
||||
final class JunitXmlLogger
|
||||
{
|
||||
private readonly Printer $printer;
|
||||
|
||||
private readonly \Pest\Logging\Converter $converter; // pest-added
|
||||
|
||||
private DOMDocument $document;
|
||||
|
||||
private DOMElement $root;
|
||||
|
||||
/**
|
||||
* @var DOMElement[]
|
||||
*/
|
||||
private array $testSuites = [];
|
||||
|
||||
/**
|
||||
* @psalm-var array<int,int>
|
||||
*/
|
||||
private array $testSuiteTests = [0];
|
||||
|
||||
/**
|
||||
* @psalm-var array<int,int>
|
||||
*/
|
||||
private array $testSuiteAssertions = [0];
|
||||
|
||||
/**
|
||||
* @psalm-var array<int,int>
|
||||
*/
|
||||
private array $testSuiteErrors = [0];
|
||||
|
||||
/**
|
||||
* @psalm-var array<int,int>
|
||||
*/
|
||||
private array $testSuiteFailures = [0];
|
||||
|
||||
/**
|
||||
* @psalm-var array<int,int>
|
||||
*/
|
||||
private array $testSuiteSkipped = [0];
|
||||
|
||||
/**
|
||||
* @psalm-var array<int,int>
|
||||
*/
|
||||
private array $testSuiteTimes = [0];
|
||||
|
||||
private int $testSuiteLevel = 0;
|
||||
|
||||
private ?DOMElement $currentTestCase = null;
|
||||
|
||||
private ?HRTime $time = null;
|
||||
|
||||
private bool $prepared = false;
|
||||
|
||||
private bool $preparationFailed = false;
|
||||
|
||||
/**
|
||||
* @throws EventFacadeIsSealedException
|
||||
* @throws UnknownSubscriberTypeException
|
||||
*/
|
||||
public function __construct(Printer $printer, Facade $facade)
|
||||
{
|
||||
$this->printer = $printer;
|
||||
$this->converter = new \Pest\Logging\Converter(\Pest\Support\Container::getInstance()->get(\Pest\TestSuite::class)->rootPath); // pest-added
|
||||
|
||||
$this->registerSubscribers($facade);
|
||||
$this->createDocument();
|
||||
}
|
||||
|
||||
public function flush(): void
|
||||
{
|
||||
$this->printer->print($this->document->saveXML());
|
||||
|
||||
$this->printer->flush();
|
||||
}
|
||||
|
||||
public function testSuiteStarted(Started $event): void
|
||||
{
|
||||
$testSuite = $this->document->createElement('testsuite');
|
||||
$testSuite->setAttribute('name', $this->converter->getTestSuiteName($event->testSuite())); // pest-changed
|
||||
|
||||
if ($event->testSuite()->isForTestClass()) {
|
||||
$testSuite->setAttribute('file', $this->converter->getTestSuiteLocation($event->testSuite()) ?? ''); // pest-changed
|
||||
}
|
||||
|
||||
if ($this->testSuiteLevel > 0) {
|
||||
$this->testSuites[$this->testSuiteLevel]->appendChild($testSuite);
|
||||
} else {
|
||||
$this->root->appendChild($testSuite);
|
||||
}
|
||||
|
||||
$this->testSuiteLevel++;
|
||||
$this->testSuites[$this->testSuiteLevel] = $testSuite;
|
||||
$this->testSuiteTests[$this->testSuiteLevel] = 0;
|
||||
$this->testSuiteAssertions[$this->testSuiteLevel] = 0;
|
||||
$this->testSuiteErrors[$this->testSuiteLevel] = 0;
|
||||
$this->testSuiteFailures[$this->testSuiteLevel] = 0;
|
||||
$this->testSuiteSkipped[$this->testSuiteLevel] = 0;
|
||||
$this->testSuiteTimes[$this->testSuiteLevel] = 0;
|
||||
}
|
||||
|
||||
public function testSuiteFinished(): void
|
||||
{
|
||||
$this->testSuites[$this->testSuiteLevel]->setAttribute(
|
||||
'tests',
|
||||
(string) $this->testSuiteTests[$this->testSuiteLevel],
|
||||
);
|
||||
|
||||
$this->testSuites[$this->testSuiteLevel]->setAttribute(
|
||||
'assertions',
|
||||
(string) $this->testSuiteAssertions[$this->testSuiteLevel],
|
||||
);
|
||||
|
||||
$this->testSuites[$this->testSuiteLevel]->setAttribute(
|
||||
'errors',
|
||||
(string) $this->testSuiteErrors[$this->testSuiteLevel],
|
||||
);
|
||||
|
||||
$this->testSuites[$this->testSuiteLevel]->setAttribute(
|
||||
'failures',
|
||||
(string) $this->testSuiteFailures[$this->testSuiteLevel],
|
||||
);
|
||||
|
||||
$this->testSuites[$this->testSuiteLevel]->setAttribute(
|
||||
'skipped',
|
||||
(string) $this->testSuiteSkipped[$this->testSuiteLevel],
|
||||
);
|
||||
|
||||
$this->testSuites[$this->testSuiteLevel]->setAttribute(
|
||||
'time',
|
||||
sprintf('%F', $this->testSuiteTimes[$this->testSuiteLevel]),
|
||||
);
|
||||
|
||||
if ($this->testSuiteLevel > 1) {
|
||||
$this->testSuiteTests[$this->testSuiteLevel - 1] += $this->testSuiteTests[$this->testSuiteLevel];
|
||||
$this->testSuiteAssertions[$this->testSuiteLevel - 1] += $this->testSuiteAssertions[$this->testSuiteLevel];
|
||||
$this->testSuiteErrors[$this->testSuiteLevel - 1] += $this->testSuiteErrors[$this->testSuiteLevel];
|
||||
$this->testSuiteFailures[$this->testSuiteLevel - 1] += $this->testSuiteFailures[$this->testSuiteLevel];
|
||||
$this->testSuiteSkipped[$this->testSuiteLevel - 1] += $this->testSuiteSkipped[$this->testSuiteLevel];
|
||||
$this->testSuiteTimes[$this->testSuiteLevel - 1] += $this->testSuiteTimes[$this->testSuiteLevel];
|
||||
}
|
||||
|
||||
$this->testSuiteLevel--;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function testPreparationStarted(PreparationStarted $event): void
|
||||
{
|
||||
$this->createTestCase($event);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function testPreparationFailed(): void
|
||||
{
|
||||
$this->preparationFailed = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function testPrepared(): void
|
||||
{
|
||||
$this->prepared = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function testFinished(Finished $event): void
|
||||
{
|
||||
if ($this->preparationFailed) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->handleFinish($event->telemetryInfo(), $event->numberOfAssertionsPerformed());
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function testMarkedIncomplete(MarkedIncomplete $event): void
|
||||
{
|
||||
$this->handleIncompleteOrSkipped($event);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function testSkipped(Skipped $event): void
|
||||
{
|
||||
$this->handleIncompleteOrSkipped($event);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function testErrored(Errored $event): void
|
||||
{
|
||||
$this->handleFault($event, 'error');
|
||||
|
||||
$this->testSuiteErrors[$this->testSuiteLevel]++;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function testFailed(Failed $event): void
|
||||
{
|
||||
$this->handleFault($event, 'failure');
|
||||
|
||||
$this->testSuiteFailures[$this->testSuiteLevel]++;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
private function handleFinish(Info $telemetryInfo, int $numberOfAssertionsPerformed): void
|
||||
{
|
||||
assert($this->currentTestCase !== null);
|
||||
assert($this->time !== null);
|
||||
|
||||
$time = $telemetryInfo->time()->duration($this->time)->asFloat();
|
||||
|
||||
$this->testSuiteAssertions[$this->testSuiteLevel] += $numberOfAssertionsPerformed;
|
||||
|
||||
$this->currentTestCase->setAttribute(
|
||||
'assertions',
|
||||
(string) $numberOfAssertionsPerformed,
|
||||
);
|
||||
|
||||
$this->currentTestCase->setAttribute(
|
||||
'time',
|
||||
sprintf('%F', $time),
|
||||
);
|
||||
|
||||
$this->testSuites[$this->testSuiteLevel]->appendChild(
|
||||
$this->currentTestCase,
|
||||
);
|
||||
|
||||
$this->testSuiteTests[$this->testSuiteLevel]++;
|
||||
$this->testSuiteTimes[$this->testSuiteLevel] += $time;
|
||||
|
||||
$this->currentTestCase = null;
|
||||
$this->time = null;
|
||||
$this->prepared = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws EventFacadeIsSealedException
|
||||
* @throws UnknownSubscriberTypeException
|
||||
*/
|
||||
private function registerSubscribers(Facade $facade): void
|
||||
{
|
||||
$facade->registerSubscribers(
|
||||
new TestSuiteStartedSubscriber($this),
|
||||
new TestSuiteFinishedSubscriber($this),
|
||||
new TestPreparationStartedSubscriber($this),
|
||||
new TestPreparationFailedSubscriber($this),
|
||||
new TestPreparedSubscriber($this),
|
||||
new TestFinishedSubscriber($this),
|
||||
new TestErroredSubscriber($this),
|
||||
new TestFailedSubscriber($this),
|
||||
new TestSkippedSubscriber($this),
|
||||
new TestRunnerExecutionFinishedSubscriber($this),
|
||||
);
|
||||
}
|
||||
|
||||
private function createDocument(): void
|
||||
{
|
||||
$this->document = new DOMDocument('1.0', 'UTF-8');
|
||||
$this->document->formatOutput = true;
|
||||
|
||||
$this->root = $this->document->createElement('testsuites');
|
||||
$this->document->appendChild($this->root);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
private function handleFault(Errored|Failed $event, string $type): void
|
||||
{
|
||||
if (! $this->prepared) {
|
||||
$this->createTestCase($event);
|
||||
}
|
||||
|
||||
assert($this->currentTestCase !== null);
|
||||
|
||||
$buffer = $this->converter->getTestCaseMethodName($event->test()); // pest-changed
|
||||
|
||||
$throwable = $event->throwable();
|
||||
$buffer .= trim(
|
||||
$this->converter->getExceptionMessage($throwable).PHP_EOL. // pest-changed
|
||||
$this->converter->getExceptionDetails($throwable), // pest-changed
|
||||
);
|
||||
|
||||
$fault = $this->document->createElement(
|
||||
$type,
|
||||
Xml::prepareString($buffer),
|
||||
);
|
||||
|
||||
$fault->setAttribute('type', $throwable->className());
|
||||
|
||||
$this->currentTestCase->appendChild($fault);
|
||||
|
||||
if (! $this->prepared) {
|
||||
$this->handleFinish($event->telemetryInfo(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
private function handleIncompleteOrSkipped(MarkedIncomplete|Skipped $event): void
|
||||
{
|
||||
if (! $this->prepared) {
|
||||
$this->createTestCase($event);
|
||||
}
|
||||
|
||||
assert($this->currentTestCase !== null);
|
||||
|
||||
$skipped = $this->document->createElement('skipped');
|
||||
|
||||
$this->currentTestCase->appendChild($skipped);
|
||||
|
||||
$this->testSuiteSkipped[$this->testSuiteLevel]++;
|
||||
|
||||
if (! $this->prepared) {
|
||||
$this->handleFinish($event->telemetryInfo(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
private function testAsString(Test $test): string
|
||||
{
|
||||
if ($test->isPhpt()) {
|
||||
return basename($test->file());
|
||||
}
|
||||
|
||||
assert($test instanceof TestMethod);
|
||||
|
||||
return sprintf(
|
||||
'%s::%s%s',
|
||||
$test->className(),
|
||||
$this->name($test),
|
||||
PHP_EOL,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
private function name(Test $test): string
|
||||
{
|
||||
if ($test->isPhpt()) {
|
||||
return basename($test->file());
|
||||
}
|
||||
|
||||
assert($test instanceof TestMethod);
|
||||
|
||||
if (! $test->testData()->hasDataFromDataProvider()) {
|
||||
return $test->methodName();
|
||||
}
|
||||
|
||||
$dataSetName = $test->testData()->dataFromDataProvider()->dataSetName();
|
||||
|
||||
if (is_int($dataSetName)) {
|
||||
return sprintf(
|
||||
'%s with data set #%d',
|
||||
$test->methodName(),
|
||||
$dataSetName,
|
||||
);
|
||||
}
|
||||
|
||||
return sprintf(
|
||||
'%s with data set "%s"',
|
||||
$test->methodName(),
|
||||
$dataSetName,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidArgumentException
|
||||
*
|
||||
* @psalm-assert !null $this->currentTestCase
|
||||
*/
|
||||
private function createTestCase(Errored|Failed|MarkedIncomplete|PreparationStarted|Prepared|Skipped $event): void
|
||||
{
|
||||
$testCase = $this->document->createElement('testcase');
|
||||
|
||||
$test = $event->test();
|
||||
$file = $this->converter->getTestCaseLocation($test); // pest-added
|
||||
|
||||
$testCase->setAttribute('name', $this->converter->getTestCaseMethodName($test)); // pest-changed
|
||||
$testCase->setAttribute('file', $file); // pest-changed
|
||||
|
||||
if ($test->isTestMethod()) {
|
||||
assert($test instanceof TestMethod);
|
||||
|
||||
//$testCase->setAttribute('line', (string) $test->line()); // pest-removed
|
||||
$className = $this->converter->getTrimmedTestClassName($test); // pest-added
|
||||
$testCase->setAttribute('class', $className); // pest-changed
|
||||
$testCase->setAttribute('classname', str_replace('\\', '.', $className)); // pest-changed
|
||||
}
|
||||
|
||||
$this->currentTestCase = $testCase;
|
||||
$this->time = $event->telemetryInfo()->time();
|
||||
}
|
||||
}
|
||||
178
overrides/Runner/Filter/NameFilterIterator.php
Normal file
178
overrides/Runner/Filter/NameFilterIterator.php
Normal file
@ -0,0 +1,178 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* BSD 3-Clause License
|
||||
*
|
||||
* Copyright (c) 2001-2023, Sebastian Bergmann
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of PHPUnit.
|
||||
*
|
||||
* (c) Sebastian Bergmann <sebastian@phpunit.de>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace PHPUnit\Runner\Filter;
|
||||
|
||||
use Pest\Contracts\HasPrintableTestCaseName;
|
||||
use PHPUnit\Framework\Test;
|
||||
use PHPUnit\Framework\TestSuite;
|
||||
use PHPUnit\Runner\PhptTestCase;
|
||||
use RecursiveFilterIterator;
|
||||
use RecursiveIterator;
|
||||
|
||||
use function end;
|
||||
use function preg_match;
|
||||
use function sprintf;
|
||||
use function str_replace;
|
||||
|
||||
/**
|
||||
* @internal This class is not covered by the backward compatibility promise for PHPUnit
|
||||
*/
|
||||
abstract class NameFilterIterator extends RecursiveFilterIterator
|
||||
{
|
||||
/**
|
||||
* @psalm-var non-empty-string
|
||||
*/
|
||||
private readonly string $regularExpression;
|
||||
|
||||
private readonly ?int $dataSetMinimum;
|
||||
|
||||
private readonly ?int $dataSetMaximum;
|
||||
|
||||
/**
|
||||
* @psalm-param RecursiveIterator<int, Test> $iterator
|
||||
* @psalm-param non-empty-string $filter
|
||||
*/
|
||||
public function __construct(RecursiveIterator $iterator, string $filter)
|
||||
{
|
||||
parent::__construct($iterator);
|
||||
|
||||
$preparedFilter = $this->prepareFilter($filter);
|
||||
|
||||
$this->regularExpression = $preparedFilter['regularExpression'];
|
||||
$this->dataSetMinimum = $preparedFilter['dataSetMinimum'];
|
||||
$this->dataSetMaximum = $preparedFilter['dataSetMaximum'];
|
||||
}
|
||||
|
||||
public function accept(): bool
|
||||
{
|
||||
$test = $this->getInnerIterator()->current();
|
||||
|
||||
if ($test instanceof TestSuite) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($test instanceof PhptTestCase) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($test instanceof HasPrintableTestCaseName) {
|
||||
$name = $test::getPrintableTestCaseName().'::'.$test->getPrintableTestCaseMethodName();
|
||||
} else {
|
||||
$name = $test::class.'::'.$test->nameWithDataSet();
|
||||
}
|
||||
|
||||
$accepted = @preg_match($this->regularExpression, $name, $matches) === 1;
|
||||
|
||||
if ($accepted && isset($this->dataSetMaximum)) {
|
||||
$set = end($matches);
|
||||
$accepted = $set >= $this->dataSetMinimum && $set <= $this->dataSetMaximum;
|
||||
}
|
||||
|
||||
return $this->doAccept($accepted);
|
||||
}
|
||||
|
||||
abstract protected function doAccept(bool $result): bool;
|
||||
|
||||
/**
|
||||
* @psalm-param non-empty-string $filter
|
||||
*
|
||||
* @psalm-return array{regularExpression: non-empty-string, dataSetMinimum: ?int, dataSetMaximum: ?int}
|
||||
*/
|
||||
private function prepareFilter(string $filter): array
|
||||
{
|
||||
$dataSetMinimum = null;
|
||||
$dataSetMaximum = null;
|
||||
|
||||
if (@preg_match($filter, '') === false) {
|
||||
// Handles:
|
||||
// * testAssertEqualsSucceeds#4
|
||||
// * testAssertEqualsSucceeds#4-8
|
||||
if (preg_match('/^(.*?)#(\d+)(?:-(\d+))?$/', $filter, $matches)) {
|
||||
if (isset($matches[3]) && $matches[2] < $matches[3]) {
|
||||
$filter = sprintf(
|
||||
'%s.*with data set #(\d+)$',
|
||||
$matches[1],
|
||||
);
|
||||
|
||||
$dataSetMinimum = (int) $matches[2];
|
||||
$dataSetMaximum = (int) $matches[3];
|
||||
} else {
|
||||
$filter = sprintf(
|
||||
'%s.*with data set #%s$',
|
||||
$matches[1],
|
||||
$matches[2],
|
||||
);
|
||||
}
|
||||
} // Handles:
|
||||
// * testDetermineJsonError@JSON_ERROR_NONE
|
||||
// * testDetermineJsonError@JSON.*
|
||||
elseif (preg_match('/^(.*?)@(.+)$/', $filter, $matches)) {
|
||||
$filter = sprintf(
|
||||
'%s.*with data set "%s"$',
|
||||
$matches[1],
|
||||
$matches[2],
|
||||
);
|
||||
}
|
||||
|
||||
// Escape delimiters in regular expression. Do NOT use preg_quote,
|
||||
// to keep magic characters.
|
||||
$filter = sprintf(
|
||||
'/%s/i',
|
||||
str_replace(
|
||||
'/',
|
||||
'\\/',
|
||||
$filter,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return [
|
||||
'regularExpression' => $filter,
|
||||
'dataSetMinimum' => $dataSetMinimum,
|
||||
'dataSetMaximum' => $dataSetMaximum,
|
||||
];
|
||||
}
|
||||
}
|
||||
195
overrides/Runner/ResultCache/DefaultResultCache.php
Normal file
195
overrides/Runner/ResultCache/DefaultResultCache.php
Normal file
@ -0,0 +1,195 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* BSD 3-Clause License
|
||||
*
|
||||
* Copyright (c) 2001-2023, Sebastian Bergmann
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of PHPUnit.
|
||||
*
|
||||
* (c) Sebastian Bergmann <sebastian@phpunit.de>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace PHPUnit\Runner\ResultCache;
|
||||
|
||||
use const DIRECTORY_SEPARATOR;
|
||||
|
||||
use PHPUnit\Framework\TestStatus\TestStatus;
|
||||
use PHPUnit\Runner\DirectoryCannotBeCreatedException;
|
||||
use PHPUnit\Runner\Exception;
|
||||
use PHPUnit\Util\Filesystem;
|
||||
|
||||
use function array_keys;
|
||||
use function assert;
|
||||
use function dirname;
|
||||
use function file_get_contents;
|
||||
use function file_put_contents;
|
||||
use function is_array;
|
||||
use function is_dir;
|
||||
use function is_file;
|
||||
use function json_decode;
|
||||
use function json_encode;
|
||||
use function Pest\version;
|
||||
|
||||
/**
|
||||
* @internal This class is not covered by the backward compatibility promise for PHPUnit
|
||||
*/
|
||||
final class DefaultResultCache implements ResultCache
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private const DEFAULT_RESULT_CACHE_FILENAME = '.phpunit.result.cache';
|
||||
|
||||
private readonly string $cacheFilename;
|
||||
|
||||
/**
|
||||
* @psalm-var array<string, TestStatus>
|
||||
*/
|
||||
private array $defects = [];
|
||||
|
||||
/**
|
||||
* @psalm-var array<string, float>
|
||||
*/
|
||||
private array $times = [];
|
||||
|
||||
public function __construct(?string $filepath = null)
|
||||
{
|
||||
if ($filepath !== null && is_dir($filepath)) {
|
||||
$filepath .= DIRECTORY_SEPARATOR.self::DEFAULT_RESULT_CACHE_FILENAME;
|
||||
}
|
||||
|
||||
$this->cacheFilename = $filepath ?? $_ENV['PHPUNIT_RESULT_CACHE'] ?? self::DEFAULT_RESULT_CACHE_FILENAME;
|
||||
}
|
||||
|
||||
public function setStatus(string $id, TestStatus $status): void
|
||||
{
|
||||
if ($status->isSuccess()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->defects[$id] = $status;
|
||||
}
|
||||
|
||||
public function status(string $id): TestStatus
|
||||
{
|
||||
return $this->defects[$id] ?? TestStatus::unknown();
|
||||
}
|
||||
|
||||
public function setTime(string $id, float $time): void
|
||||
{
|
||||
$this->times[$id] = $time;
|
||||
}
|
||||
|
||||
public function time(string $id): float
|
||||
{
|
||||
return $this->times[$id] ?? 0.0;
|
||||
}
|
||||
|
||||
public function load(): void
|
||||
{
|
||||
if (! is_file($this->cacheFilename)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$contents = file_get_contents($this->cacheFilename);
|
||||
|
||||
if ($contents === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$data = json_decode(
|
||||
$contents,
|
||||
true,
|
||||
);
|
||||
|
||||
if ($data === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (! isset($data['version'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($data['version'] !== $this->cacheVersion()) {
|
||||
return;
|
||||
}
|
||||
|
||||
assert(isset($data['defects']) && is_array($data['defects']));
|
||||
assert(isset($data['times']) && is_array($data['times']));
|
||||
|
||||
foreach (array_keys($data['defects']) as $test) {
|
||||
$data['defects'][$test] = TestStatus::from($data['defects'][$test]);
|
||||
}
|
||||
|
||||
$this->defects = $data['defects'];
|
||||
$this->times = $data['times'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
*/
|
||||
public function persist(): void
|
||||
{
|
||||
if (! Filesystem::createDirectory(dirname($this->cacheFilename))) {
|
||||
throw new DirectoryCannotBeCreatedException($this->cacheFilename);
|
||||
}
|
||||
|
||||
$data = [
|
||||
'version' => $this->cacheVersion(),
|
||||
'defects' => [],
|
||||
'times' => $this->times,
|
||||
];
|
||||
|
||||
foreach ($this->defects as $test => $status) {
|
||||
$data['defects'][$test] = $status->asInt();
|
||||
}
|
||||
|
||||
file_put_contents(
|
||||
$this->cacheFilename,
|
||||
json_encode($data),
|
||||
LOCK_EX,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the cache version.
|
||||
*/
|
||||
private function cacheVersion(): string
|
||||
{
|
||||
return 'pest_'.version();
|
||||
}
|
||||
}
|
||||
227
overrides/Runner/TestSuiteLoader.php
Normal file
227
overrides/Runner/TestSuiteLoader.php
Normal file
@ -0,0 +1,227 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* BSD 3-Clause License
|
||||
*
|
||||
* Copyright (c) 2001-2023, Sebastian Bergmann
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PHPUnit\Runner;
|
||||
|
||||
use Exception;
|
||||
use Pest\Contracts\HasPrintableTestCaseName;
|
||||
use Pest\Panic;
|
||||
use Pest\TestCases\IgnorableTestCase;
|
||||
use Pest\TestSuite;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use ReflectionClass;
|
||||
use ReflectionException;
|
||||
use Throwable;
|
||||
|
||||
use function array_diff;
|
||||
use function array_values;
|
||||
use function basename;
|
||||
use function class_exists;
|
||||
use function get_declared_classes;
|
||||
use function substr;
|
||||
|
||||
/**
|
||||
* @internal This class is not covered by the backward compatibility promise for PHPUnit
|
||||
*/
|
||||
final class TestSuiteLoader
|
||||
{
|
||||
/**
|
||||
* @psalm-var list<class-string>
|
||||
*/
|
||||
private static array $loadedClasses = [];
|
||||
|
||||
/**
|
||||
* @psalm-var array<string, array<class-string>>
|
||||
*/
|
||||
private static array $loadedClassesByFilename = [];
|
||||
|
||||
/**
|
||||
* @psalm-var list<class-string>
|
||||
*/
|
||||
private static array $declaredClasses = [];
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
if (empty(self::$declaredClasses)) {
|
||||
self::$declaredClasses = get_declared_classes();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
*/
|
||||
public function load(string $suiteClassFile): ReflectionClass
|
||||
{
|
||||
$suiteClassName = $this->classNameFromFileName($suiteClassFile);
|
||||
|
||||
(static function () use ($suiteClassFile) {
|
||||
try {
|
||||
include_once $suiteClassFile;
|
||||
} catch (Throwable $e) {
|
||||
Panic::with($e);
|
||||
}
|
||||
|
||||
TestSuite::getInstance()->tests->makeIfNeeded($suiteClassFile);
|
||||
})();
|
||||
|
||||
$loadedClasses = array_values(
|
||||
array_diff(
|
||||
get_declared_classes(),
|
||||
array_merge(
|
||||
self::$declaredClasses,
|
||||
self::$loadedClasses
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
self::$loadedClasses = array_merge($loadedClasses, self::$loadedClasses);
|
||||
|
||||
foreach ($loadedClasses as $loadedClass) {
|
||||
$reflection = new ReflectionClass($loadedClass);
|
||||
$filename = $reflection->getFileName();
|
||||
self::$loadedClassesByFilename[$filename] = [
|
||||
$loadedClass,
|
||||
...self::$loadedClassesByFilename[$filename] ?? [],
|
||||
];
|
||||
}
|
||||
|
||||
$loadedClasses = array_merge(self::$loadedClassesByFilename[$suiteClassFile] ?? [], $loadedClasses);
|
||||
|
||||
if (empty($loadedClasses)) {
|
||||
return $this->exceptionFor($suiteClassName, $suiteClassFile);
|
||||
}
|
||||
|
||||
$testCaseFound = false;
|
||||
$class = false;
|
||||
|
||||
foreach (array_reverse($loadedClasses) as $loadedClass) {
|
||||
if (
|
||||
is_subclass_of($loadedClass, HasPrintableTestCaseName::class)
|
||||
|| is_subclass_of($loadedClass, TestCase::class)) {
|
||||
try {
|
||||
$class = new ReflectionClass($loadedClass);
|
||||
// @codeCoverageIgnoreStart
|
||||
} catch (ReflectionException) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($class->isAbstract() || ($suiteClassFile !== $class->getFileName())) {
|
||||
if (! str_contains($class->getFileName(), 'TestCaseFactory.php')) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
$suiteClassName = $loadedClass;
|
||||
$testCaseFound = true;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (! $testCaseFound) {
|
||||
foreach (array_reverse($loadedClasses) as $loadedClass) {
|
||||
$offset = 0 - strlen($suiteClassName);
|
||||
|
||||
if (stripos(substr($loadedClass, $offset - 1), '\\'.$suiteClassName) === 0 ||
|
||||
stripos(substr($loadedClass, $offset - 1), '_'.$suiteClassName) === 0) {
|
||||
try {
|
||||
$class = new ReflectionClass($loadedClass);
|
||||
// @codeCoverageIgnoreStart
|
||||
} catch (ReflectionException) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$suiteClassName = $loadedClass;
|
||||
$testCaseFound = true;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (! $testCaseFound) {
|
||||
return $this->exceptionFor($suiteClassName, $suiteClassFile);
|
||||
}
|
||||
|
||||
if (! class_exists($suiteClassName, false)) {
|
||||
return $this->exceptionFor($suiteClassName, $suiteClassFile);
|
||||
}
|
||||
|
||||
// @codeCoverageIgnoreEnd
|
||||
|
||||
if ($class->isSubclassOf(TestCase::class) && ! $class->isAbstract()) {
|
||||
return $class;
|
||||
}
|
||||
|
||||
if ($class->hasMethod('suite')) {
|
||||
try {
|
||||
$method = $class->getMethod('suite');
|
||||
// @codeCoverageIgnoreStart
|
||||
} catch (ReflectionException $e) {
|
||||
throw new Exception($e->getMessage(), (int) $e->getCode(), $e);
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
|
||||
if (! $method->isAbstract() && $method->isPublic() && $method->isStatic()) {
|
||||
return $class;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->exceptionFor($suiteClassName, $suiteClassFile);
|
||||
}
|
||||
|
||||
public function reload(ReflectionClass $aClass): ReflectionClass
|
||||
{
|
||||
return $aClass;
|
||||
}
|
||||
|
||||
private function classNameFromFileName(string $suiteClassFile): string
|
||||
{
|
||||
$className = basename($suiteClassFile, '.php');
|
||||
$dotPos = strpos($className, '.');
|
||||
|
||||
if ($dotPos !== false) {
|
||||
$className = substr($className, 0, $dotPos);
|
||||
}
|
||||
|
||||
return $className;
|
||||
}
|
||||
|
||||
private function exceptionFor(string $className, string $filename): ReflectionClass
|
||||
{
|
||||
return new ReflectionClass(IgnorableTestCase::class);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,106 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* BSD 3-Clause License
|
||||
*
|
||||
* Copyright (c) 2001-2023, Sebastian Bergmann
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of PHPUnit.
|
||||
*
|
||||
* (c) Sebastian Bergmann <sebastian@phpunit.de>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace PHPUnit\TextUI\Command;
|
||||
|
||||
use const PHP_EOL;
|
||||
|
||||
use PHPUnit\TextUI\Configuration\CodeCoverageFilterRegistry;
|
||||
use PHPUnit\TextUI\Configuration\Configuration;
|
||||
use PHPUnit\TextUI\Configuration\NoCoverageCacheDirectoryException;
|
||||
use SebastianBergmann\CodeCoverage\StaticAnalysis\CacheWarmer;
|
||||
use SebastianBergmann\Timer\NoActiveTimerException;
|
||||
use SebastianBergmann\Timer\Timer;
|
||||
|
||||
/**
|
||||
* @internal This class is not covered by the backward compatibility promise for PHPUnit
|
||||
*/
|
||||
final readonly class WarmCodeCoverageCacheCommand implements Command
|
||||
{
|
||||
private Configuration $configuration;
|
||||
|
||||
private CodeCoverageFilterRegistry $codeCoverageFilterRegistry;
|
||||
|
||||
public function __construct(Configuration $configuration, CodeCoverageFilterRegistry $codeCoverageFilterRegistry)
|
||||
{
|
||||
$this->configuration = $configuration;
|
||||
$this->codeCoverageFilterRegistry = $codeCoverageFilterRegistry;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws NoActiveTimerException
|
||||
* @throws NoCoverageCacheDirectoryException
|
||||
*/
|
||||
public function execute(): Result
|
||||
{
|
||||
if (! $this->configuration->hasCoverageCacheDirectory()) {
|
||||
return Result::from(
|
||||
'Cache for static analysis has not been configured'.PHP_EOL,
|
||||
Result::FAILURE,
|
||||
);
|
||||
}
|
||||
|
||||
$this->codeCoverageFilterRegistry->init($this->configuration, true);
|
||||
|
||||
if (! $this->codeCoverageFilterRegistry->configured()) {
|
||||
return Result::from(
|
||||
'Filter for code coverage has not been configured'.PHP_EOL,
|
||||
Result::FAILURE,
|
||||
);
|
||||
}
|
||||
|
||||
$timer = new Timer;
|
||||
$timer->start();
|
||||
|
||||
(new CacheWarmer)->warmCache(
|
||||
$this->configuration->coverageCacheDirectory(),
|
||||
! $this->configuration->disableCodeCoverageIgnore(),
|
||||
$this->configuration->ignoreDeprecatedCodeUnitsFromCodeCoverage(),
|
||||
$this->codeCoverageFilterRegistry->get(),
|
||||
);
|
||||
|
||||
return Result::from();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* BSD 3-Clause License
|
||||
*
|
||||
* Copyright (c) 2001-2023, Sebastian Bergmann
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of PHPUnit.
|
||||
*
|
||||
* (c) Sebastian Bergmann <sebastian@phpunit.de>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Pest\Logging\TeamCity\Subscriber;
|
||||
|
||||
use PHPUnit\Event\Test\Skipped;
|
||||
use PHPUnit\Event\Test\SkippedSubscriber;
|
||||
use ReflectionClass;
|
||||
|
||||
/**
|
||||
* @internal This class is not covered by the backward compatibility promise for PHPUnit
|
||||
*/
|
||||
final class TestSkippedSubscriber extends Subscriber implements SkippedSubscriber
|
||||
{
|
||||
public function notify(Skipped $event): void
|
||||
{
|
||||
if (str_contains($event->message(), '__TODO__')) {
|
||||
$this->printTodoItem();
|
||||
}
|
||||
|
||||
$this->logger()->testSkipped($event);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints a "T" to the standard PHPUnit output to indicate a todo item.
|
||||
*/
|
||||
private function printTodoItem(): void
|
||||
{
|
||||
$mirror = new ReflectionClass($this->printer());
|
||||
$printerMirror = $mirror->getMethod('printProgress');
|
||||
$printerMirror->invoke($this->printer(), 'T');
|
||||
}
|
||||
}
|
||||
129
overrides/TextUI/TestSuiteFilterProcessor.php
Normal file
129
overrides/TextUI/TestSuiteFilterProcessor.php
Normal file
@ -0,0 +1,129 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* BSD 3-Clause License
|
||||
*
|
||||
* Copyright (c) 2001-2023, Sebastian Bergmann
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
/*
|
||||
* This file is part of PHPUnit.
|
||||
*
|
||||
* (c) Sebastian Bergmann <sebastian@phpunit.de>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace PHPUnit\TextUI;
|
||||
|
||||
use Pest\Plugins\Only;
|
||||
use PHPUnit\Event;
|
||||
use PHPUnit\Framework\TestSuite;
|
||||
use PHPUnit\Runner\Filter\Factory;
|
||||
use PHPUnit\TextUI\Configuration\Configuration;
|
||||
use PHPUnit\TextUI\Configuration\FilterNotConfiguredException;
|
||||
|
||||
use function array_map;
|
||||
|
||||
/**
|
||||
* @internal This class is not covered by the backward compatibility promise for PHPUnit
|
||||
*/
|
||||
final readonly class TestSuiteFilterProcessor
|
||||
{
|
||||
/**
|
||||
* @throws Event\RuntimeException
|
||||
* @throws FilterNotConfiguredException
|
||||
*/
|
||||
public function process(Configuration $configuration, TestSuite $suite): void
|
||||
{
|
||||
$factory = new Factory;
|
||||
|
||||
if (! $configuration->hasFilter() &&
|
||||
! $configuration->hasGroups() &&
|
||||
! $configuration->hasExcludeGroups() &&
|
||||
! $configuration->hasExcludeFilter() &&
|
||||
! $configuration->hasTestsCovering() &&
|
||||
! $configuration->hasTestsUsing() &&
|
||||
! Only::isEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($configuration->hasExcludeGroups()) {
|
||||
$factory->addExcludeGroupFilter(
|
||||
$configuration->excludeGroups(),
|
||||
);
|
||||
}
|
||||
|
||||
if (Only::isEnabled()) {
|
||||
$factory->addIncludeGroupFilter([Only::group()]);
|
||||
} elseif ($configuration->hasGroups()) {
|
||||
$factory->addIncludeGroupFilter(
|
||||
$configuration->groups(),
|
||||
);
|
||||
}
|
||||
|
||||
if ($configuration->hasTestsCovering()) {
|
||||
$factory->addIncludeGroupFilter(
|
||||
array_map(
|
||||
static fn (string $name): string => '__phpunit_covers_'.$name,
|
||||
$configuration->testsCovering(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
if ($configuration->hasTestsUsing()) {
|
||||
$factory->addIncludeGroupFilter(
|
||||
array_map(
|
||||
static fn (string $name): string => '__phpunit_uses_'.$name,
|
||||
$configuration->testsUsing(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
if ($configuration->hasExcludeFilter()) {
|
||||
$factory->addExcludeNameFilter(
|
||||
$configuration->excludeFilter(),
|
||||
);
|
||||
}
|
||||
|
||||
if ($configuration->hasFilter()) {
|
||||
$factory->addIncludeNameFilter(
|
||||
$configuration->filter(),
|
||||
);
|
||||
}
|
||||
|
||||
$suite->injectFilter($factory);
|
||||
|
||||
Event\Facade::emitter()->testSuiteFiltered(
|
||||
Event\TestSuite\TestSuiteBuilder::from($suite),
|
||||
);
|
||||
}
|
||||
}
|
||||
21
phpstan.neon
21
phpstan.neon
@ -9,26 +9,15 @@ parameters:
|
||||
- src
|
||||
|
||||
checkMissingIterableValueType: true
|
||||
checkGenericClassInNonGenericObjectType: false
|
||||
reportUnmatchedIgnoredErrors: true
|
||||
|
||||
ignoreErrors:
|
||||
- "#type mixed is not subtype of native#"
|
||||
- "#has a nullable return type declaration.#"
|
||||
- "#Language construct isset\\(\\) should not be used.#"
|
||||
- "#is not allowed to extend#"
|
||||
- "#Language construct eval#"
|
||||
- "#is concrete, but does not have a Test suffix#"
|
||||
- "#with a nullable type declaration#"
|
||||
- "#type mixed is not subtype of native#"
|
||||
- "# with null as default value#"
|
||||
- "#has parameter \\$closure with default value.#"
|
||||
- "#has parameter \\$description with default value.#"
|
||||
- "#Method Pest\\\\Support\\\\Reflection::getParameterClassName\\(\\) has a nullable return type declaration.#"
|
||||
-
|
||||
message: '#Call to an undefined method PHPUnit\\Framework\\Test::getName\(\)#'
|
||||
path: src/Logging
|
||||
-
|
||||
message: '#invalid typehint type Pest\\Concerns\\Testable#'
|
||||
path: src/Logging
|
||||
-
|
||||
message: '#is not subtype of native type PHPUnit\\Framework\\Test#'
|
||||
path: src/Logging
|
||||
-
|
||||
message: '#Call to an undefined method PHPUnit\\Framework\\Test::getPrintableTestCaseName\(\)#'
|
||||
path: src/Logging
|
||||
|
||||
19
phpunit.xml
19
phpunit.xml
@ -1,16 +1,29 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:noNamespaceSchemaLocation="./vendor/phpunit/phpunit/phpunit.xsd"
|
||||
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.0/phpunit.xsd"
|
||||
backupGlobals="false"
|
||||
beStrictAboutTestsThatDoNotTestAnything="true"
|
||||
beStrictAboutOutputDuringTests="true"
|
||||
bootstrap="vendor/autoload.php"
|
||||
colors="true"
|
||||
failOnRisky="true"
|
||||
failOnWarning="false"
|
||||
processIsolation="false"
|
||||
stopOnError="false"
|
||||
stopOnFailure="false"
|
||||
backupStaticProperties="false"
|
||||
>
|
||||
<testsuites>
|
||||
<testsuite name="default">
|
||||
<directory suffix=".php">./tests</directory>
|
||||
<exclude>./tests/.snapshots</exclude>
|
||||
<exclude>./tests/.tests</exclude>
|
||||
<exclude>./tests/Fixtures/Inheritance</exclude>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
<coverage processUncoveredFiles="true">
|
||||
<source>
|
||||
<include>
|
||||
<directory suffix=".php">./src</directory>
|
||||
</include>
|
||||
</coverage>
|
||||
</source>
|
||||
</phpunit>
|
||||
|
||||
23
rector.php
Normal file
23
rector.php
Normal file
@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Rector\Config\RectorConfig;
|
||||
use Rector\TypeDeclaration\Rector\ClassMethod\ReturnNeverTypeRector;
|
||||
|
||||
return RectorConfig::configure()
|
||||
->withPaths([
|
||||
__DIR__.'/src',
|
||||
])
|
||||
->withSkip([
|
||||
__DIR__.'/src/Plugins/Parallel/Paratest/WrapperRunner.php',
|
||||
ReturnNeverTypeRector::class,
|
||||
])
|
||||
->withPreparedSets(
|
||||
deadCode: true,
|
||||
codeQuality: true,
|
||||
typeDeclarations: true,
|
||||
privatization: true,
|
||||
earlyReturn: true,
|
||||
)
|
||||
->withPhpSets();
|
||||
31
resources/base-phpunit.xml
Normal file
31
resources/base-phpunit.xml
Normal file
@ -0,0 +1,31 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
|
||||
bootstrap="vendor/autoload.php"
|
||||
colors="true"
|
||||
>
|
||||
<testsuites>
|
||||
<testsuite name="Default">
|
||||
<directory>tests/</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
<source>
|
||||
<include>
|
||||
<directory>app</directory>
|
||||
<directory>src</directory>
|
||||
</include>
|
||||
</source>
|
||||
<php>
|
||||
<env name="APP_ENV" value="testing"/>
|
||||
<env name="APP_MAINTENANCE_DRIVER" value="file"/>
|
||||
<env name="BCRYPT_ROUNDS" value="4"/>
|
||||
<env name="CACHE_STORE" value="array"/>
|
||||
<!-- <env name="DB_CONNECTION" value="sqlite"/> -->
|
||||
<!-- <env name="DB_DATABASE" value=":memory:"/> -->
|
||||
<env name="MAIL_MAILER" value="array"/>
|
||||
<env name="PULSE_ENABLED" value="false"/>
|
||||
<env name="QUEUE_CONNECTION" value="sync"/>
|
||||
<env name="SESSION_DRIVER" value="array"/>
|
||||
<env name="TELESCOPE_ENABLED" value="false"/>
|
||||
</php>
|
||||
</phpunit>
|
||||
17
resources/views/components/badge.php
Normal file
17
resources/views/components/badge.php
Normal file
@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
/** @var string $type */
|
||||
/** @var string $content */
|
||||
[$bgBadgeColor, $bgBadgeText] = match ($type) {
|
||||
'INFO' => ['blue', 'INFO'],
|
||||
'ERROR' => ['red', 'ERROR'],
|
||||
};
|
||||
|
||||
?>
|
||||
|
||||
<div class="my-1">
|
||||
<span class="ml-2 px-1 bg-<?php echo $bgBadgeColor ?> font-bold"><?php echo htmlspecialchars($bgBadgeText) ?></span>
|
||||
<span class="ml-1">
|
||||
<?php echo htmlspecialchars($content) ?>
|
||||
</span>
|
||||
</div>
|
||||
1
resources/views/components/new-line.php
Normal file
1
resources/views/components/new-line.php
Normal file
@ -0,0 +1 @@
|
||||
<div></div>
|
||||
12
resources/views/components/two-column-detail.php
Normal file
12
resources/views/components/two-column-detail.php
Normal file
@ -0,0 +1,12 @@
|
||||
<div class="flex mx-2 max-w-150">
|
||||
<span>
|
||||
<?php echo htmlspecialchars($left) ?>
|
||||
</span>
|
||||
<span class="flex-1 content-repeat-[.] text-gray ml-1"></span>
|
||||
<?php if ($right !== '') { ?>
|
||||
<span class="ml-1 text-gray">
|
||||
<?php echo htmlspecialchars($right) ?>
|
||||
</span>
|
||||
<?php } ?>
|
||||
</div>
|
||||
|
||||
4
resources/views/usage.php
Normal file
4
resources/views/usage.php
Normal file
@ -0,0 +1,4 @@
|
||||
<div class="mx-2">
|
||||
<span class="text-yellow font-bold">USAGE:</span><span class="ml-1">pest</span><span class="ml-1 text-gray"><?php echo htmlspecialchars('<file>') ?> [options]</span>
|
||||
</div>
|
||||
|
||||
3
resources/views/version.php
Normal file
3
resources/views/version.php
Normal file
@ -0,0 +1,3 @@
|
||||
<div class="my-1 mx-2">
|
||||
<span>Pest Testing Framework</span><span class="ml-1 text-blue font-bold"><?php echo htmlspecialchars($version) ?></span><span>.</span>
|
||||
</div>
|
||||
@ -1,46 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\Actions;
|
||||
|
||||
use NunoMaduro\Collision\Adapters\Phpunit\Printer;
|
||||
use Pest\Logging\JUnit;
|
||||
use Pest\Logging\TeamCity;
|
||||
use PHPUnit\TextUI\DefaultResultPrinter;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class AddsDefaults
|
||||
{
|
||||
private const PRINTER = 'printer';
|
||||
|
||||
/**
|
||||
* Adds default arguments to the given `arguments` array.
|
||||
*
|
||||
* @param array<string, mixed> $arguments
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public static function to(array $arguments): array
|
||||
{
|
||||
if (!array_key_exists(self::PRINTER, $arguments)) {
|
||||
$arguments[self::PRINTER] = new Printer(null, $arguments['verbose'] ?? false, $arguments['colors'] ?? DefaultResultPrinter::COLOR_ALWAYS);
|
||||
}
|
||||
|
||||
if ($arguments[self::PRINTER] === \PHPUnit\Util\Log\TeamCity::class) {
|
||||
$arguments[self::PRINTER] = new TeamCity(null, $arguments['verbose'] ?? false, $arguments['colors'] ?? DefaultResultPrinter::COLOR_ALWAYS);
|
||||
}
|
||||
|
||||
// Load our junit logger instead.
|
||||
if (array_key_exists('junitLogfile', $arguments)) {
|
||||
$arguments['listeners'][] = new JUnit(
|
||||
$arguments['junitLogfile']
|
||||
);
|
||||
unset($arguments['junitLogfile']);
|
||||
}
|
||||
|
||||
return $arguments;
|
||||
}
|
||||
}
|
||||
@ -1,64 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\Actions;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use PHPUnit\Framework\TestSuite;
|
||||
use PHPUnit\Framework\WarningTestCase;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class AddsTests
|
||||
{
|
||||
/**
|
||||
* Adds tests to the given test suite.
|
||||
*
|
||||
* @param TestSuite<\PHPUnit\Framework\TestCase> $testSuite
|
||||
*/
|
||||
public static function to(TestSuite $testSuite, \Pest\TestSuite $pestTestSuite): void
|
||||
{
|
||||
self::removeTestClosureWarnings($testSuite);
|
||||
|
||||
$testSuites = [];
|
||||
$pestTestSuite->tests->build($pestTestSuite, function (TestCase $testCase) use (&$testSuites): void {
|
||||
$testCaseClass = get_class($testCase);
|
||||
if (!array_key_exists($testCaseClass, $testSuites)) {
|
||||
$testSuites[$testCaseClass] = [];
|
||||
}
|
||||
|
||||
$testSuites[$testCaseClass][] = $testCase;
|
||||
});
|
||||
|
||||
foreach ($testSuites as $testCaseName => $testCases) {
|
||||
$testTestSuite = new TestSuite($testCaseName);
|
||||
$testTestSuite->setTests([]);
|
||||
foreach ($testCases as $testCase) {
|
||||
$testTestSuite->addTest($testCase, $testCase->getGroups());
|
||||
}
|
||||
$testSuite->addTestSuite($testTestSuite);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TestSuite<\PHPUnit\Framework\TestCase> $testSuite
|
||||
*/
|
||||
private static function removeTestClosureWarnings(TestSuite $testSuite): void
|
||||
{
|
||||
$tests = $testSuite->tests();
|
||||
|
||||
foreach ($tests as $key => $test) {
|
||||
if ($test instanceof TestSuite) {
|
||||
self::removeTestClosureWarnings($test);
|
||||
}
|
||||
|
||||
if ($test instanceof WarningTestCase) {
|
||||
unset($tests[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
$testSuite->setTests($tests);
|
||||
}
|
||||
}
|
||||
@ -1,50 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\Actions;
|
||||
|
||||
use Pest\Contracts\Plugins\AddsOutput;
|
||||
use Pest\Contracts\Plugins\HandlesArguments;
|
||||
use Pest\Plugin\Loader;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class InteractsWithPlugins
|
||||
{
|
||||
/**
|
||||
* Transform the input arguments by passing it to the relevant plugins.
|
||||
*
|
||||
* @param array<int, string> $argv
|
||||
*
|
||||
* @return array<int, string>
|
||||
*/
|
||||
public static function handleArguments(array $argv): array
|
||||
{
|
||||
$plugins = Loader::getPlugins(HandlesArguments::class);
|
||||
|
||||
/** @var HandlesArguments $plugin */
|
||||
foreach ($plugins as $plugin) {
|
||||
$argv = $plugin->handleArguments($argv);
|
||||
}
|
||||
|
||||
return $argv;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides an opportunity for any plugins that want
|
||||
* to provide additional output after test execution.
|
||||
*/
|
||||
public static function addOutput(int $result): int
|
||||
{
|
||||
$plugins = Loader::getPlugins(AddsOutput::class);
|
||||
|
||||
/** @var AddsOutput $plugin */
|
||||
foreach ($plugins as $plugin) {
|
||||
$result = $plugin->addOutput($result);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
@ -1,63 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\Actions;
|
||||
|
||||
use Pest\Support\Str;
|
||||
use function Pest\testDirectory;
|
||||
use PHPUnit\Util\FileLoader;
|
||||
use RecursiveDirectoryIterator;
|
||||
use RecursiveIteratorIterator;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class LoadStructure
|
||||
{
|
||||
/**
|
||||
* The Pest convention.
|
||||
*
|
||||
* @var array<int, string>
|
||||
*/
|
||||
private const STRUCTURE = [
|
||||
'Expectations.php',
|
||||
'Datasets.php',
|
||||
'Helpers.php',
|
||||
'Pest.php',
|
||||
'Datasets',
|
||||
];
|
||||
|
||||
/**
|
||||
* Validates the configuration in the given `configuration`.
|
||||
*/
|
||||
public static function in(string $rootPath): void
|
||||
{
|
||||
$testsPath = $rootPath . DIRECTORY_SEPARATOR . testDirectory();
|
||||
|
||||
$load = function ($filename): bool {
|
||||
return file_exists($filename) && (bool) FileLoader::checkAndLoad($filename);
|
||||
};
|
||||
|
||||
foreach (self::STRUCTURE as $filename) {
|
||||
$filename = sprintf('%s%s%s', $testsPath, DIRECTORY_SEPARATOR, $filename);
|
||||
|
||||
if (!file_exists($filename)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (is_dir($filename)) {
|
||||
$directory = new RecursiveDirectoryIterator($filename);
|
||||
$iterator = new RecursiveIteratorIterator($directory);
|
||||
foreach ($iterator as $file) {
|
||||
$filename = $file->__toString();
|
||||
if (Str::endsWith($filename, '.php') && file_exists($filename)) {
|
||||
require_once $filename;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$load($filename);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,38 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\Actions;
|
||||
|
||||
use Pest\Exceptions\AttributeNotSupportedYet;
|
||||
use Pest\Exceptions\FileOrFolderNotFound;
|
||||
use PHPUnit\TextUI\XmlConfiguration\Loader;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class ValidatesConfiguration
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private const CONFIGURATION_KEY = 'configuration';
|
||||
|
||||
/**
|
||||
* Validates the configuration in the given `configuration`.
|
||||
*
|
||||
* @param array<string, mixed> $arguments
|
||||
*/
|
||||
public static function in($arguments): void
|
||||
{
|
||||
if (!array_key_exists(self::CONFIGURATION_KEY, $arguments) || !file_exists($arguments[self::CONFIGURATION_KEY])) {
|
||||
throw new FileOrFolderNotFound('phpunit.xml');
|
||||
}
|
||||
|
||||
$configuration = (new Loader())->load($arguments[self::CONFIGURATION_KEY])->phpunit();
|
||||
|
||||
if ($configuration->processIsolation()) {
|
||||
throw new AttributeNotSupportedYet('processIsolation', 'true');
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,41 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\Actions;
|
||||
|
||||
use Pest\Exceptions\FileOrFolderNotFound;
|
||||
use Pest\TestSuite;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class ValidatesEnvironment
|
||||
{
|
||||
/**
|
||||
* The need files on the root path.
|
||||
*
|
||||
* @var array<int, string>
|
||||
*/
|
||||
private const NEEDED_FILES = [
|
||||
'composer.json',
|
||||
];
|
||||
|
||||
/**
|
||||
* Validates the environment.
|
||||
*/
|
||||
public static function in(TestSuite $testSuite): void
|
||||
{
|
||||
$rootPath = $testSuite->rootPath;
|
||||
|
||||
$exists = function ($neededFile) use ($rootPath): bool {
|
||||
return file_exists(sprintf('%s%s%s', $rootPath, DIRECTORY_SEPARATOR, $neededFile));
|
||||
};
|
||||
|
||||
foreach (self::NEEDED_FILES as $neededFile) {
|
||||
if (!$exists($neededFile)) {
|
||||
throw new FileOrFolderNotFound($neededFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
74
src/ArchPresets/AbstractPreset.php
Normal file
74
src/ArchPresets/AbstractPreset.php
Normal file
@ -0,0 +1,74 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\ArchPresets;
|
||||
|
||||
use Pest\Arch\Contracts\ArchExpectation;
|
||||
use Pest\Expectation;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
abstract class AbstractPreset // @pest-arch-ignore-line
|
||||
{
|
||||
/**
|
||||
* The expectations.
|
||||
*
|
||||
* @var array<int, Expectation<mixed>|ArchExpectation>
|
||||
*/
|
||||
protected array $expectations = [];
|
||||
|
||||
/**
|
||||
* Creates a new preset instance.
|
||||
*
|
||||
* @param array<int, string> $userNamespaces
|
||||
*/
|
||||
public function __construct(
|
||||
private readonly array $userNamespaces,
|
||||
) {
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the arch preset.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
abstract public function execute(): void;
|
||||
|
||||
/**
|
||||
* Ignores the given "targets" or "dependencies".
|
||||
*
|
||||
* @param array<int, string>|string $targetsOrDependencies
|
||||
*/
|
||||
final public function ignoring(array|string $targetsOrDependencies): void
|
||||
{
|
||||
$this->expectations = array_map(
|
||||
fn (ArchExpectation|Expectation $expectation): Expectation|ArchExpectation => $expectation instanceof ArchExpectation ? $expectation->ignoring($targetsOrDependencies) : $expectation,
|
||||
$this->expectations,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the given callback for each namespace.
|
||||
*
|
||||
* @param callable(Expectation<string|null>): ArchExpectation ...$callbacks
|
||||
*/
|
||||
final public function eachUserNamespace(callable ...$callbacks): void
|
||||
{
|
||||
foreach ($this->userNamespaces as $namespace) {
|
||||
foreach ($callbacks as $callback) {
|
||||
$this->expectations[] = $callback(expect($namespace));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Flushes the expectations.
|
||||
*/
|
||||
final public function flush(): void
|
||||
{
|
||||
$this->expectations = [];
|
||||
}
|
||||
}
|
||||
45
src/ArchPresets/Custom.php
Normal file
45
src/ArchPresets/Custom.php
Normal file
@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\ArchPresets;
|
||||
|
||||
use Closure;
|
||||
use Pest\Arch\Contracts\ArchExpectation;
|
||||
use Pest\Expectation;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class Custom extends AbstractPreset
|
||||
{
|
||||
/**
|
||||
* Creates a new preset instance.
|
||||
*
|
||||
* @param array<int, string> $userNamespaces
|
||||
* @param Closure(array<int, string>): array<Expectation<mixed>|ArchExpectation> $execute
|
||||
*/
|
||||
public function __construct(
|
||||
private readonly array $userNamespaces,
|
||||
private readonly string $name,
|
||||
private readonly Closure $execute,
|
||||
) {
|
||||
parent::__construct($userNamespaces);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the preset.
|
||||
*/
|
||||
public function name(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the arch preset.
|
||||
*/
|
||||
public function execute(): void
|
||||
{
|
||||
$this->expectations = ($this->execute)($this->userNamespaces);
|
||||
}
|
||||
}
|
||||
163
src/ArchPresets/Laravel.php
Normal file
163
src/ArchPresets/Laravel.php
Normal file
@ -0,0 +1,163 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\ArchPresets;
|
||||
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class Laravel extends AbstractPreset
|
||||
{
|
||||
/**
|
||||
* Executes the arch preset.
|
||||
*/
|
||||
public function execute(): void
|
||||
{
|
||||
$this->expectations[] = expect('App\Traits')
|
||||
->toBeTraits();
|
||||
|
||||
$this->expectations[] = expect('App\Concerns')
|
||||
->toBeTraits();
|
||||
|
||||
$this->expectations[] = expect('App')
|
||||
->not->toBeEnums()
|
||||
->ignoring('App\Enums');
|
||||
|
||||
$this->expectations[] = expect('App\Enums')
|
||||
->toBeEnums();
|
||||
|
||||
$this->expectations[] = expect('App\Features')
|
||||
->toBeClasses();
|
||||
|
||||
$this->expectations[] = expect('App\Features')
|
||||
->toHaveMethod('resolve');
|
||||
|
||||
$this->expectations[] = expect('App\Exceptions')
|
||||
->classes()
|
||||
->toImplement('Throwable');
|
||||
|
||||
$this->expectations[] = expect('App')
|
||||
->not->toImplement(Throwable::class)
|
||||
->ignoring('App\Exceptions');
|
||||
|
||||
$this->expectations[] = expect('App\Http\Middleware')
|
||||
->classes()
|
||||
->toHaveMethod('handle');
|
||||
|
||||
$this->expectations[] = expect('App\Models')
|
||||
->classes()
|
||||
->toExtend('Illuminate\Database\Eloquent\Model')
|
||||
->ignoring('App\Models\Scopes');
|
||||
|
||||
$this->expectations[] = expect('App\Models')
|
||||
->classes()
|
||||
->not->toHaveSuffix('Model');
|
||||
|
||||
$this->expectations[] = expect('App')
|
||||
->not->toExtend('Illuminate\Database\Eloquent\Model')
|
||||
->ignoring('App\Models');
|
||||
|
||||
$this->expectations[] = expect('App\Http\Requests')
|
||||
->classes()
|
||||
->toHaveSuffix('Request');
|
||||
|
||||
$this->expectations[] = expect('App\Http\Requests')
|
||||
->toExtend('Illuminate\Foundation\Http\FormRequest');
|
||||
|
||||
$this->expectations[] = expect('App\Http\Requests')
|
||||
->toHaveMethod('rules');
|
||||
|
||||
$this->expectations[] = expect('App')
|
||||
->not->toExtend('Illuminate\Foundation\Http\FormRequest')
|
||||
->ignoring('App\Http\Requests');
|
||||
|
||||
$this->expectations[] = expect('App\Console\Commands')
|
||||
->classes()
|
||||
->toHaveSuffix('Command');
|
||||
|
||||
$this->expectations[] = expect('App\Console\Commands')
|
||||
->classes()
|
||||
->toExtend('Illuminate\Console\Command');
|
||||
|
||||
$this->expectations[] = expect('App\Console\Commands')
|
||||
->classes()
|
||||
->toHaveMethod('handle');
|
||||
|
||||
$this->expectations[] = expect('App')
|
||||
->not->toExtend('Illuminate\Console\Command')
|
||||
->ignoring('App\Console\Commands');
|
||||
|
||||
$this->expectations[] = expect('App\Mail')
|
||||
->classes()
|
||||
->toExtend('Illuminate\Mail\Mailable');
|
||||
|
||||
$this->expectations[] = expect('App\Mail')
|
||||
->classes()
|
||||
->toImplement('Illuminate\Contracts\Queue\ShouldQueue');
|
||||
|
||||
$this->expectations[] = expect('App')
|
||||
->not->toExtend('Illuminate\Mail\Mailable')
|
||||
->ignoring('App\Mail');
|
||||
|
||||
$this->expectations[] = expect('App\Jobs')
|
||||
->classes()
|
||||
->toImplement('Illuminate\Contracts\Queue\ShouldQueue');
|
||||
|
||||
$this->expectations[] = expect('App\Jobs')
|
||||
->classes()
|
||||
->toHaveMethod('handle');
|
||||
|
||||
$this->expectations[] = expect('App\Listeners')
|
||||
->toHaveMethod('handle');
|
||||
|
||||
$this->expectations[] = expect('App\Notifications')
|
||||
->toExtend('Illuminate\Notifications\Notification');
|
||||
|
||||
$this->expectations[] = expect('App')
|
||||
->not->toExtend('Illuminate\Notifications\Notification')
|
||||
->ignoring('App\Notifications');
|
||||
|
||||
$this->expectations[] = expect('App\Providers')
|
||||
->toHaveSuffix('ServiceProvider');
|
||||
|
||||
$this->expectations[] = expect('App\Providers')
|
||||
->toExtend('Illuminate\Support\ServiceProvider');
|
||||
|
||||
$this->expectations[] = expect('App\Providers')
|
||||
->not->toBeUsed();
|
||||
|
||||
$this->expectations[] = expect('App')
|
||||
->not->toExtend('Illuminate\Support\ServiceProvider')
|
||||
->ignoring('App\Providers');
|
||||
|
||||
$this->expectations[] = expect('App')
|
||||
->not->toHaveSuffix('ServiceProvider')
|
||||
->ignoring('App\Providers');
|
||||
|
||||
$this->expectations[] = expect('App')
|
||||
->not->toHaveSuffix('Controller')
|
||||
->ignoring('App\Http\Controllers');
|
||||
|
||||
$this->expectations[] = expect('App\Http\Controllers')
|
||||
->classes()
|
||||
->toHaveSuffix('Controller');
|
||||
|
||||
$this->expectations[] = expect('App\Http')
|
||||
->toOnlyBeUsedIn('App\Http');
|
||||
|
||||
$this->expectations[] = expect('App\Http\Controllers')
|
||||
->not->toHavePublicMethodsBesides(['__construct', '__invoke', 'index', 'show', 'create', 'store', 'edit', 'update', 'destroy']);
|
||||
|
||||
$this->expectations[] = expect([
|
||||
'dd',
|
||||
'ddd',
|
||||
'dump',
|
||||
'env',
|
||||
'exit',
|
||||
'ray',
|
||||
])->not->toBeUsed();
|
||||
}
|
||||
}
|
||||
93
src/ArchPresets/Php.php
Normal file
93
src/ArchPresets/Php.php
Normal file
@ -0,0 +1,93 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\ArchPresets;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class Php extends AbstractPreset
|
||||
{
|
||||
/**
|
||||
* Executes the arch preset.
|
||||
*/
|
||||
public function execute(): void
|
||||
{
|
||||
$this->expectations[] = expect([
|
||||
'debug_zval_dump',
|
||||
'debug_backtrace',
|
||||
'debug_print_backtrace',
|
||||
'dump',
|
||||
'ray',
|
||||
'ds',
|
||||
'die',
|
||||
'goto',
|
||||
'global',
|
||||
'var_dump',
|
||||
'phpinfo',
|
||||
'echo',
|
||||
'ereg',
|
||||
'eregi',
|
||||
'mysql_connect',
|
||||
'mysql_pconnect',
|
||||
'mysql_query',
|
||||
'mysql_select_db',
|
||||
'mysql_fetch_array',
|
||||
'mysql_fetch_assoc',
|
||||
'mysql_fetch_object',
|
||||
'mysql_fetch_row',
|
||||
'mysql_num_rows',
|
||||
'mysql_affected_rows',
|
||||
'mysql_free_result',
|
||||
'mysql_insert_id',
|
||||
'mysql_error',
|
||||
'mysql_real_escape_string',
|
||||
'print',
|
||||
'print_r',
|
||||
'var_export',
|
||||
'xdebug_break',
|
||||
'xdebug_call_class',
|
||||
'xdebug_call_file',
|
||||
'xdebug_call_int',
|
||||
'xdebug_call_line',
|
||||
'xdebug_code_coverage_started',
|
||||
'xdebug_connect_to_client',
|
||||
'xdebug_debug_zval',
|
||||
'xdebug_debug_zval_stdout',
|
||||
'xdebug_dump_superglobals',
|
||||
'xdebug_get_code_coverage',
|
||||
'xdebug_get_collected_errors',
|
||||
'xdebug_get_function_count',
|
||||
'xdebug_get_function_stack',
|
||||
'xdebug_get_gc_run_count',
|
||||
'xdebug_get_gc_total_collected_roots',
|
||||
'xdebug_get_gcstats_filename',
|
||||
'xdebug_get_headers',
|
||||
'xdebug_get_monitored_functions',
|
||||
'xdebug_get_profiler_filename',
|
||||
'xdebug_get_stack_depth',
|
||||
'xdebug_get_tracefile_name',
|
||||
'xdebug_info',
|
||||
'xdebug_is_debugger_active',
|
||||
'xdebug_memory_usage',
|
||||
'xdebug_notify',
|
||||
'xdebug_peak_memory_usage',
|
||||
'xdebug_print_function_stack',
|
||||
'xdebug_set_filter',
|
||||
'xdebug_start_code_coverage',
|
||||
'xdebug_start_error_collection',
|
||||
'xdebug_start_function_monitor',
|
||||
'xdebug_start_gcstats',
|
||||
'xdebug_start_trace',
|
||||
'xdebug_stop_code_coverage',
|
||||
'xdebug_stop_error_collection',
|
||||
'xdebug_stop_function_monitor',
|
||||
'xdebug_stop_gcstats',
|
||||
'xdebug_stop_trace',
|
||||
'xdebug_time_index',
|
||||
'xdebug_var_dump',
|
||||
'trap',
|
||||
])->not->toBeUsed();
|
||||
}
|
||||
}
|
||||
26
src/ArchPresets/Relaxed.php
Normal file
26
src/ArchPresets/Relaxed.php
Normal file
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\ArchPresets;
|
||||
|
||||
use Pest\Arch\Contracts\ArchExpectation;
|
||||
use Pest\Expectation;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class Relaxed extends AbstractPreset
|
||||
{
|
||||
/**
|
||||
* Executes the arch preset.
|
||||
*/
|
||||
public function execute(): void
|
||||
{
|
||||
$this->eachUserNamespace(
|
||||
fn (Expectation $namespace): ArchExpectation => $namespace->not->toUseStrictTypes(),
|
||||
fn (Expectation $namespace): ArchExpectation => $namespace->classes()->not->toBeFinal(),
|
||||
fn (Expectation $namespace): ArchExpectation => $namespace->classes()->not->toHavePrivateMethods(),
|
||||
);
|
||||
}
|
||||
}
|
||||
41
src/ArchPresets/Security.php
Normal file
41
src/ArchPresets/Security.php
Normal file
@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\ArchPresets;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class Security extends AbstractPreset
|
||||
{
|
||||
/**
|
||||
* Executes the arch preset.
|
||||
*/
|
||||
public function execute(): void
|
||||
{
|
||||
$this->expectations[] = expect([
|
||||
'md5',
|
||||
'sha1',
|
||||
'uniqid',
|
||||
'rand',
|
||||
'mt_rand',
|
||||
'tempnam',
|
||||
'str_shuffle',
|
||||
'shuffle',
|
||||
'array_rand',
|
||||
'eval',
|
||||
'exec',
|
||||
'shell_exec',
|
||||
'system',
|
||||
'passthru',
|
||||
'create_function',
|
||||
'unserialize',
|
||||
'extract',
|
||||
'parse_str',
|
||||
'mb_parse_str',
|
||||
'dl',
|
||||
'assert',
|
||||
])->not->toBeUsed();
|
||||
}
|
||||
}
|
||||
32
src/ArchPresets/Strict.php
Normal file
32
src/ArchPresets/Strict.php
Normal file
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\ArchPresets;
|
||||
|
||||
use Pest\Arch\Contracts\ArchExpectation;
|
||||
use Pest\Expectation;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class Strict extends AbstractPreset
|
||||
{
|
||||
/**
|
||||
* Executes the arch preset.
|
||||
*/
|
||||
public function execute(): void
|
||||
{
|
||||
$this->eachUserNamespace(
|
||||
fn (Expectation $namespace): ArchExpectation => $namespace->classes()->not->toHaveProtectedMethods(),
|
||||
fn (Expectation $namespace): ArchExpectation => $namespace->classes()->not->toBeAbstract(),
|
||||
fn (Expectation $namespace): ArchExpectation => $namespace->toUseStrictTypes(),
|
||||
fn (Expectation $namespace): ArchExpectation => $namespace->classes()->toBeFinal(),
|
||||
);
|
||||
|
||||
$this->expectations[] = expect([
|
||||
'sleep',
|
||||
'usleep',
|
||||
])->not->toBeUsed();
|
||||
}
|
||||
}
|
||||
39
src/Bootstrappers/BootExcludeList.php
Normal file
39
src/Bootstrappers/BootExcludeList.php
Normal file
@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\Bootstrappers;
|
||||
|
||||
use Pest\Contracts\Bootstrapper;
|
||||
use PHPUnit\Util\ExcludeList;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class BootExcludeList implements Bootstrapper
|
||||
{
|
||||
/**
|
||||
* The directories to exclude.
|
||||
*
|
||||
* @var array<int, non-empty-string>
|
||||
*/
|
||||
private const EXCLUDE_LIST = [
|
||||
'bin',
|
||||
'overrides',
|
||||
'resources',
|
||||
'src',
|
||||
'stubs',
|
||||
];
|
||||
|
||||
/**
|
||||
* Boots the "exclude list" for PHPUnit to ignore Pest files.
|
||||
*/
|
||||
public function boot(): void
|
||||
{
|
||||
$baseDirectory = dirname(__DIR__, 2);
|
||||
|
||||
foreach (self::EXCLUDE_LIST as $directory) {
|
||||
ExcludeList::addDirectory($baseDirectory.DIRECTORY_SEPARATOR.$directory);
|
||||
}
|
||||
}
|
||||
}
|
||||
91
src/Bootstrappers/BootFiles.php
Normal file
91
src/Bootstrappers/BootFiles.php
Normal file
@ -0,0 +1,91 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\Bootstrappers;
|
||||
|
||||
use Pest\Contracts\Bootstrapper;
|
||||
use Pest\Support\DatasetInfo;
|
||||
use Pest\Support\Str;
|
||||
use Pest\TestSuite;
|
||||
use RecursiveDirectoryIterator;
|
||||
use RecursiveIteratorIterator;
|
||||
use SebastianBergmann\FileIterator\Facade as PhpUnitFileIterator;
|
||||
|
||||
use function Pest\testDirectory;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class BootFiles implements Bootstrapper
|
||||
{
|
||||
/**
|
||||
* The structure of the tests directory.
|
||||
*
|
||||
* @var array<int, string>
|
||||
*/
|
||||
private const STRUCTURE = [
|
||||
'Expectations',
|
||||
'Expectations.php',
|
||||
'Helpers',
|
||||
'Helpers.php',
|
||||
'Pest.php',
|
||||
];
|
||||
|
||||
/**
|
||||
* Boots the structure of the tests directory.
|
||||
*/
|
||||
public function boot(): void
|
||||
{
|
||||
$rootPath = TestSuite::getInstance()->rootPath;
|
||||
$testsPath = $rootPath.DIRECTORY_SEPARATOR.testDirectory();
|
||||
|
||||
foreach (self::STRUCTURE as $filename) {
|
||||
$filename = sprintf('%s%s%s', $testsPath, DIRECTORY_SEPARATOR, $filename);
|
||||
|
||||
if (! file_exists($filename)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (is_dir($filename)) {
|
||||
$directory = new RecursiveDirectoryIterator($filename);
|
||||
$iterator = new RecursiveIteratorIterator($directory);
|
||||
/** @var \DirectoryIterator $file */
|
||||
foreach ($iterator as $file) {
|
||||
$this->load($file->__toString());
|
||||
}
|
||||
} else {
|
||||
$this->load($filename);
|
||||
}
|
||||
}
|
||||
|
||||
$this->bootDatasets($testsPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads, if possible, the given file.
|
||||
*/
|
||||
private function load(string $filename): void
|
||||
{
|
||||
if (! Str::endsWith($filename, '.php')) {
|
||||
return;
|
||||
}
|
||||
if (! file_exists($filename)) {
|
||||
return;
|
||||
}
|
||||
include_once $filename;
|
||||
}
|
||||
|
||||
private function bootDatasets(string $testsPath): void
|
||||
{
|
||||
assert(strlen($testsPath) > 0);
|
||||
|
||||
$files = (new PhpUnitFileIterator)->getFilesAsArray($testsPath, '.php');
|
||||
|
||||
foreach ($files as $file) {
|
||||
if (DatasetInfo::isADatasetsFile($file) || DatasetInfo::isInsideADatasetsDirectory($file)) {
|
||||
$this->load($file);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
37
src/Bootstrappers/BootKernelDump.php
Normal file
37
src/Bootstrappers/BootKernelDump.php
Normal file
@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\Bootstrappers;
|
||||
|
||||
use Pest\Contracts\Bootstrapper;
|
||||
use Pest\KernelDump;
|
||||
use Pest\Support\Container;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final readonly class BootKernelDump implements Bootstrapper
|
||||
{
|
||||
/**
|
||||
* Creates a new Boot Kernel Dump instance.
|
||||
*/
|
||||
public function __construct(
|
||||
private OutputInterface $output,
|
||||
) {
|
||||
// ...
|
||||
}
|
||||
|
||||
/**
|
||||
* Boots the kernel dump.
|
||||
*/
|
||||
public function boot(): void
|
||||
{
|
||||
Container::getInstance()->add(KernelDump::class, $kernelDump = new KernelDump(
|
||||
$this->output,
|
||||
));
|
||||
|
||||
$kernelDump->enable();
|
||||
}
|
||||
}
|
||||
46
src/Bootstrappers/BootOverrides.php
Normal file
46
src/Bootstrappers/BootOverrides.php
Normal file
@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\Bootstrappers;
|
||||
|
||||
use Pest\Contracts\Bootstrapper;
|
||||
use Pest\Exceptions\ShouldNotHappen;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class BootOverrides implements Bootstrapper
|
||||
{
|
||||
/**
|
||||
* The list of files to be overridden.
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
public const FILES = [
|
||||
'c96b1cb57d7fc8e649f4c13a8abe418c2541bcfab194fb6702b99f777f52ee84' => 'Runner/Filter/NameFilterIterator.php',
|
||||
'a4a43de01f641c6944ee83d963795a46d32b5206b5ab3bbc6cce76e67190acbf' => 'Runner/ResultCache/DefaultResultCache.php',
|
||||
'd0e81317889ad88c707db4b08a94cadee4c9010d05ff0a759f04e71af5efed89' => 'Runner/TestSuiteLoader.php',
|
||||
'3bb609b0d3bf6dee8df8d6cd62a3c8ece823c4bb941eaaae39e3cb267171b9d2' => 'TextUI/Command/Commands/WarmCodeCoverageCacheCommand.php',
|
||||
'8abdad6413329c6fe0d7d44a8b9926e390af32c0b3123f3720bb9c5bbc6fbb7e' => 'TextUI/Output/Default/ProgressPrinter/Subscriber/TestSkippedSubscriber.php',
|
||||
'43883b7e5811886cf3731c8ed6304d5a77078d9731e1e505abc2da36bde19f3e' => 'TextUI/TestSuiteFilterProcessor.php',
|
||||
'357d5cd7007f8559b26e1b8cdf43bb6fb15b51b79db981779da6f31b7ec39dad' => 'Event/Value/ThrowableBuilder.php',
|
||||
'01974a686eba69b5fbb87a904d936eae2176e39567616898c5b758db71d87a22' => 'Logging/JUnit/JunitXmlLogger.php',
|
||||
];
|
||||
|
||||
/**
|
||||
* Boots the list of files to be overridden.
|
||||
*/
|
||||
public function boot(): void
|
||||
{
|
||||
foreach (self::FILES as $file) {
|
||||
$file = __DIR__."/../../overrides/$file";
|
||||
|
||||
if (! file_exists($file)) {
|
||||
throw ShouldNotHappen::fromMessage(sprintf('File [%s] does not exist.', $file));
|
||||
}
|
||||
|
||||
require_once $file;
|
||||
}
|
||||
}
|
||||
}
|
||||
50
src/Bootstrappers/BootSubscribers.php
Normal file
50
src/Bootstrappers/BootSubscribers.php
Normal file
@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\Bootstrappers;
|
||||
|
||||
use Pest\Contracts\Bootstrapper;
|
||||
use Pest\Subscribers;
|
||||
use Pest\Support\Container;
|
||||
use PHPUnit\Event;
|
||||
use PHPUnit\Event\Subscriber;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final readonly class BootSubscribers implements Bootstrapper
|
||||
{
|
||||
/**
|
||||
* The list of Subscribers.
|
||||
*
|
||||
* @var array<int, class-string<Subscriber>>
|
||||
*/
|
||||
private const SUBSCRIBERS = [
|
||||
Subscribers\EnsureConfigurationIsAvailable::class,
|
||||
Subscribers\EnsureIgnorableTestCasesAreIgnored::class,
|
||||
Subscribers\EnsureKernelDumpIsFlushed::class,
|
||||
Subscribers\EnsureTeamCityEnabled::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* Creates a new instance of the Boot Subscribers.
|
||||
*/
|
||||
public function __construct(
|
||||
private Container $container,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Boots the list of Subscribers.
|
||||
*/
|
||||
public function boot(): void
|
||||
{
|
||||
foreach (self::SUBSCRIBERS as $subscriber) {
|
||||
$instance = $this->container->get($subscriber);
|
||||
|
||||
assert($instance instanceof Subscriber);
|
||||
|
||||
Event\Facade::instance()->registerSubscriber($instance);
|
||||
}
|
||||
}
|
||||
}
|
||||
32
src/Bootstrappers/BootView.php
Normal file
32
src/Bootstrappers/BootView.php
Normal file
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\Bootstrappers;
|
||||
|
||||
use Pest\Contracts\Bootstrapper;
|
||||
use Pest\Support\View;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final readonly class BootView implements Bootstrapper
|
||||
{
|
||||
/**
|
||||
* Creates a new instance of the Boot View.
|
||||
*/
|
||||
public function __construct(
|
||||
private OutputInterface $output
|
||||
) {
|
||||
// ..
|
||||
}
|
||||
|
||||
/**
|
||||
* Boots the view renderer.
|
||||
*/
|
||||
public function boot(): void
|
||||
{
|
||||
View::renderUsing($this->output);
|
||||
}
|
||||
}
|
||||
100
src/Collision/Events.php
Normal file
100
src/Collision/Events.php
Normal file
@ -0,0 +1,100 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\Collision;
|
||||
|
||||
use NunoMaduro\Collision\Adapters\Phpunit\TestResult;
|
||||
use Pest\Configuration\Project;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
use function Termwind\render;
|
||||
use function Termwind\renderUsing;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class Events
|
||||
{
|
||||
/**
|
||||
* Sets the output.
|
||||
*/
|
||||
private static ?OutputInterface $output = null;
|
||||
|
||||
/**
|
||||
* Sets the output.
|
||||
*/
|
||||
public static function setOutput(OutputInterface $output): void
|
||||
{
|
||||
self::$output = $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fires before the test method description is printed.
|
||||
*/
|
||||
public static function beforeTestMethodDescription(TestResult $result, string $description): string
|
||||
{
|
||||
if (($context = $result->context) === []) {
|
||||
return $description;
|
||||
}
|
||||
|
||||
renderUsing(self::$output);
|
||||
|
||||
[
|
||||
'assignees' => $assignees,
|
||||
'issues' => $issues,
|
||||
'prs' => $prs,
|
||||
] = $context;
|
||||
|
||||
if (($link = Project::getInstance()->issues) !== '') {
|
||||
$issuesDescription = array_map(fn (int $issue): string => sprintf('<a href="%s">#%s</a>', sprintf($link, $issue), $issue), $issues);
|
||||
}
|
||||
|
||||
if (($link = Project::getInstance()->prs) !== '') {
|
||||
$prsDescription = array_map(fn (int $pr): string => sprintf('<a href="%s">#%s</a>', sprintf($link, $pr), $pr), $prs);
|
||||
}
|
||||
|
||||
if (($link = Project::getInstance()->assignees) !== '' && count($assignees) > 0) {
|
||||
$assigneesDescription = array_map(fn (string $assignee): string => sprintf(
|
||||
'<a href="%s">@%s</a>',
|
||||
sprintf($link, $assignee),
|
||||
$assignee,
|
||||
), $assignees);
|
||||
}
|
||||
|
||||
if (count($assignees) > 0 || count($issues) > 0 || count($prs) > 0) {
|
||||
$description .= ' '.implode(', ', array_merge(
|
||||
$issuesDescription ?? [],
|
||||
$prsDescription ?? [],
|
||||
isset($assigneesDescription) ? ['['.implode(', ', $assigneesDescription).']'] : [],
|
||||
));
|
||||
}
|
||||
|
||||
return $description;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fires after the test method description is printed.
|
||||
*/
|
||||
public static function afterTestMethodDescription(TestResult $result): void
|
||||
{
|
||||
if (($context = $result->context) === []) {
|
||||
return;
|
||||
}
|
||||
|
||||
renderUsing(self::$output);
|
||||
|
||||
[
|
||||
'notes' => $notes,
|
||||
] = $context;
|
||||
|
||||
foreach ($notes as $note) {
|
||||
render(sprintf(<<<'HTML'
|
||||
<div class="ml-2">
|
||||
<span class="text-gray"> // %s</span>
|
||||
</div>
|
||||
HTML, $note,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -14,13 +14,12 @@ trait Expectable
|
||||
/**
|
||||
* @template TValue
|
||||
*
|
||||
* Creates a new expectation.
|
||||
* Creates a new Expectation.
|
||||
*
|
||||
* @param TValue $value
|
||||
*
|
||||
* @return Expectation<TValue>
|
||||
*/
|
||||
public function expect($value): Expectation
|
||||
public function expect(mixed $value): Expectation
|
||||
{
|
||||
return new Expectation($value);
|
||||
}
|
||||
|
||||
@ -4,7 +4,6 @@ declare(strict_types=1);
|
||||
|
||||
namespace Pest\Concerns;
|
||||
|
||||
use BadMethodCallException;
|
||||
use Closure;
|
||||
|
||||
/**
|
||||
@ -13,42 +12,25 @@ use Closure;
|
||||
trait Extendable
|
||||
{
|
||||
/**
|
||||
* The list of extends.
|
||||
*
|
||||
* @var array<string, Closure>
|
||||
*/
|
||||
private static $extends = [];
|
||||
private static array $extends = [];
|
||||
|
||||
/**
|
||||
* Register a custom extend.
|
||||
* Register a new extend.
|
||||
*/
|
||||
public static function extend(string $name, Closure $extend): void
|
||||
public function extend(string $name, Closure $extend): void
|
||||
{
|
||||
static::$extends[$name] = $extend;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if extend is registered.
|
||||
* Checks if given extend name is registered.
|
||||
*/
|
||||
public static function hasExtend(string $name): bool
|
||||
{
|
||||
return array_key_exists($name, static::$extends);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dynamically handle calls to the class.
|
||||
*
|
||||
* @param array<int, mixed> $parameters
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function __call(string $method, array $parameters)
|
||||
{
|
||||
if (!static::hasExtend($method)) {
|
||||
throw new BadMethodCallException("$method is not a callable method name.");
|
||||
}
|
||||
|
||||
/** @var Closure $extend */
|
||||
$extend = static::$extends[$method]->bindTo($this, static::class);
|
||||
|
||||
return $extend(...$parameters);
|
||||
}
|
||||
}
|
||||
|
||||
@ -9,21 +9,33 @@ namespace Pest\Concerns\Logging;
|
||||
*/
|
||||
trait WritesToConsole
|
||||
{
|
||||
/**
|
||||
* Writes the given success message to the console.
|
||||
*/
|
||||
private function writeSuccess(string $message): void
|
||||
{
|
||||
$this->writePestTestOutput($message, 'fg-green, bold', '✓');
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the given error message to the console.
|
||||
*/
|
||||
private function writeError(string $message): void
|
||||
{
|
||||
$this->writePestTestOutput($message, 'fg-red, bold', '⨯');
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the given warning message to the console.
|
||||
*/
|
||||
private function writeWarning(string $message): void
|
||||
{
|
||||
$this->writePestTestOutput($message, 'fg-yellow, bold', '-');
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the give message to the console.
|
||||
*/
|
||||
private function writePestTestOutput(string $message, string $color, string $symbol): void
|
||||
{
|
||||
$this->writeWithColor($color, "$symbol ", false);
|
||||
|
||||
71
src/Concerns/Pipeable.php
Normal file
71
src/Concerns/Pipeable.php
Normal file
@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\Concerns;
|
||||
|
||||
use Closure;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
trait Pipeable
|
||||
{
|
||||
/**
|
||||
* The list of pipes.
|
||||
*
|
||||
* @var array<string, array<Closure(Closure, mixed ...$arguments): void>>
|
||||
*/
|
||||
private static array $pipes = [];
|
||||
|
||||
/**
|
||||
* The list of interceptors.
|
||||
*
|
||||
* @var array<string, array<Closure(Closure, mixed ...$arguments): void>>
|
||||
*/
|
||||
private static array $interceptors = [];
|
||||
|
||||
/**
|
||||
* Register a pipe to be applied before an expectation is checked.
|
||||
*/
|
||||
public function pipe(string $name, Closure $pipe): void
|
||||
{
|
||||
self::$pipes[$name][] = $pipe;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register an interceptor that should replace an existing expectation.
|
||||
*
|
||||
* @param string|Closure(mixed $value, mixed ...$arguments):bool $filter
|
||||
*/
|
||||
public function intercept(string $name, string|Closure $filter, Closure $handler): void
|
||||
{
|
||||
if (is_string($filter)) {
|
||||
$filter = fn ($value): bool => $value instanceof $filter;
|
||||
}
|
||||
|
||||
self::$interceptors[$name][] = $handler;
|
||||
|
||||
$this->pipe($name, function ($next, ...$arguments) use ($handler, $filter): void {
|
||||
/* @phpstan-ignore-next-line */
|
||||
if ($filter($this->value, ...$arguments)) {
|
||||
// @phpstan-ignore-next-line
|
||||
$handler->bindTo($this, $this::class)(...$arguments);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$next();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of pipes by the given name.
|
||||
*
|
||||
* @return array<int, Closure>
|
||||
*/
|
||||
private function pipes(string $name, object $context, string $scope): array
|
||||
{
|
||||
return array_map(fn (Closure $pipe): \Closure => $pipe->bindTo($context, $scope), self::$pipes[$name] ?? []);
|
||||
}
|
||||
}
|
||||
@ -7,19 +7,19 @@ namespace Pest\Concerns;
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
trait RetrievesValues
|
||||
trait Retrievable
|
||||
{
|
||||
/**
|
||||
* @template TRetrievableValue
|
||||
*
|
||||
* Safely retrieve the value at the given key from an object or array.
|
||||
* @template TRetrievableValue
|
||||
*
|
||||
* @param array<string, TRetrievableValue>|object $value
|
||||
* @param TRetrievableValue|null $default
|
||||
*
|
||||
* @return TRetrievableValue|null
|
||||
*/
|
||||
private function retrieve(string $key, $value, $default = null)
|
||||
private function retrieve(string $key, mixed $value, mixed $default = null): mixed
|
||||
{
|
||||
if (is_array($value)) {
|
||||
return $value[$key] ?? $default;
|
||||
@ -5,187 +5,202 @@ declare(strict_types=1);
|
||||
namespace Pest\Concerns;
|
||||
|
||||
use Closure;
|
||||
use Pest\Exceptions\DatasetArgumentsMismatch;
|
||||
use Pest\Preset;
|
||||
use Pest\Support\ChainableClosure;
|
||||
use Pest\Support\ExceptionTrace;
|
||||
use Pest\Support\Reflection;
|
||||
use Pest\TestSuite;
|
||||
use PHPUnit\Framework\ExecutionOrderDependency;
|
||||
use PHPUnit\Framework\Attributes\PostCondition;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use ReflectionException;
|
||||
use ReflectionFunction;
|
||||
use ReflectionParameter;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* To avoid inheritance conflicts, all the fields related
|
||||
* to Pest only will be prefixed by double underscore.
|
||||
*
|
||||
* @internal
|
||||
*
|
||||
* @mixin TestCase
|
||||
*/
|
||||
trait Testable
|
||||
{
|
||||
/**
|
||||
* The test case description. Contains the first
|
||||
* argument of global functions like `it` and `test`.
|
||||
*
|
||||
* @var string
|
||||
* The test's description.
|
||||
*/
|
||||
private $__description;
|
||||
private string $__description;
|
||||
|
||||
/**
|
||||
* Holds the test closure function.
|
||||
*
|
||||
* @var Closure
|
||||
* The test's latest description.
|
||||
*/
|
||||
private $__test;
|
||||
private static string $__latestDescription;
|
||||
|
||||
/**
|
||||
* Holds a global/shared beforeEach ("set up") closure if one has been
|
||||
* defined.
|
||||
*
|
||||
* @var Closure|null
|
||||
* The test's assignees.
|
||||
*/
|
||||
private $beforeEach = null;
|
||||
private static array $__latestAssignees = [];
|
||||
|
||||
/**
|
||||
* Holds a global/shared afterEach ("tear down") closure if one has been
|
||||
* defined.
|
||||
*
|
||||
* @var Closure|null
|
||||
* The test's notes.
|
||||
*/
|
||||
private $afterEach = null;
|
||||
private static array $__latestNotes = [];
|
||||
|
||||
/**
|
||||
* Holds a global/shared beforeAll ("set up before") closure if one has been
|
||||
* defined.
|
||||
* The test's issues.
|
||||
*
|
||||
* @var Closure|null
|
||||
* @var array<int, int>
|
||||
*/
|
||||
private static $beforeAll = null;
|
||||
private static array $__latestIssues = [];
|
||||
|
||||
/**
|
||||
* Holds a global/shared afterAll ("tear down after") closure if one has
|
||||
* been defined.
|
||||
* The test's PRs.
|
||||
*
|
||||
* @var Closure|null
|
||||
* @var array<int, int>
|
||||
*/
|
||||
private static $afterAll = null;
|
||||
private static array $__latestPrs = [];
|
||||
|
||||
/**
|
||||
* Creates a new instance of the test case.
|
||||
* The test's describing, if any.
|
||||
*/
|
||||
public function __construct(Closure $test, string $description, array $data)
|
||||
public ?string $__describing = null;
|
||||
|
||||
/**
|
||||
* Whether the test has ran or not.
|
||||
*/
|
||||
public bool $__ran = false;
|
||||
|
||||
/**
|
||||
* The test's test closure.
|
||||
*/
|
||||
private Closure $__test;
|
||||
|
||||
/**
|
||||
* The test's before each closure.
|
||||
*/
|
||||
private ?Closure $__beforeEach = null;
|
||||
|
||||
/**
|
||||
* The test's after each closure.
|
||||
*/
|
||||
private ?Closure $__afterEach = null;
|
||||
|
||||
/**
|
||||
* The test's before all closure.
|
||||
*/
|
||||
private static ?Closure $__beforeAll = null;
|
||||
|
||||
/**
|
||||
* The test's after all closure.
|
||||
*/
|
||||
private static ?Closure $__afterAll = null;
|
||||
|
||||
/**
|
||||
* The list of snapshot changes, if any.
|
||||
*/
|
||||
private array $__snapshotChanges = [];
|
||||
|
||||
/**
|
||||
* Creates a new Test Case instance.
|
||||
*/
|
||||
public function __construct(string $name)
|
||||
{
|
||||
$this->__test = $test;
|
||||
$this->__description = $description;
|
||||
self::$beforeAll = null;
|
||||
self::$afterAll = null;
|
||||
parent::__construct($name);
|
||||
|
||||
parent::__construct('__test', $data);
|
||||
$test = TestSuite::getInstance()->tests->get(self::$__filename);
|
||||
|
||||
if ($test->hasMethod($name)) {
|
||||
$method = $test->getMethod($name);
|
||||
$this->__description = self::$__latestDescription = $method->description;
|
||||
self::$__latestAssignees = $method->assignees;
|
||||
self::$__latestNotes = $method->notes;
|
||||
self::$__latestIssues = $method->issues;
|
||||
self::$__latestPrs = $method->prs;
|
||||
$this->__describing = $method->describing;
|
||||
$this->__test = $method->getClosure();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the groups to the current test case.
|
||||
* Resets the test case static properties.
|
||||
*/
|
||||
public function addGroups(array $groups): void
|
||||
public static function flush(): void
|
||||
{
|
||||
$groups = array_unique(array_merge($this->getGroups(), $groups));
|
||||
|
||||
$this->setGroups($groups);
|
||||
self::$__beforeAll = null;
|
||||
self::$__afterAll = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add dependencies to the test case and map them to instances of ExecutionOrderDependency.
|
||||
* Adds a new "note" to the Test Case.
|
||||
*/
|
||||
public function addDependencies(array $tests): void
|
||||
public function note(array|string $note): self
|
||||
{
|
||||
$className = get_class($this);
|
||||
$note = is_array($note) ? $note : [$note];
|
||||
|
||||
$tests = array_map(function (string $test) use ($className): ExecutionOrderDependency {
|
||||
if (strpos($test, '::') === false) {
|
||||
$test = "{$className}::{$test}";
|
||||
}
|
||||
self::$__latestNotes = array_merge(self::$__latestNotes, $note);
|
||||
|
||||
return new ExecutionOrderDependency($test, null, '');
|
||||
}, $tests);
|
||||
|
||||
$this->setDependencies($tests);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a shared/"global" before all test hook that will execute **before**
|
||||
* the test defined `beforeAll` hook(s).
|
||||
* Adds a new "setUpBeforeClass" to the Test Case.
|
||||
*/
|
||||
public function addBeforeAll(?Closure $hook): void
|
||||
public function __addBeforeAll(?Closure $hook): void
|
||||
{
|
||||
if (!$hook) {
|
||||
if (! $hook instanceof \Closure) {
|
||||
return;
|
||||
}
|
||||
|
||||
self::$beforeAll = (self::$beforeAll instanceof Closure)
|
||||
? ChainableClosure::fromStatic(self::$beforeAll, $hook)
|
||||
self::$__beforeAll = (self::$__beforeAll instanceof Closure)
|
||||
? ChainableClosure::boundStatically(self::$__beforeAll, $hook)
|
||||
: $hook;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a shared/"global" after all test hook that will execute **before**
|
||||
* the test defined `afterAll` hook(s).
|
||||
* Adds a new "tearDownAfterClass" to the Test Case.
|
||||
*/
|
||||
public function addAfterAll(?Closure $hook): void
|
||||
public function __addAfterAll(?Closure $hook): void
|
||||
{
|
||||
if (!$hook) {
|
||||
if (! $hook instanceof \Closure) {
|
||||
return;
|
||||
}
|
||||
|
||||
self::$afterAll = (self::$afterAll instanceof Closure)
|
||||
? ChainableClosure::fromStatic(self::$afterAll, $hook)
|
||||
self::$__afterAll = (self::$__afterAll instanceof Closure)
|
||||
? ChainableClosure::boundStatically(self::$__afterAll, $hook)
|
||||
: $hook;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a shared/"global" before each test hook that will execute **before**
|
||||
* the test defined `beforeEach` hook.
|
||||
* Adds a new "setUp" to the Test Case.
|
||||
*/
|
||||
public function addBeforeEach(?Closure $hook): void
|
||||
public function __addBeforeEach(?Closure $hook): void
|
||||
{
|
||||
$this->addHook('beforeEach', $hook);
|
||||
$this->__addHook('__beforeEach', $hook);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a shared/"global" after each test hook that will execute **before**
|
||||
* the test defined `afterEach` hook.
|
||||
* Adds a new "tearDown" to the Test Case.
|
||||
*/
|
||||
public function addAfterEach(?Closure $hook): void
|
||||
public function __addAfterEach(?Closure $hook): void
|
||||
{
|
||||
$this->addHook('afterEach', $hook);
|
||||
$this->__addHook('__afterEach', $hook);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a shared/global hook and compose them if more than one is passed.
|
||||
* Adds a new "hook" to the Test Case.
|
||||
*/
|
||||
private function addHook(string $property, ?Closure $hook): void
|
||||
private function __addHook(string $property, ?Closure $hook): void
|
||||
{
|
||||
if (!$hook) {
|
||||
if (! $hook instanceof \Closure) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->{$property} = ($this->{$property} instanceof Closure)
|
||||
? ChainableClosure::from($this->{$property}, $hook)
|
||||
? ChainableClosure::bound($this->{$property}, $hook)
|
||||
: $hook;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the test case name. Note that, in Pest
|
||||
* we ignore withDataset argument as the description
|
||||
* already contains the dataset description.
|
||||
*/
|
||||
public function getName(bool $withDataSet = true): string
|
||||
{
|
||||
return $this->__description;
|
||||
}
|
||||
|
||||
public static function __getFileName(): string
|
||||
{
|
||||
return self::$__filename;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called before the first test of this test class is run.
|
||||
* This method is called before the first test of this Test Case is run.
|
||||
*/
|
||||
public static function setUpBeforeClass(): void
|
||||
{
|
||||
@ -193,22 +208,22 @@ trait Testable
|
||||
|
||||
$beforeAll = TestSuite::getInstance()->beforeAll->get(self::$__filename);
|
||||
|
||||
if (self::$beforeAll instanceof Closure) {
|
||||
$beforeAll = ChainableClosure::fromStatic(self::$beforeAll, $beforeAll);
|
||||
if (self::$__beforeAll instanceof Closure) {
|
||||
$beforeAll = ChainableClosure::boundStatically(self::$__beforeAll, $beforeAll);
|
||||
}
|
||||
|
||||
call_user_func(Closure::bind($beforeAll, null, self::class));
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called after the last test of this test class is run.
|
||||
* This method is called after the last test of this Test Case is run.
|
||||
*/
|
||||
public static function tearDownAfterClass(): void
|
||||
{
|
||||
$afterAll = TestSuite::getInstance()->afterAll->get(self::$__filename);
|
||||
|
||||
if (self::$afterAll instanceof Closure) {
|
||||
$afterAll = ChainableClosure::fromStatic(self::$afterAll, $afterAll);
|
||||
if (self::$__afterAll instanceof Closure) {
|
||||
$afterAll = ChainableClosure::boundStatically(self::$__afterAll, $afterAll);
|
||||
}
|
||||
|
||||
call_user_func(Closure::bind($afterAll, null, self::class));
|
||||
@ -217,63 +232,91 @@ trait Testable
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets executed before the test.
|
||||
* Gets executed before the Test Case.
|
||||
*/
|
||||
protected function setUp(): void
|
||||
protected function setUp(...$arguments): void
|
||||
{
|
||||
TestSuite::getInstance()->test = $this;
|
||||
|
||||
parent::setUp();
|
||||
$method = TestSuite::getInstance()->tests->get(self::$__filename)->getMethod($this->name());
|
||||
|
||||
$beforeEach = TestSuite::getInstance()->beforeEach->get(self::$__filename);
|
||||
$method->setUp($this);
|
||||
|
||||
if ($this->beforeEach instanceof Closure) {
|
||||
$beforeEach = ChainableClosure::from($this->beforeEach, $beforeEach);
|
||||
$description = $method->description;
|
||||
if ($this->dataName()) {
|
||||
$description = str_contains((string) $description, ':dataset')
|
||||
? str_replace(':dataset', str_replace('dataset ', '', $this->dataName()), (string) $description)
|
||||
: $description.' with '.$this->dataName();
|
||||
}
|
||||
|
||||
$this->__callClosure($beforeEach, func_get_args());
|
||||
$description = htmlspecialchars(html_entity_decode((string) $description), ENT_NOQUOTES);
|
||||
|
||||
if ($method->repetitions > 1) {
|
||||
$matches = [];
|
||||
preg_match('/\((.*?)\)/', $description, $matches);
|
||||
|
||||
if (count($matches) > 1) {
|
||||
if (str_contains($description, 'with '.$matches[0].' /')) {
|
||||
$description = str_replace('with '.$matches[0].' /', '', $description);
|
||||
} else {
|
||||
$description = str_replace('with '.$matches[0], '', $description);
|
||||
}
|
||||
}
|
||||
|
||||
$description .= ' @ repetition '.($matches[1].' of '.$method->repetitions);
|
||||
}
|
||||
|
||||
$this->__description = self::$__latestDescription = $description;
|
||||
self::$__latestAssignees = $method->assignees;
|
||||
self::$__latestNotes = $method->notes;
|
||||
self::$__latestIssues = $method->issues;
|
||||
self::$__latestPrs = $method->prs;
|
||||
|
||||
parent::setUp();
|
||||
|
||||
$beforeEach = TestSuite::getInstance()->beforeEach->get(self::$__filename)[1];
|
||||
|
||||
if ($this->__beforeEach instanceof Closure) {
|
||||
$beforeEach = ChainableClosure::bound($this->__beforeEach, $beforeEach);
|
||||
}
|
||||
|
||||
$this->__callClosure($beforeEach, $arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets executed after the test.
|
||||
* Gets executed after the Test Case.
|
||||
*/
|
||||
protected function tearDown(): void
|
||||
protected function tearDown(...$arguments): void
|
||||
{
|
||||
$afterEach = TestSuite::getInstance()->afterEach->get(self::$__filename);
|
||||
|
||||
if ($this->afterEach instanceof Closure) {
|
||||
$afterEach = ChainableClosure::from($this->afterEach, $afterEach);
|
||||
if ($this->__afterEach instanceof Closure) {
|
||||
$afterEach = ChainableClosure::bound($this->__afterEach, $afterEach);
|
||||
}
|
||||
|
||||
try {
|
||||
$this->__callClosure($afterEach, func_get_args());
|
||||
|
||||
} finally {
|
||||
parent::tearDown();
|
||||
|
||||
TestSuite::getInstance()->test = null;
|
||||
|
||||
$method = TestSuite::getInstance()->tests->get(self::$__filename)->getMethod($this->name());
|
||||
$method->tearDown($this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the test case as string.
|
||||
*/
|
||||
public function toString(): string
|
||||
{
|
||||
return \sprintf(
|
||||
'%s::%s',
|
||||
self::$__filename,
|
||||
$this->__description
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the test.
|
||||
*
|
||||
* @return mixed
|
||||
* Executes the Test Case current test.
|
||||
*
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function __test()
|
||||
private function __runTest(Closure $closure, ...$args): mixed
|
||||
{
|
||||
return $this->__callClosure($this->__test, $this->resolveTestArguments(func_get_args()));
|
||||
$arguments = $this->__resolveTestArguments($args);
|
||||
$this->__ensureDatasetArgumentNameAndNumberMatches($arguments);
|
||||
|
||||
return $this->__callClosure($closure, $arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -281,27 +324,159 @@ trait Testable
|
||||
*
|
||||
* @throws Throwable
|
||||
*/
|
||||
private function resolveTestArguments(array $arguments): array
|
||||
private function __resolveTestArguments(array $arguments): array
|
||||
{
|
||||
return array_map(function ($data) {
|
||||
return $data instanceof Closure ? $this->__callClosure($data, []) : $data;
|
||||
}, $arguments);
|
||||
$method = TestSuite::getInstance()->tests->get(self::$__filename)->getMethod($this->name());
|
||||
|
||||
if ($method->repetitions > 1) {
|
||||
// If the test is repeated, the first argument is the iteration number
|
||||
// we need to move it to the end of the arguments list
|
||||
// so that the datasets are the first n arguments
|
||||
// and the iteration number is the last argument
|
||||
$firstArgument = array_shift($arguments);
|
||||
$arguments[] = $firstArgument;
|
||||
}
|
||||
|
||||
$underlyingTest = Reflection::getFunctionVariable($this->__test, 'closure');
|
||||
$testParameterTypes = array_values(Reflection::getFunctionArguments($underlyingTest));
|
||||
|
||||
if (count($arguments) !== 1) {
|
||||
foreach ($arguments as $argumentIndex => $argumentValue) {
|
||||
if (! $argumentValue instanceof Closure) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (in_array($testParameterTypes[$argumentIndex], [Closure::class, 'callable', 'mixed'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$arguments[$argumentIndex] = $this->__callClosure($argumentValue, []);
|
||||
}
|
||||
|
||||
return $arguments;
|
||||
}
|
||||
|
||||
if (! isset($arguments[0]) || ! $arguments[0] instanceof Closure) {
|
||||
return $arguments;
|
||||
}
|
||||
|
||||
if (isset($testParameterTypes[0]) && in_array($testParameterTypes[0], [Closure::class, 'callable'])) {
|
||||
return $arguments;
|
||||
}
|
||||
|
||||
$boundDatasetResult = $this->__callClosure($arguments[0], []);
|
||||
if (count($testParameterTypes) === 1) {
|
||||
return [$boundDatasetResult];
|
||||
}
|
||||
if (! is_array($boundDatasetResult)) {
|
||||
return [$boundDatasetResult];
|
||||
}
|
||||
|
||||
return array_values($boundDatasetResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
* Ensures dataset items count matches underlying test case required parameters
|
||||
*
|
||||
* @throws Throwable
|
||||
* @throws ReflectionException
|
||||
* @throws DatasetArgumentsMismatch
|
||||
*/
|
||||
private function __callClosure(Closure $closure, array $arguments)
|
||||
private function __ensureDatasetArgumentNameAndNumberMatches(array $arguments): void
|
||||
{
|
||||
return ExceptionTrace::ensure(function () use ($closure, $arguments) {
|
||||
return call_user_func_array(Closure::bind($closure, $this, get_class($this)), $arguments);
|
||||
});
|
||||
if ($arguments === []) {
|
||||
return;
|
||||
}
|
||||
|
||||
public function getPrintableTestCaseName(): string
|
||||
$underlyingTest = Reflection::getFunctionVariable($this->__test, 'closure');
|
||||
$testReflection = new ReflectionFunction($underlyingTest);
|
||||
$requiredParametersCount = $testReflection->getNumberOfRequiredParameters();
|
||||
$suppliedParametersCount = count($arguments);
|
||||
|
||||
$datasetParameterNames = array_keys($arguments);
|
||||
$testParameterNames = array_map(
|
||||
fn (ReflectionParameter $reflectionParameter): string => $reflectionParameter->getName(),
|
||||
array_filter($testReflection->getParameters(), fn (ReflectionParameter $reflectionParameter): bool => ! $reflectionParameter->isOptional()),
|
||||
);
|
||||
|
||||
if (array_diff($testParameterNames, $datasetParameterNames) === []) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isset($testParameterNames[0]) && $suppliedParametersCount >= $requiredParametersCount) {
|
||||
return;
|
||||
}
|
||||
|
||||
throw new DatasetArgumentsMismatch($requiredParametersCount, $suppliedParametersCount);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Throwable
|
||||
*/
|
||||
private function __callClosure(Closure $closure, array $arguments): mixed
|
||||
{
|
||||
return ltrim(self::class, 'P\\');
|
||||
return ExceptionTrace::ensure(fn (): mixed => call_user_func_array(Closure::bind($closure, $this, $this::class), $arguments));
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses the given preset on the test.
|
||||
*/
|
||||
public function preset(): Preset
|
||||
{
|
||||
return new Preset;
|
||||
}
|
||||
|
||||
#[PostCondition]
|
||||
protected function __MarkTestIncompleteIfSnapshotHaveChanged(): void
|
||||
{
|
||||
if (count($this->__snapshotChanges) === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (count($this->__snapshotChanges) === 1) {
|
||||
$this->markTestIncomplete($this->__snapshotChanges[0]);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$messages = implode(PHP_EOL, array_map(static fn (string $message): string => '- $message', $this->__snapshotChanges));
|
||||
|
||||
$this->markTestIncomplete($messages);
|
||||
}
|
||||
|
||||
/**
|
||||
* The printable test case name.
|
||||
*/
|
||||
public static function getPrintableTestCaseName(): string
|
||||
{
|
||||
return preg_replace('/P\\\/', '', self::class, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* The printable test case method name.
|
||||
*/
|
||||
public function getPrintableTestCaseMethodName(): string
|
||||
{
|
||||
return $this->__description;
|
||||
}
|
||||
|
||||
/**
|
||||
* The latest printable test case method name.
|
||||
*/
|
||||
public static function getLatestPrintableTestCaseMethodName(): string
|
||||
{
|
||||
return self::$__latestDescription;
|
||||
}
|
||||
|
||||
/**
|
||||
* The printable test case method context.
|
||||
*/
|
||||
public static function getPrintableContext(): array
|
||||
{
|
||||
return [
|
||||
'assignees' => self::$__latestAssignees,
|
||||
'issues' => self::$__latestIssues,
|
||||
'prs' => self::$__latestPrs,
|
||||
'notes' => self::$__latestNotes,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
114
src/Configuration.php
Normal file
114
src/Configuration.php
Normal file
@ -0,0 +1,114 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest;
|
||||
|
||||
use Pest\PendingCalls\UsesCall;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @mixin UsesCall
|
||||
*/
|
||||
final readonly class Configuration
|
||||
{
|
||||
/**
|
||||
* The filename of the configuration.
|
||||
*/
|
||||
private string $filename;
|
||||
|
||||
/**
|
||||
* Creates a new configuration instance.
|
||||
*/
|
||||
public function __construct(
|
||||
string $filename,
|
||||
) {
|
||||
$this->filename = str_ends_with($filename, DIRECTORY_SEPARATOR.'Pest.php') ? dirname($filename) : $filename;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use the given classes and traits in the given targets.
|
||||
*/
|
||||
public function in(string ...$targets): UsesCall
|
||||
{
|
||||
return (new UsesCall($this->filename, []))->in(...$targets);
|
||||
}
|
||||
|
||||
/**
|
||||
* Depending on where is called, it will extend the given classes and traits globally or locally.
|
||||
*/
|
||||
public function extend(string ...$classAndTraits): UsesCall
|
||||
{
|
||||
return new UsesCall(
|
||||
$this->filename,
|
||||
array_values($classAndTraits)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Depending on where is called, it will extend the given classes and traits globally or locally.
|
||||
*/
|
||||
public function extends(string ...$classAndTraits): UsesCall
|
||||
{
|
||||
return $this->extend(...$classAndTraits);
|
||||
}
|
||||
|
||||
/**
|
||||
* Depending on where is called, it will add the given groups globally or locally.
|
||||
*/
|
||||
public function group(string ...$groups): UsesCall
|
||||
{
|
||||
return (new UsesCall($this->filename, []))->group(...$groups);
|
||||
}
|
||||
|
||||
/**
|
||||
* Depending on where is called, it will extend the given classes and traits globally or locally.
|
||||
*/
|
||||
public function use(string ...$classAndTraits): UsesCall
|
||||
{
|
||||
return $this->extend(...$classAndTraits);
|
||||
}
|
||||
|
||||
/**
|
||||
* Depending on where is called, it will extend the given classes and traits globally or locally.
|
||||
*/
|
||||
public function uses(string ...$classAndTraits): UsesCall
|
||||
{
|
||||
return $this->extends(...$classAndTraits);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the printer configuration.
|
||||
*/
|
||||
public function printer(): Configuration\Printer
|
||||
{
|
||||
return new Configuration\Printer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the presets configuration.
|
||||
*/
|
||||
public function presets(): Configuration\Presets
|
||||
{
|
||||
return new Configuration\Presets;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the project configuration.
|
||||
*/
|
||||
public function project(): Configuration\Project
|
||||
{
|
||||
return Configuration\Project::getInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Proxies calls to the uses method.
|
||||
*
|
||||
* @param array<array-key, mixed> $arguments
|
||||
*/
|
||||
public function __call(string $name, array $arguments): mixed
|
||||
{
|
||||
return $this->uses()->$name(...$arguments); // @phpstan-ignore-line
|
||||
}
|
||||
}
|
||||
19
src/Configuration/Presets.php
Normal file
19
src/Configuration/Presets.php
Normal file
@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\Configuration;
|
||||
|
||||
use Closure;
|
||||
use Pest\Preset;
|
||||
|
||||
final class Presets
|
||||
{
|
||||
/**
|
||||
* Creates a custom preset instance, and adds it to the list of presets.
|
||||
*/
|
||||
public function custom(string $name, Closure $execute): void
|
||||
{
|
||||
Preset::custom($name, $execute);
|
||||
}
|
||||
}
|
||||
23
src/Configuration/Printer.php
Normal file
23
src/Configuration/Printer.php
Normal file
@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\Configuration;
|
||||
|
||||
use NunoMaduro\Collision\Adapters\Phpunit\Printers\DefaultPrinter;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final readonly class Printer
|
||||
{
|
||||
/**
|
||||
* Sets the theme to compact.
|
||||
*/
|
||||
public function compact(): self
|
||||
{
|
||||
DefaultPrinter::compact(true);
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
109
src/Configuration/Project.php
Normal file
109
src/Configuration/Project.php
Normal file
@ -0,0 +1,109 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\Configuration;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class Project
|
||||
{
|
||||
/**
|
||||
* The assignees link.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public string $assignees = '';
|
||||
|
||||
/**
|
||||
* The issues link.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public string $issues = '';
|
||||
|
||||
/**
|
||||
* The PRs link.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public string $prs = '';
|
||||
|
||||
/**
|
||||
* The singleton instance.
|
||||
*/
|
||||
private static ?self $instance = null;
|
||||
|
||||
/**
|
||||
* Creates a new instance of the project.
|
||||
*/
|
||||
public static function getInstance(): self
|
||||
{
|
||||
return self::$instance ??= new self;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the test project to GitHub.
|
||||
*/
|
||||
public function github(string $project): self
|
||||
{
|
||||
$this->issues = "https://github.com/{$project}/issues/%s";
|
||||
$this->prs = "https://github.com/{$project}/pull/%s";
|
||||
|
||||
$this->assignees = 'https://github.com/%s';
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the test project to GitLab.
|
||||
*/
|
||||
public function gitlab(string $project): self
|
||||
{
|
||||
$this->issues = "https://gitlab.com/{$project}/issues/%s";
|
||||
$this->prs = "https://gitlab.com/{$project}/merge_requests/%s";
|
||||
|
||||
$this->assignees = 'https://gitlab.com/%s';
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the test project to Bitbucket.
|
||||
*/
|
||||
public function bitbucket(string $project): self
|
||||
{
|
||||
$this->issues = "https://bitbucket.org/{$project}/issues/%s";
|
||||
$this->prs = "https://bitbucket.org/{$project}/pull-requests/%s";
|
||||
|
||||
$this->assignees = 'https://bitbucket.org/%s';
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the test project to Jira.
|
||||
*/
|
||||
public function jira(string $namespace, string $project): self
|
||||
{
|
||||
$this->issues = "https://{$namespace}.atlassian.net/browse/{$project}-%s";
|
||||
|
||||
$this->assignees = "https://{$namespace}.atlassian.net/secure/ViewProfile?name=%s";
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the test project to custom.
|
||||
*/
|
||||
public function custom(string $issues, string $prs, string $assignees): self
|
||||
{
|
||||
$this->issues = $issues;
|
||||
$this->prs = $prs;
|
||||
|
||||
$this->assignees = $assignees;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
@ -1,132 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\Console;
|
||||
|
||||
use Pest\Actions\AddsDefaults;
|
||||
use Pest\Actions\AddsTests;
|
||||
use Pest\Actions\InteractsWithPlugins;
|
||||
use Pest\Actions\LoadStructure;
|
||||
use Pest\Actions\ValidatesConfiguration;
|
||||
use Pest\Plugins\Version;
|
||||
use Pest\Support\Container;
|
||||
use Pest\TestSuite;
|
||||
use PHPUnit\Framework\TestSuite as BaseTestSuite;
|
||||
use PHPUnit\TextUI\Command as BaseCommand;
|
||||
use PHPUnit\TextUI\TestRunner;
|
||||
use SebastianBergmann\FileIterator\Facade as FileIteratorFacade;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class Command extends BaseCommand
|
||||
{
|
||||
/**
|
||||
* Holds the current testing suite.
|
||||
*
|
||||
* @var TestSuite
|
||||
*/
|
||||
private $testSuite;
|
||||
|
||||
/**
|
||||
* Holds the current console output.
|
||||
*
|
||||
* @var OutputInterface
|
||||
*/
|
||||
private $output;
|
||||
|
||||
/**
|
||||
* Creates a new instance of the command class.
|
||||
*/
|
||||
public function __construct(TestSuite $testSuite, OutputInterface $output)
|
||||
{
|
||||
$this->testSuite = $testSuite;
|
||||
$this->output = $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @phpstan-ignore-next-line
|
||||
*
|
||||
* @param array<int, string> $argv
|
||||
*/
|
||||
protected function handleArguments(array $argv): void
|
||||
{
|
||||
$argv = InteractsWithPlugins::handleArguments($argv);
|
||||
|
||||
parent::handleArguments($argv);
|
||||
|
||||
/*
|
||||
* Let's validate the configuration. Making
|
||||
* sure all options are yet supported by Pest.
|
||||
*/
|
||||
ValidatesConfiguration::in($this->arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new PHPUnit test runner.
|
||||
*/
|
||||
protected function createRunner(): TestRunner
|
||||
{
|
||||
/*
|
||||
* First, let's add the defaults we use on `pest`. Those
|
||||
* are the printer class, and others that may be appear.
|
||||
*/
|
||||
$this->arguments = AddsDefaults::to($this->arguments);
|
||||
|
||||
$testRunner = new TestRunner($this->arguments['loader']);
|
||||
$testSuite = $this->arguments['test'];
|
||||
|
||||
if (is_string($testSuite)) {
|
||||
if (\is_dir($testSuite)) {
|
||||
/** @var string[] $files */
|
||||
$files = (new FileIteratorFacade())->getFilesAsArray(
|
||||
$testSuite,
|
||||
$this->arguments['testSuffixes']
|
||||
);
|
||||
} else {
|
||||
$files = [$testSuite];
|
||||
}
|
||||
|
||||
$testSuite = new BaseTestSuite($testSuite);
|
||||
|
||||
$testSuite->addTestFiles($files);
|
||||
|
||||
$this->arguments['test'] = $testSuite;
|
||||
}
|
||||
|
||||
AddsTests::to($testSuite, $this->testSuite);
|
||||
|
||||
return $testRunner;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @phpstan-ignore-next-line
|
||||
*
|
||||
* @param array<int, string> $argv
|
||||
*/
|
||||
public function run(array $argv, bool $exit = true): int
|
||||
{
|
||||
LoadStructure::in($this->testSuite->rootPath);
|
||||
|
||||
$result = parent::run($argv, false);
|
||||
$result = InteractsWithPlugins::addOutput($result);
|
||||
|
||||
exit($result);
|
||||
}
|
||||
|
||||
protected function showHelp(): void
|
||||
{
|
||||
/** @var Version $version */
|
||||
$version = Container::getInstance()->get(Version::class);
|
||||
$version->handleArguments(['--version']);
|
||||
parent::showHelp();
|
||||
|
||||
(new Help($this->output))();
|
||||
}
|
||||
}
|
||||
@ -9,9 +9,13 @@ use Symfony\Component\Console\Output\OutputInterface;
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class Help
|
||||
final readonly class Help
|
||||
{
|
||||
/** @var array<int, string> */
|
||||
/**
|
||||
* The Command messages.
|
||||
*
|
||||
* @var array<int, string>
|
||||
*/
|
||||
private const HELP_MESSAGES = [
|
||||
'<comment>Pest Options:</comment>',
|
||||
' <info>--init</info> Initialise a standard Pest configuration',
|
||||
@ -20,14 +24,17 @@ final class Help
|
||||
' <info>--group=<fg=cyan><name></></info> Only runs tests from the specified group(s)',
|
||||
];
|
||||
|
||||
/** @var OutputInterface */
|
||||
private $output;
|
||||
|
||||
public function __construct(OutputInterface $output)
|
||||
/**
|
||||
* Creates a new Console Command instance.
|
||||
*/
|
||||
public function __construct(private OutputInterface $output)
|
||||
{
|
||||
$this->output = $output;
|
||||
// ..
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the Console Command.
|
||||
*/
|
||||
public function __invoke(): void
|
||||
{
|
||||
foreach (self::HELP_MESSAGES as $message) {
|
||||
|
||||
@ -4,63 +4,83 @@ declare(strict_types=1);
|
||||
|
||||
namespace Pest\Console;
|
||||
|
||||
use Pest\Bootstrappers\BootView;
|
||||
use Pest\Support\View;
|
||||
use Symfony\Component\Console\Helper\SymfonyQuestionHelper;
|
||||
use Symfony\Component\Console\Input\ArrayInput;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Question\ConfirmationQuestion;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class Thanks
|
||||
final readonly class Thanks
|
||||
{
|
||||
/** @var array<int, string> */
|
||||
/**
|
||||
* The support options.
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
private const FUNDING_MESSAGES = [
|
||||
'',
|
||||
' - Star or contribute to Pest:',
|
||||
' <options=bold>https://github.com/pestphp/pest</>',
|
||||
' - Tweet something about Pest on Twitter:',
|
||||
' <options=bold>https://twitter.com/pestphp</>',
|
||||
' - Sponsor the creator:',
|
||||
' <options=bold>https://github.com/sponsors/nunomaduro</>',
|
||||
'Star' => 'https://github.com/pestphp/pest',
|
||||
'News' => 'https://twitter.com/pestphp',
|
||||
'Videos' => 'https://youtube.com/@nunomaduro',
|
||||
'Sponsor' => 'https://github.com/sponsors/nunomaduro',
|
||||
];
|
||||
|
||||
/** @var OutputInterface */
|
||||
private $output;
|
||||
|
||||
public function __construct(OutputInterface $output)
|
||||
{
|
||||
$this->output = $output;
|
||||
/**
|
||||
* Creates a new Console Command instance.
|
||||
*/
|
||||
public function __construct(
|
||||
private InputInterface $input,
|
||||
private OutputInterface $output
|
||||
) {
|
||||
// ..
|
||||
}
|
||||
|
||||
/**
|
||||
* Asks the user to support Pest.
|
||||
* Executes the Console Command.
|
||||
*/
|
||||
public function __invoke(): void
|
||||
{
|
||||
$wantsToSupport = (new SymfonyQuestionHelper())->ask(
|
||||
$bootstrapper = new BootView($this->output);
|
||||
$bootstrapper->boot();
|
||||
|
||||
$wantsToSupport = false;
|
||||
|
||||
if (getenv('PEST_NO_SUPPORT') !== 'true' && $this->input->isInteractive()) {
|
||||
$wantsToSupport = (new SymfonyQuestionHelper)->ask(
|
||||
new ArrayInput([]),
|
||||
$this->output,
|
||||
new ConfirmationQuestion(
|
||||
'Can you quickly <options=bold>star our GitHub repository</>? 🙏🏻',
|
||||
true,
|
||||
' <options=bold>Wanna show Pest some love by starring it on GitHub?</>',
|
||||
false,
|
||||
)
|
||||
);
|
||||
|
||||
View::render('components.new-line');
|
||||
|
||||
foreach (self::FUNDING_MESSAGES as $message => $link) {
|
||||
View::render('components.two-column-detail', [
|
||||
'left' => $message,
|
||||
'right' => $link,
|
||||
]);
|
||||
}
|
||||
|
||||
View::render('components.new-line');
|
||||
}
|
||||
|
||||
if ($wantsToSupport === true) {
|
||||
if (PHP_OS_FAMILY == 'Darwin') {
|
||||
if (PHP_OS_FAMILY === 'Darwin') {
|
||||
exec('open https://github.com/pestphp/pest');
|
||||
}
|
||||
if (PHP_OS_FAMILY == 'Windows') {
|
||||
if (PHP_OS_FAMILY === 'Windows') {
|
||||
exec('start https://github.com/pestphp/pest');
|
||||
}
|
||||
if (PHP_OS_FAMILY == 'Linux') {
|
||||
if (PHP_OS_FAMILY === 'Linux') {
|
||||
exec('xdg-open https://github.com/pestphp/pest');
|
||||
}
|
||||
}
|
||||
|
||||
foreach (self::FUNDING_MESSAGES as $message) {
|
||||
$this->output->writeln($message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
10
src/Contracts/ArchPreset.php
Normal file
10
src/Contracts/ArchPreset.php
Normal file
@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\Contracts;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
interface ArchPreset {}
|
||||
16
src/Contracts/Bootstrapper.php
Normal file
16
src/Contracts/Bootstrapper.php
Normal file
@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\Contracts;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
interface Bootstrapper
|
||||
{
|
||||
/**
|
||||
* Boots the bootstrapper.
|
||||
*/
|
||||
public function boot(): void;
|
||||
}
|
||||
@ -4,18 +4,12 @@ declare(strict_types=1);
|
||||
|
||||
namespace Pest\Contracts;
|
||||
|
||||
if (interface_exists(\NunoMaduro\Collision\Contracts\Adapters\Phpunit\HasPrintableTestCaseName::class)) {
|
||||
use NunoMaduro\Collision\Contracts\Adapters\Phpunit\HasPrintableTestCaseName as BaseHasPrintableTestCaseName;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
interface HasPrintableTestCaseName extends \NunoMaduro\Collision\Contracts\Adapters\Phpunit\HasPrintableTestCaseName
|
||||
interface HasPrintableTestCaseName extends BaseHasPrintableTestCaseName
|
||||
{
|
||||
}
|
||||
} else {
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
interface HasPrintableTestCaseName
|
||||
{
|
||||
}
|
||||
// ..
|
||||
}
|
||||
|
||||
23
src/Contracts/Panicable.php
Normal file
23
src/Contracts/Panicable.php
Normal file
@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\Contracts;
|
||||
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
interface Panicable
|
||||
{
|
||||
/**
|
||||
* Renders the panic on the given output.
|
||||
*/
|
||||
public function render(OutputInterface $output): void;
|
||||
|
||||
/**
|
||||
* The exit code to be used.
|
||||
*/
|
||||
public function exitCode(): int;
|
||||
}
|
||||
@ -10,7 +10,7 @@ namespace Pest\Contracts\Plugins;
|
||||
interface AddsOutput
|
||||
{
|
||||
/**
|
||||
* Allows to add custom output after the test suite was executed.
|
||||
* Adds output after the Test Suite execution.
|
||||
*/
|
||||
public function addOutput(int $testReturnCode): int;
|
||||
public function addOutput(int $exitCode): int;
|
||||
}
|
||||
|
||||
16
src/Contracts/Plugins/Bootable.php
Normal file
16
src/Contracts/Plugins/Bootable.php
Normal file
@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\Contracts\Plugins;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
interface Bootable
|
||||
{
|
||||
/**
|
||||
* Boots the plugin.
|
||||
*/
|
||||
public function boot(): void;
|
||||
}
|
||||
@ -10,14 +10,10 @@ namespace Pest\Contracts\Plugins;
|
||||
interface HandlesArguments
|
||||
{
|
||||
/**
|
||||
* Allows to handle custom command line arguments.
|
||||
*
|
||||
* PLEASE NOTE: it is necessary to remove any custom argument from the array
|
||||
* because otherwise the application will complain about them
|
||||
* Adds arguments before the Test Suite execution.
|
||||
*
|
||||
* @param array<int, string> $arguments
|
||||
*
|
||||
* @return array<int, string> the updated list of arguments
|
||||
* @return array<int, string>
|
||||
*/
|
||||
public function handleArguments(array $arguments): array;
|
||||
}
|
||||
|
||||
18
src/Contracts/Plugins/HandlesOriginalArguments.php
Normal file
18
src/Contracts/Plugins/HandlesOriginalArguments.php
Normal file
@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\Contracts\Plugins;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
interface HandlesOriginalArguments
|
||||
{
|
||||
/**
|
||||
* Adds original arguments before the Test Suite execution.
|
||||
*
|
||||
* @param array<int, string> $arguments
|
||||
*/
|
||||
public function handleOriginalArguments(array $arguments): void;
|
||||
}
|
||||
16
src/Contracts/Plugins/Terminable.php
Normal file
16
src/Contracts/Plugins/Terminable.php
Normal file
@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\Contracts\Plugins;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
interface Terminable
|
||||
{
|
||||
/**
|
||||
* Terminates the plugin.
|
||||
*/
|
||||
public function terminate(): void;
|
||||
}
|
||||
13
src/Contracts/TestCaseFilter.php
Normal file
13
src/Contracts/TestCaseFilter.php
Normal file
@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\Contracts;
|
||||
|
||||
interface TestCaseFilter
|
||||
{
|
||||
/**
|
||||
* Whether the test case is accepted.
|
||||
*/
|
||||
public function accept(string $testCaseFilename): bool;
|
||||
}
|
||||
15
src/Contracts/TestCaseMethodFilter.php
Normal file
15
src/Contracts/TestCaseMethodFilter.php
Normal file
@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\Contracts;
|
||||
|
||||
use Pest\Factories\TestCaseMethodFactory;
|
||||
|
||||
interface TestCaseMethodFilter
|
||||
{
|
||||
/**
|
||||
* Whether the test case method is accepted.
|
||||
*/
|
||||
public function accept(TestCaseMethodFactory $factory): bool;
|
||||
}
|
||||
177
src/Datasets.php
177
src/Datasets.php
@ -1,177 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest;
|
||||
|
||||
use Closure;
|
||||
use Pest\Exceptions\DatasetAlreadyExist;
|
||||
use Pest\Exceptions\DatasetDoesNotExist;
|
||||
use SebastianBergmann\Exporter\Exporter;
|
||||
use Traversable;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class Datasets
|
||||
{
|
||||
/**
|
||||
* Holds the datasets.
|
||||
*
|
||||
* @var array<int|string, Closure|iterable<int|string, mixed>>
|
||||
*/
|
||||
private static $datasets = [];
|
||||
|
||||
/**
|
||||
* Sets the given.
|
||||
*
|
||||
* @param Closure|iterable<int|string, mixed> $data
|
||||
*/
|
||||
public static function set(string $name, $data): void
|
||||
{
|
||||
if (array_key_exists($name, self::$datasets)) {
|
||||
throw new DatasetAlreadyExist($name);
|
||||
}
|
||||
|
||||
self::$datasets[$name] = $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Closure|iterable<int|string, mixed>
|
||||
*/
|
||||
public static function get(string $name)
|
||||
{
|
||||
if (!array_key_exists($name, self::$datasets)) {
|
||||
throw new DatasetDoesNotExist($name);
|
||||
}
|
||||
|
||||
return self::$datasets[$name];
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the current dataset to an array value.
|
||||
*
|
||||
* @param array<Closure|iterable<int|string, mixed>|string> $datasets
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public static function resolve(string $description, array $datasets): array
|
||||
{
|
||||
/* @phpstan-ignore-next-line */
|
||||
if (empty($datasets)) {
|
||||
return [$description => []];
|
||||
}
|
||||
|
||||
$datasets = self::processDatasets($datasets);
|
||||
|
||||
$datasetCombinations = self::getDataSetsCombinations($datasets);
|
||||
|
||||
$dataSetDescriptions = [];
|
||||
$dataSetValues = [];
|
||||
|
||||
foreach ($datasetCombinations as $datasetCombination) {
|
||||
$partialDescriptions = [];
|
||||
$values = [];
|
||||
|
||||
foreach ($datasetCombination as $dataset_data) {
|
||||
$partialDescriptions[] = $dataset_data['label'];
|
||||
$values = array_merge($values, $dataset_data['values']);
|
||||
}
|
||||
|
||||
$dataSetDescriptions[] = $description . ' with ' . implode(' / ', $partialDescriptions);
|
||||
$dataSetValues[] = $values;
|
||||
}
|
||||
|
||||
foreach (array_count_values($dataSetDescriptions) as $descriptionToCheck => $count) {
|
||||
if ($count > 1) {
|
||||
$index = 1;
|
||||
foreach ($dataSetDescriptions as $i => $dataSetDescription) {
|
||||
if ($dataSetDescription === $descriptionToCheck) {
|
||||
$dataSetDescriptions[$i] .= sprintf(' #%d', $index++);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$namedData = [];
|
||||
foreach ($dataSetDescriptions as $i => $dataSetDescription) {
|
||||
$namedData[$dataSetDescription] = $dataSetValues[$i];
|
||||
}
|
||||
|
||||
return $namedData;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<Closure|iterable<int|string, mixed>|string> $datasets
|
||||
*
|
||||
* @return array<array>
|
||||
*/
|
||||
private static function processDatasets(array $datasets): array
|
||||
{
|
||||
$processedDatasets = [];
|
||||
|
||||
foreach ($datasets as $index => $data) {
|
||||
$processedDataset = [];
|
||||
|
||||
if (is_string($data)) {
|
||||
$datasets[$index] = self::get($data);
|
||||
}
|
||||
|
||||
if (is_callable($datasets[$index])) {
|
||||
$datasets[$index] = call_user_func($datasets[$index]);
|
||||
}
|
||||
|
||||
if ($datasets[$index] instanceof Traversable) {
|
||||
$datasets[$index] = iterator_to_array($datasets[$index]);
|
||||
}
|
||||
|
||||
foreach ($datasets[$index] as $key => $values) {
|
||||
$values = is_array($values) ? $values : [$values];
|
||||
$processedDataset[] = [
|
||||
'label' => self::getDataSetDescription($key, $values),
|
||||
'values' => $values,
|
||||
];
|
||||
}
|
||||
|
||||
$processedDatasets[] = $processedDataset;
|
||||
}
|
||||
|
||||
return $processedDatasets;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<array> $combinations
|
||||
*
|
||||
* @return array<array>
|
||||
*/
|
||||
private static function getDataSetsCombinations(array $combinations): array
|
||||
{
|
||||
$result = [[]];
|
||||
foreach ($combinations as $index => $values) {
|
||||
$tmp = [];
|
||||
foreach ($result as $resultItem) {
|
||||
foreach ($values as $value) {
|
||||
$tmp[] = array_merge($resultItem, [$index => $value]);
|
||||
}
|
||||
}
|
||||
$result = $tmp;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int|string $key
|
||||
* @param array<int, mixed> $data
|
||||
*/
|
||||
private static function getDataSetDescription($key, array $data): string
|
||||
{
|
||||
$exporter = new Exporter();
|
||||
|
||||
if (is_int($key)) {
|
||||
return \sprintf('(%s)', $exporter->shortenedRecursiveExport($data));
|
||||
}
|
||||
|
||||
return \sprintf('data set "%s"', $key);
|
||||
}
|
||||
}
|
||||
33
src/Evaluators/Attributes.php
Normal file
33
src/Evaluators/Attributes.php
Normal file
@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\Evaluators;
|
||||
|
||||
use Pest\Factories\Attribute;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class Attributes
|
||||
{
|
||||
/**
|
||||
* Evaluates the given attributes and returns the code.
|
||||
*
|
||||
* @param iterable<int, Attribute> $attributes
|
||||
*/
|
||||
public static function code(iterable $attributes): string
|
||||
{
|
||||
return implode(PHP_EOL, array_map(function (Attribute $attribute): string {
|
||||
$name = $attribute->name;
|
||||
|
||||
if ($attribute->arguments === []) {
|
||||
return " #[\\{$name}]";
|
||||
}
|
||||
|
||||
$arguments = array_map(fn (string $argument): string => var_export($argument, true), iterator_to_array($attribute->arguments));
|
||||
|
||||
return sprintf(' #[\\%s(%s)]', $name, implode(', ', $arguments));
|
||||
}, iterator_to_array($attributes)));
|
||||
}
|
||||
}
|
||||
@ -15,10 +15,10 @@ use Symfony\Component\Console\Exception\ExceptionInterface;
|
||||
final class AfterAllAlreadyExist extends InvalidArgumentException implements ExceptionInterface, RenderlessEditor, RenderlessTrace
|
||||
{
|
||||
/**
|
||||
* Creates a new instance of after all already exist exception.
|
||||
* Creates a new Exception instance.
|
||||
*/
|
||||
public function __construct(string $filename)
|
||||
{
|
||||
parent::__construct(sprintf('The afterAll already exist in the filename `%s`.', $filename));
|
||||
parent::__construct(sprintf('The afterAll already exists in the filename `%s`.', $filename));
|
||||
}
|
||||
}
|
||||
|
||||
@ -12,13 +12,13 @@ use Symfony\Component\Console\Exception\ExceptionInterface;
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class BeforeEachAlreadyExist extends InvalidArgumentException implements ExceptionInterface, RenderlessEditor, RenderlessTrace
|
||||
final class AfterAllWithinDescribe extends InvalidArgumentException implements ExceptionInterface, RenderlessEditor, RenderlessTrace
|
||||
{
|
||||
/**
|
||||
* Creates a new instance of before each already exist exception.
|
||||
* Creates a new Exception instance.
|
||||
*/
|
||||
public function __construct(string $filename)
|
||||
{
|
||||
parent::__construct(sprintf('The beforeEach already exist in the filename `%s`.', $filename));
|
||||
parent::__construct(sprintf('The afterAll method can not be used within describe functions. Filename `%s`.', $filename));
|
||||
}
|
||||
}
|
||||
24
src/Exceptions/AfterBeforeTestFunction.php
Normal file
24
src/Exceptions/AfterBeforeTestFunction.php
Normal file
@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\Exceptions;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use NunoMaduro\Collision\Contracts\RenderlessEditor;
|
||||
use NunoMaduro\Collision\Contracts\RenderlessTrace;
|
||||
use Symfony\Component\Console\Exception\ExceptionInterface;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class AfterBeforeTestFunction extends InvalidArgumentException implements ExceptionInterface, RenderlessEditor, RenderlessTrace
|
||||
{
|
||||
/**
|
||||
* Creates a new Exception instance.
|
||||
*/
|
||||
public function __construct(string $filename)
|
||||
{
|
||||
parent::__construct('After method cannot be used with before the [test|it] functions in the filename `['.$filename.']`.');
|
||||
}
|
||||
}
|
||||
@ -1,24 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\Exceptions;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use NunoMaduro\Collision\Contracts\RenderlessEditor;
|
||||
use NunoMaduro\Collision\Contracts\RenderlessTrace;
|
||||
use Symfony\Component\Console\Exception\ExceptionInterface;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class AttributeNotSupportedYet extends InvalidArgumentException implements ExceptionInterface, RenderlessEditor, RenderlessTrace
|
||||
{
|
||||
/**
|
||||
* Creates a new instance of attribute not supported yet.
|
||||
*/
|
||||
public function __construct(string $attribute, string $value)
|
||||
{
|
||||
parent::__construct(sprintf('The PHPUnit attribute `%s` with value `%s` is not supported yet.', $attribute, $value));
|
||||
}
|
||||
}
|
||||
@ -12,13 +12,13 @@ use Symfony\Component\Console\Exception\ExceptionInterface;
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class AfterEachAlreadyExist extends InvalidArgumentException implements ExceptionInterface, RenderlessEditor, RenderlessTrace
|
||||
final class BeforeAllAlreadyExist extends InvalidArgumentException implements ExceptionInterface, RenderlessEditor, RenderlessTrace
|
||||
{
|
||||
/**
|
||||
* Creates a new instance of after each already exist exception.
|
||||
* Creates a new Exception instance.
|
||||
*/
|
||||
public function __construct(string $filename)
|
||||
{
|
||||
parent::__construct(sprintf('The afterEach already exist in the filename `%s`.', $filename));
|
||||
parent::__construct(sprintf('The beforeAll already exists in the filename `%s`.', $filename));
|
||||
}
|
||||
}
|
||||
24
src/Exceptions/BeforeAllWithinDescribe.php
Normal file
24
src/Exceptions/BeforeAllWithinDescribe.php
Normal file
@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\Exceptions;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use NunoMaduro\Collision\Contracts\RenderlessEditor;
|
||||
use NunoMaduro\Collision\Contracts\RenderlessTrace;
|
||||
use Symfony\Component\Console\Exception\ExceptionInterface;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class BeforeAllWithinDescribe extends InvalidArgumentException implements ExceptionInterface, RenderlessEditor, RenderlessTrace
|
||||
{
|
||||
/**
|
||||
* Creates a new Exception instance.
|
||||
*/
|
||||
public function __construct(string $filename)
|
||||
{
|
||||
parent::__construct(sprintf('The beforeAll method can not be used within describe functions. Filename `%s`.', $filename));
|
||||
}
|
||||
}
|
||||
@ -12,13 +12,13 @@ use Symfony\Component\Console\Exception\ExceptionInterface;
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class DatasetAlreadyExist extends InvalidArgumentException implements ExceptionInterface, RenderlessEditor, RenderlessTrace
|
||||
final class DatasetAlreadyExists extends InvalidArgumentException implements ExceptionInterface, RenderlessEditor, RenderlessTrace
|
||||
{
|
||||
/**
|
||||
* Creates a new instance of dataset already exist.
|
||||
* Creates a new Exception instance.
|
||||
*/
|
||||
public function __construct(string $name)
|
||||
public function __construct(string $name, string $scope)
|
||||
{
|
||||
parent::__construct(sprintf('A dataset with the name `%s` already exist.', $name));
|
||||
parent::__construct(sprintf('A dataset with the name `%s` already exists in scope [%s].', $name, $scope));
|
||||
}
|
||||
}
|
||||
21
src/Exceptions/DatasetArgumentsMismatch.php
Normal file
21
src/Exceptions/DatasetArgumentsMismatch.php
Normal file
@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
final class DatasetArgumentsMismatch extends Exception
|
||||
{
|
||||
public function __construct(int $requiredCount, int $suppliedCount)
|
||||
{
|
||||
if ($requiredCount <= $suppliedCount) {
|
||||
parent::__construct('Test argument names and dataset keys do not match');
|
||||
} else {
|
||||
parent::__construct(sprintf('Test expects %d arguments but dataset only provides %d', $requiredCount, $suppliedCount));
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
}
|
||||
@ -15,7 +15,7 @@ use Symfony\Component\Console\Exception\ExceptionInterface;
|
||||
final class DatasetDoesNotExist extends InvalidArgumentException implements ExceptionInterface, RenderlessEditor, RenderlessTrace
|
||||
{
|
||||
/**
|
||||
* Creates a new instance of dataset does not exist.
|
||||
* Creates a new Exception instance.
|
||||
*/
|
||||
public function __construct(string $name)
|
||||
{
|
||||
|
||||
@ -10,26 +10,22 @@ use NunoMaduro\Collision\Contracts\RenderlessTrace;
|
||||
use Symfony\Component\Console\Exception\ExceptionInterface;
|
||||
|
||||
/**
|
||||
* Creates a new instance of dataset is not present for test that has arguments.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
final class DatasetMissing extends BadFunctionCallException implements ExceptionInterface, RenderlessEditor, RenderlessTrace
|
||||
{
|
||||
/**
|
||||
* Create new exception instance.
|
||||
* Creates a new Exception instance.
|
||||
*
|
||||
* @param array<string, string> $args A map of argument names to their typee
|
||||
* @param array<string, string> $arguments
|
||||
*/
|
||||
public function __construct(string $file, string $name, array $args)
|
||||
public function __construct(string $file, string $name, array $arguments)
|
||||
{
|
||||
parent::__construct(sprintf(
|
||||
"A test with the description '%s' has %d argument(s) ([%s]) and no dataset(s) provided in %s",
|
||||
$name,
|
||||
count($args),
|
||||
implode(', ', array_map(static function (string $arg, string $type): string {
|
||||
return sprintf('%s $%s', $type, $arg);
|
||||
}, array_keys($args), $args)),
|
||||
count($arguments),
|
||||
implode(', ', array_map(static fn (string $arg, string $type): string => sprintf('%s $%s', $type, $arg), array_keys($arguments), $arguments)),
|
||||
$file,
|
||||
));
|
||||
}
|
||||
|
||||
21
src/Exceptions/ExpectationNotFound.php
Normal file
21
src/Exceptions/ExpectationNotFound.php
Normal file
@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class ExpectationNotFound extends Exception
|
||||
{
|
||||
/**
|
||||
* Creates a new ExpectationNotFound instance from the given name.
|
||||
*/
|
||||
public static function fromName(string $name): ExpectationNotFound
|
||||
{
|
||||
return new self("Expectation [$name] does not exist.");
|
||||
}
|
||||
}
|
||||
16
src/Exceptions/FatalException.php
Normal file
16
src/Exceptions/FatalException.php
Normal file
@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\Exceptions;
|
||||
|
||||
use NunoMaduro\Collision\Contracts\RenderlessTrace;
|
||||
use RuntimeException;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class FatalException extends RuntimeException implements RenderlessTrace
|
||||
{
|
||||
//
|
||||
}
|
||||
@ -15,10 +15,10 @@ use Symfony\Component\Console\Exception\ExceptionInterface;
|
||||
final class FileOrFolderNotFound extends InvalidArgumentException implements ExceptionInterface, RenderlessEditor, RenderlessTrace
|
||||
{
|
||||
/**
|
||||
* Creates a new instance of file not found.
|
||||
* Creates a new Exception instance.
|
||||
*/
|
||||
public function __construct(string $filename)
|
||||
{
|
||||
parent::__construct(sprintf('The file or folder with the name `%s` not found.', $filename));
|
||||
parent::__construct(sprintf('The file or folder with the name `%s` could not be found.', $filename));
|
||||
}
|
||||
}
|
||||
|
||||
24
src/Exceptions/InvalidArgumentException.php
Normal file
24
src/Exceptions/InvalidArgumentException.php
Normal file
@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\Exceptions;
|
||||
|
||||
use InvalidArgumentException as BaseInvalidArgumentException;
|
||||
use NunoMaduro\Collision\Contracts\RenderlessEditor;
|
||||
use NunoMaduro\Collision\Contracts\RenderlessTrace;
|
||||
use Symfony\Component\Console\Exception\ExceptionInterface;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class InvalidArgumentException extends BaseInvalidArgumentException implements ExceptionInterface, RenderlessEditor, RenderlessTrace
|
||||
{
|
||||
/**
|
||||
* Creates a new Exception instance.
|
||||
*/
|
||||
public function __construct(string $message)
|
||||
{
|
||||
parent::__construct($message, 1);
|
||||
}
|
||||
}
|
||||
26
src/Exceptions/InvalidExpectation.php
Normal file
26
src/Exceptions/InvalidExpectation.php
Normal file
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\Exceptions;
|
||||
|
||||
use LogicException;
|
||||
use NunoMaduro\Collision\Contracts\RenderlessEditor;
|
||||
use NunoMaduro\Collision\Contracts\RenderlessTrace;
|
||||
use Symfony\Component\Console\Exception\ExceptionInterface;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class InvalidExpectation extends LogicException implements ExceptionInterface, RenderlessEditor, RenderlessTrace
|
||||
{
|
||||
/**
|
||||
* @param array<int, string> $methods
|
||||
*
|
||||
* @throws self
|
||||
*/
|
||||
public static function fromMethods(array $methods): never
|
||||
{
|
||||
throw new self(sprintf('Expectation [%s] is not valid.', implode('->', $methods)));
|
||||
}
|
||||
}
|
||||
21
src/Exceptions/InvalidExpectationValue.php
Normal file
21
src/Exceptions/InvalidExpectationValue.php
Normal file
@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\Exceptions;
|
||||
|
||||
use InvalidArgumentException;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class InvalidExpectationValue extends InvalidArgumentException
|
||||
{
|
||||
/**
|
||||
* @throws self
|
||||
*/
|
||||
public static function expected(string $type): never
|
||||
{
|
||||
throw new self(sprintf('Invalid expectation value type. Expected [%s].', $type));
|
||||
}
|
||||
}
|
||||
@ -12,10 +12,10 @@ use Symfony\Component\Console\Exception\ExceptionInterface;
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class InvalidConsoleArgument extends InvalidArgumentException implements ExceptionInterface, RenderlessEditor, RenderlessTrace
|
||||
final class InvalidOption extends InvalidArgumentException implements ExceptionInterface, RenderlessEditor, RenderlessTrace
|
||||
{
|
||||
/**
|
||||
* Creates a new instance of should not happen.
|
||||
* Creates a new Exception instance.
|
||||
*/
|
||||
public function __construct(string $message)
|
||||
{
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user