first commit
This commit is contained in:
parent
a744962900
commit
cc1f5371b1
|
@ -0,0 +1,6 @@
|
|||
__pycache__/
|
||||
.pytest_cache/
|
||||
.idea/
|
||||
./config/settings.py
|
||||
*.pyc
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
[[source]]
|
||||
url = "https://pypi.douban.com/simple"
|
||||
verify_ssl = true
|
||||
name = "pypi"
|
||||
|
||||
[packages]
|
||||
selenium = "==3.141.0"
|
||||
allure-python-commons = "==2.9.43"
|
||||
pytest = "==6.2.4"
|
||||
yagmail = "==0.14.256"
|
||||
requests = "==2.26.0"
|
||||
urllib3 = "==1.26.6"
|
||||
openpyxl = "==3.0.7"
|
||||
click = "==8.0.1"
|
||||
loguru = "==0.5.3"
|
||||
pytest-rerunfailures = "==10.1"
|
||||
Faker = "==8.10.1"
|
||||
PyMySQL = "==1.0.2"
|
||||
allure-pytest = "*"
|
||||
PyYAML = "==5.4.1"
|
||||
pytest-assume = "*"
|
||||
|
||||
[dev-packages]
|
||||
|
||||
[requires]
|
||||
python_version = "3.8"
|
|
@ -0,0 +1,362 @@
|
|||
{
|
||||
"_meta": {
|
||||
"hash": {
|
||||
"sha256": "85c5967db3107f2f6694d875cfc0e8292f0d9b880ff52376e83065bcb2f24eaf"
|
||||
},
|
||||
"pipfile-spec": 6,
|
||||
"requires": {
|
||||
"python_version": "3.8"
|
||||
},
|
||||
"sources": [
|
||||
{
|
||||
"name": "pypi",
|
||||
"url": "https://pypi.douban.com/simple",
|
||||
"verify_ssl": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"default": {
|
||||
"allure-pytest": {
|
||||
"hashes": [
|
||||
"sha256:9ba613858a33c6becba539966116be5720c15f8c0427122e03c41082cfaa173a",
|
||||
"sha256:b2ba613fe33b53924fdef2f21ee72cf14c2cc39a0ee8dadefd5d6ed7c927f430"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==2.9.43"
|
||||
},
|
||||
"allure-python-commons": {
|
||||
"hashes": [
|
||||
"sha256:07e366f099c5ae248a1e8c56713bc9c25316ce8269944b4b038c62d2aacd8dbb",
|
||||
"sha256:461bdd17c6514e130cfbb01d7060c82821a5ae1d2e049daefe64c4738d865cef"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==2.9.43"
|
||||
},
|
||||
"attrs": {
|
||||
"hashes": [
|
||||
"sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1",
|
||||
"sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
|
||||
"version": "==21.2.0"
|
||||
},
|
||||
"cachetools": {
|
||||
"hashes": [
|
||||
"sha256:2cc0b89715337ab6dbba85b5b50effe2b0c74e035d83ee8ed637cf52f12ae001",
|
||||
"sha256:61b5ed1e22a0924aed1d23b478f37e8d52549ff8a961de2909c69bf950020cff"
|
||||
],
|
||||
"markers": "python_version ~= '3.5'",
|
||||
"version": "==4.2.2"
|
||||
},
|
||||
"certifi": {
|
||||
"hashes": [
|
||||
"sha256:2bbf76fd432960138b3ef6dda3dde0544f27cbf8546c458e60baf371917ba9ee",
|
||||
"sha256:50b1e4f8446b06f41be7dd6338db18e0990601dce795c2b1686458aa7e8fa7d8"
|
||||
],
|
||||
"version": "==2021.5.30"
|
||||
},
|
||||
"charset-normalizer": {
|
||||
"hashes": [
|
||||
"sha256:5d209c0a931f215cee683b6445e2d77677e7e75e159f78def0db09d68fafcaa6",
|
||||
"sha256:5ec46d183433dcbd0ab716f2d7f29d8dee50505b3fdb40c6b985c7c4f5a3591f"
|
||||
],
|
||||
"markers": "python_version >= '3'",
|
||||
"version": "==2.0.6"
|
||||
},
|
||||
"click": {
|
||||
"hashes": [
|
||||
"sha256:8c04c11192119b1ef78ea049e0a6f0463e4c48ef00a30160c704337586f3ad7a",
|
||||
"sha256:fba402a4a47334742d782209a7c79bc448911afe1149d07bdabdf480b3e2f4b6"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==8.0.1"
|
||||
},
|
||||
"cssselect": {
|
||||
"hashes": [
|
||||
"sha256:f612ee47b749c877ebae5bb77035d8f4202c6ad0f0fc1271b3c18ad6c4468ecf",
|
||||
"sha256:f95f8dedd925fd8f54edb3d2dfb44c190d9d18512377d3c1e2388d16126879bc"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
||||
"version": "==1.1.0"
|
||||
},
|
||||
"cssutils": {
|
||||
"hashes": [
|
||||
"sha256:0cf1f6086b020dee18048ff3999339499f725934017ef9ae2cd5bb77f9ab5f46",
|
||||
"sha256:b2d3b16047caae82e5c590036935bafa1b621cf45c2f38885af4be4838f0fd00"
|
||||
],
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==2.3.0"
|
||||
},
|
||||
"et-xmlfile": {
|
||||
"hashes": [
|
||||
"sha256:8eb9e2bc2f8c97e37a2dc85a09ecdcdec9d8a396530a6d5a33b30b9a92da0c5c",
|
||||
"sha256:a2ba85d1d6a74ef63837eed693bcb89c3f752169b0e3e7ae5b16ca5e1b3deada"
|
||||
],
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==1.1.0"
|
||||
},
|
||||
"faker": {
|
||||
"hashes": [
|
||||
"sha256:9ac6b39b9618f55be6b8b45089e624564469a035cc845c69ce990332ce3663f4",
|
||||
"sha256:a665e6e2e9087ec9ad4ebcd2f09acd031b44193ee93401817001b6557c6502b4"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==8.10.1"
|
||||
},
|
||||
"idna": {
|
||||
"hashes": [
|
||||
"sha256:14475042e284991034cb48e06f6851428fb14c4dc953acd9be9a5e95c7b6dd7a",
|
||||
"sha256:467fbad99067910785144ce333826c71fb0e63a425657295239737f7ecd125f3"
|
||||
],
|
||||
"markers": "python_version >= '3'",
|
||||
"version": "==3.2"
|
||||
},
|
||||
"iniconfig": {
|
||||
"hashes": [
|
||||
"sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3",
|
||||
"sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"
|
||||
],
|
||||
"version": "==1.1.1"
|
||||
},
|
||||
"loguru": {
|
||||
"hashes": [
|
||||
"sha256:b28e72ac7a98be3d28ad28570299a393dfcd32e5e3f6a353dec94675767b6319",
|
||||
"sha256:f8087ac396b5ee5f67c963b495d615ebbceac2796379599820e324419d53667c"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==0.5.3"
|
||||
},
|
||||
"lxml": {
|
||||
"hashes": [
|
||||
"sha256:079f3ae844f38982d156efce585bc540c16a926d4436712cf4baee0cce487a3d",
|
||||
"sha256:0fbcf5565ac01dff87cbfc0ff323515c823081c5777a9fc7703ff58388c258c3",
|
||||
"sha256:122fba10466c7bd4178b07dba427aa516286b846b2cbd6f6169141917283aae2",
|
||||
"sha256:1b38116b6e628118dea5b2186ee6820ab138dbb1e24a13e478490c7db2f326ae",
|
||||
"sha256:1b7584d421d254ab86d4f0b13ec662a9014397678a7c4265a02a6d7c2b18a75f",
|
||||
"sha256:26e761ab5b07adf5f555ee82fb4bfc35bf93750499c6c7614bd64d12aaa67927",
|
||||
"sha256:289e9ca1a9287f08daaf796d96e06cb2bc2958891d7911ac7cae1c5f9e1e0ee3",
|
||||
"sha256:2a9d50e69aac3ebee695424f7dbd7b8c6d6eb7de2a2eb6b0f6c7db6aa41e02b7",
|
||||
"sha256:3082c518be8e97324390614dacd041bb1358c882d77108ca1957ba47738d9d59",
|
||||
"sha256:33bb934a044cf32157c12bfcfbb6649807da20aa92c062ef51903415c704704f",
|
||||
"sha256:3439c71103ef0e904ea0a1901611863e51f50b5cd5e8654a151740fde5e1cade",
|
||||
"sha256:36108c73739985979bf302006527cf8a20515ce444ba916281d1c43938b8bb96",
|
||||
"sha256:39b78571b3b30645ac77b95f7c69d1bffc4cf8c3b157c435a34da72e78c82468",
|
||||
"sha256:4289728b5e2000a4ad4ab8da6e1db2e093c63c08bdc0414799ee776a3f78da4b",
|
||||
"sha256:4bff24dfeea62f2e56f5bab929b4428ae6caba2d1eea0c2d6eb618e30a71e6d4",
|
||||
"sha256:4c61b3a0db43a1607d6264166b230438f85bfed02e8cff20c22e564d0faff354",
|
||||
"sha256:542d454665a3e277f76954418124d67516c5f88e51a900365ed54a9806122b83",
|
||||
"sha256:5a0a14e264069c03e46f926be0d8919f4105c1623d620e7ec0e612a2e9bf1c04",
|
||||
"sha256:5c8c163396cc0df3fd151b927e74f6e4acd67160d6c33304e805b84293351d16",
|
||||
"sha256:64812391546a18896adaa86c77c59a4998f33c24788cadc35789e55b727a37f4",
|
||||
"sha256:66e575c62792c3f9ca47cb8b6fab9e35bab91360c783d1606f758761810c9791",
|
||||
"sha256:6f12e1427285008fd32a6025e38e977d44d6382cf28e7201ed10d6c1698d2a9a",
|
||||
"sha256:74f7d8d439b18fa4c385f3f5dfd11144bb87c1da034a466c5b5577d23a1d9b51",
|
||||
"sha256:7610b8c31688f0b1be0ef882889817939490a36d0ee880ea562a4e1399c447a1",
|
||||
"sha256:76fa7b1362d19f8fbd3e75fe2fb7c79359b0af8747e6f7141c338f0bee2f871a",
|
||||
"sha256:7728e05c35412ba36d3e9795ae8995e3c86958179c9770e65558ec3fdfd3724f",
|
||||
"sha256:8157dadbb09a34a6bd95a50690595e1fa0af1a99445e2744110e3dca7831c4ee",
|
||||
"sha256:820628b7b3135403540202e60551e741f9b6d3304371712521be939470b454ec",
|
||||
"sha256:884ab9b29feaca361f7f88d811b1eea9bfca36cf3da27768d28ad45c3ee6f969",
|
||||
"sha256:89b8b22a5ff72d89d48d0e62abb14340d9e99fd637d046c27b8b257a01ffbe28",
|
||||
"sha256:92e821e43ad382332eade6812e298dc9701c75fe289f2a2d39c7960b43d1e92a",
|
||||
"sha256:b007cbb845b28db4fb8b6a5cdcbf65bacb16a8bd328b53cbc0698688a68e1caa",
|
||||
"sha256:bc4313cbeb0e7a416a488d72f9680fffffc645f8a838bd2193809881c67dd106",
|
||||
"sha256:bccbfc27563652de7dc9bdc595cb25e90b59c5f8e23e806ed0fd623755b6565d",
|
||||
"sha256:c1a40c06fd5ba37ad39caa0b3144eb3772e813b5fb5b084198a985431c2f1e8d",
|
||||
"sha256:c47ff7e0a36d4efac9fd692cfa33fbd0636674c102e9e8d9b26e1b93a94e7617",
|
||||
"sha256:c4f05c5a7c49d2fb70223d0d5bcfbe474cf928310ac9fa6a7c6dddc831d0b1d4",
|
||||
"sha256:cdaf11d2bd275bf391b5308f86731e5194a21af45fbaaaf1d9e8147b9160ea92",
|
||||
"sha256:ce256aaa50f6cc9a649c51be3cd4ff142d67295bfc4f490c9134d0f9f6d58ef0",
|
||||
"sha256:d2e35d7bf1c1ac8c538f88d26b396e73dd81440d59c1ef8522e1ea77b345ede4",
|
||||
"sha256:d916d31fd85b2f78c76400d625076d9124de3e4bda8b016d25a050cc7d603f24",
|
||||
"sha256:df7c53783a46febb0e70f6b05df2ba104610f2fb0d27023409734a3ecbb78fb2",
|
||||
"sha256:e1cbd3f19a61e27e011e02f9600837b921ac661f0c40560eefb366e4e4fb275e",
|
||||
"sha256:efac139c3f0bf4f0939f9375af4b02c5ad83a622de52d6dfa8e438e8e01d0eb0",
|
||||
"sha256:efd7a09678fd8b53117f6bae4fa3825e0a22b03ef0a932e070c0bdbb3a35e654",
|
||||
"sha256:f2380a6376dfa090227b663f9678150ef27543483055cc327555fb592c5967e2",
|
||||
"sha256:f8380c03e45cf09f8557bdaa41e1fa7c81f3ae22828e1db470ab2a6c96d8bc23",
|
||||
"sha256:f90ba11136bfdd25cae3951af8da2e95121c9b9b93727b1b896e3fa105b2f586"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
|
||||
"version": "==4.6.3"
|
||||
},
|
||||
"openpyxl": {
|
||||
"hashes": [
|
||||
"sha256:46af4eaf201a89b610fcca177eed957635f88770a5462fb6aae4a2a52b0ff516",
|
||||
"sha256:6456a3b472e1ef0facb1129f3c6ef00713cebf62e736cd7a75bcc3247432f251"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==3.0.7"
|
||||
},
|
||||
"packaging": {
|
||||
"hashes": [
|
||||
"sha256:7dc96269f53a4ccec5c0670940a4281106dd0bb343f47b7471f779df49c2fbe7",
|
||||
"sha256:c86254f9220d55e31cc94d69bade760f0847da8000def4dfe1c6b872fd14ff14"
|
||||
],
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==21.0"
|
||||
},
|
||||
"pluggy": {
|
||||
"hashes": [
|
||||
"sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0",
|
||||
"sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
||||
"version": "==0.13.1"
|
||||
},
|
||||
"premailer": {
|
||||
"hashes": [
|
||||
"sha256:021b8196364d7df96d04f9ade51b794d0b77bcc19e998321c515633a2273be1a",
|
||||
"sha256:d1875a8411f5dc92b53ef9f193db6c0f879dc378d618e0ad292723e388bfe4c2"
|
||||
],
|
||||
"version": "==3.10.0"
|
||||
},
|
||||
"py": {
|
||||
"hashes": [
|
||||
"sha256:21b81bda15b66ef5e1a777a21c4dcd9c20ad3efd0b3f817e7a809035269e1bd3",
|
||||
"sha256:3b80836aa6d1feeaa108e046da6423ab8f6ceda6468545ae8d02d9d58d18818a"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
||||
"version": "==1.10.0"
|
||||
},
|
||||
"pymysql": {
|
||||
"hashes": [
|
||||
"sha256:41fc3a0c5013d5f039639442321185532e3e2c8924687abe6537de157d403641",
|
||||
"sha256:816927a350f38d56072aeca5dfb10221fe1dc653745853d30a216637f5d7ad36"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==1.0.2"
|
||||
},
|
||||
"pyparsing": {
|
||||
"hashes": [
|
||||
"sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1",
|
||||
"sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"
|
||||
],
|
||||
"markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
||||
"version": "==2.4.7"
|
||||
},
|
||||
"pytest": {
|
||||
"hashes": [
|
||||
"sha256:50bcad0a0b9c5a72c8e4e7c9855a3ad496ca6a881a3641b4260605450772c54b",
|
||||
"sha256:91ef2131a9bd6be8f76f1f08eac5c5317221d6ad1e143ae03894b862e8976890"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==6.2.4"
|
||||
},
|
||||
"pytest-assume": {
|
||||
"hashes": [
|
||||
"sha256:09074220213b87cf1f400609701a44b75078b4e69588c7227e061ff9b3ecf54b",
|
||||
"sha256:983e90537660fd7aa18bb554693523020272e7edeee3e40435593297bcc6347e"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==2.4.3"
|
||||
},
|
||||
"pytest-rerunfailures": {
|
||||
"hashes": [
|
||||
"sha256:53db94acf7499c75c5257c79d8a1dc22c3db4bc8d32ec3a713ea91eda3f98359",
|
||||
"sha256:7617c06de13ee6dd2df9add7e275bfb2bcebbaaf3e450f5937cd0200df824273"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==10.1"
|
||||
},
|
||||
"python-dateutil": {
|
||||
"hashes": [
|
||||
"sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86",
|
||||
"sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
||||
"version": "==2.8.2"
|
||||
},
|
||||
"pyyaml": {
|
||||
"hashes": [
|
||||
"sha256:08682f6b72c722394747bddaf0aa62277e02557c0fd1c42cb853016a38f8dedf",
|
||||
"sha256:0f5f5786c0e09baddcd8b4b45f20a7b5d61a7e7e99846e3c799b05c7c53fa696",
|
||||
"sha256:129def1b7c1bf22faffd67b8f3724645203b79d8f4cc81f674654d9902cb4393",
|
||||
"sha256:294db365efa064d00b8d1ef65d8ea2c3426ac366c0c4368d930bf1c5fb497f77",
|
||||
"sha256:3b2b1824fe7112845700f815ff6a489360226a5609b96ec2190a45e62a9fc922",
|
||||
"sha256:3bd0e463264cf257d1ffd2e40223b197271046d09dadf73a0fe82b9c1fc385a5",
|
||||
"sha256:4465124ef1b18d9ace298060f4eccc64b0850899ac4ac53294547536533800c8",
|
||||
"sha256:49d4cdd9065b9b6e206d0595fee27a96b5dd22618e7520c33204a4a3239d5b10",
|
||||
"sha256:4e0583d24c881e14342eaf4ec5fbc97f934b999a6828693a99157fde912540cc",
|
||||
"sha256:5accb17103e43963b80e6f837831f38d314a0495500067cb25afab2e8d7a4018",
|
||||
"sha256:607774cbba28732bfa802b54baa7484215f530991055bb562efbed5b2f20a45e",
|
||||
"sha256:6c78645d400265a062508ae399b60b8c167bf003db364ecb26dcab2bda048253",
|
||||
"sha256:72a01f726a9c7851ca9bfad6fd09ca4e090a023c00945ea05ba1638c09dc3347",
|
||||
"sha256:74c1485f7707cf707a7aef42ef6322b8f97921bd89be2ab6317fd782c2d53183",
|
||||
"sha256:895f61ef02e8fed38159bb70f7e100e00f471eae2bc838cd0f4ebb21e28f8541",
|
||||
"sha256:8c1be557ee92a20f184922c7b6424e8ab6691788e6d86137c5d93c1a6ec1b8fb",
|
||||
"sha256:bb4191dfc9306777bc594117aee052446b3fa88737cd13b7188d0e7aa8162185",
|
||||
"sha256:bfb51918d4ff3d77c1c856a9699f8492c612cde32fd3bcd344af9be34999bfdc",
|
||||
"sha256:c20cfa2d49991c8b4147af39859b167664f2ad4561704ee74c1de03318e898db",
|
||||
"sha256:cb333c16912324fd5f769fff6bc5de372e9e7a202247b48870bc251ed40239aa",
|
||||
"sha256:d2d9808ea7b4af864f35ea216be506ecec180628aced0704e34aca0b040ffe46",
|
||||
"sha256:d483ad4e639292c90170eb6f7783ad19490e7a8defb3e46f97dfe4bacae89122",
|
||||
"sha256:dd5de0646207f053eb0d6c74ae45ba98c3395a571a2891858e87df7c9b9bd51b",
|
||||
"sha256:e1d4970ea66be07ae37a3c2e48b5ec63f7ba6804bdddfdbd3cfd954d25a82e63",
|
||||
"sha256:e4fac90784481d221a8e4b1162afa7c47ed953be40d31ab4629ae917510051df",
|
||||
"sha256:fa5ae20527d8e831e8230cbffd9f8fe952815b2b7dae6ffec25318803a7528fc",
|
||||
"sha256:fd7f6999a8070df521b6384004ef42833b9bd62cfee11a09bda1079b4b704247",
|
||||
"sha256:fdc842473cd33f45ff6bce46aea678a54e3d21f1b61a7750ce3c498eedfe25d6",
|
||||
"sha256:fe69978f3f768926cfa37b867e3843918e012cf83f680806599ddce33c2c68b0"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==5.4.1"
|
||||
},
|
||||
"requests": {
|
||||
"hashes": [
|
||||
"sha256:6c1246513ecd5ecd4528a0906f910e8f0f9c6b8ec72030dc9fd154dc1a6efd24",
|
||||
"sha256:b8aa58f8cf793ffd8782d3d8cb19e66ef36f7aba4353eec859e74678b01b07a7"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==2.26.0"
|
||||
},
|
||||
"selenium": {
|
||||
"hashes": [
|
||||
"sha256:2d7131d7bc5a5b99a2d9b04aaf2612c411b03b8ca1b1ee8d3de5845a9be2cb3c",
|
||||
"sha256:deaf32b60ad91a4611b98d8002757f29e6f2c2d5fcaf202e1c9ad06d6772300d"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==3.141.0"
|
||||
},
|
||||
"six": {
|
||||
"hashes": [
|
||||
"sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926",
|
||||
"sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
||||
"version": "==1.16.0"
|
||||
},
|
||||
"text-unidecode": {
|
||||
"hashes": [
|
||||
"sha256:1311f10e8b895935241623731c2ba64f4c455287888b18189350b67134a822e8",
|
||||
"sha256:bad6603bb14d279193107714b288be206cac565dfa49aa5b105294dd5c4aab93"
|
||||
],
|
||||
"version": "==1.3"
|
||||
},
|
||||
"toml": {
|
||||
"hashes": [
|
||||
"sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b",
|
||||
"sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"
|
||||
],
|
||||
"markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
||||
"version": "==0.10.2"
|
||||
},
|
||||
"urllib3": {
|
||||
"hashes": [
|
||||
"sha256:39fb8672126159acb139a7718dd10806104dec1e2f0f6c88aab05d17df10c8d4",
|
||||
"sha256:f57b4c16c62fa2760b7e3d97c35b255512fb6b59a259730f36ba32ce9f8e342f"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==1.26.6"
|
||||
},
|
||||
"yagmail": {
|
||||
"hashes": [
|
||||
"sha256:30dc2bc9d84fe56897df34adcd63fadf1d8dc2505d54c3bad9ba101915fabb1b",
|
||||
"sha256:88f20899a193025fcdc6c93d2781abbcd2f029d99e6242f67d5fbb15e1ebc366"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==0.14.256"
|
||||
}
|
||||
},
|
||||
"develop": {}
|
||||
}
|
97
README.md
97
README.md
|
@ -1,2 +1,97 @@
|
|||
# auotest-demo
|
||||
# auotest
|
||||
python+request/selenium+pytest_allure集成的API & UI自动化测试框架
|
||||
|
||||
# 项目结构
|
||||
├────.gitignore git上传时忽略的文件
|
||||
├────api/ 存放项目的基础API信息
|
||||
│ ├────__init__.py
|
||||
│ ├────login_api.py 登录接口
|
||||
│ └────project_api.py 项目接口
|
||||
├────common/ 存放公共方法
|
||||
│ ├────__init__.py
|
||||
│ ├────baseapi.py 封装基础的request方法
|
||||
│ ├────basepage.py 封装浏览器基础的操作方法,用于UI自动化测试
|
||||
│ ├────download_img.py 封装通过URL下载图片的方法
|
||||
│ ├────handle_email.py python自带的SMTP模块发送邮件 (目前项目未使用该模块)
|
||||
│ ├────handle_excel.py 使用openpyxl对excel进行读写操作
|
||||
│ ├────handle_faker.py 使用faker模块造测试数据
|
||||
│ ├────handle_log.py python自带的logging模块对日志进行处理(目前项目未使用该模块)
|
||||
│ ├────handle_mysql.py 使用pymysql对mysql数据库进行操作
|
||||
│ ├────handle_platform.py 跨平台的支持allure,用于生成allure测试报告
|
||||
│ ├────handle_time.py 处理时间的类
|
||||
│ ├────handle_yagmail.py 使用yagmail对自动发送报告邮件
|
||||
│ ├────handle_yaml.py 使用pyyaml对yaml文件的读写方法
|
||||
│ ├────helper.py 封装一些小方法
|
||||
│ └────project_tree.py 获取项目文件树的方法
|
||||
├────config/ 存放环境配置信息
|
||||
│ ├────__init__.py
|
||||
│ ├────report_template.html 直接通过python发送邮件的内容正文模板
|
||||
│ ├────report_template_jenkins.html 通过jenkins发送邮件的内容正文模板
|
||||
│ ├────settings.py 项目配置文件
|
||||
│ └────settings_example.py 项目配置示例
|
||||
├────data/ 存放测试数据
|
||||
│ ├────api_data/ 存放API测试的测试数据
|
||||
│ │ └────login_data.yaml 登录用例的测试数据
|
||||
│ └────ui_data/ 存放UI测试的测试数据
|
||||
│ │ ├────login_data.py 登录用例的测试数据
|
||||
│ │ └────new_project_data.py 新建项目的测试数据
|
||||
├────page/ 封装页面元素定位和操作,用于UI自动化测试
|
||||
│ ├────__init__.py
|
||||
│ ├────login_page.py 登录页面的元素定位和操作
|
||||
│ ├────project_detail_page.py 项目详情页的元素定位和操作
|
||||
│ └────users_center_page.py 个人主页的元素定位和操作
|
||||
├────tools/
|
||||
│ ├────__init__.py
|
||||
│ └────export_issue.py
|
||||
├────lib/ 存放第三方库
|
||||
│ └────__init__.py
|
||||
├────log/ 存放日志文件
|
||||
├────test_api/ 存放api测试用例
|
||||
│ ├────__init__.py
|
||||
│ └────conftest.py 作用于当前包的fixture的配置文件
|
||||
├────test_app/ 存放app测试用例
|
||||
│ ├────__init__.py
|
||||
│ └────conftest.py 作用于当前包的fixture的配置文件
|
||||
├────test_ui/ 存放UI测试用例
|
||||
│ ├────__init__.py
|
||||
│ └────conftest.py 作用于当前包的fixture的配置文件
|
||||
├────report/ 存放allure测试报告
|
||||
├────image/ 存放UI测试过程中的截图
|
||||
├────conftest.py 作用于整个项目的全局的fixture的配置文件
|
||||
├────Pipfile 记录虚拟环境的相关信息
|
||||
├────Pipfile.lock 记录了当前虚拟环境中安装的依赖的版本号以及哈希
|
||||
├────pytest.ini pytest的配置文件
|
||||
├────README.md
|
||||
├────requirements.txt 项目依赖包,使用了pipenv后,该文件不需要
|
||||
└────run.py 项目的运行文件
|
||||
|
||||
|
||||
# pipenv使用
|
||||
# 参考连接:https://zhuanlan.zhihu.com/p/71598248
|
||||
安装pipenv: pip3 install pipenv
|
||||
创建虚拟环境:pipenv install
|
||||
激活已存在的虚拟环境(如果不存在会创建一个):pipenv shell
|
||||
|
||||
#注意:以下三个参数只能单独使用。它们还具有破坏性,会删除当前的虚拟环境,然后用适当版本的虚拟环境替代。
|
||||
指定使用Python3.6的虚拟环境: pipenv --python 3.6
|
||||
使用系统的Python2在创建虚拟环境: pipenv --two
|
||||
使用系统的Python3在创建虚拟环境: pipenv --three
|
||||
|
||||
列出本地工程路径: pipenv --where
|
||||
列出虚拟环境路径:pipenv --venv
|
||||
列出虚拟环境python的可执行路径:pipenv --py
|
||||
安装包:pipenv isntall 包名
|
||||
安装包到开发环境:pipenv install 包名--dev
|
||||
更新包:pipenv update 包名
|
||||
卸载包:pipenv uninstall 包名
|
||||
卸载所有包:pipenv uninstall --all
|
||||
查看包依赖:pipenv graph
|
||||
更新Pipfile.lock文件锁定当前环境的依赖版本:pipenv lock
|
||||
运行py文件:pipenv run python "python文件.py"
|
||||
删除虚拟环境:pipenv --rm
|
||||
退出虚拟环境:exit
|
||||
|
||||
# 命令参考
|
||||
更新依赖包:pipreqs . --encoding=utf8 --force
|
||||
安装依赖包:pipenv install -r requirements.txt
|
||||
jenkins工作目录:/var/lib/jenkins/workspace
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# @Time : 2021/8/13 20:39
|
||||
# @Author : Flora.Chen
|
||||
# @File : __init__.py.py
|
||||
# @Software: PyCharm
|
||||
# @Desc:
|
|
@ -0,0 +1,43 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# @Time : 2021/8/11 9:02
|
||||
# @Author : Flora.Chen
|
||||
# @File : login_api.py
|
||||
# @Software: PyCharm
|
||||
# @Desc: 登录接口
|
||||
|
||||
from common.baseapi import BaseApi
|
||||
from loguru import logger
|
||||
|
||||
|
||||
class TrustieLogin(BaseApi):
|
||||
"""trustie登录"""
|
||||
|
||||
def __init__(self, host):
|
||||
super().__init__()
|
||||
self.host = host
|
||||
|
||||
def get_token_session_api(self):
|
||||
"""获取cookie"""
|
||||
response = self.send_request(url=self.host, method="GET", verify=False)
|
||||
return response.cookies
|
||||
|
||||
def login_api(self, user, pwd):
|
||||
"""登录"""
|
||||
headers = {"Content-Type": "application/json;charset=UTF-8"}
|
||||
|
||||
data = {"login": user, "password": pwd,
|
||||
"autologin": 1}
|
||||
cookies = self.get_token_session_api()
|
||||
response = self.send_request(url=self.host + "/api/accounts/login.json", method="POST", headers=headers,
|
||||
json=data,
|
||||
cookies=cookies,
|
||||
allow_redirects=False)
|
||||
try:
|
||||
json_data = response.json()
|
||||
if json_data["login"] == user:
|
||||
logger.debug(f'登录接口请求成功!user_id={json_data["user_id"]}')
|
||||
return response
|
||||
except Exception as e:
|
||||
logger.error(f"登录接口请求错误:{e}")
|
||||
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# @Time : 2021/8/30 10:34
|
||||
# @Author : Flora.Chen
|
||||
# @File : project_api.py
|
||||
# @Software: PyCharm
|
||||
# @Desc: 项目接口
|
||||
|
||||
from common.baseapi import BaseApi
|
||||
from api.login_api import TrustieLogin
|
||||
from loguru import logger
|
||||
|
||||
|
||||
class ProjectApi(BaseApi):
|
||||
"""项目相关的api"""
|
||||
|
||||
def __init__(self, host):
|
||||
super().__init__()
|
||||
self.host = host
|
||||
|
||||
def delete_project(self, **kwargs):
|
||||
"""删除项目"""
|
||||
url = "/api/" + str(kwargs.get("owner")) + "/" + str(kwargs.get("repository_name")) + ".json"
|
||||
response = self.send_request(url=self.host + url, method="DELETE", cookies=kwargs.get("cookies"))
|
||||
try:
|
||||
json_data = response.json()
|
||||
if json_data["message"] == "success" and json_data["status"] == 0:
|
||||
logger.debug('删除项目接口请求成功!')
|
||||
return response
|
||||
except Exception as e:
|
||||
logger.error(f"删除项目接口请求错误:{e}")
|
||||
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# @Time : 2021/8/13 20:39
|
||||
# @Author : Flora.Chen
|
||||
# @File : __init__.py.py
|
||||
# @Software: PyCharm
|
||||
# @Desc:
|
|
@ -0,0 +1,72 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# @Time : 2021/8/14 12:56
|
||||
# @Author : Flora.Chen
|
||||
# @File : baseapi.py
|
||||
# @Software: PyCharm
|
||||
# @Desc:
|
||||
import requests
|
||||
from loguru import logger
|
||||
import urllib3
|
||||
from urllib3.exceptions import InsecureRequestWarning
|
||||
|
||||
# 加这句不会报错(requests证书警告)
|
||||
urllib3.disable_warnings(InsecureRequestWarning)
|
||||
|
||||
|
||||
class BaseApi(object):
|
||||
"""
|
||||
BaseApi作为所有单接口的父类出现,将所有接口公共的属性或者方法信息进行抽象封装
|
||||
"""
|
||||
session = None
|
||||
|
||||
def __init__(self):
|
||||
self.url = None
|
||||
self.method = None
|
||||
self.headers = None
|
||||
self.data = None
|
||||
self.params = None
|
||||
self.json = None
|
||||
self.response = None
|
||||
|
||||
@classmethod
|
||||
def get_session(cls):
|
||||
"""
|
||||
单例模式保证测试过程中使用的都是一个session对象
|
||||
:return:
|
||||
"""
|
||||
if cls.session is None:
|
||||
cls.session = requests.Session()
|
||||
return cls.session
|
||||
|
||||
def send_api(self, **kwargs):
|
||||
session = self.get_session()
|
||||
return session.request(**kwargs)
|
||||
|
||||
def send_request(self, **kwargs):
|
||||
"""
|
||||
发送请求
|
||||
:param kwargs: 表示接口在发起时有一些自定义的参数或者其他的数据
|
||||
:return:
|
||||
"""
|
||||
# if处理表示的是调用方如果不传递某些参数,那么就用当前对象自己的属性
|
||||
if kwargs.get("method") is None:
|
||||
kwargs["method"] = self.method
|
||||
if kwargs.get("url") is None:
|
||||
kwargs["url"] = self.url
|
||||
if kwargs.get("params") is None:
|
||||
kwargs["params"] = self.params
|
||||
if kwargs.get("data") is None:
|
||||
kwargs["data"] = self.data
|
||||
if kwargs.get("json") is None:
|
||||
kwargs["json"] = self.json
|
||||
if kwargs.get("headers") is None:
|
||||
kwargs["headers"] = self.headers
|
||||
logger.debug("----------- 开始发送请求 -----------\n")
|
||||
logger.debug(f"----------- 请求参数:{kwargs} -----------\n")
|
||||
response = self.send_api(**kwargs)
|
||||
if response.status_code // 100 == 2:
|
||||
logger.debug(f"----------- 响应码200-299返回的响应数据(text):{response.text} -----------\n")
|
||||
else:
|
||||
logger.debug(f"----------- 响应码不是200-299返回的响应数据:{response.text} -----------\n")
|
||||
logger.debug("----------- 结束发送请求 -----------\n")
|
||||
return response
|
|
@ -0,0 +1,228 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# @Time : 2021/8/14 12:24
|
||||
# @Author : Flora.Chen
|
||||
# @File : basepage.py
|
||||
# @Software: PyCharm
|
||||
# @Desc: UI自动化测试的一些基础浏览器操作方法
|
||||
|
||||
import os
|
||||
from datetime import datetime
|
||||
from selenium.common.exceptions import NoSuchElementException
|
||||
from selenium.webdriver import ActionChains
|
||||
from selenium.webdriver.support import expected_conditions as EC
|
||||
from selenium.webdriver.support.wait import WebDriverWait
|
||||
|
||||
|
||||
class BasePage(object):
|
||||
"""
|
||||
UI自动化基础操作封装
|
||||
"""
|
||||
|
||||
def __int__(self, host, driver):
|
||||
self.host = host
|
||||
self.driver = driver
|
||||
|
||||
def visit(self, url: str):
|
||||
"""
|
||||
访问页面
|
||||
:param url:
|
||||
:return:
|
||||
"""
|
||||
if not url.startswith("http"):
|
||||
url = self.host + url
|
||||
self.driver.get(url)
|
||||
return self
|
||||
|
||||
def hit(self, locator: tuple, force=False):
|
||||
"""
|
||||
鼠标点击,当元素不可点击的时候,使用强制点击
|
||||
:param locator: 元素定位,元祖类型
|
||||
:param force: 强制点击,默认false
|
||||
:return: self
|
||||
"""
|
||||
try:
|
||||
elem = self.driver.find_element(*locator)
|
||||
if not force:
|
||||
self.driver.execute_script("arguments[0].click()", elem)
|
||||
else:
|
||||
self.driver.execute_script("arguments[0].click({force: true})", elem)
|
||||
except Exception as e:
|
||||
print("未找到元素:{}".format(e))
|
||||
else:
|
||||
return self
|
||||
|
||||
def input(self, locator: tuple, text):
|
||||
"""
|
||||
输入内容
|
||||
:param locator: 元素定位,元祖类型
|
||||
:param text: 输入的内容
|
||||
:return: self
|
||||
"""
|
||||
try:
|
||||
elem = self.driver.find_element(*locator)
|
||||
elem.send_keys(text)
|
||||
return self
|
||||
except NoSuchElementException as e:
|
||||
print("未找到元素:{}".format(e))
|
||||
|
||||
def wait_element_visibility(self, locator: tuple, timeout=20, poll_frequency=0.2):
|
||||
"""
|
||||
显性等待: 等待元素可见
|
||||
:param locator: 元素定位,元祖类型
|
||||
:return:
|
||||
"""
|
||||
return WebDriverWait(self.driver, timeout, poll_frequency).until(
|
||||
EC.visibility_of_element_located(locator)
|
||||
)
|
||||
|
||||
def wait_element_clickable(self, locator: tuple, timeout=10, poll_frequency=0.2):
|
||||
"""
|
||||
显性等待: 等待元素可点击
|
||||
:param locator: 元素定位,元祖类型
|
||||
:return:
|
||||
"""
|
||||
return WebDriverWait(self.driver, timeout, poll_frequency).until(
|
||||
EC.element_to_be_clickable(locator)
|
||||
)
|
||||
|
||||
def wait_element_presence(self, locator: tuple, timeout=10, poll_frequency=0.2):
|
||||
"""
|
||||
显性等待: 等待元素被加载出来
|
||||
:param locator: 元素定位,元祖类型
|
||||
:return:
|
||||
"""
|
||||
return WebDriverWait(self.driver, timeout, poll_frequency).until(
|
||||
EC.presence_of_all_elements_located(locator)
|
||||
)
|
||||
|
||||
def get_element_attribute(self, locator: tuple, attr_name):
|
||||
"""
|
||||
获取元素属性值
|
||||
:param locator: 元素定位,元祖类型
|
||||
:return: 元素属性值
|
||||
"""
|
||||
try:
|
||||
return self.driver.find_element(*locator).get_attribute(attr_name)
|
||||
except NoSuchElementException as e:
|
||||
print("未找到元素:{}".format(e))
|
||||
|
||||
def get_name(self, locator: tuple):
|
||||
"""
|
||||
获取元素的name属性值
|
||||
"""
|
||||
return self.get_element_attribute(locator, "name")
|
||||
|
||||
def get_title(self, locator: tuple):
|
||||
"""
|
||||
获取元素的title属性值
|
||||
"""
|
||||
return self.get_element_attribute(locator, "title")
|
||||
|
||||
def get_class(self, locator: tuple):
|
||||
"""
|
||||
获取元素的class属性值
|
||||
"""
|
||||
return self.get_element_attribute(locator, "class")
|
||||
|
||||
# ------------------------ START: 鼠标事件:双击,悬停,拖动 ------------------------ #
|
||||
|
||||
def double_click(self, locator):
|
||||
"""
|
||||
鼠标双击
|
||||
:param locator:
|
||||
:return:
|
||||
"""
|
||||
try:
|
||||
elem = self.driver.find_element(*locator)
|
||||
action = ActionChains(self.driver)
|
||||
action.double_click(elem).perform()
|
||||
return self
|
||||
except NoSuchElementException as e:
|
||||
print("未找到元素:{}".format(e))
|
||||
|
||||
def drag_and_drop(self, start_locator, end_locator):
|
||||
"""鼠标拖动"""
|
||||
elem_start = self.driver.find_element(*start_locator)
|
||||
elem_end = self.driver.find_element(*end_locator)
|
||||
action = ActionChains(self.driver)
|
||||
action.double_click((elem_start, elem_end)).perform()
|
||||
return self
|
||||
|
||||
def hover(self, locator):
|
||||
"""鼠标悬停"""
|
||||
el = self.driver.find_element(*locator)
|
||||
action = ActionChains(self.driver)
|
||||
action.move_to_element(el).perform()
|
||||
return self
|
||||
|
||||
# ------------------------ END: 鼠标事件:双击,悬停,拖动 ------------------------ #
|
||||
|
||||
def switch_to_frame(self, reference=None, timeout=10, poll=0.2):
|
||||
"""
|
||||
iframe切换
|
||||
:param reference: 可以是id, name,索引或者元素定位(元祖)
|
||||
:param timeout:
|
||||
:param poll:
|
||||
:return:
|
||||
"""
|
||||
if not reference:
|
||||
return self.driver.switch_to.default_content()
|
||||
return WebDriverWait(self.driver, timeout, poll).until(
|
||||
EC.frame_to_be_available_and_switch_to_it(reference)
|
||||
)
|
||||
|
||||
def find_elements(self, locator):
|
||||
"""
|
||||
查找元素们
|
||||
:param locator:
|
||||
:return:
|
||||
"""
|
||||
try:
|
||||
return self.driver.find_elements(*locator)
|
||||
except NoSuchElementException as e:
|
||||
print("未找到元素:{}".format(e))
|
||||
|
||||
def get_text(self, locator: tuple):
|
||||
"""
|
||||
获取元素的文本值
|
||||
:param locator: 元素定位
|
||||
:return:
|
||||
"""
|
||||
try:
|
||||
elem = self.driver.find_element(*locator)
|
||||
value = elem.text
|
||||
return value
|
||||
except NoSuchElementException as e:
|
||||
print(f"get未找到元素{e}")
|
||||
|
||||
def screenshot(self, path, filename):
|
||||
"""
|
||||
截图
|
||||
:param path: 文件保存的目录
|
||||
:param filename: 截图文件名
|
||||
:return:
|
||||
"""
|
||||
file_path = os.path.join(path, filename)
|
||||
self.driver.save_screenshot(file_path)
|
||||
return self
|
||||
|
||||
# ------------------------ START: JS事件 ------------------------ #
|
||||
def execute_js(self, js):
|
||||
self.driver.execute_script(js)
|
||||
return self
|
||||
|
||||
def new_open_window(self, url):
|
||||
# 获取所有的窗口
|
||||
start_window = self.driver.window_handls
|
||||
# 打开新窗口
|
||||
js = "window.open({})".format(url)
|
||||
self.driver.execute_script(js)
|
||||
# 等待新窗口出现,进行切换
|
||||
WebDriverWait(self.driver, 5, 0.5).until(
|
||||
EC.new_window_is_opened(start_window)
|
||||
)
|
||||
# 切换窗口
|
||||
self.driver.switch_to.window(self.driver.window_handls[-1])
|
||||
return self
|
||||
|
||||
# ------------------------ END: JS事件 ------------------------ #
|
|
@ -0,0 +1,29 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# @Time : 2021/9/10 13:44
|
||||
# @Author : Flora.Chen
|
||||
# @File : download_img.py
|
||||
# @Software: PyCharm
|
||||
# @Desc: 下载图片
|
||||
|
||||
import urllib.request
|
||||
from loguru import logger
|
||||
|
||||
|
||||
def download_img(url, path):
|
||||
"""
|
||||
通过Python自带的库urllib下载图片
|
||||
需要导入:import urllib.request
|
||||
:param url: url地址
|
||||
:param path: 图片保存的绝对路径
|
||||
"""
|
||||
try:
|
||||
request = urllib.request.Request(url)
|
||||
response = urllib.request.urlopen(request)
|
||||
if response.getcode() == 200:
|
||||
logger.debug("请求状态码200, 开始下载图片......")
|
||||
with open(path, "wb") as fb:
|
||||
fb.write(response.read())
|
||||
return path
|
||||
except Exception as e:
|
||||
logger.debug("捕获到异常,下载图片失败")
|
||||
return f"{e}"
|
|
@ -0,0 +1,78 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# @Time : 2021/8/14 12:21
|
||||
# @Author : Flora.Chen
|
||||
# @File : handle_email.py
|
||||
# @Software: PyCharm
|
||||
# @Desc: python自带的SMTP模块发送邮件
|
||||
|
||||
import smtplib
|
||||
from email.mime.text import MIMEText # 用来发送邮件
|
||||
from email.mime.application import MIMEApplication # 用来添加附件
|
||||
from email.mime.multipart import MIMEMultipart # 用来构造多组件邮件
|
||||
from email.header import Header
|
||||
|
||||
|
||||
class HandleEmail:
|
||||
"""邮件处理类"""
|
||||
|
||||
def __init__(self, host, port, mail_user, mail_auth_code, sender, recipients):
|
||||
"""
|
||||
初始化邮件服务器信息
|
||||
:param host: 邮箱服务器地址
|
||||
:param port: 邮箱服务器端口号
|
||||
:param mail_user: 邮箱登录账号
|
||||
:param mail_auth_code: 邮箱授权码/密码
|
||||
:param sender: 邮件发送人
|
||||
:param recipients: 邮件接收人
|
||||
"""
|
||||
self.sender = sender
|
||||
self.recipients = recipients
|
||||
# ---------- 连接smtp服务器并登录 ----------
|
||||
# 连接到smtp服务器
|
||||
self.smtp = smtplib.SMTP_SSL(host=host, port=port)
|
||||
|
||||
# 登录smtp服务(邮箱账号+授权码登录)
|
||||
self.smtp.login(user=mail_user, password=mail_auth_code)
|
||||
|
||||
def send_email(self, subject, content, file=None):
|
||||
"""
|
||||
发送邮件
|
||||
:param subject: 邮件主题
|
||||
:param content: 邮件内容
|
||||
:param file: 邮件附件
|
||||
:return:
|
||||
"""
|
||||
|
||||
# ---------- 构建邮件内容 ----------
|
||||
msg = MIMEMultipart()
|
||||
# 邮件主题
|
||||
msg['Subject'] = Header(subject, charset="utf8")
|
||||
# 邮件内容
|
||||
text = MIMEText(content, _subtype="html", _charset='utf8')
|
||||
# 收件人:是邮件中显示的收件人,不是实际的收件人
|
||||
msg['To'] = self.recipients
|
||||
# 发件人:是邮件中显示的发件人,不是实际的发件人
|
||||
msg['From'] = self.sender
|
||||
|
||||
# 将构造的文本内容,添加到多组件邮件中
|
||||
msg.attach(text)
|
||||
|
||||
# 构造邮件附件 问题:发送html样式没有了?
|
||||
# 读取报告中的内容,作为附件发送
|
||||
if file:
|
||||
with open(file, 'rb') as f:
|
||||
f_msg = f.read()
|
||||
att = MIMEApplication(f_msg)
|
||||
att.add_header('content-disposition', 'attachment', filename=file)
|
||||
msg.attach(att)
|
||||
# att = MIMEText(content, _subtype="html", _charset='utf8')
|
||||
# att["Content-Type"] = "appilication/octet-stream"
|
||||
# att.add_header('content-disposition', 'attachment', filename=file)
|
||||
# msg.attach(att)
|
||||
|
||||
# ---------- 发送邮件 ----------
|
||||
# from_addr是真正的发件人地址 to_addrs真正的收件人地址
|
||||
self.smtp.send_message(msg, from_addr=self.sender, to_addrs=self.recipients)
|
||||
|
||||
# 关闭服务
|
||||
self.smtp.quit()
|
|
@ -0,0 +1,91 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# @Time : 2021/8/14 12:22
|
||||
# @Author : Flora.Chen
|
||||
# @File : handle_excel.py
|
||||
# @Software: PyCharm
|
||||
# @Desc: 使用openpyxl对excel进行读写操作
|
||||
import os
|
||||
|
||||
import openpyxl
|
||||
from loguru import logger
|
||||
|
||||
|
||||
class HandleExcel:
|
||||
"""
|
||||
从excel读取写入数据的类
|
||||
"""
|
||||
|
||||
def __init__(self, file):
|
||||
"""
|
||||
初始化用例文件
|
||||
:param file: 文件绝对路径,如:D:\test\test.xlsx
|
||||
"""
|
||||
self.file = file
|
||||
|
||||
def create_excel(self, excel_name):
|
||||
"""
|
||||
创建excel文件
|
||||
"""
|
||||
# 创建文件对象
|
||||
wb = openpyxl.Workbook()
|
||||
path = os.path.join(self.file, excel_name)
|
||||
logger.debug(f"self.file:{self.file}")
|
||||
logger.debug(f"excel_name:{excel_name}")
|
||||
logger.debug(f"创建的excel路径:{path}")
|
||||
wb.save(path)
|
||||
self.file = path
|
||||
|
||||
def read(self, sheet_name):
|
||||
"""
|
||||
读取excel数据并返回
|
||||
:param sheet_name: 表单名称
|
||||
:return: 存在传入的表单, 返回表单数据,不存在则返回空
|
||||
"""
|
||||
# 创建一个工作簿工作对象(excel文件已存在的情况)
|
||||
workbook = openpyxl.open(self.file)
|
||||
# 跟上面那句一个意思 workbook = openpyxl.load_workbook(self.file)
|
||||
|
||||
# 获取excel当中所有的sheet,返回的是一个列表
|
||||
sheets = workbook.sheetnames
|
||||
# 获取sheet对象
|
||||
if sheet_name in sheets:
|
||||
sheet = workbook[sheet_name]
|
||||
all_values = list(sheet.values)
|
||||
header = all_values[0]
|
||||
data = []
|
||||
for i in all_values[1:]:
|
||||
data.append(dict(zip(header, i)))
|
||||
# 关闭excel
|
||||
workbook.close()
|
||||
return data
|
||||
else:
|
||||
# 关闭excel
|
||||
workbook.close()
|
||||
logger.error(f"表单: 【{sheet_name}】文件不存在")
|
||||
|
||||
def write(self, row, column, data, sheet_name=None):
|
||||
"""
|
||||
往excel写入数据
|
||||
:param sheet_name: 表单名称
|
||||
:param row: 要写入的行
|
||||
:param column: 要写入的列
|
||||
:param data: 要写入的数据
|
||||
:return: None
|
||||
"""
|
||||
workbook = openpyxl.open(self.file)
|
||||
# 获取excel当中所有的sheet,返回的是一个列表
|
||||
sheets = workbook.sheetnames
|
||||
if sheet_name in sheets:
|
||||
sheet = workbook[sheet_name]
|
||||
logger.error(f"往表单【{sheet_name}】中写入数据")
|
||||
else:
|
||||
# 如果表单为空,就默认使用第一个表单
|
||||
sheet = workbook.active
|
||||
logger.error(f"表单【{sheet_name}】不存在,默认往第一个表单中写入数据")
|
||||
|
||||
sheet.cell(row=row, column=column, value=data)
|
||||
# 更上面写法效果一样 sheet.cell(row=row, column=column).value = data
|
||||
|
||||
# 保存并关闭文件
|
||||
workbook.save(self.file)
|
||||
workbook.close()
|
|
@ -0,0 +1,25 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# @Time : 2021/8/23 14:32
|
||||
# @Author : Flora.Chen
|
||||
# @File : handle_faker.py
|
||||
# @Software: PyCharm
|
||||
# @Desc: 使用faker模块造测试数据
|
||||
|
||||
from faker import Faker
|
||||
|
||||
# 生成中文的随机数据
|
||||
z_fk = Faker(locale="zh_CN")
|
||||
# 生成英文的随机数据
|
||||
e_fk = Faker()
|
||||
|
||||
# 随机名字
|
||||
z_fk_name = z_fk.name()
|
||||
e_fk_name = e_fk.name()
|
||||
|
||||
# ----------------------------- 文本相关 ----------------------------------#
|
||||
# 单个文本
|
||||
z_fk_text = z_fk.text(max_nb_chars=200, ext_word_list=None)
|
||||
e_fk_text = e_fk.text(max_nb_chars=200, ext_word_list=None)
|
||||
# 多个句子
|
||||
z_fk_sentences = z_fk.sentences(nb=3, ext_word_list=None)
|
||||
e_fk_sentences = e_fk.sentences(nb=3, ext_word_list=None)
|
|
@ -0,0 +1,63 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# @Time : 2021/8/14 12:08
|
||||
# @Author : Flora.Chen
|
||||
# @File : handle_log.py
|
||||
# @Software: PyCharm
|
||||
# @Desc: python内置的logging模块处理日志
|
||||
|
||||
import logging
|
||||
|
||||
|
||||
def get_logger(name,
|
||||
level,
|
||||
fmt,
|
||||
handler_level,
|
||||
file_level,
|
||||
file=None):
|
||||
"""
|
||||
日志收集器
|
||||
:param name: 日志收集器名称
|
||||
:param level: 日志收集等级
|
||||
:param fmt: 日志输出格式
|
||||
:param handler_level: 控制台输出日志最低等级
|
||||
:param file_level: 文件输出日志最低等级
|
||||
:param file: 日志文件路径
|
||||
:return: logger 日志收集器
|
||||
"""
|
||||
# 创建日志收集器
|
||||
logger = logging.getLogger(name)
|
||||
|
||||
# 设置日志收集器收集等级
|
||||
logger.setLevel(level)
|
||||
|
||||
# ----------- 控制台输出日志 --------------------#
|
||||
# 创建日志输出渠道-控制台
|
||||
handler = logging.StreamHandler()
|
||||
|
||||
# 设置日志收集等级-控制台
|
||||
handler.setLevel(handler_level)
|
||||
|
||||
# 创建一个日志输出格式对象
|
||||
fmt = logging.Formatter(fmt)
|
||||
|
||||
# 设置日志输出格式 - 控制台
|
||||
handler.setFormatter(fmt)
|
||||
|
||||
# 添加日志处理器-控制台
|
||||
logger.addHandler(handler)
|
||||
|
||||
# ----------- 日志保存在日志文件 --------------------#
|
||||
if file:
|
||||
# 创建日志输出渠道-文件
|
||||
file_handler = logging.FileHandler(file, encoding="utf-8")
|
||||
|
||||
# 设置日志收集等级-文件
|
||||
file_handler.setLevel(file_level)
|
||||
|
||||
# 设置日志输出格式 - 文件
|
||||
file_handler.setFormatter(fmt)
|
||||
|
||||
# 添加日志处理器-文件
|
||||
logger.addHandler(file_handler)
|
||||
|
||||
return logger
|
|
@ -0,0 +1,119 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# @Time : 2021/8/14 12:15
|
||||
# @Author : Flora.Chen
|
||||
# @File : handle_mysql.py
|
||||
# @Software: PyCharm
|
||||
# @Desc: 使用pymysql模块连接mysql数据库的公共方法
|
||||
import pymysql
|
||||
from typing import Union
|
||||
import json
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
class DBHandler:
|
||||
"""
|
||||
初始化数据库连接,并指定查询的结果集以字典形式返回
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
host=None,
|
||||
port=None,
|
||||
user=None,
|
||||
password=None,
|
||||
database=None,
|
||||
charset="utf8",
|
||||
cursorclass=pymysql.cursors.DictCursor # 加上这个返回的就是字典
|
||||
):
|
||||
"""
|
||||
初始化方法中, 连接到数据库
|
||||
"""
|
||||
|
||||
# 建立连接
|
||||
self.conn = pymysql.connect(host=host,
|
||||
port=port,
|
||||
user=user,
|
||||
password=password,
|
||||
database=database,
|
||||
charset=charset,
|
||||
cursorclass=cursorclass
|
||||
)
|
||||
# 创建一个游标对象
|
||||
self.cursor = self.conn.cursor()
|
||||
|
||||
def query_all(self, sql):
|
||||
"""
|
||||
查询所有符合sql条件的数据
|
||||
:param sql: 执行的sql
|
||||
:return: 查询结果
|
||||
"""
|
||||
|
||||
self.conn.commit()
|
||||
self.cursor.execute(sql)
|
||||
data = self.cursor.fetchall()
|
||||
self.cursor.close()
|
||||
return self.verify(data)
|
||||
|
||||
def query_one(self, sql):
|
||||
"""
|
||||
查询符合sql条件的数据的第一条数据
|
||||
:param sql: 执行的sql
|
||||
:return: 返回查询结果的第一条数据
|
||||
"""
|
||||
|
||||
self.conn.commit()
|
||||
self.cursor.execute(sql)
|
||||
data = self.cursor.fetchone()
|
||||
self.cursor.close()
|
||||
return self.verify(data)
|
||||
|
||||
def insert(self, sql):
|
||||
"""
|
||||
插入数据
|
||||
:param sql: 执行的sql
|
||||
"""
|
||||
|
||||
self.cursor.execute(sql)
|
||||
# 提交 只要数据库更新就要commit
|
||||
self.conn.commit()
|
||||
self.cursor.close()
|
||||
|
||||
def update(self, sql):
|
||||
"""
|
||||
更新数据
|
||||
:param sql: 执行的sql
|
||||
"""
|
||||
|
||||
self.cursor.execute(sql)
|
||||
# 提交 只要数据库更新就要commit
|
||||
self.conn.commit()
|
||||
self.cursor.close()
|
||||
|
||||
def query(self, sql, one=True):
|
||||
"""
|
||||
根据传值决定查询一条数据还是所有
|
||||
:param sql: 查询的SQL语句
|
||||
:param one: 默认True. True查一条数据,否则查所有
|
||||
:return:
|
||||
"""
|
||||
if one:
|
||||
return self.query_one(sql)
|
||||
else:
|
||||
return self.query_all(sql)
|
||||
|
||||
def close(self):
|
||||
"""
|
||||
断开游标,关闭数据库
|
||||
:return:
|
||||
"""
|
||||
self.conn.close()
|
||||
|
||||
def verify(self, result: dict) -> Union[dict, None]:
|
||||
"""验证结果能否被json.dumps序列化"""
|
||||
# 尝试变成字符串,解决datetime 无法被json 序列化问题
|
||||
try:
|
||||
json.dumps(result)
|
||||
except TypeError: # TypeError: Object of type datetime is not JSON serializable
|
||||
for k, v in result.items():
|
||||
if isinstance(v, datetime):
|
||||
result[k] = str(v)
|
||||
return result
|
|
@ -0,0 +1,21 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# @Time : 2021/8/19 9:13
|
||||
# @Author : Flora.Chen
|
||||
# @File : handle_platform.py
|
||||
# @Software: PyCharm
|
||||
# @Desc: 跨平台的支持allure,用于生成allure测试报告
|
||||
|
||||
import platform
|
||||
|
||||
|
||||
class HandlePlatform:
|
||||
"""跨平台的支持allure, webdriver"""
|
||||
|
||||
@property
|
||||
def allure(self):
|
||||
if platform.system() == "Windows":
|
||||
cmd = "allure.bat"
|
||||
else:
|
||||
cmd = "allure"
|
||||
return cmd
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# @Time : 2021/8/14 12:13
|
||||
# @Author : Flora.Chen
|
||||
# @File : handle_time.py
|
||||
# @Software: PyCharm
|
||||
# @Desc: 时间计算的方法
|
||||
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
now = datetime.now().strftime("%Y-%m-%d %H_%M_%S")
|
||||
|
||||
|
||||
class AddTime:
|
||||
"""
|
||||
计算时间加减的类
|
||||
"""
|
||||
|
||||
def __init__(self, start_time=now, time_unit="days", interval=1):
|
||||
if isinstance(start_time, str):
|
||||
start_time = datetime.strptime(start_time, "%Y-%m-%d %H:%M:%S")
|
||||
self.start_time = start_time
|
||||
self.time_unit = time_unit
|
||||
self.interval = interval
|
||||
|
||||
def add_time(self):
|
||||
"""
|
||||
在指定时间基础上加减指定时间
|
||||
:return: 计算结果
|
||||
"""
|
||||
if self.time_unit == "seconds":
|
||||
return self.add_seconds()
|
||||
elif self.time_unit == "hours":
|
||||
return self.add_hours()
|
||||
elif self.time_unit == "weeks":
|
||||
return self.add_weeks()
|
||||
elif self.time_unit == "minutes":
|
||||
return self.add_minutes()
|
||||
else:
|
||||
return self.add_day()
|
||||
|
||||
def add_minutes(self):
|
||||
"""
|
||||
在当前时间基础上加减指定分钟
|
||||
:return: 计算结果
|
||||
"""
|
||||
return (self.start_time + timedelta(minutes=self.interval)).strftime("%Y-%m-%d %H:%M:%S")
|
||||
|
||||
def add_hours(self):
|
||||
"""
|
||||
在当前时间基础上加减指定小时
|
||||
:return: 计算结果
|
||||
"""
|
||||
return (self.start_time + timedelta(hours=self.interval)).strftime("%Y-%m-%d %H:%M:%S")
|
||||
|
||||
def add_day(self):
|
||||
"""
|
||||
在当前时间基础上加减指定天
|
||||
:return: 计算结果
|
||||
"""
|
||||
return (self.start_time + timedelta(days=self.interval)).strftime("%Y-%m-%d %H:%M:%S")
|
||||
|
||||
def add_seconds(self):
|
||||
"""
|
||||
在当前时间基础上加减指定秒
|
||||
:return: 计算结果
|
||||
"""
|
||||
return (self.start_time + timedelta(seconds=self.interval)).strftime("%Y-%m-%d %H:%M:%S")
|
||||
|
||||
def add_weeks(self):
|
||||
"""
|
||||
在当前时间基础上加减指定周
|
||||
:return: 计算结果
|
||||
"""
|
||||
return (self.start_time + timedelta(weeks=self.interval)).strftime("%Y-%m-%d %H:%M:%S")
|
||||
|
||||
|
||||
def mistiming(start_time, end_time):
|
||||
"""计算两个时间差"""
|
||||
stime = datetime.strptime(start_time, "%Y-%m-%d %H:%M:%S")
|
||||
etime = datetime.strptime(end_time, "%Y-%m-%d %H:%M:%S")
|
||||
total_seconds = (etime - stime).total_seconds()
|
||||
if total_seconds < 0:
|
||||
total_seconds = (stime - etime).total_seconds()
|
||||
day = total_seconds // (60 * 60 * 24)
|
||||
rest_seconds = total_seconds % (60 * 60 * 24)
|
||||
hours = rest_seconds // (60 * 60)
|
||||
rest_seconds = rest_seconds % (60 * 60)
|
||||
mins = rest_seconds // 60
|
||||
seconds = rest_seconds % 60
|
||||
return f"{int(day)}天 {int(hours)}小时 {int(mins)}分钟 {int(seconds)}秒"
|
|
@ -0,0 +1,48 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# @Time : 2021/8/14 12:21
|
||||
# @Author : Flora.Chen
|
||||
# @File : handle_yagmail.py
|
||||
# @Software: PyCharm
|
||||
# @Desc: 通过第三方模块yagmail发送邮件
|
||||
|
||||
import yagmail
|
||||
from loguru import logger
|
||||
import os
|
||||
|
||||
|
||||
class EmailServe:
|
||||
|
||||
@staticmethod
|
||||
def send_email(setting: dict):
|
||||
"""
|
||||
入参一个字典
|
||||
:param user: 发件人邮箱
|
||||
:param password: 邮箱授权码
|
||||
:param host: 发件人使用的邮箱服务 例如:smtp.163.com
|
||||
:param contents: 内容
|
||||
:param addressees: 收件人列表
|
||||
:param title: 邮件标题
|
||||
:param enclosures: 附件列表
|
||||
:return:
|
||||
"""
|
||||
logger.debug("------------ 连接邮件服务器 ---------------")
|
||||
yag = yagmail.SMTP(
|
||||
setting['user'],
|
||||
setting['password'],
|
||||
setting['host'])
|
||||
logger.debug("------------ 开始发送邮件 ---------------")
|
||||
if os.path.exists(setting['enclosures']):
|
||||
yag.send(
|
||||
to=setting['addressees'],
|
||||
subject=setting['title'],
|
||||
contents=setting['contents'],
|
||||
attachments=setting['enclosures'])
|
||||
else:
|
||||
yag.send(
|
||||
to=setting['addressees'],
|
||||
subject=setting['title'],
|
||||
contents=setting['contents'])
|
||||
logger.debug("------------ 邮件发送完毕,关闭邮件服务器连接 ---------------")
|
||||
yag.close()
|
||||
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# @Time : 2021/8/14 12:10
|
||||
# @Author : Flora.Chen
|
||||
# @File : handle_yaml.py
|
||||
# @Software: PyCharm
|
||||
# @Desc: yaml文件的读写方法
|
||||
|
||||
import yaml
|
||||
import os
|
||||
|
||||
|
||||
class HandleYaml:
|
||||
"""
|
||||
读取yaml数据,以及往yaml文件中写入数据
|
||||
"""
|
||||
|
||||
def __init__(self, file):
|
||||
"""
|
||||
初始化yaml文件,如果不存在则创建
|
||||
:param file: 文件绝对路径
|
||||
"""
|
||||
if not os.path.exists(file):
|
||||
with open(file, mode="a", encoding="utf-8"):
|
||||
print("{}文件创建成功~".format(file))
|
||||
self.file = file
|
||||
|
||||
def read(self):
|
||||
"""
|
||||
读取yaml文件数据
|
||||
:return: 读取到的数据
|
||||
"""
|
||||
with open(file=self.file, mode="r", encoding="UTF-8") as f:
|
||||
return yaml.load(f, Loader=yaml.SafeLoader)
|
||||
|
||||
def write(self, data, mode="a"):
|
||||
"""
|
||||
往yaml文件中写入数据,默认是追加写入
|
||||
:param data: 要写入的数据
|
||||
:param mode: 写入模式
|
||||
:return:
|
||||
"""
|
||||
with open(self.file, mode=mode, encoding="utf-8") as f:
|
||||
yaml.dump(data, f)
|
|
@ -0,0 +1,122 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# @Time : 2021/8/14 12:17
|
||||
# @Author : Flora.Chen
|
||||
# @File : helper.py
|
||||
# @Software: PyCharm
|
||||
# @Desc: 封装一些小方法
|
||||
|
||||
|
||||
import os
|
||||
from faker import Faker
|
||||
import urllib.request
|
||||
from loguru import logger
|
||||
import zipfile
|
||||
import os
|
||||
|
||||
|
||||
def get_newest_file(path):
|
||||
"""获取目录中创建时间最新的文件名"""
|
||||
files = os.listdir(path)
|
||||
a = 0
|
||||
newest_file = ""
|
||||
for file in files:
|
||||
file_path = os.path.join(path, file)
|
||||
timestamp = os.path.getctime(file_path)
|
||||
if timestamp > a:
|
||||
a = timestamp
|
||||
newest_file = file_path
|
||||
return newest_file
|
||||
|
||||
|
||||
def generate_characters(n):
|
||||
"""随机生成指定长度的字符"""
|
||||
fk = Faker("zh_CN")
|
||||
res = fk.name()
|
||||
while True:
|
||||
if len(res) < n:
|
||||
res = res + fk.sentence()
|
||||
else:
|
||||
break
|
||||
return res[:n]
|
||||
|
||||
|
||||
def create_dir(path):
|
||||
"""创建目录"""
|
||||
if not os.path.exists(path):
|
||||
os.mkdir(path)
|
||||
logger.debug(f"成功创建目录: {path}")
|
||||
logger.debug(f"目录地址已存在: {path}")
|
||||
|
||||
|
||||
def download_img(url, path):
|
||||
"""
|
||||
通过Python自带的库urllib下载图片
|
||||
需要导入:import urllib.request
|
||||
:param url: url地址
|
||||
:param path: 图片保存的绝对路径
|
||||
"""
|
||||
try:
|
||||
request = urllib.request.Request(url)
|
||||
response = urllib.request.urlopen(request)
|
||||
if response.getcode() == 200:
|
||||
logger.debug("请求状态码200, 开始下载图片......")
|
||||
with open(path, "wb") as fb:
|
||||
fb.write(response.read())
|
||||
return path
|
||||
except Exception as e:
|
||||
logger.debug("捕获到异常,下载图片失败")
|
||||
return f"{e}"
|
||||
|
||||
|
||||
def zip_file(file_path: str, out_path: str):
|
||||
"""
|
||||
压缩指定文件夹
|
||||
:param file_path: 目标文件夹路径
|
||||
:param out_path: 压缩文件保存路径+xxxx.zip
|
||||
:return: 无
|
||||
"""
|
||||
# 如果传入的路径是一个目录才进行压缩操作
|
||||
if os.path.isdir(file_path):
|
||||
logger.debug("目标路径是一个目录,开始进行压缩......")
|
||||
# 写入
|
||||
zip = zipfile.ZipFile(out_path, "w", zipfile.ZIP_DEFLATED)
|
||||
for path, dirnames, filenames in os.walk(file_path):
|
||||
# 去掉目标跟路径,只对目标文件夹下边的文件及文件夹进行压缩
|
||||
fpath = path.replace(file_path, '')
|
||||
for filename in filenames:
|
||||
zip.write(
|
||||
os.path.join(
|
||||
path, filename), os.path.join(
|
||||
fpath, filename))
|
||||
zip.close()
|
||||
logger.debug("压缩完成!")
|
||||
else:
|
||||
logger.error("目标路径不是一个目录,请检查!")
|
||||
|
||||
|
||||
def unzip_file():
|
||||
pass
|
||||
|
||||
|
||||
def delete_dir_file(file_path):
|
||||
"""
|
||||
删除指定目录下的所有文件
|
||||
:param file_path: 目标文件夹路径 (存在多级路径的暂不支持)
|
||||
"""
|
||||
paths = os.listdir(file_path)
|
||||
if paths:
|
||||
logger.debug("目标目录存在文件或目录,进行删除操作")
|
||||
for item in paths:
|
||||
path = os.path.join(file_path, item)
|
||||
# 如果目标路径是一个文件,使用os.remove删除
|
||||
if os.path.isfile(path):
|
||||
os.remove(path)
|
||||
# 如果目标路径是一个目录,使用os.rmdir删除
|
||||
if os.path.isdir(path):
|
||||
os.rmdir(path)
|
||||
else:
|
||||
logger.debug("目标目录不存在文件或目录,不需要删除")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
delete_dir_file(r"/home/flora/chy/auotest/report")
|
|
@ -0,0 +1,62 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# @Time : 2021/8/16 11:42
|
||||
# @Author : Flora.Chen
|
||||
# @File : project_tree.py
|
||||
# @Software: PyCharm
|
||||
# @Desc: 快速生成文件夹目录结构图
|
||||
|
||||
import re
|
||||
from pathlib import Path
|
||||
from pathlib import WindowsPath
|
||||
from typing import Optional, List
|
||||
|
||||
|
||||
class DirectionTree:
|
||||
def __init__(self,
|
||||
direction_name: str = 'WorkingDirection',
|
||||
direction_path: str = '.',
|
||||
ignore_list: Optional[List[str]] = None):
|
||||
self.owner: WindowsPath = Path(direction_path)
|
||||
self.tree: str = direction_name + '/\n'
|
||||
self.ignore_list = ignore_list
|
||||
if ignore_list is None:
|
||||
self.ignore_list = []
|
||||
self.direction_ergodic(path_object=self.owner, n=0)
|
||||
|
||||
def tree_add(self, path_object: WindowsPath, n=0, last=False):
|
||||
if n > 0:
|
||||
if last:
|
||||
self.tree += '│' + (' │' * (n - 1)) + ' └────' + path_object.name
|
||||
else:
|
||||
self.tree += '│' + (' │' * (n - 1)) + ' ├────' + path_object.name
|
||||
else:
|
||||
if last:
|
||||
self.tree += '└' + ('──' * 2) + path_object.name
|
||||
else:
|
||||
self.tree += '├' + ('──' * 2) + path_object.name
|
||||
if path_object.is_file():
|
||||
self.tree += '\n'
|
||||
return False
|
||||
elif path_object.is_dir():
|
||||
self.tree += '/\n'
|
||||
return True
|
||||
|
||||
def filter_file(self, file):
|
||||
for item in self.ignore_list:
|
||||
if re.fullmatch(item, file.name):
|
||||
return False
|
||||
return True
|
||||
|
||||
def direction_ergodic(self, path_object: WindowsPath, n=0):
|
||||
dir_file: list = list(path_object.iterdir())
|
||||
dir_file.sort(key=lambda x: x.name.lower())
|
||||
dir_file = [f for f in filter(self.filter_file, dir_file)]
|
||||
for i, item in enumerate(dir_file):
|
||||
if i + 1 == len(dir_file):
|
||||
if self.tree_add(item, n, last=True):
|
||||
self.direction_ergodic(item, n + 1)
|
||||
else:
|
||||
if self.tree_add(item, n, last=False):
|
||||
self.direction_ergodic(item, n + 1)
|
||||
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# @Time : 2021/8/13 20:41
|
||||
# @Author : Flora.Chen
|
||||
# @File : __init__.py.py
|
||||
# @Software: PyCharm
|
||||
# @Desc:
|
|
@ -0,0 +1,20 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Title</title>
|
||||
<h1 class="title" style="text-align: center;color: #bd2c00">API & UI 自动化测试报告</h1>
|
||||
<h3> 各位同事,大家好!</h3>
|
||||
<h4> 本轮自动化测试已完成!可以通过下载附件查看测试报告的详细信息!</h4>
|
||||
查看测试报告的方法:<br/>
|
||||
下载附件:est_report.zip(自动化测试报告)<br/>
|
||||
本地解压test_report.zip(自动化测试报告)<br/>
|
||||
请使用已安装Live Server 插件的VsCode,打开解压目录下的index.html查看报告<br/>
|
||||
|
||||
|
||||
|
||||
</head>
|
||||
<body>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,77 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Title</title>
|
||||
<style type="text/css">
|
||||
.logo {
|
||||
float: left;
|
||||
min-width: 40px;
|
||||
height: 40px;
|
||||
}
|
||||
.title{
|
||||
text-align: center;
|
||||
color: rgb(235, 30, 15);
|
||||
}
|
||||
.desc{
|
||||
text-align: left;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div >
|
||||
<a href="https://*******/" class="fl mr50" style="min-width: 45px;">
|
||||
<img alt="Trustie" class="logo" src="https://*******/images/avatars/LaboratorySetting/1nav?t=1610102853">
|
||||
</a>
|
||||
</div>
|
||||
<div>
|
||||
<h1 class="title">API & UI 自动化测试报告</h1>
|
||||
</div>
|
||||
<div class="desc">
|
||||
<p><span style="color: red; "> Jenkins自动发送的测试报告邮件,无需回复!</span></p>
|
||||
<h4> 各位同事,大家好,以下为${PROJECT_NAME}自动化测试构建信息</h4><br/>
|
||||
<h4> 在线测试报告直达链接:<a href="${PROJECT_URL}/${BUILD_NUMBER}/allure">${PROJECT_URL}/${BUILD_NUMBER}/allure</a></h4>
|
||||
</div>
|
||||
<br/>
|
||||
<div>
|
||||
<table width="900" cellpadding="8px" cellspacing="8px" class="table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><br/>
|
||||
<b><font color="#0B610B">项目描述:${JOB_DESCRIPTION}<br></font></b>
|
||||
<hr size="2" width="100%" align="center" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>项目名称 : ${PROJECT_NAME}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>构建编号 : 第${BUILD_NUMBER}次构建</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>触发原因: ${CAUSE}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>构建状态: ${BUILD_STATUS}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>构建日志: <a href="${PROJECT_URL}${BUILD_NUMBER}/console">${PROJECT_URL}${BUILD_NUMBER}/console</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>构建Url : <a href="${BUILD_URL}">${BUILD_URL}</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>工作目录 : <a href="${PROJECT_URL}ws">${PROJECT_URL}ws</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>项目Url : <a href="${PROJECT_URL}">${PROJECT_URL}</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>allure在线测试报告:<a href="${PROJECT_URL}/${BUILD_NUMBER}/allure">${PROJECT_URL}/${BUILD_NUMBER}/allure</a></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</body>
|
|
@ -0,0 +1,131 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# @Time : 2021/8/14 22:50
|
||||
# @Author : Flora.Chen
|
||||
# @File : settings.py
|
||||
# @Software: PyCharm
|
||||
# @Desc: 配置文件
|
||||
import enum
|
||||
import os
|
||||
import platform
|
||||
|
||||
# 项目根目录
|
||||
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
|
||||
# 通用模块目录
|
||||
COMMON_DIR = os.path.join(BASE_DIR, "common")
|
||||
|
||||
# 第三方库目录
|
||||
LIB_DIR = os.path.join(BASE_DIR, "lib")
|
||||
|
||||
# 配置模块目录
|
||||
CONF_DIR = os.path.join(BASE_DIR, "config")
|
||||
|
||||
# 数据模块目录
|
||||
DATA_DIR = os.path.join(BASE_DIR, "data")
|
||||
|
||||
# API数据模块目录
|
||||
API_DATA = os.path.join(DATA_DIR, "api_data")
|
||||
|
||||
# UI数据模块目录
|
||||
UI_DATA = os.path.join(DATA_DIR, "ui_data")
|
||||
|
||||
# 日志保存目录
|
||||
LOG_DIR = os.path.join(BASE_DIR, "log")
|
||||
if not os.path.exists(LOG_DIR):
|
||||
os.mkdir(LOG_DIR)
|
||||
|
||||
# 图片保存目录
|
||||
IMG_DIR = os.path.join(BASE_DIR, "image")
|
||||
if not os.path.exists(IMG_DIR):
|
||||
os.mkdir(IMG_DIR)
|
||||
|
||||
# 报告保存目录
|
||||
REPORT_DIR = os.path.join(BASE_DIR, "report")
|
||||
if not os.path.exists(REPORT_DIR):
|
||||
os.mkdir(REPORT_DIR)
|
||||
|
||||
# API测试用例模块
|
||||
API_CASE_DIR = os.path.join(BASE_DIR, "test_api")
|
||||
|
||||
# API测试用例模块
|
||||
UI_CASE_DIR = os.path.join(BASE_DIR, "test_ui")
|
||||
|
||||
|
||||
class EnvConfig(enum.Enum):
|
||||
"""环境配置信息"""
|
||||
env_mapping = {
|
||||
"test": {
|
||||
"forge": "https://test*****.trustie.net"
|
||||
},
|
||||
"live": {
|
||||
"forge": "https://*****.trustie.net"
|
||||
},
|
||||
}
|
||||
|
||||
# 不同环境对应的用户名密码
|
||||
users_mapping = {
|
||||
"test": {"user": "*****.", "pwd": "*****.", "nickname": "*****."},
|
||||
"live": {"user": "*****.", "pwd": "*****.@", "nickname": "*****."}
|
||||
}
|
||||
|
||||
# 数据库配置
|
||||
db_mapping = {
|
||||
|
||||
}
|
||||
|
||||
|
||||
class RunConfig:
|
||||
"""
|
||||
运行测试配置
|
||||
"""
|
||||
# 配置浏览器驱动类型(chrome/firefox/chrome-headless/firefox-headless)。
|
||||
driver_type = "chrome-headless"
|
||||
# driver_type = "chrome"
|
||||
|
||||
# 失败重跑次数
|
||||
rerun = "0"
|
||||
|
||||
# 当达到最大失败数,停止执行
|
||||
max_fail = "10"
|
||||
|
||||
# 浏览器驱动(不需要修改)
|
||||
driver = None
|
||||
|
||||
# 浏览器驱动放置的位置
|
||||
if platform.system() == "Linux":
|
||||
if "chrome" in driver_type:
|
||||
driver_path = "/usr/bin/chromedriver"
|
||||
if "firefox" in driver_type:
|
||||
driver_path = ""
|
||||
else:
|
||||
if "chrome" in driver_type:
|
||||
driver_path = r"D:/Program Files/python39/chromedriver.exe"
|
||||
if "firefox" in driver_type:
|
||||
driver_path = ""
|
||||
|
||||
|
||||
# 基准的请求头信息
|
||||
headers = {
|
||||
"Accept-Encoding": "gzip, deflate",
|
||||
"Accept-Language": "zh-CN,zh;q=0.9",
|
||||
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36"
|
||||
}
|
||||
|
||||
# 发送邮件的相关配置信息
|
||||
email = {
|
||||
"user": "*****.@*****..com.cn", # 发件人邮箱
|
||||
"password": "*****", # 发件人邮箱授权码
|
||||
"host": "smtp.qiye.aliyun.com",
|
||||
"contents": ['<h1 class="title" style="text-align: center;color: #bd2c00">API & UI 自动化测试报告</h1>',
|
||||
'<h3> 各位同事,大家好!</h3>',
|
||||
'<h4> 本轮自动化测试已完成!可以通过下载附件查看测试报告的详细信息!</h4>',
|
||||
" 查看测试报告的方法:<br/>"
|
||||
" 下载附件:est_report.zip(自动化测试报告)<br/>"
|
||||
" 本地解压test_report.zip(自动化测试报告)<br/>",
|
||||
" 请使用已安装Live Server 插件的VsCode,打开解压目录下的index.html查看报告<br/>",
|
||||
" "]
|
||||
,
|
||||
"addressees": ["*****.@*****..com.cn"], # 收件人邮箱
|
||||
"title": "自动化测试报告",
|
||||
"enclosures": os.path.join(REPORT_DIR, "autotest_report.zip") # 附件
|
||||
}
|
|
@ -0,0 +1,131 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# @Time : 2021/8/14 22:50
|
||||
# @Author : Flora.Chen
|
||||
# @File : settings_example.py
|
||||
# @Software: PyCharm
|
||||
# @Desc: 配置文件
|
||||
import enum
|
||||
import os
|
||||
import platform
|
||||
|
||||
# 项目根目录
|
||||
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
|
||||
# 通用模块目录
|
||||
COMMON_DIR = os.path.join(BASE_DIR, "common")
|
||||
|
||||
# 第三方库目录
|
||||
LIB_DIR = os.path.join(BASE_DIR, "lib")
|
||||
|
||||
# 配置模块目录
|
||||
CONF_DIR = os.path.join(BASE_DIR, "config")
|
||||
|
||||
# 数据模块目录
|
||||
DATA_DIR = os.path.join(BASE_DIR, "data")
|
||||
|
||||
# API数据模块目录
|
||||
API_DATA = os.path.join(DATA_DIR, "api_data")
|
||||
|
||||
# UI数据模块目录
|
||||
UI_DATA = os.path.join(DATA_DIR, "ui_data")
|
||||
|
||||
# 日志保存目录
|
||||
LOG_DIR = os.path.join(BASE_DIR, "log")
|
||||
if not os.path.exists(LOG_DIR):
|
||||
os.mkdir(LOG_DIR)
|
||||
|
||||
# 图片保存目录
|
||||
IMG_DIR = os.path.join(BASE_DIR, "image")
|
||||
if not os.path.exists(IMG_DIR):
|
||||
os.mkdir(IMG_DIR)
|
||||
|
||||
# 报告保存目录
|
||||
REPORT_DIR = os.path.join(BASE_DIR, "report")
|
||||
if not os.path.exists(REPORT_DIR):
|
||||
os.mkdir(REPORT_DIR)
|
||||
|
||||
# API测试用例模块
|
||||
API_CASE_DIR = os.path.join(BASE_DIR, "test_api")
|
||||
|
||||
# API测试用例模块
|
||||
UI_CASE_DIR = os.path.join(BASE_DIR, "test_ui")
|
||||
|
||||
|
||||
class EnvConfig(enum.Enum):
|
||||
"""环境配置信息"""
|
||||
env_mapping = {
|
||||
"test": {
|
||||
"forge": "https://test*****.trustie.net"
|
||||
},
|
||||
"live": {
|
||||
"forge": "https://*****.trustie.net"
|
||||
},
|
||||
}
|
||||
|
||||
# 不同环境对应的用户名密码
|
||||
users_mapping = {
|
||||
"test": {"user": "*****.", "pwd": "*****.", "nickname": "*****."},
|
||||
"live": {"user": "*****.", "pwd": "*****.@", "nickname": "*****."}
|
||||
}
|
||||
|
||||
# 数据库配置
|
||||
db_mapping = {
|
||||
|
||||
}
|
||||
|
||||
|
||||
class RunConfig:
|
||||
"""
|
||||
运行测试配置
|
||||
"""
|
||||
# 配置浏览器驱动类型(chrome/firefox/chrome-headless/firefox-headless)。
|
||||
driver_type = "chrome-headless"
|
||||
# driver_type = "chrome"
|
||||
|
||||
# 失败重跑次数
|
||||
rerun = "0"
|
||||
|
||||
# 当达到最大失败数,停止执行
|
||||
max_fail = "10"
|
||||
|
||||
# 浏览器驱动(不需要修改)
|
||||
driver = None
|
||||
|
||||
# 浏览器驱动放置的位置
|
||||
if platform.system() == "Linux":
|
||||
if "chrome" in driver_type:
|
||||
driver_path = "/usr/bin/chromedriver"
|
||||
if "firefox" in driver_type:
|
||||
driver_path = ""
|
||||
else:
|
||||
if "chrome" in driver_type:
|
||||
driver_path = r"D:/Program Files/python39/chromedriver.exe"
|
||||
if "firefox" in driver_type:
|
||||
driver_path = ""
|
||||
|
||||
|
||||
# 基准的请求头信息
|
||||
headers = {
|
||||
"Accept-Encoding": "gzip, deflate",
|
||||
"Accept-Language": "zh-CN,zh;q=0.9",
|
||||
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36"
|
||||
}
|
||||
|
||||
# 发送邮件的相关配置信息
|
||||
email = {
|
||||
"user": "*****.@*****..com.cn", # 发件人邮箱
|
||||
"password": "*****", # 发件人邮箱授权码
|
||||
"host": "smtp.qiye.aliyun.com",
|
||||
"contents": ['<h1 class="title" style="text-align: center;color: #bd2c00">API & UI 自动化测试报告</h1>',
|
||||
'<h3> 各位同事,大家好!</h3>',
|
||||
'<h4> 本轮自动化测试已完成!可以通过下载附件查看测试报告的详细信息!</h4>',
|
||||
" 查看测试报告的方法:<br/>"
|
||||
" 下载附件:est_report.zip(自动化测试报告)<br/>"
|
||||
" 本地解压test_report.zip(自动化测试报告)<br/>",
|
||||
" 请使用已安装Live Server 插件的VsCode,打开解压目录下的index.html查看报告<br/>",
|
||||
" "]
|
||||
,
|
||||
"addressees": ["*****.@*****..com.cn"], # 收件人邮箱
|
||||
"title": "自动化测试报告",
|
||||
"enclosures": os.path.join(REPORT_DIR, "autotest_report.zip") # 附件
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# @Time : 2021/8/14 12:33
|
||||
# @Author : Flora.Chen
|
||||
# @File : draft_bk.py.py
|
||||
# @Software: PyCharm
|
||||
# @Desc:
|
||||
|
||||
import pytest
|
||||
from loguru import logger
|
||||
from config.settings import EnvConfig
|
||||
from api.login_api import TrustieLogin
|
||||
from api.project_api import ProjectApi
|
||||
|
||||
|
||||
# ------------------------------------- START: 配置运行环境 ---------------------------------------#
|
||||
|
||||
def pytest_addoption(parser):
|
||||
"""
|
||||
pytest_addoption 可以让用户注册一个自定义的命令行参数,方便用户将数据传递给 pytest;
|
||||
这个 Hook 方法一般和 内置 fixture pytestconfig 配合使用,pytest_addoption 注册命令行参数,pytestconfig 通过配置对象读取参数的值;
|
||||
:param parser:
|
||||
:return:
|
||||
"""
|
||||
|
||||
parser.addoption(
|
||||
# action="store" 默认,只存储参数的值,可以存储任何类型的值,此时 default 也可以是任何类型的值,而且命令行参数多次使用也只能生效一个,最后一个值覆盖之前的值;
|
||||
"--env", action="store",
|
||||
default="test",
|
||||
choices=["test", "live"], # choices 只允许输入的值的范围
|
||||
help="通过自定义命令行参数-env设置当前运行的环境"
|
||||
)
|
||||
|
||||
|
||||
# 从配置对象中读取自定义参数的值
|
||||
@pytest.fixture(scope="session", autouse=True)
|
||||
def env(request):
|
||||
env = request.config.getoption("--env")
|
||||
env_mapping = EnvConfig.env_mapping.value
|
||||
env = env_mapping.get(f"{env}")
|
||||
forge_env = env.get("forge")
|
||||
logger.debug(f"当前的执行环境:{forge_env}")
|
||||
return {"forge_env": forge_env}
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def get_user(request):
|
||||
env = request.config.getoption("--env")
|
||||
users_mapping = EnvConfig.users_mapping.value
|
||||
users = users_mapping.get(f"{env}")
|
||||
return users
|
||||
|
||||
|
||||
# ------------------------------------- END: 配置运行环境 ---------------------------------------#
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def get_cookies(env, get_user):
|
||||
login = TrustieLogin(host=env)
|
||||
return login.login_api(user=get_user.get("user"), pwd=get_user.get("pwd")).cookies
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
# 登录的用例数据
|
||||
case:
|
||||
-
|
||||
id: 1
|
||||
title: 用户名密码正确,登录成功
|
||||
interface: login
|
||||
url: /api/accounts/login.json
|
||||
method: POST
|
||||
header: { }
|
||||
json: {"login": "*****","password": "*****","autologin": 1}
|
||||
expected: auotest
|
||||
extractor: $..user_id
|
|
@ -0,0 +1,39 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# @Time : 2021/8/14 14:22
|
||||
# @Author : Flora.Chen
|
||||
# @File : login_data.py
|
||||
# @Software: PyCharm
|
||||
# @Desc:
|
||||
|
||||
|
||||
# 登录成功
|
||||
success = [
|
||||
{"title": "正确的用户名和密码登录成功", "user": "{}", "pwd": "{}", "expected": "{}"}
|
||||
]
|
||||
|
||||
# 登录失败-错误提示是输入框下红色提示
|
||||
failed_error_message = [
|
||||
{"title": "用户名为空,登录失败,有错误提示:用户名不能为空", "user": "", "pwd": "{}", "expected": ["用户名不能为空"]}
|
||||
|
||||
]
|
||||
|
||||
# 登录失败,错误提示是弹窗
|
||||
failed_error_pop = [
|
||||
{"title": "用户名错误,登录失败,提示错误的账号或密码:", "user": "18774970061111", "pwd": "", "expected": ["错误的账号或密码"]},
|
||||
{"title": "用户名正确,密码第一次错误,提示:你已经输错密码1次,还剩余4次机会", "user": "{}", "pwd": "1111111111",
|
||||
"expected": ["你已经输错密码1次,还剩余4次机会"]},
|
||||
{"title": "用户名正确,密码第二次错误,提示:你已经输错密码2次,还剩余3次机会", "user": "{}", "pwd": "11111222222",
|
||||
"expected": ["你已经输错密码2次,还剩余3次机会"]},
|
||||
{"title": "用户名正确,密码第三次错误,提示:你已经输错密码3次,还剩余2次机会", "user": "{}", "pwd": "1111333333",
|
||||
"expected": ["你已经输错密码3次,还剩余2次机会"]},
|
||||
# {"title": "用户名正确,密码第四次错误,提示:你已经输错密码4次,还剩余1次机会", "user": "{}", "pwd": "11115555555",
|
||||
# "expected": ["你已经输错密码4次,还剩余1次机会"]},
|
||||
# {"title": "用户名正确,密码第五次错误,提示:登录密码出错已达上限,账号已被锁定, 请60分钟后重新登录或找回密码", "user": "{}", "pwd": "11115555555",
|
||||
# "expected": ["登录密码出错已达上限,账号已被锁定, 请60分钟后重新登录或找回密码"]}
|
||||
|
||||
]
|
||||
|
||||
# 登录失败,密码为空不能点击登录
|
||||
failed_password_empty = [
|
||||
{"title": "用户名正确, 密码为空,登录按钮无法点击", "user": "{}", "pwd": "", "expected": "bluebutton"}
|
||||
]
|
|
@ -0,0 +1,107 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# @Time : 2021/8/23 14:25
|
||||
# @Author : Flora.Chen
|
||||
# @File : new_project_data.py
|
||||
# @Software: PyCharm
|
||||
# @Desc:
|
||||
|
||||
from common.handle_faker import *
|
||||
import random
|
||||
|
||||
new_project = [
|
||||
{
|
||||
"title": "新建公有项目",
|
||||
"owner": "",
|
||||
"name": "Auto Test " + e_fk.name(),
|
||||
"desc": z_fk_text,
|
||||
"repository_name": e_fk.name().replace(" ", ""),
|
||||
"project_category": random.choice(["机器学习", "大数据", "深度学习", "人工智能", "量子计算", "智慧医疗", "自动驾驶", "其他"]),
|
||||
"project_language": random.choice(["C", "Ruby", "C#", "HTML", "C++"]),
|
||||
"ignore": random.choice(["Ada", "Actionscript", "Ansible", "Android", "Agda"]),
|
||||
"license": random.choice(["0BSD", "AAL", "AFL-1.1", "389-exception"]),
|
||||
"private": False,
|
||||
"expected": ""
|
||||
},
|
||||
{
|
||||
"title": "新建私有项目",
|
||||
"owner": "",
|
||||
"name": "Auto Test " + e_fk.name(),
|
||||
"desc": z_fk_text,
|
||||
"repository_name": e_fk.name().replace(" ", ""),
|
||||
"project_category": random.choice(["机器学习", "大数据", "深度学习", "人工智能", "量子计算", "智慧医疗", "自动驾驶", "其他"]),
|
||||
"project_language": random.choice(["C", "Ruby", "C#", "HTML", "C++"]),
|
||||
"ignore": random.choice(["Ada", "Actionscript", "Ansible", "Android", "Agda"]),
|
||||
"license": random.choice(["0BSD", "AAL", "AFL-1.1", "389-exception"]),
|
||||
"private": True,
|
||||
"expected": ""
|
||||
}
|
||||
|
||||
]
|
||||
|
||||
export_project = [
|
||||
{
|
||||
"title": "导入项目(公有)-原项目是公有项目",
|
||||
"owner": "",
|
||||
"mirror_url": "https://gitee.com/paddlepaddle/Parakeet.git",
|
||||
"mirror_private": False,
|
||||
"name": "Auto Test " + e_fk.name(),
|
||||
"desc": z_fk_text,
|
||||
"repository_name": "Parakeet",
|
||||
"project_category": random.choice(["机器学习", "大数据", "深度学习", "人工智能", "量子计算", "智慧医疗", "自动驾驶", "其他"]),
|
||||
"project_language": random.choice(["C", "Ruby", "C#", "HTML", "C++"]),
|
||||
"ignore": random.choice(["Ada", "Actionscript", "Ansible", "Android", "Agda"]),
|
||||
"private": False,
|
||||
"expected": "",
|
||||
"mirror_type": False
|
||||
},
|
||||
{
|
||||
"title": "导入项目(私有)-原项目是公有项目",
|
||||
"owner": "",
|
||||
"mirror_url": "https://gitee.com/chinasoft4_ohos/NewQuickAction.git",
|
||||
"mirror_private": False,
|
||||
"name": "Auto Test " + e_fk.name(),
|
||||
"desc": z_fk_text,
|
||||
"repository_name": "NewQuickAction",
|
||||
"project_category": random.choice(["机器学习", "大数据", "深度学习", "人工智能", "量子计算", "智慧医疗", "自动驾驶", "其他"]),
|
||||
"project_language": random.choice(["C", "Ruby", "C#", "HTML", "C++"]),
|
||||
"ignore": random.choice(["Ada", "Actionscript", "Ansible", "Android", "Agda"]),
|
||||
"private": True,
|
||||
"expected": "",
|
||||
"mirror_type": True
|
||||
},
|
||||
{
|
||||
"title": "导入项目(私有)-原项目是私有项目",
|
||||
"owner": "",
|
||||
"mirror_url": "https://gitee.com/florachy/notification-system.git",
|
||||
"mirror_user": "*****",
|
||||
"mirror_pwd": "*****",
|
||||
"mirror_private": True,
|
||||
"name": "Auto Test " + e_fk.name(),
|
||||
"desc": z_fk_text,
|
||||
"repository_name": "notification-system",
|
||||
"project_category": random.choice(["机器学习", "大数据", "深度学习", "人工智能", "量子计算", "智慧医疗", "自动驾驶", "其他"]),
|
||||
"project_language": random.choice(["C", "Ruby", "C#", "HTML", "C++"]),
|
||||
"ignore": random.choice(["Ada", "Actionscript", "Ansible", "Android", "Agda"]),
|
||||
"private": True,
|
||||
"expected": "",
|
||||
"mirror_type": True
|
||||
},
|
||||
{
|
||||
"title": "导入项目(公有)-原项目是私有项目",
|
||||
"owner": "",
|
||||
"mirror_url": "https://gitee.com/florachy/finger-change.git",
|
||||
"mirror_user": "*****",
|
||||
"mirror_pwd": "*****",
|
||||
"mirror_private": True,
|
||||
"name": "Auto Test " + e_fk.name(),
|
||||
"desc": z_fk_text,
|
||||
"repository_name": "finger-change",
|
||||
"project_category": random.choice(["机器学习", "大数据", "深度学习", "人工智能", "量子计算", "智慧医疗", "自动驾驶", "其他"]),
|
||||
"project_language": random.choice(["C", "Ruby", "C#", "HTML", "C++"]),
|
||||
"ignore": random.choice(["Ada", "Actionscript", "Ansible", "Android", "Agda"]),
|
||||
"private": False,
|
||||
"expected": "",
|
||||
"mirror_type": False
|
||||
}
|
||||
|
||||
]
|
|
@ -0,0 +1,6 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# @Time : 2021/8/14 12:37
|
||||
# @Author : Flora.Chen
|
||||
# @File : __init__.py.py
|
||||
# @Software: PyCharm
|
||||
# @Desc:
|
|
@ -0,0 +1,185 @@
|
|||
#!/usr/bin/env sh
|
||||
|
||||
#
|
||||
# Copyright 2015 the original author or authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
## allure start up script for UN*X
|
||||
##
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
# Resolve links: $0 may be a link
|
||||
PRG="$0"
|
||||
# Need this for relative symlinks.
|
||||
while [ -h "$PRG" ] ; do
|
||||
ls=`ls -ld "$PRG"`
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
PRG="$link"
|
||||
else
|
||||
PRG=`dirname "$PRG"`"/$link"
|
||||
fi
|
||||
done
|
||||
SAVED="`pwd`"
|
||||
cd "`dirname \"$PRG\"`/.." >/dev/null
|
||||
export APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >/dev/null
|
||||
|
||||
APP_NAME="allure"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and ALLURE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS=""
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
}
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
NONSTOP* )
|
||||
nonstop=true
|
||||
;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/lib/allure-commandline-2.14.0.jar:$APP_HOME/lib/jcommander-1.81.jar:$APP_HOME/lib/allure-generator-2.14.0.jar:$APP_HOME/lib/jackson-dataformat-xml-2.12.3.jar:$APP_HOME/lib/jackson-module-jaxb-annotations-2.12.3.jar:$APP_HOME/lib/jackson-annotations-2.12.3.jar:$APP_HOME/lib/jackson-core-2.12.3.jar:$APP_HOME/lib/jackson-dataformat-yaml-2.12.3.jar:$APP_HOME/lib/allure-plugin-api-2.14.0.jar:$APP_HOME/lib/allure-model-2.13.0.jar:$APP_HOME/lib/jackson-databind-2.12.3.jar:$APP_HOME/lib/commons-io-2.8.0.jar:$APP_HOME/lib/jetty-server-9.4.20.v20190813.jar:$APP_HOME/lib/slf4j-log4j12-1.7.28.jar:$APP_HOME/lib/snakeyaml-1.27.jar:$APP_HOME/lib/javax.servlet-api-3.1.0.jar:$APP_HOME/lib/jetty-http-9.4.20.v20190813.jar:$APP_HOME/lib/jetty-io-9.4.20.v20190813.jar:$APP_HOME/lib/allure1-model-1.0.jar:$APP_HOME/lib/slf4j-api-1.7.28.jar:$APP_HOME/lib/log4j-1.2.17.jar:$APP_HOME/lib/jaxb-api-2.3.1.jar:$APP_HOME/lib/httpclient-4.5.13.jar:$APP_HOME/lib/tika-core-1.26.jar:$APP_HOME/lib/freemarker-2.3.31.jar:$APP_HOME/lib/jetty-util-9.4.20.v20190813.jar:$APP_HOME/lib/opencsv-4.6.jar:$APP_HOME/lib/flexmark-0.62.2.jar:$APP_HOME/lib/woodstox-core-6.2.4.jar:$APP_HOME/lib/stax2-api-4.2.1.jar:$APP_HOME/lib/javax.activation-api-1.2.0.jar:$APP_HOME/lib/properties-2.0.RC5.jar:$APP_HOME/lib/jaxb-utils-1.0.jar:$APP_HOME/lib/httpcore-4.4.13.jar:$APP_HOME/lib/commons-beanutils-1.9.3.jar:$APP_HOME/lib/commons-logging-1.2.jar:$APP_HOME/lib/commons-codec-1.11.jar:$APP_HOME/lib/commons-text-1.3.jar:$APP_HOME/lib/commons-lang3-3.12.0.jar:$APP_HOME/lib/commons-collections4-4.2.jar:$APP_HOME/lib/flexmark-util-format-0.62.2.jar:$APP_HOME/lib/flexmark-util-ast-0.62.2.jar:$APP_HOME/lib/flexmark-util-builder-0.62.2.jar:$APP_HOME/lib/flexmark-util-dependency-0.62.2.jar:$APP_HOME/lib/flexmark-util-html-0.62.2.jar:$APP_HOME/lib/flexmark-util-sequence-0.62.2.jar:$APP_HOME/lib/flexmark-util-collection-0.62.2.jar:$APP_HOME/lib/flexmark-util-data-0.62.2.jar:$APP_HOME/lib/flexmark-util-misc-0.62.2.jar:$APP_HOME/lib/flexmark-util-visitor-0.62.2.jar:$APP_HOME/lib/jakarta.xml.bind-api-2.3.2.jar:$APP_HOME/lib/jakarta.activation-api-1.2.1.jar:$APP_HOME/lib/commons-collections-3.2.2.jar:$APP_HOME/lib/annotations-15.0.jar:$APP_HOME/lib/config
|
||||
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD="java"
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||
MAX_FD_LIMIT=`ulimit -H -n`
|
||||
if [ $? -eq 0 ] ; then
|
||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||
MAX_FD="$MAX_FD_LIMIT"
|
||||
fi
|
||||
ulimit -n $MAX_FD
|
||||
if [ $? -ne 0 ] ; then
|
||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||
fi
|
||||
else
|
||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||
fi
|
||||
fi
|
||||
|
||||
# For Darwin, add options to specify how the application appears in the dock
|
||||
if $darwin; then
|
||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||
fi
|
||||
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
|
||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||
|
||||
# We build the pattern for arguments to be converted via cygpath
|
||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||
SEP=""
|
||||
for dir in $ROOTDIRSRAW ; do
|
||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||
SEP="|"
|
||||
done
|
||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||
# Add a user-defined pattern to the cygpath arguments
|
||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||
fi
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
i=0
|
||||
for arg in "$@" ; do
|
||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||
|
||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||
else
|
||||
eval `echo args$i`="\"$arg\""
|
||||
fi
|
||||
i=`expr $i + 1`
|
||||
done
|
||||
case $i in
|
||||
0) set -- ;;
|
||||
1) set -- "$args0" ;;
|
||||
2) set -- "$args0" "$args1" ;;
|
||||
3) set -- "$args0" "$args1" "$args2" ;;
|
||||
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# Escape application args
|
||||
save () {
|
||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||
echo " "
|
||||
}
|
||||
APP_ARGS=`save "$@"`
|
||||
|
||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $ALLURE_OPTS -classpath "\"$CLASSPATH\"" io.qameta.allure.CommandLine "$APP_ARGS"
|
||||
|
||||
exec "$JAVACMD" "$@"
|
|
@ -0,0 +1,89 @@
|
|||
@rem
|
||||
@rem Copyright 2015 the original author or authors.
|
||||
@rem
|
||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@rem you may not use this file except in compliance with the License.
|
||||
@rem You may obtain a copy of the License at
|
||||
@rem
|
||||
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||
@rem
|
||||
@rem Unless required by applicable law or agreed to in writing, software
|
||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem allure startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%..
|
||||
|
||||
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and ALLURE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS=
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\lib\allure-commandline-2.14.0.jar;%APP_HOME%\lib\jcommander-1.81.jar;%APP_HOME%\lib\allure-generator-2.14.0.jar;%APP_HOME%\lib\jackson-dataformat-xml-2.12.3.jar;%APP_HOME%\lib\jackson-module-jaxb-annotations-2.12.3.jar;%APP_HOME%\lib\jackson-annotations-2.12.3.jar;%APP_HOME%\lib\jackson-core-2.12.3.jar;%APP_HOME%\lib\jackson-dataformat-yaml-2.12.3.jar;%APP_HOME%\lib\allure-plugin-api-2.14.0.jar;%APP_HOME%\lib\allure-model-2.13.0.jar;%APP_HOME%\lib\jackson-databind-2.12.3.jar;%APP_HOME%\lib\commons-io-2.8.0.jar;%APP_HOME%\lib\jetty-server-9.4.20.v20190813.jar;%APP_HOME%\lib\slf4j-log4j12-1.7.28.jar;%APP_HOME%\lib\snakeyaml-1.27.jar;%APP_HOME%\lib\javax.servlet-api-3.1.0.jar;%APP_HOME%\lib\jetty-http-9.4.20.v20190813.jar;%APP_HOME%\lib\jetty-io-9.4.20.v20190813.jar;%APP_HOME%\lib\allure1-model-1.0.jar;%APP_HOME%\lib\slf4j-api-1.7.28.jar;%APP_HOME%\lib\log4j-1.2.17.jar;%APP_HOME%\lib\jaxb-api-2.3.1.jar;%APP_HOME%\lib\httpclient-4.5.13.jar;%APP_HOME%\lib\tika-core-1.26.jar;%APP_HOME%\lib\freemarker-2.3.31.jar;%APP_HOME%\lib\jetty-util-9.4.20.v20190813.jar;%APP_HOME%\lib\opencsv-4.6.jar;%APP_HOME%\lib\flexmark-0.62.2.jar;%APP_HOME%\lib\woodstox-core-6.2.4.jar;%APP_HOME%\lib\stax2-api-4.2.1.jar;%APP_HOME%\lib\javax.activation-api-1.2.0.jar;%APP_HOME%\lib\properties-2.0.RC5.jar;%APP_HOME%\lib\jaxb-utils-1.0.jar;%APP_HOME%\lib\httpcore-4.4.13.jar;%APP_HOME%\lib\commons-beanutils-1.9.3.jar;%APP_HOME%\lib\commons-logging-1.2.jar;%APP_HOME%\lib\commons-codec-1.11.jar;%APP_HOME%\lib\commons-text-1.3.jar;%APP_HOME%\lib\commons-lang3-3.12.0.jar;%APP_HOME%\lib\commons-collections4-4.2.jar;%APP_HOME%\lib\flexmark-util-format-0.62.2.jar;%APP_HOME%\lib\flexmark-util-ast-0.62.2.jar;%APP_HOME%\lib\flexmark-util-builder-0.62.2.jar;%APP_HOME%\lib\flexmark-util-dependency-0.62.2.jar;%APP_HOME%\lib\flexmark-util-html-0.62.2.jar;%APP_HOME%\lib\flexmark-util-sequence-0.62.2.jar;%APP_HOME%\lib\flexmark-util-collection-0.62.2.jar;%APP_HOME%\lib\flexmark-util-data-0.62.2.jar;%APP_HOME%\lib\flexmark-util-misc-0.62.2.jar;%APP_HOME%\lib\flexmark-util-visitor-0.62.2.jar;%APP_HOME%\lib\jakarta.xml.bind-api-2.3.2.jar;%APP_HOME%\lib\jakarta.activation-api-1.2.1.jar;%APP_HOME%\lib\commons-collections-3.2.2.jar;%APP_HOME%\lib\annotations-15.0.jar;%APP_HOME%\lib\config
|
||||
|
||||
|
||||
@rem Execute allure
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %ALLURE_OPTS% -classpath "%CLASSPATH%" io.qameta.allure.CommandLine %*
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable ALLURE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%ALLURE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
|
@ -0,0 +1,3 @@
|
|||
plugins:
|
||||
- junit-plugin
|
||||
- packages-plugin
|
|
@ -0,0 +1,10 @@
|
|||
plugins:
|
||||
- junit-xml-plugin
|
||||
- xunit-xml-plugin
|
||||
- trx-plugin
|
||||
- behaviors-plugin
|
||||
- packages-plugin
|
||||
- screen-diff-plugin
|
||||
- xctest-plugin
|
||||
- jira-plugin
|
||||
- xray-plugin
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,2 @@
|
|||
org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.LoggerLog
|
||||
org.eclipse.jetty.LEVEL=WARN
|
|
@ -0,0 +1,8 @@
|
|||
log4j.rootLogger=INFO, stdout
|
||||
|
||||
log4j.appender.stdout = org.apache.log4j.ConsoleAppender
|
||||
log4j.appender.stdout.Target=System.out
|
||||
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
|
||||
log4j.appender.stdout.layout.ConversionPattern=%m%n
|
||||
|
||||
log4j.logger.org.mortbay.log = INFO
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1 @@
|
|||
The directory with Allure plugins. To add the plugin simply unpack it to this folder.
|
|
@ -0,0 +1,7 @@
|
|||
id: behaviors
|
||||
name: Behaviors aggregator
|
||||
description: The aggregator adds behaviors tab to the report
|
||||
extensions:
|
||||
- io.qameta.allure.behaviors.BehaviorsPlugin
|
||||
jsFiles:
|
||||
- index.js
|
Binary file not shown.
|
@ -0,0 +1,178 @@
|
|||
'use strict';
|
||||
|
||||
allure.api.addTranslation('en', {
|
||||
tab: {
|
||||
behaviors: {
|
||||
name: 'Behaviors'
|
||||
}
|
||||
},
|
||||
widget: {
|
||||
behaviors: {
|
||||
name: 'Features by stories',
|
||||
showAll: 'show all'
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
allure.api.addTranslation('ru', {
|
||||
tab: {
|
||||
behaviors: {
|
||||
name: 'Функциональность'
|
||||
}
|
||||
},
|
||||
widget: {
|
||||
behaviors: {
|
||||
name: 'Функциональность',
|
||||
showAll: 'показать все'
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
allure.api.addTranslation('zh', {
|
||||
tab: {
|
||||
behaviors: {
|
||||
name: '功能'
|
||||
}
|
||||
},
|
||||
widget: {
|
||||
behaviors: {
|
||||
name: '特性场景',
|
||||
showAll: '显示所有'
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
allure.api.addTranslation('de', {
|
||||
tab: {
|
||||
behaviors: {
|
||||
name: 'Verhalten'
|
||||
}
|
||||
},
|
||||
widget: {
|
||||
behaviors: {
|
||||
name: 'Features nach Stories',
|
||||
showAll: 'Zeige alle'
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
allure.api.addTranslation('nl', {
|
||||
tab: {
|
||||
behaviors: {
|
||||
name: 'Functionaliteit'
|
||||
}
|
||||
},
|
||||
widget: {
|
||||
behaviors: {
|
||||
name: 'Features en story’s',
|
||||
showAll: 'Toon alle'
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
allure.api.addTranslation('he', {
|
||||
tab: {
|
||||
behaviors: {
|
||||
name: 'התנהגויות'
|
||||
}
|
||||
},
|
||||
widget: {
|
||||
behaviors: {
|
||||
name: 'תכונות לפי סיפורי משתמש',
|
||||
showAll: 'הצג הכול'
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
allure.api.addTranslation('br', {
|
||||
tab: {
|
||||
behaviors: {
|
||||
name: 'Comportamentos'
|
||||
}
|
||||
},
|
||||
widget: {
|
||||
behaviors: {
|
||||
name: 'Funcionalidades por história',
|
||||
showAll: 'Mostrar tudo'
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
allure.api.addTranslation('ja', {
|
||||
tab: {
|
||||
behaviors: {
|
||||
name: '振る舞い'
|
||||
}
|
||||
},
|
||||
widget: {
|
||||
behaviors: {
|
||||
name: 'ストーリー別の機能',
|
||||
showAll: '全て表示'
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
allure.api.addTranslation('es', {
|
||||
tab: {
|
||||
behaviors: {
|
||||
name: 'Funcionalidades'
|
||||
}
|
||||
},
|
||||
widget: {
|
||||
behaviors: {
|
||||
name: 'Funcionalidades por Historias de Usuario',
|
||||
showAll: 'mostrar todo'
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
allure.api.addTranslation('kr', {
|
||||
tab: {
|
||||
behaviors: {
|
||||
name: '동작'
|
||||
}
|
||||
},
|
||||
widget: {
|
||||
behaviors: {
|
||||
name: '스토리별 기능',
|
||||
showAll: '전체 보기'
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
allure.api.addTranslation('fr', {
|
||||
tab: {
|
||||
behaviors: {
|
||||
name: 'Comportements'
|
||||
}
|
||||
},
|
||||
widget: {
|
||||
behaviors: {
|
||||
name: 'Thèmes par histoires',
|
||||
showAll: 'Montrer tout'
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
allure.api.addTab('behaviors', {
|
||||
title: 'tab.behaviors.name', icon: 'fa fa-list',
|
||||
route: 'behaviors(/)(:testGroup)(/)(:testResult)(/)(:testResultTab)(/)',
|
||||
onEnter: (function (testGroup, testResult, testResultTab) {
|
||||
return new allure.components.TreeLayout({
|
||||
testGroup: testGroup,
|
||||
testResult: testResult,
|
||||
testResultTab: testResultTab,
|
||||
tabName: 'tab.behaviors.name',
|
||||
baseUrl: 'behaviors',
|
||||
url: 'data/behaviors.json',
|
||||
csvUrl: 'data/behaviors.csv'
|
||||
});
|
||||
})
|
||||
});
|
||||
|
||||
allure.api.addWidget('widgets', 'behaviors', allure.components.WidgetStatusView.extend({
|
||||
rowTag: 'a',
|
||||
title: 'widget.behaviors.name',
|
||||
baseUrl: 'behaviors',
|
||||
showLinks: true
|
||||
}));
|
|
@ -0,0 +1,5 @@
|
|||
id: custom-logo
|
||||
name: Custom logo aggregator
|
||||
description: The aggregator replaces default Allure logo with a custom one
|
||||
cssFiles:
|
||||
- styles.css
|
Binary file not shown.
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" ?><!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN' 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd'><svg enable-background="new 0 0 128 128" version="1.1" viewBox="0 0 128 128" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><g id="Layer_1"><rect fill="#F4F5F5" height="1520" opacity="0" width="727.938" x="-59.984" y="-351"/></g><g id="Layer_2"><g><circle cx="64" cy="64" fill="#6E9583" r="64"/><g><defs><circle cx="64" cy="64" id="SVGID_3_" r="64"/></defs><clipPath id="SVGID_2_"><use overflow="visible" xlink:href="#SVGID_3_"/></clipPath><polygon clip-path="url(#SVGID_2_)" fill="#648778" points="93.572,29.677 128,64 128,128 54.36,128 33.341,106.906 "/></g><path d="M84.044,20H36.018C33.579,20,32,22.11,32,24.549v78.903c0,2.439,1.579,4.549,4.018,4.549h55.989 c2.439,0,4.018-2.11,4.018-4.549V32.143L84.044,20z" fill="#F1F1F1"/><g><defs><path d="M84.044,20H36.018C33.579,20,32,22.11,32,24.549v78.903c0,2.439,1.579,4.549,4.018,4.549h55.989 c2.439,0,4.018-2.11,4.018-4.549V32.143L84.044,20z" id="SVGID_5_"/></defs><clipPath id="SVGID_4_"><use overflow="visible" xlink:href="#SVGID_5_"/></clipPath><g clip-path="url(#SVGID_4_)"><polygon fill="#DDE1F1" points="50.948,67.621 65.539,82.042 42.971,83.087 49.777,90 42.971,91.087 49.277,97.555 42.971,99.087 53.027,109.305 97.684,109.305 97.684,75.707 97.075,54.055 81.059,37.758 70.97,44.918 62.684,35.107 "/></g></g><path d="M88.186,32.138l7.839,0.005L84.044,20v7.96C84.044,30.398,85.769,32.138,88.186,32.138z" fill="#C2DFC9"/><path d="M84,83.5H44c-0.828,0-1.5-0.672-1.5-1.5s0.672-1.5,1.5-1.5h40c0.828,0,1.5,0.672,1.5,1.5 S84.828,83.5,84,83.5z" fill="#495260"/><path d="M84,91.5H44c-0.828,0-1.5-0.672-1.5-1.5s0.672-1.5,1.5-1.5h40c0.828,0,1.5,0.672,1.5,1.5 S84.828,91.5,84,91.5z" fill="#495260"/><path d="M84,99.5H44c-0.828,0-1.5-0.672-1.5-1.5s0.672-1.5,1.5-1.5h40c0.828,0,1.5,0.672,1.5,1.5 S84.828,99.5,84,99.5z" fill="#495260"/><g><path d="M69.568,31.844l-1.319,11.303c2.314,0.88,4.242,2.728,5.132,5.245c0.573,1.619,0.631,3.292,0.274,4.851 l10.257,4.895c0.527,0.252,1.155-0.023,1.329-0.581c1.308-4.188,1.323-8.819-0.253-13.273 c-2.379-6.723-7.827-11.477-14.212-13.254C70.21,30.872,69.636,31.26,69.568,31.844z" fill="#0E9CD9"/><path d="M66.68,59.901c-3.653,0.668-7.398-1.12-9.176-4.38c-1.094-2.006-1.312-4.174-0.858-6.157L46.39,44.469 c-0.527-0.251-1.155,0.023-1.329,0.58c-1.286,4.118-1.322,8.663,0.175,13.049c3.701,10.842,15.624,16.783,26.503,13.191 c4.655-1.537,8.399-4.531,10.911-8.3c0.324-0.486,0.141-1.147-0.385-1.398l-10.257-4.896 C70.751,58.296,68.929,59.49,66.68,59.901z" fill="#E95037"/><path d="M62.239,43.074c0.734-0.26,1.479-0.405,2.22-0.464l1.316-11.275c0.067-0.576-0.389-1.08-0.968-1.071 c-2.218,0.035-4.469,0.421-6.676,1.202c-4.455,1.576-8.045,4.5-10.479,8.151c-0.324,0.486-0.142,1.147,0.385,1.399l10.257,4.895 C59.282,44.654,60.62,43.647,62.239,43.074z" fill="#69B32D"/><g><defs><path d="M69.695,30.76l-1.446,12.387c2.314,0.88,4.242,2.728,5.132,5.245c0.573,1.619,0.631,3.292,0.274,4.851 l10.257,4.895c0.527,0.252,1.155-0.023,1.329-0.581c1.308-4.188,1.323-8.819-0.253-13.273 C82.476,37.185,76.541,32.281,69.695,30.76z M66.68,59.901c-3.653,0.668-7.398-1.12-9.176-4.38 c-1.094-2.006-1.312-4.174-0.858-6.157L46.39,44.469c-0.527-0.251-1.155,0.023-1.329,0.58 c-1.286,4.118-1.322,8.663,0.175,13.049c3.701,10.842,15.624,16.783,26.503,13.191c4.655-1.537,8.399-4.531,10.911-8.3 c0.324-0.486,0.141-1.147-0.385-1.398l-10.257-4.896C70.751,58.296,68.929,59.49,66.68,59.901z M62.239,43.074 c0.734-0.26,1.479-0.405,2.22-0.464l1.316-11.275c0.067-0.576-0.389-1.08-0.968-1.071c-2.218,0.035-4.469,0.421-6.676,1.202 c-4.455,1.576-8.045,4.5-10.479,8.151c-0.324,0.486-0.142,1.147,0.385,1.399l10.257,4.895 C59.282,44.654,60.62,43.647,62.239,43.074z" id="SVGID_7_"/></defs><clipPath id="SVGID_6_"><use overflow="visible" xlink:href="#SVGID_7_"/></clipPath><circle clip-path="url(#SVGID_6_)" cx="65.151" cy="51.304" fill="#FFFFFF" opacity="0.4" r="12.507"/></g></g></g></g></svg>
|
After Width: | Height: | Size: 4.0 KiB |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue