mirror of
https://github.com/pestphp/pest.git
synced 2026-03-06 15:57:21 +01:00
Compare commits
1431 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e8fa98c810 | |||
| 07e314fbf5 | |||
| 4baf27911e | |||
| 12e48a14d1 | |||
| 1bc0f79508 | |||
| cb0f256791 | |||
| 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 | |||
| 887bed3d45 | |||
| 79da02c500 | |||
| 0aecd5d5d7 | |||
| e95c4ee636 | |||
| 2e7fec6be5 | |||
| 4be7082de5 | |||
| fb90f778b9 | |||
| 9d58e1a77e | |||
| 9c077ed352 | |||
| 2562d36518 | |||
| 1d2fe2de2d | |||
| 2d82ee2837 | |||
| 1eee9df679 | |||
| 8c57cc1731 | |||
| 4febd8a11b | |||
| 880b003bee | |||
| e0f9d0bccf | |||
| d4853feecd | |||
| 86e812284d | |||
| f75063c420 | |||
| 1f8e6e4e9f | |||
| bb593846e5 | |||
| 108d181a05 | |||
| ac5d6c1f67 | |||
| 5aa3b91d56 | |||
| 9a01504b76 | |||
| 0ab636e436 | |||
| b9d2be87a2 | |||
| fef02594db | |||
| e135e2671f | |||
| 6d74965727 | |||
| 146e141b2a | |||
| 6fed7545c0 | |||
| be407ac904 | |||
| 5332858782 | |||
| 3457841a9b | |||
| 5258e569c1 | |||
| abb416c2ff | |||
| b1c59ec2e6 | |||
| 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 | |||
| 2ffafd445d | |||
| 6068ef6150 | |||
| 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 | |||
| 075c31bc78 | |||
| 2125bf9668 | |||
| dbf3c0a8cf | |||
| eca5f89e59 | |||
| 0b0beac122 | |||
| 578e97123d | |||
| 01d672d563 | |||
| 490b2d66e5 | |||
| 0368c4846f | |||
| 4dfc02c5da | |||
| 5c84b0c6d3 | |||
| b6c06e8c30 | |||
| c6435d5606 | |||
| 2887d212e3 | |||
| cadae52d5d | |||
| d9749ca65b | |||
| c2070cd99d | |||
| 03d34e9a10 | |||
| 45e76a6df6 | |||
| 28dd3c2a03 | |||
| 5de981d923 | |||
| a55b31e7c3 | |||
| 5f0bd8180e | |||
| e1f1fcccbe | |||
| ab04aef561 | |||
| 79ddb1f58e | |||
| c7a2e68941 | |||
| 5c592928d4 | |||
| bcab4224fb | |||
| 892f70b5b5 | |||
| 5c7de5ad75 | |||
| 995088b522 | |||
| ef503646ee | |||
| a760470e48 | |||
| 1d4c1a5359 | |||
| 8e32b88fc8 | |||
| 1a7baad338 | |||
| 31d1b1b91d | |||
| 7524c80af6 | |||
| 721d5134b7 | |||
| 0b5321fdd7 | |||
| c86058fed1 | |||
| 8b295b5e9d | |||
| 221248e691 | |||
| 7621247bb7 | |||
| 463a50ebd4 | |||
| 62aabc6ae1 | |||
| 1ca9aa5ca6 | |||
| 7a76f8dce2 | |||
| beca27599c | |||
| 256b167eaf | |||
| 5526d4c24d | |||
| 7ea138c640 | |||
| c4a659c3b5 | |||
| 0a3991c314 | |||
| d1a9e0bbe3 | |||
| 17eacfdf95 | |||
| 9ec0762d41 | |||
| 30f39f1850 | |||
| 8ee07330b3 | |||
| cffde4564d | |||
| ce7a7649a2 | |||
| eeed7e6a0a | |||
| d6844f5239 | |||
| 06c4019e81 | |||
| 7785a8cc58 | |||
| 663516c1e3 | |||
| e83667a20b | |||
| aa96d75fb9 | |||
| a5af4bc5ed | |||
| 57161ba5ad | |||
| b1a9254fc1 | |||
| e56e818659 | |||
| 79de0d5875 | |||
| f6f8140ebc | |||
| eed221af46 | |||
| 524457a4e6 | |||
| 8e289b7a7d | |||
| 172b69cf15 | |||
| 475279a4fa | |||
| 4b236bf9ff | |||
| 50e2d10029 | |||
| fa8a57f1ab | |||
| 715f8b420b | |||
| c776bcf86d | |||
| a0637d86ff | |||
| 1a941d7f92 | |||
| b5959aa3fa | |||
| 8861dd2401 | |||
| da73f4b395 | |||
| d5097d0fe5 | |||
| 022ad4be0d | |||
| 2d2a83e9e8 | |||
| 67c7bee4fa | |||
| 675b0f1ec8 | |||
| c2b86c3ab3 | |||
| 46337b8085 | |||
| df172d8eed | |||
| 24b9160b79 | |||
| a7860b0b8e | |||
| 7471c224fa | |||
| 2996135155 | |||
| 1abab8d440 | |||
| e52c83e5be | |||
| 5ed4545737 | |||
| 8b39df68ce | |||
| 04d8a3762b | |||
| 0f7c8d00d6 | |||
| 12fb4f8639 | |||
| 6a84b825e6 | |||
| e8595c56b3 | |||
| 4a9fb2fa74 | |||
| d8fae6d689 | |||
| 252f9a0e46 | |||
| 8d24b4a217 | |||
| 43920f79a9 | |||
| 55bfc5856b | |||
| cd9d4acbc2 | |||
| 2b5355419a | |||
| 22b822ce87 | |||
| b2c298b926 | |||
| 671f3df115 | |||
| 2dd77001b7 | |||
| 5f574ded81 | |||
| 6309e6818d | |||
| 4813ab6ffb | |||
| 4ebba1298a | |||
| 2e0d1bb5a0 | |||
| 09d2b16767 | |||
| f56556eb73 | |||
| f387ca8624 | |||
| ca9d783cf9 | |||
| 863ddea50b | |||
| 00b4bb6305 | |||
| d217503a6a | |||
| b6012862c4 | |||
| 60c0ad006f | |||
| 79ff332afe | |||
| 595bbe32a4 | |||
| 5e0a0855ea | |||
| 7ec3460d73 | |||
| d10281f851 | |||
| 05f72f9b6d | |||
| f9de1b9c00 | |||
| 9516e56242 | |||
| 5f315fc899 | |||
| e59606818d | |||
| e16104350e | |||
| df31191f4e | |||
| 5b310f6f93 | |||
| 0200f90e9a | |||
| dc75b34deb | |||
| 8e22803797 | |||
| 82bd836ae9 | |||
| e64856c664 | |||
| de86598c0d | |||
| 553b45306f | |||
| 2e7192ab95 | |||
| e21e3080e0 | |||
| 6a7ee90ff5 | |||
| 9d66893d5a | |||
| 64e780cf72 | |||
| 9bf141f698 | |||
| 9904094590 | |||
| 9e5b779abc |
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.1
|
||||
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
|
||||
|
||||
39
.github/workflows/tests.yml
vendored
39
.github/workflows/tests.yml
vendored
@ -1,21 +1,32 @@
|
||||
name: Tests
|
||||
|
||||
on: ['push', 'pull_request']
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
schedule:
|
||||
- cron: '0 0 * * *'
|
||||
|
||||
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]
|
||||
symfony: ['6.4', '7.0']
|
||||
php: ['8.1', '8.2', '8.3']
|
||||
dependency_version: [prefer-lowest, prefer-stable]
|
||||
exclude:
|
||||
- php: '8.1'
|
||||
symfony: '7.0'
|
||||
|
||||
name: PHP ${{ matrix.php }} - ${{ matrix.os }} - ${{ matrix.dependency-version }}
|
||||
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
|
||||
@ -29,16 +40,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
|
||||
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,31 +0,0 @@
|
||||
<?php
|
||||
|
||||
$finder = PhpCsFixer\Finder::create()
|
||||
->in(__DIR__ . DIRECTORY_SEPARATOR . 'tests')
|
||||
->in(__DIR__ . DIRECTORY_SEPARATOR . 'bin')
|
||||
->in(__DIR__ . DIRECTORY_SEPARATOR . 'scripts')
|
||||
->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);
|
||||
285
CHANGELOG.md
285
CHANGELOG.md
@ -1,285 +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.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.2"
|
||||
```
|
||||
|
||||
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
|
||||
35
README.md
35
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/v2/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,21 +9,30 @@
|
||||
</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.
|
||||
**Pest** is an elegant PHP testing Framework with a focus on simplicity, meticulously designed to bring back the joy of testing in 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)**
|
||||
- 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)**
|
||||
|
||||
## Pest Sponsors
|
||||
## Sponsors
|
||||
|
||||
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).
|
||||
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
|
||||
|
||||
- **[Forge](https://forge.laravel.com)**
|
||||
- **[Spatie](https://spatie.be)**
|
||||
- **[Worksome](https://www.worksome.com/)**
|
||||
|
||||
### Premium Sponsors
|
||||
|
||||
- **[Akaunting](https://akaunting.com)**
|
||||
- **[Scout APM](https://scoutapm.com)**
|
||||
- **[Meema](https://meema.io/)**
|
||||
- **[Spatie](https://spatie.be/)**
|
||||
- [Akaunting](https://akaunting.com/?ref=pestphp)
|
||||
- [Codecourse](https://codecourse.com/?ref=pestphp)
|
||||
- [Laracasts](https://laracasts.com/?ref=pestphp)
|
||||
- [Laradir](https://laradir.com/?ref=pestphp)
|
||||
- [Localazy](https://localazy.com/?ref=pestphp)
|
||||
- [Stormlikes](https://www.stormlikes.net/?ref=pestphp)
|
||||
- [Zapiet](https://www.zapiet.com/?ref=pestphp)
|
||||
|
||||
Pest was created by **[Nuno Maduro](https://twitter.com/enunomaduro)** under the **[Sponsorware license](https://github.com/sponsorware/docs)**. It got open-sourced and is now licensed under the **[MIT license](https://opensource.org/licenses/MIT)**.
|
||||
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 v1 you should use the `1.x` branch instead.**
|
||||
|
||||
- Clear your local repository with: `git add . && git reset --hard && git checkout 2.x`
|
||||
- On the GitHub repository, check the contents of [github.com/pestphp/pest/compare/{latest_version}...2.x](https://github.com/pestphp/pest/compare/{latest_version}...2.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 "docs: update changelog"`
|
||||
- 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
|
||||
|
||||
|
||||
102
bin/pest
102
bin/pest
@ -1,21 +1,60 @@
|
||||
#!/usr/bin/env php
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
use NunoMaduro\Collision\Provider;
|
||||
use Pest\Actions\ValidatesEnvironment;
|
||||
use Pest\Console\Command;
|
||||
use Pest\Support\Container;
|
||||
use Pest\Kernel;
|
||||
use Pest\Panic;
|
||||
use Pest\TestCaseFilters\GitDirtyTestCaseFilter;
|
||||
use Pest\TestCaseMethodFilters\TodoTestCaseFilter;
|
||||
use Pest\TestSuite;
|
||||
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;
|
||||
|
||||
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]);
|
||||
}
|
||||
|
||||
if ($value === '--dirty') {
|
||||
$dirty = true;
|
||||
unset($arguments[$key]);
|
||||
}
|
||||
|
||||
if (in_array($value, ['--todo', '--todos'], true)) {
|
||||
$todo = true;
|
||||
unset($arguments[$key]);
|
||||
}
|
||||
|
||||
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';
|
||||
$vendorPath = dirname(__DIR__, 4).'/vendor/autoload.php';
|
||||
|
||||
// Used when Pest maintainers are running Pest tests.
|
||||
$localPath = dirname(__DIR__) . '/vendor/autoload.php';
|
||||
$localPath = dirname(__DIR__).'/vendor/autoload.php';
|
||||
|
||||
if (file_exists($vendorPath)) {
|
||||
include_once $vendorPath;
|
||||
@ -25,31 +64,36 @@ use Symfony\Component\Console\Output\OutputInterface;
|
||||
$autoloadPath = $localPath;
|
||||
}
|
||||
|
||||
(new Provider())->register();
|
||||
|
||||
// get $rootPath based on $autoloadPath
|
||||
// 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'),
|
||||
);
|
||||
|
||||
$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);
|
||||
|
||||
ValidatesEnvironment::in($testSuite);
|
||||
|
||||
// lets remove any arguments that PHPUnit does not understand
|
||||
if ($argv->hasParameterOption('--test-directory')) {
|
||||
foreach ($_SERVER['argv'] as $key => $value) {
|
||||
if (strpos($value, '--test-directory') !== false) {
|
||||
unset($_SERVER['argv'][$key]);
|
||||
}
|
||||
}
|
||||
if ($dirty) {
|
||||
$testSuite->tests->addTestCaseFilter(new GitDirtyTestCaseFilter($rootPath));
|
||||
}
|
||||
|
||||
exit($container->get(Command::class)->run($_SERVER['argv']));
|
||||
if ($todo) {
|
||||
$testSuite->tests->addTestCaseMethodFilter(new TodoTestCaseFilter());
|
||||
}
|
||||
|
||||
$isDecorated = $input->getParameterOption('--colors', 'always') !== 'never';
|
||||
|
||||
$output = new ConsoleOutput(ConsoleOutput::VERBOSITY_NORMAL, $isDecorated);
|
||||
|
||||
try {
|
||||
$kernel = Kernel::boot($testSuite, $input, $output);
|
||||
|
||||
$result = $kernel->handle($originalArguments, $arguments);
|
||||
|
||||
$kernel->terminate();
|
||||
} catch (Throwable|Error $e) {
|
||||
Panic::with($e);
|
||||
}
|
||||
|
||||
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,18 @@
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": "^7.3 || ^8.0",
|
||||
"nunomaduro/collision": "^5.4.0",
|
||||
"pestphp/pest-plugin": "^1.0.0",
|
||||
"phpunit/phpunit": ">= 9.3.7 <= 9.5.6"
|
||||
"php": "^8.1.0",
|
||||
"brianium/paratest": "^7.4.1",
|
||||
"nunomaduro/collision": "^7.10.0|^8.1.0",
|
||||
"nunomaduro/termwind": "^1.15.1|^2.0.0",
|
||||
"pestphp/pest-plugin": "^2.1.1",
|
||||
"pestphp/pest-plugin-arch": "^2.7.0",
|
||||
"phpunit/phpunit": "^10.5.10"
|
||||
},
|
||||
"conflict": {
|
||||
"phpunit/phpunit": ">10.5.10",
|
||||
"sebastian/exporter": "<5.1.0",
|
||||
"webmozart/assert": "<1.11.0"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
@ -33,6 +41,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,48 +51,68 @@
|
||||
]
|
||||
},
|
||||
"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-dev-tools": "^2.16.0",
|
||||
"pestphp/pest-plugin-type-coverage": "^2.8.0",
|
||||
"symfony/process": "^6.4.0|^7.0.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=0",
|
||||
"test:unit": "php bin/pest --colors=always --exclude-group=integration",
|
||||
"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 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",
|
||||
"update:snapshots": "REBUILD_SNAPSHOTS=true php bin/pest --colors=always",
|
||||
"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\\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
|
||||
56
overrides/Event/Value/ThrowableBuilder.php
Normal file
56
overrides/Event/Value/ThrowableBuilder.php
Normal file
@ -0,0 +1,56 @@
|
||||
<?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\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 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
|
||||
);
|
||||
}
|
||||
}
|
||||
459
overrides/Logging/JUnit/JunitXmlLogger.php
Normal file
459
overrides/Logging/JUnit/JunitXmlLogger.php
Normal file
@ -0,0 +1,459 @@
|
||||
<?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 TestMarkedIncompleteSubscriber($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();
|
||||
}
|
||||
}
|
||||
172
overrides/Runner/Filter/NameFilterIterator.php
Normal file
172
overrides/Runner/Filter/NameFilterIterator.php
Normal file
@ -0,0 +1,172 @@
|
||||
<?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.
|
||||
*/
|
||||
|
||||
namespace PHPUnit\Runner\Filter;
|
||||
|
||||
use Exception;
|
||||
use Pest\Contracts\HasPrintableTestCaseName;
|
||||
use PHPUnit\Framework\SelfDescribing;
|
||||
use PHPUnit\Framework\Test;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use PHPUnit\Framework\TestSuite;
|
||||
use RecursiveFilterIterator;
|
||||
use RecursiveIterator;
|
||||
|
||||
use function end;
|
||||
use function implode;
|
||||
use function preg_match;
|
||||
use function sprintf;
|
||||
use function str_replace;
|
||||
|
||||
/**
|
||||
* @internal This class is not covered by the backward compatibility promise for PHPUnit
|
||||
*/
|
||||
final class NameFilterIterator extends RecursiveFilterIterator
|
||||
{
|
||||
private ?string $filter = null;
|
||||
|
||||
private ?int $filterMin = null;
|
||||
|
||||
private ?int $filterMax = null;
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
*/
|
||||
public function __construct(RecursiveIterator $iterator, string $filter)
|
||||
{
|
||||
parent::__construct($iterator);
|
||||
|
||||
$this->setFilter($filter);
|
||||
}
|
||||
|
||||
public function accept(): bool
|
||||
{
|
||||
$test = $this->getInnerIterator()->current();
|
||||
|
||||
if ($test instanceof TestSuite) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$tmp = $this->describe($test);
|
||||
|
||||
if ($tmp[0] !== '') {
|
||||
$name = implode('::', $tmp);
|
||||
} else {
|
||||
$name = $tmp[1];
|
||||
}
|
||||
|
||||
$accepted = @preg_match($this->filter, $name, $matches);
|
||||
|
||||
if ($accepted && isset($this->filterMax)) {
|
||||
$set = end($matches);
|
||||
$accepted = $set >= $this->filterMin && $set <= $this->filterMax;
|
||||
}
|
||||
|
||||
return (bool) $accepted;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
*/
|
||||
private function setFilter(string $filter): void
|
||||
{
|
||||
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 dataset #(\d+)$',
|
||||
$matches[1]
|
||||
);
|
||||
|
||||
$this->filterMin = (int) $matches[2];
|
||||
$this->filterMax = (int) $matches[3];
|
||||
} else {
|
||||
$filter = sprintf(
|
||||
'%s.*with dataset #%s$',
|
||||
$matches[1],
|
||||
$matches[2]
|
||||
);
|
||||
}
|
||||
} // Handles:
|
||||
// * testDetermineJsonError@JSON_ERROR_NONE
|
||||
// * testDetermineJsonError@JSON.*
|
||||
elseif (preg_match('/^(.*?)@(.+)$/', $filter, $matches)) {
|
||||
$filter = sprintf(
|
||||
'%s.*with dataset "%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
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$this->filter = $filter;
|
||||
}
|
||||
|
||||
/**
|
||||
* @psalm-return array{0: string, 1: string}
|
||||
*/
|
||||
private function describe(Test $test): array
|
||||
{
|
||||
if ($test instanceof HasPrintableTestCaseName) {
|
||||
return [
|
||||
$test::getPrintableTestCaseName(),
|
||||
$test->getPrintableTestCaseMethodName(),
|
||||
];
|
||||
}
|
||||
|
||||
if ($test instanceof TestCase) {
|
||||
return [$test::class, $test->nameWithDataSet()];
|
||||
}
|
||||
|
||||
if ($test instanceof SelfDescribing) {
|
||||
return ['', $test->toString()];
|
||||
}
|
||||
|
||||
return ['', $test::class];
|
||||
}
|
||||
}
|
||||
197
overrides/Runner/ResultCache/DefaultResultCache.php
Normal file
197
overrides/Runner/ResultCache/DefaultResultCache.php
Normal file
@ -0,0 +1,197 @@
|
||||
<?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, TestStatus>
|
||||
*/
|
||||
private array $currentDefects = [];
|
||||
|
||||
/**
|
||||
* @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->isFailure() || $status->isError()) {
|
||||
$this->currentDefects[$id] = $status;
|
||||
$this->defects[$id] = $status;
|
||||
}
|
||||
}
|
||||
|
||||
public function status(string $id): TestStatus
|
||||
{
|
||||
return $this->defects[$id] ?? TestStatus::unknown();
|
||||
}
|
||||
|
||||
public function setTime(string $id, float $time): void
|
||||
{
|
||||
if (! isset($this->currentDefects[$id])) {
|
||||
unset($this->defects[$id]);
|
||||
}
|
||||
|
||||
$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;
|
||||
}
|
||||
|
||||
$data = json_decode(
|
||||
file_get_contents($this->cacheFilename),
|
||||
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();
|
||||
}
|
||||
}
|
||||
221
overrides/Runner/TestSuiteLoader.php
Normal file
221
overrides/Runner/TestSuiteLoader.php
Normal file
@ -0,0 +1,221 @@
|
||||
<?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\TestCases\IgnorableTestCase;
|
||||
use Pest\TestSuite;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use ReflectionClass;
|
||||
use ReflectionException;
|
||||
|
||||
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) {
|
||||
include_once $suiteClassFile;
|
||||
|
||||
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,104 @@
|
||||
<?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 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 class WarmCodeCoverageCacheCommand implements Command
|
||||
{
|
||||
private readonly Configuration $configuration;
|
||||
|
||||
private readonly 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);
|
||||
|
||||
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,80 @@
|
||||
<?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\Output\Default\ProgressPrinter;
|
||||
|
||||
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
|
||||
*
|
||||
* This file is overridden to allow Pest Parallel to show todo items in the progress output.
|
||||
*/
|
||||
final class TestSkippedSubscriber extends Subscriber implements SkippedSubscriber
|
||||
{
|
||||
/**
|
||||
* Notifies the printer that a test was skipped.
|
||||
*/
|
||||
public function notify(Skipped $event): void
|
||||
{
|
||||
if (str_contains($event->message(), '__TODO__')) {
|
||||
$this->printTodoItem();
|
||||
}
|
||||
|
||||
$this->printer()->testSkipped();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 class TestSuiteFilterProcessor
|
||||
{
|
||||
private Factory $filterFactory;
|
||||
|
||||
public function __construct(Factory $factory = new Factory)
|
||||
{
|
||||
$this->filterFactory = $factory;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Event\RuntimeException
|
||||
* @throws FilterNotConfiguredException
|
||||
*/
|
||||
public function process(Configuration $configuration, TestSuite $suite): void
|
||||
{
|
||||
if (! $configuration->hasFilter() &&
|
||||
! $configuration->hasGroups() &&
|
||||
! $configuration->hasExcludeGroups() &&
|
||||
! $configuration->hasTestsCovering() &&
|
||||
! $configuration->hasTestsUsing() &&
|
||||
! Only::isEnabled()
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($configuration->hasExcludeGroups()) {
|
||||
$this->filterFactory->addExcludeGroupFilter(
|
||||
$configuration->excludeGroups()
|
||||
);
|
||||
}
|
||||
|
||||
if (Only::isEnabled()) {
|
||||
$this->filterFactory->addIncludeGroupFilter(['__pest_only']);
|
||||
} elseif ($configuration->hasGroups()) {
|
||||
$this->filterFactory->addIncludeGroupFilter(
|
||||
$configuration->groups()
|
||||
);
|
||||
}
|
||||
|
||||
if ($configuration->hasTestsCovering()) {
|
||||
$this->filterFactory->addIncludeGroupFilter(
|
||||
array_map(
|
||||
static fn (string $name): string => '__phpunit_covers_'.$name,
|
||||
$configuration->testsCovering()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if ($configuration->hasTestsUsing()) {
|
||||
$this->filterFactory->addIncludeGroupFilter(
|
||||
array_map(
|
||||
static fn (string $name): string => '__phpunit_uses_'.$name,
|
||||
$configuration->testsUsing()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if ($configuration->hasFilter()) {
|
||||
$this->filterFactory->addNameFilter(
|
||||
$configuration->filter()
|
||||
);
|
||||
}
|
||||
|
||||
$suite->injectFilter($this->filterFactory);
|
||||
|
||||
Event\Facade::emitter()->testSuiteFiltered(
|
||||
Event\TestSuite\TestSuiteBuilder::from($suite)
|
||||
);
|
||||
}
|
||||
}
|
||||
22
phpstan.neon
22
phpstan.neon
@ -7,29 +7,17 @@ parameters:
|
||||
level: max
|
||||
paths:
|
||||
- src
|
||||
- scripts
|
||||
|
||||
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
|
||||
|
||||
35
phpunit.xml
35
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>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
<coverage processUncoveredFiles="true">
|
||||
<include>
|
||||
<directory suffix=".php">./src</directory>
|
||||
</include>
|
||||
</coverage>
|
||||
<testsuites>
|
||||
<testsuite name="default">
|
||||
<directory suffix=".php">./tests</directory>
|
||||
<exclude>./tests/.snapshots</exclude>
|
||||
<exclude>./tests/.tests</exclude>
|
||||
<exclude>./tests/Fixtures/Inheritance</exclude>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
<source>
|
||||
<include>
|
||||
<directory suffix=".php">./src</directory>
|
||||
</include>
|
||||
</source>
|
||||
</phpunit>
|
||||
|
||||
31
rector.php
Normal file
31
rector.php
Normal file
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Rector\CodeQuality\Rector\Class_\InlineConstructorDefaultToPropertyRector;
|
||||
use Rector\Config\RectorConfig;
|
||||
use Rector\Set\ValueObject\LevelSetList;
|
||||
use Rector\Set\ValueObject\SetList;
|
||||
|
||||
return static function (RectorConfig $rectorConfig): void {
|
||||
$rectorConfig->paths([
|
||||
__DIR__.'/src',
|
||||
]);
|
||||
|
||||
$rectorConfig->rules([
|
||||
InlineConstructorDefaultToPropertyRector::class,
|
||||
]);
|
||||
|
||||
$rectorConfig->skip([
|
||||
__DIR__.'/src/Plugins/Parallel/Paratest/WrapperRunner.php',
|
||||
]);
|
||||
|
||||
$rectorConfig->sets([
|
||||
LevelSetList::UP_TO_PHP_81,
|
||||
SetList::CODE_QUALITY,
|
||||
SetList::DEAD_CODE,
|
||||
SetList::EARLY_RETURN,
|
||||
SetList::TYPE_DECLARATION,
|
||||
SetList::PRIVATIZATION,
|
||||
]);
|
||||
};
|
||||
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,37 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
$globalsFilePath = implode(DIRECTORY_SEPARATOR, [
|
||||
dirname(__DIR__),
|
||||
'vendor',
|
||||
'phpunit',
|
||||
'phpunit',
|
||||
'src',
|
||||
'Framework',
|
||||
'Assert',
|
||||
'Functions.php',
|
||||
]);
|
||||
|
||||
$compiledFilePath = implode(DIRECTORY_SEPARATOR, [dirname(__DIR__), 'compiled', 'globals.php']);
|
||||
|
||||
/* @phpstan-ignore-next-line */
|
||||
@unlink($compiledFilePath);
|
||||
|
||||
$replace = function ($contents, $string, $by) {
|
||||
return str_replace($string, $by, $contents);
|
||||
};
|
||||
|
||||
$remove = function ($contents, $string) {
|
||||
return str_replace($string, '', $contents);
|
||||
};
|
||||
|
||||
$contents = file_get_contents($globalsFilePath);
|
||||
$contents = $replace($contents, 'namespace PHPUnit\Framework;', 'use PHPUnit\Framework\Assert;');
|
||||
$contents = $remove($contents, 'use ArrayAccess;');
|
||||
$contents = $remove($contents, 'use Countable;');
|
||||
$contents = $remove($contents, 'use DOMDocument;');
|
||||
$contents = $remove($contents, 'use DOMElement;');
|
||||
$contents = $remove($contents, 'use Throwable;');
|
||||
|
||||
file_put_contents(implode(DIRECTORY_SEPARATOR, [dirname(__DIR__), 'compiled', 'globals.php']), $contents);
|
||||
@ -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($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,66 +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);
|
||||
|
||||
// @todo refactor this...
|
||||
|
||||
$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,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
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 class BootKernelDump implements Bootstrapper
|
||||
{
|
||||
/**
|
||||
* Creates a new Boot Kernel Dump instance.
|
||||
*/
|
||||
public function __construct(
|
||||
private readonly 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 = [
|
||||
'c7b9c8a96006dea314204a8f09a8764e51ce0b9b79aadd58da52e8c328db4870' => 'Runner/Filter/NameFilterIterator.php',
|
||||
'52b2574e96269aca1bb2d41bbf418c3bcf23dd21d14c66f90789025c309e39df' => 'Runner/ResultCache/DefaultResultCache.php',
|
||||
'bc8718c89264f65800beabc23e51c6d3bcff87dfc764a12179ef5dbfde272c8b' => 'Runner/TestSuiteLoader.php',
|
||||
'f41e48d6cb546772a7de4f8e66b6b7ce894a5318d063eb52e354d206e96c701c' => 'TextUI/Command/Commands/WarmCodeCoverageCacheCommand.php',
|
||||
'cb7519f2d82893640b694492cf7ec9528da80773cc1d259634181b5d393528b5' => 'TextUI/Output/Default/ProgressPrinter/Subscriber/TestSkippedSubscriber.php',
|
||||
'6db25ee539e9b12b1fb4e044a0a93410e015bc983ecdd3909cd394fe44ae8c95' => 'TextUI/TestSuiteFilterProcessor.php',
|
||||
'ef64a657ed9c0067791483784944107827bf227c7e3200f212b6751876b99e25' => 'Event/Value/ThrowableBuilder.php',
|
||||
'c78f96e34b98ed01dd8106539d59b8aa8d67f733274118b827c01c5c4111c033' => '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;
|
||||
}
|
||||
}
|
||||
}
|
||||
51
src/Bootstrappers/BootSubscribers.php
Normal file
51
src/Bootstrappers/BootSubscribers.php
Normal file
@ -0,0 +1,51 @@
|
||||
<?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 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 readonly 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 class BootView implements Bootstrapper
|
||||
{
|
||||
/**
|
||||
* Creates a new instance of the Boot View.
|
||||
*/
|
||||
public function __construct(
|
||||
private readonly OutputInterface $output
|
||||
) {
|
||||
// ..
|
||||
}
|
||||
|
||||
/**
|
||||
* Boots the view renderer.
|
||||
*/
|
||||
public function boot(): void
|
||||
{
|
||||
View::renderUsing($this->output);
|
||||
}
|
||||
}
|
||||
@ -14,13 +14,12 @@ trait Expectable
|
||||
/**
|
||||
* @template TValue
|
||||
*
|
||||
* Creates a new expectation.
|
||||
*
|
||||
* @param TValue $value
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
|
||||
45
src/Concerns/Logging/WritesToConsole.php
Normal file
45
src/Concerns/Logging/WritesToConsole.php
Normal file
@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\Concerns\Logging;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
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);
|
||||
$this->write($message);
|
||||
$this->writeNewLine();
|
||||
}
|
||||
}
|
||||
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
|
||||
*
|
||||
* @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,185 +5,154 @@ declare(strict_types=1);
|
||||
namespace Pest\Concerns;
|
||||
|
||||
use Closure;
|
||||
use Pest\Exceptions\DatasetArgsCountMismatch;
|
||||
use Pest\Support\ChainableClosure;
|
||||
use Pest\Support\ExceptionTrace;
|
||||
use Pest\Support\Reflection;
|
||||
use Pest\TestSuite;
|
||||
use PHPUnit\Framework\ExecutionOrderDependency;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use ReflectionException;
|
||||
use ReflectionFunction;
|
||||
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 describing, if any.
|
||||
*/
|
||||
private $beforeEach = null;
|
||||
public ?string $__describing = null;
|
||||
|
||||
/**
|
||||
* Holds a global/shared afterEach ("tear down") closure if one has been
|
||||
* defined.
|
||||
*
|
||||
* @var Closure|null
|
||||
* The test's test closure.
|
||||
*/
|
||||
private $afterEach = null;
|
||||
private Closure $__test;
|
||||
|
||||
/**
|
||||
* Holds a global/shared beforeAll ("set up before") closure if one has been
|
||||
* defined.
|
||||
*
|
||||
* @var Closure|null
|
||||
* The test's before each closure.
|
||||
*/
|
||||
private static $beforeAll = null;
|
||||
private ?Closure $__beforeEach = null;
|
||||
|
||||
/**
|
||||
* Holds a global/shared afterAll ("tear down after") closure if one has
|
||||
* been defined.
|
||||
*
|
||||
* @var Closure|null
|
||||
* The test's after each closure.
|
||||
*/
|
||||
private static $afterAll = null;
|
||||
private ?Closure $__afterEach = null;
|
||||
|
||||
/**
|
||||
* Creates a new instance of the test case.
|
||||
* The test's before all closure.
|
||||
*/
|
||||
public function __construct(Closure $test, string $description, array $data)
|
||||
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 = [];
|
||||
|
||||
/**
|
||||
* Resets the test case static properties.
|
||||
*/
|
||||
public static function flush(): void
|
||||
{
|
||||
$this->__test = $test;
|
||||
$this->__description = $description;
|
||||
|
||||
parent::__construct('__test', $data);
|
||||
self::$__beforeAll = null;
|
||||
self::$__afterAll = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the groups to the current test case.
|
||||
* Creates a new Test Case instance.
|
||||
*/
|
||||
public function addGroups(array $groups): void
|
||||
public function __construct(string $name)
|
||||
{
|
||||
$groups = array_unique(array_merge($this->getGroups(), $groups));
|
||||
parent::__construct($name);
|
||||
|
||||
$this->setGroups($groups);
|
||||
$test = TestSuite::getInstance()->tests->get(self::$__filename);
|
||||
|
||||
if ($test->hasMethod($name)) {
|
||||
$method = $test->getMethod($name);
|
||||
$this->__description = self::$__latestDescription = $method->description;
|
||||
$this->__describing = $method->describing;
|
||||
$this->__test = $method->getClosure($this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add dependencies to the test case and map them to instances of ExecutionOrderDependency.
|
||||
* Adds a new "setUpBeforeClass" to the Test Case.
|
||||
*/
|
||||
public function addDependencies(array $tests): void
|
||||
public function __addBeforeAll(?Closure $hook): void
|
||||
{
|
||||
$className = get_class($this);
|
||||
|
||||
$tests = array_map(function (string $test) use ($className): ExecutionOrderDependency {
|
||||
if (strpos($test, '::') === false) {
|
||||
$test = "{$className}::{$test}";
|
||||
}
|
||||
|
||||
return new ExecutionOrderDependency($test, null, '');
|
||||
}, $tests);
|
||||
|
||||
$this->setDependencies($tests);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a shared/"global" before all test hook that will execute **before**
|
||||
* the test defined `beforeAll` hook(s).
|
||||
*/
|
||||
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
|
||||
{
|
||||
@ -191,22 +160,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));
|
||||
@ -215,32 +184,54 @@ trait Testable
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets executed before the test.
|
||||
* Gets executed before the Test Case.
|
||||
*/
|
||||
protected function setUp(): void
|
||||
{
|
||||
TestSuite::getInstance()->test = $this;
|
||||
|
||||
$method = TestSuite::getInstance()->tests->get(self::$__filename)->getMethod($this->name());
|
||||
|
||||
$description = $this->dataName() ? $method->description.' with '.$this->dataName() : $method->description;
|
||||
$description = htmlspecialchars(html_entity_decode($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;
|
||||
|
||||
parent::setUp();
|
||||
|
||||
$beforeEach = TestSuite::getInstance()->beforeEach->get(self::$__filename);
|
||||
$beforeEach = TestSuite::getInstance()->beforeEach->get(self::$__filename)[1];
|
||||
|
||||
if ($this->beforeEach instanceof Closure) {
|
||||
$beforeEach = ChainableClosure::from($this->beforeEach, $beforeEach);
|
||||
if ($this->__beforeEach instanceof Closure) {
|
||||
$beforeEach = ChainableClosure::bound($this->__beforeEach, $beforeEach);
|
||||
}
|
||||
|
||||
$this->__callClosure($beforeEach, func_get_args());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets executed after the test.
|
||||
* Gets executed after the Test Case.
|
||||
*/
|
||||
protected function tearDown(): 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);
|
||||
}
|
||||
|
||||
$this->__callClosure($afterEach, func_get_args());
|
||||
@ -251,43 +242,140 @@ trait Testable
|
||||
}
|
||||
|
||||
/**
|
||||
* 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, func_get_args());
|
||||
$arguments = $this->__resolveTestArguments($args);
|
||||
$this->__ensureDatasetArgumentNumberMatches($arguments);
|
||||
|
||||
return $this->__callClosure($closure, $arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
* Resolve the passed arguments. Any Closures will be bound to the testcase and resolved.
|
||||
*
|
||||
* @throws Throwable
|
||||
*/
|
||||
private function __callClosure(Closure $closure, array $arguments)
|
||||
private function __resolveTestArguments(array $arguments): array
|
||||
{
|
||||
return ExceptionTrace::ensure(function () use ($closure, $arguments) {
|
||||
return call_user_func_array(Closure::bind($closure, $this, get_class($this)), $arguments);
|
||||
});
|
||||
$method = TestSuite::getInstance()->tests->get(self::$__filename)->getMethod($this->name());
|
||||
|
||||
if ($method->repetitions > 1) {
|
||||
array_shift($arguments);
|
||||
}
|
||||
|
||||
$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 (! $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);
|
||||
}
|
||||
|
||||
public function getPrintableTestCaseName(): string
|
||||
/**
|
||||
* Ensures dataset items count matches underlying test case required parameters
|
||||
*
|
||||
* @throws ReflectionException
|
||||
* @throws DatasetArgsCountMismatch
|
||||
*/
|
||||
private function __ensureDatasetArgumentNumberMatches(array $arguments): void
|
||||
{
|
||||
return ltrim(self::class, 'P\\');
|
||||
if ($arguments === []) {
|
||||
return;
|
||||
}
|
||||
|
||||
$underlyingTest = Reflection::getFunctionVariable($this->__test, 'closure');
|
||||
$testReflection = new ReflectionFunction($underlyingTest);
|
||||
$requiredParametersCount = $testReflection->getNumberOfRequiredParameters();
|
||||
$suppliedParametersCount = count($arguments);
|
||||
|
||||
if ($suppliedParametersCount >= $requiredParametersCount) {
|
||||
return;
|
||||
}
|
||||
|
||||
throw new DatasetArgsCountMismatch($requiredParametersCount, $suppliedParametersCount);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Throwable
|
||||
*/
|
||||
private function __callClosure(Closure $closure, array $arguments): mixed
|
||||
{
|
||||
return ExceptionTrace::ensure(fn (): mixed => call_user_func_array(Closure::bind($closure, $this, $this::class), $arguments));
|
||||
}
|
||||
|
||||
/** @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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,154 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\Console;
|
||||
|
||||
use Pest\Actions\AddsDefaults;
|
||||
use Pest\Actions\AddsTests;
|
||||
use Pest\Actions\LoadStructure;
|
||||
use Pest\Actions\ValidatesConfiguration;
|
||||
use Pest\Contracts\Plugins\AddsOutput;
|
||||
use Pest\Contracts\Plugins\HandlesArguments;
|
||||
use Pest\Plugin\Loader;
|
||||
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
|
||||
{
|
||||
/*
|
||||
* First, let's call all plugins that want to handle arguments
|
||||
*/
|
||||
$plugins = Loader::getPlugins(HandlesArguments::class);
|
||||
|
||||
/** @var HandlesArguments $plugin */
|
||||
foreach ($plugins as $plugin) {
|
||||
$argv = $plugin->handleArguments($argv);
|
||||
}
|
||||
|
||||
/*
|
||||
* Next, as usual, let's send the console arguments to PHPUnit.
|
||||
*/
|
||||
parent::handleArguments($argv);
|
||||
|
||||
/*
|
||||
* Finally, 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);
|
||||
|
||||
/*
|
||||
* Let's call all plugins that want to add output after test execution
|
||||
*/
|
||||
$plugins = Loader::getPlugins(AddsOutput::class);
|
||||
|
||||
/** @var AddsOutput $plugin */
|
||||
foreach ($plugins as $plugin) {
|
||||
$result = $plugin->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))();
|
||||
}
|
||||
}
|
||||
@ -11,7 +11,11 @@ use Symfony\Component\Console\Output\OutputInterface;
|
||||
*/
|
||||
final 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 readonly OutputInterface $output)
|
||||
{
|
||||
$this->output = $output;
|
||||
// ..
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the Console Command.
|
||||
*/
|
||||
public function __invoke(): void
|
||||
{
|
||||
foreach (self::HELP_MESSAGES as $message) {
|
||||
|
||||
@ -4,8 +4,11 @@ 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;
|
||||
|
||||
@ -14,38 +17,59 @@ use Symfony\Component\Console\Question\ConfirmationQuestion;
|
||||
*/
|
||||
final 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 readonly InputInterface $input,
|
||||
private readonly OutputInterface $output
|
||||
) {
|
||||
// ..
|
||||
}
|
||||
|
||||
/**
|
||||
* Asks the user to support Pest.
|
||||
* Executes the Console Command.
|
||||
*/
|
||||
public function __invoke(): void
|
||||
{
|
||||
$wantsToSupport = (new SymfonyQuestionHelper())->ask(
|
||||
new ArrayInput([]),
|
||||
$this->output,
|
||||
new ConfirmationQuestion(
|
||||
'Can you quickly <options=bold>star our GitHub repository</>? 🙏🏻',
|
||||
true,
|
||||
)
|
||||
);
|
||||
$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(
|
||||
' <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') {
|
||||
@ -58,9 +82,5 @@ final class Thanks
|
||||
exec('xdg-open https://github.com/pestphp/pest');
|
||||
}
|
||||
}
|
||||
|
||||
foreach (self::FUNDING_MESSAGES as $message) {
|
||||
$this->output->writeln($message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
21
src/Contracts/AddsAnnotations.php
Normal file
21
src/Contracts/AddsAnnotations.php
Normal file
@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\Contracts;
|
||||
|
||||
use Pest\Factories\TestCaseMethodFactory;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
interface AddsAnnotations
|
||||
{
|
||||
/**
|
||||
* Adds annotations to the given test case method.
|
||||
*
|
||||
* @param array<int, string> $annotations
|
||||
* @return array<int, string>
|
||||
*/
|
||||
public function __invoke(TestCaseMethodFactory $method, array $annotations): array;
|
||||
}
|
||||
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)) {
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
interface HasPrintableTestCaseName extends \NunoMaduro\Collision\Contracts\Adapters\Phpunit\HasPrintableTestCaseName
|
||||
{
|
||||
}
|
||||
} else {
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
interface HasPrintableTestCaseName
|
||||
{
|
||||
}
|
||||
use NunoMaduro\Collision\Contracts\Adapters\Phpunit\HasPrintableTestCaseName as BaseHasPrintableTestCaseName;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
interface HasPrintableTestCaseName extends BaseHasPrintableTestCaseName
|
||||
{
|
||||
// ..
|
||||
}
|
||||
|
||||
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.
|
||||
* Adds arguments before the Test Suite execution.
|
||||
*
|
||||
* PLEASE NOTE: it is necessary to remove any custom argument from the array
|
||||
* because otherwise the application will complain about them
|
||||
*
|
||||
* @param array<int, string> $arguments
|
||||
*
|
||||
* @return array<int, string> the updated list of arguments
|
||||
* @param array<int, string> $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);
|
||||
}
|
||||
}
|
||||
@ -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));
|
||||
}
|
||||
}
|
||||
@ -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));
|
||||
}
|
||||
}
|
||||
15
src/Exceptions/DatasetArgsCountMismatch.php
Normal file
15
src/Exceptions/DatasetArgsCountMismatch.php
Normal file
@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
final class DatasetArgsCountMismatch extends Exception
|
||||
{
|
||||
public function __construct(int $requiredCount, int $suppliedCount)
|
||||
{
|
||||
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)
|
||||
{
|
||||
|
||||
32
src/Exceptions/DatasetMissing.php
Normal file
32
src/Exceptions/DatasetMissing.php
Normal file
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\Exceptions;
|
||||
|
||||
use BadFunctionCallException;
|
||||
use NunoMaduro\Collision\Contracts\RenderlessEditor;
|
||||
use NunoMaduro\Collision\Contracts\RenderlessTrace;
|
||||
use Symfony\Component\Console\Exception\ExceptionInterface;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class DatasetMissing extends BadFunctionCallException implements ExceptionInterface, RenderlessEditor, RenderlessTrace
|
||||
{
|
||||
/**
|
||||
* Creates a new Exception instance.
|
||||
*
|
||||
* @param array<string, string> $arguments
|
||||
*/
|
||||
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($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)
|
||||
{
|
||||
@ -15,10 +15,10 @@ use Symfony\Component\Console\Exception\ExceptionInterface;
|
||||
final class InvalidPestCommand extends InvalidArgumentException implements ExceptionInterface, RenderlessEditor, RenderlessTrace
|
||||
{
|
||||
/**
|
||||
* Creates a new instance of invalid pest command exception.
|
||||
* Creates a new Exception instance.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct('Please run `./vendor/bin/pest` instead of `/vendor/bin/phpunit`.');
|
||||
parent::__construct('Please run [./vendor/bin/pest] instead.');
|
||||
}
|
||||
}
|
||||
|
||||
@ -15,7 +15,7 @@ use Symfony\Component\Console\Exception\ExceptionInterface;
|
||||
final class MissingDependency extends InvalidArgumentException implements ExceptionInterface, RenderlessEditor, RenderlessTrace
|
||||
{
|
||||
/**
|
||||
* Creates a new instance of missing dependency.
|
||||
* Creates a new Exception instance.
|
||||
*/
|
||||
public function __construct(string $feature, string $dependency)
|
||||
{
|
||||
|
||||
38
src/Exceptions/NoDirtyTestsFound.php
Normal file
38
src/Exceptions/NoDirtyTestsFound.php
Normal file
@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\Exceptions;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use NunoMaduro\Collision\Contracts\RenderlessEditor;
|
||||
use NunoMaduro\Collision\Contracts\RenderlessTrace;
|
||||
use Pest\Contracts\Panicable;
|
||||
use Symfony\Component\Console\Exception\ExceptionInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class NoDirtyTestsFound extends InvalidArgumentException implements ExceptionInterface, Panicable, RenderlessEditor, RenderlessTrace
|
||||
{
|
||||
/**
|
||||
* Renders the panic on the given output.
|
||||
*/
|
||||
public function render(OutputInterface $output): void
|
||||
{
|
||||
$output->writeln([
|
||||
'',
|
||||
' <fg=white;options=bold;bg=blue> INFO </> No "dirty" tests found.',
|
||||
'',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* The exit code to be used.
|
||||
*/
|
||||
public function exitCode(): int
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@ -13,19 +13,18 @@ use RuntimeException;
|
||||
final class ShouldNotHappen extends RuntimeException
|
||||
{
|
||||
/**
|
||||
* Creates a new instance of should not happen.
|
||||
* Creates a new Exception instance.
|
||||
*/
|
||||
public function __construct(Exception $exception)
|
||||
{
|
||||
$message = $exception->getMessage();
|
||||
|
||||
parent::__construct(sprintf(<<<EOF
|
||||
|
||||
parent::__construct(sprintf(<<<'EOF'
|
||||
This should not happen - please create an new issue here: https://github.com/pestphp/pest.
|
||||
|
||||
- Issue: %s
|
||||
- PHP version: %s
|
||||
- Operating system: %s
|
||||
Issue: %s
|
||||
PHP version: %s
|
||||
Operating system: %s
|
||||
EOF
|
||||
, $message, phpversion(), PHP_OS), 1, $exception);
|
||||
}
|
||||
|
||||
@ -15,10 +15,10 @@ use Symfony\Component\Console\Exception\ExceptionInterface;
|
||||
final class TestAlreadyExist extends InvalidArgumentException implements ExceptionInterface, RenderlessEditor, RenderlessTrace
|
||||
{
|
||||
/**
|
||||
* Creates a new instance of test already exist.
|
||||
* Creates a new Exception instance.
|
||||
*/
|
||||
public function __construct(string $fileName, string $description)
|
||||
{
|
||||
parent::__construct(sprintf('A test with the description `%s` already exist in the filename `%s`.', $description, $fileName));
|
||||
parent::__construct(sprintf('A test with the description `%s` already exists in the filename `%s`.', $description, $fileName));
|
||||
}
|
||||
}
|
||||
|
||||
@ -15,7 +15,7 @@ use Symfony\Component\Console\Exception\ExceptionInterface;
|
||||
final class TestCaseAlreadyInUse extends InvalidArgumentException implements ExceptionInterface, RenderlessEditor, RenderlessTrace
|
||||
{
|
||||
/**
|
||||
* Creates a new instance of test case already in use.
|
||||
* Creates a new Exception instance.
|
||||
*/
|
||||
public function __construct(string $inUse, string $newOne, string $folder)
|
||||
{
|
||||
|
||||
@ -15,7 +15,7 @@ use Symfony\Component\Console\Exception\ExceptionInterface;
|
||||
final class TestCaseClassOrTraitNotFound 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 $testCaseClass)
|
||||
{
|
||||
|
||||
24
src/Exceptions/TestDescriptionMissing.php
Normal file
24
src/Exceptions/TestDescriptionMissing.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 TestDescriptionMissing extends InvalidArgumentException implements ExceptionInterface, RenderlessEditor, RenderlessTrace
|
||||
{
|
||||
/**
|
||||
* Creates a new Exception instance.
|
||||
*/
|
||||
public function __construct(string $fileName)
|
||||
{
|
||||
parent::__construct(sprintf('Test description is missing in the filename `%s`.', $fileName));
|
||||
}
|
||||
}
|
||||
1384
src/Expectation.php
1384
src/Expectation.php
File diff suppressed because it is too large
Load Diff
@ -2,47 +2,51 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest;
|
||||
namespace Pest\Expectations;
|
||||
|
||||
use Pest\Expectation;
|
||||
|
||||
use function expect;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @mixin Expectation
|
||||
* @template TValue
|
||||
*
|
||||
* @mixin Expectation<TValue>
|
||||
*/
|
||||
final class Each
|
||||
final class EachExpectation
|
||||
{
|
||||
/**
|
||||
* @var Expectation
|
||||
*/
|
||||
private $original;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $opposite = false;
|
||||
private bool $opposite = false;
|
||||
|
||||
/**
|
||||
* Creates an expectation on each item of the iterable "value".
|
||||
*
|
||||
* @param Expectation<TValue> $original
|
||||
*/
|
||||
public function __construct(Expectation $original)
|
||||
public function __construct(private readonly Expectation $original)
|
||||
{
|
||||
$this->original = $original;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new expectation.
|
||||
*
|
||||
* @param mixed $value
|
||||
* @template TAndValue
|
||||
*
|
||||
* @param TAndValue $value
|
||||
* @return Expectation<TAndValue>
|
||||
*/
|
||||
public function and($value): Expectation
|
||||
public function and(mixed $value): Expectation
|
||||
{
|
||||
return $this->original->and($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the opposite expectation for the value.
|
||||
*
|
||||
* @return self<TValue>
|
||||
*/
|
||||
public function not(): Each
|
||||
public function not(): self
|
||||
{
|
||||
$this->opposite = true;
|
||||
|
||||
@ -52,9 +56,10 @@ final class Each
|
||||
/**
|
||||
* Dynamically calls methods on the class with the given arguments on each item.
|
||||
*
|
||||
* @param array<int|string, mixed> $arguments
|
||||
* @param array<int|string, mixed> $arguments
|
||||
* @return self<TValue>
|
||||
*/
|
||||
public function __call(string $name, array $arguments): Each
|
||||
public function __call(string $name, array $arguments): self
|
||||
{
|
||||
foreach ($this->original->value as $item) {
|
||||
/* @phpstan-ignore-next-line */
|
||||
@ -68,8 +73,10 @@ final class Each
|
||||
|
||||
/**
|
||||
* Dynamically calls methods on the class without any arguments on each item.
|
||||
*
|
||||
* @return self<TValue>
|
||||
*/
|
||||
public function __get(string $name): Each
|
||||
public function __get(string $name): self
|
||||
{
|
||||
/* @phpstan-ignore-next-line */
|
||||
return $this->$name();
|
||||
183
src/Expectations/HigherOrderExpectation.php
Normal file
183
src/Expectations/HigherOrderExpectation.php
Normal file
@ -0,0 +1,183 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\Expectations;
|
||||
|
||||
use Closure;
|
||||
use Pest\Concerns\Retrievable;
|
||||
use Pest\Expectation;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @template TOriginalValue
|
||||
* @template TValue
|
||||
*
|
||||
* @mixin Expectation<TOriginalValue>
|
||||
*/
|
||||
final class HigherOrderExpectation
|
||||
{
|
||||
use Retrievable;
|
||||
|
||||
/**
|
||||
* @var Expectation<TValue>|EachExpectation<TValue>
|
||||
*/
|
||||
private Expectation|EachExpectation $expectation;
|
||||
|
||||
private bool $opposite = false;
|
||||
|
||||
private bool $shouldReset = false;
|
||||
|
||||
/**
|
||||
* Creates a new higher order expectation.
|
||||
*
|
||||
* @param Expectation<TOriginalValue> $original
|
||||
* @param TValue $value
|
||||
*/
|
||||
public function __construct(private readonly Expectation $original, mixed $value)
|
||||
{
|
||||
$this->expectation = $this->expect($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the opposite expectation for the value.
|
||||
*
|
||||
* @return self<TOriginalValue, TValue>
|
||||
*/
|
||||
public function not(): self
|
||||
{
|
||||
$this->opposite = ! $this->opposite;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new Expectation.
|
||||
*
|
||||
* @template TExpectValue
|
||||
*
|
||||
* @param TExpectValue $value
|
||||
* @return Expectation<TExpectValue>
|
||||
*/
|
||||
public function expect(mixed $value): Expectation
|
||||
{
|
||||
return new Expectation($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new expectation.
|
||||
*
|
||||
* @template TExpectValue
|
||||
*
|
||||
* @param TExpectValue $value
|
||||
* @return Expectation<TExpectValue>
|
||||
*/
|
||||
public function and(mixed $value): Expectation
|
||||
{
|
||||
return $this->expect($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope an expectation callback to the current value in
|
||||
* the HigherOrderExpectation chain.
|
||||
*
|
||||
* @param Closure(Expectation<TValue>): void $expectation
|
||||
* @return HigherOrderExpectation<TOriginalValue, TOriginalValue>
|
||||
*/
|
||||
public function scoped(Closure $expectation): self
|
||||
{
|
||||
$expectation->__invoke($this->expectation);
|
||||
|
||||
return new self($this->original, $this->original->value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new expectation with the decoded JSON value.
|
||||
*
|
||||
* @return self<TOriginalValue, array<string|int, mixed>|bool>
|
||||
*/
|
||||
public function json(): self
|
||||
{
|
||||
return new self($this->original, $this->expectation->json()->value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dynamically calls methods on the class with the given arguments.
|
||||
*
|
||||
* @param array<int, mixed> $arguments
|
||||
* @return self<TOriginalValue, mixed>|self<TOriginalValue, TValue>
|
||||
*/
|
||||
public function __call(string $name, array $arguments): self
|
||||
{
|
||||
if (! $this->expectationHasMethod($name)) {
|
||||
/* @phpstan-ignore-next-line */
|
||||
return new self($this->original, $this->getValue()->$name(...$arguments));
|
||||
}
|
||||
|
||||
return $this->performAssertion($name, $arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* Accesses properties in the value or in the expectation.
|
||||
*
|
||||
* @return self<TOriginalValue, mixed>|self<TOriginalValue, TValue>
|
||||
*/
|
||||
public function __get(string $name): self
|
||||
{
|
||||
if ($name === 'not') {
|
||||
return $this->not();
|
||||
}
|
||||
|
||||
if (! $this->expectationHasMethod($name)) {
|
||||
/** @var array<string, mixed>|object $value */
|
||||
$value = $this->getValue();
|
||||
|
||||
return new self($this->original, $this->retrieve($name, $value));
|
||||
}
|
||||
|
||||
return $this->performAssertion($name, []);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the original expectation has the given method name.
|
||||
*/
|
||||
private function expectationHasMethod(string $name): bool
|
||||
{
|
||||
if (method_exists($this->original, $name)) {
|
||||
return true;
|
||||
}
|
||||
if ($this->original::hasMethod($name)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $this->original::hasExtend($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the applicable value based on the current reset condition.
|
||||
*
|
||||
* @return TOriginalValue|TValue
|
||||
*/
|
||||
private function getValue(): mixed
|
||||
{
|
||||
return $this->shouldReset ? $this->original->value : $this->expectation->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs the given assertion with the current expectation.
|
||||
*
|
||||
* @param array<int, mixed> $arguments
|
||||
* @return self<TOriginalValue, TValue>
|
||||
*/
|
||||
private function performAssertion(string $name, array $arguments): self
|
||||
{
|
||||
/* @phpstan-ignore-next-line */
|
||||
$this->expectation = ($this->opposite ? $this->expectation->not() : $this->expectation)->{$name}(...$arguments);
|
||||
|
||||
$this->opposite = false;
|
||||
$this->shouldReset = true;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
535
src/Expectations/OppositeExpectation.php
Normal file
535
src/Expectations/OppositeExpectation.php
Normal file
@ -0,0 +1,535 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\Expectations;
|
||||
|
||||
use Attribute;
|
||||
use Pest\Arch\Contracts\ArchExpectation;
|
||||
use Pest\Arch\Expectations\Targeted;
|
||||
use Pest\Arch\Expectations\ToBeUsedIn;
|
||||
use Pest\Arch\Expectations\ToBeUsedInNothing;
|
||||
use Pest\Arch\Expectations\ToUse;
|
||||
use Pest\Arch\GroupArchExpectation;
|
||||
use Pest\Arch\PendingArchExpectation;
|
||||
use Pest\Arch\SingleArchExpectation;
|
||||
use Pest\Arch\Support\FileLineFinder;
|
||||
use Pest\Exceptions\InvalidExpectation;
|
||||
use Pest\Expectation;
|
||||
use Pest\Support\Arr;
|
||||
use Pest\Support\Exporter;
|
||||
use PHPUnit\Architecture\Elements\ObjectDescription;
|
||||
use PHPUnit\Framework\AssertionFailedError;
|
||||
use PHPUnit\Framework\ExpectationFailedException;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @template TValue
|
||||
*
|
||||
* @mixin Expectation<TValue>
|
||||
*/
|
||||
final class OppositeExpectation
|
||||
{
|
||||
/**
|
||||
* Creates a new opposite expectation.
|
||||
*
|
||||
* @param Expectation<TValue> $original
|
||||
*/
|
||||
public function __construct(private readonly Expectation $original)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the value array not has the provided $keys.
|
||||
*
|
||||
* @param array<int, int|string|array<int-string, mixed>> $keys
|
||||
* @return Expectation<TValue>
|
||||
*/
|
||||
public function toHaveKeys(array $keys): Expectation
|
||||
{
|
||||
foreach ($keys as $k => $key) {
|
||||
try {
|
||||
if (is_array($key)) {
|
||||
$this->toHaveKeys(array_keys(Arr::dot($key, $k.'.')));
|
||||
} else {
|
||||
$this->original->toHaveKey($key);
|
||||
}
|
||||
} catch (ExpectationFailedException) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->throwExpectationFailedException('toHaveKey', [$key]);
|
||||
}
|
||||
|
||||
return $this->original;
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the given expectation target does not use any of the given dependencies.
|
||||
*
|
||||
* @param array<int, string>|string $targets
|
||||
*/
|
||||
public function toUse(array|string $targets): ArchExpectation
|
||||
{
|
||||
return GroupArchExpectation::fromExpectations($this->original, array_map(fn (string $target): SingleArchExpectation => ToUse::make($this->original, $target)->opposite(
|
||||
fn () => $this->throwExpectationFailedException('toUse', $target),
|
||||
), is_string($targets) ? [$targets] : $targets));
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the given expectation target does not use the "declare(strict_types=1)" declaration.
|
||||
*/
|
||||
public function toUseStrictTypes(): ArchExpectation
|
||||
{
|
||||
return Targeted::make(
|
||||
$this->original,
|
||||
fn (ObjectDescription $object): bool => ! str_contains((string) file_get_contents($object->path), 'declare(strict_types=1);'),
|
||||
'not to use strict types',
|
||||
FileLineFinder::where(fn (string $line): bool => str_contains($line, '<?php')),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the given expectation target is not final.
|
||||
*/
|
||||
public function toBeFinal(): ArchExpectation
|
||||
{
|
||||
return Targeted::make(
|
||||
$this->original,
|
||||
fn (ObjectDescription $object): bool => ! enum_exists($object->name) && ! $object->reflectionClass->isFinal(),
|
||||
'not to be final',
|
||||
FileLineFinder::where(fn (string $line): bool => str_contains($line, 'class')),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the given expectation target is not readonly.
|
||||
*/
|
||||
public function toBeReadonly(): ArchExpectation
|
||||
{
|
||||
return Targeted::make(
|
||||
$this->original,
|
||||
fn (ObjectDescription $object): bool => ! enum_exists($object->name) && ! $object->reflectionClass->isReadOnly() && assert(true), // @phpstan-ignore-line
|
||||
'not to be readonly',
|
||||
FileLineFinder::where(fn (string $line): bool => str_contains($line, 'class')),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the given expectation target is not trait.
|
||||
*/
|
||||
public function toBeTrait(): ArchExpectation
|
||||
{
|
||||
return Targeted::make(
|
||||
$this->original,
|
||||
fn (ObjectDescription $object): bool => ! $object->reflectionClass->isTrait(),
|
||||
'not to be trait',
|
||||
FileLineFinder::where(fn (string $line): bool => str_contains($line, 'class')),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the given expectation targets are not traits.
|
||||
*/
|
||||
public function toBeTraits(): ArchExpectation
|
||||
{
|
||||
return $this->toBeTrait();
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the given expectation target is not abstract.
|
||||
*/
|
||||
public function toBeAbstract(): ArchExpectation
|
||||
{
|
||||
return Targeted::make(
|
||||
$this->original,
|
||||
fn (ObjectDescription $object): bool => ! $object->reflectionClass->isAbstract(),
|
||||
'not to be abstract',
|
||||
FileLineFinder::where(fn (string $line): bool => str_contains($line, 'class')),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the given expectation target does not have a specific method.
|
||||
*/
|
||||
public function toHaveMethod(string $method): ArchExpectation
|
||||
{
|
||||
return Targeted::make(
|
||||
$this->original,
|
||||
fn (ObjectDescription $object): bool => ! $object->reflectionClass->hasMethod($method),
|
||||
'to not have method',
|
||||
FileLineFinder::where(fn (string $line): bool => str_contains($line, 'class')),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the given expectation target is not enum.
|
||||
*/
|
||||
public function toBeEnum(): ArchExpectation
|
||||
{
|
||||
return Targeted::make(
|
||||
$this->original,
|
||||
fn (ObjectDescription $object): bool => ! $object->reflectionClass->isEnum(),
|
||||
'not to be enum',
|
||||
FileLineFinder::where(fn (string $line): bool => str_contains($line, 'class')),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the given expectation targets are not enums.
|
||||
*/
|
||||
public function toBeEnums(): ArchExpectation
|
||||
{
|
||||
return $this->toBeEnum();
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the given expectation targets is not class.
|
||||
*/
|
||||
public function toBeClass(): ArchExpectation
|
||||
{
|
||||
return Targeted::make(
|
||||
$this->original,
|
||||
fn (ObjectDescription $object): bool => ! class_exists($object->name),
|
||||
'not to be class',
|
||||
FileLineFinder::where(fn (string $line): bool => true),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the given expectation targets are not classes.
|
||||
*/
|
||||
public function toBeClasses(): ArchExpectation
|
||||
{
|
||||
return $this->toBeClass();
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the given expectation target is not interface.
|
||||
*/
|
||||
public function toBeInterface(): ArchExpectation
|
||||
{
|
||||
return Targeted::make(
|
||||
$this->original,
|
||||
fn (ObjectDescription $object): bool => ! $object->reflectionClass->isInterface(),
|
||||
'not to be interface',
|
||||
FileLineFinder::where(fn (string $line): bool => str_contains($line, 'class')),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the given expectation targets are not interfaces.
|
||||
*/
|
||||
public function toBeInterfaces(): ArchExpectation
|
||||
{
|
||||
return $this->toBeInterface();
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the given expectation target to be not subclass of the given class.
|
||||
*
|
||||
* @param class-string $class
|
||||
*/
|
||||
public function toExtend(string $class): ArchExpectation
|
||||
{
|
||||
return Targeted::make(
|
||||
$this->original,
|
||||
fn (ObjectDescription $object): bool => ! $object->reflectionClass->isSubclassOf($class),
|
||||
sprintf("not to extend '%s'", $class),
|
||||
FileLineFinder::where(fn (string $line): bool => str_contains($line, 'class')),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the given expectation target to be not have any parent class.
|
||||
*/
|
||||
public function toExtendNothing(): ArchExpectation
|
||||
{
|
||||
return Targeted::make(
|
||||
$this->original,
|
||||
fn (ObjectDescription $object): bool => $object->reflectionClass->getParentClass() !== false,
|
||||
'to extend a class',
|
||||
FileLineFinder::where(fn (string $line): bool => str_contains($line, 'class')),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the given expectation target not to implement the given interfaces.
|
||||
*
|
||||
* @param array<int, class-string>|string $interfaces
|
||||
*/
|
||||
public function toImplement(array|string $interfaces): ArchExpectation
|
||||
{
|
||||
$interfaces = is_array($interfaces) ? $interfaces : [$interfaces];
|
||||
|
||||
return Targeted::make(
|
||||
$this->original,
|
||||
function (ObjectDescription $object) use ($interfaces): bool {
|
||||
foreach ($interfaces as $interface) {
|
||||
if ($object->reflectionClass->implementsInterface($interface)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
"not to implement '".implode("', '", $interfaces)."'",
|
||||
FileLineFinder::where(fn (string $line): bool => str_contains($line, 'class')),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the given expectation target to not implement any interfaces.
|
||||
*/
|
||||
public function toImplementNothing(): ArchExpectation
|
||||
{
|
||||
return Targeted::make(
|
||||
$this->original,
|
||||
fn (ObjectDescription $object): bool => $object->reflectionClass->getInterfaceNames() !== [],
|
||||
'to implement an interface',
|
||||
FileLineFinder::where(fn (string $line): bool => str_contains($line, 'class')),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Not supported.
|
||||
*
|
||||
* @param array<int, class-string>|string $interfaces
|
||||
*/
|
||||
public function toOnlyImplement(array|string $interfaces): never
|
||||
{
|
||||
throw InvalidExpectation::fromMethods(['not', 'toOnlyImplement']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the given expectation target to not have the given prefix.
|
||||
*/
|
||||
public function toHavePrefix(string $prefix): ArchExpectation
|
||||
{
|
||||
return Targeted::make(
|
||||
$this->original,
|
||||
fn (ObjectDescription $object): bool => ! str_starts_with($object->reflectionClass->getShortName(), $prefix),
|
||||
"not to have prefix '{$prefix}'",
|
||||
FileLineFinder::where(fn (string $line): bool => str_contains($line, 'class')),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the given expectation target to not have the given suffix.
|
||||
*/
|
||||
public function toHaveSuffix(string $suffix): ArchExpectation
|
||||
{
|
||||
return Targeted::make(
|
||||
$this->original,
|
||||
fn (ObjectDescription $object): bool => ! str_ends_with($object->reflectionClass->getName(), $suffix),
|
||||
"not to have suffix '{$suffix}'",
|
||||
FileLineFinder::where(fn (string $line): bool => str_contains($line, 'class')),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Not supported.
|
||||
*
|
||||
* @param array<int, string>|string $targets
|
||||
*/
|
||||
public function toOnlyUse(array|string $targets): never
|
||||
{
|
||||
throw InvalidExpectation::fromMethods(['not', 'toOnlyUse']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Not supported.
|
||||
*/
|
||||
public function toUseNothing(): never
|
||||
{
|
||||
throw InvalidExpectation::fromMethods(['not', 'toUseNothing']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the given expectation dependency is not used.
|
||||
*/
|
||||
public function toBeUsed(): ArchExpectation
|
||||
{
|
||||
return ToBeUsedInNothing::make($this->original);
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the given expectation dependency is not used by any of the given targets.
|
||||
*
|
||||
* @param array<int, string>|string $targets
|
||||
*/
|
||||
public function toBeUsedIn(array|string $targets): ArchExpectation
|
||||
{
|
||||
return GroupArchExpectation::fromExpectations($this->original, array_map(fn (string $target): GroupArchExpectation => ToBeUsedIn::make($this->original, $target)->opposite(
|
||||
fn () => $this->throwExpectationFailedException('toBeUsedIn', $target),
|
||||
), is_string($targets) ? [$targets] : $targets));
|
||||
}
|
||||
|
||||
public function toOnlyBeUsedIn(): never
|
||||
{
|
||||
throw InvalidExpectation::fromMethods(['not', 'toOnlyBeUsedIn']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the given expectation dependency is not used.
|
||||
*/
|
||||
public function toBeUsedInNothing(): never
|
||||
{
|
||||
throw InvalidExpectation::fromMethods(['not', 'toBeUsedInNothing']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the given expectation dependency is not an invokable class.
|
||||
*/
|
||||
public function toBeInvokable(): ArchExpectation
|
||||
{
|
||||
return Targeted::make(
|
||||
$this->original,
|
||||
fn (ObjectDescription $object): bool => ! $object->reflectionClass->hasMethod('__invoke'),
|
||||
'to not be invokable',
|
||||
FileLineFinder::where(fn (string $line): bool => str_contains($line, 'class'))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the given expectation target not to have the given attribute.
|
||||
*
|
||||
* @param class-string<Attribute> $attribute
|
||||
*/
|
||||
public function toHaveAttribute(string $attribute): ArchExpectation
|
||||
{
|
||||
return Targeted::make(
|
||||
$this->original,
|
||||
fn (ObjectDescription $object): bool => $object->reflectionClass->getAttributes($attribute) === [],
|
||||
"to not have attribute '{$attribute}'",
|
||||
FileLineFinder::where(fn (string $line): bool => str_contains($line, 'class'))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle dynamic method calls into the original expectation.
|
||||
*
|
||||
* @param array<int, mixed> $arguments
|
||||
* @return Expectation<TValue>|Expectation<mixed>|never
|
||||
*/
|
||||
public function __call(string $name, array $arguments): Expectation
|
||||
{
|
||||
try {
|
||||
if (! is_object($this->original->value) && method_exists(PendingArchExpectation::class, $name)) {
|
||||
throw InvalidExpectation::fromMethods(['not', $name]);
|
||||
}
|
||||
|
||||
/* @phpstan-ignore-next-line */
|
||||
$this->original->{$name}(...$arguments);
|
||||
} catch (ExpectationFailedException|AssertionFailedError) {
|
||||
return $this->original;
|
||||
}
|
||||
|
||||
$this->throwExpectationFailedException($name, $arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle dynamic properties gets into the original expectation.
|
||||
*
|
||||
* @return Expectation<TValue>|Expectation<mixed>|never
|
||||
*/
|
||||
public function __get(string $name): Expectation
|
||||
{
|
||||
try {
|
||||
if (! is_object($this->original->value) && method_exists(PendingArchExpectation::class, $name)) {
|
||||
throw InvalidExpectation::fromMethods(['not', $name]);
|
||||
}
|
||||
|
||||
$this->original->{$name}; // @phpstan-ignore-line
|
||||
} catch (ExpectationFailedException) {
|
||||
return $this->original;
|
||||
}
|
||||
|
||||
$this->throwExpectationFailedException($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new expectation failed exception with a nice readable message.
|
||||
*
|
||||
* @param array<int, mixed>|string $arguments
|
||||
*/
|
||||
public function throwExpectationFailedException(string $name, array|string $arguments = []): never
|
||||
{
|
||||
$arguments = is_array($arguments) ? $arguments : [$arguments];
|
||||
|
||||
$exporter = Exporter::default();
|
||||
|
||||
$toString = fn (mixed $argument): string => $exporter->shortenedExport($argument);
|
||||
|
||||
throw new ExpectationFailedException(sprintf(
|
||||
'Expecting %s not %s %s.',
|
||||
$toString($this->original->value),
|
||||
strtolower((string) preg_replace('/(?<!\ )[A-Z]/', ' $0', $name)),
|
||||
implode(' ', array_map(fn (mixed $argument): string => $toString($argument), $arguments)),
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the given expectation target does not have a constructor method.
|
||||
*/
|
||||
public function toHaveConstructor(): ArchExpectation
|
||||
{
|
||||
return $this->toHaveMethod('__construct');
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the given expectation target does not have a destructor method.
|
||||
*/
|
||||
public function toHaveDestructor(): ArchExpectation
|
||||
{
|
||||
return $this->toHaveMethod('__destruct');
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the given expectation target is not a backed enum of given type.
|
||||
*/
|
||||
private function toBeBackedEnum(string $backingType): ArchExpectation
|
||||
{
|
||||
return Targeted::make(
|
||||
$this->original,
|
||||
fn (ObjectDescription $object): bool => ! $object->reflectionClass->isEnum()
|
||||
|| ! (new \ReflectionEnum($object->name))->isBacked() // @phpstan-ignore-line
|
||||
|| (string) (new \ReflectionEnum($object->name))->getBackingType() !== $backingType, // @phpstan-ignore-line
|
||||
'not to be '.$backingType.' backed enum',
|
||||
FileLineFinder::where(fn (string $line): bool => str_contains($line, 'class')),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the given expectation targets are not string backed enums.
|
||||
*/
|
||||
public function toBeStringBackedEnums(): ArchExpectation
|
||||
{
|
||||
return $this->toBeStringBackedEnum();
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the given expectation targets are not int backed enums.
|
||||
*/
|
||||
public function toBeIntBackedEnums(): ArchExpectation
|
||||
{
|
||||
return $this->toBeIntBackedEnum();
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the given expectation target is not a string backed enum.
|
||||
*/
|
||||
public function toBeStringBackedEnum(): ArchExpectation
|
||||
{
|
||||
return $this->toBeBackedEnum('string');
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the given expectation target is not an int backed enum.
|
||||
*/
|
||||
public function toBeIntBackedEnum(): ArchExpectation
|
||||
{
|
||||
return $this->toBeBackedEnum('int');
|
||||
}
|
||||
}
|
||||
27
src/Factories/Annotations/CoversNothing.php
Normal file
27
src/Factories/Annotations/CoversNothing.php
Normal file
@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\Factories\Annotations;
|
||||
|
||||
use Pest\Contracts\AddsAnnotations;
|
||||
use Pest\Factories\Covers\CoversNothing as CoversNothingFactory;
|
||||
use Pest\Factories\TestCaseMethodFactory;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class CoversNothing implements AddsAnnotations
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __invoke(TestCaseMethodFactory $method, array $annotations): array
|
||||
{
|
||||
if (($method->covers[0] ?? null) instanceof CoversNothingFactory) {
|
||||
$annotations[] = '@coversNothing';
|
||||
}
|
||||
|
||||
return $annotations;
|
||||
}
|
||||
}
|
||||
29
src/Factories/Annotations/Depends.php
Normal file
29
src/Factories/Annotations/Depends.php
Normal file
@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\Factories\Annotations;
|
||||
|
||||
use Pest\Contracts\AddsAnnotations;
|
||||
use Pest\Factories\TestCaseMethodFactory;
|
||||
use Pest\Support\Str;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class Depends implements AddsAnnotations
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __invoke(TestCaseMethodFactory $method, array $annotations): array
|
||||
{
|
||||
foreach ($method->depends as $depend) {
|
||||
$depend = Str::evaluable($method->describing !== null ? Str::describe($method->describing, $depend) : $depend);
|
||||
|
||||
$annotations[] = "@depends $depend";
|
||||
}
|
||||
|
||||
return $annotations;
|
||||
}
|
||||
}
|
||||
26
src/Factories/Annotations/Groups.php
Normal file
26
src/Factories/Annotations/Groups.php
Normal file
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\Factories\Annotations;
|
||||
|
||||
use Pest\Contracts\AddsAnnotations;
|
||||
use Pest\Factories\TestCaseMethodFactory;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class Groups implements AddsAnnotations
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __invoke(TestCaseMethodFactory $method, array $annotations): array
|
||||
{
|
||||
foreach ($method->groups as $group) {
|
||||
$annotations[] = "@group $group";
|
||||
}
|
||||
|
||||
return $annotations;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user