Compare commits
3 Commits
Author | SHA1 | Date |
---|---|---|
234dgdfgdfgdfgdfgd | a41f80fda9 | |
234dgdfgdfgdfgdfgd | 4efcf38ed8 | |
test | 7e34a3f6f7 |
|
@ -0,0 +1,174 @@
|
|||
{
|
||||
"TechnicianId": 161555,
|
||||
"AffiliateId": 0,
|
||||
"ScheduleStartTime": "2019-07-25T08:00:00",
|
||||
"ScheduleEndTime": "2019-07-25T12:00:00",
|
||||
"WorkOrders": [{
|
||||
"Id": 2035478,
|
||||
"StartTime": "2019-07-24T10:00:00",
|
||||
"EndTime": "2019-07-24T11:22:39",
|
||||
"ScheduleStartTime": "2019-07-24T10:00:00",
|
||||
"ScheduleEndTime": "2019-07-24T14:00:00",
|
||||
"Technician": 161555,
|
||||
"WOClientId": 1200,
|
||||
"AffiliateId": 116531,
|
||||
"WoTypeId": 70,
|
||||
"ProjectId": 8637,
|
||||
"IsLockedTechnician": false,
|
||||
"Section": 2,
|
||||
"OnSiteTime": 3600,
|
||||
"PriorityId": 0,
|
||||
"ToTechnicianDrivingTime": 0,
|
||||
"InsertWoSort": 0,
|
||||
"ServiceCodeId": 8164,
|
||||
"ServiceTypeId": 1637,
|
||||
"ServiceCategoryId": 96,
|
||||
"IsScheduleAtRisk": false,
|
||||
"IsDetach": false,
|
||||
"LocationId": 808648,
|
||||
"UTC": 0,
|
||||
"DST": null,
|
||||
"ConvertZipCode": true,
|
||||
"Lat": 36.09564,
|
||||
"Lng": -80.7957,
|
||||
"ZipCode": "27020",
|
||||
"State": "NC",
|
||||
"Region": "Carolinas"
|
||||
},
|
||||
{
|
||||
"Id": 2035476,
|
||||
"StartTime": "2019-07-24T11:22:39",
|
||||
"EndTime": "2019-07-24T13:10:38",
|
||||
"ScheduleStartTime": "2019-07-24T10:00:00",
|
||||
"ScheduleEndTime": "2019-07-24T14:00:00",
|
||||
"Technician": 161555,
|
||||
"WOClientId": 1200,
|
||||
"AffiliateId": 116531,
|
||||
"WoTypeId": 70,
|
||||
"ProjectId": 8636,
|
||||
"IsLockedTechnician": false,
|
||||
"Section": 2,
|
||||
"OnSiteTime": 3600,
|
||||
"PriorityId": 0,
|
||||
"ToTechnicianDrivingTime": 0,
|
||||
"InsertWoSort": 0,
|
||||
"ServiceCodeId": 8164,
|
||||
"ServiceTypeId": 1637,
|
||||
"ServiceCategoryId": 96,
|
||||
"IsScheduleAtRisk": false,
|
||||
"IsDetach": false,
|
||||
"LocationId": 808646,
|
||||
"UTC": 0,
|
||||
"DST": null,
|
||||
"ConvertZipCode": true,
|
||||
"Lat": 35.575801,
|
||||
"Lng": -80.819083,
|
||||
"ZipCode": "28115",
|
||||
"State": "NC",
|
||||
"Region": "Carolinas"
|
||||
},
|
||||
{
|
||||
"Id": 2035670,
|
||||
"StartTime": "2019-07-24T11:22:39",
|
||||
"EndTime": "2019-07-24T13:10:38",
|
||||
"ScheduleStartTime": "2019-07-24T10:00:00",
|
||||
"ScheduleEndTime": "2019-07-24T14:00:00",
|
||||
"Technician": 161555,
|
||||
"WOClientId": 1200,
|
||||
"AffiliateId": 116531,
|
||||
"WoTypeId": 30,
|
||||
"ProjectId": 0,
|
||||
"IsLockedTechnician": false,
|
||||
"Section": 2,
|
||||
"OnSiteTime": 3600,
|
||||
"PriorityId": 0,
|
||||
"ToTechnicianDrivingTime": 0,
|
||||
"InsertWoSort": 0,
|
||||
"ServiceCodeId": 5316,
|
||||
"ServiceTypeId": 1248,
|
||||
"ServiceCategoryId": 1,
|
||||
"IsScheduleAtRisk": true,
|
||||
"IsDetach": false,
|
||||
"LocationId": 808646,
|
||||
"UTC": 0,
|
||||
"DST": null,
|
||||
"ConvertZipCode": true,
|
||||
"Lat": 35.575801,
|
||||
"Lng": -80.819083,
|
||||
"ZipCode": "28115",
|
||||
"State": "NC",
|
||||
"Region": "Carolinas"
|
||||
},
|
||||
{
|
||||
"Id": 2036049,
|
||||
"StartTime": "2019-07-25T08:00:00",
|
||||
"EndTime": "2019-07-25T09:58:38",
|
||||
"ScheduleStartTime": "2019-07-25T08:00:00",
|
||||
"ScheduleEndTime": "2019-07-25T12:00:00",
|
||||
"Technician": 161555,
|
||||
"WOClientId": 1200,
|
||||
"AffiliateId": 116531,
|
||||
"WoTypeId": 70,
|
||||
"ProjectId": 8652,
|
||||
"IsLockedTechnician": false,
|
||||
"Section": 1,
|
||||
"OnSiteTime": 3600,
|
||||
"PriorityId": 0,
|
||||
"ToTechnicianDrivingTime": 0,
|
||||
"InsertWoSort": 0,
|
||||
"ServiceCodeId": 8164,
|
||||
"ServiceTypeId": 1637,
|
||||
"ServiceCategoryId": 96,
|
||||
"IsScheduleAtRisk": false,
|
||||
"IsDetach": false,
|
||||
"LocationId": 808651,
|
||||
"UTC": 0,
|
||||
"DST": null,
|
||||
"ConvertZipCode": true,
|
||||
"Lat": 35.483897,
|
||||
"Lng": -80.599575,
|
||||
"ZipCode": "28083",
|
||||
"State": "NC",
|
||||
"Region": "Carolinas"
|
||||
},
|
||||
{
|
||||
"Id": 0,
|
||||
"StartTime": "2019-07-25T08:00:00",
|
||||
"EndTime": "2019-07-25T09:58:38",
|
||||
"ScheduleStartTime": "2019-07-25T08:00:00",
|
||||
"ScheduleEndTime": "2019-07-25T12:00:00",
|
||||
"Technician": 0,
|
||||
"WOClientId": 1200,
|
||||
"AffiliateId": 0,
|
||||
"WoTypeId": 0,
|
||||
"ProjectId": 8652,
|
||||
"IsLockedTechnician": false,
|
||||
"Section": 0,
|
||||
"OnSiteTime": 3600,
|
||||
"PriorityId": 9,
|
||||
"ToTechnicianDrivingTime": 0,
|
||||
"InsertWoSort": 1,
|
||||
"ServiceCodeId": 8164,
|
||||
"ServiceTypeId": 1637,
|
||||
"ServiceCategoryId": 8164,
|
||||
"IsScheduleAtRisk": false,
|
||||
"IsDetach": false,
|
||||
"LocationId": 808651,
|
||||
"UTC": 0,
|
||||
"DST": null,
|
||||
"ConvertZipCode": false,
|
||||
"Lat": 35.483897,
|
||||
"Lng": -80.599575,
|
||||
"ZipCode": "28083",
|
||||
"State": null,
|
||||
"Region": null
|
||||
}
|
||||
],
|
||||
"IsSchedule": false,
|
||||
"OnSiteTime": 0,
|
||||
"IncrementSecond": 0,
|
||||
"TotalDrivingTime": 9090,
|
||||
"AtRiskWoIds": [
|
||||
2035670
|
||||
]
|
||||
}
|
|
@ -0,0 +1,174 @@
|
|||
{
|
||||
"TechnicianId": 161555,
|
||||
"AffiliateId": 0,
|
||||
"ScheduleStartTime": "2019-07-25T08:00:00",
|
||||
"ScheduleEndTime": "2019-07-25T12:00:00",
|
||||
"WorkOrders": [{
|
||||
"Id": 2035478,
|
||||
"StartTime": "2019-07-24T10:00:00",
|
||||
"EndTime": "2019-07-24T11:22:39",
|
||||
"ScheduleStartTime": "2019-07-24T10:00:00",
|
||||
"ScheduleEndTime": "2019-07-24T14:00:00",
|
||||
"Technician": 161555,
|
||||
"WOClientId": 1200,
|
||||
"AffiliateId": 116531,
|
||||
"WoTypeId": 70,
|
||||
"ProjectId": 8637,
|
||||
"IsLockedTechnician": false,
|
||||
"Section": 2,
|
||||
"OnSiteTime": 3600,
|
||||
"PriorityId": 0,
|
||||
"ToTechnicianDrivingTime": 0,
|
||||
"InsertWoSort": 0,
|
||||
"ServiceCodeId": 8164,
|
||||
"ServiceTypeId": 1637,
|
||||
"ServiceCategoryId": 96,
|
||||
"IsScheduleAtRisk": false,
|
||||
"IsDetach": false,
|
||||
"LocationId": 808648,
|
||||
"UTC": 0,
|
||||
"DST": null,
|
||||
"ConvertZipCode": true,
|
||||
"Lat": 36.09564,
|
||||
"Lng": -80.7957,
|
||||
"ZipCode": "27020",
|
||||
"State": "NC",
|
||||
"Region": "Carolinas"
|
||||
},
|
||||
{
|
||||
"Id": 2035476,
|
||||
"StartTime": "2019-07-24T11:22:39",
|
||||
"EndTime": "2019-07-24T13:10:38",
|
||||
"ScheduleStartTime": "2019-07-24T10:00:00",
|
||||
"ScheduleEndTime": "2019-07-24T14:00:00",
|
||||
"Technician": 161555,
|
||||
"WOClientId": 1200,
|
||||
"AffiliateId": 116531,
|
||||
"WoTypeId": 70,
|
||||
"ProjectId": 8636,
|
||||
"IsLockedTechnician": false,
|
||||
"Section": 2,
|
||||
"OnSiteTime": 3600,
|
||||
"PriorityId": 0,
|
||||
"ToTechnicianDrivingTime": 0,
|
||||
"InsertWoSort": 0,
|
||||
"ServiceCodeId": 8164,
|
||||
"ServiceTypeId": 1637,
|
||||
"ServiceCategoryId": 96,
|
||||
"IsScheduleAtRisk": false,
|
||||
"IsDetach": false,
|
||||
"LocationId": 808646,
|
||||
"UTC": 0,
|
||||
"DST": null,
|
||||
"ConvertZipCode": true,
|
||||
"Lat": 35.575801,
|
||||
"Lng": -80.819083,
|
||||
"ZipCode": "28115",
|
||||
"State": "NC",
|
||||
"Region": "Carolinas"
|
||||
},
|
||||
{
|
||||
"Id": 2035670,
|
||||
"StartTime": "2019-07-24T11:22:39",
|
||||
"EndTime": "2019-07-24T13:10:38",
|
||||
"ScheduleStartTime": "2019-07-24T10:00:00",
|
||||
"ScheduleEndTime": "2019-07-24T14:00:00",
|
||||
"Technician": 161555,
|
||||
"WOClientId": 1200,
|
||||
"AffiliateId": 116531,
|
||||
"WoTypeId": 30,
|
||||
"ProjectId": 0,
|
||||
"IsLockedTechnician": false,
|
||||
"Section": 2,
|
||||
"OnSiteTime": 3600,
|
||||
"PriorityId": 0,
|
||||
"ToTechnicianDrivingTime": 0,
|
||||
"InsertWoSort": 0,
|
||||
"ServiceCodeId": 5316,
|
||||
"ServiceTypeId": 1248,
|
||||
"ServiceCategoryId": 1,
|
||||
"IsScheduleAtRisk": true,
|
||||
"IsDetach": false,
|
||||
"LocationId": 808646,
|
||||
"UTC": 0,
|
||||
"DST": null,
|
||||
"ConvertZipCode": true,
|
||||
"Lat": 35.575801,
|
||||
"Lng": -80.819083,
|
||||
"ZipCode": "28115",
|
||||
"State": "NC",
|
||||
"Region": "Carolinas"
|
||||
},
|
||||
{
|
||||
"Id": 2036049,
|
||||
"StartTime": "2019-07-25T08:00:00",
|
||||
"EndTime": "2019-07-25T09:58:38",
|
||||
"ScheduleStartTime": "2019-07-25T08:00:00",
|
||||
"ScheduleEndTime": "2019-07-25T12:00:00",
|
||||
"Technician": 161555,
|
||||
"WOClientId": 1200,
|
||||
"AffiliateId": 116531,
|
||||
"WoTypeId": 70,
|
||||
"ProjectId": 8652,
|
||||
"IsLockedTechnician": false,
|
||||
"Section": 1,
|
||||
"OnSiteTime": 3600,
|
||||
"PriorityId": 0,
|
||||
"ToTechnicianDrivingTime": 0,
|
||||
"InsertWoSort": 0,
|
||||
"ServiceCodeId": 8164,
|
||||
"ServiceTypeId": 1637,
|
||||
"ServiceCategoryId": 96,
|
||||
"IsScheduleAtRisk": false,
|
||||
"IsDetach": false,
|
||||
"LocationId": 808651,
|
||||
"UTC": 0,
|
||||
"DST": null,
|
||||
"ConvertZipCode": true,
|
||||
"Lat": 35.483897,
|
||||
"Lng": -80.599575,
|
||||
"ZipCode": "28083",
|
||||
"State": "NC",
|
||||
"Region": "Carolinas"
|
||||
},
|
||||
{
|
||||
"Id": 0,
|
||||
"StartTime": "2019-07-25T08:00:00",
|
||||
"EndTime": "2019-07-25T09:58:38",
|
||||
"ScheduleStartTime": "2019-07-25T08:00:00",
|
||||
"ScheduleEndTime": "2019-07-25T12:00:00",
|
||||
"Technician": 0,
|
||||
"WOClientId": 1200,
|
||||
"AffiliateId": 0,
|
||||
"WoTypeId": 0,
|
||||
"ProjectId": 8652,
|
||||
"IsLockedTechnician": false,
|
||||
"Section": 0,
|
||||
"OnSiteTime": 3600,
|
||||
"PriorityId": 9,
|
||||
"ToTechnicianDrivingTime": 0,
|
||||
"InsertWoSort": 1,
|
||||
"ServiceCodeId": 8164,
|
||||
"ServiceTypeId": 1637,
|
||||
"ServiceCategoryId": 8164,
|
||||
"IsScheduleAtRisk": false,
|
||||
"IsDetach": false,
|
||||
"LocationId": 808651,
|
||||
"UTC": 0,
|
||||
"DST": null,
|
||||
"ConvertZipCode": false,
|
||||
"Lat": 35.483897,
|
||||
"Lng": -80.599575,
|
||||
"ZipCode": "28083",
|
||||
"State": null,
|
||||
"Region": null
|
||||
}
|
||||
],
|
||||
"IsSchedule": false,
|
||||
"OnSiteTime": 0,
|
||||
"IncrementSecond": 0,
|
||||
"TotalDrivingTime": 9090,
|
||||
"AtRiskWoIds": [
|
||||
2035670
|
||||
]
|
||||
}
|
|
@ -0,0 +1,174 @@
|
|||
{
|
||||
"TechnicianId": 161555,
|
||||
"AffiliateId": 0,
|
||||
"ScheduleStartTime": "2019-07-25T08:00:00",
|
||||
"ScheduleEndTime": "2019-07-25T12:00:00",
|
||||
"WorkOrders": [{
|
||||
"Id": 2035478,
|
||||
"StartTime": "2019-07-24T10:00:00",
|
||||
"EndTime": "2019-07-24T11:22:39",
|
||||
"ScheduleStartTime": "2019-07-24T10:00:00",
|
||||
"ScheduleEndTime": "2019-07-24T14:00:00",
|
||||
"Technician": 161555,
|
||||
"WOClientId": 1200,
|
||||
"AffiliateId": 116531,
|
||||
"WoTypeId": 70,
|
||||
"ProjectId": 8637,
|
||||
"IsLockedTechnician": false,
|
||||
"Section": 2,
|
||||
"OnSiteTime": 3600,
|
||||
"PriorityId": 0,
|
||||
"ToTechnicianDrivingTime": 0,
|
||||
"InsertWoSort": 0,
|
||||
"ServiceCodeId": 8164,
|
||||
"ServiceTypeId": 1637,
|
||||
"ServiceCategoryId": 96,
|
||||
"IsScheduleAtRisk": false,
|
||||
"IsDetach": false,
|
||||
"LocationId": 808648,
|
||||
"UTC": 0,
|
||||
"DST": null,
|
||||
"ConvertZipCode": true,
|
||||
"Lat": 36.09564,
|
||||
"Lng": -80.7957,
|
||||
"ZipCode": "27020",
|
||||
"State": "NC",
|
||||
"Region": "Carolinas"
|
||||
},
|
||||
{
|
||||
"Id": 2035476,
|
||||
"StartTime": "2019-07-24T11:22:39",
|
||||
"EndTime": "2019-07-24T13:10:38",
|
||||
"ScheduleStartTime": "2019-07-24T10:00:00",
|
||||
"ScheduleEndTime": "2019-07-24T14:00:00",
|
||||
"Technician": 161555,
|
||||
"WOClientId": 1200,
|
||||
"AffiliateId": 116531,
|
||||
"WoTypeId": 70,
|
||||
"ProjectId": 8636,
|
||||
"IsLockedTechnician": false,
|
||||
"Section": 2,
|
||||
"OnSiteTime": 3600,
|
||||
"PriorityId": 0,
|
||||
"ToTechnicianDrivingTime": 0,
|
||||
"InsertWoSort": 0,
|
||||
"ServiceCodeId": 8164,
|
||||
"ServiceTypeId": 1637,
|
||||
"ServiceCategoryId": 96,
|
||||
"IsScheduleAtRisk": false,
|
||||
"IsDetach": false,
|
||||
"LocationId": 808646,
|
||||
"UTC": 0,
|
||||
"DST": null,
|
||||
"ConvertZipCode": true,
|
||||
"Lat": 35.575801,
|
||||
"Lng": -80.819083,
|
||||
"ZipCode": "28115",
|
||||
"State": "NC",
|
||||
"Region": "Carolinas"
|
||||
},
|
||||
{
|
||||
"Id": 2035670,
|
||||
"StartTime": "2019-07-24T11:22:39",
|
||||
"EndTime": "2019-07-24T13:10:38",
|
||||
"ScheduleStartTime": "2019-07-24T10:00:00",
|
||||
"ScheduleEndTime": "2019-07-24T14:00:00",
|
||||
"Technician": 161555,
|
||||
"WOClientId": 1200,
|
||||
"AffiliateId": 116531,
|
||||
"WoTypeId": 30,
|
||||
"ProjectId": 0,
|
||||
"IsLockedTechnician": false,
|
||||
"Section": 2,
|
||||
"OnSiteTime": 3600,
|
||||
"PriorityId": 0,
|
||||
"ToTechnicianDrivingTime": 0,
|
||||
"InsertWoSort": 0,
|
||||
"ServiceCodeId": 5316,
|
||||
"ServiceTypeId": 1248,
|
||||
"ServiceCategoryId": 1,
|
||||
"IsScheduleAtRisk": true,
|
||||
"IsDetach": false,
|
||||
"LocationId": 808646,
|
||||
"UTC": 0,
|
||||
"DST": null,
|
||||
"ConvertZipCode": true,
|
||||
"Lat": 35.575801,
|
||||
"Lng": -80.819083,
|
||||
"ZipCode": "28115",
|
||||
"State": "NC",
|
||||
"Region": "Carolinas"
|
||||
},
|
||||
{
|
||||
"Id": 2036049,
|
||||
"StartTime": "2019-07-25T08:00:00",
|
||||
"EndTime": "2019-07-25T09:58:38",
|
||||
"ScheduleStartTime": "2019-07-25T08:00:00",
|
||||
"ScheduleEndTime": "2019-07-25T12:00:00",
|
||||
"Technician": 161555,
|
||||
"WOClientId": 1200,
|
||||
"AffiliateId": 116531,
|
||||
"WoTypeId": 70,
|
||||
"ProjectId": 8652,
|
||||
"IsLockedTechnician": false,
|
||||
"Section": 1,
|
||||
"OnSiteTime": 3600,
|
||||
"PriorityId": 0,
|
||||
"ToTechnicianDrivingTime": 0,
|
||||
"InsertWoSort": 0,
|
||||
"ServiceCodeId": 8164,
|
||||
"ServiceTypeId": 1637,
|
||||
"ServiceCategoryId": 96,
|
||||
"IsScheduleAtRisk": false,
|
||||
"IsDetach": false,
|
||||
"LocationId": 808651,
|
||||
"UTC": 0,
|
||||
"DST": null,
|
||||
"ConvertZipCode": true,
|
||||
"Lat": 35.483897,
|
||||
"Lng": -80.599575,
|
||||
"ZipCode": "28083",
|
||||
"State": "NC",
|
||||
"Region": "Carolinas"
|
||||
},
|
||||
{
|
||||
"Id": 0,
|
||||
"StartTime": "2019-07-25T08:00:00",
|
||||
"EndTime": "2019-07-25T09:58:38",
|
||||
"ScheduleStartTime": "2019-07-25T08:00:00",
|
||||
"ScheduleEndTime": "2019-07-25T12:00:00",
|
||||
"Technician": 0,
|
||||
"WOClientId": 1200,
|
||||
"AffiliateId": 0,
|
||||
"WoTypeId": 0,
|
||||
"ProjectId": 8652,
|
||||
"IsLockedTechnician": false,
|
||||
"Section": 0,
|
||||
"OnSiteTime": 3600,
|
||||
"PriorityId": 9,
|
||||
"ToTechnicianDrivingTime": 0,
|
||||
"InsertWoSort": 1,
|
||||
"ServiceCodeId": 8164,
|
||||
"ServiceTypeId": 1637,
|
||||
"ServiceCategoryId": 8164,
|
||||
"IsScheduleAtRisk": false,
|
||||
"IsDetach": false,
|
||||
"LocationId": 808651,
|
||||
"UTC": 0,
|
||||
"DST": null,
|
||||
"ConvertZipCode": false,
|
||||
"Lat": 35.483897,
|
||||
"Lng": -80.599575,
|
||||
"ZipCode": "28083",
|
||||
"State": null,
|
||||
"Region": null
|
||||
}
|
||||
],
|
||||
"IsSchedule": false,
|
||||
"OnSiteTime": 0,
|
||||
"IncrementSecond": 0,
|
||||
"TotalDrivingTime": 9090,
|
||||
"AtRiskWoIds": [
|
||||
2035670
|
||||
]
|
||||
}
|
|
@ -0,0 +1,174 @@
|
|||
{
|
||||
"TechnicianId": 161555,
|
||||
"AffiliateId": 0,
|
||||
"ScheduleStartTime": "2019-07-25T08:00:00",
|
||||
"ScheduleEndTime": "2019-07-25T12:00:00",
|
||||
"WorkOrders": [{
|
||||
"Id": 2035478,
|
||||
"StartTime": "2019-07-24T10:00:00",
|
||||
"EndTime": "2019-07-24T11:22:39",
|
||||
"ScheduleStartTime": "2019-07-24T10:00:00",
|
||||
"ScheduleEndTime": "2019-07-24T14:00:00",
|
||||
"Technician": 161555,
|
||||
"WOClientId": 1200,
|
||||
"AffiliateId": 116531,
|
||||
"WoTypeId": 70,
|
||||
"ProjectId": 8637,
|
||||
"IsLockedTechnician": false,
|
||||
"Section": 2,
|
||||
"OnSiteTime": 3600,
|
||||
"PriorityId": 0,
|
||||
"ToTechnicianDrivingTime": 0,
|
||||
"InsertWoSort": 0,
|
||||
"ServiceCodeId": 8164,
|
||||
"ServiceTypeId": 1637,
|
||||
"ServiceCategoryId": 96,
|
||||
"IsScheduleAtRisk": false,
|
||||
"IsDetach": false,
|
||||
"LocationId": 808648,
|
||||
"UTC": 0,
|
||||
"DST": null,
|
||||
"ConvertZipCode": true,
|
||||
"Lat": 36.09564,
|
||||
"Lng": -80.7957,
|
||||
"ZipCode": "27020",
|
||||
"State": "NC",
|
||||
"Region": "Carolinas"
|
||||
},
|
||||
{
|
||||
"Id": 2035476,
|
||||
"StartTime": "2019-07-24T11:22:39",
|
||||
"EndTime": "2019-07-24T13:10:38",
|
||||
"ScheduleStartTime": "2019-07-24T10:00:00",
|
||||
"ScheduleEndTime": "2019-07-24T14:00:00",
|
||||
"Technician": 161555,
|
||||
"WOClientId": 1200,
|
||||
"AffiliateId": 116531,
|
||||
"WoTypeId": 70,
|
||||
"ProjectId": 8636,
|
||||
"IsLockedTechnician": false,
|
||||
"Section": 2,
|
||||
"OnSiteTime": 3600,
|
||||
"PriorityId": 0,
|
||||
"ToTechnicianDrivingTime": 0,
|
||||
"InsertWoSort": 0,
|
||||
"ServiceCodeId": 8164,
|
||||
"ServiceTypeId": 1637,
|
||||
"ServiceCategoryId": 96,
|
||||
"IsScheduleAtRisk": false,
|
||||
"IsDetach": false,
|
||||
"LocationId": 808646,
|
||||
"UTC": 0,
|
||||
"DST": null,
|
||||
"ConvertZipCode": true,
|
||||
"Lat": 35.575801,
|
||||
"Lng": -80.819083,
|
||||
"ZipCode": "28115",
|
||||
"State": "NC",
|
||||
"Region": "Carolinas"
|
||||
},
|
||||
{
|
||||
"Id": 2035670,
|
||||
"StartTime": "2019-07-24T11:22:39",
|
||||
"EndTime": "2019-07-24T13:10:38",
|
||||
"ScheduleStartTime": "2019-07-24T10:00:00",
|
||||
"ScheduleEndTime": "2019-07-24T14:00:00",
|
||||
"Technician": 161555,
|
||||
"WOClientId": 1200,
|
||||
"AffiliateId": 116531,
|
||||
"WoTypeId": 30,
|
||||
"ProjectId": 0,
|
||||
"IsLockedTechnician": false,
|
||||
"Section": 2,
|
||||
"OnSiteTime": 3600,
|
||||
"PriorityId": 0,
|
||||
"ToTechnicianDrivingTime": 0,
|
||||
"InsertWoSort": 0,
|
||||
"ServiceCodeId": 5316,
|
||||
"ServiceTypeId": 1248,
|
||||
"ServiceCategoryId": 1,
|
||||
"IsScheduleAtRisk": true,
|
||||
"IsDetach": false,
|
||||
"LocationId": 808646,
|
||||
"UTC": 0,
|
||||
"DST": null,
|
||||
"ConvertZipCode": true,
|
||||
"Lat": 35.575801,
|
||||
"Lng": -80.819083,
|
||||
"ZipCode": "28115",
|
||||
"State": "NC",
|
||||
"Region": "Carolinas"
|
||||
},
|
||||
{
|
||||
"Id": 2036049,
|
||||
"StartTime": "2019-07-25T08:00:00",
|
||||
"EndTime": "2019-07-25T09:58:38",
|
||||
"ScheduleStartTime": "2019-07-25T08:00:00",
|
||||
"ScheduleEndTime": "2019-07-25T12:00:00",
|
||||
"Technician": 161555,
|
||||
"WOClientId": 1200,
|
||||
"AffiliateId": 116531,
|
||||
"WoTypeId": 70,
|
||||
"ProjectId": 8652,
|
||||
"IsLockedTechnician": false,
|
||||
"Section": 1,
|
||||
"OnSiteTime": 3600,
|
||||
"PriorityId": 0,
|
||||
"ToTechnicianDrivingTime": 0,
|
||||
"InsertWoSort": 0,
|
||||
"ServiceCodeId": 8164,
|
||||
"ServiceTypeId": 1637,
|
||||
"ServiceCategoryId": 96,
|
||||
"IsScheduleAtRisk": false,
|
||||
"IsDetach": false,
|
||||
"LocationId": 808651,
|
||||
"UTC": 0,
|
||||
"DST": null,
|
||||
"ConvertZipCode": true,
|
||||
"Lat": 35.483897,
|
||||
"Lng": -80.599575,
|
||||
"ZipCode": "28083",
|
||||
"State": "NC",
|
||||
"Region": "Carolinas"
|
||||
},
|
||||
{
|
||||
"Id": 0,
|
||||
"StartTime": "2019-07-25T08:00:00",
|
||||
"EndTime": "2019-07-25T09:58:38",
|
||||
"ScheduleStartTime": "2019-07-25T08:00:00",
|
||||
"ScheduleEndTime": "2019-07-25T12:00:00",
|
||||
"Technician": 0,
|
||||
"WOClientId": 1200,
|
||||
"AffiliateId": 0,
|
||||
"WoTypeId": 0,
|
||||
"ProjectId": 8652,
|
||||
"IsLockedTechnician": false,
|
||||
"Section": 0,
|
||||
"OnSiteTime": 3600,
|
||||
"PriorityId": 9,
|
||||
"ToTechnicianDrivingTime": 0,
|
||||
"InsertWoSort": 1,
|
||||
"ServiceCodeId": 8164,
|
||||
"ServiceTypeId": 1637,
|
||||
"ServiceCategoryId": 8164,
|
||||
"IsScheduleAtRisk": false,
|
||||
"IsDetach": false,
|
||||
"LocationId": 808651,
|
||||
"UTC": 0,
|
||||
"DST": null,
|
||||
"ConvertZipCode": false,
|
||||
"Lat": 35.483897,
|
||||
"Lng": -80.599575,
|
||||
"ZipCode": "28083",
|
||||
"State": null,
|
||||
"Region": null
|
||||
}
|
||||
],
|
||||
"IsSchedule": false,
|
||||
"OnSiteTime": 0,
|
||||
"IncrementSecond": 0,
|
||||
"TotalDrivingTime": 9090,
|
||||
"AtRiskWoIds": [
|
||||
2035670
|
||||
]
|
||||
}
|
|
@ -0,0 +1,174 @@
|
|||
{
|
||||
"TechnicianId": 161555,
|
||||
"AffiliateId": 0,
|
||||
"ScheduleStartTime": "2019-07-25T08:00:00",
|
||||
"ScheduleEndTime": "2019-07-25T12:00:00",
|
||||
"WorkOrders": [{
|
||||
"Id": 2035478,
|
||||
"StartTime": "2019-07-24T10:00:00",
|
||||
"EndTime": "2019-07-24T11:22:39",
|
||||
"ScheduleStartTime": "2019-07-24T10:00:00",
|
||||
"ScheduleEndTime": "2019-07-24T14:00:00",
|
||||
"Technician": 161555,
|
||||
"WOClientId": 1200,
|
||||
"AffiliateId": 116531,
|
||||
"WoTypeId": 70,
|
||||
"ProjectId": 8637,
|
||||
"IsLockedTechnician": false,
|
||||
"Section": 2,
|
||||
"OnSiteTime": 3600,
|
||||
"PriorityId": 0,
|
||||
"ToTechnicianDrivingTime": 0,
|
||||
"InsertWoSort": 0,
|
||||
"ServiceCodeId": 8164,
|
||||
"ServiceTypeId": 1637,
|
||||
"ServiceCategoryId": 96,
|
||||
"IsScheduleAtRisk": false,
|
||||
"IsDetach": false,
|
||||
"LocationId": 808648,
|
||||
"UTC": 0,
|
||||
"DST": null,
|
||||
"ConvertZipCode": true,
|
||||
"Lat": 36.09564,
|
||||
"Lng": -80.7957,
|
||||
"ZipCode": "27020",
|
||||
"State": "NC",
|
||||
"Region": "Carolinas"
|
||||
},
|
||||
{
|
||||
"Id": 2035476,
|
||||
"StartTime": "2019-07-24T11:22:39",
|
||||
"EndTime": "2019-07-24T13:10:38",
|
||||
"ScheduleStartTime": "2019-07-24T10:00:00",
|
||||
"ScheduleEndTime": "2019-07-24T14:00:00",
|
||||
"Technician": 161555,
|
||||
"WOClientId": 1200,
|
||||
"AffiliateId": 116531,
|
||||
"WoTypeId": 70,
|
||||
"ProjectId": 8636,
|
||||
"IsLockedTechnician": false,
|
||||
"Section": 2,
|
||||
"OnSiteTime": 3600,
|
||||
"PriorityId": 0,
|
||||
"ToTechnicianDrivingTime": 0,
|
||||
"InsertWoSort": 0,
|
||||
"ServiceCodeId": 8164,
|
||||
"ServiceTypeId": 1637,
|
||||
"ServiceCategoryId": 96,
|
||||
"IsScheduleAtRisk": false,
|
||||
"IsDetach": false,
|
||||
"LocationId": 808646,
|
||||
"UTC": 0,
|
||||
"DST": null,
|
||||
"ConvertZipCode": true,
|
||||
"Lat": 35.575801,
|
||||
"Lng": -80.819083,
|
||||
"ZipCode": "28115",
|
||||
"State": "NC",
|
||||
"Region": "Carolinas"
|
||||
},
|
||||
{
|
||||
"Id": 2035670,
|
||||
"StartTime": "2019-07-24T11:22:39",
|
||||
"EndTime": "2019-07-24T13:10:38",
|
||||
"ScheduleStartTime": "2019-07-24T10:00:00",
|
||||
"ScheduleEndTime": "2019-07-24T14:00:00",
|
||||
"Technician": 161555,
|
||||
"WOClientId": 1200,
|
||||
"AffiliateId": 116531,
|
||||
"WoTypeId": 30,
|
||||
"ProjectId": 0,
|
||||
"IsLockedTechnician": false,
|
||||
"Section": 2,
|
||||
"OnSiteTime": 3600,
|
||||
"PriorityId": 0,
|
||||
"ToTechnicianDrivingTime": 0,
|
||||
"InsertWoSort": 0,
|
||||
"ServiceCodeId": 5316,
|
||||
"ServiceTypeId": 1248,
|
||||
"ServiceCategoryId": 1,
|
||||
"IsScheduleAtRisk": true,
|
||||
"IsDetach": false,
|
||||
"LocationId": 808646,
|
||||
"UTC": 0,
|
||||
"DST": null,
|
||||
"ConvertZipCode": true,
|
||||
"Lat": 35.575801,
|
||||
"Lng": -80.819083,
|
||||
"ZipCode": "28115",
|
||||
"State": "NC",
|
||||
"Region": "Carolinas"
|
||||
},
|
||||
{
|
||||
"Id": 2036049,
|
||||
"StartTime": "2019-07-25T08:00:00",
|
||||
"EndTime": "2019-07-25T09:58:38",
|
||||
"ScheduleStartTime": "2019-07-25T08:00:00",
|
||||
"ScheduleEndTime": "2019-07-25T12:00:00",
|
||||
"Technician": 161555,
|
||||
"WOClientId": 1200,
|
||||
"AffiliateId": 116531,
|
||||
"WoTypeId": 70,
|
||||
"ProjectId": 8652,
|
||||
"IsLockedTechnician": false,
|
||||
"Section": 1,
|
||||
"OnSiteTime": 3600,
|
||||
"PriorityId": 0,
|
||||
"ToTechnicianDrivingTime": 0,
|
||||
"InsertWoSort": 0,
|
||||
"ServiceCodeId": 8164,
|
||||
"ServiceTypeId": 1637,
|
||||
"ServiceCategoryId": 96,
|
||||
"IsScheduleAtRisk": false,
|
||||
"IsDetach": false,
|
||||
"LocationId": 808651,
|
||||
"UTC": 0,
|
||||
"DST": null,
|
||||
"ConvertZipCode": true,
|
||||
"Lat": 35.483897,
|
||||
"Lng": -80.599575,
|
||||
"ZipCode": "28083",
|
||||
"State": "NC",
|
||||
"Region": "Carolinas"
|
||||
},
|
||||
{
|
||||
"Id": 0,
|
||||
"StartTime": "2019-07-25T08:00:00",
|
||||
"EndTime": "2019-07-25T09:58:38",
|
||||
"ScheduleStartTime": "2019-07-25T08:00:00",
|
||||
"ScheduleEndTime": "2019-07-25T12:00:00",
|
||||
"Technician": 0,
|
||||
"WOClientId": 1200,
|
||||
"AffiliateId": 0,
|
||||
"WoTypeId": 0,
|
||||
"ProjectId": 8652,
|
||||
"IsLockedTechnician": false,
|
||||
"Section": 0,
|
||||
"OnSiteTime": 3600,
|
||||
"PriorityId": 9,
|
||||
"ToTechnicianDrivingTime": 0,
|
||||
"InsertWoSort": 1,
|
||||
"ServiceCodeId": 8164,
|
||||
"ServiceTypeId": 1637,
|
||||
"ServiceCategoryId": 8164,
|
||||
"IsScheduleAtRisk": false,
|
||||
"IsDetach": false,
|
||||
"LocationId": 808651,
|
||||
"UTC": 0,
|
||||
"DST": null,
|
||||
"ConvertZipCode": false,
|
||||
"Lat": 35.483897,
|
||||
"Lng": -80.599575,
|
||||
"ZipCode": "28083",
|
||||
"State": null,
|
||||
"Region": null
|
||||
}
|
||||
],
|
||||
"IsSchedule": false,
|
||||
"OnSiteTime": 0,
|
||||
"IncrementSecond": 0,
|
||||
"TotalDrivingTime": 9090,
|
||||
"AtRiskWoIds": [
|
||||
2035670
|
||||
]
|
||||
}
|
|
@ -0,0 +1,174 @@
|
|||
{
|
||||
"TechnicianId": 161555,
|
||||
"AffiliateId": 0,
|
||||
"ScheduleStartTime": "2019-07-25T08:00:00",
|
||||
"ScheduleEndTime": "2019-07-25T12:00:00",
|
||||
"WorkOrders": [{
|
||||
"Id": 2035478,
|
||||
"StartTime": "2019-07-24T10:00:00",
|
||||
"EndTime": "2019-07-24T11:22:39",
|
||||
"ScheduleStartTime": "2019-07-24T10:00:00",
|
||||
"ScheduleEndTime": "2019-07-24T14:00:00",
|
||||
"Technician": 161555,
|
||||
"WOClientId": 1200,
|
||||
"AffiliateId": 116531,
|
||||
"WoTypeId": 70,
|
||||
"ProjectId": 8637,
|
||||
"IsLockedTechnician": false,
|
||||
"Section": 2,
|
||||
"OnSiteTime": 3600,
|
||||
"PriorityId": 0,
|
||||
"ToTechnicianDrivingTime": 0,
|
||||
"InsertWoSort": 0,
|
||||
"ServiceCodeId": 8164,
|
||||
"ServiceTypeId": 1637,
|
||||
"ServiceCategoryId": 96,
|
||||
"IsScheduleAtRisk": false,
|
||||
"IsDetach": false,
|
||||
"LocationId": 808648,
|
||||
"UTC": 0,
|
||||
"DST": null,
|
||||
"ConvertZipCode": true,
|
||||
"Lat": 36.09564,
|
||||
"Lng": -80.7957,
|
||||
"ZipCode": "27020",
|
||||
"State": "NC",
|
||||
"Region": "Carolinas"
|
||||
},
|
||||
{
|
||||
"Id": 2035476,
|
||||
"StartTime": "2019-07-24T11:22:39",
|
||||
"EndTime": "2019-07-24T13:10:38",
|
||||
"ScheduleStartTime": "2019-07-24T10:00:00",
|
||||
"ScheduleEndTime": "2019-07-24T14:00:00",
|
||||
"Technician": 161555,
|
||||
"WOClientId": 1200,
|
||||
"AffiliateId": 116531,
|
||||
"WoTypeId": 70,
|
||||
"ProjectId": 8636,
|
||||
"IsLockedTechnician": false,
|
||||
"Section": 2,
|
||||
"OnSiteTime": 3600,
|
||||
"PriorityId": 0,
|
||||
"ToTechnicianDrivingTime": 0,
|
||||
"InsertWoSort": 0,
|
||||
"ServiceCodeId": 8164,
|
||||
"ServiceTypeId": 1637,
|
||||
"ServiceCategoryId": 96,
|
||||
"IsScheduleAtRisk": false,
|
||||
"IsDetach": false,
|
||||
"LocationId": 808646,
|
||||
"UTC": 0,
|
||||
"DST": null,
|
||||
"ConvertZipCode": true,
|
||||
"Lat": 35.575801,
|
||||
"Lng": -80.819083,
|
||||
"ZipCode": "28115",
|
||||
"State": "NC",
|
||||
"Region": "Carolinas"
|
||||
},
|
||||
{
|
||||
"Id": 2035670,
|
||||
"StartTime": "2019-07-24T11:22:39",
|
||||
"EndTime": "2019-07-24T13:10:38",
|
||||
"ScheduleStartTime": "2019-07-24T10:00:00",
|
||||
"ScheduleEndTime": "2019-07-24T14:00:00",
|
||||
"Technician": 161555,
|
||||
"WOClientId": 1200,
|
||||
"AffiliateId": 116531,
|
||||
"WoTypeId": 30,
|
||||
"ProjectId": 0,
|
||||
"IsLockedTechnician": false,
|
||||
"Section": 2,
|
||||
"OnSiteTime": 3600,
|
||||
"PriorityId": 0,
|
||||
"ToTechnicianDrivingTime": 0,
|
||||
"InsertWoSort": 0,
|
||||
"ServiceCodeId": 5316,
|
||||
"ServiceTypeId": 1248,
|
||||
"ServiceCategoryId": 1,
|
||||
"IsScheduleAtRisk": true,
|
||||
"IsDetach": false,
|
||||
"LocationId": 808646,
|
||||
"UTC": 0,
|
||||
"DST": null,
|
||||
"ConvertZipCode": true,
|
||||
"Lat": 35.575801,
|
||||
"Lng": -80.819083,
|
||||
"ZipCode": "28115",
|
||||
"State": "NC",
|
||||
"Region": "Carolinas"
|
||||
},
|
||||
{
|
||||
"Id": 2036049,
|
||||
"StartTime": "2019-07-25T08:00:00",
|
||||
"EndTime": "2019-07-25T09:58:38",
|
||||
"ScheduleStartTime": "2019-07-25T08:00:00",
|
||||
"ScheduleEndTime": "2019-07-25T12:00:00",
|
||||
"Technician": 161555,
|
||||
"WOClientId": 1200,
|
||||
"AffiliateId": 116531,
|
||||
"WoTypeId": 70,
|
||||
"ProjectId": 8652,
|
||||
"IsLockedTechnician": false,
|
||||
"Section": 1,
|
||||
"OnSiteTime": 3600,
|
||||
"PriorityId": 0,
|
||||
"ToTechnicianDrivingTime": 0,
|
||||
"InsertWoSort": 0,
|
||||
"ServiceCodeId": 8164,
|
||||
"ServiceTypeId": 1637,
|
||||
"ServiceCategoryId": 96,
|
||||
"IsScheduleAtRisk": false,
|
||||
"IsDetach": false,
|
||||
"LocationId": 808651,
|
||||
"UTC": 0,
|
||||
"DST": null,
|
||||
"ConvertZipCode": true,
|
||||
"Lat": 35.483897,
|
||||
"Lng": -80.599575,
|
||||
"ZipCode": "28083",
|
||||
"State": "NC",
|
||||
"Region": "Carolinas"
|
||||
},
|
||||
{
|
||||
"Id": 0,
|
||||
"StartTime": "2019-07-25T08:00:00",
|
||||
"EndTime": "2019-07-25T09:58:38",
|
||||
"ScheduleStartTime": "2019-07-25T08:00:00",
|
||||
"ScheduleEndTime": "2019-07-25T12:00:00",
|
||||
"Technician": 0,
|
||||
"WOClientId": 1200,
|
||||
"AffiliateId": 0,
|
||||
"WoTypeId": 0,
|
||||
"ProjectId": 8652,
|
||||
"IsLockedTechnician": false,
|
||||
"Section": 0,
|
||||
"OnSiteTime": 3600,
|
||||
"PriorityId": 9,
|
||||
"ToTechnicianDrivingTime": 0,
|
||||
"InsertWoSort": 1,
|
||||
"ServiceCodeId": 8164,
|
||||
"ServiceTypeId": 1637,
|
||||
"ServiceCategoryId": 8164,
|
||||
"IsScheduleAtRisk": false,
|
||||
"IsDetach": false,
|
||||
"LocationId": 808651,
|
||||
"UTC": 0,
|
||||
"DST": null,
|
||||
"ConvertZipCode": false,
|
||||
"Lat": 35.483897,
|
||||
"Lng": -80.599575,
|
||||
"ZipCode": "28083",
|
||||
"State": null,
|
||||
"Region": null
|
||||
}
|
||||
],
|
||||
"IsSchedule": false,
|
||||
"OnSiteTime": 0,
|
||||
"IncrementSecond": 0,
|
||||
"TotalDrivingTime": 9090,
|
||||
"AtRiskWoIds": [
|
||||
2035670
|
||||
]
|
||||
}
|
|
@ -0,0 +1,174 @@
|
|||
{
|
||||
"TechnicianId": 161555,
|
||||
"AffiliateId": 0,
|
||||
"ScheduleStartTime": "2019-07-25T08:00:00",
|
||||
"ScheduleEndTime": "2019-07-25T12:00:00",
|
||||
"WorkOrders": [{
|
||||
"Id": 2035478,
|
||||
"StartTime": "2019-07-24T10:00:00",
|
||||
"EndTime": "2019-07-24T11:22:39",
|
||||
"ScheduleStartTime": "2019-07-24T10:00:00",
|
||||
"ScheduleEndTime": "2019-07-24T14:00:00",
|
||||
"Technician": 161555,
|
||||
"WOClientId": 1200,
|
||||
"AffiliateId": 116531,
|
||||
"WoTypeId": 70,
|
||||
"ProjectId": 8637,
|
||||
"IsLockedTechnician": false,
|
||||
"Section": 2,
|
||||
"OnSiteTime": 3600,
|
||||
"PriorityId": 0,
|
||||
"ToTechnicianDrivingTime": 0,
|
||||
"InsertWoSort": 0,
|
||||
"ServiceCodeId": 8164,
|
||||
"ServiceTypeId": 1637,
|
||||
"ServiceCategoryId": 96,
|
||||
"IsScheduleAtRisk": false,
|
||||
"IsDetach": false,
|
||||
"LocationId": 808648,
|
||||
"UTC": 0,
|
||||
"DST": null,
|
||||
"ConvertZipCode": true,
|
||||
"Lat": 36.09564,
|
||||
"Lng": -80.7957,
|
||||
"ZipCode": "27020",
|
||||
"State": "NC",
|
||||
"Region": "Carolinas"
|
||||
},
|
||||
{
|
||||
"Id": 2035476,
|
||||
"StartTime": "2019-07-24T11:22:39",
|
||||
"EndTime": "2019-07-24T13:10:38",
|
||||
"ScheduleStartTime": "2019-07-24T10:00:00",
|
||||
"ScheduleEndTime": "2019-07-24T14:00:00",
|
||||
"Technician": 161555,
|
||||
"WOClientId": 1200,
|
||||
"AffiliateId": 116531,
|
||||
"WoTypeId": 70,
|
||||
"ProjectId": 8636,
|
||||
"IsLockedTechnician": false,
|
||||
"Section": 2,
|
||||
"OnSiteTime": 3600,
|
||||
"PriorityId": 0,
|
||||
"ToTechnicianDrivingTime": 0,
|
||||
"InsertWoSort": 0,
|
||||
"ServiceCodeId": 8164,
|
||||
"ServiceTypeId": 1637,
|
||||
"ServiceCategoryId": 96,
|
||||
"IsScheduleAtRisk": false,
|
||||
"IsDetach": false,
|
||||
"LocationId": 808646,
|
||||
"UTC": 0,
|
||||
"DST": null,
|
||||
"ConvertZipCode": true,
|
||||
"Lat": 35.575801,
|
||||
"Lng": -80.819083,
|
||||
"ZipCode": "28115",
|
||||
"State": "NC",
|
||||
"Region": "Carolinas"
|
||||
},
|
||||
{
|
||||
"Id": 2035670,
|
||||
"StartTime": "2019-07-24T11:22:39",
|
||||
"EndTime": "2019-07-24T13:10:38",
|
||||
"ScheduleStartTime": "2019-07-24T10:00:00",
|
||||
"ScheduleEndTime": "2019-07-24T14:00:00",
|
||||
"Technician": 161555,
|
||||
"WOClientId": 1200,
|
||||
"AffiliateId": 116531,
|
||||
"WoTypeId": 30,
|
||||
"ProjectId": 0,
|
||||
"IsLockedTechnician": false,
|
||||
"Section": 2,
|
||||
"OnSiteTime": 3600,
|
||||
"PriorityId": 0,
|
||||
"ToTechnicianDrivingTime": 0,
|
||||
"InsertWoSort": 0,
|
||||
"ServiceCodeId": 5316,
|
||||
"ServiceTypeId": 1248,
|
||||
"ServiceCategoryId": 1,
|
||||
"IsScheduleAtRisk": true,
|
||||
"IsDetach": false,
|
||||
"LocationId": 808646,
|
||||
"UTC": 0,
|
||||
"DST": null,
|
||||
"ConvertZipCode": true,
|
||||
"Lat": 35.575801,
|
||||
"Lng": -80.819083,
|
||||
"ZipCode": "28115",
|
||||
"State": "NC",
|
||||
"Region": "Carolinas"
|
||||
},
|
||||
{
|
||||
"Id": 2036049,
|
||||
"StartTime": "2019-07-25T08:00:00",
|
||||
"EndTime": "2019-07-25T09:58:38",
|
||||
"ScheduleStartTime": "2019-07-25T08:00:00",
|
||||
"ScheduleEndTime": "2019-07-25T12:00:00",
|
||||
"Technician": 161555,
|
||||
"WOClientId": 1200,
|
||||
"AffiliateId": 116531,
|
||||
"WoTypeId": 70,
|
||||
"ProjectId": 8652,
|
||||
"IsLockedTechnician": false,
|
||||
"Section": 1,
|
||||
"OnSiteTime": 3600,
|
||||
"PriorityId": 0,
|
||||
"ToTechnicianDrivingTime": 0,
|
||||
"InsertWoSort": 0,
|
||||
"ServiceCodeId": 8164,
|
||||
"ServiceTypeId": 1637,
|
||||
"ServiceCategoryId": 96,
|
||||
"IsScheduleAtRisk": false,
|
||||
"IsDetach": false,
|
||||
"LocationId": 808651,
|
||||
"UTC": 0,
|
||||
"DST": null,
|
||||
"ConvertZipCode": true,
|
||||
"Lat": 35.483897,
|
||||
"Lng": -80.599575,
|
||||
"ZipCode": "28083",
|
||||
"State": "NC",
|
||||
"Region": "Carolinas"
|
||||
},
|
||||
{
|
||||
"Id": 0,
|
||||
"StartTime": "2019-07-25T08:00:00",
|
||||
"EndTime": "2019-07-25T09:58:38",
|
||||
"ScheduleStartTime": "2019-07-25T08:00:00",
|
||||
"ScheduleEndTime": "2019-07-25T12:00:00",
|
||||
"Technician": 0,
|
||||
"WOClientId": 1200,
|
||||
"AffiliateId": 0,
|
||||
"WoTypeId": 0,
|
||||
"ProjectId": 8652,
|
||||
"IsLockedTechnician": false,
|
||||
"Section": 0,
|
||||
"OnSiteTime": 3600,
|
||||
"PriorityId": 9,
|
||||
"ToTechnicianDrivingTime": 0,
|
||||
"InsertWoSort": 1,
|
||||
"ServiceCodeId": 8164,
|
||||
"ServiceTypeId": 1637,
|
||||
"ServiceCategoryId": 8164,
|
||||
"IsScheduleAtRisk": false,
|
||||
"IsDetach": false,
|
||||
"LocationId": 808651,
|
||||
"UTC": 0,
|
||||
"DST": null,
|
||||
"ConvertZipCode": false,
|
||||
"Lat": 35.483897,
|
||||
"Lng": -80.599575,
|
||||
"ZipCode": "28083",
|
||||
"State": null,
|
||||
"Region": null
|
||||
}
|
||||
],
|
||||
"IsSchedule": false,
|
||||
"OnSiteTime": 0,
|
||||
"IncrementSecond": 0,
|
||||
"TotalDrivingTime": 9090,
|
||||
"AtRiskWoIds": [
|
||||
2035670
|
||||
]
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="PYTHON_MODULE" version="4">
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$" />
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
<component name="PackageRequirementsSettings">
|
||||
<option name="requirementsPath" value="" />
|
||||
</component>
|
||||
<component name="TestRunnerService">
|
||||
<option name="PROJECT_TEST_RUNNER" value="pytest" />
|
||||
</component>
|
||||
</module>
|
|
@ -0,0 +1,6 @@
|
|||
<component name="InspectionProjectProfileManager">
|
||||
<settings>
|
||||
<option name="USE_PROJECT_PROFILE" value="false" />
|
||||
<version value="1.0" />
|
||||
</settings>
|
||||
</component>
|
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.6" project-jdk-type="Python SDK" />
|
||||
</project>
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/WebUiPy36.iml" filepath="$PROJECT_DIR$/.idea/WebUiPy36.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
|
@ -0,0 +1,169 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ChangeListManager">
|
||||
<list default="true" id="063831f7-0198-40fb-ba64-766a49951e4c" name="Default Changelist" comment="" />
|
||||
<option name="SHOW_DIALOG" value="false" />
|
||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
|
||||
<option name="LAST_RESOLUTION" value="IGNORE" />
|
||||
</component>
|
||||
<component name="FileTemplateManagerImpl">
|
||||
<option name="RECENT_TEMPLATES">
|
||||
<list>
|
||||
<option value="Python Script" />
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
<component name="ProjectId" id="1p3FqnBRtmnXqrsKpEyYq6InGOw" />
|
||||
<component name="ProjectViewState">
|
||||
<option name="hideEmptyMiddlePackages" value="true" />
|
||||
<option name="showExcludedFiles" value="true" />
|
||||
<option name="showLibraryContents" value="true" />
|
||||
</component>
|
||||
<component name="PropertiesComponent">
|
||||
<property name="RunOnceActivity.ShowReadmeOnStart" value="true" />
|
||||
<property name="last_opened_file_path" value="$PROJECT_DIR$/../TestDeveloperPlatform36" />
|
||||
<property name="settings.editor.selected.configurable" value="com.jetbrains.python.configuration.PyActiveSdkModuleConfigurable" />
|
||||
</component>
|
||||
<component name="RunManager" selected="Python.practice">
|
||||
<configuration name="practice" type="PythonConfigurationType" factoryName="Python" temporary="true" nameIsGenerated="true">
|
||||
<module name="WebUiPy36" />
|
||||
<option name="INTERPRETER_OPTIONS" value="" />
|
||||
<option name="PARENT_ENVS" value="true" />
|
||||
<envs>
|
||||
<env name="PYTHONUNBUFFERED" value="1" />
|
||||
</envs>
|
||||
<option name="SDK_HOME" value="" />
|
||||
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
|
||||
<option name="IS_MODULE_SDK" value="true" />
|
||||
<option name="ADD_CONTENT_ROOTS" value="true" />
|
||||
<option name="ADD_SOURCE_ROOTS" value="true" />
|
||||
<option name="SCRIPT_NAME" value="$PROJECT_DIR$/practice.py" />
|
||||
<option name="PARAMETERS" value="" />
|
||||
<option name="SHOW_COMMAND_LINE" value="false" />
|
||||
<option name="EMULATE_TERMINAL" value="false" />
|
||||
<option name="MODULE_MODE" value="false" />
|
||||
<option name="REDIRECT_INPUT" value="false" />
|
||||
<option name="INPUT_FILE" value="" />
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
<configuration name="test_baidu" type="PythonConfigurationType" factoryName="Python" temporary="true" nameIsGenerated="true">
|
||||
<module name="WebUiPy36" />
|
||||
<option name="INTERPRETER_OPTIONS" value="" />
|
||||
<option name="PARENT_ENVS" value="true" />
|
||||
<envs>
|
||||
<env name="PYTHONUNBUFFERED" value="1" />
|
||||
</envs>
|
||||
<option name="SDK_HOME" value="" />
|
||||
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
|
||||
<option name="IS_MODULE_SDK" value="true" />
|
||||
<option name="ADD_CONTENT_ROOTS" value="true" />
|
||||
<option name="ADD_SOURCE_ROOTS" value="true" />
|
||||
<option name="SCRIPT_NAME" value="$PROJECT_DIR$/test_baidu.py" />
|
||||
<option name="PARAMETERS" value="" />
|
||||
<option name="SHOW_COMMAND_LINE" value="false" />
|
||||
<option name="EMULATE_TERMINAL" value="false" />
|
||||
<option name="MODULE_MODE" value="false" />
|
||||
<option name="REDIRECT_INPUT" value="false" />
|
||||
<option name="INPUT_FILE" value="" />
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
<configuration name="pytest in test_login.py" type="tests" factoryName="py.test" temporary="true" nameIsGenerated="true">
|
||||
<module name="WebUiPy36" />
|
||||
<option name="INTERPRETER_OPTIONS" value="" />
|
||||
<option name="PARENT_ENVS" value="true" />
|
||||
<option name="SDK_HOME" value="" />
|
||||
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
|
||||
<option name="IS_MODULE_SDK" value="true" />
|
||||
<option name="ADD_CONTENT_ROOTS" value="true" />
|
||||
<option name="ADD_SOURCE_ROOTS" value="true" />
|
||||
<option name="_new_keywords" value="""" />
|
||||
<option name="_new_additionalArguments" value="""" />
|
||||
<option name="_new_target" value=""$PROJECT_DIR$/test_login.py"" />
|
||||
<option name="_new_targetType" value=""PATH"" />
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
<recent_temporary>
|
||||
<list>
|
||||
<item itemvalue="Python.practice" />
|
||||
<item itemvalue="Python tests.pytest in test_login.py" />
|
||||
<item itemvalue="Python.test_baidu" />
|
||||
</list>
|
||||
</recent_temporary>
|
||||
</component>
|
||||
<component name="ServiceViewManager">
|
||||
<option name="viewStates">
|
||||
<list>
|
||||
<serviceView>
|
||||
<treeState>
|
||||
<expand />
|
||||
<select />
|
||||
</treeState>
|
||||
</serviceView>
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
<component name="SvnConfiguration">
|
||||
<configuration />
|
||||
</component>
|
||||
<component name="TaskManager">
|
||||
<task active="true" id="Default" summary="Default task">
|
||||
<changelist id="063831f7-0198-40fb-ba64-766a49951e4c" name="Default Changelist" comment="" />
|
||||
<created>1614404059892</created>
|
||||
<option name="number" value="Default" />
|
||||
<option name="presentableId" value="Default" />
|
||||
<updated>1614404059892</updated>
|
||||
</task>
|
||||
<servers />
|
||||
</component>
|
||||
<component name="WindowStateProjectService">
|
||||
<state x="503" y="174" key="EnvironmentVariablesDialog" timestamp="1614404285113">
|
||||
<screen x="0" y="0" width="1536" height="824" />
|
||||
</state>
|
||||
<state x="503" y="174" key="EnvironmentVariablesDialog/0.0.1536.824@0.0.1536.824" timestamp="1614404285113" />
|
||||
<state x="549" y="168" key="FileChooserDialogImpl" timestamp="1615470175929">
|
||||
<screen x="0" y="0" width="1536" height="824" />
|
||||
</state>
|
||||
<state x="549" y="168" key="FileChooserDialogImpl/0.0.1536.824@0.0.1536.824" timestamp="1615470175929" />
|
||||
<state width="1515" height="104" key="GridCell.Tab.0.bottom" timestamp="1615387370026">
|
||||
<screen x="0" y="0" width="1536" height="824" />
|
||||
</state>
|
||||
<state width="1515" height="104" key="GridCell.Tab.0.bottom/0.0.1536.824@0.0.1536.824" timestamp="1615387370026" />
|
||||
<state width="1515" height="104" key="GridCell.Tab.0.center" timestamp="1615387370026">
|
||||
<screen x="0" y="0" width="1536" height="824" />
|
||||
</state>
|
||||
<state width="1515" height="104" key="GridCell.Tab.0.center/0.0.1536.824@0.0.1536.824" timestamp="1615387370026" />
|
||||
<state width="1515" height="104" key="GridCell.Tab.0.left" timestamp="1615387370026">
|
||||
<screen x="0" y="0" width="1536" height="824" />
|
||||
</state>
|
||||
<state width="1515" height="104" key="GridCell.Tab.0.left/0.0.1536.824@0.0.1536.824" timestamp="1615387370026" />
|
||||
<state width="1515" height="104" key="GridCell.Tab.0.right" timestamp="1615387370026">
|
||||
<screen x="0" y="0" width="1536" height="824" />
|
||||
</state>
|
||||
<state width="1515" height="104" key="GridCell.Tab.0.right/0.0.1536.824@0.0.1536.824" timestamp="1615387370026" />
|
||||
<state width="1515" height="261" key="GridCell.Tab.1.bottom" timestamp="1614954899410">
|
||||
<screen x="0" y="0" width="1536" height="824" />
|
||||
</state>
|
||||
<state width="1515" height="261" key="GridCell.Tab.1.bottom/0.0.1536.824@0.0.1536.824" timestamp="1614954899410" />
|
||||
<state width="1515" height="261" key="GridCell.Tab.1.center" timestamp="1614954899410">
|
||||
<screen x="0" y="0" width="1536" height="824" />
|
||||
</state>
|
||||
<state width="1515" height="261" key="GridCell.Tab.1.center/0.0.1536.824@0.0.1536.824" timestamp="1614954899410" />
|
||||
<state width="1515" height="261" key="GridCell.Tab.1.left" timestamp="1614954899410">
|
||||
<screen x="0" y="0" width="1536" height="824" />
|
||||
</state>
|
||||
<state width="1515" height="261" key="GridCell.Tab.1.left/0.0.1536.824@0.0.1536.824" timestamp="1614954899410" />
|
||||
<state width="1515" height="261" key="GridCell.Tab.1.right" timestamp="1614954899410">
|
||||
<screen x="0" y="0" width="1536" height="824" />
|
||||
</state>
|
||||
<state width="1515" height="261" key="GridCell.Tab.1.right/0.0.1536.824@0.0.1536.824" timestamp="1614954899410" />
|
||||
<state x="270" y="55" key="SettingsEditor" timestamp="1614405157675">
|
||||
<screen x="0" y="0" width="1536" height="824" />
|
||||
</state>
|
||||
<state x="270" y="55" key="SettingsEditor/0.0.1536.824@0.0.1536.824" timestamp="1614405157675" />
|
||||
<state x="431" y="145" width="672" height="678" key="search.everywhere.popup" timestamp="1614781810714">
|
||||
<screen x="0" y="0" width="1536" height="824" />
|
||||
</state>
|
||||
<state x="431" y="145" width="672" height="678" key="search.everywhere.popup/0.0.1536.824@0.0.1536.824" timestamp="1614781810714" />
|
||||
</component>
|
||||
</project>
|
Binary file not shown.
|
@ -0,0 +1,177 @@
|
|||
body {
|
||||
font-family: Helvetica, Arial, sans-serif;
|
||||
font-size: 12px;
|
||||
/* do not increase min-width as some may use split screens */
|
||||
min-width: 800px;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 24px;
|
||||
color: black;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 16px;
|
||||
color: black;
|
||||
}
|
||||
|
||||
p {
|
||||
color: black;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
/******************************
|
||||
* SUMMARY INFORMATION
|
||||
******************************/
|
||||
|
||||
#environment td {
|
||||
padding: 5px;
|
||||
border: 1px solid #E6E6E6;
|
||||
}
|
||||
|
||||
#environment tr:nth-child(odd) {
|
||||
background-color: #f6f6f6;
|
||||
}
|
||||
|
||||
/******************************
|
||||
* TEST RESULT COLORS
|
||||
******************************/
|
||||
span.passed, .passed .col-result {
|
||||
color: green;
|
||||
}
|
||||
span.skipped, span.xfailed, span.rerun, .skipped .col-result, .xfailed .col-result, .rerun .col-result {
|
||||
color: orange;
|
||||
}
|
||||
span.error, span.failed, span.xpassed, .error .col-result, .failed .col-result, .xpassed .col-result {
|
||||
color: red;
|
||||
}
|
||||
|
||||
|
||||
/******************************
|
||||
* RESULTS TABLE
|
||||
*
|
||||
* 1. Table Layout
|
||||
* 2. Extra
|
||||
* 3. Sorting items
|
||||
*
|
||||
******************************/
|
||||
|
||||
/*------------------
|
||||
* 1. Table Layout
|
||||
*------------------*/
|
||||
|
||||
#results-table {
|
||||
border: 1px solid #e6e6e6;
|
||||
color: #999;
|
||||
font-size: 12px;
|
||||
width: 100%
|
||||
}
|
||||
|
||||
#results-table th, #results-table td {
|
||||
padding: 5px;
|
||||
border: 1px solid #E6E6E6;
|
||||
text-align: left
|
||||
}
|
||||
#results-table th {
|
||||
font-weight: bold
|
||||
}
|
||||
|
||||
/*------------------
|
||||
* 2. Extra
|
||||
*------------------*/
|
||||
|
||||
.log:only-child {
|
||||
height: inherit
|
||||
}
|
||||
.log {
|
||||
background-color: #e6e6e6;
|
||||
border: 1px solid #e6e6e6;
|
||||
color: black;
|
||||
display: block;
|
||||
font-family: "Courier New", Courier, monospace;
|
||||
height: 230px;
|
||||
overflow-y: scroll;
|
||||
padding: 5px;
|
||||
white-space: pre-wrap
|
||||
}
|
||||
div.image {
|
||||
border: 1px solid #e6e6e6;
|
||||
float: right;
|
||||
height: 240px;
|
||||
margin-left: 5px;
|
||||
overflow: hidden;
|
||||
width: 320px
|
||||
}
|
||||
div.image img {
|
||||
width: 320px
|
||||
}
|
||||
div.video {
|
||||
border: 1px solid #e6e6e6;
|
||||
float: right;
|
||||
height: 240px;
|
||||
margin-left: 5px;
|
||||
overflow: hidden;
|
||||
width: 320px
|
||||
}
|
||||
div.video video {
|
||||
overflow: hidden;
|
||||
width: 320px;
|
||||
height: 240px;
|
||||
}
|
||||
.collapsed {
|
||||
display: none;
|
||||
}
|
||||
.expander::after {
|
||||
content: " (show details)";
|
||||
color: #BBB;
|
||||
font-style: italic;
|
||||
cursor: pointer;
|
||||
}
|
||||
.collapser::after {
|
||||
content: " (hide details)";
|
||||
color: #BBB;
|
||||
font-style: italic;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/*------------------
|
||||
* 3. Sorting items
|
||||
*------------------*/
|
||||
.sortable {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.sort-icon {
|
||||
font-size: 0px;
|
||||
float: left;
|
||||
margin-right: 5px;
|
||||
margin-top: 5px;
|
||||
/*triangle*/
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-left: 8px solid transparent;
|
||||
border-right: 8px solid transparent;
|
||||
}
|
||||
|
||||
.inactive .sort-icon {
|
||||
/*finish triangle*/
|
||||
border-top: 8px solid #E6E6E6;
|
||||
}
|
||||
|
||||
.asc.active .sort-icon {
|
||||
/*finish triangle*/
|
||||
border-bottom: 8px solid #999;
|
||||
}
|
||||
|
||||
.desc.active .sort-icon {
|
||||
/*finish triangle*/
|
||||
border-top: 8px solid #999;
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
// 封装一个函数
|
||||
function assignClass(name, age){
|
||||
// 定义变量 方式一
|
||||
var name = name
|
||||
// 定义变量 方式二 推荐
|
||||
let age = age
|
||||
|
||||
if (age >= 3){
|
||||
window.alert(name + " 应该分配到小班")
|
||||
}
|
||||
else if (age>=4){
|
||||
window.alert(name + " 应该分配到中班")
|
||||
}
|
||||
else if (age >=5){
|
||||
window.alert(name + " 应该分配到大班")
|
||||
}
|
||||
else{
|
||||
window.alert(name + " 未达到入园年级")
|
||||
}
|
||||
}
|
||||
// 调用函数
|
||||
assignClass("flora", 3)
|
||||
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
utl = "https://www.ketangpai.com/#/homePage"
|
||||
|
||||
# 首页 tab
|
||||
//div[@id='tab-/homePage']
|
||||
|
||||
# 产品功能 tab
|
||||
//div[@id='tab-/productFunction']
|
||||
|
||||
// 机构版 tab
|
||||
//div[@id='tab-/mechanism']
|
||||
|
||||
// 其他tab定位类似 不写了
|
||||
|
||||
// 切换旧版本 按钮
|
||||
//span[contains(text(), ' 切换旧版本 ')]
|
||||
|
||||
// 进入课堂按钮
|
||||
//span[contains(text(), '进入课堂')]
|
||||
|
||||
url = "https://www.ketangpai.com/#/main"
|
||||
|
||||
//我的课堂
|
||||
//li[@role='menuitem' and text()='我的课堂']
|
||||
|
||||
// 加入课程
|
||||
//span[text()='加入课程 ']
|
||||
|
||||
// 待办事项 - 查看全部
|
||||
//div[text()=' 查看全部 ']
|
||||
|
||||
// 置顶课堂 - python36期
|
||||
//h2[text()='置顶课程']/../following-sibling::div//h3[text()='Python自动化第36期']
|
||||
|
||||
// 置顶课程 - 指定作业
|
||||
//h2[text()='置顶课程']/..//following-sibling::div//p[starts-with(text(), '0303-开放性题目')]
|
||||
|
||||
// 返回所有的(负责人:妮妮)的第一个
|
||||
(//div[contains(text(), '负责人:妮妮')])[1]
|
||||
|
||||
// 搜索我学的课程
|
||||
//input[@placeholder='搜索我学的课程']
|
||||
|
||||
// 侧边栏 - 会话
|
||||
//span[@class='name' and text()='会话']
|
|
@ -0,0 +1,62 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Login</title>
|
||||
<style type="text/css">
|
||||
.login{
|
||||
height: 60px;
|
||||
width: 311px;
|
||||
font-size: 38px;
|
||||
margin-left:240px;
|
||||
}
|
||||
.info{
|
||||
margin-top:10px;
|
||||
margin-bottom:10px;
|
||||
margin-right:50px;
|
||||
margin-left:50px;
|
||||
font-size: 28px;
|
||||
}
|
||||
.input, select{
|
||||
height: 30px;
|
||||
width: 300px;
|
||||
font-size: 18px;
|
||||
}
|
||||
.submit{
|
||||
height: 40px;
|
||||
width: 100px;
|
||||
font-size: 28px;
|
||||
margin-left:300px;
|
||||
color: blue;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<form action="http://httpbin.org/post" method="post">
|
||||
<div>
|
||||
<div class="login">
|
||||
<span>Login</span>
|
||||
</div>
|
||||
<div class="info">
|
||||
username: <input id="user" name="username" placeholder="Please input your username" class="input"/>
|
||||
</div>
|
||||
<div class="info">
|
||||
password: <input id="pwd" name="password" type="password" placeholder="Please type your password" class="input"/>
|
||||
</div>
|
||||
<div class="info">
|
||||
<select id="role" class="select">
|
||||
<option value="0">admin</option>
|
||||
<option value="1">OM</option>
|
||||
<option value="2">AM</option>
|
||||
<option value="3">VP</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="info">
|
||||
<input id="submitbtn" name="submit" type="submit" class="submit" />
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,21 @@
|
|||
"""
|
||||
======================================
|
||||
Author: Flora.Chen
|
||||
Time: 2021/3/5 22:24
|
||||
~ _ ~ ~ _ ~ ~ _ ~ ~ _ ~ ~ _ ~ ~ _ ~
|
||||
======================================
|
||||
"""
|
||||
from selenium import webdriver
|
||||
from selenium.webdriver.common.by import By
|
||||
import time
|
||||
|
||||
driver = webdriver.Chrome()
|
||||
driver.implicitly_wait(10)
|
||||
|
||||
driver.get("https://www.12306.cn/index/")
|
||||
|
||||
time.sleep(5)
|
||||
elem = driver.find_element(By.XPATH, "//h2[text()='友情链接'")
|
||||
# 将元素滚动到元素可见
|
||||
elem.location_once_scrolled_into_view
|
||||
time.sleep(2)
|
|
@ -0,0 +1,282 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Test Report</title>
|
||||
<link href="assets/style.css" rel="stylesheet" type="text/css"/></head>
|
||||
<body onLoad="init()">
|
||||
<script>/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
|
||||
function toArray(iter) {
|
||||
if (iter === null) {
|
||||
return null;
|
||||
}
|
||||
return Array.prototype.slice.call(iter);
|
||||
}
|
||||
|
||||
function find(selector, elem) {
|
||||
if (!elem) {
|
||||
elem = document;
|
||||
}
|
||||
return elem.querySelector(selector);
|
||||
}
|
||||
|
||||
function find_all(selector, elem) {
|
||||
if (!elem) {
|
||||
elem = document;
|
||||
}
|
||||
return toArray(elem.querySelectorAll(selector));
|
||||
}
|
||||
|
||||
function sort_column(elem) {
|
||||
toggle_sort_states(elem);
|
||||
var colIndex = toArray(elem.parentNode.childNodes).indexOf(elem);
|
||||
var key;
|
||||
if (elem.classList.contains('numeric')) {
|
||||
key = key_num;
|
||||
} else if (elem.classList.contains('result')) {
|
||||
key = key_result;
|
||||
} else {
|
||||
key = key_alpha;
|
||||
}
|
||||
sort_table(elem, key(colIndex));
|
||||
}
|
||||
|
||||
function show_all_extras() {
|
||||
find_all('.col-result').forEach(show_extras);
|
||||
}
|
||||
|
||||
function hide_all_extras() {
|
||||
find_all('.col-result').forEach(hide_extras);
|
||||
}
|
||||
|
||||
function show_extras(colresult_elem) {
|
||||
var extras = colresult_elem.parentNode.nextElementSibling;
|
||||
var expandcollapse = colresult_elem.firstElementChild;
|
||||
extras.classList.remove("collapsed");
|
||||
expandcollapse.classList.remove("expander");
|
||||
expandcollapse.classList.add("collapser");
|
||||
}
|
||||
|
||||
function hide_extras(colresult_elem) {
|
||||
var extras = colresult_elem.parentNode.nextElementSibling;
|
||||
var expandcollapse = colresult_elem.firstElementChild;
|
||||
extras.classList.add("collapsed");
|
||||
expandcollapse.classList.remove("collapser");
|
||||
expandcollapse.classList.add("expander");
|
||||
}
|
||||
|
||||
function show_filters() {
|
||||
var filter_items = document.getElementsByClassName('filter');
|
||||
for (var i = 0; i < filter_items.length; i++)
|
||||
filter_items[i].hidden = false;
|
||||
}
|
||||
|
||||
function add_collapse() {
|
||||
// Add links for show/hide all
|
||||
var resulttable = find('table#results-table');
|
||||
var showhideall = document.createElement("p");
|
||||
showhideall.innerHTML = '<a href="javascript:show_all_extras()">Show all details</a> / ' +
|
||||
'<a href="javascript:hide_all_extras()">Hide all details</a>';
|
||||
resulttable.parentElement.insertBefore(showhideall, resulttable);
|
||||
|
||||
// Add show/hide link to each result
|
||||
find_all('.col-result').forEach(function(elem) {
|
||||
var collapsed = get_query_parameter('collapsed') || 'Passed';
|
||||
var extras = elem.parentNode.nextElementSibling;
|
||||
var expandcollapse = document.createElement("span");
|
||||
if (extras.classList.contains("collapsed")) {
|
||||
expandcollapse.classList.add("expander")
|
||||
} else if (collapsed.includes(elem.innerHTML)) {
|
||||
extras.classList.add("collapsed");
|
||||
expandcollapse.classList.add("expander");
|
||||
} else {
|
||||
expandcollapse.classList.add("collapser");
|
||||
}
|
||||
elem.appendChild(expandcollapse);
|
||||
|
||||
elem.addEventListener("click", function(event) {
|
||||
if (event.currentTarget.parentNode.nextElementSibling.classList.contains("collapsed")) {
|
||||
show_extras(event.currentTarget);
|
||||
} else {
|
||||
hide_extras(event.currentTarget);
|
||||
}
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
function get_query_parameter(name) {
|
||||
var match = RegExp('[?&]' + name + '=([^&]*)').exec(window.location.search);
|
||||
return match && decodeURIComponent(match[1].replace(/\+/g, ' '));
|
||||
}
|
||||
|
||||
function init () {
|
||||
reset_sort_headers();
|
||||
|
||||
add_collapse();
|
||||
|
||||
show_filters();
|
||||
|
||||
sort_column(find('.initial-sort'));
|
||||
|
||||
find_all('.sortable').forEach(function(elem) {
|
||||
elem.addEventListener("click",
|
||||
function(event) {
|
||||
sort_column(elem);
|
||||
}, false)
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
function sort_table(clicked, key_func) {
|
||||
var rows = find_all('.results-table-row');
|
||||
var reversed = !clicked.classList.contains('asc');
|
||||
var sorted_rows = sort(rows, key_func, reversed);
|
||||
/* Whole table is removed here because browsers acts much slower
|
||||
* when appending existing elements.
|
||||
*/
|
||||
var thead = document.getElementById("results-table-head");
|
||||
document.getElementById('results-table').remove();
|
||||
var parent = document.createElement("table");
|
||||
parent.id = "results-table";
|
||||
parent.appendChild(thead);
|
||||
sorted_rows.forEach(function(elem) {
|
||||
parent.appendChild(elem);
|
||||
});
|
||||
document.getElementsByTagName("BODY")[0].appendChild(parent);
|
||||
}
|
||||
|
||||
function sort(items, key_func, reversed) {
|
||||
var sort_array = items.map(function(item, i) {
|
||||
return [key_func(item), i];
|
||||
});
|
||||
|
||||
sort_array.sort(function(a, b) {
|
||||
var key_a = a[0];
|
||||
var key_b = b[0];
|
||||
|
||||
if (key_a == key_b) return 0;
|
||||
|
||||
if (reversed) {
|
||||
return (key_a < key_b ? 1 : -1);
|
||||
} else {
|
||||
return (key_a > key_b ? 1 : -1);
|
||||
}
|
||||
});
|
||||
|
||||
return sort_array.map(function(item) {
|
||||
var index = item[1];
|
||||
return items[index];
|
||||
});
|
||||
}
|
||||
|
||||
function key_alpha(col_index) {
|
||||
return function(elem) {
|
||||
return elem.childNodes[1].childNodes[col_index].firstChild.data.toLowerCase();
|
||||
};
|
||||
}
|
||||
|
||||
function key_num(col_index) {
|
||||
return function(elem) {
|
||||
return parseFloat(elem.childNodes[1].childNodes[col_index].firstChild.data);
|
||||
};
|
||||
}
|
||||
|
||||
function key_result(col_index) {
|
||||
return function(elem) {
|
||||
var strings = ['Error', 'Failed', 'Rerun', 'XFailed', 'XPassed',
|
||||
'Skipped', 'Passed'];
|
||||
return strings.indexOf(elem.childNodes[1].childNodes[col_index].firstChild.data);
|
||||
};
|
||||
}
|
||||
|
||||
function reset_sort_headers() {
|
||||
find_all('.sort-icon').forEach(function(elem) {
|
||||
elem.parentNode.removeChild(elem);
|
||||
});
|
||||
find_all('.sortable').forEach(function(elem) {
|
||||
var icon = document.createElement("div");
|
||||
icon.className = "sort-icon";
|
||||
icon.textContent = "vvv";
|
||||
elem.insertBefore(icon, elem.firstChild);
|
||||
elem.classList.remove("desc", "active");
|
||||
elem.classList.add("asc", "inactive");
|
||||
});
|
||||
}
|
||||
|
||||
function toggle_sort_states(elem) {
|
||||
//if active, toggle between asc and desc
|
||||
if (elem.classList.contains('active')) {
|
||||
elem.classList.toggle('asc');
|
||||
elem.classList.toggle('desc');
|
||||
}
|
||||
|
||||
//if inactive, reset all other functions and add ascending active
|
||||
if (elem.classList.contains('inactive')) {
|
||||
reset_sort_headers();
|
||||
elem.classList.remove('inactive');
|
||||
elem.classList.add('active');
|
||||
}
|
||||
}
|
||||
|
||||
function is_all_rows_hidden(value) {
|
||||
return value.hidden == false;
|
||||
}
|
||||
|
||||
function filter_table(elem) {
|
||||
var outcome_att = "data-test-result";
|
||||
var outcome = elem.getAttribute(outcome_att);
|
||||
class_outcome = outcome + " results-table-row";
|
||||
var outcome_rows = document.getElementsByClassName(class_outcome);
|
||||
|
||||
for(var i = 0; i < outcome_rows.length; i++){
|
||||
outcome_rows[i].hidden = !elem.checked;
|
||||
}
|
||||
|
||||
var rows = find_all('.results-table-row').filter(is_all_rows_hidden);
|
||||
var all_rows_hidden = rows.length == 0 ? true : false;
|
||||
var not_found_message = document.getElementById("not-found-message");
|
||||
not_found_message.hidden = !all_rows_hidden;
|
||||
}
|
||||
</script>
|
||||
<h1>result.html</h1>
|
||||
<p>Report generated on 27-Feb-2021 at 14:06:34 by <a href="https://pypi.python.org/pypi/pytest-html">pytest-html</a> v2.1.1</p>
|
||||
<h2>Environment</h2>
|
||||
<table id="environment">
|
||||
<tr>
|
||||
<td>Packages</td>
|
||||
<td>{"pluggy": "0.13.1", "py": "1.8.1", "pytest": "5.4.1"}</td></tr>
|
||||
<tr>
|
||||
<td>Platform</td>
|
||||
<td>Windows-10-10.0.18362-SP0</td></tr>
|
||||
<tr>
|
||||
<td>Plugins</td>
|
||||
<td>{"Faker": "6.1.1", "allure-pytest": "2.8.12", "html": "2.1.1", "metadata": "1.8.0"}</td></tr>
|
||||
<tr>
|
||||
<td>Python</td>
|
||||
<td>3.6.4</td></tr></table>
|
||||
<h2>Summary</h2>
|
||||
<p>1 tests ran in 9.59 seconds. </p>
|
||||
<p class="filter" hidden="true">(Un)check the boxes to filter the results.</p><input checked="true" class="filter" data-test-result="passed" hidden="true" name="filter_checkbox" onChange="filter_table(this)" type="checkbox"/><span class="passed">1 passed</span>, <input checked="true" class="filter" data-test-result="skipped" disabled="true" hidden="true" name="filter_checkbox" onChange="filter_table(this)" type="checkbox"/><span class="skipped">0 skipped</span>, <input checked="true" class="filter" data-test-result="failed" disabled="true" hidden="true" name="filter_checkbox" onChange="filter_table(this)" type="checkbox"/><span class="failed">0 failed</span>, <input checked="true" class="filter" data-test-result="error" disabled="true" hidden="true" name="filter_checkbox" onChange="filter_table(this)" type="checkbox"/><span class="error">0 errors</span>, <input checked="true" class="filter" data-test-result="xfailed" disabled="true" hidden="true" name="filter_checkbox" onChange="filter_table(this)" type="checkbox"/><span class="xfailed">0 expected failures</span>, <input checked="true" class="filter" data-test-result="xpassed" disabled="true" hidden="true" name="filter_checkbox" onChange="filter_table(this)" type="checkbox"/><span class="xpassed">0 unexpected passes</span>
|
||||
<h2>Results</h2>
|
||||
<table id="results-table">
|
||||
<thead id="results-table-head">
|
||||
<tr>
|
||||
<th class="sortable result initial-sort" col="result">Result</th>
|
||||
<th class="sortable" col="name">Test</th>
|
||||
<th class="sortable numeric" col="duration">Duration</th>
|
||||
<th>Links</th></tr>
|
||||
<tr hidden="true" id="not-found-message">
|
||||
<th colspan="4">No results found. Try to check the filters</th></tr></thead>
|
||||
<tbody class="passed results-table-row">
|
||||
<tr>
|
||||
<td class="col-result">Passed</td>
|
||||
<td class="col-name">test_baidu.py::test_baidu</td>
|
||||
<td class="col-duration">9.31</td>
|
||||
<td class="col-links"></td></tr>
|
||||
<tr>
|
||||
<td class="extra" colspan="4">
|
||||
<div class="empty log">No log output captured.</div></td></tr></tbody></table></body></html>
|
|
@ -0,0 +1,76 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>WEB Test</title>
|
||||
<style type="text/css">
|
||||
h4{
|
||||
color:red
|
||||
}
|
||||
</style>
|
||||
<!--
|
||||
<style type="text/css">
|
||||
h4{
|
||||
color:red
|
||||
}
|
||||
|
||||
伪元素 html中看不到这个元素,就定位不到
|
||||
h4::before{
|
||||
content:"hello"
|
||||
}
|
||||
</style>
|
||||
-->
|
||||
</head>
|
||||
<body>
|
||||
<!-- 表单 前后端进行用户数据交互的一种方式-->
|
||||
<form action="http://httpbin.org/post" method="post">
|
||||
<div>
|
||||
<h4>标题</h4>
|
||||
<!-- 段落 -->
|
||||
<p>这是一个段落</p>
|
||||
<a href="http://www.baidu.com">
|
||||
<img src="https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=3363295869,2467511306&fm=26&gp=0.jpg" width="100" height="100">
|
||||
</a>
|
||||
</div>
|
||||
<!-- 在一个html中嵌套另一个页面 -->
|
||||
<div>
|
||||
<iframe src="http://www.baidu.com" width="100" height="200">
|
||||
|
||||
</iframe>
|
||||
</div>
|
||||
<ul>
|
||||
<li>1</li>
|
||||
<li>2</li>
|
||||
<li>3</li>
|
||||
<li>4</li>
|
||||
<li>5</li>
|
||||
</ul>
|
||||
<!-- 用户输入 默认是type="text"可省略 -->
|
||||
username: <input name="username" type="text" placeholder="请输入用户名" disabled/>
|
||||
password: <input name="password" type="password" readonly/>
|
||||
<div>
|
||||
<!-- 进行单选时,name属性要保持一致 -->
|
||||
选择你的性别:
|
||||
<input type="radio" name="sex" value="male"/>男
|
||||
<input type="radio" name="sex" value=""female/>女
|
||||
</div>
|
||||
<div>
|
||||
<!-- 进行单选时,name属性要保持一致 -->
|
||||
你最喜欢吃的食物:
|
||||
<input type="checkbox" name="food" value="apple"/>苹果
|
||||
<input type="checkbox" name="food" value="banana"/>香蕉
|
||||
</div>
|
||||
<div>
|
||||
选择头像:<input type="file" name="avatar" />
|
||||
</div>
|
||||
选择你的爱好:
|
||||
<select>
|
||||
<option>打球</option>
|
||||
<option>钓鱼</option>
|
||||
<option>看书</option>
|
||||
</select>
|
||||
|
||||
<input type="submit" />
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,203 @@
|
|||
from selenium import webdriver
|
||||
|
||||
from selenium.webdriver.support import expected_conditions as EC
|
||||
|
||||
from selenium.webdriver.support.wait import WebDriverWait
|
||||
|
||||
import time, pytest
|
||||
|
||||
from selenium.webdriver import ActionChains
|
||||
from selenium.webdriver.common.by import By
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class TestHomeWork:
|
||||
|
||||
"""
|
||||
|
||||
家庭作业
|
||||
|
||||
"""
|
||||
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
|
||||
def init_driver(self):
|
||||
|
||||
driver = webdriver.Chrome()
|
||||
|
||||
driver.implicitly_wait(2)
|
||||
|
||||
yield driver
|
||||
|
||||
driver.quit()
|
||||
|
||||
|
||||
|
||||
def test_homework_01(self, init_driver):
|
||||
|
||||
"""
|
||||
|
||||
作业一:
|
||||
|
||||
1,进入百度
|
||||
|
||||
2,输入柠檬班
|
||||
|
||||
3,定位柠檬班腾讯课堂,点击进入
|
||||
|
||||
4,定位腾讯课堂页面的任意元素。
|
||||
|
||||
注意:所有元素定位练习显性等待的用法。
|
||||
|
||||
:return: None
|
||||
|
||||
"""
|
||||
|
||||
# 进入百度
|
||||
|
||||
init_driver.get("https://www.baidu.com/")
|
||||
|
||||
# 输入柠檬班
|
||||
|
||||
WebDriverWait(init_driver, 10, 0.1).until(
|
||||
|
||||
EC.visibility_of_element_located(("id", "kw"))
|
||||
|
||||
).send_keys("柠檬班")
|
||||
|
||||
# 点击 百度一下 按钮
|
||||
|
||||
WebDriverWait(init_driver, 10, 0.1).until(
|
||||
|
||||
EC.element_to_be_clickable(("id", "su"))
|
||||
|
||||
).click()
|
||||
|
||||
time.sleep(2)
|
||||
|
||||
assert init_driver.title == "柠檬班_百度搜索"
|
||||
|
||||
# 定位柠檬班腾讯课堂,点击进入
|
||||
|
||||
located = "(//em[text()='柠檬班']/parent::a[text()='腾讯课堂官网'])[1]"
|
||||
|
||||
WebDriverWait(init_driver, 10, 0.1).until(
|
||||
|
||||
EC.presence_of_element_located(("xpath", located))
|
||||
|
||||
).click()
|
||||
|
||||
# 切换窗口
|
||||
# 获取所有的窗口句柄
|
||||
all_handles = init_driver.window_handles
|
||||
|
||||
WebDriverWait(init_driver, 10, 0.1).until(
|
||||
EC.new_window_is_opened(all_handles)
|
||||
)
|
||||
|
||||
init_driver.switch_to.window(init_driver.window_handles[-1])
|
||||
|
||||
assert init_driver.title == "柠檬班_柠檬班腾讯课堂官网"
|
||||
|
||||
# 定位腾讯课堂页面的任意元素
|
||||
|
||||
located = "//span[text()='立即报名']"
|
||||
|
||||
# 点击立即报名
|
||||
|
||||
WebDriverWait(init_driver, 10, 0.1).until(
|
||||
|
||||
EC.presence_of_element_located(("xpath", located))
|
||||
|
||||
).click()
|
||||
|
||||
# 切换窗口
|
||||
|
||||
init_driver.switch_to.window(init_driver.window_handles[-1])
|
||||
|
||||
assert "软件测试之python全栈自动化测试工程师" in init_driver.title
|
||||
|
||||
|
||||
|
||||
def test_homework_02(self, init_driver):
|
||||
|
||||
"""
|
||||
|
||||
作业二:
|
||||
|
||||
https://www.runoob.com/try/try.php?filename=jqueryui-api-droppable, 通过代码完成该网址的鼠标拖拽动作
|
||||
|
||||
:return: None
|
||||
|
||||
"""
|
||||
|
||||
init_driver.get("https://www.runoob.com/try/try.php?filename=jqueryui-api-droppable")
|
||||
|
||||
# 等待iframe可见并切换
|
||||
|
||||
WebDriverWait(init_driver, 10, 0.2).until(
|
||||
|
||||
EC.frame_to_be_available_and_switch_to_it(("xpath", "//iframe[@id='iframeResult']"))
|
||||
|
||||
)
|
||||
|
||||
# 等待元素可见
|
||||
|
||||
elem_01 = WebDriverWait(init_driver, 10, 0.2).until(
|
||||
|
||||
EC.visibility_of_element_located(("xpath", "//div[contains(text(), '请拖拽我')]"))
|
||||
|
||||
)
|
||||
|
||||
elem_02 = WebDriverWait(init_driver, 10, 0.2).until(
|
||||
|
||||
EC.visibility_of_element_located(("xpath", "//div[contains(text(), '请放置到这里')]"))
|
||||
|
||||
)
|
||||
|
||||
# elem_01 = init_driver.find_element("xpath", "//div[contains(text(), '请拖拽我')]")
|
||||
|
||||
# elem_02 = init_driver.find_element("xpath", "//div[contains(text(), '请放置到这里')]")
|
||||
|
||||
# 创建一个鼠标操作对象
|
||||
|
||||
action = ActionChains(init_driver)
|
||||
|
||||
# 将元素1拖拽到元素2所在位置
|
||||
|
||||
action.drag_and_drop(elem_01, elem_02).perform()
|
||||
|
||||
try:
|
||||
|
||||
# 切换到弹窗 并点击关闭
|
||||
WebDriverWait(init_driver, 10, 0.1).until(
|
||||
EC.alert_is_present()
|
||||
)
|
||||
alert = init_driver.switch_to.alert
|
||||
|
||||
|
||||
alert.accept()
|
||||
|
||||
time.sleep(2)
|
||||
|
||||
except Exception as e:
|
||||
|
||||
raise e
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
pytest.main(["test_baidu.py"])
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
"""
|
||||
=================================
|
||||
Author: Flora Chen
|
||||
Time: 2021/3/2 21:54
|
||||
-_- -_- -_- -_- -_- -_- -_- -_-
|
||||
=================================
|
||||
"""
|
||||
from selenium import webdriver
|
||||
import pytest
|
||||
from selenium.webdriver.common.by import By
|
||||
import time
|
||||
from selenium.webdriver.support.select import Select
|
||||
|
||||
|
||||
def test_login():
|
||||
driver = webdriver.Chrome()
|
||||
|
||||
driver.get("file:///D:/PythonProject/WebUiPy36/login.html")
|
||||
driver.find_element_by_id("user").send_keys("floratest")
|
||||
driver.find_element_by_name("password").send_keys("123456")
|
||||
# 定位-------------select-----------------
|
||||
driver.find_element(By.XPATH, "//option[text()='AM']").click()
|
||||
time.sleep(2)
|
||||
elem = driver.find_element(By.ID, "role")
|
||||
Select(elem).select_by_index(0)
|
||||
time.sleep(2)
|
||||
Select(elem).select_by_value("2")
|
||||
time.sleep(2)
|
||||
Select(elem).select_by_visible_text("OM")
|
||||
# 定位-------------select-----------------
|
||||
time.sleep(2)
|
||||
|
||||
driver.find_element_by_class_name("submit").click()
|
||||
|
||||
assert driver.current_url == "http://httpbin.org/post"
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
pytest.main(["test_login.py"])
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 00f4f8b4f613fcf23ed0279877df893efb76e211
|
|
@ -0,0 +1,84 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title></title>
|
||||
<style type="text/css">
|
||||
.title{
|
||||
color: red;
|
||||
font-size: 30px;
|
||||
}
|
||||
|
||||
</style>
|
||||
<!-- <script type="text/javascript" src="js/main.js">
|
||||
|
||||
</script> -->
|
||||
<script>
|
||||
window.onload = function(){
|
||||
//1、 js定位页面元素
|
||||
// 通过id获取元素
|
||||
var b = document.getElementById('box')
|
||||
console.log(b)
|
||||
// 通过class属性定位元素
|
||||
var b1 = document.getElementsByClassName('box1')
|
||||
console.log(b1)
|
||||
// 通过css选择器去定位元素
|
||||
var b3 = document.querySelector('.box1 a')
|
||||
|
||||
// 2、js修改元素的文本
|
||||
// // innerText
|
||||
// document.querySelector('#box').innerText = 'python666'
|
||||
// document.querySelector('#box').innerText = '<h1>python123</h1>'
|
||||
|
||||
// innerHTML
|
||||
document.querySelector('#box').innerHTML = '<h1>python123</h1>'
|
||||
|
||||
// 3、js修改元素的属性
|
||||
document.querySelector('.box1 a').href = 'http://www.baidu.com'
|
||||
|
||||
// 4、js修改元素的class属性
|
||||
document.querySelector('.box1 a').className = 'title'
|
||||
|
||||
// 5、js修改style属性的值
|
||||
document.querySelector('#box').style.color = 'yellow'
|
||||
// 注意点:font-size 这种多个单词组成的样式,需要写成小驼峰命令的风格-->fontSize
|
||||
document.querySelector('#box').style.fontSize = '40px'
|
||||
|
||||
|
||||
// 6、给元素绑定事件
|
||||
document.querySelector('#btn').onclick = function(){
|
||||
document.querySelector('#box').style.color = 'blue'
|
||||
alert('python666')
|
||||
}
|
||||
|
||||
// 7、window.onload事件 会等到页面文档加载关闭之后才会执行。
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
|
||||
<div id="box" class="box1" style="color: aqua;"></div>
|
||||
<div class="box1">
|
||||
<a href="">百度</a>
|
||||
|
||||
</div>
|
||||
|
||||
<button id="btn">按钮</button>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,59 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title></title>
|
||||
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app">
|
||||
<!-- 整个页面的所有的内容都放到一个根节点里面 -->
|
||||
<!-- <ul>
|
||||
<li id='n' >姓名:</li>
|
||||
<li id='a'>年龄:</li>
|
||||
<li id='s'>性别:</li>
|
||||
</ul> -->
|
||||
<h1>{{desc}}</h1>
|
||||
<ul>
|
||||
<li>姓名:{{info.name}}</li>
|
||||
<li>年龄:{{info.age}}</li>
|
||||
<li>性别:{{info.sex}}</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<script type="text/javascript">
|
||||
// 1、使用原生的js 往页面中填充数据
|
||||
// data = {
|
||||
// name: "木森",
|
||||
// age: 18,
|
||||
// sex: "男"
|
||||
// }
|
||||
// //
|
||||
// document.getElementById('n').innerHTML = document.getElementById('n').innerText+data.name
|
||||
// document.getElementById('a').innerHTML = document.getElementById('a').innerText+data.age
|
||||
// document.getElementById('s').innerHTML = document.getElementById('s').innerText+data.sex
|
||||
|
||||
//2、使用vue往页面中填充数据
|
||||
// 初始化一个vue对象
|
||||
var vm = new Vue({
|
||||
// el 用来指定挂载对象
|
||||
el: "#app",
|
||||
// data 用来保存数据的
|
||||
data: {
|
||||
// 个人信息的数据
|
||||
info: {
|
||||
name: "木森",
|
||||
age: 19,
|
||||
sex: "男"
|
||||
},
|
||||
desc:"这个是标题"
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,56 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title></title>
|
||||
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id="app">
|
||||
<p>{{msg}}</p>
|
||||
<p v-text="msg"></p>
|
||||
<p v-html="desc"></p>
|
||||
<p v-pre>显示的内容:{{msg1}}</p>
|
||||
|
||||
<a v-bind:href="url">点击跳转到我的主页</a>
|
||||
<a :href="url2">淘宝</a>
|
||||
</div>
|
||||
|
||||
|
||||
<script type="text/javascript">
|
||||
/*
|
||||
一、html的文本填充
|
||||
1、插值表达式:{{ 值}}
|
||||
2、v-text指令:往元素中填充文本
|
||||
3、v-html指令:往元素中填充html
|
||||
4、v-pre指定: 显示原始的数据格式
|
||||
|
||||
二、元素的属性绑定:v-bind
|
||||
v-bind 可以简写 :
|
||||
|
||||
|
||||
|
||||
*/
|
||||
|
||||
var vm = new Vue({
|
||||
el: "#app",
|
||||
data: {
|
||||
msg: "hello 木森",
|
||||
age: 18,
|
||||
desc: '<h1>musen<\h1>',
|
||||
url: "http://www.baidu.com",
|
||||
url2: "http://www.taobao.com"
|
||||
},
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,71 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title></title>
|
||||
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id="app">
|
||||
<!-- 数据单向绑定:把data中的数据绑定到页面的元素中 -->
|
||||
<!-- 数值A: <input type="text" name="a" :value="a">
|
||||
<br>
|
||||
数值B: <input type="text" name="b" :value="b"> -->
|
||||
|
||||
<!-- 数据双向绑定 -->
|
||||
数值A: <input type="text" name="a" v-model.number="a">
|
||||
<br>
|
||||
数值B: <input type="text" name="b" v-model.number="b">
|
||||
<br>
|
||||
<br>
|
||||
<!-- <button type="button" v-on:click="countNumber()">计算</button> -->
|
||||
<button type="button" @click="countNumber()">计算</button>
|
||||
<h5>结果:{{c}}</h5>
|
||||
</div>
|
||||
|
||||
|
||||
<script type="text/javascript">
|
||||
/*
|
||||
1、事件绑定:v-on
|
||||
v-on可以简写为:@
|
||||
|
||||
|
||||
2、数据双向绑定:v-model (绑定的是表单元素的value)
|
||||
双向绑定,用户输入的数据默认或当成字符串处理
|
||||
|
||||
3、双向绑定时:表单输入内容数据的转换(表单修饰符)
|
||||
.lazy:input数据输入完之后出发
|
||||
.number:自动把输入的数字转换为数值类型
|
||||
.trim:去除首尾的空白字符
|
||||
|
||||
*/
|
||||
|
||||
var vm = new Vue({
|
||||
// 指定vue挂载的根节点
|
||||
el: "#app",
|
||||
// 页面中的数据
|
||||
data: {
|
||||
a: 11,
|
||||
b: 22,
|
||||
c: null
|
||||
},
|
||||
// 页面操作要使用的函数
|
||||
methods: {
|
||||
// shu计算的方法
|
||||
countNumber: function() {
|
||||
this.c = this.a + this.b
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,48 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title></title>
|
||||
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app">
|
||||
|
||||
<div v-if="case_.result=='success'" style="color: chartreuse;">{{case_}}</div>
|
||||
<div v-else-if="case_.result=='fail'" style="color: #994444">{{case_}}</div>
|
||||
<div v-else style="color: red;">{{case_}}</div>
|
||||
|
||||
|
||||
<div v-show="false" >9999999999999999</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<script>
|
||||
/*
|
||||
v-if
|
||||
v-else-if
|
||||
v-else
|
||||
v-show:控制元素的dispaly属性:
|
||||
|
||||
*/
|
||||
|
||||
|
||||
var vm = new Vue({
|
||||
el: '#app',
|
||||
data: {
|
||||
case_: {
|
||||
case_id: 1,
|
||||
title: "用例001",
|
||||
result: "error",
|
||||
show:true
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,77 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title></title>
|
||||
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app">
|
||||
<table border="" cellspacing="" cellpadding="">
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>用例名</th>
|
||||
<th>结果</th>
|
||||
</tr>
|
||||
|
||||
<tr v-for='item in cases'>
|
||||
<td>{{item.case_id}}</td>
|
||||
<td>{{item.title}}</td>
|
||||
|
||||
<td v-if="item.result=='success'" style="color: green;">{{item.result}}</td>
|
||||
<td v-else-if="item.result=='fail'" style="color: #5e0000;">{{item.result}}</td>
|
||||
<td v-else style="color: red;">{{item.result}}</td>
|
||||
</tr>
|
||||
<!--遍历数组 -->
|
||||
<div v-for="(item,index) in cases"> {{item}}-----------{{index}}</div>
|
||||
|
||||
<!-- 遍历对象 -->
|
||||
<div v-for="(value,key) in stus"> {{key}}------{{value}}-----</div>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
|
||||
<script>
|
||||
var vm = new Vue({
|
||||
el: '#app',
|
||||
data: {
|
||||
cases: [{
|
||||
case_id: 1,
|
||||
title: "用例001",
|
||||
result: "success"
|
||||
},
|
||||
{
|
||||
case_id: 2,
|
||||
title: "用例002",
|
||||
result: "error"
|
||||
},
|
||||
{
|
||||
case_id: 3,
|
||||
title: "用例003",
|
||||
result: "fail"
|
||||
}, {
|
||||
case_id: 4,
|
||||
title: "用例004",
|
||||
result: "success"
|
||||
}, {
|
||||
case_id: 5,
|
||||
title: "用例004",
|
||||
result: "success"
|
||||
},
|
||||
],
|
||||
stus: {
|
||||
case_id: 5,
|
||||
title: "用例004",
|
||||
result: "success"
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
</body>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</html>
|
|
@ -0,0 +1,41 @@
|
|||
window.onload = function() {
|
||||
|
||||
//1、 js定位页面元素
|
||||
// 通过id获取元素
|
||||
var b = document.getElementById('box')
|
||||
console.log(b)
|
||||
// 通过class属性定位元素
|
||||
var b1 = document.getElementsByClassName('box1')
|
||||
console.log(b1)
|
||||
// 通过css选择器去定位元素
|
||||
var b3 = document.querySelector('.box1 a')
|
||||
|
||||
// 2、js修改元素的文本
|
||||
// // innerText
|
||||
// document.querySelector('#box').innerText = 'python666'
|
||||
// document.querySelector('#box').innerText = '<h1>python123</h1>'
|
||||
|
||||
// innerHTML
|
||||
document.querySelector('#box').innerHTML = '<h1>python123</h1>'
|
||||
|
||||
// 3、js修改元素的属性
|
||||
document.querySelector('.box1 a').href = 'http://www.baidu.com'
|
||||
|
||||
// 4、js修改元素的class属性
|
||||
document.querySelector('.box1 a').className = 'title'
|
||||
|
||||
// 5、js修改style属性的值
|
||||
document.querySelector('#box').style.color = 'yellow'
|
||||
// 注意点:font-size 这种多个单词组成的样式,需要写成小驼峰命令的风格-->fontSize
|
||||
document.querySelector('#box').style.fontSize = '40px'
|
||||
|
||||
|
||||
// 6、给元素绑定事件
|
||||
document.querySelector('#btn').onclick = function() {
|
||||
document.querySelector('#box').style.color = 'blue'
|
||||
alert('python666')
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
<component name="InspectionProjectProfileManager">
|
||||
<settings>
|
||||
<option name="USE_PROJECT_PROFILE" value="false" />
|
||||
<version value="1.0" />
|
||||
</settings>
|
||||
</component>
|
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.6" project-jdk-type="Python SDK" />
|
||||
</project>
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/py08chy.iml" filepath="$PROJECT_DIR$/.idea/py08chy.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="PYTHON_MODULE" version="4">
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$" />
|
||||
<orderEntry type="jdk" jdkName="Python 3.6" jdkType="Python SDK" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
|
@ -0,0 +1,303 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ChangeListManager">
|
||||
<list default="true" id="ea394686-91b0-48fb-b3e9-f98523c6f99c" name="Default Changelist" comment="klll llll.lio mmmmmmmmm mi,">
|
||||
<change afterPath="$PROJECT_DIR$/homework/class0521.py" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/homework/class0524.py" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/homework/class0526.py" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/homework/class0528.py" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/homework/class0531.py" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/homework/class0602.py" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/homework/class0604.py" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/homework/homework0608/__init__.py" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/homework/homework0608/basepage.py" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/homework/homework0608/run.py" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/homework/homework0608/test_demo.py" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/homework/homework0608/test_demo2.py" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/homework/homework0611/__init__.py" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/homework/homework0611/request_work2.py" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/homework/homework0611/request_work3.py" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/homework/homework0611/work1.py" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/homework/homework0611/work2.py" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/homework/homework0611/work3.py" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/myddtR/__init__.py" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/myddtR/myddt.py" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/myddtR/run.py" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/myddtR/test_case.py" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
|
||||
</list>
|
||||
<option name="SHOW_DIALOG" value="false" />
|
||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
|
||||
<option name="LAST_RESOLUTION" value="IGNORE" />
|
||||
</component>
|
||||
<component name="FileTemplateManagerImpl">
|
||||
<option name="RECENT_TEMPLATES">
|
||||
<list>
|
||||
<option value="Python Script" />
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
<component name="Git.Settings">
|
||||
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
|
||||
</component>
|
||||
<component name="GitSEFilterConfiguration">
|
||||
<file-type-list>
|
||||
<filtered-out-file-type name="LOCAL_BRANCH" />
|
||||
<filtered-out-file-type name="REMOTE_BRANCH" />
|
||||
<filtered-out-file-type name="TAG" />
|
||||
<filtered-out-file-type name="COMMIT_BY_MESSAGE" />
|
||||
</file-type-list>
|
||||
</component>
|
||||
<component name="ProjectId" id="1shUvWPaAi2NGVhtjNgGqiHBHYM" />
|
||||
<component name="ProjectLevelVcsManager" settingsEditedManually="true">
|
||||
<ConfirmationsSetting value="2" id="Add" />
|
||||
</component>
|
||||
<component name="ProjectViewState">
|
||||
<option name="hideEmptyMiddlePackages" value="true" />
|
||||
<option name="showLibraryContents" value="true" />
|
||||
</component>
|
||||
<component name="PropertiesComponent">
|
||||
<property name="RunOnceActivity.OpenProjectViewOnStart" value="true" />
|
||||
<property name="RunOnceActivity.ShowReadmeOnStart" value="true" />
|
||||
<property name="WebServerToolWindowFactoryState" value="false" />
|
||||
<property name="com.intellij.ide.scratch.LRUPopupBuilder$1/New Scratch File" value="Python" />
|
||||
<property name="last_opened_file_path" value="$PROJECT_DIR$/../../0-xianjinyuan/6-TrustieProject/apiautotest" />
|
||||
<property name="settings.editor.selected.configurable" value="com.jetbrains.python.configuration.PyActiveSdkModuleConfigurable" />
|
||||
</component>
|
||||
<component name="RecentsManager">
|
||||
<key name="CopyFile.RECENT_KEYS">
|
||||
<recent name="D:\1-PythonProject\py08chy\homework\homework0611" />
|
||||
<recent name="D:\PythonProject\py08chy\homework\homework0608" />
|
||||
</key>
|
||||
<key name="MoveFile.RECENT_KEYS">
|
||||
<recent name="D:\1-PythonProject\py08chy\homework\homework0611" />
|
||||
<recent name="D:\PythonProject\py08chy\homework" />
|
||||
<recent name="D:\PythonProject\py08chy\common" />
|
||||
<recent name="D:\PythonProject\py08chy\bookmanage" />
|
||||
</key>
|
||||
</component>
|
||||
<component name="RunManager" selected="Python.make_code">
|
||||
<configuration name="make_code" type="PythonConfigurationType" factoryName="Python" temporary="true" nameIsGenerated="true">
|
||||
<module name="py08chy" />
|
||||
<option name="INTERPRETER_OPTIONS" value="" />
|
||||
<option name="PARENT_ENVS" value="true" />
|
||||
<envs>
|
||||
<env name="PYTHONUNBUFFERED" value="1" />
|
||||
</envs>
|
||||
<option name="SDK_HOME" value="" />
|
||||
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
|
||||
<option name="IS_MODULE_SDK" value="true" />
|
||||
<option name="ADD_CONTENT_ROOTS" value="true" />
|
||||
<option name="ADD_SOURCE_ROOTS" value="true" />
|
||||
<EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
|
||||
<option name="SCRIPT_NAME" value="$PROJECT_DIR$/make_code.py" />
|
||||
<option name="PARAMETERS" value="" />
|
||||
<option name="SHOW_COMMAND_LINE" value="false" />
|
||||
<option name="EMULATE_TERMINAL" value="false" />
|
||||
<option name="MODULE_MODE" value="false" />
|
||||
<option name="REDIRECT_INPUT" value="false" />
|
||||
<option name="INPUT_FILE" value="" />
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
<configuration name="request_work2 (1)" type="PythonConfigurationType" factoryName="Python" temporary="true" nameIsGenerated="true">
|
||||
<module name="py08chy" />
|
||||
<option name="INTERPRETER_OPTIONS" value="" />
|
||||
<option name="PARENT_ENVS" value="true" />
|
||||
<envs>
|
||||
<env name="PYTHONUNBUFFERED" value="1" />
|
||||
</envs>
|
||||
<option name="SDK_HOME" value="" />
|
||||
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/homework/homework0611" />
|
||||
<option name="IS_MODULE_SDK" value="true" />
|
||||
<option name="ADD_CONTENT_ROOTS" value="true" />
|
||||
<option name="ADD_SOURCE_ROOTS" value="true" />
|
||||
<EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
|
||||
<option name="SCRIPT_NAME" value="$PROJECT_DIR$/homework/homework0611/request_work2.py" />
|
||||
<option name="PARAMETERS" value="" />
|
||||
<option name="SHOW_COMMAND_LINE" value="false" />
|
||||
<option name="EMULATE_TERMINAL" value="false" />
|
||||
<option name="MODULE_MODE" value="false" />
|
||||
<option name="REDIRECT_INPUT" value="false" />
|
||||
<option name="INPUT_FILE" value="" />
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
<configuration name="request_work3" type="PythonConfigurationType" factoryName="Python" temporary="true" nameIsGenerated="true">
|
||||
<module name="py08chy" />
|
||||
<option name="INTERPRETER_OPTIONS" value="" />
|
||||
<option name="PARENT_ENVS" value="true" />
|
||||
<envs>
|
||||
<env name="PYTHONUNBUFFERED" value="1" />
|
||||
</envs>
|
||||
<option name="SDK_HOME" value="" />
|
||||
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/homework/homework0611" />
|
||||
<option name="IS_MODULE_SDK" value="true" />
|
||||
<option name="ADD_CONTENT_ROOTS" value="true" />
|
||||
<option name="ADD_SOURCE_ROOTS" value="true" />
|
||||
<EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
|
||||
<option name="SCRIPT_NAME" value="$PROJECT_DIR$/homework/homework0611/request_work3.py" />
|
||||
<option name="PARAMETERS" value="" />
|
||||
<option name="SHOW_COMMAND_LINE" value="false" />
|
||||
<option name="EMULATE_TERMINAL" value="false" />
|
||||
<option name="MODULE_MODE" value="false" />
|
||||
<option name="REDIRECT_INPUT" value="false" />
|
||||
<option name="INPUT_FILE" value="" />
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
<configuration name="work1" type="PythonConfigurationType" factoryName="Python" temporary="true" nameIsGenerated="true">
|
||||
<module name="py08chy" />
|
||||
<option name="INTERPRETER_OPTIONS" value="" />
|
||||
<option name="PARENT_ENVS" value="true" />
|
||||
<envs>
|
||||
<env name="PYTHONUNBUFFERED" value="1" />
|
||||
</envs>
|
||||
<option name="SDK_HOME" value="" />
|
||||
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/homework/homework0611" />
|
||||
<option name="IS_MODULE_SDK" value="true" />
|
||||
<option name="ADD_CONTENT_ROOTS" value="true" />
|
||||
<option name="ADD_SOURCE_ROOTS" value="true" />
|
||||
<EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
|
||||
<option name="SCRIPT_NAME" value="$PROJECT_DIR$/homework/homework0611/work1.py" />
|
||||
<option name="PARAMETERS" value="" />
|
||||
<option name="SHOW_COMMAND_LINE" value="false" />
|
||||
<option name="EMULATE_TERMINAL" value="false" />
|
||||
<option name="MODULE_MODE" value="false" />
|
||||
<option name="REDIRECT_INPUT" value="false" />
|
||||
<option name="INPUT_FILE" value="" />
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
<configuration name="work2" type="PythonConfigurationType" factoryName="Python" temporary="true" nameIsGenerated="true">
|
||||
<module name="py08chy" />
|
||||
<option name="INTERPRETER_OPTIONS" value="" />
|
||||
<option name="PARENT_ENVS" value="true" />
|
||||
<envs>
|
||||
<env name="PYTHONUNBUFFERED" value="1" />
|
||||
</envs>
|
||||
<option name="SDK_HOME" value="" />
|
||||
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/homework/homework0611" />
|
||||
<option name="IS_MODULE_SDK" value="true" />
|
||||
<option name="ADD_CONTENT_ROOTS" value="true" />
|
||||
<option name="ADD_SOURCE_ROOTS" value="true" />
|
||||
<EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
|
||||
<option name="SCRIPT_NAME" value="$PROJECT_DIR$/homework/homework0611/work2.py" />
|
||||
<option name="PARAMETERS" value="" />
|
||||
<option name="SHOW_COMMAND_LINE" value="false" />
|
||||
<option name="EMULATE_TERMINAL" value="false" />
|
||||
<option name="MODULE_MODE" value="false" />
|
||||
<option name="REDIRECT_INPUT" value="false" />
|
||||
<option name="INPUT_FILE" value="" />
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
<recent_temporary>
|
||||
<list>
|
||||
<item itemvalue="Python.make_code" />
|
||||
<item itemvalue="Python.work1" />
|
||||
<item itemvalue="Python.request_work2 (1)" />
|
||||
<item itemvalue="Python.work2" />
|
||||
<item itemvalue="Python.request_work3" />
|
||||
</list>
|
||||
</recent_temporary>
|
||||
</component>
|
||||
<component name="ServiceViewManager">
|
||||
<option name="viewStates">
|
||||
<list>
|
||||
<serviceView>
|
||||
<treeState>
|
||||
<expand />
|
||||
<select />
|
||||
</treeState>
|
||||
</serviceView>
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
<component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="application-level" UseSingleDictionary="true" transferred="true" />
|
||||
<component name="SvnConfiguration">
|
||||
<configuration />
|
||||
</component>
|
||||
<component name="TaskManager">
|
||||
<task active="true" id="Default" summary="Default task">
|
||||
<changelist id="ea394686-91b0-48fb-b3e9-f98523c6f99c" name="Default Changelist" comment="" />
|
||||
<created>1621324545548</created>
|
||||
<option name="number" value="Default" />
|
||||
<option name="presentableId" value="Default" />
|
||||
<updated>1621324545548</updated>
|
||||
<workItem from="1622034779799" duration="30000" />
|
||||
<workItem from="1622034822095" duration="53000" />
|
||||
<workItem from="1622034886778" duration="16000" />
|
||||
<workItem from="1622034912862" duration="74000" />
|
||||
<workItem from="1622038633971" duration="1054000" />
|
||||
<workItem from="1622075873308" duration="4829000" />
|
||||
<workItem from="1622097679205" duration="929000" />
|
||||
<workItem from="1622121465010" duration="5179000" />
|
||||
<workItem from="1622126851753" duration="4454000" />
|
||||
<workItem from="1622203327135" duration="1290000" />
|
||||
<workItem from="1622293331369" duration="8696000" />
|
||||
<workItem from="1622437625143" duration="9000" />
|
||||
<workItem from="1622437701462" duration="9000" />
|
||||
<workItem from="1622462469978" duration="5964000" />
|
||||
<workItem from="1622508939572" duration="241000" />
|
||||
<workItem from="1622519038434" duration="18000" />
|
||||
<workItem from="1622552471957" duration="954000" />
|
||||
<workItem from="1622553495846" duration="6645000" />
|
||||
<workItem from="1622635384229" duration="1148000" />
|
||||
<workItem from="1622639528457" duration="4258000" />
|
||||
<workItem from="1622684049017" duration="69000" />
|
||||
<workItem from="1622685167381" duration="2000" />
|
||||
<workItem from="1622705218045" duration="171000" />
|
||||
<workItem from="1622726067341" duration="7494000" />
|
||||
<workItem from="1622782794881" duration="35000" />
|
||||
<workItem from="1622808086764" duration="2157000" />
|
||||
<workItem from="1622898255899" duration="7999000" />
|
||||
<workItem from="1623031764486" duration="593000" />
|
||||
<workItem from="1623056534942" duration="43000" />
|
||||
<workItem from="1623067309933" duration="1309000" />
|
||||
<workItem from="1623117107318" duration="595000" />
|
||||
<workItem from="1623160444099" duration="6935000" />
|
||||
<workItem from="1623167515173" duration="151000" />
|
||||
<workItem from="1623240133364" duration="1740000" />
|
||||
<workItem from="1623296320827" duration="16000" />
|
||||
<workItem from="1623337921803" duration="2106000" />
|
||||
<workItem from="1623340059459" duration="2918000" />
|
||||
<workItem from="1623373476621" duration="109000" />
|
||||
<workItem from="1623377374627" duration="610000" />
|
||||
<workItem from="1623766655591" duration="9671000" />
|
||||
<workItem from="1623805060974" duration="5015000" />
|
||||
<workItem from="1623844863916" duration="438000" />
|
||||
<workItem from="1623891684266" duration="15000" />
|
||||
<workItem from="1624496105541" duration="2667000" />
|
||||
<workItem from="1625151336162" duration="42000" />
|
||||
<workItem from="1625151403225" duration="3189000" />
|
||||
<workItem from="1625186907753" duration="10000" />
|
||||
</task>
|
||||
<servers />
|
||||
</component>
|
||||
<component name="TypeScriptGeneratedFilesManager">
|
||||
<option name="version" value="3" />
|
||||
</component>
|
||||
<component name="VcsManagerConfiguration">
|
||||
<ignored-roots>
|
||||
<path value="$PROJECT_DIR$/py08chy" />
|
||||
</ignored-roots>
|
||||
</component>
|
||||
<component name="com.intellij.coverage.CoverageDataManagerImpl">
|
||||
<SUITE FILE_PATH="coverage/py08chy$request_work2__1_.coverage" NAME="request_work2 (1) Coverage Results" MODIFIED="1623810349437" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="true" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/homework/homework0611" />
|
||||
<SUITE FILE_PATH="coverage/py08chy$class0531.coverage" NAME="class0531 Coverage Results" MODIFIED="1622635904116" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="true" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/homework" />
|
||||
<SUITE FILE_PATH="coverage/py08chy$work2.coverage" NAME="work2 Coverage Results" MODIFIED="1623810345962" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="true" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/homework/homework0611" />
|
||||
<SUITE FILE_PATH="coverage/py08chy$class0528.coverage" NAME="class0528 Coverage Results" MODIFIED="1622463636257" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="true" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/homework" />
|
||||
<SUITE FILE_PATH="coverage/py08chy$Unittests_for_test_demo_TestDemo_test_login.coverage" NAME="Unittests for test_demo.TestDemo.test_login Coverage Results" MODIFIED="1623164859337" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="true" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/homework/homework0608" />
|
||||
<SUITE FILE_PATH="coverage/py08chy$class0604.coverage" NAME="class0604 Coverage Results" MODIFIED="1622906478953" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="true" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/homework" />
|
||||
<SUITE FILE_PATH="coverage/py08chy$work3.coverage" NAME="work3 Coverage Results" MODIFIED="1623809867450" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="true" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/homework/homework0611" />
|
||||
<SUITE FILE_PATH="coverage/py08chy$make_code.coverage" NAME="make_code Coverage Results" MODIFIED="1624501521974" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="true" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" />
|
||||
<SUITE FILE_PATH="coverage/py08chy$work1.coverage" NAME="work1 Coverage Results" MODIFIED="1623810387029" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="true" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/homework/homework0611" />
|
||||
<SUITE FILE_PATH="coverage/py08chy$class0602.coverage" NAME="class0602 Coverage Results" MODIFIED="1622733518948" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="true" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/homework" />
|
||||
<SUITE FILE_PATH="coverage/py08chy$scratch.coverage" NAME="scratch Coverage Results" MODIFIED="1623767776471" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="true" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$APPLICATION_CONFIG_DIR$/scratches" />
|
||||
<SUITE FILE_PATH="coverage/py08chy$Unittests_in_test_demo_py.coverage" NAME="Unittests in test_demo.py Coverage Results" MODIFIED="1623165227568" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="true" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/homework/homework0608" />
|
||||
<SUITE FILE_PATH="coverage/py08chy$run.coverage" NAME="run Coverage Results" MODIFIED="1623167594090" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="true" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/homework/homework0608" />
|
||||
<SUITE FILE_PATH="coverage/py08chy$reque.coverage" NAME="reque Coverage Results" MODIFIED="1623772151611" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="true" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$APPLICATION_CONFIG_DIR$/scratches" />
|
||||
<SUITE FILE_PATH="coverage/py08chy$client__2_.coverage" NAME="client (2) Coverage Results" MODIFIED="1623805690210" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="true" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/homework/homework0611" />
|
||||
<SUITE FILE_PATH="coverage/py08chy$request_work3.coverage" NAME="request_work3 Coverage Results" MODIFIED="1623810251102" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="true" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/homework/homework0611" />
|
||||
<SUITE FILE_PATH="coverage/py08chy$class0526.coverage" NAME="class0526 Coverage Results" MODIFIED="1622132329295" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="true" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/homework" />
|
||||
<SUITE FILE_PATH="coverage/py08chy$server.coverage" NAME="server Coverage Results" MODIFIED="1623773392357" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="true" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$APPLICATION_CONFIG_DIR$/scratches" />
|
||||
<SUITE FILE_PATH="coverage/py08chy$client__1_.coverage" NAME="client (1) Coverage Results" MODIFIED="1623771109813" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="true" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$APPLICATION_CONFIG_DIR$/scratches" />
|
||||
</component>
|
||||
</project>
|
|
@ -0,0 +1,7 @@
|
|||
"""
|
||||
======================================
|
||||
Author: Flora.Chen
|
||||
Time: 2021/5/20 22:30
|
||||
~ _ ~ ~ _ ~ ~ _ ~ ~ _ ~ ~ _ ~ ~ _ ~
|
||||
======================================
|
||||
"""
|
|
@ -0,0 +1,219 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
===============================
|
||||
@Time : 2021/5/18 16:39
|
||||
@Author : flora.chen
|
||||
@FileName: bookmanager.py
|
||||
@Software: PyCharm
|
||||
===============================
|
||||
"""
|
||||
from common.handle_mysql import MysqlDB
|
||||
|
||||
db = MysqlDB(host="localhost", database="bookmanage", user="root", pwd="root")
|
||||
|
||||
|
||||
class BookManage:
|
||||
"""
|
||||
图书管理系统
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def print_menu():
|
||||
"""
|
||||
菜单打印
|
||||
:return:
|
||||
"""
|
||||
print("---------------------菜单-------------------------")
|
||||
print("[1]: 添加图书")
|
||||
print("[2]: 修改图书")
|
||||
print("[3]: 删除图书")
|
||||
print("[4]: 查询图书")
|
||||
print("[5]: 图书列表")
|
||||
print("[6]: 出借图书")
|
||||
print("[7]: 归还图书")
|
||||
print("[8]: 退出")
|
||||
|
||||
def add_book(self):
|
||||
"""
|
||||
[1]: 添加图书
|
||||
:return:
|
||||
"""
|
||||
print("****************添加图书****************")
|
||||
name = input("请输入书名:")
|
||||
position = input("请输入图书位置:")
|
||||
if name and position:
|
||||
db.update("insert into books(name, position) value ('{}', '{}');".format(name, position))
|
||||
print("图书添加成功")
|
||||
else:
|
||||
print("书名或者图书位置不能为空,请重新输入!")
|
||||
|
||||
num = input("继续添加请输入1, 回车退回主菜单")
|
||||
if num == "1":
|
||||
self.add_book()
|
||||
|
||||
def update_book(self):
|
||||
"""
|
||||
[2]: 修改图书
|
||||
:return:
|
||||
"""
|
||||
print("****************修改图书****************")
|
||||
book_id = input("请输入需要修改的图书ID:")
|
||||
result = db.query("select * from books where id={};".format(book_id), one=True)
|
||||
if result:
|
||||
print("当前数据为:{}".format(result))
|
||||
name = input("重新输入书名,不修改输入回车:") or result["name"]
|
||||
position = input("重新输入位置,不修改输入回车:") or result["position"]
|
||||
db.update("update books set name='{}', position='{}' where id={};".format(name, position, book_id))
|
||||
print("修改成功")
|
||||
else:
|
||||
print("您输入的图书ID不存在,请重新输入~")
|
||||
|
||||
num = input("继续修改请输入1, 回车退回主菜单")
|
||||
if num == "1":
|
||||
self.update_book()
|
||||
|
||||
def delete_book(self):
|
||||
"""
|
||||
[3]: 删除图书
|
||||
:return:
|
||||
"""
|
||||
print("****************删除图书****************")
|
||||
book_id = input("请输入需要修改的图书ID:")
|
||||
result = db.query("select * from books where id={};".format(book_id), one=True)
|
||||
if result:
|
||||
print("当前数据为:{}".format(result))
|
||||
confirm_num = input("确定需要删除这本书吗?确认请按1,取消请按2:")
|
||||
if confirm_num == "1":
|
||||
db.update("delete from books where id={};".format(book_id))
|
||||
print("删除成功")
|
||||
else:
|
||||
print("已确认不删除该书籍~")
|
||||
else:
|
||||
print("系统中未找到该书籍!")
|
||||
|
||||
num = input("继续删除请输入1, 回车退回主菜单")
|
||||
if num == "1":
|
||||
self.delete_book()
|
||||
|
||||
def query_book(self):
|
||||
"""
|
||||
[4]: 查询图书
|
||||
:return:
|
||||
"""
|
||||
print("****************查询图书****************")
|
||||
name = input("请输入您要查询的图书名称(模糊匹配):")
|
||||
if name:
|
||||
result = db.query("select * from books where name like '%{}%';".format(name))
|
||||
if result:
|
||||
print("当前查询到如下书籍信息:{}".format(result))
|
||||
else:
|
||||
print("未查询到相关书籍信息~")
|
||||
else:
|
||||
print("书名不能为空!")
|
||||
|
||||
num = input("继续查询请输入1, 回车退回主菜单")
|
||||
if num == "1":
|
||||
self.query_book()
|
||||
|
||||
def book_list(self):
|
||||
"""
|
||||
[5]: 图书列表
|
||||
:return:
|
||||
"""
|
||||
print("****************图书列表****************")
|
||||
result = db.query("select * from books;")
|
||||
for i in result:
|
||||
print("编号:{}, 书籍名:{}, 位置:{}, 状态:{}, 借阅人:{}".format(i["id"], i["name"], i["position"], i["status"],
|
||||
i["borrower"]))
|
||||
|
||||
def borrow_book(self):
|
||||
"""
|
||||
[6]: 出借图书
|
||||
:return:
|
||||
"""
|
||||
print("****************出借图书****************")
|
||||
book_id = input("请输入需要借阅的图书ID:")
|
||||
result = db.query("select * from books where id={};".format(book_id), one=True)
|
||||
if result:
|
||||
if result["status"] == "出借":
|
||||
print("抱歉,该书已经借出!")
|
||||
else:
|
||||
while True:
|
||||
borrower = input("请输入借阅者的名字:")
|
||||
if borrower:
|
||||
db.update("update books set borrower='{}' where id={};".format(borrower, book_id))
|
||||
db.update("update books set status='出借' where id={};".format(book_id))
|
||||
print("图书借阅成功~")
|
||||
break
|
||||
else:
|
||||
print("借阅者的名字不能为空, 请重新输入")
|
||||
else:
|
||||
print("未查询到相关书籍信息~")
|
||||
|
||||
num = input("继续借阅请输入1, 回车退回主菜单")
|
||||
if num == "1":
|
||||
self.borrow_book()
|
||||
|
||||
def back_book(self):
|
||||
"""
|
||||
[7]: 归还图书
|
||||
:return:
|
||||
"""
|
||||
print("****************归还图书****************")
|
||||
book_id = input("请输入需要归还的图书ID:")
|
||||
result = db.query("select * from books where id={};".format(book_id), one=True)
|
||||
if result:
|
||||
if result["status"] == "在库":
|
||||
print("该书是在库状态,请确认图书编号是否正确!")
|
||||
else:
|
||||
db.update("update books set status='在库' where id={};".format(book_id))
|
||||
db.update("update books set borrower='' where id={};".format(book_id))
|
||||
print("书籍归还成功~")
|
||||
else:
|
||||
print("未查询到相关书籍信息~")
|
||||
|
||||
num = input("继续归还书籍请输入1, 回车退回主菜单")
|
||||
if num == "1":
|
||||
self.borrow_book()
|
||||
|
||||
def quit(self):
|
||||
"""
|
||||
[8]: 退出
|
||||
:return:
|
||||
"""
|
||||
print("****************退出****************")
|
||||
db.close()
|
||||
|
||||
def main(self):
|
||||
"""
|
||||
程序运行的流程控制
|
||||
:return:
|
||||
"""
|
||||
print("---------------欢迎进入图书管理系统----------------")
|
||||
while True:
|
||||
self.print_menu()
|
||||
num = input("请输入选项:")
|
||||
if num == "1":
|
||||
self.add_book()
|
||||
elif num == "2":
|
||||
self.update_book()
|
||||
elif num == "3":
|
||||
self.delete_book()
|
||||
elif num == "4":
|
||||
self.query_book()
|
||||
elif num == "5":
|
||||
self.book_list()
|
||||
elif num == "6":
|
||||
self.borrow_book()
|
||||
elif num == "7":
|
||||
self.back_book()
|
||||
elif num == "8":
|
||||
self.quit()
|
||||
break
|
||||
else:
|
||||
print("您的输入有误~ 请按照菜单提示输入,谢谢!")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
book = BookManage()
|
||||
book.main()
|
|
@ -0,0 +1,7 @@
|
|||
"""
|
||||
======================================
|
||||
Author: Flora.Chen
|
||||
Time: 2021/5/20 22:30
|
||||
~ _ ~ ~ _ ~ ~ _ ~ ~ _ ~ ~ _ ~ ~ _ ~
|
||||
======================================
|
||||
"""
|
|
@ -0,0 +1,120 @@
|
|||
"""
|
||||
============================
|
||||
Author:柠檬班-木森
|
||||
Time:2021/6/3 13:42
|
||||
E-mail:3247119728@qq.com
|
||||
Company:湖南零檬信息技术有限公司
|
||||
=======
|
||||
"""
|
||||
|
||||
import unittest
|
||||
|
||||
|
||||
class BaseCase(type):
|
||||
"""
|
||||
定义的用例类时只要指定该类作为元类,会自动化根据用例类中定义的cases属性中的用例数据,来动态的生成测试用例
|
||||
"""
|
||||
|
||||
def __new__(cls, name, bases, attrs, *args, **kwargs):
|
||||
"""定义类时,动态的给类添加用例方法"""
|
||||
# 1、获取类的cases属性
|
||||
cases = attrs.get('cases')
|
||||
# 2、创建类
|
||||
new_cls = super().__new__(cls, name, bases, attrs)
|
||||
# 3、遍历类的cases属性值(用例数据)
|
||||
for test_name, datas in list(cases.items()):
|
||||
# 4、获取类里面定义的用例方法
|
||||
func = attrs.get(test_name)
|
||||
for index, case in enumerate(datas):
|
||||
# 5、传入原用例名,和数据的索引值,创建一个新的用例名
|
||||
new_test_name = cls._create_test_name(index, test_name)
|
||||
# 6、创建用例方法的文档字符串描述(第6步只是为了在测试报告中显示用例的标题)
|
||||
if isinstance(case, dict) and case.get("title"):
|
||||
# 6.1如果每条用例数据是一个字典,那么就获取用例数据中的title字段对应的值
|
||||
test_desc = case.get("title")
|
||||
elif hasattr(case, 'title'):
|
||||
# 6.2如果用例数据有title属性,那么就获取title属性对应的值
|
||||
test_desc = case.title
|
||||
else:
|
||||
# 6.3上述条件都不成立,则获取原方法中的文档字符串注释
|
||||
test_desc = func.__doc__
|
||||
# 7、对用例类中定义的方法的文档字符串注释修改,传入用例参数
|
||||
new_func = cls._update_func(case, test_desc, func)
|
||||
# 8、将创建出来的方法(用例),动态添加到类中
|
||||
setattr(new_cls, new_test_name, new_func)
|
||||
|
||||
delattr(new_cls, test_name)
|
||||
|
||||
return new_cls
|
||||
|
||||
@staticmethod
|
||||
def _create_test_name(index, name):
|
||||
"""
|
||||
:param index: 用例数据的索引值
|
||||
:param name: 测试类中定义的方法名称
|
||||
:return:
|
||||
"""
|
||||
if index + 1 < 10:
|
||||
test_name = name + "_0" + str(index + 1)
|
||||
else:
|
||||
test_name = name + "_" + str(index + 1)
|
||||
return test_name
|
||||
|
||||
@staticmethod
|
||||
def _update_func(case, test_desc, func):
|
||||
"""
|
||||
1、定义一个嵌套函数wrapper,
|
||||
2、修改wrapper的文档字符串注释
|
||||
3、返回wrapper这个函数,外部接收到wrapper时,会把wrapper设置为测试类的用例方法,
|
||||
unittest在执行setattr动态添加的用例时,实际上是执行这里的wrapper,
|
||||
在wrapper中执行的最初类中定义的用例方法,并传入用例数据
|
||||
:param case: 用例数据
|
||||
:param test_desc: 用例的文档字符串注释(会在显示在测试报告中)
|
||||
:param func: 类里面定义的用例方法
|
||||
:return:
|
||||
"""
|
||||
|
||||
# 1、定义一个函数wrapper
|
||||
def wrapper(self, *args, **kwargs):
|
||||
# 调用类中定义的原用例方法,在调用的时候传入用例数据case
|
||||
return func(self, case, *args, **kwargs)
|
||||
|
||||
# 2、修改wrapper的文档字符串注释
|
||||
wrapper.__doc__ = test_desc
|
||||
# 3、返回内层的嵌套函数wrapper
|
||||
return wrapper
|
||||
|
||||
|
||||
# -------------------------------------下面为测试代码-------------------------------------------------------
|
||||
class TestDome(unittest.TestCase, metaclass=BaseCase):
|
||||
"""
|
||||
测试用例类:
|
||||
指定上面的定义元类来创建测试类,可以替代ddt所实现的功能
|
||||
用例数据只要定义在测试类中的cases属性中,每个用例方法的数据,在cases中通过方法名来指定即可。
|
||||
"""
|
||||
cases = {
|
||||
# 定义test_login方法的用例数据
|
||||
'test_login': [
|
||||
{'title': "登录用例1", "data": 11},
|
||||
{'title': "登录用例2", "data": 11},
|
||||
{'title': "登录用例3", "data": 11}],
|
||||
# 定义test_register方法的用例数据
|
||||
'test_register': [
|
||||
{'title': "注册用例1", "data": 22},
|
||||
{'title': "注册用例2", "data": 22},
|
||||
{'title': "注册用例3", "data": 22}],
|
||||
}
|
||||
|
||||
def test_login(self, case):
|
||||
# 用例方法中定义一个参数,用例介绍用例的数据
|
||||
assert case['data'] == 11
|
||||
|
||||
def test_register(self, case):
|
||||
assert case['data'] == 22
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
import unittestreport
|
||||
|
||||
suite = unittest.defaultTestLoader.loadTestsFromTestCase(TestDome)
|
||||
unittestreport.TestRunner(suite).run()
|
|
@ -0,0 +1,76 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
===============================
|
||||
@Time : 2021/5/18 15:56
|
||||
@Author : flora.chen
|
||||
@FileName: handle_mysql.py
|
||||
@Software: PyCharm
|
||||
===============================
|
||||
"""
|
||||
import pymysql
|
||||
|
||||
|
||||
class MysqlDB:
|
||||
"""
|
||||
操作mysql数据库
|
||||
"""
|
||||
|
||||
def __init__(self, host, user, pwd, database=None, port=3306):
|
||||
"""
|
||||
初始化数据库链接
|
||||
:param host: 主机地址
|
||||
:param user: 用户名
|
||||
:param pwd: 密码
|
||||
:param database: 数据库名称,默认为空
|
||||
:param port: 端口号,默认3306
|
||||
"""
|
||||
self.conn = pymysql.connect(
|
||||
host=host,
|
||||
user=user,
|
||||
password=pwd,
|
||||
database=database,
|
||||
port=port,
|
||||
cursorclass=pymysql.cursors.DictCursor
|
||||
)
|
||||
# 创建一个游标对象
|
||||
self.cur = self.conn.cursor()
|
||||
|
||||
def update(self, sql):
|
||||
"""
|
||||
进行增删改操作
|
||||
:param sql: 需要执行的SQL
|
||||
:return:
|
||||
"""
|
||||
# 执行SQL
|
||||
result = self.cur.execute(sql)
|
||||
# 提交事务
|
||||
self.conn.commit()
|
||||
return result
|
||||
|
||||
def query(self, sql, one=False):
|
||||
"""
|
||||
进行查询操作
|
||||
:param one: 判断是要返回所有查询数据还是第一条,默认是所有
|
||||
:param sql: 要执行的SQL
|
||||
:return:
|
||||
"""
|
||||
# 执行SQL
|
||||
self.cur.execute(sql)
|
||||
if one:
|
||||
return self.cur.fetchone()
|
||||
else:
|
||||
return self.cur.fetchall()
|
||||
|
||||
def close(self):
|
||||
"""
|
||||
断开游标,关闭数据库连接
|
||||
:return:
|
||||
"""
|
||||
self.cur.close()
|
||||
self.conn.close()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
db = MysqlDB(host="localhost", user="root", pwd="root")
|
||||
print(db.query("select * from bookmanage.books"))
|
||||
# db.update("insert into bookmanage.books(name, position) value ('python从入门到放弃', 'A-1-1');")
|
|
@ -0,0 +1,7 @@
|
|||
"""
|
||||
======================================
|
||||
Author: Flora.Chen
|
||||
Time: 2021/5/20 22:30
|
||||
~ _ ~ ~ _ ~ ~ _ ~ ~ _ ~ ~ _ ~ ~ _ ~
|
||||
======================================
|
||||
"""
|
Binary file not shown.
|
@ -0,0 +1,46 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
===============================
|
||||
@Time : 2021/5/20 10:58
|
||||
@Author : flora.chen
|
||||
@FileName: class0519.py
|
||||
@Software: PyCharm
|
||||
===============================
|
||||
"""
|
||||
|
||||
"""
|
||||
2、通过列表推导式完成下面数据类型转换
|
||||
|
||||
现在有以下数据, li1 = ["{'a':11,'b':2}","[11,22,33,44]"]
|
||||
|
||||
|
||||
|
||||
需要转换为以下格式: li1 = [{'a':11,'b':2},[11,22,33,44]]
|
||||
|
||||
|
||||
"""
|
||||
li1 = ["{'a':11,'b':2}", "[11,22,33,44]"]
|
||||
|
||||
li2 = [eval(i) for i in li1]
|
||||
|
||||
print(li2)
|
||||
|
||||
"""
|
||||
Names=['python','java','php','c','c++','django','unittest','pytest','pymysql'],请通过列表推导式,获取names中字符串长度大于4的元素
|
||||
"""
|
||||
Names = ['python', 'java', 'php', 'c', 'c++', 'django', 'unittest', 'pytest', 'pymysql']
|
||||
|
||||
name = [i for i in Names if len(i) > 4]
|
||||
print(name)
|
||||
|
||||
"""
|
||||
使用字典推倒是将下面字符串格式的数据,改成字典类型的数据
|
||||
cook_str='BIDUPSID=D0727533D7147B7;PSTM=1530348042; BAIDUID=B1005C9BC2EB28; sugstore=0;__cfduid=d0a13458f8ac2a;BD_UPN=12314353;ispeed_lsm=2;BDORZ=B490B5EBF6F3CD402'
|
||||
|
||||
转换后的结果:
|
||||
{'BIDUPSID': 'D0727533D7147B7', 'PSTM': '1530348042', ' BAIDUID': 'B1005C9BC2EB28', ' sugstore': '0', '__cfduid': 'd0a13458f8ac2a', 'BD_UPN': '12314353', 'ispeed_lsm': '2', 'BDORZ': 'B490B5EBF6F3CD402'}
|
||||
"""
|
||||
|
||||
cook_str = 'BIDUPSID=D0727533D7147B7;PSTM=1530348042;BAIDUID=B1005C9BC2EB28; sugstore=0;__cfduid=d0a13458f8ac2a;BD_UPN=12314353;ispeed_lsm=2;BDORZ=B490B5EBF6F3CD402'
|
||||
cook_dic = {i.split("=")[0]: i.split("=")[1] for i in cook_str.split(";")}
|
||||
print(cook_dic)
|
|
@ -0,0 +1,90 @@
|
|||
"""
|
||||
======================================
|
||||
Author: Flora.Chen
|
||||
Time: 2021/5/22 12:02
|
||||
~ _ ~ ~ _ ~ ~ _ ~ ~ _ ~ ~ _ ~ ~ _ ~
|
||||
======================================
|
||||
"""
|
||||
"""
|
||||
2、现在有一个列表 li = [11,21,4,55,6,67,123,54,66,9,90,56,34,22],
|
||||
请将 大于5的数据过滤出来,然后除以2取余数,结果放到一个生成器中
|
||||
"""
|
||||
|
||||
|
||||
# 生成器函数
|
||||
def generate_num():
|
||||
li = [11, 21, 4, 55, 6, 67, 123, 54, 66, 9, 90, 56, 34, 22]
|
||||
for i in li:
|
||||
if i > 5:
|
||||
yield i % 2
|
||||
|
||||
# 创建一个生成器对象
|
||||
n = generate_num()
|
||||
|
||||
# 获取生成器中的数据放置到列表中
|
||||
result = [j for j in n]
|
||||
# 打印列表
|
||||
print(result)
|
||||
|
||||
# 生成器表达式
|
||||
li = [11, 21, 4, 55, 6, 67, 123, 54, 66, 9, 90, 56, 34, 22]
|
||||
nums = (i % 2 for i in li if i > 5)
|
||||
print(nums)
|
||||
|
||||
"""
|
||||
3、定义一个可以使用send传入域名,自动生成一个在前面加上http://,在后面加上路径/user/login的url地址,
|
||||
|
||||
生成器最多可以生成5个url,生成5条数据之后再去生成,则报错StopIteration
|
||||
|
||||
使用案例:
|
||||
|
||||
# 例如:
|
||||
res = g.send('www.baidu.com')
|
||||
# 生成数据res为:http://www.baidu.com/user/logim'
|
||||
"""
|
||||
|
||||
|
||||
def generate_url():
|
||||
url = "/user/login"
|
||||
host = "http://"
|
||||
whole_url = host + url
|
||||
for i in range(5):
|
||||
print(f"第{i + 1}条数据~")
|
||||
s = yield whole_url
|
||||
whole_url = host + s + url
|
||||
|
||||
|
||||
g = generate_url()
|
||||
g.__next__()
|
||||
print(g.send("www.baidu.com"))
|
||||
print(g.send("www.ketangpai.com"))
|
||||
print(g.send("www.lenmon.com"))
|
||||
print(g.send("www.tecent.com"))
|
||||
# print(g.send("www.flora.com"))
|
||||
|
||||
"""
|
||||
4、面试扩展题
|
||||
有一个正整数列表(数据是无序的,并且允许有相等的整数存在),
|
||||
编写一个能实现下列功能的函数,传入列表array,和正整数X,返回下面要求的三个数据
|
||||
def func(array, x)
|
||||
'''逻辑代码'''
|
||||
return count, li, new_array
|
||||
1、统计并返回在列表中,比正整数x大的数有几个(相同的数只计算一次),并返回-----返回值中的的count
|
||||
2、计算列表中比正整数X小的所有偶数,并返回 -----------返回值中的li
|
||||
3、将列表中比正整数X小的偶数去掉,未去掉的数添加到新列表中,并返回-------返回值中的new_array
|
||||
"""
|
||||
|
||||
|
||||
def func(array, x):
|
||||
# 统计并返回在列表中,比正整数x大的数有几个(相同的数只计算一次)
|
||||
count = len([i for i in set(array) if i > x])
|
||||
# 计算列表中比正整数X小的所有偶数,并返回
|
||||
li = [i for i in array if i < x and i % 2 == 0]
|
||||
# 将列表中比正整数X小的偶数去掉,未去掉的数添加到新列表中
|
||||
new_array = [i for i in array if i not in li]
|
||||
return count, li, new_array
|
||||
|
||||
|
||||
li = [1, 2, 2, 3, 4, 5, 5, 6, 7, 7, 8, 8]
|
||||
res = func(li, 5)
|
||||
print(res)
|
|
@ -0,0 +1,59 @@
|
|||
"""
|
||||
======================================
|
||||
Author: Flora.Chen
|
||||
Time: 2021/5/25 20:40
|
||||
~ _ ~ ~ _ ~ ~ _ ~ ~ _ ~ ~ _ ~ ~ _ ~
|
||||
======================================
|
||||
"""
|
||||
|
||||
|
||||
def work1(height, count):
|
||||
"""
|
||||
2 一个球从100米高度自由落下,每次落地后反跳回原高度的一半;再落下,
|
||||
求它在第10次落地时,共经过多少米?请用递归实现!
|
||||
:param height:
|
||||
:param count:
|
||||
:return:
|
||||
"""
|
||||
if count == 1:
|
||||
return height
|
||||
else:
|
||||
return work1(height / 2, count - 1) + (height + height / 2)
|
||||
|
||||
|
||||
print(work1(100, 10))
|
||||
"""
|
||||
3、问题:小明有一对刚出生的兔子,兔子的成长期为2个月,从第三个月开始每个月都生一对小兔子,
|
||||
小兔子从第三个月开始每个月也会生一对兔子,假如兔子都不死,问n个月后的兔子总数为多少?请用递归实现(意味着生长期为2个月)
|
||||
"""
|
||||
|
||||
|
||||
def work2(month):
|
||||
if month == 1 or month == 2:
|
||||
return 1
|
||||
else:
|
||||
return work2(month - 1) + work2(month - 2)
|
||||
|
||||
|
||||
print("兔子的个数:{}只".format(work2(5) * 2))
|
||||
|
||||
"""
|
||||
4、有一个列表 li2 = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15],
|
||||
请用代码处理成下面的格式:
|
||||
li4 = [[1,2,3],[4,5,6],[7,8,9],[10,11,12],[13,14,15]]
|
||||
"""
|
||||
|
||||
|
||||
def work3(n):
|
||||
li2 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
|
||||
li4 = []
|
||||
for j in range(int(len(li2) / n)):
|
||||
new_li = []
|
||||
for i in range(n):
|
||||
new_li.append(li2[j * n + i])
|
||||
li4.append(new_li)
|
||||
|
||||
return li4
|
||||
|
||||
|
||||
print(work3(3))
|
|
@ -0,0 +1,109 @@
|
|||
"""
|
||||
======================================
|
||||
Author: Flora.Chen
|
||||
Time: 2021/5/27 8:38
|
||||
~ _ ~ ~ _ ~ ~ _ ~ ~ _ ~ ~ _ ~ ~ _ ~
|
||||
======================================
|
||||
"""
|
||||
import time
|
||||
|
||||
"""
|
||||
2、实现一个重运行的装饰器,可以用来装饰任何一个功能函数,只要被装饰的函数执行出现AssertionError,则重新调用该函数,
|
||||
同一个函数最多重运行三次。
|
||||
"""
|
||||
|
||||
|
||||
def retry(count=3):
|
||||
def wrapper(func):
|
||||
def inner_wrapper(*args, **kwargs):
|
||||
for i in range(count):
|
||||
try:
|
||||
res = func(*args, **kwargs)
|
||||
return res
|
||||
except AssertionError as err:
|
||||
print(f"第{i + 1}失败")
|
||||
raise AssertionError
|
||||
|
||||
return inner_wrapper
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
@retry(3)
|
||||
def test_case():
|
||||
assert 1 == 1
|
||||
|
||||
|
||||
test_case()
|
||||
|
||||
"""
|
||||
3、编写装饰器,为多个函数加上登录认证的功能(设置个默认的初始账号密码),
|
||||
要求登录成功一次,后续的函数都无需再输入用户名和密码
|
||||
"""
|
||||
|
||||
|
||||
login_status = False
|
||||
|
||||
|
||||
def login(func):
|
||||
def wrapper(*args, **kwargs):
|
||||
global login_status
|
||||
if login_status:
|
||||
print("已经登录过了,无需再登录。")
|
||||
res = func(*args, **kwargs)
|
||||
return res
|
||||
else:
|
||||
username = input("请输入用户名:")
|
||||
password = input("请输入密码:")
|
||||
if all([username, password]):
|
||||
print("用户名密码正确,登录成功")
|
||||
res = func(*args, **kwargs)
|
||||
login_status = True
|
||||
return res
|
||||
else:
|
||||
print("用户名密码错误,登录失败")
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
@login
|
||||
def search():
|
||||
print("查询成功")
|
||||
|
||||
|
||||
@login
|
||||
def update():
|
||||
print("更新成功")
|
||||
|
||||
|
||||
search()
|
||||
update()
|
||||
"""
|
||||
4、请设计一个装饰器,接收一个int类型的参数number,可以用来装饰任何的函数,
|
||||
如果函数运行的时间大于number,则打印出函数名和函数的运行时间
|
||||
"""
|
||||
|
||||
|
||||
def count_time(num: int):
|
||||
def wrapper(func):
|
||||
def inner_wrapper(*args, **kwargs):
|
||||
# 获取开始执行的时间
|
||||
s_time = time.time()
|
||||
func(*args, **kwargs)
|
||||
# 获取结束执行的时间
|
||||
e_time = time.time()
|
||||
count = int(e_time - s_time)
|
||||
if count > num:
|
||||
print(f"函数运行的时间:{count} 大于num:{num}")
|
||||
|
||||
return inner_wrapper
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
@count_time(4)
|
||||
def work4():
|
||||
time.sleep(5)
|
||||
|
||||
|
||||
work4()
|
|
@ -0,0 +1,130 @@
|
|||
"""
|
||||
======================================
|
||||
Author: Flora.Chen
|
||||
Time: 2021/5/29 21:47
|
||||
~ _ ~ ~ _ ~ ~ _ ~ ~ _ ~ ~ _ ~ ~ _ ~
|
||||
======================================
|
||||
"""
|
||||
import random
|
||||
import time
|
||||
|
||||
print("------------------第2题-------------------------")
|
||||
|
||||
"""
|
||||
第2题 : 通过装饰器实现单例模式,只要任意一个类使用该装饰器装饰,
|
||||
那么就会变成一个单例模式的类。(面试真题)
|
||||
"""
|
||||
|
||||
|
||||
def single_object(cls):
|
||||
def wrapper(*args, **kwargs):
|
||||
if not hasattr(cls, "__instance"):
|
||||
cls.__instance = cls(*args, **kwargs)
|
||||
else:
|
||||
cls.__instance.__init__(*args, **kwargs)
|
||||
return cls.__instance
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
def single_object2(cls):
|
||||
dic = {}
|
||||
|
||||
def wrapper(*args, **kwargs):
|
||||
# 判断该类是否创建对象
|
||||
if dic.get(cls):
|
||||
return dic[cls]
|
||||
else:
|
||||
# 保存到dic这个字典中
|
||||
dic[cls] = cls(*args, **kwargs)
|
||||
return dic[cls]
|
||||
|
||||
|
||||
@single_object
|
||||
class TestCase:
|
||||
def __init__(self, a, b):
|
||||
self.a = a
|
||||
self.b = b
|
||||
|
||||
|
||||
t = TestCase(1, 2)
|
||||
print(t, t.a)
|
||||
|
||||
t2 = TestCase(3, 4)
|
||||
print(t2, t2.a)
|
||||
|
||||
"""
|
||||
第3题:请实现一个类,前五次创建对象,每次都可以返回一个新的对象,
|
||||
第六次开始,每次创建,都随机返回前5个对象中的一个
|
||||
"""
|
||||
print("------------------第3题-------------------------")
|
||||
|
||||
import random
|
||||
|
||||
|
||||
class TestWork:
|
||||
# count = 0
|
||||
objs = []
|
||||
|
||||
def __new__(cls, *args, **kwargs):
|
||||
if len(cls.objs) < 5:
|
||||
# if cls.count < 5:
|
||||
obj = super().__new__(cls)
|
||||
cls.objs.append(obj)
|
||||
# cls.count += 1
|
||||
return obj
|
||||
else:
|
||||
return random.choice(cls.objs)
|
||||
# return cls.objs[random.randint(0, 4)]
|
||||
|
||||
|
||||
print("-----------前面五次---------------")
|
||||
test1 = TestWork()
|
||||
print(test1)
|
||||
test2 = TestWork()
|
||||
print(test2)
|
||||
test3 = TestWork()
|
||||
print(test3)
|
||||
test4 = TestWork()
|
||||
print(test4)
|
||||
test5 = TestWork()
|
||||
print(test5)
|
||||
print("-----------第六次---------------")
|
||||
test6 = TestWork()
|
||||
print(test6)
|
||||
|
||||
"""
|
||||
第4题:通过类实现一个装饰器,既可以装饰函数,又可以装饰器类,不管函数和类需不需要传参都可以装饰,
|
||||
"""
|
||||
print("------------------第4题-------------------------")
|
||||
|
||||
|
||||
class ClassDecorator:
|
||||
def __init__(self, func_cls):
|
||||
self.func_cls = func_cls
|
||||
|
||||
def __call__(self, *args, **kwargs):
|
||||
current_time = time.strftime("%H:%M:%S")
|
||||
print(f"当前时间:{current_time}")
|
||||
return self.func_cls(*args, **kwargs)
|
||||
|
||||
|
||||
@ClassDecorator
|
||||
class Case:
|
||||
@staticmethod
|
||||
def get_time():
|
||||
current_date = time.strftime("%Y-%m-%d")
|
||||
print(f"当前日期:{current_date}")
|
||||
|
||||
|
||||
@ClassDecorator
|
||||
def work4(a, b):
|
||||
return a + b
|
||||
|
||||
|
||||
c = Case()
|
||||
print(c)
|
||||
c.get_time()
|
||||
|
||||
res = work4(1, 2)
|
||||
print(res)
|
|
@ -0,0 +1,115 @@
|
|||
"""
|
||||
======================================
|
||||
Project:py08chy
|
||||
File:class0531.py
|
||||
IDE:PyCharm
|
||||
Author: Flora.Chen
|
||||
Time: 2021/5/31 22:25
|
||||
======================================
|
||||
"""
|
||||
"""
|
||||
第一题、整理上课的笔记
|
||||
"""
|
||||
|
||||
"""
|
||||
第二题:自定义一个列表类型,实现对象可以之间可以 使用 - 来进行操作
|
||||
# 要求:如果一个对象减去另一个对象,则把和被减对象中一样的数据给删除掉
|
||||
# 如下:
|
||||
li1 = MyList([11, 22, 33,22, 44])
|
||||
li2 = MyList([1, 22])
|
||||
res = li1 - li2
|
||||
# res 打印的结果为[11,33,44]
|
||||
"""
|
||||
|
||||
|
||||
class MyList:
|
||||
def __init__(self, value):
|
||||
self.value = value
|
||||
|
||||
def __sub__(self, other):
|
||||
return [i for i in self.value if i not in other.value]
|
||||
|
||||
|
||||
li1 = MyList([11, 22, 33, 22, 44])
|
||||
li2 = MyList([1, 22])
|
||||
res = li1 - li2
|
||||
print(res)
|
||||
print("---------------------------------数字25255她3")
|
||||
"""
|
||||
第三题、自定义一个类
|
||||
1、通过上课的相关知识点对这个类创建的对象,进行属性限制,对象只能设置这个三个属性: title money data
|
||||
2、通过相关机制对设置的属性类型进行限制,title只能设置字符串类型数据
|
||||
money设置为int类型数据 data可以设置为任意类型
|
||||
3、通过相关机制实现,data 属性不能进行删除
|
||||
4、当money设置的值少于0时,确保查询出来的值为0,
|
||||
"""
|
||||
|
||||
|
||||
class Test:
|
||||
# 通过上课的相关知识点对这个类创建的对象,进行属性限制,对象只能设置这个三个属性: title money data
|
||||
__slots__ = ["title", "money", "data"]
|
||||
|
||||
def __init__(self, title, money, data):
|
||||
self.money = money
|
||||
self.title = title
|
||||
self.data = data
|
||||
|
||||
def __setattr__(self, key, value):
|
||||
# title只能设置字符串类型数据
|
||||
if key == "title":
|
||||
if isinstance(value, str):
|
||||
super().__setattr__(key, value)
|
||||
else:
|
||||
raise ValueError("title只能是字符串类型")
|
||||
# money设置为int类型数据
|
||||
elif key == "money":
|
||||
if isinstance(value, int):
|
||||
# # 当money设置的值少于0时,确保查询出来的值为0
|
||||
# if value < 0:
|
||||
# value = 0
|
||||
super().__setattr__(key, value)
|
||||
else:
|
||||
raise ValueError("money只能是int类型")
|
||||
# data可以设置为任意类型
|
||||
else:
|
||||
super().__setattr__(key, value)
|
||||
|
||||
def __getattribute__(self, item):
|
||||
# 先查询出来属性值
|
||||
if item == "money":
|
||||
value = super().__getattribute__(item)
|
||||
# 在判断属性值少于0时,确保查询出来的值为0
|
||||
if value < 0:
|
||||
value = 0
|
||||
return value
|
||||
else:
|
||||
return super().__getattribute__(item)
|
||||
|
||||
def __delattr__(self, item):
|
||||
if item == "data":
|
||||
raise AttributeError("data属性不能删除")
|
||||
else:
|
||||
super().__delattr__(item)
|
||||
|
||||
|
||||
demo = Test("test", 100, "数据")
|
||||
print(demo.title, demo.money, demo.data) # 输出:test 100 数据
|
||||
|
||||
# data可以是任意数据类型
|
||||
demo = Test("test", 100, 123)
|
||||
print(demo.title, demo.money, demo.data) # 输出:test 100 123
|
||||
demo = Test("test", 100, 123.1)
|
||||
print(demo.title, demo.money, demo.data) # 输出:test 100 123.1
|
||||
demo = Test("test", 100, True)
|
||||
print(demo.title, demo.money, demo.data) # 输出:test 100 True
|
||||
|
||||
# 设置非字符串类型的title
|
||||
# demo = Test(1, 100, "数据") # 报错:ValueError: title只能是字符串类型
|
||||
|
||||
# 设置非int的money
|
||||
# demo = Test("test", 100.1, "数据") # 报错:ValueError: money只能是int类型
|
||||
# demo = Test("test", "1", "数据") # 报错:ValueError: money只能是int类型
|
||||
|
||||
# money小于0
|
||||
demo = Test("test", -1, "数据")
|
||||
print(demo.title, demo.money, demo.data) # 输出:test 0 数据
|
|
@ -0,0 +1,125 @@
|
|||
"""
|
||||
======================================
|
||||
Project:py08chy
|
||||
File:class0602.py
|
||||
IDE:PyCharm
|
||||
Author: Flora.Chen
|
||||
Time: 2021/6/3 15:27
|
||||
======================================
|
||||
"""
|
||||
import threading
|
||||
import time
|
||||
|
||||
"""
|
||||
1、整理上课笔记
|
||||
"""
|
||||
|
||||
"""
|
||||
2、一个列表中有100个url地址,请设计程序一个程序,获取列表中的url地址,
|
||||
使用4个线程去发送这100个请求(假设请求每个地址需要0.5秒,请求的代码用time.sleep(0.5)代替)
|
||||
计算出总耗时!
|
||||
"""
|
||||
from faker import Faker
|
||||
from threading import Thread
|
||||
|
||||
|
||||
def count_time(func):
|
||||
def wrapper(*args, **kwargs):
|
||||
# 获取开始执行的时间
|
||||
s_time = time.time()
|
||||
func(*args, **kwargs)
|
||||
# 获取结束执行的时间
|
||||
e_time = time.time()
|
||||
count = int(e_time - s_time)
|
||||
print("运行时间:{}".format(count))
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
def generate_url(n):
|
||||
"""
|
||||
随机生成指定数量的url地址
|
||||
:param n: 指定数量
|
||||
:return:
|
||||
"""
|
||||
faker = Faker()
|
||||
urls = [faker.url() for i in range(n)]
|
||||
return urls
|
||||
|
||||
|
||||
def request(urls):
|
||||
"""模拟请求"""
|
||||
for url in urls:
|
||||
print(f"线程{threading.current_thread().ident} 请求的地址是:{url}")
|
||||
time.sleep(0.5)
|
||||
|
||||
|
||||
@count_time
|
||||
def work1(thread_num, url_num):
|
||||
"""使用thread_num个线程去发送这url_num个请求"""
|
||||
# url_num = 100
|
||||
urls = generate_url(url_num)
|
||||
# 开线程
|
||||
threads = []
|
||||
for i in range(thread_num):
|
||||
thread = Thread(target=request, args=(urls[int(i * url_num / thread_num):int(url_num / thread_num * (i + 1))],))
|
||||
threads.append(thread)
|
||||
print(f"所有的线程:{threads}")
|
||||
for i in threads:
|
||||
i.start()
|
||||
for i in threads:
|
||||
i.join()
|
||||
|
||||
|
||||
work1(4, 100)
|
||||
|
||||
"""
|
||||
3、自定义一个元类,可以在创建类的时候,自动给类添加(class_name,create_time)这两个类属性,属性值自己随便写一个
|
||||
"""
|
||||
|
||||
|
||||
class MyClass(type):
|
||||
"""创建类的时候自动给类添加属性的元类"""
|
||||
|
||||
def __new__(cls, *args, **kwargs):
|
||||
# 创建类
|
||||
cls = super().__new__(cls, *args, **kwargs)
|
||||
# 给类动态添加属性
|
||||
setattr(cls, "class_name", "class_name属性值")
|
||||
setattr(cls, "create_time", "create_time属性值")
|
||||
return cls
|
||||
|
||||
|
||||
TestClass1 = MyClass("TestClass1", (object,), {})
|
||||
print(TestClass1)
|
||||
print(TestClass1().class_name)
|
||||
|
||||
"""
|
||||
4、(面试扩展算法题,和上课内容无关,不计分,可以不交在作业里,不是必做题)
|
||||
有一艘船上有40个人,由于触礁出现了漏水,现在船上最多只能载20个人,需要20个人下船。
|
||||
于是这40个人排成一队,根据站位,每个人领取了一个编号,从1开始到40。
|
||||
然后从1开始到9进行循环报数,报数为9的人出列下船,一直循环,直到船上只剩下20人。
|
||||
示例:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19....40
|
||||
第一次报到9下船的人,编号为9(1,2,3,...编号为9的人报9)
|
||||
第二次下船的,编号为18,(10的人报1....18的人报9)
|
||||
第三次下船的,编号为27 (19的人报1....27的人报9)
|
||||
第四次下船,编号为36 (28的人报1....36的人报9)
|
||||
第五次下船,编号为5 (37的人报1,38报2,39报3,40报4....5的人报9)
|
||||
第六次下船,编号为15
|
||||
.....
|
||||
请问最后那些编号的人下船了?
|
||||
"""
|
||||
people = [i for i in range(1, 41)]
|
||||
off_boat = []
|
||||
on_boat = []
|
||||
num = 0
|
||||
while len(people) > 20:
|
||||
for index, value in enumerate(people):
|
||||
num += 1
|
||||
if num % 9 == 0:
|
||||
value = people[index]
|
||||
off_boat.append(value)
|
||||
people = [i for i in people if i not in off_boat]
|
||||
|
||||
print(f"留在船上的人:{people}")
|
||||
print(f"下船的人:{off_boat}")
|
|
@ -0,0 +1,135 @@
|
|||
"""
|
||||
======================================
|
||||
Project:py08chy
|
||||
File:class0604.py
|
||||
IDE:PyCharm
|
||||
Author: Flora.Chen
|
||||
Time: 2021/6/5 21:11
|
||||
======================================
|
||||
"""
|
||||
import threading
|
||||
|
||||
"""
|
||||
一、整理上课笔记
|
||||
"""
|
||||
|
||||
"""
|
||||
二、使用队列和线程完成下面要求(生产者消费者模式)
|
||||
|
||||
1、用一个队列来存储数据
|
||||
|
||||
2、定义一个专门生产数据的线程类,当队列中数据数量少于50时,开始生产数据,每次生产200个数据,添加到队列中,每生产完一轮 暂停1秒
|
||||
|
||||
3、定义一个专门获取数据的线程类,当队列中数据数量大于10时就开始获取,循环获取,每次获取20个。当队列中数据数量少于10的时候,暂停2秒
|
||||
|
||||
4、启动程序 创建一个线程生产数据 ,5个线程获取数据
|
||||
"""
|
||||
from queue import Queue
|
||||
from threading import Thread
|
||||
import time
|
||||
|
||||
# 用一个队列来存储数据
|
||||
q = Queue()
|
||||
|
||||
|
||||
class GenerateData(Thread):
|
||||
"""专门生产数据的线程类"""
|
||||
|
||||
def run(self) -> None:
|
||||
"""定义线程对象执行的任务"""
|
||||
"""当队列中数据数量少于50时,开始生产数据,每次生产200个数据,添加到队列中,每生产完一轮 暂停1秒"""
|
||||
while True:
|
||||
if q.qsize() < 50:
|
||||
print(f'队列中还剩{q.qsize()}个数,开始生产200个')
|
||||
for i in range(200):
|
||||
q.put(i + 1)
|
||||
time.sleep(1)
|
||||
print('生产完成,休眠1秒')
|
||||
|
||||
|
||||
class GetData(Thread):
|
||||
"""专门获取数据的线程类"""
|
||||
|
||||
def run(self) -> None:
|
||||
"""定义线程对象执行的任务"""
|
||||
"""当队列中数据数量大于10时就开始获取,循环获取,每次获取20个。当队列中数据数量少于10的时候,暂停2秒"""
|
||||
while True:
|
||||
if q.qsize() > 10:
|
||||
print(f'线程{threading.current_thread().ident}消耗20个')
|
||||
for i in range(20):
|
||||
q.get()
|
||||
else:
|
||||
print(f'队列中还剩{q.qsize()},线程{threading.current_thread().ident}休眠2秒')
|
||||
time.sleep(2)
|
||||
|
||||
|
||||
def main():
|
||||
"""启动程序 创建一个进线程生产数据 ,5个线程获取数据"""
|
||||
# 生产数据的线程
|
||||
generate_data_thread = GenerateData()
|
||||
# 获取数据的线程
|
||||
get_data_thread1 = GetData()
|
||||
get_data_thread2 = GetData()
|
||||
get_data_thread3 = GetData()
|
||||
get_data_thread4 = GetData()
|
||||
get_data_thread5 = GetData()
|
||||
# 启动线程
|
||||
|
||||
generate_data_thread.start()
|
||||
|
||||
get_data_thread1.start()
|
||||
get_data_thread2.start()
|
||||
get_data_thread3.start()
|
||||
get_data_thread4.start()
|
||||
get_data_thread5.start()
|
||||
|
||||
# 主线程join子线程
|
||||
generate_data_thread.join()
|
||||
get_data_thread1.join()
|
||||
get_data_thread2.join()
|
||||
get_data_thread3.join()
|
||||
get_data_thread4.join()
|
||||
get_data_thread5.join()
|
||||
|
||||
|
||||
|
||||
main()
|
||||
|
||||
"""
|
||||
import gevent
|
||||
import unittest
|
||||
|
||||
|
||||
class TestRunner:
|
||||
# 线程池实现
|
||||
|
||||
def __init__(self):
|
||||
self.suites = []
|
||||
|
||||
def get_suite(self, suite):
|
||||
# 将测试套件以测试类进行拆分,加入队列中
|
||||
for item in suite:
|
||||
if isinstance(item, unittest.TestCase):
|
||||
self.suites.append(suite)
|
||||
break
|
||||
else:
|
||||
self.get_suite(item)
|
||||
|
||||
def run(self, suite):
|
||||
# 分隔套件
|
||||
self.get_suite(suite)
|
||||
result = unittest.TestResult()
|
||||
|
||||
gs = [gevent.spawn(sute.run, result) for sute in self.suites]
|
||||
# for sute in suite:
|
||||
# g = gevent.spawn(sute.run, result)
|
||||
# gs.append(g)
|
||||
[i.join() for i in gs]
|
||||
for i in gs:
|
||||
i.join()
|
||||
|
||||
print(result)
|
||||
|
||||
|
||||
|
||||
"""
|
|
@ -0,0 +1,9 @@
|
|||
"""
|
||||
======================================
|
||||
Project:py08chy
|
||||
File:__init__.py.py
|
||||
IDE:PyCharm
|
||||
Author: Flora.Chen
|
||||
Time: 2021/6/8 22:54
|
||||
======================================
|
||||
"""
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,85 @@
|
|||
"""
|
||||
============================
|
||||
Author:柠檬班-木森
|
||||
Time:2021/6/3 13:42
|
||||
E-mail:3247119728@qq.com
|
||||
Company:湖南零檬信息技术有限公司
|
||||
=======
|
||||
"""
|
||||
|
||||
class BaseCase(type):
|
||||
"""
|
||||
定义的用例类时只要指定该类作为元类,会自动化根据用例类中定义的cases属性中的用例数据,来动态的生成测试用例
|
||||
"""
|
||||
|
||||
def __new__(cls, name, bases, attrs, *args, **kwargs):
|
||||
"""定义类时,动态的给类添加用例方法"""
|
||||
# 1、获取类的cases属性
|
||||
cases = attrs.get('cases')
|
||||
# 2、创建类
|
||||
new_cls = super().__new__(cls, name, bases, attrs)
|
||||
# 3、遍历类的cases属性值(用例数据)
|
||||
for test_name, datas in list(cases.items()):
|
||||
# 4、获取类里面定义的用例方法
|
||||
func = attrs.get(test_name)
|
||||
# 遍历用例方法的数据
|
||||
for index, case in enumerate(datas):
|
||||
# 5、传入原用例名,和数据的索引值,创建一个新的用例名
|
||||
new_test_name = cls._create_test_name(index, test_name)
|
||||
# 6、创建用例方法的文档字符串描述(第6步只是为了在测试报告中显示用例的标题)
|
||||
if isinstance(case, dict) and case.get("title"):
|
||||
# 6.1如果每条用例数据是一个字典,那么就获取用例数据中的title字段对应的值
|
||||
test_desc = case.get("title")
|
||||
elif hasattr(case, 'title'):
|
||||
# 6.2如果用例数据有title属性,那么就获取title属性对应的值
|
||||
test_desc = str(case.title)
|
||||
else:
|
||||
# 6.3上述条件都不成立,则获取原方法中的文档字符串注释
|
||||
test_desc = func.__doc__
|
||||
# 7、对用例类中定义的方法的文档字符串注释修改,传入用例参数
|
||||
new_func = cls._update_func(case, test_desc, func)
|
||||
# 8、将创建出来的方法(用例),动态添加到类中
|
||||
setattr(new_cls, new_test_name, new_func)
|
||||
|
||||
delattr(new_cls, test_name)
|
||||
|
||||
return new_cls
|
||||
|
||||
@staticmethod
|
||||
def _create_test_name(index, name):
|
||||
"""
|
||||
:param index: 用例数据的索引值
|
||||
:param name: 测试类中定义的方法名称
|
||||
:return:
|
||||
"""
|
||||
if index + 1 < 10:
|
||||
test_name = name + "_0" + str(index + 1)
|
||||
else:
|
||||
test_name = name + "_" + str(index + 1)
|
||||
return test_name
|
||||
|
||||
@staticmethod
|
||||
def _update_func(case, test_desc, func):
|
||||
"""
|
||||
1、定义一个嵌套函数wrapper,
|
||||
2、修改wrapper的文档字符串注释
|
||||
3、返回wrapper这个函数,外部接收到wrapper时,会把wrapper设置为测试类的用例方法,
|
||||
unittest在执行setattr动态添加的用例时,实际上是执行这里的wrapper,
|
||||
在wrapper中执行的最初类中定义的用例方法,并传入用例数据
|
||||
:param case: 用例数据
|
||||
:param test_desc: 用例的文档字符串注释(会在显示在测试报告中)
|
||||
:param func: 类里面定义的用例方法
|
||||
:return:
|
||||
"""
|
||||
|
||||
# 1、定义一个函数wrapper
|
||||
def wrapper(self, *args, **kwargs):
|
||||
# 调用类中定义的原用例方法,在调用的时候传入用例数据case
|
||||
return func(self, case, *args, **kwargs)
|
||||
|
||||
# 2、修改wrapper的文档字符串注释
|
||||
wrapper.__doc__ = test_desc
|
||||
# 3、返回内层的嵌套函数wrapper
|
||||
return wrapper
|
||||
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
"""
|
||||
======================================
|
||||
Project:py08chy
|
||||
File:run.py
|
||||
IDE:PyCharm
|
||||
Author: Flora.Chen
|
||||
Time: 2021/6/8 22:59
|
||||
======================================
|
||||
"""
|
||||
import unittest
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
from queue import Queue
|
||||
from homework.homework0608 import test_demo, test_demo2
|
||||
|
||||
"""
|
||||
# --------------------------- 单线程执行 ---------------------------
|
||||
suite = unittest.defaultTestLoader.discover(r'D:\PythonProject\py08chy\homework\homework0608')
|
||||
result = unittest.TestResult()
|
||||
# 运行用例获取运行结果
|
||||
res = suite.run(result)
|
||||
# 打印运行结果
|
||||
print(res)
|
||||
"""
|
||||
|
||||
"""
|
||||
# --------------- 一个个用例文件去收集,每个文件作为一个任务 ---------------------------
|
||||
|
||||
suite1 = unittest.defaultTestLoader.loadTestsFromModule(test_demo)
|
||||
suite2 = unittest.defaultTestLoader.loadTestsFromModule(test_demo2)
|
||||
|
||||
result = unittest.TestResult()
|
||||
with ThreadPoolExecutor(4) as ts:
|
||||
ts.submit(suite1.run, result)
|
||||
ts.submit(suite2.run, result)
|
||||
|
||||
print(result)
|
||||
"""
|
||||
suite = unittest.defaultTestLoader.discover(r'D:\PythonProject\py08chy\homework\homework0608')
|
||||
|
||||
|
||||
# --------------以测试类为单位拆分测试套件------------
|
||||
|
||||
class TestRunner:
|
||||
"""
|
||||
线程池实现
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.suites = []
|
||||
|
||||
def get_suite(self, suite):
|
||||
"""将测试套件以测试类进行拆分,加入队列中"""
|
||||
for item in suite:
|
||||
if isinstance(item, unittest.TestCase):
|
||||
self.suites.append(suite)
|
||||
break
|
||||
else:
|
||||
self.get_suite(item)
|
||||
|
||||
def run(self, suite, thread_count):
|
||||
# 分隔套件
|
||||
self.get_suite(suite)
|
||||
result = unittest.TestResult()
|
||||
|
||||
# 创建线程池
|
||||
with ThreadPoolExecutor(thread_count) as ts:
|
||||
for sute in self.suites:
|
||||
ts.submit(sute.run, result)
|
||||
|
||||
return result
|
|
@ -0,0 +1,38 @@
|
|||
"""
|
||||
======================================
|
||||
Project:py08chy
|
||||
File:test_demo.py
|
||||
IDE:PyCharm
|
||||
Author: Flora.Chen
|
||||
Time: 2021/6/8 22:54
|
||||
======================================
|
||||
"""
|
||||
import time
|
||||
import unittest
|
||||
from basepage import BaseCase
|
||||
|
||||
|
||||
class TestDemo(unittest.TestCase, metaclass=BaseCase):
|
||||
cases = {
|
||||
"test_login": [
|
||||
{"title": "登录成功", "data": {"name": "floraachy", "pwd": "123456"}, "expected": 0},
|
||||
{"title": "登录失败-用户名为空", "data": {"name": "", "pwd": "123456"}, "expected": 0},
|
||||
{"title": "登录失败-密码为空", "data": {"name": "floraachy", "pwd": ""}, "expected": 0},
|
||||
{"title": "登录成功", "data": {"name": "floraachy", "pwd": "123456"}, "expected": 0},
|
||||
{"title": "登录失败-用户名为空", "data": {"name": "", "pwd": "123456"}, "expected": 0},
|
||||
{"title": "登录失败-密码为空", "data": {"name": "floraachy", "pwd": ""}, "expected": 0},
|
||||
{"title": "登录成功", "data": {"name": "floraachy", "pwd": "123456"}, "expected": 0},
|
||||
{"title": "登录失败-用户名为空", "data": {"name": "", "pwd": "123456"}, "expected": 0},
|
||||
{"title": "登录失败-密码为空", "data": {"name": "floraachy", "pwd": ""}, "expected": 0},
|
||||
{"title": "登录成功", "data": {"name": "floraachy", "pwd": "123456"}, "expected": 0},
|
||||
{"title": "登录失败-用户名为空", "data": {"name": "", "pwd": "123456"}, "expected": 0},
|
||||
{"title": "登录失败-密码为空", "data": {"name": "floraachy", "pwd": ""}, "expected": 0},
|
||||
{"title": "登录成功", "data": {"name": "floraachy", "pwd": "123456"}, "expected": 0},
|
||||
{"title": "登录失败-用户名为空", "data": {"name": "", "pwd": "123456"}, "expected": 0},
|
||||
{"title": "登录失败-密码为空", "data": {"name": "floraachy", "pwd": ""}, "expected": 0}
|
||||
]
|
||||
}
|
||||
|
||||
def test_login(self, case):
|
||||
time.sleep(1)
|
||||
assert case["expected"] == 0
|
|
@ -0,0 +1,38 @@
|
|||
"""
|
||||
======================================
|
||||
Project:py08chy
|
||||
File:test_demo.py
|
||||
IDE:PyCharm
|
||||
Author: Flora.Chen
|
||||
Time: 2021/6/8 22:54
|
||||
======================================
|
||||
"""
|
||||
import time
|
||||
import unittest
|
||||
from basepage import BaseCase
|
||||
|
||||
|
||||
class TestDemo(unittest.TestCase, metaclass=BaseCase):
|
||||
cases = {
|
||||
"test_login": [
|
||||
{"title": "登录成功", "data": {"name": "floraachy", "pwd": "123456"}, "expected": 0},
|
||||
{"title": "登录失败-用户名为空", "data": {"name": "", "pwd": "123456"}, "expected": 0},
|
||||
{"title": "登录失败-密码为空", "data": {"name": "floraachy", "pwd": ""}, "expected": 0},
|
||||
{"title": "登录成功", "data": {"name": "floraachy", "pwd": "123456"}, "expected": 0},
|
||||
{"title": "登录失败-用户名为空", "data": {"name": "", "pwd": "123456"}, "expected": 0},
|
||||
{"title": "登录失败-密码为空", "data": {"name": "floraachy", "pwd": ""}, "expected": 0},
|
||||
{"title": "登录成功", "data": {"name": "floraachy", "pwd": "123456"}, "expected": 0},
|
||||
{"title": "登录失败-用户名为空", "data": {"name": "", "pwd": "123456"}, "expected": 0},
|
||||
{"title": "登录失败-密码为空", "data": {"name": "floraachy", "pwd": ""}, "expected": 0},
|
||||
{"title": "登录成功", "data": {"name": "floraachy", "pwd": "123456"}, "expected": 0},
|
||||
{"title": "登录失败-用户名为空", "data": {"name": "", "pwd": "123456"}, "expected": 0},
|
||||
{"title": "登录失败-密码为空", "data": {"name": "floraachy", "pwd": ""}, "expected": 0},
|
||||
{"title": "登录成功", "data": {"name": "floraachy", "pwd": "123456"}, "expected": 0},
|
||||
{"title": "登录失败-用户名为空", "data": {"name": "", "pwd": "123456"}, "expected": 0},
|
||||
{"title": "登录失败-密码为空", "data": {"name": "floraachy", "pwd": ""}, "expected": 0}
|
||||
]
|
||||
}
|
||||
|
||||
def test_login(self, case):
|
||||
time.sleep(1)
|
||||
assert case["expected"] == 0
|
|
@ -0,0 +1,9 @@
|
|||
"""
|
||||
======================================
|
||||
Project:py08chy
|
||||
File:__init__.py.py
|
||||
IDE:PyCharm
|
||||
Author: Flora.Chen
|
||||
Time: 2021/6/16 0:15
|
||||
======================================
|
||||
"""
|
|
@ -0,0 +1,32 @@
|
|||
import requests
|
||||
|
||||
url = "http://127.0.0.1:8899/login"
|
||||
res = requests.get(url=url)
|
||||
res2 = requests.post(url=url, data={"name": "flora", "pwd": "123456"})
|
||||
print(res.text)
|
||||
print(res2.text)
|
||||
|
||||
|
||||
url = "http://127.0.0.1:8899/report"
|
||||
res = requests.get(url=url)
|
||||
res2 = requests.post(url=url, data={"name": "flora1", "pwd": "123456"})
|
||||
print(res.text)
|
||||
print(res2.text)
|
||||
|
||||
|
||||
url = "http://127.0.0.1:8899/register"
|
||||
res = requests.get(url=url)
|
||||
res2 = requests.post(url=url, data={"name": "flora1s", "pwd": "123456"})
|
||||
print(res.text)
|
||||
print(res2.text)
|
||||
|
||||
|
||||
url = "http://127.0.0.1:8899"
|
||||
res = requests.get(url=url)
|
||||
res2 = requests.post(url=url, data={"name": "flora1s11", "pwd": "123456"})
|
||||
print(res.text)
|
||||
print(res2.text)
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
import requests
|
||||
|
||||
url = "http://127.0.0.1:8899/user/login"
|
||||
res = requests.get(url=url)
|
||||
res2 = requests.post(url=url, json={"user": "flora", "pwd": "123456"})
|
||||
res3 = requests.post(url=url, data={"user": "flor1a", "pwd": "123456"})
|
||||
res4 = requests.post(url=url, data={"user": "", "pwd": "123456"})
|
||||
print(res.json())
|
||||
print(res2.json())
|
||||
print(res3.json())
|
||||
print(res4.json())
|
|
@ -0,0 +1,120 @@
|
|||
"""
|
||||
======================================
|
||||
Project:py08chy
|
||||
File:work1.py
|
||||
IDE:PyCharm
|
||||
Author: Flora.Chen
|
||||
Time: 2021/6/16 0:11
|
||||
======================================
|
||||
"""
|
||||
"""
|
||||
1、基本要求:使用Socket实现一个可以根据不同访问路径,返回不同结果的简易版HTTP服务器
|
||||
"""
|
||||
import json
|
||||
import re
|
||||
from socket import socket, AF_INET, SOCK_STREAM
|
||||
from threading import Thread
|
||||
|
||||
|
||||
class HttpServer:
|
||||
def __init__(self):
|
||||
# 1. 创建一个TCP套接字
|
||||
self.server = socket(AF_INET, SOCK_STREAM)
|
||||
|
||||
# 2. 绑定IP和端口
|
||||
self.server.bind(
|
||||
("127.0.0.1", 8899)
|
||||
)
|
||||
|
||||
# 3. 监听客户端的连接
|
||||
self.server.listen(100) # 指定最大连接数量100
|
||||
|
||||
def main(self):
|
||||
while True:
|
||||
# 4. 等待客户端的连接
|
||||
client_sock, addr = self.server.accept()
|
||||
Thread(target=self.handler_request, args=(client_sock,)).start()
|
||||
|
||||
def handler_request(self, client_sock):
|
||||
content = ""
|
||||
while True:
|
||||
res = client_sock.recv(1024)
|
||||
if res:
|
||||
content += res.decode()
|
||||
if len(res) < 1024:
|
||||
break
|
||||
print(f"客户端发送过来的数据:{content}")
|
||||
|
||||
# 解析HTTP请求报文
|
||||
request_data = self.parser_request_data(content)
|
||||
if request_data["path"] == "/login":
|
||||
body = login()
|
||||
# 5. 给客户端回复数据 (返回的数据要以http报文的格式返回)
|
||||
header = "HTTP/1.1 200 OK\r\n"
|
||||
header += "Content-Type: text/html;charset=utf-8\r\n"
|
||||
header += "\r\n\r\n"
|
||||
data = header + body
|
||||
elif request_data["path"] == "/report":
|
||||
body = report()
|
||||
# 5. 给客户端回复数据 (返回的数据要以http报文的格式返回)
|
||||
header = "HTTP/1.1 200 OK\r\n"
|
||||
header += "Content-Type: text/html;charset=utf-8\r\n"
|
||||
header += "\r\n\r\n"
|
||||
data = header + body
|
||||
elif request_data["path"] == "/register":
|
||||
body = register()
|
||||
# 5. 给客户端回复数据 (返回的数据要以http报文的格式返回)
|
||||
header = "HTTP/1.1 200 OK\r\n"
|
||||
header += "Content-Type: text/html;charset=utf-8\r\n"
|
||||
header += "\r\n\r\n"
|
||||
data = header + body
|
||||
else:
|
||||
body = "404页面不存在"
|
||||
# 5. 给客户端回复数据 (返回的数据要以http报文的格式返回)
|
||||
header = "HTTP/1.1 404 NOT FOUND \r\n"
|
||||
header += "Content-Type: text/html;charset=utf-8\r\n"
|
||||
header += "\r\n\r\n"
|
||||
data = header + body
|
||||
|
||||
client_sock.send(data.encode())
|
||||
client_sock.close()
|
||||
|
||||
def parser_request_data(self, content):
|
||||
"""解析HTTP请求报文"""
|
||||
# 按行进行分割
|
||||
lines = content.splitlines()
|
||||
# 请求方法
|
||||
method = re.search("^(.+?) ", lines[0]).group(1)
|
||||
# 请求路径
|
||||
path = re.search(" (.+?) ", lines[0]).group(1)
|
||||
# 请求头
|
||||
headers = content.split("\r\n\r\n")[0].splitlines()[1:]
|
||||
headers = {item.split(": ")[0]: item.split(": ")[1] for item in headers}
|
||||
# 请求参数
|
||||
body = content.split("\r\n\r\n")[1]
|
||||
params = {}
|
||||
if headers.get("Content-Type"):
|
||||
# 判断是否为表单参数
|
||||
if headers.get("Content-Type") == "application/json":
|
||||
params["json"] = json.loads(body)
|
||||
# 判断是否为表单参数
|
||||
elif headers.get("Content-Type") == "application/X-www-form-urlencoded":
|
||||
params["data"] = {i.split("=")[0]: i.split("=")[1] for i in body.split("&")}
|
||||
return dict(method=method, path=path, headers=headers, params=params)
|
||||
|
||||
|
||||
def report():
|
||||
return open("report.html", "r", encoding="utf-8").read()
|
||||
|
||||
|
||||
def login():
|
||||
return "登录页面"
|
||||
|
||||
|
||||
def register():
|
||||
return "注册页面"
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
HttpServer().main()
|
||||
|
|
@ -0,0 +1,118 @@
|
|||
"""
|
||||
======================================
|
||||
Project:py08chy
|
||||
File:work2.py
|
||||
IDE:PyCharm
|
||||
Author: Flora.Chen
|
||||
Time: 2021/6/16 0:15
|
||||
======================================
|
||||
"""
|
||||
"""
|
||||
2、扩展要求1:实现同一个路径,不同的请求方法,返回不同的数据
|
||||
思路提示:判断请求的方法,返回不同的数据
|
||||
"""
|
||||
import json
|
||||
import re
|
||||
from socket import socket, AF_INET, SOCK_STREAM
|
||||
from threading import Thread
|
||||
|
||||
|
||||
class HttpServer:
|
||||
def __init__(self):
|
||||
# 1. 创建一个TCP套接字
|
||||
self.server = socket(AF_INET, SOCK_STREAM)
|
||||
|
||||
# 2. 绑定IP和端口
|
||||
self.server.bind(
|
||||
("127.0.0.1", 8899)
|
||||
)
|
||||
|
||||
# 3. 监听客户端的连接
|
||||
self.server.listen(100) # 指定最大连接数量100
|
||||
|
||||
def main(self):
|
||||
while True:
|
||||
# 4. 等待客户端的连接
|
||||
client_sock, addr = self.server.accept()
|
||||
Thread(target=self.handler_request, args=(client_sock,)).start()
|
||||
|
||||
def handler_request(self, client_sock):
|
||||
content = ""
|
||||
while True:
|
||||
res = client_sock.recv(1024)
|
||||
if res:
|
||||
content += res.decode()
|
||||
if len(res) < 1024:
|
||||
break
|
||||
print(f"客户端发送过来的数据:{content}")
|
||||
|
||||
# 解析HTTP请求报文
|
||||
request_data = self.parser_request_data(content)
|
||||
method = request_data["method"]
|
||||
if request_data["path"] == "/login":
|
||||
if method == "GET":
|
||||
body = "登录页面"
|
||||
else:
|
||||
body = json.dumps(request_data["params"])
|
||||
# 5. 给客户端回复数据 (返回的数据要以http报文的格式返回)
|
||||
header = "HTTP/1.1 200 OK\r\n"
|
||||
header += "Content-Type: text/html;charset=utf-8\r\n"
|
||||
header += "\r\n\r\n"
|
||||
data = header + body
|
||||
elif request_data["path"] == "/report":
|
||||
if method == "GET":
|
||||
body = "报告页面"
|
||||
else:
|
||||
body = json.dumps(request_data["params"])
|
||||
# 5. 给客户端回复数据 (返回的数据要以http报文的格式返回)
|
||||
header = "HTTP/1.1 200 OK\r\n"
|
||||
header += "Content-Type: text/html;charset=utf-8\r\n"
|
||||
header += "\r\n\r\n"
|
||||
data = header + body
|
||||
elif request_data["path"] == "/register":
|
||||
if method == "GET":
|
||||
body = "注册页面"
|
||||
else:
|
||||
body = json.dumps(request_data["params"])
|
||||
# 5. 给客户端回复数据 (返回的数据要以http报文的格式返回)
|
||||
header = "HTTP/1.1 200 OK\r\n"
|
||||
header += "Content-Type: text/html;charset=utf-8\r\n"
|
||||
header += "\r\n\r\n"
|
||||
data = header + body
|
||||
else:
|
||||
body = "404页面不存在"
|
||||
# 5. 给客户端回复数据 (返回的数据要以http报文的格式返回)
|
||||
header = "HTTP/1.1 404 NOT FOUND \r\n"
|
||||
header += "Content-Type: text/html;charset=utf-8\r\n"
|
||||
header += "\r\n\r\n"
|
||||
data = header + body
|
||||
|
||||
client_sock.send(data.encode())
|
||||
client_sock.close()
|
||||
|
||||
def parser_request_data(self, content):
|
||||
"""解析HTTP请求报文"""
|
||||
# 按行进行分割
|
||||
lines = content.splitlines()
|
||||
# 请求方法
|
||||
method = re.search("^(.+?) ", lines[0]).group(1)
|
||||
# 请求路径
|
||||
path = re.search(" (.+?) ", lines[0]).group(1)
|
||||
# 请求头
|
||||
headers = content.split("\r\n\r\n")[0].splitlines()[1:]
|
||||
headers = {item.split(": ")[0]: item.split(": ")[1] for item in headers}
|
||||
# 请求参数
|
||||
body = content.split("\r\n\r\n")[1]
|
||||
params = {}
|
||||
if headers.get("Content-Type"):
|
||||
# 判断是否为表单参数
|
||||
if headers.get("Content-Type") == "application/json":
|
||||
params["json"] = json.loads(body)
|
||||
# 判断是否为表单参数
|
||||
elif headers.get("Content-Type") == "application/x-www-form-urlencoded":
|
||||
params["data"] = {i.split("=")[0]: i.split("=")[1] for i in body.split("&")}
|
||||
return dict(method=method, path=path, headers=headers, params=params)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
HttpServer().main()
|
|
@ -0,0 +1,129 @@
|
|||
"""
|
||||
======================================
|
||||
Project:py08chy
|
||||
File:work3.py
|
||||
IDE:PyCharm
|
||||
Author: Flora.Chen
|
||||
Time: 2021/6/16 0:16
|
||||
======================================
|
||||
"""
|
||||
"""
|
||||
3、扩展要求2:再上面的两个要求的基础实现一个求登录接口(不计分,做不出来没关系):
|
||||
接口路径: / user / login
|
||||
请求方法: post
|
||||
参数:
|
||||
user: 用户名
|
||||
pwd: 登录密码
|
||||
请求参数类型:Content - Type: application / json
|
||||
返回数据示例:
|
||||
账号密码正确返回:{'code': 1, 'msg': '登录成功'}
|
||||
账号密码错误返回:{'code': 0, 'msg': '账号密码有误'}
|
||||
其他异常情况(参数为空, 参数格式错误等等),返回按上述数据格式,内容不做限制
|
||||
|
||||
思路提示:
|
||||
1、从请求数据中提取:请求方法,请求路径,请求头,请求参数
|
||||
2、先判断请求路径,如果是 / user / login
|
||||
3、在判断请求方法
|
||||
4、在判断请求参数
|
||||
5、校验参数中的账号密码
|
||||
"""
|
||||
import json
|
||||
import re
|
||||
from socket import socket, AF_INET, SOCK_STREAM
|
||||
from threading import Thread
|
||||
|
||||
|
||||
class HttpServer:
|
||||
def __init__(self):
|
||||
# 1. 创建一个TCP套接字
|
||||
self.server = socket(AF_INET, SOCK_STREAM)
|
||||
|
||||
# 2. 绑定IP和端口
|
||||
self.server.bind(
|
||||
("127.0.0.1", 8899)
|
||||
)
|
||||
|
||||
# 3. 监听客户端的连接
|
||||
self.server.listen(100) # 指定最大连接数量100
|
||||
|
||||
def main(self):
|
||||
while True:
|
||||
# 4. 等待客户端的连接
|
||||
client_sock, addr = self.server.accept()
|
||||
Thread(target=self.handler_request, args=(client_sock,)).start()
|
||||
|
||||
def handler_request(self, client_sock):
|
||||
content = ""
|
||||
while True:
|
||||
res = client_sock.recv(1024)
|
||||
if res:
|
||||
content += res.decode()
|
||||
if len(res) <= 1024:
|
||||
break
|
||||
print(f"客户端发送过来的数据:{content}")
|
||||
|
||||
# 解析HTTP请求报文
|
||||
request_data = self.parser_request_data(content)
|
||||
method = request_data["method"]
|
||||
params = request_data["params"]
|
||||
if request_data["path"] == "/user/login":
|
||||
body = json.dumps(login(method, params))
|
||||
# 5. 给客户端回复数据 (返回的数据要以http报文的格式返回)
|
||||
header = "HTTP/1.1 200 OK\r\n"
|
||||
header += "Content-Type: text/html;charset=utf-8\r\n"
|
||||
header += "\r\n\r\n"
|
||||
data = header + body
|
||||
else:
|
||||
body = "404页面不存在"
|
||||
# 5. 给客户端回复数据 (返回的数据要以http报文的格式返回)
|
||||
header = "HTTP/1.1 404 NOT FOUND \r\n"
|
||||
header += "Content-Type: text/html;charset=utf-8\r\n"
|
||||
header += "\r\n\r\n"
|
||||
data = header + body
|
||||
|
||||
client_sock.send(data.encode())
|
||||
client_sock.close()
|
||||
|
||||
def parser_request_data(self, content):
|
||||
"""解析HTTP请求报文"""
|
||||
# 按行进行分割
|
||||
lines = content.splitlines()
|
||||
# 请求方法
|
||||
method = re.search("^(.+?) ", lines[0]).group(1)
|
||||
# 请求路径
|
||||
path = re.search(" (.*?) ", lines[0]).group(1)
|
||||
# 请求头
|
||||
headers = content.split("\r\n\r\n")[0].splitlines()[1:]
|
||||
headers = {item.split(": ")[0]: item.split(": ")[1] for item in headers}
|
||||
# 请求参数
|
||||
body = content.split("\r\n\r\n")[1]
|
||||
params = {}
|
||||
if headers.get("Content-Type"):
|
||||
# 判断是否为表单参数
|
||||
if headers.get("Content-Type") == "application/json":
|
||||
params["data"] = json.loads(body)
|
||||
# 判断是否为表单参数
|
||||
elif headers.get("Content-Type") == "application/x-www-form-urlencoded":
|
||||
params["data"] = {i.split("=")[0]: i.split("=")[1] for i in body.split("&")}
|
||||
return dict(method=method, path=path, headers=headers, params=params)
|
||||
|
||||
|
||||
|
||||
def login(method, params):
|
||||
user = "flora"
|
||||
pwd = "123456"
|
||||
if method == "GET":
|
||||
body = "登录页面"
|
||||
else:
|
||||
if params["data"]["user"] and params["data"]["pwd"]:
|
||||
if params["data"]["user"] == user and params["data"]["pwd"] == pwd:
|
||||
body = {'code': 1, 'msg': '登录成功'}
|
||||
else:
|
||||
body = {'code': 0, 'msg': '账号密码有误'}
|
||||
else:
|
||||
body = {'code': 2, 'msg': 'user或pwd不能为空'}
|
||||
return body
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
HttpServer().main()
|
|
@ -0,0 +1,109 @@
|
|||
"""
|
||||
============================
|
||||
author:MuSen
|
||||
time:2019/6/21
|
||||
E-mail:3247119728@qq.com
|
||||
============================
|
||||
"""
|
||||
|
||||
from flask import Flask, request, jsonify
|
||||
|
||||
from flask_cors import CORS
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
# 测试数据
|
||||
user_info = {"user": 'python01', 'pwd': 'lemonban'}
|
||||
|
||||
project_data = {"code": "1",
|
||||
"data": [{"title": "前程贷", "id": "1001"},
|
||||
{"title": "智慧金融", "id": "1002"},
|
||||
{"title": "生鲜到家", "id": "1003"},
|
||||
{"title": "柠檬班app", "id": "1004"}],
|
||||
"msg": "四个项目",
|
||||
}
|
||||
# 接口数据
|
||||
interface_data = {
|
||||
"1001": {"code": "1",
|
||||
"data": [{"name": "前程贷登录1001"},
|
||||
{"name": "前程贷注册1001"}],
|
||||
"msg": "2个接口", },
|
||||
|
||||
"1002": {"code": "1",
|
||||
"data": [{"name": "智慧-登录1002"},
|
||||
{"name": "智慧-注册1002"},
|
||||
{"name": "智慧-贷款1004"}, ],
|
||||
"msg": "3个接口", },
|
||||
|
||||
"1003": {"code": "1",
|
||||
"data": [{"name": "生鲜-登录1003"},
|
||||
{"name": "生鲜-注册1003"},
|
||||
{"name": "生鲜下单1003"}, ],
|
||||
"msg": "3个接口", },
|
||||
|
||||
"1004": {"code": "1",
|
||||
"data": [{"name": "app登录1004"},
|
||||
{"name": "app注册1004"},
|
||||
{"name": "app报名1004"},
|
||||
{"name": "app缴费1004"},
|
||||
],
|
||||
"msg": "4个接口", },
|
||||
}
|
||||
|
||||
|
||||
# 登录
|
||||
@app.route('/api/user/login', methods=['post'])
|
||||
def login():
|
||||
"""
|
||||
接口地址:http://127.0.0.1:5000/api/user/login
|
||||
请求方法:post
|
||||
参数: {user:账号,pwd:密码}
|
||||
参数类型:表单 、json都支持
|
||||
返回:登录之后的结果
|
||||
"""
|
||||
data = request.form or request.json
|
||||
# 判断账号,密码是否正确
|
||||
if user_info.get('user') == data.get('user') and user_info.get('pwd') == data.get('pwd'):
|
||||
return jsonify({'code': "1", "data": None, "msg": "成功"})
|
||||
else:
|
||||
return jsonify({'code': "0", "data": None, "msg": "账号或密码有误"})
|
||||
|
||||
|
||||
# 获取项目列表
|
||||
@app.route('/api/projects', methods=['get'])
|
||||
def pro_list():
|
||||
"""
|
||||
接口地址:http://127.0.0.1:5000/api/projects
|
||||
请求方法:get
|
||||
参数:无
|
||||
返回所有的项目
|
||||
:return:
|
||||
"""
|
||||
return jsonify(project_data)
|
||||
|
||||
|
||||
# 获取接口列表
|
||||
@app.route('/api/interface', methods=['get'])
|
||||
def interface():
|
||||
"""
|
||||
接口地址:http://127.0.0.1:5000/api/interface
|
||||
请求方法:get
|
||||
参数: id(项目的id)
|
||||
参数类型:查询字符串
|
||||
返回:该项目的所有接口
|
||||
"""
|
||||
inter_id = request.args.get('id')
|
||||
|
||||
if inter_id:
|
||||
res_data = interface_data.get(inter_id)
|
||||
if res_data:
|
||||
return jsonify(res_data)
|
||||
else:
|
||||
return jsonify({"code": "0", "data": None, "msg": "没有该项目"})
|
||||
else:
|
||||
return jsonify({"code": "0", "data": None, "msg": "参数id不能为空"})
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
cors = CORS(app)
|
||||
app.run(debug=True)
|
|
@ -0,0 +1,106 @@
|
|||
import json
|
||||
import re
|
||||
# import pprint
|
||||
# import requests
|
||||
# from common.requests_handler import Request_handler
|
||||
|
||||
data = None
|
||||
url = None
|
||||
|
||||
|
||||
def fiddler_requests():
|
||||
"""
|
||||
fiddler请求作为参数通过正则匹配,数据处理转成requests请求
|
||||
:param fiddler_info:
|
||||
:return:
|
||||
"""
|
||||
with open(r"./make_demo.txt", "r", encoding="utf-8") as f:
|
||||
fiddler_info = f.read()
|
||||
params_type = {}
|
||||
headers = {}
|
||||
if re.search('(.*?) http', fiddler_info) == None:
|
||||
print("请先复制fiddler请求信息在make_demo.txt文件中")
|
||||
return
|
||||
method = re.search('(.*?) http', fiddler_info).group().split(" ")[0]
|
||||
all_url = re.search(f'{method}(.*?) HTTP/', fiddler_info).group().split(" ")[1]
|
||||
request_line = fiddler_info.split("\n\n")[0]
|
||||
request_headers = request_line.split('\n')[1:]
|
||||
for i in request_headers:
|
||||
info = "".join(i.split()).split(":")
|
||||
for header_keys in ["Content-Length", "Host", "Connection", "accept", "User-Agent", "Content-Type", "Origin",
|
||||
"Referer", "Accept-Encoding", "Cookie", "Accept-Language"]:
|
||||
if info[0] == header_keys:
|
||||
headers[info[0]] = info[1]
|
||||
host_lis = re.compile(r'Host: (.*?)\n', re.DOTALL).findall(fiddler_info)
|
||||
if host_lis != []:
|
||||
host = host_lis[0]
|
||||
url = all_url.split(host)[1]
|
||||
data_info = re.compile(r'\n\n(.*)', re.DOTALL)
|
||||
re_data = data_info.findall(fiddler_info)
|
||||
if re_data != []:
|
||||
re_data[0] = re_data[0].replace("null", '1')
|
||||
print(json.loads(re_data[0]))
|
||||
data_dic = json.loads(re_data[0])
|
||||
print(type(data_dic))
|
||||
if isinstance(data_dic, dict):
|
||||
re_data = data_dic
|
||||
if headers["Content-Type"] != None:
|
||||
params_type["Content-Type"] = headers["Content-Type"]
|
||||
if params_type["Content-Type"] == "application/json":
|
||||
params_type["type"] = "json"
|
||||
elif params_type["Content-Type"].find("text/html") != -1 or params_type["Content-Type"].find(
|
||||
"form") != -1:
|
||||
params_type["type"] = "form"
|
||||
else:
|
||||
re_data = None
|
||||
headers = json.dumps(headers)
|
||||
re_data = json.dumps(re_data)
|
||||
case_name = "test_" + url.split("/")[-2] + "_" + url.split("/")[-1]
|
||||
make_code = f"""
|
||||
#如果需要读excel测试用例传入info参数,需要admin账号鉴权cookie传入admin_cookie
|
||||
def {case_name}(info, admin_cookie):
|
||||
allure.dynamic.feature(info["feature"]) # allure报告中一级分类
|
||||
allure.dynamic.story(info["story"]) # allure报告中二级分类
|
||||
allure.dynamic.title(info["title"]) # allure报告中用例名字
|
||||
url = "{url}" # 接口路由
|
||||
headers = {headers} # 接口请求头
|
||||
json = {re_data} # 接口请求数据
|
||||
expect_code = info["expected"]["code"] # 预期结果
|
||||
# 构建接口请求
|
||||
res = Request_handler("{method}", r"{url}", {re_data}, {headers}).post({params_type["type"]})
|
||||
# --------------------分界线,下边的根据响应结果断言-----------------------------------------
|
||||
res_code = jsonpath(resp, "$..code")[0] # 响应业务码
|
||||
assert_equal(res_code, expect_code)
|
||||
"""
|
||||
|
||||
with open("make_demo.txt", "w", encoding="utf8") as f:
|
||||
f.write(make_code)
|
||||
print("""成功生成测试脚本""")
|
||||
print(make_code)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
fiddler_info = """
|
||||
POST https://testabb.admin.chargedot.com/api/v1//order/order/search HTTP/1.1
|
||||
Host: testabb.admin.chargedot.com
|
||||
Connection: keep-alive
|
||||
Content-Length: 142
|
||||
sec-ch-ua: " Not;A Brand";v="99", "Google Chrome";v="91", "Chromium";v="91"
|
||||
Accept: application/json, text/plain, */*
|
||||
X-CSRF-TOKEN: cSSFg1JULveUmvC91vmUU14HLiN0Pi8z1PrApprn
|
||||
sec-ch-ua-mobile: ?0
|
||||
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.106 Safari/537.36
|
||||
Content-Type: application/json
|
||||
Origin: https://testabb.admin.chargedot.com
|
||||
Sec-Fetch-Site: same-origin
|
||||
Sec-Fetch-Mode: cors
|
||||
Sec-Fetch-Dest: empty
|
||||
Referer: https://testabb.admin.chargedot.com/
|
||||
Accept-Encoding: gzip, deflate, br
|
||||
Accept-Language: zh-CN,zh;q=0.9
|
||||
Cookie: juno_pc_session=TgfjYtMcAmTrT1hPgyqJ5BeWWW5Z807kp7zGotm1
|
||||
|
||||
{"status":0,"query":"","offset":0,"limit":15,"downexcel":0,"deviceId":null,"nologin":9999,"_token":"cSSFg1JULveUmvC91vmUU14HLiN0Pi8z1PrApprn"}
|
||||
"""
|
||||
|
||||
fiddler_requests()
|
|
@ -0,0 +1,16 @@
|
|||
|
||||
#如果需要读excel测试用例传入info参数,需要admin账号鉴权cookie传入admin_cookie
|
||||
def test_order_search(info, admin_cookie):
|
||||
allure.dynamic.feature(info["feature"]) # allure报告中一级分类
|
||||
allure.dynamic.story(info["story"]) # allure报告中二级分类
|
||||
allure.dynamic.title(info["title"]) # allure报告中用例名字
|
||||
url = "/api/v1//order/order/search" # 接口路由
|
||||
headers = {"Host": "testabb.admin.chargedot.com", "Connection": "keep-alive", "Content-Length": "142", "User-Agent": "Mozilla/5.0(WindowsNT10.0;Win64;x64)AppleWebKit/537.36(KHTML,likeGecko)Chrome/91.0.4472.106Safari/537.36", "Content-Type": "application/json", "Origin": "https", "Referer": "https", "Accept-Encoding": "gzip,deflate,br", "Accept-Language": "zh-CN,zh;q=0.9", "Cookie": "juno_pc_session=TgfjYtMcAmTrT1hPgyqJ5BeWWW5Z807kp7zGot"} # 接口请求头
|
||||
json = {"status": 0, "query": "", "offset": 0, "limit": 15, "downexcel": 0, "deviceId": 1, "nologin": 9999, "_token": "cSSFg1JULveUmvC91vmUU14HLiN0Pi8z1PrApprn"} # 接口请求数据
|
||||
expect_code = info["expected"]["code"] # 预期结果
|
||||
# 构建接口请求
|
||||
res = Request_handler("POST", r"/api/v1//order/order/search", {"status": 0, "query": "", "offset": 0, "limit": 15, "downexcel": 0, "deviceId": 1, "nologin": 9999, "_token": "cSSFg1JULveUmvC91vmUU14HLiN0Pi8z1PrApprn"}, {"Host": "testabb.admin.chargedot.com", "Connection": "keep-alive", "Content-Length": "142", "User-Agent": "Mozilla/5.0(WindowsNT10.0;Win64;x64)AppleWebKit/537.36(KHTML,likeGecko)Chrome/91.0.4472.106Safari/537.36", "Content-Type": "application/json", "Origin": "https", "Referer": "https", "Accept-Encoding": "gzip,deflate,br", "Accept-Language": "zh-CN,zh;q=0.9", "Cookie": "juno_pc_session=TgfjYtMcAmTrT1hPgyqJ5BeWWW5Z807kp7zGot"}).post(json)
|
||||
# --------------------分界线,下边的根据响应结果断言-----------------------------------------
|
||||
res_code = jsonpath(resp, "$..code")[0] # 响应业务码
|
||||
assert_equal(res_code, expect_code)
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
"""
|
||||
======================================
|
||||
Author: Flora.Chen
|
||||
Time: 2021/5/27 22:41
|
||||
~ _ ~ ~ _ ~ ~ _ ~ ~ _ ~ ~ _ ~ ~ _ ~
|
||||
======================================
|
||||
"""
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,41 @@
|
|||
"""
|
||||
======================================
|
||||
Author: Flora.Chen
|
||||
Time: 2021/5/27 22:41
|
||||
~ _ ~ ~ _ ~ ~ _ ~ ~ _ ~ ~ _ ~ ~ _ ~
|
||||
======================================
|
||||
"""
|
||||
|
||||
|
||||
def ddt(cls):
|
||||
"""
|
||||
用来装饰测试类
|
||||
:param cls:
|
||||
:return:
|
||||
"""
|
||||
for name, value in list(cls.__dict__.items()):
|
||||
if hasattr(value, "DATA"):
|
||||
data = getattr(value, "DATA")
|
||||
for i in data:
|
||||
setattr(cls, f"test_01_{i}", value)
|
||||
print(name, value)
|
||||
return cls
|
||||
|
||||
|
||||
def data(data):
|
||||
"""
|
||||
用来装饰测试方法
|
||||
:param data: 测试数据
|
||||
:return:
|
||||
"""
|
||||
|
||||
def wrapper(func):
|
||||
"""
|
||||
接收测试方法
|
||||
:param func:
|
||||
:return:
|
||||
"""
|
||||
setattr(func, "DATA", data)
|
||||
return func
|
||||
|
||||
return wrapper
|
|
@ -0,0 +1,558 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>测试报告</title>
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.5.0/dist/css/bootstrap.min.css"
|
||||
integrity="sha384-9aIt2nRpC12Uk9gS9baDl411NQApFmC26EwAOH8WgZl5MYYxFfc+NcPb1dKGj7Sk" crossorigin="anonymous">
|
||||
<!-- 页面样式-->
|
||||
<style type="text/css">
|
||||
/*标题样式*/
|
||||
.title {
|
||||
width: auto;
|
||||
height: 60px;
|
||||
text-align: center;
|
||||
font: bolder 38px/60px "Microsoft YaHei UI";
|
||||
}
|
||||
|
||||
/*汇总信息样式*/
|
||||
.summary {
|
||||
width: 90%;
|
||||
position: absolute;
|
||||
top: 120px;
|
||||
margin-left: 5%;
|
||||
|
||||
}
|
||||
|
||||
.text-left {
|
||||
font: bolder 20px/30px "Microsoft YaHei UI";
|
||||
}
|
||||
|
||||
.left {
|
||||
width: 50%;
|
||||
float: left;
|
||||
|
||||
}
|
||||
|
||||
.right {
|
||||
width: 50%;
|
||||
float: right;
|
||||
|
||||
}
|
||||
|
||||
.desc {
|
||||
float: left;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.list-group-item span {
|
||||
font: normal 16px/38px "Microsoft YaHei UI";
|
||||
padding: 30px;
|
||||
|
||||
}
|
||||
|
||||
.list-group-item {
|
||||
position: relative;
|
||||
display: block;
|
||||
padding: .4rem 1.25rem;
|
||||
background-color: #fff;
|
||||
border: 1px solid rgba(0, 0, 0, .125);
|
||||
}
|
||||
|
||||
/* 执行信息样式 */
|
||||
.test_info {
|
||||
width: 90%;
|
||||
position: absolute;
|
||||
top: 900px;
|
||||
margin-left: 5%;
|
||||
|
||||
color: #28a745 !important;
|
||||
}
|
||||
|
||||
.table td, th {
|
||||
border: solid 2px rgba(9, 122, 51, 0.11) !important;
|
||||
padding: 0;
|
||||
line-height: 40px;
|
||||
text-align: center;
|
||||
|
||||
}
|
||||
|
||||
|
||||
select {
|
||||
border: 0;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
height: 2em;
|
||||
width: 8em;
|
||||
margin-left: 2em;
|
||||
}
|
||||
|
||||
option {
|
||||
text-align: center;
|
||||
height: 36px;
|
||||
font: none 18px/36px "Microsoft YaHei UI";
|
||||
color: #28a745 !important;
|
||||
}
|
||||
|
||||
.test_log {
|
||||
background: rgba(163, 171, 189, 0.15);
|
||||
width: 100%;
|
||||
height: 50px;
|
||||
border-top: none;
|
||||
border-bottom: none;
|
||||
display: none;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.test_log td {
|
||||
text-align: left;
|
||||
height: 30px;
|
||||
margin: 0;
|
||||
padding-left: 3em;
|
||||
padding-right:3em;
|
||||
font: none 18px/24px "Microsoft YaHei UI";
|
||||
color: #9e141a;
|
||||
}
|
||||
|
||||
pre {
|
||||
margin: 0;
|
||||
white-space: pre-wrap;
|
||||
white-space: -moz-pre-wrap;
|
||||
white-space: -o-pre-wrap;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
/* 测试图表显示*/
|
||||
.char {
|
||||
width: 90%;
|
||||
position: absolute;
|
||||
top: 450px;
|
||||
margin-left: 5%;
|
||||
color: #28a745 !important;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<!--报告标题-->
|
||||
<div class="title text-success">
|
||||
<div class="shadow-lg p-3 mb-5 bg-white rounded">测试报告</div>
|
||||
</div>
|
||||
|
||||
<!--汇总信息-->
|
||||
<div class="summary">
|
||||
<p class="text-left text-success">测试结果汇总</p>
|
||||
<div class="left">
|
||||
<ul class="list-group">
|
||||
<li class="list-group-item">
|
||||
<button type="button" class="btn btn-success">测试人员</button>
|
||||
<span class="text-dark">测试员</span>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<button type="button" class="btn btn-success">开始时间</button>
|
||||
<span class="text-dark">2021-05-27 23:03:45</span>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<button type="button" class="btn btn-success">执行时间</button>
|
||||
<span class="text-dark">0.00 S</span>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<button type="button" class="btn btn-success">用例总数</button>
|
||||
<span class="text-dark">5</span>
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
<div class="right">
|
||||
<ul class="list-group">
|
||||
|
||||
<li class="list-group-item">
|
||||
<button type="button" class="btn btn-success">成功用例</button>
|
||||
<span class="text-success">5</span>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<button type="button" class="btn btn-danger">失败用例</button>
|
||||
<span class="text-warning">0</span>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<button type="button" class="btn btn-warning">错误用例</button>
|
||||
<span class="text-danger">0</span>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<button type="button" class="btn btn-secondary">跳过用例</button>
|
||||
<span class="text-secondary">0</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="desc">
|
||||
<ul class="list-group">
|
||||
<li class="list-group-item">
|
||||
<button type="button" class="btn btn-success">描述信息</button>
|
||||
<span class="text-secondary">XX项目测试生成的报告</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<!--测试图表-->
|
||||
<div class="char">
|
||||
<p class="text-left text-success">图表展示</p>
|
||||
<div id="char2" style="width: 49%;height: 400px;float: left"></div>
|
||||
<div id="char" style="width: 49%;height: 400px ;float: left"></div>
|
||||
</div>
|
||||
|
||||
<!--详细信息-->
|
||||
<div class="test_info">
|
||||
|
||||
<p class="text-left text-success">详细信息</p>
|
||||
<div class="table_data">
|
||||
|
||||
<table class="table">
|
||||
<thead class="bg-success text-light">
|
||||
<tr>
|
||||
<th scope="col" style="width: 5%;padding: 0">编号</th>
|
||||
<th scope="col" style="width: 20%;padding: 0">
|
||||
<span>测试类</span>
|
||||
<select id="testClass">
|
||||
<option>所有</option>
|
||||
|
||||
<option>TestLogin</option>
|
||||
|
||||
</select>
|
||||
|
||||
</th>
|
||||
|
||||
<th scope="col" style="width: 15%;padding: 0">测试方法</th>
|
||||
<th scope="col" style="width: 20%;padding: 0">用例描述</th>
|
||||
<th scope="col" style="width: 10%;padding: 0">执行时间</th>
|
||||
<th scope="col" style="width: 20%;padding: 0">
|
||||
|
||||
<span>执行结果</span>
|
||||
<select id="testResult">
|
||||
<option>所有</option>
|
||||
<option class="text-success">成功</option>
|
||||
<option class="text-danger">失败</option>
|
||||
<option class="text-warning">错误</option>
|
||||
<option class="text-info">跳过</option>
|
||||
</select>
|
||||
|
||||
</th>
|
||||
<th scope="col" style="width: 10%;padding: 0">详细信息</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
<tr>
|
||||
<td>1</td>
|
||||
<td class="TestLogin">TestLogin</td>
|
||||
<td>test_01_1</td>
|
||||
<td>None</td>
|
||||
<td>0.0s</td>
|
||||
|
||||
<td class="text-success">成功</td>
|
||||
|
||||
<td>
|
||||
<button type="button" class="btn btn-success btn_info">查看详情</button>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr class="test_log">
|
||||
|
||||
<td colspan="7" class="small text-muted" style=" word-wrap:break-word; word-break:break-all">
|
||||
|
||||
|
||||
<pre>测试方法
|
||||
test_01_1 (myddtR.test_case.TestLogin)执行——>【通过】
|
||||
</pre>
|
||||
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>2</td>
|
||||
<td class="TestLogin">TestLogin</td>
|
||||
<td>test_01_2</td>
|
||||
<td>None</td>
|
||||
<td>0.0s</td>
|
||||
|
||||
<td class="text-success">成功</td>
|
||||
|
||||
<td>
|
||||
<button type="button" class="btn btn-success btn_info">查看详情</button>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr class="test_log">
|
||||
|
||||
<td colspan="7" class="small text-muted" style=" word-wrap:break-word; word-break:break-all">
|
||||
|
||||
|
||||
<pre>测试方法
|
||||
test_01_2 (myddtR.test_case.TestLogin)执行——>【通过】
|
||||
</pre>
|
||||
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>3</td>
|
||||
<td class="TestLogin">TestLogin</td>
|
||||
<td>test_01_3</td>
|
||||
<td>None</td>
|
||||
<td>0.0s</td>
|
||||
|
||||
<td class="text-success">成功</td>
|
||||
|
||||
<td>
|
||||
<button type="button" class="btn btn-success btn_info">查看详情</button>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr class="test_log">
|
||||
|
||||
<td colspan="7" class="small text-muted" style=" word-wrap:break-word; word-break:break-all">
|
||||
|
||||
|
||||
<pre>测试方法
|
||||
test_01_3 (myddtR.test_case.TestLogin)执行——>【通过】
|
||||
</pre>
|
||||
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>4</td>
|
||||
<td class="TestLogin">TestLogin</td>
|
||||
<td>test_01_4</td>
|
||||
<td>None</td>
|
||||
<td>0.0s</td>
|
||||
|
||||
<td class="text-success">成功</td>
|
||||
|
||||
<td>
|
||||
<button type="button" class="btn btn-success btn_info">查看详情</button>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr class="test_log">
|
||||
|
||||
<td colspan="7" class="small text-muted" style=" word-wrap:break-word; word-break:break-all">
|
||||
|
||||
|
||||
<pre>测试方法
|
||||
test_01_4 (myddtR.test_case.TestLogin)执行——>【通过】
|
||||
</pre>
|
||||
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>5</td>
|
||||
<td class="TestLogin">TestLogin</td>
|
||||
<td>test_login</td>
|
||||
<td>None</td>
|
||||
<td>0.0s</td>
|
||||
|
||||
<td class="text-success">成功</td>
|
||||
|
||||
<td>
|
||||
<button type="button" class="btn btn-success btn_info">查看详情</button>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr class="test_log">
|
||||
|
||||
<td colspan="7" class="small text-muted" style=" word-wrap:break-word; word-break:break-all">
|
||||
|
||||
|
||||
<pre>测试方法
|
||||
test_login (myddtR.test_case.TestLogin)执行——>【通过】
|
||||
</pre>
|
||||
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div style="height: 200px"></div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.slim.min.js"
|
||||
integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj"
|
||||
crossorigin="anonymous"></script>
|
||||
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/echarts/dist/echarts.min.js"></script>
|
||||
|
||||
<script>
|
||||
var tbodyTr = $('tbody tr');
|
||||
var testResult = $("#testResult");
|
||||
var testClass = $("#testClass");
|
||||
<!-- 用例执行详细信息显示切换-->
|
||||
$(".btn_info").click(function () {
|
||||
$(this).parent().parent().next().toggle();
|
||||
|
||||
});
|
||||
// 当选择用例类之后触发
|
||||
testClass.change(function () {
|
||||
var cls = $(this).val();
|
||||
var res = testResult.val();
|
||||
elementDisplay(cls, res);
|
||||
sort()
|
||||
});
|
||||
testResult.change(function () {
|
||||
var res = $(this).val();
|
||||
var cls = testClass.val();
|
||||
elementDisplay(cls, res);
|
||||
sort()
|
||||
});
|
||||
|
||||
function elementDisplay(cls, res) {
|
||||
// 用例数据的显示
|
||||
if (cls === "所有") {
|
||||
if (res === "所有") {
|
||||
tbodyTr.has('button').show();
|
||||
} else if (res === '成功') {
|
||||
tbodyTr.hide();
|
||||
tbodyTr.has('button').has('.text-success').show()
|
||||
|
||||
} else if (res === '失败') {
|
||||
tbodyTr.hide();
|
||||
tbodyTr.has('button').has('.text-danger').show()
|
||||
|
||||
} else if (res === '错误') {
|
||||
tbodyTr.hide();
|
||||
tbodyTr.has('button').has('.text-warning').show()
|
||||
|
||||
} else if (res === '跳过') {
|
||||
tbodyTr.hide();
|
||||
tbodyTr.has('button').has('.text-info').show()
|
||||
}
|
||||
} else {
|
||||
if (res === "所有") {
|
||||
tbodyTr.hide();
|
||||
tbodyTr.has('button').has('.' + cls + '').show()
|
||||
} else if (res === '成功') {
|
||||
tbodyTr.hide();
|
||||
tbodyTr.has('button').has('.' + cls + '').has('.text-success').show()
|
||||
} else if (res === '失败') {
|
||||
tbodyTr.hide();
|
||||
tbodyTr.has('button').has('.' + cls + '').has('.text-danger').show()
|
||||
} else if (res === '错误') {
|
||||
tbodyTr.hide();
|
||||
tbodyTr.has('button').has('.' + cls + '').has('.text-warning').show()
|
||||
} else if (res === '跳过') {
|
||||
tbodyTr.hide();
|
||||
tbodyTr.has('button').has('.' + cls + '').has('.text-info').show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function sort() {
|
||||
//重新排列显示序号
|
||||
// 选择所有可以见的tr
|
||||
var visibleTr = tbodyTr.filter(":visible");
|
||||
|
||||
visibleTr.each(function (index, element) {
|
||||
element.firstElementChild.innerHTML = index + 1;
|
||||
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
// 基于准备好的dom,初始化echarts实例
|
||||
var myChart = echarts.init(document.getElementById('char'));
|
||||
var myChart2 = echarts.init(document.getElementById('char2'));
|
||||
// 指定图表的配置项和数据
|
||||
option = {
|
||||
color: ['#00a10a', '#ddb518', 'rgba(204,46,41,0.73)', '#85898c'],
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
formatter: '{a} <br/>{b}: {c} ({d}%)'
|
||||
},
|
||||
legend: {
|
||||
orient: 'vertical',
|
||||
left: 10,
|
||||
data: ['通过', '失败', '错误', '跳过']
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '测试结果',
|
||||
type: 'pie',
|
||||
radius: ['50%', '70%'],
|
||||
avoidLabelOverlap: false,
|
||||
label: {
|
||||
show: false,
|
||||
position: 'center'
|
||||
},
|
||||
emphasis: {
|
||||
label: {
|
||||
show: true,
|
||||
fontSize: '30',
|
||||
fontWeight: 'bold'
|
||||
}
|
||||
},
|
||||
labelLine: {
|
||||
show: false
|
||||
},
|
||||
data: [
|
||||
{value: 5, name: '通过'},
|
||||
{value: 0, name: '失败'},
|
||||
{value: 0, name: '错误'},
|
||||
{value: 0, name: '跳过'}
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
option2 = {
|
||||
tooltip: {
|
||||
formatter: '{a} <br/>{b} : {c}%'
|
||||
},
|
||||
toolbox: {
|
||||
feature: {
|
||||
restore: {},
|
||||
saveAsImage: {}
|
||||
}
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '测试结果',
|
||||
type: 'gauge',
|
||||
detail: {formatter: '100.00%'},
|
||||
data: [{value: '100.00', name: '用例通过率'}],
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: [
|
||||
[0.2, '#c20000'],
|
||||
[0.8, '#ddb518'],
|
||||
[1, '#00a10a']]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
|
||||
myChart2.setOption(option2);
|
||||
// 使用刚指定的配置项和数据显示图表。
|
||||
myChart.setOption(option);
|
||||
</script>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,15 @@
|
|||
"""
|
||||
======================================
|
||||
Author: Flora.Chen
|
||||
Time: 2021/5/27 22:41
|
||||
~ _ ~ ~ _ ~ ~ _ ~ ~ _ ~ ~ _ ~ ~ _ ~
|
||||
======================================
|
||||
"""
|
||||
import unittest
|
||||
import unittestreport
|
||||
from myddtR.test_case import TestLogin
|
||||
|
||||
suite = unittest.defaultTestLoader.loadTestsFromTestCase(TestLogin)
|
||||
|
||||
runner = unittestreport.TestRunner(suite)
|
||||
runner.run()
|
|
@ -0,0 +1,16 @@
|
|||
"""
|
||||
======================================
|
||||
Author: Flora.Chen
|
||||
Time: 2021/5/27 22:42
|
||||
~ _ ~ ~ _ ~ ~ _ ~ ~ _ ~ ~ _ ~ ~ _ ~
|
||||
======================================
|
||||
"""
|
||||
import unittest
|
||||
from myddtR.myddt import ddt, data
|
||||
|
||||
|
||||
@ddt
|
||||
class TestLogin(unittest.TestCase):
|
||||
@data([1, 2, 3, 4])
|
||||
def test_login(self):
|
||||
print("测试方法")
|
|
@ -0,0 +1,116 @@
|
|||
|
||||
import json
|
||||
import os
|
||||
import requests
|
||||
from jsonpath import jsonpath
|
||||
import allure
|
||||
from common.yaml_handler import read_yaml
|
||||
from config import path
|
||||
from middleware.config_handler import Config_handler
|
||||
configdata_path = os.path.join(path.config_path, "config_data.yaml")
|
||||
config_info = read_yaml(configdata_path)
|
||||
host_config = config_info["host"]
|
||||
api_code = config_info["api_code"]
|
||||
|
||||
|
||||
|
||||
class Request_handler:
|
||||
"""
|
||||
二次封装requests
|
||||
:param
|
||||
:return
|
||||
"""
|
||||
def __init__(self, method, path, data, headers):
|
||||
self.url = host_config + path
|
||||
self.headers = headers
|
||||
self.data = data
|
||||
self.method = method
|
||||
|
||||
def post(self, params_type, **kwargs):
|
||||
"""
|
||||
结合allure,日志信息,项目配置化信息。重构post请求,提升易用性
|
||||
:param params_type:
|
||||
:param kwargs:
|
||||
:return:
|
||||
"""
|
||||
try:
|
||||
if self.method in ["post", "POST"]:
|
||||
with allure.step(f"第一步:获取请求数据。\nheaders:{self.headers}\ndata:{self.data}\nurl:{self.url}"):
|
||||
Config_handler.logger.info(f"第一步:获取请求数据。\nheaders:{self.headers}\ndata:{self.data}\nurl:{self.url}")
|
||||
with allure.step(f"第二步:构建{self.method }请求"):
|
||||
Config_handler.logger.info(f"第二步:构建{self.method }请求")
|
||||
global response
|
||||
if params_type == "json":
|
||||
response = requests.request(method='POST', url=self.url, headers=self.headers, json=self.data, **kwargs)
|
||||
elif params_type == "form": # 发送表单数据,使用data参数传递
|
||||
response = requests.request(method='POST', url=self.url, headers=self.headers, data=self.data, **kwargs)
|
||||
else:
|
||||
Config_handler.logger.error(f"{self.method}请求数据类型参数为json或者form,实际参数为{params_type}")
|
||||
res_data = response.json()
|
||||
Config_handler.logger.info(f"响应数据为{res_data}")
|
||||
response_code = jsonpath(res_data, "$..code")[0]
|
||||
Config_handler.logger.info('请求参数:{}'.format(self.data))
|
||||
if response_code == 0:
|
||||
with allure.step(f"第三步:判断请求结果,{self.method}请求成功"):
|
||||
Config_handler.logger.info(f"第三步:判断请求结果,{self.method}请求成功")
|
||||
if res_data is not None:
|
||||
with allure.step(f"第四步:获取响应数据\n{res_data}"):
|
||||
Config_handler.logger.info(f"第四步:获取响应数据\n{res_data}")
|
||||
elif response_code in api_code.keys():
|
||||
"""获取业务code进行失败原因分析"""
|
||||
with allure.step(f"第三步:判断请求结果,{self.method}请求成功"):
|
||||
print(response_code)
|
||||
code = api_code[response_code]
|
||||
with allure.step(f"第四步:响应结果为code:{response_code} \t失败原因:{code}"):
|
||||
Config_handler.logger.info(f"判断请求结果,{self.method}请求失败。code:{response_code} \t失败原因:{code}")
|
||||
else:
|
||||
Config_handler.logger.info(f"第三步:判断请求结果,{self.method}请求失败。code:{response_code}业务状态码未维护 \t失败原因:未知")
|
||||
return response.json()
|
||||
else:
|
||||
Config_handler.logger.err(f"请求方法不为post,实际输入请求方法为{self.method}")
|
||||
raise ValueError('request method "{}" error ! please check'.format(self.method))
|
||||
except requests.RequestException as e:
|
||||
Config_handler.logger.err(f"请求错误:\n{e}")
|
||||
raise
|
||||
|
||||
def get(self):
|
||||
try:
|
||||
if self.method in ["get", "GET"]:
|
||||
with allure.step(f"第一步:获取请求数据。\nheaders:{self.headers}\ndata:{self.data}\nurl:{self.url}"):
|
||||
Config_handler.logger.info(f"第一步:获取请求数据。\nheaders:{self.headers}\ndata:{self.data}\nurl:{self.url}")
|
||||
with allure.step(f"第二步:构建{self.method }请求"):
|
||||
Config_handler.logger.info(f"第二步:构建{self.method }请求")
|
||||
response = requests.request(
|
||||
method='get', url=self.url, headers=self.headers, params=self.data)
|
||||
res_data = response.json()
|
||||
response_code = jsonpath(res_data, "$..code")[0]
|
||||
if len(data) != 0:
|
||||
Config_handler.logger.info('请求参数:{}'.format(json.dumps(self.data)))
|
||||
if response_code == 0:
|
||||
with allure.step(f"第三步:判断请求结果,{self.method}请求成功"):
|
||||
Config_handler.logger.info(f"第三步:判断请求结果,{self.method}请求成功")
|
||||
if res_data is not None:
|
||||
with allure.step(f"第四步:获取响应数据\n{res_data}"):
|
||||
Config_handler.logger.info(f"第四步:获取响应数据\n{res_data}")
|
||||
elif response_code in api_code.keys():
|
||||
Config_handler.logger.info("调试",response_code, api_code.keys())
|
||||
# code = api_code[response_code]
|
||||
# Config_handler.logger.info(f"第三步:判断请求结果,{self.method}请求失败。code:{response_code} \t失败原因:{code}")
|
||||
else:
|
||||
Config_handler.logger.info(f"第三步:判断请求结果,{self.method}请求失败。code:{response_code}未维护 \t失败原因:未知")
|
||||
return response.json()
|
||||
else:
|
||||
Config_handler.logger.err(f"请求方法不为post,实际输入请求方法为{self.method}")
|
||||
except requests.RequestException as e:
|
||||
Config_handler.logger.err(f"请求错误\n:{e}")
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
headers = {"Content-Type": "application/json"}
|
||||
data ={'name': 'admin@chargedot.com', 'pwd': '26a50c46349c1d990376022bd62f7251'}
|
||||
a = Request_handler("post", "/api/v1//system/user/login", data, headers).post("json")
|
||||
# b = jsonpath(a, "$..token")[0]
|
||||
# print(b)
|
|
@ -0,0 +1,109 @@
|
|||
"""
|
||||
============================
|
||||
author:MuSen
|
||||
time:2019/6/21
|
||||
E-mail:3247119728@qq.com
|
||||
============================
|
||||
"""
|
||||
|
||||
from flask import Flask, request, jsonify
|
||||
|
||||
from flask_cors import CORS
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
# 测试数据
|
||||
user_info = {"user": 'python01', 'pwd': 'lemonban'}
|
||||
|
||||
project_data = {"code": "1",
|
||||
"data": [{"title": "前程贷", "id": "1001"},
|
||||
{"title": "智慧金融", "id": "1002"},
|
||||
{"title": "生鲜到家", "id": "1003"},
|
||||
{"title": "柠檬班app", "id": "1004"}],
|
||||
"msg": "四个项目",
|
||||
}
|
||||
# 接口数据
|
||||
interface_data = {
|
||||
"1001": {"code": "1",
|
||||
"data": [{"name": "前程贷登录1001"},
|
||||
{"name": "前程贷注册1001"}],
|
||||
"msg": "2个接口", },
|
||||
|
||||
"1002": {"code": "1",
|
||||
"data": [{"name": "智慧-登录1002"},
|
||||
{"name": "智慧-注册1002"},
|
||||
{"name": "智慧-贷款1004"}, ],
|
||||
"msg": "3个接口", },
|
||||
|
||||
"1003": {"code": "1",
|
||||
"data": [{"name": "生鲜-登录1003"},
|
||||
{"name": "生鲜-注册1003"},
|
||||
{"name": "生鲜下单1003"}, ],
|
||||
"msg": "3个接口", },
|
||||
|
||||
"1004": {"code": "1",
|
||||
"data": [{"name": "app登录1004"},
|
||||
{"name": "app注册1004"},
|
||||
{"name": "app报名1004"},
|
||||
{"name": "app缴费1004"},
|
||||
],
|
||||
"msg": "4个接口", },
|
||||
}
|
||||
|
||||
|
||||
# 登录
|
||||
@app.route('/api/user/login', methods=['post'])
|
||||
def login():
|
||||
"""
|
||||
接口地址:http://127.0.0.1:5000/api/user/login
|
||||
请求方法:post
|
||||
参数: {user:账号,pwd:密码}
|
||||
参数类型:表单 、json都支持
|
||||
返回:登录之后的结果
|
||||
"""
|
||||
data = request.form or request.json
|
||||
# 判断账号,密码是否正确
|
||||
if user_info.get('user') == data.get('user') and user_info.get('pwd') == data.get('pwd'):
|
||||
return jsonify({'code': "1", "data": None, "msg": "成功"})
|
||||
else:
|
||||
return jsonify({'code': "0", "data": None, "msg": "账号或密码有误"})
|
||||
|
||||
|
||||
# 获取项目列表
|
||||
@app.route('/api/projects', methods=['get'])
|
||||
def pro_list():
|
||||
"""
|
||||
接口地址:http://127.0.0.1:5000/api/projects
|
||||
请求方法:get
|
||||
参数:无
|
||||
返回所有的项目
|
||||
:return:
|
||||
"""
|
||||
return jsonify(project_data)
|
||||
|
||||
|
||||
# 获取接口列表
|
||||
@app.route('/api/interface', methods=['get'])
|
||||
def interface():
|
||||
"""
|
||||
接口地址:http://127.0.0.1:5000/api/interface
|
||||
请求方法:get
|
||||
参数: id(项目的id)
|
||||
参数类型:查询字符串
|
||||
返回:该项目的所有接口
|
||||
"""
|
||||
inter_id = request.args.get('id')
|
||||
|
||||
if inter_id:
|
||||
res_data = interface_data.get(inter_id)
|
||||
if res_data:
|
||||
return jsonify(res_data)
|
||||
else:
|
||||
return jsonify({"code": "0", "data": None, "msg": "没有该项目"})
|
||||
else:
|
||||
return jsonify({"code": "0", "data": None, "msg": "参数id不能为空"})
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
cors = CORS(app)
|
||||
app.run(debug=True)
|
|
@ -0,0 +1,137 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<!--引入axios -->
|
||||
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
|
||||
<!-- <script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script> -->
|
||||
<!--引入VUE -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
|
||||
<!--引入elementUI-->
|
||||
<link rel="stylesheet" href="https://unpkg.com/element-plus/lib/theme-chalk/index.css">
|
||||
<script src="https://unpkg.com/element-ui/lib/index.js"></script>
|
||||
<!--引入vue-router-->
|
||||
<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
|
||||
<style type="text/css">
|
||||
.login{
|
||||
width: 600px;
|
||||
height: 400px;
|
||||
margin: 100px auto;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app">
|
||||
|
||||
<el-container>
|
||||
<el-header>Header</el-header>
|
||||
<el-card>
|
||||
<el-main>
|
||||
<div class="login">
|
||||
<h2 style="text-align:center;">登录</h2>
|
||||
<el-form :model="form" label-width="80px" method="post">
|
||||
<el-form-item label="账号">
|
||||
<el-input v-model="form.username" placeholder="请输入用户名"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="密码">
|
||||
<el-input v-model="form.password" placeholder="请输入密码" show-password></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item style="text-align:right;">
|
||||
<el-button type="primary" @click="login" round>登录</el-button>
|
||||
</el-form-item>
|
||||
|
||||
</el-form>
|
||||
</div>
|
||||
<hr/>
|
||||
<div>
|
||||
<el-button type="info" @click="getProject" round>项目列表</el-button>
|
||||
<el-table :data="projects" stripe style="width: 100%">
|
||||
<el-table-column prop="id" label="ID"width="180"></el-table-column>
|
||||
<el-table-column prop="name" label="项目名" width="180"></el-table-column>
|
||||
<el-table-column prop="create_time" label="创建时间" width="180"></el-table-column>
|
||||
<el-table-column prop="leader" label="负责人"width="180"></el-table-column>
|
||||
<el-table-column prop="tester" label="测试者"width="180"></el-table-column>
|
||||
<el-table-column prop="testcases" label="用例数"width="180"></el-table-column>
|
||||
|
||||
</el-table>
|
||||
</div>
|
||||
</el-main>
|
||||
</el-card>
|
||||
|
||||
<el-footer>Footer</el-footer>
|
||||
</el-container>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
// 创建请求对象
|
||||
const http = axios.create({
|
||||
// 设置基本的url地址
|
||||
baseURL: 'http://api.keyou.site:8000',
|
||||
timeout: 1000,
|
||||
// 指定http状态码错误范围
|
||||
validateStatus: function(status) {
|
||||
return true
|
||||
}
|
||||
})
|
||||
// 请求拦截器
|
||||
http.interceptors.request.use(function(config){
|
||||
// 获取sessionStore中的token值,添加到请求信息中
|
||||
token = window.sessionStorage.getItem("token")
|
||||
if (token){
|
||||
config.headers.Authorization = "JWT " + token
|
||||
}
|
||||
return config;
|
||||
})
|
||||
const Login = {template: `<h1>登录页面</h1>`}
|
||||
const Home = {template: `<h1>项目首页</h1>`}
|
||||
const router = new VueRouter({
|
||||
routers: [
|
||||
{path: "/login", component: Login}
|
||||
{path: "/home", component: Home}
|
||||
]
|
||||
})
|
||||
|
||||
var vm = new Vue({
|
||||
el: "#app",
|
||||
router,
|
||||
data: {
|
||||
form: {
|
||||
username: "",
|
||||
password: ""
|
||||
},
|
||||
projects: []
|
||||
|
||||
},
|
||||
methods: {
|
||||
async login(){
|
||||
// 发送登录请求
|
||||
const response = await http.post("/user/login/", this.form)
|
||||
// const response = await http.post('/user/login', this.form)
|
||||
console.log(response.data)
|
||||
if (response.status === 200){
|
||||
// 提取响应中的token
|
||||
const token = response.data.token
|
||||
// 将token存储到sessionstorage中
|
||||
window.sessionStorage.setItem("token", token)
|
||||
alert("登录成功")
|
||||
}else{
|
||||
alert("登录失败!")
|
||||
}
|
||||
|
||||
},
|
||||
// 发送请求获取所有项目
|
||||
async getProject(){
|
||||
const response = await http.get("/projects/")
|
||||
console.log(response)
|
||||
if (response.status === 200){
|
||||
this.projects = response.data.results
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
})
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,685 @@
|
|||
# Changelog
|
||||
|
||||
### 0.21.1 (December 21, 2020)
|
||||
|
||||
Fixes and Functionality:
|
||||
|
||||
- Hotfix: Prevent SSRF (#3410)
|
||||
- Protocol not parsed when setting proxy config from env vars (#3070)
|
||||
- Updating axios in types to be lower case (#2797)
|
||||
- Adding a type guard for `AxiosError` (#2949)
|
||||
|
||||
Internal and Tests:
|
||||
|
||||
- Remove the skipping of the `socket` http test (#3364)
|
||||
- Use different socket for Win32 test (#3375)
|
||||
|
||||
Huge thanks to everyone who contributed to this release via code (authors listed below) or via reviews and triaging on GitHub:
|
||||
|
||||
- Daniel Lopretto <timemachine3030@users.noreply.github.com>
|
||||
- Jason Kwok <JasonHK@users.noreply.github.com>
|
||||
- Jay <jasonsaayman@gmail.com>
|
||||
- Jonathan Foster <jonathan@jonathanfoster.io>
|
||||
- Remco Haszing <remcohaszing@gmail.com>
|
||||
- Xianming Zhong <chinesedfan@qq.com>
|
||||
|
||||
### 0.21.0 (October 23, 2020)
|
||||
|
||||
Fixes and Functionality:
|
||||
|
||||
- Fixing requestHeaders.Authorization ([#3287](https://github.com/axios/axios/pull/3287))
|
||||
- Fixing node types ([#3237](https://github.com/axios/axios/pull/3237))
|
||||
- Fixing axios.delete ignores config.data ([#3282](https://github.com/axios/axios/pull/3282))
|
||||
- Revert "Fixing overwrite Blob/File type as Content-Type in browser. (#1773)" ([#3289](https://github.com/axios/axios/pull/3289))
|
||||
- Fixing an issue that type 'null' and 'undefined' is not assignable to validateStatus when typescript strict option is enabled ([#3200](https://github.com/axios/axios/pull/3200))
|
||||
|
||||
Internal and Tests:
|
||||
|
||||
- Lock travis to not use node v15 ([#3361](https://github.com/axios/axios/pull/3361))
|
||||
|
||||
Documentation:
|
||||
|
||||
- Fixing simple typo, existant -> existent ([#3252](https://github.com/axios/axios/pull/3252))
|
||||
- Fixing typos ([#3309](https://github.com/axios/axios/pull/3309))
|
||||
|
||||
Huge thanks to everyone who contributed to this release via code (authors listed below) or via reviews and triaging on GitHub:
|
||||
|
||||
- Allan Cruz <57270969+Allanbcruz@users.noreply.github.com>
|
||||
- George Cheng <Gerhut@GMail.com>
|
||||
- Jay <jasonsaayman@gmail.com>
|
||||
- Kevin Kirsche <Kev.Kirsche+GitHub@gmail.com>
|
||||
- Remco Haszing <remcohaszing@gmail.com>
|
||||
- Taemin Shin <cprayer13@gmail.com>
|
||||
- Tim Gates <tim.gates@iress.com>
|
||||
- Xianming Zhong <chinesedfan@qq.com>
|
||||
|
||||
### 0.20.0 (August 20, 2020)
|
||||
|
||||
Release of 0.20.0-pre as a full release with no other changes.
|
||||
|
||||
### 0.20.0-pre (July 15, 2020)
|
||||
|
||||
Fixes and Functionality:
|
||||
|
||||
- Fixing response with utf-8 BOM can not parse to json ([#2419](https://github.com/axios/axios/pull/2419))
|
||||
- fix: remove byte order marker (UTF-8 BOM) when transform response
|
||||
- fix: remove BOM only utf-8
|
||||
- test: utf-8 BOM
|
||||
- fix: incorrect param name
|
||||
- Refactor mergeConfig without utils.deepMerge ([#2844](https://github.com/axios/axios/pull/2844))
|
||||
- Adding failing test
|
||||
- Fixing #2587 default custom config persisting
|
||||
- Adding Concat keys and filter duplicates
|
||||
- Fixed value from CPE
|
||||
- update for review feedbacks
|
||||
- no deepMerge
|
||||
- only merge between plain objects
|
||||
- fix rename
|
||||
- always merge config by mergeConfig
|
||||
- extract function mergeDeepProperties
|
||||
- refactor mergeConfig with all keys, and add special logic for validateStatus
|
||||
- add test for resetting headers
|
||||
- add lots of tests and fix a bug
|
||||
- should not inherit `data`
|
||||
- use simple toString
|
||||
- Fixing overwrite Blob/File type as Content-Type in browser. ([#1773](https://github.com/axios/axios/pull/1773))
|
||||
- Fixing an issue that type 'null' is not assignable to validateStatus ([#2773](https://github.com/axios/axios/pull/2773))
|
||||
- Fixing special char encoding ([#1671](https://github.com/axios/axios/pull/1671))
|
||||
- removing @ character from replacement list since it is a reserved character
|
||||
- Updating buildURL test to not include the @ character
|
||||
- Removing console logs
|
||||
- Fixing password encoding with special characters in basic authentication ([#1492](https://github.com/axios/axios/pull/1492))
|
||||
- Fixing password encoding with special characters in basic authentication
|
||||
- Adding test to check if password with non-Latin1 characters pass
|
||||
- Fixing 'Network Error' in react native android ([#1487](https://github.com/axios/axios/pull/1487))
|
||||
There is a bug in react native Android platform when using get method. It will trigger a 'Network Error' when passing the requestData which is an empty string to request.send function. So if the requestData is an empty string we can set it to null as well to fix the bug.
|
||||
- Fixing Cookie Helper with Asyc Components ([#1105](https://github.com/axios/axios/pull/1105)) ([#1107](https://github.com/axios/axios/pull/1107))
|
||||
- Fixing 'progressEvent' type ([#2851](https://github.com/axios/axios/pull/2851))
|
||||
- Fix 'progressEvent' type
|
||||
- Update axios.ts
|
||||
- Fixing getting local files (file://) failed ([#2470](https://github.com/axios/axios/pull/2470))
|
||||
- fix issue #2416, #2396
|
||||
- fix Eslint warn
|
||||
- Modify judgment conditions
|
||||
- add unit test
|
||||
- update unit test
|
||||
- update unit test
|
||||
- Allow PURGE method in typings ([#2191](https://github.com/axios/axios/pull/2191))
|
||||
- Adding option to disable automatic decompression ([#2661](https://github.com/axios/axios/pull/2661))
|
||||
- Adding ability to disable auto decompression
|
||||
- Updating decompress documentation in README
|
||||
- Fixing test\unit\adapters\http.js lint errors
|
||||
- Adding test for disabling auto decompression
|
||||
- Removing changes that fixed lint errors in tests
|
||||
- Removing formatting change to unit test
|
||||
- Add independent `maxBodyLength` option ([#2781](https://github.com/axios/axios/pull/2781))
|
||||
- Add independent option to set the maximum size of the request body
|
||||
- Remove maxBodyLength check
|
||||
- Update README
|
||||
- Assert for error code and message
|
||||
- Adding responseEncoding to mergeConfig ([#1745](https://github.com/axios/axios/pull/1745))
|
||||
- Compatible with follow-redirect aborts the request ([#2689](https://github.com/axios/axios/pull/2689))
|
||||
- Compatible with follow-redirect aborts the request
|
||||
- Use the error code
|
||||
- Fix merging of params ([#2656](https://github.com/axios/axios/pull/2656))
|
||||
- Name function to avoid ESLint func-names warning
|
||||
- Switch params config to merge list and update tests
|
||||
- Restore testing of both false and null
|
||||
- Restore test cases for keys without defaults
|
||||
- Include test for non-object values that aren't false-y.
|
||||
- Revert `finally` as `then` ([#2683](https://github.com/axios/axios/pull/2683))
|
||||
|
||||
Internal and Tests:
|
||||
|
||||
- Fix stale bot config ([#3049](https://github.com/axios/axios/pull/3049))
|
||||
- fix stale bot config
|
||||
- fix multiple lines
|
||||
- Add days and change name to work ([#3035](https://github.com/axios/axios/pull/3035))
|
||||
- Update close-issues.yml ([#3031](https://github.com/axios/axios/pull/3031))
|
||||
- Update close-issues.yml
|
||||
Update close message to read better 😄
|
||||
- Fix use of quotations
|
||||
Use single quotes as per other .yml files
|
||||
- Remove user name form message
|
||||
- Add GitHub actions to close stale issues/prs ([#3029](https://github.com/axios/axios/pull/3029))
|
||||
- prepare stale actions
|
||||
- update messages
|
||||
- Add exempt labels and lighten up comments
|
||||
- Add GitHub actions to close invalid issues ([#3022](https://github.com/axios/axios/pull/3022))
|
||||
- add close actions
|
||||
- fix with checkout
|
||||
- update issue templates
|
||||
- add reminder
|
||||
- update close message
|
||||
- Add test with Node.js 12 ([#2860](https://github.com/axios/axios/pull/2860))
|
||||
- test with Node.js 12
|
||||
- test with latest
|
||||
- Adding console log on sandbox server startup ([#2210](https://github.com/axios/axios/pull/2210))
|
||||
- Adding console log on sandbox server startup
|
||||
- Update server.js
|
||||
Add server error handling
|
||||
- Update server.js
|
||||
Better error message, remove retry.
|
||||
- Adding tests for method `options` type definitions ([#1996](https://github.com/axios/axios/pull/1996))
|
||||
Update tests.
|
||||
- Add test for redirecting with too large response ([#2695](https://github.com/axios/axios/pull/2695))
|
||||
- Fixing unit test failure in Windows OS ([#2601](https://github.com/axios/axios/pull/2601))
|
||||
- Fixing issue for HEAD method and gzipped response ([#2666](https://github.com/axios/axios/pull/2666))
|
||||
- Fix tests in browsers ([#2748](https://github.com/axios/axios/pull/2748))
|
||||
- chore: add `jsdelivr` and `unpkg` support ([#2443](https://github.com/axios/axios/pull/2443))
|
||||
|
||||
Documentation:
|
||||
|
||||
- Adding support for URLSearchParams in node ([#1900](https://github.com/axios/axios/pull/1900))
|
||||
- Adding support for URLSearchParams in node
|
||||
- Remove un-needed code
|
||||
- Update utils.js
|
||||
- Make changes as suggested
|
||||
- Adding table of content (preview) ([#3050](https://github.com/axios/axios/pull/3050))
|
||||
- add toc (preview)
|
||||
- remove toc in toc
|
||||
Signed-off-by: Moni <usmoni@gmail.com>
|
||||
- fix sublinks
|
||||
- fix indentation
|
||||
- remove redundant table links
|
||||
- update caps and indent
|
||||
- remove axios
|
||||
- Replace 'blacklist' with 'blocklist' ([#3006](https://github.com/axios/axios/pull/3006))
|
||||
- docs(): Detailed config options environment. ([#2088](https://github.com/axios/axios/pull/2088))
|
||||
- docs(): Detailed config options environment.
|
||||
- Update README.md
|
||||
- Include axios-data-unpacker in ECOSYSTEM.md ([#2080](https://github.com/axios/axios/pull/2080))
|
||||
- Allow opening examples in Gitpod ([#1958](https://github.com/axios/axios/pull/1958))
|
||||
- Remove axios.all() and axios.spread() from Readme.md ([#2727](https://github.com/axios/axios/pull/2727))
|
||||
- remove axios.all(), axios.spread()
|
||||
- replace example
|
||||
- axios.all() -> Promise.all()
|
||||
- axios.spread(function (acct, perms)) -> function (acct, perms)
|
||||
- add deprecated mark
|
||||
- Update README.md ([#2887](https://github.com/axios/axios/pull/2887))
|
||||
Small change to the data attribute doc of the config. A request body can also be set for DELETE methods but this wasn't mentioned in the documentation (it only mentioned POST, PUT and PATCH). Took my some 10-20 minutes until I realized that I don't need to manipulate the request body with transformRequest in the case of DELETE.
|
||||
- Include swagger-taxos-codegen in ECOSYSTEM.md ([#2162](https://github.com/axios/axios/pull/2162))
|
||||
- Add CDNJS version badge in README.md ([#878](https://github.com/axios/axios/pull/878))
|
||||
This badge will show the version on CDNJS!
|
||||
- Documentation update to clear up ambiguity in code examples ([#2928](https://github.com/axios/axios/pull/2928))
|
||||
- Made an adjustment to the documentation to clear up any ambiguity around the use of "fs". This should help clear up that the code examples with "fs" cannot be used on the client side.
|
||||
- Update README.md about validateStatus ([#2912](https://github.com/axios/axios/pull/2912))
|
||||
Rewrote the comment from "Reject only if the status code is greater than or equal to 500" to "Resolve only if the status code is less than 500"
|
||||
- Updating documentation for usage form-data ([#2805](https://github.com/axios/axios/pull/2805))
|
||||
Closes #2049
|
||||
- Fixing CHANGELOG.md issue link ([#2784](https://github.com/axios/axios/pull/2784))
|
||||
- Include axios-hooks in ECOSYSTEM.md ([#2003](https://github.com/axios/axios/pull/2003))
|
||||
- Added Response header access instructions ([#1901](https://github.com/axios/axios/pull/1901))
|
||||
- Added Response header access instructions
|
||||
- Added note about using bracket notation
|
||||
- Add `onUploadProgress` and `onDownloadProgress` are browser only ([#2763](https://github.com/axios/axios/pull/2763))
|
||||
Saw in #928 and #1966 that `onUploadProgress` and `onDownloadProgress` only work in the browser and was missing that from the README.
|
||||
- Update ' sign to ` in proxy spec ([#2778](https://github.com/axios/axios/pull/2778))
|
||||
- Adding jsDelivr link in README ([#1110](https://github.com/axios/axios/pull/1110))
|
||||
- Adding jsDelivr link
|
||||
- Add SRI
|
||||
- Remove SRI
|
||||
|
||||
Huge thanks to everyone who contributed to this release via code (authors listed
|
||||
below) or via reviews and triaging on GitHub:
|
||||
|
||||
- Alan Wang <wp_scut@163.com>
|
||||
- Alexandru Ungureanu <khakcarot@gmail.com>
|
||||
- Anubhav Srivastava <anubhav.srivastava00@gmail.com>
|
||||
- Benny Neugebauer <bn@bennyn.de>
|
||||
- Cr <631807682@qq.com>
|
||||
- David <cygnidavid@gmail.com>
|
||||
- David Ko <david.ko@pvtmethod.com>
|
||||
- David Tanner <david.tanner@lifeomic.com>
|
||||
- Emily Morehouse <emilyemorehouse@gmail.com>
|
||||
- Felipe Martins <felipewmartins@gmail.com>
|
||||
- Fonger <5862369+Fonger@users.noreply.github.com>
|
||||
- Frostack <soulburn007@gmail.com>
|
||||
- George Cheng <Gerhut@GMail.com>
|
||||
- grumblerchester <grumblerchester@users.noreply.github.com>
|
||||
- Gustavo López <gualopezb@gmail.com>
|
||||
- hexaez <45806662+hexaez@users.noreply.github.com>
|
||||
- huangzuizui <huangzuizui@gmail.com>
|
||||
- Ian Wijma <ian@wij.ma>
|
||||
- Jay <jasonsaayman@gmail.com>
|
||||
- jeffjing <zgayjjf@qq.com>
|
||||
- jennynju <46782518+jennynju@users.noreply.github.com>
|
||||
- Jimmy Liao <52391190+jimmy-liao-gogoro@users.noreply.github.com>
|
||||
- Jonathan Sharpe <j.r.sharpe@gmail.com>
|
||||
- JounQin <admin@1stg.me>
|
||||
- Justin Beckwith <justin.beckwith@gmail.com>
|
||||
- Kamil Posiadała <3dcreator.pl@gmail.com>
|
||||
- Lukas Drgon <lukas.drgon@gmail.com>
|
||||
- marcinx <mail@marcinx.com>
|
||||
- Martti Laine <martti@codeclown.net>
|
||||
- Michał Zarach <michal.m.zarach@gmail.com>
|
||||
- Moni <usmoni@gmail.com>
|
||||
- Motonori Iwata <121048+iwata@users.noreply.github.com>
|
||||
- Nikita Galkin <nikita@galk.in>
|
||||
- Petr Mares <petr@mares.tw>
|
||||
- Philippe Recto <precto1285@gmal.com>
|
||||
- Remco Haszing <remcohaszing@gmail.com>
|
||||
- rockcs1992 <chengshi1219@gmail.com>
|
||||
- Ryan Bown <rbown@niftee.com.au>
|
||||
- Samina Fu <sufuf3@gmail.com>
|
||||
- Simone Busoli <simone.busoli@gmail.com>
|
||||
- Spencer von der Ohe <s.vonderohe40@gmail.com>
|
||||
- Sven Efftinge <sven.efftinge@typefox.io>
|
||||
- Taegyeoung Oh <otk1090@naver.com>
|
||||
- Taemin Shin <cprayer13@gmail.com>
|
||||
- Thibault Ehrhart <1208424+ehrhart@users.noreply.github.com>
|
||||
- Xianming Zhong <chinesedfan@qq.com>
|
||||
- Yasu Flores <carlosyasu91@gmail.com>
|
||||
- Zac Delventhal <delventhalz@gmail.com>
|
||||
|
||||
### 0.19.2 (Jan 20, 2020)
|
||||
|
||||
- Remove unnecessary XSS check ([#2679](https://github.com/axios/axios/pull/2679)) (see ([#2646](https://github.com/axios/axios/issues/2646)) for discussion)
|
||||
|
||||
### 0.19.1 (Jan 7, 2020)
|
||||
|
||||
Fixes and Functionality:
|
||||
|
||||
- Fixing invalid agent issue ([#1904](https://github.com/axios/axios/pull/1904))
|
||||
- Fix ignore set withCredentials false ([#2582](https://github.com/axios/axios/pull/2582))
|
||||
- Delete useless default to hash ([#2458](https://github.com/axios/axios/pull/2458))
|
||||
- Fix HTTP/HTTPs agents passing to follow-redirect ([#1904](https://github.com/axios/axios/pull/1904))
|
||||
- Fix ignore set withCredentials false ([#2582](https://github.com/axios/axios/pull/2582))
|
||||
- Fix CI build failure ([#2570](https://github.com/axios/axios/pull/2570))
|
||||
- Remove dependency on is-buffer from package.json ([#1816](https://github.com/axios/axios/pull/1816))
|
||||
- Adding options typings ([#2341](https://github.com/axios/axios/pull/2341))
|
||||
- Adding Typescript HTTP method definition for LINK and UNLINK. ([#2444](https://github.com/axios/axios/pull/2444))
|
||||
- Update dist with newest changes, fixes Custom Attributes issue
|
||||
- Change syntax to see if build passes ([#2488](https://github.com/axios/axios/pull/2488))
|
||||
- Update Webpack + deps, remove now unnecessary polyfills ([#2410](https://github.com/axios/axios/pull/2410))
|
||||
- Fix to prevent XSS, throw an error when the URL contains a JS script ([#2464](https://github.com/axios/axios/pull/2464))
|
||||
- Add custom timeout error copy in config ([#2275](https://github.com/axios/axios/pull/2275))
|
||||
- Add error toJSON example ([#2466](https://github.com/axios/axios/pull/2466))
|
||||
- Fixing Vulnerability A Fortify Scan finds a critical Cross-Site Scrip… ([#2451](https://github.com/axios/axios/pull/2451))
|
||||
- Fixing subdomain handling on no_proxy ([#2442](https://github.com/axios/axios/pull/2442))
|
||||
- Make redirection from HTTP to HTTPS work ([#2426](https://github.com/axios/axios/pull/2426)) and ([#2547](https://github.com/axios/axios/pull/2547))
|
||||
- Add toJSON property to AxiosError type ([#2427](https://github.com/axios/axios/pull/2427))
|
||||
- Fixing socket hang up error on node side for slow response. ([#1752](https://github.com/axios/axios/pull/1752))
|
||||
- Alternative syntax to send data into the body ([#2317](https://github.com/axios/axios/pull/2317))
|
||||
- Fixing custom config options ([#2207](https://github.com/axios/axios/pull/2207))
|
||||
- Fixing set `config.method` after mergeConfig for Axios.prototype.request ([#2383](https://github.com/axios/axios/pull/2383))
|
||||
- Axios create url bug ([#2290](https://github.com/axios/axios/pull/2290))
|
||||
- Do not modify config.url when using a relative baseURL (resolves [#1628](https://github.com/axios/axios/issues/1098)) ([#2391](https://github.com/axios/axios/pull/2391))
|
||||
- Add typescript HTTP method definition for LINK and UNLINK ([#2444](https://github.com/axios/axios/pull/2444))
|
||||
|
||||
Internal:
|
||||
|
||||
- Revert "Update Webpack + deps, remove now unnecessary polyfills" ([#2479](https://github.com/axios/axios/pull/2479))
|
||||
- Order of if/else blocks is causing unit tests mocking XHR. ([#2201](https://github.com/axios/axios/pull/2201))
|
||||
- Add license badge ([#2446](https://github.com/axios/axios/pull/2446))
|
||||
- Fix travis CI build [#2386](https://github.com/axios/axios/pull/2386)
|
||||
- Fix cancellation error on build master. #2290 #2207 ([#2407](https://github.com/axios/axios/pull/2407))
|
||||
|
||||
Documentation:
|
||||
|
||||
- Fixing typo in CHANGELOG.md: s/Functionallity/Functionality ([#2639](https://github.com/axios/axios/pull/2639))
|
||||
- Fix badge, use master branch ([#2538](https://github.com/axios/axios/pull/2538))
|
||||
- Fix typo in changelog [#2193](https://github.com/axios/axios/pull/2193)
|
||||
- Document fix ([#2514](https://github.com/axios/axios/pull/2514))
|
||||
- Update docs with no_proxy change, issue #2484 ([#2513](https://github.com/axios/axios/pull/2513))
|
||||
- Fixing missing words in docs template ([#2259](https://github.com/axios/axios/pull/2259))
|
||||
- 🐛Fix request finally documentation in README ([#2189](https://github.com/axios/axios/pull/2189))
|
||||
- updating spelling and adding link to docs ([#2212](https://github.com/axios/axios/pull/2212))
|
||||
- docs: minor tweak ([#2404](https://github.com/axios/axios/pull/2404))
|
||||
- Update response interceptor docs ([#2399](https://github.com/axios/axios/pull/2399))
|
||||
- Update README.md ([#2504](https://github.com/axios/axios/pull/2504))
|
||||
- Fix word 'sintaxe' to 'syntax' in README.md ([#2432](https://github.com/axios/axios/pull/2432))
|
||||
- updating README: notes on CommonJS autocomplete ([#2256](https://github.com/axios/axios/pull/2256))
|
||||
- Fix grammar in README.md ([#2271](https://github.com/axios/axios/pull/2271))
|
||||
- Doc fixes, minor examples cleanup ([#2198](https://github.com/axios/axios/pull/2198))
|
||||
|
||||
### 0.19.0 (May 30, 2019)
|
||||
|
||||
Fixes and Functionality:
|
||||
|
||||
- Added support for no_proxy env variable ([#1693](https://github.com/axios/axios/pull/1693/files)) - Chance Dickson
|
||||
- Unzip response body only for statuses != 204 ([#1129](https://github.com/axios/axios/pull/1129)) - drawski
|
||||
- Destroy stream on exceeding maxContentLength (fixes [#1098](https://github.com/axios/axios/issues/1098)) ([#1485](https://github.com/axios/axios/pull/1485)) - Gadzhi Gadzhiev
|
||||
- Makes Axios error generic to use AxiosResponse ([#1738](https://github.com/axios/axios/pull/1738)) - Suman Lama
|
||||
- Fixing Mocha tests by locking follow-redirects version to 1.5.10 ([#1993](https://github.com/axios/axios/pull/1993)) - grumblerchester
|
||||
- Allow uppercase methods in typings. ([#1781](https://github.com/axios/axios/pull/1781)) - Ken Powers
|
||||
- Fixing building url with hash mark ([#1771](https://github.com/axios/axios/pull/1771)) - Anatoly Ryabov
|
||||
- This commit fix building url with hash map (fragment identifier) when parameters are present: they must not be added after `#`, because client cut everything after `#`
|
||||
- Preserve HTTP method when following redirect ([#1758](https://github.com/axios/axios/pull/1758)) - Rikki Gibson
|
||||
- Add `getUri` signature to TypeScript definition. ([#1736](https://github.com/axios/axios/pull/1736)) - Alexander Trauzzi
|
||||
- Adding isAxiosError flag to errors thrown by axios ([#1419](https://github.com/axios/axios/pull/1419)) - Ayush Gupta
|
||||
|
||||
Internal:
|
||||
|
||||
- Fixing .eslintrc without extension ([#1789](https://github.com/axios/axios/pull/1789)) - Manoel
|
||||
- Fix failing SauceLabs tests by updating configuration - Emily Morehouse
|
||||
- Add issue templates - Emily Morehouse
|
||||
|
||||
Documentation:
|
||||
|
||||
- Consistent coding style in README ([#1787](https://github.com/axios/axios/pull/1787)) - Ali Servet Donmez
|
||||
- Add information about auth parameter to README ([#2166](https://github.com/axios/axios/pull/2166)) - xlaguna
|
||||
- Add DELETE to list of methods that allow data as a config option ([#2169](https://github.com/axios/axios/pull/2169)) - Daniela Borges Matos de Carvalho
|
||||
- Update ECOSYSTEM.md - Add Axios Endpoints ([#2176](https://github.com/axios/axios/pull/2176)) - Renan
|
||||
- Add r2curl in ECOSYSTEM ([#2141](https://github.com/axios/axios/pull/2141)) - 유용우 / CX
|
||||
- Update README.md - Add instructions for installing with yarn ([#2036](https://github.com/axios/axios/pull/2036)) - Victor Hermes
|
||||
- Fixing spacing for README.md ([#2066](https://github.com/axios/axios/pull/2066)) - Josh McCarty
|
||||
- Update README.md. - Change `.then` to `.finally` in example code ([#2090](https://github.com/axios/axios/pull/2090)) - Omar Cai
|
||||
- Clarify what values responseType can have in Node ([#2121](https://github.com/axios/axios/pull/2121)) - Tyler Breisacher
|
||||
- docs(ECOSYSTEM): add axios-api-versioning ([#2020](https://github.com/axios/axios/pull/2020)) - Weffe
|
||||
- It seems that `responseType: 'blob'` doesn't actually work in Node (when I tried using it, response.data was a string, not a Blob, since Node doesn't have Blobs), so this clarifies that this option should only be used in the browser
|
||||
- Update README.md. - Add Querystring library note ([#1896](https://github.com/axios/axios/pull/1896)) - Dmitriy Eroshenko
|
||||
- Add react-hooks-axios to Libraries section of ECOSYSTEM.md ([#1925](https://github.com/axios/axios/pull/1925)) - Cody Chan
|
||||
- Clarify in README that default timeout is 0 (no timeout) ([#1750](https://github.com/axios/axios/pull/1750)) - Ben Standefer
|
||||
|
||||
### 0.19.0-beta.1 (Aug 9, 2018)
|
||||
|
||||
**NOTE:** This is a beta version of this release. There may be functionality that is broken in
|
||||
certain browsers, though we suspect that builds are hanging and not erroring. See
|
||||
https://saucelabs.com/u/axios for the most up-to-date information.
|
||||
|
||||
New Functionality:
|
||||
|
||||
- Add getUri method ([#1712](https://github.com/axios/axios/issues/1712))
|
||||
- Add support for no_proxy env variable ([#1693](https://github.com/axios/axios/issues/1693))
|
||||
- Add toJSON to decorated Axios errors to facilitate serialization ([#1625](https://github.com/axios/axios/issues/1625))
|
||||
- Add second then on axios call ([#1623](https://github.com/axios/axios/issues/1623))
|
||||
- Typings: allow custom return types
|
||||
- Add option to specify character set in responses (with http adapter)
|
||||
|
||||
Fixes:
|
||||
|
||||
- Fix Keep defaults local to instance ([#385](https://github.com/axios/axios/issues/385))
|
||||
- Correctly catch exception in http test ([#1475](https://github.com/axios/axios/issues/1475))
|
||||
- Fix accept header normalization ([#1698](https://github.com/axios/axios/issues/1698))
|
||||
- Fix http adapter to allow HTTPS connections via HTTP ([#959](https://github.com/axios/axios/issues/959))
|
||||
- Fix Removes usage of deprecated Buffer constructor. ([#1555](https://github.com/axios/axios/issues/1555), [#1622](https://github.com/axios/axios/issues/1622))
|
||||
- Fix defaults to use httpAdapter if available ([#1285](https://github.com/axios/axios/issues/1285))
|
||||
- Fixing defaults to use httpAdapter if available
|
||||
- Use a safer, cross-platform method to detect the Node environment
|
||||
- Fix Reject promise if request is cancelled by the browser ([#537](https://github.com/axios/axios/issues/537))
|
||||
- [Typescript] Fix missing type parameters on delete/head methods
|
||||
- [NS]: Send `false` flag isStandardBrowserEnv for Nativescript
|
||||
- Fix missing type parameters on delete/head
|
||||
- Fix Default method for an instance always overwritten by get
|
||||
- Fix type error when socketPath option in AxiosRequestConfig
|
||||
- Capture errors on request data streams
|
||||
- Decorate resolve and reject to clear timeout in all cases
|
||||
|
||||
Huge thanks to everyone who contributed to this release via code (authors listed
|
||||
below) or via reviews and triaging on GitHub:
|
||||
|
||||
- Andrew Scott <ascott18@gmail.com>
|
||||
- Anthony Gauthier <antho325@hotmail.com>
|
||||
- arpit <arpit2438735@gmail.com>
|
||||
- ascott18
|
||||
- Benedikt Rötsch <axe312ger@users.noreply.github.com>
|
||||
- Chance Dickson <me@chancedickson.com>
|
||||
- Dave Stewart <info@davestewart.co.uk>
|
||||
- Deric Cain <deric.cain@gmail.com>
|
||||
- Guillaume Briday <guillaumebriday@gmail.com>
|
||||
- Jacob Wejendorp <jacob@wejendorp.dk>
|
||||
- Jim Lynch <mrdotjim@gmail.com>
|
||||
- johntron
|
||||
- Justin Beckwith <beckwith@google.com>
|
||||
- Justin Beckwith <justin.beckwith@gmail.com>
|
||||
- Khaled Garbaya <khaledgarbaya@gmail.com>
|
||||
- Lim Jing Rong <jjingrong@users.noreply.github.com>
|
||||
- Mark van den Broek <mvdnbrk@gmail.com>
|
||||
- Martti Laine <martti@codeclown.net>
|
||||
- mattridley
|
||||
- mattridley <matt.r@joinblink.com>
|
||||
- Nicolas Del Valle <nicolas.delvalle@gmail.com>
|
||||
- Nilegfx
|
||||
- pbarbiero
|
||||
- Rikki Gibson <rikkigibson@gmail.com>
|
||||
- Sako Hartounian <sakohartounian@yahoo.com>
|
||||
- Shane Fitzpatrick <fitzpasd@gmail.com>
|
||||
- Stephan Schneider <stephanschndr@gmail.com>
|
||||
- Steven <steven@ceriously.com>
|
||||
- Tim Garthwaite <tim.garthwaite@jibo.com>
|
||||
- Tim Johns <timjohns@yahoo.com>
|
||||
- Yutaro Miyazaki <yutaro@studio-rubbish.com>
|
||||
|
||||
### 0.18.0 (Feb 19, 2018)
|
||||
|
||||
- Adding support for UNIX Sockets when running with Node.js ([#1070](https://github.com/axios/axios/pull/1070))
|
||||
- Fixing typings ([#1177](https://github.com/axios/axios/pull/1177)):
|
||||
- AxiosRequestConfig.proxy: allows type false
|
||||
- AxiosProxyConfig: added auth field
|
||||
- Adding function signature in AxiosInstance interface so AxiosInstance can be invoked ([#1192](https://github.com/axios/axios/pull/1192), [#1254](https://github.com/axios/axios/pull/1254))
|
||||
- Allowing maxContentLength to pass through to redirected calls as maxBodyLength in follow-redirects config ([#1287](https://github.com/axios/axios/pull/1287))
|
||||
- Fixing configuration when using an instance - method can now be set ([#1342](https://github.com/axios/axios/pull/1342))
|
||||
|
||||
### 0.17.1 (Nov 11, 2017)
|
||||
|
||||
- Fixing issue with web workers ([#1160](https://github.com/axios/axios/pull/1160))
|
||||
- Allowing overriding transport ([#1080](https://github.com/axios/axios/pull/1080))
|
||||
- Updating TypeScript typings ([#1165](https://github.com/axios/axios/pull/1165), [#1125](https://github.com/axios/axios/pull/1125), [#1131](https://github.com/axios/axios/pull/1131))
|
||||
|
||||
### 0.17.0 (Oct 21, 2017)
|
||||
|
||||
- **BREAKING** Fixing issue with `baseURL` and interceptors ([#950](https://github.com/axios/axios/pull/950))
|
||||
- **BREAKING** Improving handing of duplicate headers ([#874](https://github.com/axios/axios/pull/874))
|
||||
- Adding support for disabling proxies ([#691](https://github.com/axios/axios/pull/691))
|
||||
- Updating TypeScript typings with generic type parameters ([#1061](https://github.com/axios/axios/pull/1061))
|
||||
|
||||
### 0.16.2 (Jun 3, 2017)
|
||||
|
||||
- Fixing issue with including `buffer` in bundle ([#887](https://github.com/axios/axios/pull/887))
|
||||
- Including underlying request in errors ([#830](https://github.com/axios/axios/pull/830))
|
||||
- Convert `method` to lowercase ([#930](https://github.com/axios/axios/pull/930))
|
||||
|
||||
### 0.16.1 (Apr 8, 2017)
|
||||
|
||||
- Improving HTTP adapter to return last request in case of redirects ([#828](https://github.com/axios/axios/pull/828))
|
||||
- Updating `follow-redirects` dependency ([#829](https://github.com/axios/axios/pull/829))
|
||||
- Adding support for passing `Buffer` in node ([#773](https://github.com/axios/axios/pull/773))
|
||||
|
||||
### 0.16.0 (Mar 31, 2017)
|
||||
|
||||
- **BREAKING** Removing `Promise` from axios typings in favor of built-in type declarations ([#480](https://github.com/axios/axios/issues/480))
|
||||
- Adding `options` shortcut method ([#461](https://github.com/axios/axios/pull/461))
|
||||
- Fixing issue with using `responseType: 'json'` in browsers incompatible with XHR Level 2 ([#654](https://github.com/axios/axios/pull/654))
|
||||
- Improving React Native detection ([#731](https://github.com/axios/axios/pull/731))
|
||||
- Fixing `combineURLs` to support empty `relativeURL` ([#581](https://github.com/axios/axios/pull/581))
|
||||
- Removing `PROTECTION_PREFIX` support ([#561](https://github.com/axios/axios/pull/561))
|
||||
|
||||
### 0.15.3 (Nov 27, 2016)
|
||||
|
||||
- Fixing issue with custom instances and global defaults ([#443](https://github.com/axios/axios/issues/443))
|
||||
- Renaming `axios.d.ts` to `index.d.ts` ([#519](https://github.com/axios/axios/issues/519))
|
||||
- Adding `get`, `head`, and `delete` to `defaults.headers` ([#509](https://github.com/axios/axios/issues/509))
|
||||
- Fixing issue with `btoa` and IE ([#507](https://github.com/axios/axios/issues/507))
|
||||
- Adding support for proxy authentication ([#483](https://github.com/axios/axios/pull/483))
|
||||
- Improving HTTP adapter to use `http` protocol by default ([#493](https://github.com/axios/axios/pull/493))
|
||||
- Fixing proxy issues ([#491](https://github.com/axios/axios/pull/491))
|
||||
|
||||
### 0.15.2 (Oct 17, 2016)
|
||||
|
||||
- Fixing issue with calling `cancel` after response has been received ([#482](https://github.com/axios/axios/issues/482))
|
||||
|
||||
### 0.15.1 (Oct 14, 2016)
|
||||
|
||||
- Fixing issue with UMD ([#485](https://github.com/axios/axios/issues/485))
|
||||
|
||||
### 0.15.0 (Oct 10, 2016)
|
||||
|
||||
- Adding cancellation support ([#452](https://github.com/axios/axios/pull/452))
|
||||
- Moving default adapter to global defaults ([#437](https://github.com/axios/axios/pull/437))
|
||||
- Fixing issue with `file` URI scheme ([#440](https://github.com/axios/axios/pull/440))
|
||||
- Fixing issue with `params` objects that have no prototype ([#445](https://github.com/axios/axios/pull/445))
|
||||
|
||||
### 0.14.0 (Aug 27, 2016)
|
||||
|
||||
- **BREAKING** Updating TypeScript definitions ([#419](https://github.com/axios/axios/pull/419))
|
||||
- **BREAKING** Replacing `agent` option with `httpAgent` and `httpsAgent` ([#387](https://github.com/axios/axios/pull/387))
|
||||
- **BREAKING** Splitting `progress` event handlers into `onUploadProgress` and `onDownloadProgress` ([#423](https://github.com/axios/axios/pull/423))
|
||||
- Adding support for `http_proxy` and `https_proxy` environment variables ([#366](https://github.com/axios/axios/pull/366))
|
||||
- Fixing issue with `auth` config option and `Authorization` header ([#397](https://github.com/axios/axios/pull/397))
|
||||
- Don't set XSRF header if `xsrfCookieName` is `null` ([#406](https://github.com/axios/axios/pull/406))
|
||||
|
||||
### 0.13.1 (Jul 16, 2016)
|
||||
|
||||
- Fixing issue with response data not being transformed on error ([#378](https://github.com/axios/axios/issues/378))
|
||||
|
||||
### 0.13.0 (Jul 13, 2016)
|
||||
|
||||
- **BREAKING** Improved error handling ([#345](https://github.com/axios/axios/pull/345))
|
||||
- **BREAKING** Response transformer now invoked in dispatcher not adapter ([10eb238](https://github.com/axios/axios/commit/10eb23865101f9347570552c04e9d6211376e25e))
|
||||
- **BREAKING** Request adapters now return a `Promise` ([157efd5](https://github.com/axios/axios/commit/157efd5615890301824e3121cc6c9d2f9b21f94a))
|
||||
- Fixing issue with `withCredentials` not being overwritten ([#343](https://github.com/axios/axios/issues/343))
|
||||
- Fixing regression with request transformer being called before request interceptor ([#352](https://github.com/axios/axios/issues/352))
|
||||
- Fixing custom instance defaults ([#341](https://github.com/axios/axios/issues/341))
|
||||
- Fixing instances created from `axios.create` to have same API as default axios ([#217](https://github.com/axios/axios/issues/217))
|
||||
|
||||
### 0.12.0 (May 31, 2016)
|
||||
|
||||
- Adding support for `URLSearchParams` ([#317](https://github.com/axios/axios/pull/317))
|
||||
- Adding `maxRedirects` option ([#307](https://github.com/axios/axios/pull/307))
|
||||
|
||||
### 0.11.1 (May 17, 2016)
|
||||
|
||||
- Fixing IE CORS support ([#313](https://github.com/axios/axios/pull/313))
|
||||
- Fixing detection of `FormData` ([#325](https://github.com/axios/axios/pull/325))
|
||||
- Adding `Axios` class to exports ([#321](https://github.com/axios/axios/pull/321))
|
||||
|
||||
### 0.11.0 (Apr 26, 2016)
|
||||
|
||||
- Adding support for Stream with HTTP adapter ([#296](https://github.com/axios/axios/pull/296))
|
||||
- Adding support for custom HTTP status code error ranges ([#308](https://github.com/axios/axios/pull/308))
|
||||
- Fixing issue with ArrayBuffer ([#299](https://github.com/axios/axios/pull/299))
|
||||
|
||||
### 0.10.0 (Apr 20, 2016)
|
||||
|
||||
- Fixing issue with some requests sending `undefined` instead of `null` ([#250](https://github.com/axios/axios/pull/250))
|
||||
- Fixing basic auth for HTTP adapter ([#252](https://github.com/axios/axios/pull/252))
|
||||
- Fixing request timeout for XHR adapter ([#227](https://github.com/axios/axios/pull/227))
|
||||
- Fixing IE8 support by using `onreadystatechange` instead of `onload` ([#249](https://github.com/axios/axios/pull/249))
|
||||
- Fixing IE9 cross domain requests ([#251](https://github.com/axios/axios/pull/251))
|
||||
- Adding `maxContentLength` option ([#275](https://github.com/axios/axios/pull/275))
|
||||
- Fixing XHR support for WebWorker environment ([#279](https://github.com/axios/axios/pull/279))
|
||||
- Adding request instance to response ([#200](https://github.com/axios/axios/pull/200))
|
||||
|
||||
### 0.9.1 (Jan 24, 2016)
|
||||
|
||||
- Improving handling of request timeout in node ([#124](https://github.com/axios/axios/issues/124))
|
||||
- Fixing network errors not rejecting ([#205](https://github.com/axios/axios/pull/205))
|
||||
- Fixing issue with IE rejecting on HTTP 204 ([#201](https://github.com/axios/axios/issues/201))
|
||||
- Fixing host/port when following redirects ([#198](https://github.com/axios/axios/pull/198))
|
||||
|
||||
### 0.9.0 (Jan 18, 2016)
|
||||
|
||||
- Adding support for custom adapters
|
||||
- Fixing Content-Type header being removed when data is false ([#195](https://github.com/axios/axios/pull/195))
|
||||
- Improving XDomainRequest implementation ([#185](https://github.com/axios/axios/pull/185))
|
||||
- Improving config merging and order of precedence ([#183](https://github.com/axios/axios/pull/183))
|
||||
- Fixing XDomainRequest support for only <= IE9 ([#182](https://github.com/axios/axios/pull/182))
|
||||
|
||||
### 0.8.1 (Dec 14, 2015)
|
||||
|
||||
- Adding support for passing XSRF token for cross domain requests when using `withCredentials` ([#168](https://github.com/axios/axios/pull/168))
|
||||
- Fixing error with format of basic auth header ([#178](https://github.com/axios/axios/pull/173))
|
||||
- Fixing error with JSON payloads throwing `InvalidStateError` in some cases ([#174](https://github.com/axios/axios/pull/174))
|
||||
|
||||
### 0.8.0 (Dec 11, 2015)
|
||||
|
||||
- Adding support for creating instances of axios ([#123](https://github.com/axios/axios/pull/123))
|
||||
- Fixing http adapter to use `Buffer` instead of `String` in case of `responseType === 'arraybuffer'` ([#128](https://github.com/axios/axios/pull/128))
|
||||
- Adding support for using custom parameter serializer with `paramsSerializer` option ([#121](https://github.com/axios/axios/pull/121))
|
||||
- Fixing issue in IE8 caused by `forEach` on `arguments` ([#127](https://github.com/axios/axios/pull/127))
|
||||
- Adding support for following redirects in node ([#146](https://github.com/axios/axios/pull/146))
|
||||
- Adding support for transparent decompression if `content-encoding` is set ([#149](https://github.com/axios/axios/pull/149))
|
||||
- Adding support for transparent XDomainRequest to handle cross domain requests in IE9 ([#140](https://github.com/axios/axios/pull/140))
|
||||
- Adding support for HTTP basic auth via Authorization header ([#167](https://github.com/axios/axios/pull/167))
|
||||
- Adding support for baseURL option ([#160](https://github.com/axios/axios/pull/160))
|
||||
|
||||
### 0.7.0 (Sep 29, 2015)
|
||||
|
||||
- Fixing issue with minified bundle in IE8 ([#87](https://github.com/axios/axios/pull/87))
|
||||
- Adding support for passing agent in node ([#102](https://github.com/axios/axios/pull/102))
|
||||
- Adding support for returning result from `axios.spread` for chaining ([#106](https://github.com/axios/axios/pull/106))
|
||||
- Fixing typescript definition ([#105](https://github.com/axios/axios/pull/105))
|
||||
- Fixing default timeout config for node ([#112](https://github.com/axios/axios/pull/112))
|
||||
- Adding support for use in web workers, and react-native ([#70](https://github.com/axios/axios/issue/70)), ([#98](https://github.com/axios/axios/pull/98))
|
||||
- Adding support for fetch like API `axios(url[, config])` ([#116](https://github.com/axios/axios/issues/116))
|
||||
|
||||
### 0.6.0 (Sep 21, 2015)
|
||||
|
||||
- Removing deprecated success/error aliases
|
||||
- Fixing issue with array params not being properly encoded ([#49](https://github.com/axios/axios/pull/49))
|
||||
- Fixing issue with User-Agent getting overridden ([#69](https://github.com/axios/axios/issues/69))
|
||||
- Adding support for timeout config ([#56](https://github.com/axios/axios/issues/56))
|
||||
- Removing es6-promise dependency
|
||||
- Fixing issue preventing `length` to be used as a parameter ([#91](https://github.com/axios/axios/pull/91))
|
||||
- Fixing issue with IE8 ([#85](https://github.com/axios/axios/pull/85))
|
||||
- Converting build to UMD
|
||||
|
||||
### 0.5.4 (Apr 08, 2015)
|
||||
|
||||
- Fixing issue with FormData not being sent ([#53](https://github.com/axios/axios/issues/53))
|
||||
|
||||
### 0.5.3 (Apr 07, 2015)
|
||||
|
||||
- Using JSON.parse unconditionally when transforming response string ([#55](https://github.com/axios/axios/issues/55))
|
||||
|
||||
### 0.5.2 (Mar 13, 2015)
|
||||
|
||||
- Adding support for `statusText` in response ([#46](https://github.com/axios/axios/issues/46))
|
||||
|
||||
### 0.5.1 (Mar 10, 2015)
|
||||
|
||||
- Fixing issue using strict mode ([#45](https://github.com/axios/axios/issues/45))
|
||||
- Fixing issue with standalone build ([#47](https://github.com/axios/axios/issues/47))
|
||||
|
||||
### 0.5.0 (Jan 23, 2015)
|
||||
|
||||
- Adding support for intercepetors ([#14](https://github.com/axios/axios/issues/14))
|
||||
- Updating es6-promise dependency
|
||||
|
||||
### 0.4.2 (Dec 10, 2014)
|
||||
|
||||
- Fixing issue with `Content-Type` when using `FormData` ([#22](https://github.com/axios/axios/issues/22))
|
||||
- Adding support for TypeScript ([#25](https://github.com/axios/axios/issues/25))
|
||||
- Fixing issue with standalone build ([#29](https://github.com/axios/axios/issues/29))
|
||||
- Fixing issue with verbs needing to be capitalized in some browsers ([#30](https://github.com/axios/axios/issues/30))
|
||||
|
||||
### 0.4.1 (Oct 15, 2014)
|
||||
|
||||
- Adding error handling to request for node.js ([#18](https://github.com/axios/axios/issues/18))
|
||||
|
||||
### 0.4.0 (Oct 03, 2014)
|
||||
|
||||
- Adding support for `ArrayBuffer` and `ArrayBufferView` ([#10](https://github.com/axios/axios/issues/10))
|
||||
- Adding support for utf-8 for node.js ([#13](https://github.com/axios/axios/issues/13))
|
||||
- Adding support for SSL for node.js ([#12](https://github.com/axios/axios/issues/12))
|
||||
- Fixing incorrect `Content-Type` header ([#9](https://github.com/axios/axios/issues/9))
|
||||
- Adding standalone build without bundled es6-promise ([#11](https://github.com/axios/axios/issues/11))
|
||||
- Deprecating `success`/`error` in favor of `then`/`catch`
|
||||
|
||||
### 0.3.1 (Sep 16, 2014)
|
||||
|
||||
- Fixing missing post body when using node.js ([#3](https://github.com/axios/axios/issues/3))
|
||||
|
||||
### 0.3.0 (Sep 16, 2014)
|
||||
|
||||
- Fixing `success` and `error` to properly receive response data as individual arguments ([#8](https://github.com/axios/axios/issues/8))
|
||||
- Updating `then` and `catch` to receive response data as a single object ([#6](https://github.com/axios/axios/issues/6))
|
||||
- Fixing issue with `all` not working ([#7](https://github.com/axios/axios/issues/7))
|
||||
|
||||
### 0.2.2 (Sep 14, 2014)
|
||||
|
||||
- Fixing bundling with browserify ([#4](https://github.com/axios/axios/issues/4))
|
||||
|
||||
### 0.2.1 (Sep 12, 2014)
|
||||
|
||||
- Fixing build problem causing ridiculous file sizes
|
||||
|
||||
### 0.2.0 (Sep 12, 2014)
|
||||
|
||||
- Adding support for `all` and `spread`
|
||||
- Adding support for node.js ([#1](https://github.com/axios/axios/issues/1))
|
||||
|
||||
### 0.1.0 (Aug 29, 2014)
|
||||
|
||||
- Initial release
|
|
@ -0,0 +1,19 @@
|
|||
Copyright (c) 2014-present Matt Zabriskie
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
|
@ -0,0 +1,800 @@
|
|||
# axios
|
||||
|
||||
[![npm version](https://img.shields.io/npm/v/axios.svg?style=flat-square)](https://www.npmjs.org/package/axios)
|
||||
[![CDNJS](https://img.shields.io/cdnjs/v/axios.svg?style=flat-square)](https://cdnjs.com/libraries/axios)
|
||||
[![build status](https://img.shields.io/travis/axios/axios/master.svg?style=flat-square)](https://travis-ci.org/axios/axios)
|
||||
[![code coverage](https://img.shields.io/coveralls/mzabriskie/axios.svg?style=flat-square)](https://coveralls.io/r/mzabriskie/axios)
|
||||
[![install size](https://packagephobia.now.sh/badge?p=axios)](https://packagephobia.now.sh/result?p=axios)
|
||||
[![npm downloads](https://img.shields.io/npm/dm/axios.svg?style=flat-square)](http://npm-stat.com/charts.html?package=axios)
|
||||
[![gitter chat](https://img.shields.io/gitter/room/mzabriskie/axios.svg?style=flat-square)](https://gitter.im/mzabriskie/axios)
|
||||
[![code helpers](https://www.codetriage.com/axios/axios/badges/users.svg)](https://www.codetriage.com/axios/axios)
|
||||
|
||||
Promise based HTTP client for the browser and node.js
|
||||
## Table of Contents
|
||||
|
||||
- [Features](#features)
|
||||
- [Browser Support](#browser-support)
|
||||
- [Installing](#installing)
|
||||
- [Example](#example)
|
||||
- [Axios API](#axios-api)
|
||||
- [Request method aliases](#request-method-aliases)
|
||||
- [Concurrency (Deprecated)](#concurrency-deprecated)
|
||||
- [Creating an instance](#creating-an-instance)
|
||||
- [Instance methods](#instance-methods)
|
||||
- [Request Config](#request-config)
|
||||
- [Response Schema](#response-schema)
|
||||
- [Config Defaults](#config-defaults)
|
||||
- [Global axios defaults](#global-axios-defaults)
|
||||
- [Custom instance defaults](#custom-instance-defaults)
|
||||
- [Config order of precedence](#config-order-of-precedence)
|
||||
- [Interceptors](#interceptors)
|
||||
- [Handling Errors](#handling-errors)
|
||||
- [Cancellation](#cancellation)
|
||||
- [Using application/x-www-form-urlencoded format](#using-applicationx-www-form-urlencoded-format)
|
||||
- [Browser](#browser)
|
||||
- [Node.js](#nodejs)
|
||||
- [Query string](#query-string)
|
||||
- [Form data](#form-data)
|
||||
- [Semver](#semver)
|
||||
- [Promises](#promises)
|
||||
- [TypeScript](#typescript)
|
||||
- [Resources](#resources)
|
||||
- [Credits](#credits)
|
||||
- [License](#license)
|
||||
|
||||
## Features
|
||||
|
||||
- Make [XMLHttpRequests](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest) from the browser
|
||||
- Make [http](http://nodejs.org/api/http.html) requests from node.js
|
||||
- Supports the [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) API
|
||||
- Intercept request and response
|
||||
- Transform request and response data
|
||||
- Cancel requests
|
||||
- Automatic transforms for JSON data
|
||||
- Client side support for protecting against [XSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery)
|
||||
|
||||
## Browser Support
|
||||
|
||||
![Chrome](https://raw.github.com/alrra/browser-logos/master/src/chrome/chrome_48x48.png) | ![Firefox](https://raw.github.com/alrra/browser-logos/master/src/firefox/firefox_48x48.png) | ![Safari](https://raw.github.com/alrra/browser-logos/master/src/safari/safari_48x48.png) | ![Opera](https://raw.github.com/alrra/browser-logos/master/src/opera/opera_48x48.png) | ![Edge](https://raw.github.com/alrra/browser-logos/master/src/edge/edge_48x48.png) | ![IE](https://raw.github.com/alrra/browser-logos/master/src/archive/internet-explorer_9-11/internet-explorer_9-11_48x48.png) |
|
||||
--- | --- | --- | --- | --- | --- |
|
||||
Latest ✔ | Latest ✔ | Latest ✔ | Latest ✔ | Latest ✔ | 11 ✔ |
|
||||
|
||||
[![Browser Matrix](https://saucelabs.com/open_sauce/build_matrix/axios.svg)](https://saucelabs.com/u/axios)
|
||||
|
||||
## Installing
|
||||
|
||||
Using npm:
|
||||
|
||||
```bash
|
||||
$ npm install axios
|
||||
```
|
||||
|
||||
Using bower:
|
||||
|
||||
```bash
|
||||
$ bower install axios
|
||||
```
|
||||
|
||||
Using yarn:
|
||||
|
||||
```bash
|
||||
$ yarn add axios
|
||||
```
|
||||
|
||||
Using jsDelivr CDN:
|
||||
|
||||
```html
|
||||
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
|
||||
```
|
||||
|
||||
Using unpkg CDN:
|
||||
|
||||
```html
|
||||
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
|
||||
```
|
||||
|
||||
## Example
|
||||
|
||||
### note: CommonJS usage
|
||||
In order to gain the TypeScript typings (for intellisense / autocomplete) while using CommonJS imports with `require()` use the following approach:
|
||||
|
||||
```js
|
||||
const axios = require('axios').default;
|
||||
|
||||
// axios.<method> will now provide autocomplete and parameter typings
|
||||
```
|
||||
|
||||
Performing a `GET` request
|
||||
|
||||
```js
|
||||
const axios = require('axios');
|
||||
|
||||
// Make a request for a user with a given ID
|
||||
axios.get('/user?ID=12345')
|
||||
.then(function (response) {
|
||||
// handle success
|
||||
console.log(response);
|
||||
})
|
||||
.catch(function (error) {
|
||||
// handle error
|
||||
console.log(error);
|
||||
})
|
||||
.then(function () {
|
||||
// always executed
|
||||
});
|
||||
|
||||
// Optionally the request above could also be done as
|
||||
axios.get('/user', {
|
||||
params: {
|
||||
ID: 12345
|
||||
}
|
||||
})
|
||||
.then(function (response) {
|
||||
console.log(response);
|
||||
})
|
||||
.catch(function (error) {
|
||||
console.log(error);
|
||||
})
|
||||
.then(function () {
|
||||
// always executed
|
||||
});
|
||||
|
||||
// Want to use async/await? Add the `async` keyword to your outer function/method.
|
||||
async function getUser() {
|
||||
try {
|
||||
const response = await axios.get('/user?ID=12345');
|
||||
console.log(response);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
> **NOTE:** `async/await` is part of ECMAScript 2017 and is not supported in Internet
|
||||
> Explorer and older browsers, so use with caution.
|
||||
|
||||
Performing a `POST` request
|
||||
|
||||
```js
|
||||
axios.post('/user', {
|
||||
firstName: 'Fred',
|
||||
lastName: 'Flintstone'
|
||||
})
|
||||
.then(function (response) {
|
||||
console.log(response);
|
||||
})
|
||||
.catch(function (error) {
|
||||
console.log(error);
|
||||
});
|
||||
```
|
||||
|
||||
Performing multiple concurrent requests
|
||||
|
||||
```js
|
||||
function getUserAccount() {
|
||||
return axios.get('/user/12345');
|
||||
}
|
||||
|
||||
function getUserPermissions() {
|
||||
return axios.get('/user/12345/permissions');
|
||||
}
|
||||
|
||||
Promise.all([getUserAccount(), getUserPermissions()])
|
||||
.then(function (results) {
|
||||
const acct = results[0];
|
||||
const perm = results[1];
|
||||
});
|
||||
```
|
||||
|
||||
## axios API
|
||||
|
||||
Requests can be made by passing the relevant config to `axios`.
|
||||
|
||||
##### axios(config)
|
||||
|
||||
```js
|
||||
// Send a POST request
|
||||
axios({
|
||||
method: 'post',
|
||||
url: '/user/12345',
|
||||
data: {
|
||||
firstName: 'Fred',
|
||||
lastName: 'Flintstone'
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
```js
|
||||
// GET request for remote image in node.js
|
||||
axios({
|
||||
method: 'get',
|
||||
url: 'http://bit.ly/2mTM3nY',
|
||||
responseType: 'stream'
|
||||
})
|
||||
.then(function (response) {
|
||||
response.data.pipe(fs.createWriteStream('ada_lovelace.jpg'))
|
||||
});
|
||||
```
|
||||
|
||||
##### axios(url[, config])
|
||||
|
||||
```js
|
||||
// Send a GET request (default method)
|
||||
axios('/user/12345');
|
||||
```
|
||||
|
||||
### Request method aliases
|
||||
|
||||
For convenience aliases have been provided for all supported request methods.
|
||||
|
||||
##### axios.request(config)
|
||||
##### axios.get(url[, config])
|
||||
##### axios.delete(url[, config])
|
||||
##### axios.head(url[, config])
|
||||
##### axios.options(url[, config])
|
||||
##### axios.post(url[, data[, config]])
|
||||
##### axios.put(url[, data[, config]])
|
||||
##### axios.patch(url[, data[, config]])
|
||||
|
||||
###### NOTE
|
||||
When using the alias methods `url`, `method`, and `data` properties don't need to be specified in config.
|
||||
|
||||
### Concurrency (Deprecated)
|
||||
Please use `Promise.all` to replace the below functions.
|
||||
|
||||
Helper functions for dealing with concurrent requests.
|
||||
|
||||
axios.all(iterable)
|
||||
axios.spread(callback)
|
||||
|
||||
### Creating an instance
|
||||
|
||||
You can create a new instance of axios with a custom config.
|
||||
|
||||
##### axios.create([config])
|
||||
|
||||
```js
|
||||
const instance = axios.create({
|
||||
baseURL: 'https://some-domain.com/api/',
|
||||
timeout: 1000,
|
||||
headers: {'X-Custom-Header': 'foobar'}
|
||||
});
|
||||
```
|
||||
|
||||
### Instance methods
|
||||
|
||||
The available instance methods are listed below. The specified config will be merged with the instance config.
|
||||
|
||||
##### axios#request(config)
|
||||
##### axios#get(url[, config])
|
||||
##### axios#delete(url[, config])
|
||||
##### axios#head(url[, config])
|
||||
##### axios#options(url[, config])
|
||||
##### axios#post(url[, data[, config]])
|
||||
##### axios#put(url[, data[, config]])
|
||||
##### axios#patch(url[, data[, config]])
|
||||
##### axios#getUri([config])
|
||||
|
||||
## Request Config
|
||||
|
||||
These are the available config options for making requests. Only the `url` is required. Requests will default to `GET` if `method` is not specified.
|
||||
|
||||
```js
|
||||
{
|
||||
// `url` is the server URL that will be used for the request
|
||||
url: '/user',
|
||||
|
||||
// `method` is the request method to be used when making the request
|
||||
method: 'get', // default
|
||||
|
||||
// `baseURL` will be prepended to `url` unless `url` is absolute.
|
||||
// It can be convenient to set `baseURL` for an instance of axios to pass relative URLs
|
||||
// to methods of that instance.
|
||||
baseURL: 'https://some-domain.com/api/',
|
||||
|
||||
// `transformRequest` allows changes to the request data before it is sent to the server
|
||||
// This is only applicable for request methods 'PUT', 'POST', 'PATCH' and 'DELETE'
|
||||
// The last function in the array must return a string or an instance of Buffer, ArrayBuffer,
|
||||
// FormData or Stream
|
||||
// You may modify the headers object.
|
||||
transformRequest: [function (data, headers) {
|
||||
// Do whatever you want to transform the data
|
||||
|
||||
return data;
|
||||
}],
|
||||
|
||||
// `transformResponse` allows changes to the response data to be made before
|
||||
// it is passed to then/catch
|
||||
transformResponse: [function (data) {
|
||||
// Do whatever you want to transform the data
|
||||
|
||||
return data;
|
||||
}],
|
||||
|
||||
// `headers` are custom headers to be sent
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
|
||||
// `params` are the URL parameters to be sent with the request
|
||||
// Must be a plain object or a URLSearchParams object
|
||||
params: {
|
||||
ID: 12345
|
||||
},
|
||||
|
||||
// `paramsSerializer` is an optional function in charge of serializing `params`
|
||||
// (e.g. https://www.npmjs.com/package/qs, http://api.jquery.com/jquery.param/)
|
||||
paramsSerializer: function (params) {
|
||||
return Qs.stringify(params, {arrayFormat: 'brackets'})
|
||||
},
|
||||
|
||||
// `data` is the data to be sent as the request body
|
||||
// Only applicable for request methods 'PUT', 'POST', 'DELETE , and 'PATCH'
|
||||
// When no `transformRequest` is set, must be of one of the following types:
|
||||
// - string, plain object, ArrayBuffer, ArrayBufferView, URLSearchParams
|
||||
// - Browser only: FormData, File, Blob
|
||||
// - Node only: Stream, Buffer
|
||||
data: {
|
||||
firstName: 'Fred'
|
||||
},
|
||||
|
||||
// syntax alternative to send data into the body
|
||||
// method post
|
||||
// only the value is sent, not the key
|
||||
data: 'Country=Brasil&City=Belo Horizonte',
|
||||
|
||||
// `timeout` specifies the number of milliseconds before the request times out.
|
||||
// If the request takes longer than `timeout`, the request will be aborted.
|
||||
timeout: 1000, // default is `0` (no timeout)
|
||||
|
||||
// `withCredentials` indicates whether or not cross-site Access-Control requests
|
||||
// should be made using credentials
|
||||
withCredentials: false, // default
|
||||
|
||||
// `adapter` allows custom handling of requests which makes testing easier.
|
||||
// Return a promise and supply a valid response (see lib/adapters/README.md).
|
||||
adapter: function (config) {
|
||||
/* ... */
|
||||
},
|
||||
|
||||
// `auth` indicates that HTTP Basic auth should be used, and supplies credentials.
|
||||
// This will set an `Authorization` header, overwriting any existing
|
||||
// `Authorization` custom headers you have set using `headers`.
|
||||
// Please note that only HTTP Basic auth is configurable through this parameter.
|
||||
// For Bearer tokens and such, use `Authorization` custom headers instead.
|
||||
auth: {
|
||||
username: 'janedoe',
|
||||
password: 's00pers3cret'
|
||||
},
|
||||
|
||||
// `responseType` indicates the type of data that the server will respond with
|
||||
// options are: 'arraybuffer', 'document', 'json', 'text', 'stream'
|
||||
// browser only: 'blob'
|
||||
responseType: 'json', // default
|
||||
|
||||
// `responseEncoding` indicates encoding to use for decoding responses (Node.js only)
|
||||
// Note: Ignored for `responseType` of 'stream' or client-side requests
|
||||
responseEncoding: 'utf8', // default
|
||||
|
||||
// `xsrfCookieName` is the name of the cookie to use as a value for xsrf token
|
||||
xsrfCookieName: 'XSRF-TOKEN', // default
|
||||
|
||||
// `xsrfHeaderName` is the name of the http header that carries the xsrf token value
|
||||
xsrfHeaderName: 'X-XSRF-TOKEN', // default
|
||||
|
||||
// `onUploadProgress` allows handling of progress events for uploads
|
||||
// browser only
|
||||
onUploadProgress: function (progressEvent) {
|
||||
// Do whatever you want with the native progress event
|
||||
},
|
||||
|
||||
// `onDownloadProgress` allows handling of progress events for downloads
|
||||
// browser only
|
||||
onDownloadProgress: function (progressEvent) {
|
||||
// Do whatever you want with the native progress event
|
||||
},
|
||||
|
||||
// `maxContentLength` defines the max size of the http response content in bytes allowed in node.js
|
||||
maxContentLength: 2000,
|
||||
|
||||
// `maxBodyLength` (Node only option) defines the max size of the http request content in bytes allowed
|
||||
maxBodyLength: 2000,
|
||||
|
||||
// `validateStatus` defines whether to resolve or reject the promise for a given
|
||||
// HTTP response status code. If `validateStatus` returns `true` (or is set to `null`
|
||||
// or `undefined`), the promise will be resolved; otherwise, the promise will be
|
||||
// rejected.
|
||||
validateStatus: function (status) {
|
||||
return status >= 200 && status < 300; // default
|
||||
},
|
||||
|
||||
// `maxRedirects` defines the maximum number of redirects to follow in node.js.
|
||||
// If set to 0, no redirects will be followed.
|
||||
maxRedirects: 5, // default
|
||||
|
||||
// `socketPath` defines a UNIX Socket to be used in node.js.
|
||||
// e.g. '/var/run/docker.sock' to send requests to the docker daemon.
|
||||
// Only either `socketPath` or `proxy` can be specified.
|
||||
// If both are specified, `socketPath` is used.
|
||||
socketPath: null, // default
|
||||
|
||||
// `httpAgent` and `httpsAgent` define a custom agent to be used when performing http
|
||||
// and https requests, respectively, in node.js. This allows options to be added like
|
||||
// `keepAlive` that are not enabled by default.
|
||||
httpAgent: new http.Agent({ keepAlive: true }),
|
||||
httpsAgent: new https.Agent({ keepAlive: true }),
|
||||
|
||||
// `proxy` defines the hostname, port, and protocol of the proxy server.
|
||||
// You can also define your proxy using the conventional `http_proxy` and
|
||||
// `https_proxy` environment variables. If you are using environment variables
|
||||
// for your proxy configuration, you can also define a `no_proxy` environment
|
||||
// variable as a comma-separated list of domains that should not be proxied.
|
||||
// Use `false` to disable proxies, ignoring environment variables.
|
||||
// `auth` indicates that HTTP Basic auth should be used to connect to the proxy, and
|
||||
// supplies credentials.
|
||||
// This will set an `Proxy-Authorization` header, overwriting any existing
|
||||
// `Proxy-Authorization` custom headers you have set using `headers`.
|
||||
// If the proxy server uses HTTPS, then you must set the protocol to `https`.
|
||||
proxy: {
|
||||
protocol: 'https',
|
||||
host: '127.0.0.1',
|
||||
port: 9000,
|
||||
auth: {
|
||||
username: 'mikeymike',
|
||||
password: 'rapunz3l'
|
||||
}
|
||||
},
|
||||
|
||||
// `cancelToken` specifies a cancel token that can be used to cancel the request
|
||||
// (see Cancellation section below for details)
|
||||
cancelToken: new CancelToken(function (cancel) {
|
||||
}),
|
||||
|
||||
// `decompress` indicates whether or not the response body should be decompressed
|
||||
// automatically. If set to `true` will also remove the 'content-encoding' header
|
||||
// from the responses objects of all decompressed responses
|
||||
// - Node only (XHR cannot turn off decompression)
|
||||
decompress: true // default
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
## Response Schema
|
||||
|
||||
The response for a request contains the following information.
|
||||
|
||||
```js
|
||||
{
|
||||
// `data` is the response that was provided by the server
|
||||
data: {},
|
||||
|
||||
// `status` is the HTTP status code from the server response
|
||||
status: 200,
|
||||
|
||||
// `statusText` is the HTTP status message from the server response
|
||||
statusText: 'OK',
|
||||
|
||||
// `headers` the HTTP headers that the server responded with
|
||||
// All header names are lower cased and can be accessed using the bracket notation.
|
||||
// Example: `response.headers['content-type']`
|
||||
headers: {},
|
||||
|
||||
// `config` is the config that was provided to `axios` for the request
|
||||
config: {},
|
||||
|
||||
// `request` is the request that generated this response
|
||||
// It is the last ClientRequest instance in node.js (in redirects)
|
||||
// and an XMLHttpRequest instance in the browser
|
||||
request: {}
|
||||
}
|
||||
```
|
||||
|
||||
When using `then`, you will receive the response as follows:
|
||||
|
||||
```js
|
||||
axios.get('/user/12345')
|
||||
.then(function (response) {
|
||||
console.log(response.data);
|
||||
console.log(response.status);
|
||||
console.log(response.statusText);
|
||||
console.log(response.headers);
|
||||
console.log(response.config);
|
||||
});
|
||||
```
|
||||
|
||||
When using `catch`, or passing a [rejection callback](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/then) as second parameter of `then`, the response will be available through the `error` object as explained in the [Handling Errors](#handling-errors) section.
|
||||
|
||||
## Config Defaults
|
||||
|
||||
You can specify config defaults that will be applied to every request.
|
||||
|
||||
### Global axios defaults
|
||||
|
||||
```js
|
||||
axios.defaults.baseURL = 'https://api.example.com';
|
||||
axios.defaults.headers.common['Authorization'] = AUTH_TOKEN;
|
||||
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
|
||||
```
|
||||
|
||||
### Custom instance defaults
|
||||
|
||||
```js
|
||||
// Set config defaults when creating the instance
|
||||
const instance = axios.create({
|
||||
baseURL: 'https://api.example.com'
|
||||
});
|
||||
|
||||
// Alter defaults after instance has been created
|
||||
instance.defaults.headers.common['Authorization'] = AUTH_TOKEN;
|
||||
```
|
||||
|
||||
### Config order of precedence
|
||||
|
||||
Config will be merged with an order of precedence. The order is library defaults found in [lib/defaults.js](https://github.com/axios/axios/blob/master/lib/defaults.js#L28), then `defaults` property of the instance, and finally `config` argument for the request. The latter will take precedence over the former. Here's an example.
|
||||
|
||||
```js
|
||||
// Create an instance using the config defaults provided by the library
|
||||
// At this point the timeout config value is `0` as is the default for the library
|
||||
const instance = axios.create();
|
||||
|
||||
// Override timeout default for the library
|
||||
// Now all requests using this instance will wait 2.5 seconds before timing out
|
||||
instance.defaults.timeout = 2500;
|
||||
|
||||
// Override timeout for this request as it's known to take a long time
|
||||
instance.get('/longRequest', {
|
||||
timeout: 5000
|
||||
});
|
||||
```
|
||||
|
||||
## Interceptors
|
||||
|
||||
You can intercept requests or responses before they are handled by `then` or `catch`.
|
||||
|
||||
```js
|
||||
// Add a request interceptor
|
||||
axios.interceptors.request.use(function (config) {
|
||||
// Do something before request is sent
|
||||
return config;
|
||||
}, function (error) {
|
||||
// Do something with request error
|
||||
return Promise.reject(error);
|
||||
});
|
||||
|
||||
// Add a response interceptor
|
||||
axios.interceptors.response.use(function (response) {
|
||||
// Any status code that lie within the range of 2xx cause this function to trigger
|
||||
// Do something with response data
|
||||
return response;
|
||||
}, function (error) {
|
||||
// Any status codes that falls outside the range of 2xx cause this function to trigger
|
||||
// Do something with response error
|
||||
return Promise.reject(error);
|
||||
});
|
||||
```
|
||||
|
||||
If you need to remove an interceptor later you can.
|
||||
|
||||
```js
|
||||
const myInterceptor = axios.interceptors.request.use(function () {/*...*/});
|
||||
axios.interceptors.request.eject(myInterceptor);
|
||||
```
|
||||
|
||||
You can add interceptors to a custom instance of axios.
|
||||
|
||||
```js
|
||||
const instance = axios.create();
|
||||
instance.interceptors.request.use(function () {/*...*/});
|
||||
```
|
||||
|
||||
## Handling Errors
|
||||
|
||||
```js
|
||||
axios.get('/user/12345')
|
||||
.catch(function (error) {
|
||||
if (error.response) {
|
||||
// The request was made and the server responded with a status code
|
||||
// that falls out of the range of 2xx
|
||||
console.log(error.response.data);
|
||||
console.log(error.response.status);
|
||||
console.log(error.response.headers);
|
||||
} else if (error.request) {
|
||||
// The request was made but no response was received
|
||||
// `error.request` is an instance of XMLHttpRequest in the browser and an instance of
|
||||
// http.ClientRequest in node.js
|
||||
console.log(error.request);
|
||||
} else {
|
||||
// Something happened in setting up the request that triggered an Error
|
||||
console.log('Error', error.message);
|
||||
}
|
||||
console.log(error.config);
|
||||
});
|
||||
```
|
||||
|
||||
Using the `validateStatus` config option, you can define HTTP code(s) that should throw an error.
|
||||
|
||||
```js
|
||||
axios.get('/user/12345', {
|
||||
validateStatus: function (status) {
|
||||
return status < 500; // Resolve only if the status code is less than 500
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
Using `toJSON` you get an object with more information about the HTTP error.
|
||||
|
||||
```js
|
||||
axios.get('/user/12345')
|
||||
.catch(function (error) {
|
||||
console.log(error.toJSON());
|
||||
});
|
||||
```
|
||||
|
||||
## Cancellation
|
||||
|
||||
You can cancel a request using a *cancel token*.
|
||||
|
||||
> The axios cancel token API is based on the withdrawn [cancelable promises proposal](https://github.com/tc39/proposal-cancelable-promises).
|
||||
|
||||
You can create a cancel token using the `CancelToken.source` factory as shown below:
|
||||
|
||||
```js
|
||||
const CancelToken = axios.CancelToken;
|
||||
const source = CancelToken.source();
|
||||
|
||||
axios.get('/user/12345', {
|
||||
cancelToken: source.token
|
||||
}).catch(function (thrown) {
|
||||
if (axios.isCancel(thrown)) {
|
||||
console.log('Request canceled', thrown.message);
|
||||
} else {
|
||||
// handle error
|
||||
}
|
||||
});
|
||||
|
||||
axios.post('/user/12345', {
|
||||
name: 'new name'
|
||||
}, {
|
||||
cancelToken: source.token
|
||||
})
|
||||
|
||||
// cancel the request (the message parameter is optional)
|
||||
source.cancel('Operation canceled by the user.');
|
||||
```
|
||||
|
||||
You can also create a cancel token by passing an executor function to the `CancelToken` constructor:
|
||||
|
||||
```js
|
||||
const CancelToken = axios.CancelToken;
|
||||
let cancel;
|
||||
|
||||
axios.get('/user/12345', {
|
||||
cancelToken: new CancelToken(function executor(c) {
|
||||
// An executor function receives a cancel function as a parameter
|
||||
cancel = c;
|
||||
})
|
||||
});
|
||||
|
||||
// cancel the request
|
||||
cancel();
|
||||
```
|
||||
|
||||
> Note: you can cancel several requests with the same cancel token.
|
||||
|
||||
## Using application/x-www-form-urlencoded format
|
||||
|
||||
By default, axios serializes JavaScript objects to `JSON`. To send data in the `application/x-www-form-urlencoded` format instead, you can use one of the following options.
|
||||
|
||||
### Browser
|
||||
|
||||
In a browser, you can use the [`URLSearchParams`](https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams) API as follows:
|
||||
|
||||
```js
|
||||
const params = new URLSearchParams();
|
||||
params.append('param1', 'value1');
|
||||
params.append('param2', 'value2');
|
||||
axios.post('/foo', params);
|
||||
```
|
||||
|
||||
> Note that `URLSearchParams` is not supported by all browsers (see [caniuse.com](http://www.caniuse.com/#feat=urlsearchparams)), but there is a [polyfill](https://github.com/WebReflection/url-search-params) available (make sure to polyfill the global environment).
|
||||
|
||||
Alternatively, you can encode data using the [`qs`](https://github.com/ljharb/qs) library:
|
||||
|
||||
```js
|
||||
const qs = require('qs');
|
||||
axios.post('/foo', qs.stringify({ 'bar': 123 }));
|
||||
```
|
||||
|
||||
Or in another way (ES6),
|
||||
|
||||
```js
|
||||
import qs from 'qs';
|
||||
const data = { 'bar': 123 };
|
||||
const options = {
|
||||
method: 'POST',
|
||||
headers: { 'content-type': 'application/x-www-form-urlencoded' },
|
||||
data: qs.stringify(data),
|
||||
url,
|
||||
};
|
||||
axios(options);
|
||||
```
|
||||
|
||||
### Node.js
|
||||
|
||||
#### Query string
|
||||
|
||||
In node.js, you can use the [`querystring`](https://nodejs.org/api/querystring.html) module as follows:
|
||||
|
||||
```js
|
||||
const querystring = require('querystring');
|
||||
axios.post('http://something.com/', querystring.stringify({ foo: 'bar' }));
|
||||
```
|
||||
|
||||
or ['URLSearchParams'](https://nodejs.org/api/url.html#url_class_urlsearchparams) from ['url module'](https://nodejs.org/api/url.html) as follows:
|
||||
|
||||
```js
|
||||
const url = require('url');
|
||||
const params = new url.URLSearchParams({ foo: 'bar' });
|
||||
axios.post('http://something.com/', params.toString());
|
||||
```
|
||||
|
||||
You can also use the [`qs`](https://github.com/ljharb/qs) library.
|
||||
|
||||
###### NOTE
|
||||
The `qs` library is preferable if you need to stringify nested objects, as the `querystring` method has known issues with that use case (https://github.com/nodejs/node-v0.x-archive/issues/1665).
|
||||
|
||||
#### Form data
|
||||
|
||||
In node.js, you can use the [`form-data`](https://github.com/form-data/form-data) library as follows:
|
||||
|
||||
```js
|
||||
const FormData = require('form-data');
|
||||
|
||||
const form = new FormData();
|
||||
form.append('my_field', 'my value');
|
||||
form.append('my_buffer', new Buffer(10));
|
||||
form.append('my_file', fs.createReadStream('/foo/bar.jpg'));
|
||||
|
||||
axios.post('https://example.com', form, { headers: form.getHeaders() })
|
||||
```
|
||||
|
||||
Alternatively, use an interceptor:
|
||||
|
||||
```js
|
||||
axios.interceptors.request.use(config => {
|
||||
if (config.data instanceof FormData) {
|
||||
Object.assign(config.headers, config.data.getHeaders());
|
||||
}
|
||||
return config;
|
||||
});
|
||||
```
|
||||
|
||||
## Semver
|
||||
|
||||
Until axios reaches a `1.0` release, breaking changes will be released with a new minor version. For example `0.5.1`, and `0.5.4` will have the same API, but `0.6.0` will have breaking changes.
|
||||
|
||||
## Promises
|
||||
|
||||
axios depends on a native ES6 Promise implementation to be [supported](http://caniuse.com/promises).
|
||||
If your environment doesn't support ES6 Promises, you can [polyfill](https://github.com/jakearchibald/es6-promise).
|
||||
|
||||
## TypeScript
|
||||
axios includes [TypeScript](http://typescriptlang.org) definitions.
|
||||
```typescript
|
||||
import axios from 'axios';
|
||||
axios.get('/user?ID=12345');
|
||||
```
|
||||
|
||||
## Resources
|
||||
|
||||
* [Changelog](https://github.com/axios/axios/blob/master/CHANGELOG.md)
|
||||
* [Upgrade Guide](https://github.com/axios/axios/blob/master/UPGRADE_GUIDE.md)
|
||||
* [Ecosystem](https://github.com/axios/axios/blob/master/ECOSYSTEM.md)
|
||||
* [Contributing Guide](https://github.com/axios/axios/blob/master/CONTRIBUTING.md)
|
||||
* [Code of Conduct](https://github.com/axios/axios/blob/master/CODE_OF_CONDUCT.md)
|
||||
|
||||
## Credits
|
||||
|
||||
axios is heavily inspired by the [$http service](https://docs.angularjs.org/api/ng/service/$http) provided in [Angular](https://angularjs.org/). Ultimately axios is an effort to provide a standalone `$http`-like service for use outside of Angular.
|
||||
|
||||
## License
|
||||
|
||||
[MIT](LICENSE)
|
|
@ -0,0 +1,162 @@
|
|||
# Upgrade Guide
|
||||
|
||||
### 0.15.x -> 0.16.0
|
||||
|
||||
#### `Promise` Type Declarations
|
||||
|
||||
The `Promise` type declarations have been removed from the axios typings in favor of the built-in type declarations. If you use axios in a TypeScript project that targets `ES5`, please make sure to include the `es2015.promise` lib. Please see [this post](https://blog.mariusschulz.com/2016/11/25/typescript-2-0-built-in-type-declarations) for details.
|
||||
|
||||
### 0.13.x -> 0.14.0
|
||||
|
||||
#### TypeScript Definitions
|
||||
|
||||
The axios TypeScript definitions have been updated to match the axios API and use the ES2015 module syntax.
|
||||
|
||||
Please use the following `import` statement to import axios in TypeScript:
|
||||
|
||||
```typescript
|
||||
import axios from 'axios';
|
||||
|
||||
axios.get('/foo')
|
||||
.then(response => console.log(response))
|
||||
.catch(error => console.log(error));
|
||||
```
|
||||
|
||||
#### `agent` Config Option
|
||||
|
||||
The `agent` config option has been replaced with two new options: `httpAgent` and `httpsAgent`. Please use them instead.
|
||||
|
||||
```js
|
||||
{
|
||||
// Define a custom agent for HTTP
|
||||
httpAgent: new http.Agent({ keepAlive: true }),
|
||||
// Define a custom agent for HTTPS
|
||||
httpsAgent: new https.Agent({ keepAlive: true })
|
||||
}
|
||||
```
|
||||
|
||||
#### `progress` Config Option
|
||||
|
||||
The `progress` config option has been replaced with the `onUploadProgress` and `onDownloadProgress` options.
|
||||
|
||||
```js
|
||||
{
|
||||
// Define a handler for upload progress events
|
||||
onUploadProgress: function (progressEvent) {
|
||||
// ...
|
||||
},
|
||||
|
||||
// Define a handler for download progress events
|
||||
onDownloadProgress: function (progressEvent) {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 0.12.x -> 0.13.0
|
||||
|
||||
The `0.13.0` release contains several changes to custom adapters and error handling.
|
||||
|
||||
#### Error Handling
|
||||
|
||||
Previous to this release an error could either be a server response with bad status code or an actual `Error`. With this release Promise will always reject with an `Error`. In the case that a response was received, the `Error` will also include the response.
|
||||
|
||||
```js
|
||||
axios.get('/user/12345')
|
||||
.catch((error) => {
|
||||
console.log(error.message);
|
||||
console.log(error.code); // Not always specified
|
||||
console.log(error.config); // The config that was used to make the request
|
||||
console.log(error.response); // Only available if response was received from the server
|
||||
});
|
||||
```
|
||||
|
||||
#### Request Adapters
|
||||
|
||||
This release changes a few things about how request adapters work. Please take note if you are using your own custom adapter.
|
||||
|
||||
1. Response transformer is now called outside of adapter.
|
||||
2. Request adapter returns a `Promise`.
|
||||
|
||||
This means that you no longer need to invoke `transformData` on response data. You will also no longer receive `resolve` and `reject` as arguments in your adapter.
|
||||
|
||||
Previous code:
|
||||
|
||||
```js
|
||||
function myAdapter(resolve, reject, config) {
|
||||
var response = {
|
||||
data: transformData(
|
||||
responseData,
|
||||
responseHeaders,
|
||||
config.transformResponse
|
||||
),
|
||||
status: request.status,
|
||||
statusText: request.statusText,
|
||||
headers: responseHeaders
|
||||
};
|
||||
settle(resolve, reject, response);
|
||||
}
|
||||
```
|
||||
|
||||
New code:
|
||||
|
||||
```js
|
||||
function myAdapter(config) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
var response = {
|
||||
data: responseData,
|
||||
status: request.status,
|
||||
statusText: request.statusText,
|
||||
headers: responseHeaders
|
||||
};
|
||||
settle(resolve, reject, response);
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
See the related commits for more details:
|
||||
- [Response transformers](https://github.com/axios/axios/commit/10eb23865101f9347570552c04e9d6211376e25e)
|
||||
- [Request adapter Promise](https://github.com/axios/axios/commit/157efd5615890301824e3121cc6c9d2f9b21f94a)
|
||||
|
||||
### 0.5.x -> 0.6.0
|
||||
|
||||
The `0.6.0` release contains mostly bug fixes, but there are a couple things to be aware of when upgrading.
|
||||
|
||||
#### ES6 Promise Polyfill
|
||||
|
||||
Up until the `0.6.0` release ES6 `Promise` was being polyfilled using [es6-promise](https://github.com/jakearchibald/es6-promise). With this release, the polyfill has been removed, and you will need to supply it yourself if your environment needs it.
|
||||
|
||||
```js
|
||||
require('es6-promise').polyfill();
|
||||
var axios = require('axios');
|
||||
```
|
||||
|
||||
This will polyfill the global environment, and only needs to be done once.
|
||||
|
||||
#### `axios.success`/`axios.error`
|
||||
|
||||
The `success`, and `error` aliases were deprecated in [0.4.0](https://github.com/axios/axios/blob/master/CHANGELOG.md#040-oct-03-2014). As of this release they have been removed entirely. Instead please use `axios.then`, and `axios.catch` respectively.
|
||||
|
||||
```js
|
||||
axios.get('some/url')
|
||||
.then(function (res) {
|
||||
/* ... */
|
||||
})
|
||||
.catch(function (err) {
|
||||
/* ... */
|
||||
});
|
||||
```
|
||||
|
||||
#### UMD
|
||||
|
||||
Previous versions of axios shipped with an AMD, CommonJS, and Global build. This has all been rolled into a single UMD build.
|
||||
|
||||
```js
|
||||
// AMD
|
||||
require(['bower_components/axios/dist/axios'], function (axios) {
|
||||
/* ... */
|
||||
});
|
||||
|
||||
// CommonJS
|
||||
var axios = require('axios/dist/axios');
|
||||
```
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,161 @@
|
|||
export interface AxiosTransformer {
|
||||
(data: any, headers?: any): any;
|
||||
}
|
||||
|
||||
export interface AxiosAdapter {
|
||||
(config: AxiosRequestConfig): AxiosPromise<any>;
|
||||
}
|
||||
|
||||
export interface AxiosBasicCredentials {
|
||||
username: string;
|
||||
password: string;
|
||||
}
|
||||
|
||||
export interface AxiosProxyConfig {
|
||||
host: string;
|
||||
port: number;
|
||||
auth?: {
|
||||
username: string;
|
||||
password:string;
|
||||
};
|
||||
protocol?: string;
|
||||
}
|
||||
|
||||
export type Method =
|
||||
| 'get' | 'GET'
|
||||
| 'delete' | 'DELETE'
|
||||
| 'head' | 'HEAD'
|
||||
| 'options' | 'OPTIONS'
|
||||
| 'post' | 'POST'
|
||||
| 'put' | 'PUT'
|
||||
| 'patch' | 'PATCH'
|
||||
| 'purge' | 'PURGE'
|
||||
| 'link' | 'LINK'
|
||||
| 'unlink' | 'UNLINK'
|
||||
|
||||
export type ResponseType =
|
||||
| 'arraybuffer'
|
||||
| 'blob'
|
||||
| 'document'
|
||||
| 'json'
|
||||
| 'text'
|
||||
| 'stream'
|
||||
|
||||
export interface AxiosRequestConfig {
|
||||
url?: string;
|
||||
method?: Method;
|
||||
baseURL?: string;
|
||||
transformRequest?: AxiosTransformer | AxiosTransformer[];
|
||||
transformResponse?: AxiosTransformer | AxiosTransformer[];
|
||||
headers?: any;
|
||||
params?: any;
|
||||
paramsSerializer?: (params: any) => string;
|
||||
data?: any;
|
||||
timeout?: number;
|
||||
timeoutErrorMessage?: string;
|
||||
withCredentials?: boolean;
|
||||
adapter?: AxiosAdapter;
|
||||
auth?: AxiosBasicCredentials;
|
||||
responseType?: ResponseType;
|
||||
xsrfCookieName?: string;
|
||||
xsrfHeaderName?: string;
|
||||
onUploadProgress?: (progressEvent: any) => void;
|
||||
onDownloadProgress?: (progressEvent: any) => void;
|
||||
maxContentLength?: number;
|
||||
validateStatus?: ((status: number) => boolean) | null;
|
||||
maxBodyLength?: number;
|
||||
maxRedirects?: number;
|
||||
socketPath?: string | null;
|
||||
httpAgent?: any;
|
||||
httpsAgent?: any;
|
||||
proxy?: AxiosProxyConfig | false;
|
||||
cancelToken?: CancelToken;
|
||||
decompress?: boolean;
|
||||
}
|
||||
|
||||
export interface AxiosResponse<T = any> {
|
||||
data: T;
|
||||
status: number;
|
||||
statusText: string;
|
||||
headers: any;
|
||||
config: AxiosRequestConfig;
|
||||
request?: any;
|
||||
}
|
||||
|
||||
export interface AxiosError<T = any> extends Error {
|
||||
config: AxiosRequestConfig;
|
||||
code?: string;
|
||||
request?: any;
|
||||
response?: AxiosResponse<T>;
|
||||
isAxiosError: boolean;
|
||||
toJSON: () => object;
|
||||
}
|
||||
|
||||
export interface AxiosPromise<T = any> extends Promise<AxiosResponse<T>> {
|
||||
}
|
||||
|
||||
export interface CancelStatic {
|
||||
new (message?: string): Cancel;
|
||||
}
|
||||
|
||||
export interface Cancel {
|
||||
message: string;
|
||||
}
|
||||
|
||||
export interface Canceler {
|
||||
(message?: string): void;
|
||||
}
|
||||
|
||||
export interface CancelTokenStatic {
|
||||
new (executor: (cancel: Canceler) => void): CancelToken;
|
||||
source(): CancelTokenSource;
|
||||
}
|
||||
|
||||
export interface CancelToken {
|
||||
promise: Promise<Cancel>;
|
||||
reason?: Cancel;
|
||||
throwIfRequested(): void;
|
||||
}
|
||||
|
||||
export interface CancelTokenSource {
|
||||
token: CancelToken;
|
||||
cancel: Canceler;
|
||||
}
|
||||
|
||||
export interface AxiosInterceptorManager<V> {
|
||||
use(onFulfilled?: (value: V) => V | Promise<V>, onRejected?: (error: any) => any): number;
|
||||
eject(id: number): void;
|
||||
}
|
||||
|
||||
export interface AxiosInstance {
|
||||
(config: AxiosRequestConfig): AxiosPromise;
|
||||
(url: string, config?: AxiosRequestConfig): AxiosPromise;
|
||||
defaults: AxiosRequestConfig;
|
||||
interceptors: {
|
||||
request: AxiosInterceptorManager<AxiosRequestConfig>;
|
||||
response: AxiosInterceptorManager<AxiosResponse>;
|
||||
};
|
||||
getUri(config?: AxiosRequestConfig): string;
|
||||
request<T = any, R = AxiosResponse<T>> (config: AxiosRequestConfig): Promise<R>;
|
||||
get<T = any, R = AxiosResponse<T>>(url: string, config?: AxiosRequestConfig): Promise<R>;
|
||||
delete<T = any, R = AxiosResponse<T>>(url: string, config?: AxiosRequestConfig): Promise<R>;
|
||||
head<T = any, R = AxiosResponse<T>>(url: string, config?: AxiosRequestConfig): Promise<R>;
|
||||
options<T = any, R = AxiosResponse<T>>(url: string, config?: AxiosRequestConfig): Promise<R>;
|
||||
post<T = any, R = AxiosResponse<T>>(url: string, data?: any, config?: AxiosRequestConfig): Promise<R>;
|
||||
put<T = any, R = AxiosResponse<T>>(url: string, data?: any, config?: AxiosRequestConfig): Promise<R>;
|
||||
patch<T = any, R = AxiosResponse<T>>(url: string, data?: any, config?: AxiosRequestConfig): Promise<R>;
|
||||
}
|
||||
|
||||
export interface AxiosStatic extends AxiosInstance {
|
||||
create(config?: AxiosRequestConfig): AxiosInstance;
|
||||
Cancel: CancelStatic;
|
||||
CancelToken: CancelTokenStatic;
|
||||
isCancel(value: any): boolean;
|
||||
all<T>(values: (T | Promise<T>)[]): Promise<T[]>;
|
||||
spread<T, R>(callback: (...args: T[]) => R): (array: T[]) => R;
|
||||
isAxiosError(payload: any): payload is AxiosError;
|
||||
}
|
||||
|
||||
declare const axios: AxiosStatic;
|
||||
|
||||
export default axios;
|
|
@ -0,0 +1 @@
|
|||
module.exports = require('./lib/axios');
|
|
@ -0,0 +1,37 @@
|
|||
# axios // adapters
|
||||
|
||||
The modules under `adapters/` are modules that handle dispatching a request and settling a returned `Promise` once a response is received.
|
||||
|
||||
## Example
|
||||
|
||||
```js
|
||||
var settle = require('./../core/settle');
|
||||
|
||||
module.exports = function myAdapter(config) {
|
||||
// At this point:
|
||||
// - config has been merged with defaults
|
||||
// - request transformers have already run
|
||||
// - request interceptors have already run
|
||||
|
||||
// Make the request using config provided
|
||||
// Upon response settle the Promise
|
||||
|
||||
return new Promise(function(resolve, reject) {
|
||||
|
||||
var response = {
|
||||
data: responseData,
|
||||
status: request.status,
|
||||
statusText: request.statusText,
|
||||
headers: responseHeaders,
|
||||
config: config,
|
||||
request: request
|
||||
};
|
||||
|
||||
settle(resolve, reject, response);
|
||||
|
||||
// From here:
|
||||
// - response transformers will run
|
||||
// - response interceptors will run
|
||||
});
|
||||
}
|
||||
```
|
|
@ -0,0 +1,303 @@
|
|||
'use strict';
|
||||
|
||||
var utils = require('./../utils');
|
||||
var settle = require('./../core/settle');
|
||||
var buildFullPath = require('../core/buildFullPath');
|
||||
var buildURL = require('./../helpers/buildURL');
|
||||
var http = require('http');
|
||||
var https = require('https');
|
||||
var httpFollow = require('follow-redirects').http;
|
||||
var httpsFollow = require('follow-redirects').https;
|
||||
var url = require('url');
|
||||
var zlib = require('zlib');
|
||||
var pkg = require('./../../package.json');
|
||||
var createError = require('../core/createError');
|
||||
var enhanceError = require('../core/enhanceError');
|
||||
|
||||
var isHttps = /https:?/;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {http.ClientRequestArgs} options
|
||||
* @param {AxiosProxyConfig} proxy
|
||||
* @param {string} location
|
||||
*/
|
||||
function setProxy(options, proxy, location) {
|
||||
options.hostname = proxy.host;
|
||||
options.host = proxy.host;
|
||||
options.port = proxy.port;
|
||||
options.path = location;
|
||||
|
||||
// Basic proxy authorization
|
||||
if (proxy.auth) {
|
||||
var base64 = Buffer.from(proxy.auth.username + ':' + proxy.auth.password, 'utf8').toString('base64');
|
||||
options.headers['Proxy-Authorization'] = 'Basic ' + base64;
|
||||
}
|
||||
|
||||
// If a proxy is used, any redirects must also pass through the proxy
|
||||
options.beforeRedirect = function beforeRedirect(redirection) {
|
||||
redirection.headers.host = redirection.host;
|
||||
setProxy(redirection, proxy, redirection.href);
|
||||
};
|
||||
}
|
||||
|
||||
/*eslint consistent-return:0*/
|
||||
module.exports = function httpAdapter(config) {
|
||||
return new Promise(function dispatchHttpRequest(resolvePromise, rejectPromise) {
|
||||
var resolve = function resolve(value) {
|
||||
resolvePromise(value);
|
||||
};
|
||||
var reject = function reject(value) {
|
||||
rejectPromise(value);
|
||||
};
|
||||
var data = config.data;
|
||||
var headers = config.headers;
|
||||
|
||||
// Set User-Agent (required by some servers)
|
||||
// Only set header if it hasn't been set in config
|
||||
// See https://github.com/axios/axios/issues/69
|
||||
if (!headers['User-Agent'] && !headers['user-agent']) {
|
||||
headers['User-Agent'] = 'axios/' + pkg.version;
|
||||
}
|
||||
|
||||
if (data && !utils.isStream(data)) {
|
||||
if (Buffer.isBuffer(data)) {
|
||||
// Nothing to do...
|
||||
} else if (utils.isArrayBuffer(data)) {
|
||||
data = Buffer.from(new Uint8Array(data));
|
||||
} else if (utils.isString(data)) {
|
||||
data = Buffer.from(data, 'utf-8');
|
||||
} else {
|
||||
return reject(createError(
|
||||
'Data after transformation must be a string, an ArrayBuffer, a Buffer, or a Stream',
|
||||
config
|
||||
));
|
||||
}
|
||||
|
||||
// Add Content-Length header if data exists
|
||||
headers['Content-Length'] = data.length;
|
||||
}
|
||||
|
||||
// HTTP basic authentication
|
||||
var auth = undefined;
|
||||
if (config.auth) {
|
||||
var username = config.auth.username || '';
|
||||
var password = config.auth.password || '';
|
||||
auth = username + ':' + password;
|
||||
}
|
||||
|
||||
// Parse url
|
||||
var fullPath = buildFullPath(config.baseURL, config.url);
|
||||
var parsed = url.parse(fullPath);
|
||||
var protocol = parsed.protocol || 'http:';
|
||||
|
||||
if (!auth && parsed.auth) {
|
||||
var urlAuth = parsed.auth.split(':');
|
||||
var urlUsername = urlAuth[0] || '';
|
||||
var urlPassword = urlAuth[1] || '';
|
||||
auth = urlUsername + ':' + urlPassword;
|
||||
}
|
||||
|
||||
if (auth) {
|
||||
delete headers.Authorization;
|
||||
}
|
||||
|
||||
var isHttpsRequest = isHttps.test(protocol);
|
||||
var agent = isHttpsRequest ? config.httpsAgent : config.httpAgent;
|
||||
|
||||
var options = {
|
||||
path: buildURL(parsed.path, config.params, config.paramsSerializer).replace(/^\?/, ''),
|
||||
method: config.method.toUpperCase(),
|
||||
headers: headers,
|
||||
agent: agent,
|
||||
agents: { http: config.httpAgent, https: config.httpsAgent },
|
||||
auth: auth
|
||||
};
|
||||
|
||||
if (config.socketPath) {
|
||||
options.socketPath = config.socketPath;
|
||||
} else {
|
||||
options.hostname = parsed.hostname;
|
||||
options.port = parsed.port;
|
||||
}
|
||||
|
||||
var proxy = config.proxy;
|
||||
if (!proxy && proxy !== false) {
|
||||
var proxyEnv = protocol.slice(0, -1) + '_proxy';
|
||||
var proxyUrl = process.env[proxyEnv] || process.env[proxyEnv.toUpperCase()];
|
||||
if (proxyUrl) {
|
||||
var parsedProxyUrl = url.parse(proxyUrl);
|
||||
var noProxyEnv = process.env.no_proxy || process.env.NO_PROXY;
|
||||
var shouldProxy = true;
|
||||
|
||||
if (noProxyEnv) {
|
||||
var noProxy = noProxyEnv.split(',').map(function trim(s) {
|
||||
return s.trim();
|
||||
});
|
||||
|
||||
shouldProxy = !noProxy.some(function proxyMatch(proxyElement) {
|
||||
if (!proxyElement) {
|
||||
return false;
|
||||
}
|
||||
if (proxyElement === '*') {
|
||||
return true;
|
||||
}
|
||||
if (proxyElement[0] === '.' &&
|
||||
parsed.hostname.substr(parsed.hostname.length - proxyElement.length) === proxyElement) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return parsed.hostname === proxyElement;
|
||||
});
|
||||
}
|
||||
|
||||
if (shouldProxy) {
|
||||
proxy = {
|
||||
host: parsedProxyUrl.hostname,
|
||||
port: parsedProxyUrl.port,
|
||||
protocol: parsedProxyUrl.protocol
|
||||
};
|
||||
|
||||
if (parsedProxyUrl.auth) {
|
||||
var proxyUrlAuth = parsedProxyUrl.auth.split(':');
|
||||
proxy.auth = {
|
||||
username: proxyUrlAuth[0],
|
||||
password: proxyUrlAuth[1]
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (proxy) {
|
||||
options.headers.host = parsed.hostname + (parsed.port ? ':' + parsed.port : '');
|
||||
setProxy(options, proxy, protocol + '//' + parsed.hostname + (parsed.port ? ':' + parsed.port : '') + options.path);
|
||||
}
|
||||
|
||||
var transport;
|
||||
var isHttpsProxy = isHttpsRequest && (proxy ? isHttps.test(proxy.protocol) : true);
|
||||
if (config.transport) {
|
||||
transport = config.transport;
|
||||
} else if (config.maxRedirects === 0) {
|
||||
transport = isHttpsProxy ? https : http;
|
||||
} else {
|
||||
if (config.maxRedirects) {
|
||||
options.maxRedirects = config.maxRedirects;
|
||||
}
|
||||
transport = isHttpsProxy ? httpsFollow : httpFollow;
|
||||
}
|
||||
|
||||
if (config.maxBodyLength > -1) {
|
||||
options.maxBodyLength = config.maxBodyLength;
|
||||
}
|
||||
|
||||
// Create the request
|
||||
var req = transport.request(options, function handleResponse(res) {
|
||||
if (req.aborted) return;
|
||||
|
||||
// uncompress the response body transparently if required
|
||||
var stream = res;
|
||||
|
||||
// return the last request in case of redirects
|
||||
var lastRequest = res.req || req;
|
||||
|
||||
|
||||
// if no content, is HEAD request or decompress disabled we should not decompress
|
||||
if (res.statusCode !== 204 && lastRequest.method !== 'HEAD' && config.decompress !== false) {
|
||||
switch (res.headers['content-encoding']) {
|
||||
/*eslint default-case:0*/
|
||||
case 'gzip':
|
||||
case 'compress':
|
||||
case 'deflate':
|
||||
// add the unzipper to the body stream processing pipeline
|
||||
stream = stream.pipe(zlib.createUnzip());
|
||||
|
||||
// remove the content-encoding in order to not confuse downstream operations
|
||||
delete res.headers['content-encoding'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var response = {
|
||||
status: res.statusCode,
|
||||
statusText: res.statusMessage,
|
||||
headers: res.headers,
|
||||
config: config,
|
||||
request: lastRequest
|
||||
};
|
||||
|
||||
if (config.responseType === 'stream') {
|
||||
response.data = stream;
|
||||
settle(resolve, reject, response);
|
||||
} else {
|
||||
var responseBuffer = [];
|
||||
stream.on('data', function handleStreamData(chunk) {
|
||||
responseBuffer.push(chunk);
|
||||
|
||||
// make sure the content length is not over the maxContentLength if specified
|
||||
if (config.maxContentLength > -1 && Buffer.concat(responseBuffer).length > config.maxContentLength) {
|
||||
stream.destroy();
|
||||
reject(createError('maxContentLength size of ' + config.maxContentLength + ' exceeded',
|
||||
config, null, lastRequest));
|
||||
}
|
||||
});
|
||||
|
||||
stream.on('error', function handleStreamError(err) {
|
||||
if (req.aborted) return;
|
||||
reject(enhanceError(err, config, null, lastRequest));
|
||||
});
|
||||
|
||||
stream.on('end', function handleStreamEnd() {
|
||||
var responseData = Buffer.concat(responseBuffer);
|
||||
if (config.responseType !== 'arraybuffer') {
|
||||
responseData = responseData.toString(config.responseEncoding);
|
||||
if (!config.responseEncoding || config.responseEncoding === 'utf8') {
|
||||
responseData = utils.stripBOM(responseData);
|
||||
}
|
||||
}
|
||||
|
||||
response.data = responseData;
|
||||
settle(resolve, reject, response);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Handle errors
|
||||
req.on('error', function handleRequestError(err) {
|
||||
if (req.aborted && err.code !== 'ERR_FR_TOO_MANY_REDIRECTS') return;
|
||||
reject(enhanceError(err, config, null, req));
|
||||
});
|
||||
|
||||
// Handle request timeout
|
||||
if (config.timeout) {
|
||||
// Sometime, the response will be very slow, and does not respond, the connect event will be block by event loop system.
|
||||
// And timer callback will be fired, and abort() will be invoked before connection, then get "socket hang up" and code ECONNRESET.
|
||||
// At this time, if we have a large number of request, nodejs will hang up some socket on background. and the number will up and up.
|
||||
// And then these socket which be hang up will devoring CPU little by little.
|
||||
// ClientRequest.setTimeout will be fired on the specify milliseconds, and can make sure that abort() will be fired after connect.
|
||||
req.setTimeout(config.timeout, function handleRequestTimeout() {
|
||||
req.abort();
|
||||
reject(createError('timeout of ' + config.timeout + 'ms exceeded', config, 'ECONNABORTED', req));
|
||||
});
|
||||
}
|
||||
|
||||
if (config.cancelToken) {
|
||||
// Handle cancellation
|
||||
config.cancelToken.promise.then(function onCanceled(cancel) {
|
||||
if (req.aborted) return;
|
||||
|
||||
req.abort();
|
||||
reject(cancel);
|
||||
});
|
||||
}
|
||||
|
||||
// Send the request
|
||||
if (utils.isStream(data)) {
|
||||
data.on('error', function handleStreamError(err) {
|
||||
reject(enhanceError(err, config, null, req));
|
||||
}).pipe(req);
|
||||
} else {
|
||||
req.end(data);
|
||||
}
|
||||
});
|
||||
};
|
|
@ -0,0 +1,179 @@
|
|||
'use strict';
|
||||
|
||||
var utils = require('./../utils');
|
||||
var settle = require('./../core/settle');
|
||||
var cookies = require('./../helpers/cookies');
|
||||
var buildURL = require('./../helpers/buildURL');
|
||||
var buildFullPath = require('../core/buildFullPath');
|
||||
var parseHeaders = require('./../helpers/parseHeaders');
|
||||
var isURLSameOrigin = require('./../helpers/isURLSameOrigin');
|
||||
var createError = require('../core/createError');
|
||||
|
||||
module.exports = function xhrAdapter(config) {
|
||||
return new Promise(function dispatchXhrRequest(resolve, reject) {
|
||||
var requestData = config.data;
|
||||
var requestHeaders = config.headers;
|
||||
|
||||
if (utils.isFormData(requestData)) {
|
||||
delete requestHeaders['Content-Type']; // Let the browser set it
|
||||
}
|
||||
|
||||
var request = new XMLHttpRequest();
|
||||
|
||||
// HTTP basic authentication
|
||||
if (config.auth) {
|
||||
var username = config.auth.username || '';
|
||||
var password = config.auth.password ? unescape(encodeURIComponent(config.auth.password)) : '';
|
||||
requestHeaders.Authorization = 'Basic ' + btoa(username + ':' + password);
|
||||
}
|
||||
|
||||
var fullPath = buildFullPath(config.baseURL, config.url);
|
||||
request.open(config.method.toUpperCase(), buildURL(fullPath, config.params, config.paramsSerializer), true);
|
||||
|
||||
// Set the request timeout in MS
|
||||
request.timeout = config.timeout;
|
||||
|
||||
// Listen for ready state
|
||||
request.onreadystatechange = function handleLoad() {
|
||||
if (!request || request.readyState !== 4) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The request errored out and we didn't get a response, this will be
|
||||
// handled by onerror instead
|
||||
// With one exception: request that using file: protocol, most browsers
|
||||
// will return status as 0 even though it's a successful request
|
||||
if (request.status === 0 && !(request.responseURL && request.responseURL.indexOf('file:') === 0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Prepare the response
|
||||
var responseHeaders = 'getAllResponseHeaders' in request ? parseHeaders(request.getAllResponseHeaders()) : null;
|
||||
var responseData = !config.responseType || config.responseType === 'text' ? request.responseText : request.response;
|
||||
var response = {
|
||||
data: responseData,
|
||||
status: request.status,
|
||||
statusText: request.statusText,
|
||||
headers: responseHeaders,
|
||||
config: config,
|
||||
request: request
|
||||
};
|
||||
|
||||
settle(resolve, reject, response);
|
||||
|
||||
// Clean up request
|
||||
request = null;
|
||||
};
|
||||
|
||||
// Handle browser request cancellation (as opposed to a manual cancellation)
|
||||
request.onabort = function handleAbort() {
|
||||
if (!request) {
|
||||
return;
|
||||
}
|
||||
|
||||
reject(createError('Request aborted', config, 'ECONNABORTED', request));
|
||||
|
||||
// Clean up request
|
||||
request = null;
|
||||
};
|
||||
|
||||
// Handle low level network errors
|
||||
request.onerror = function handleError() {
|
||||
// Real errors are hidden from us by the browser
|
||||
// onerror should only fire if it's a network error
|
||||
reject(createError('Network Error', config, null, request));
|
||||
|
||||
// Clean up request
|
||||
request = null;
|
||||
};
|
||||
|
||||
// Handle timeout
|
||||
request.ontimeout = function handleTimeout() {
|
||||
var timeoutErrorMessage = 'timeout of ' + config.timeout + 'ms exceeded';
|
||||
if (config.timeoutErrorMessage) {
|
||||
timeoutErrorMessage = config.timeoutErrorMessage;
|
||||
}
|
||||
reject(createError(timeoutErrorMessage, config, 'ECONNABORTED',
|
||||
request));
|
||||
|
||||
// Clean up request
|
||||
request = null;
|
||||
};
|
||||
|
||||
// Add xsrf header
|
||||
// This is only done if running in a standard browser environment.
|
||||
// Specifically not if we're in a web worker, or react-native.
|
||||
if (utils.isStandardBrowserEnv()) {
|
||||
// Add xsrf header
|
||||
var xsrfValue = (config.withCredentials || isURLSameOrigin(fullPath)) && config.xsrfCookieName ?
|
||||
cookies.read(config.xsrfCookieName) :
|
||||
undefined;
|
||||
|
||||
if (xsrfValue) {
|
||||
requestHeaders[config.xsrfHeaderName] = xsrfValue;
|
||||
}
|
||||
}
|
||||
|
||||
// Add headers to the request
|
||||
if ('setRequestHeader' in request) {
|
||||
utils.forEach(requestHeaders, function setRequestHeader(val, key) {
|
||||
if (typeof requestData === 'undefined' && key.toLowerCase() === 'content-type') {
|
||||
// Remove Content-Type if data is undefined
|
||||
delete requestHeaders[key];
|
||||
} else {
|
||||
// Otherwise add header to the request
|
||||
request.setRequestHeader(key, val);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Add withCredentials to request if needed
|
||||
if (!utils.isUndefined(config.withCredentials)) {
|
||||
request.withCredentials = !!config.withCredentials;
|
||||
}
|
||||
|
||||
// Add responseType to request if needed
|
||||
if (config.responseType) {
|
||||
try {
|
||||
request.responseType = config.responseType;
|
||||
} catch (e) {
|
||||
// Expected DOMException thrown by browsers not compatible XMLHttpRequest Level 2.
|
||||
// But, this can be suppressed for 'json' type as it can be parsed by default 'transformResponse' function.
|
||||
if (config.responseType !== 'json') {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle progress if needed
|
||||
if (typeof config.onDownloadProgress === 'function') {
|
||||
request.addEventListener('progress', config.onDownloadProgress);
|
||||
}
|
||||
|
||||
// Not all browsers support upload events
|
||||
if (typeof config.onUploadProgress === 'function' && request.upload) {
|
||||
request.upload.addEventListener('progress', config.onUploadProgress);
|
||||
}
|
||||
|
||||
if (config.cancelToken) {
|
||||
// Handle cancellation
|
||||
config.cancelToken.promise.then(function onCanceled(cancel) {
|
||||
if (!request) {
|
||||
return;
|
||||
}
|
||||
|
||||
request.abort();
|
||||
reject(cancel);
|
||||
// Clean up request
|
||||
request = null;
|
||||
});
|
||||
}
|
||||
|
||||
if (!requestData) {
|
||||
requestData = null;
|
||||
}
|
||||
|
||||
// Send the request
|
||||
request.send(requestData);
|
||||
});
|
||||
};
|
|
@ -0,0 +1,56 @@
|
|||
'use strict';
|
||||
|
||||
var utils = require('./utils');
|
||||
var bind = require('./helpers/bind');
|
||||
var Axios = require('./core/Axios');
|
||||
var mergeConfig = require('./core/mergeConfig');
|
||||
var defaults = require('./defaults');
|
||||
|
||||
/**
|
||||
* Create an instance of Axios
|
||||
*
|
||||
* @param {Object} defaultConfig The default config for the instance
|
||||
* @return {Axios} A new instance of Axios
|
||||
*/
|
||||
function createInstance(defaultConfig) {
|
||||
var context = new Axios(defaultConfig);
|
||||
var instance = bind(Axios.prototype.request, context);
|
||||
|
||||
// Copy axios.prototype to instance
|
||||
utils.extend(instance, Axios.prototype, context);
|
||||
|
||||
// Copy context to instance
|
||||
utils.extend(instance, context);
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
// Create the default instance to be exported
|
||||
var axios = createInstance(defaults);
|
||||
|
||||
// Expose Axios class to allow class inheritance
|
||||
axios.Axios = Axios;
|
||||
|
||||
// Factory for creating new instances
|
||||
axios.create = function create(instanceConfig) {
|
||||
return createInstance(mergeConfig(axios.defaults, instanceConfig));
|
||||
};
|
||||
|
||||
// Expose Cancel & CancelToken
|
||||
axios.Cancel = require('./cancel/Cancel');
|
||||
axios.CancelToken = require('./cancel/CancelToken');
|
||||
axios.isCancel = require('./cancel/isCancel');
|
||||
|
||||
// Expose all/spread
|
||||
axios.all = function all(promises) {
|
||||
return Promise.all(promises);
|
||||
};
|
||||
axios.spread = require('./helpers/spread');
|
||||
|
||||
// Expose isAxiosError
|
||||
axios.isAxiosError = require('./helpers/isAxiosError');
|
||||
|
||||
module.exports = axios;
|
||||
|
||||
// Allow use of default import syntax in TypeScript
|
||||
module.exports.default = axios;
|
|
@ -0,0 +1,19 @@
|
|||
'use strict';
|
||||
|
||||
/**
|
||||
* A `Cancel` is an object that is thrown when an operation is canceled.
|
||||
*
|
||||
* @class
|
||||
* @param {string=} message The message.
|
||||
*/
|
||||
function Cancel(message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
Cancel.prototype.toString = function toString() {
|
||||
return 'Cancel' + (this.message ? ': ' + this.message : '');
|
||||
};
|
||||
|
||||
Cancel.prototype.__CANCEL__ = true;
|
||||
|
||||
module.exports = Cancel;
|
|
@ -0,0 +1,57 @@
|
|||
'use strict';
|
||||
|
||||
var Cancel = require('./Cancel');
|
||||
|
||||
/**
|
||||
* A `CancelToken` is an object that can be used to request cancellation of an operation.
|
||||
*
|
||||
* @class
|
||||
* @param {Function} executor The executor function.
|
||||
*/
|
||||
function CancelToken(executor) {
|
||||
if (typeof executor !== 'function') {
|
||||
throw new TypeError('executor must be a function.');
|
||||
}
|
||||
|
||||
var resolvePromise;
|
||||
this.promise = new Promise(function promiseExecutor(resolve) {
|
||||
resolvePromise = resolve;
|
||||
});
|
||||
|
||||
var token = this;
|
||||
executor(function cancel(message) {
|
||||
if (token.reason) {
|
||||
// Cancellation has already been requested
|
||||
return;
|
||||
}
|
||||
|
||||
token.reason = new Cancel(message);
|
||||
resolvePromise(token.reason);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Throws a `Cancel` if cancellation has been requested.
|
||||
*/
|
||||
CancelToken.prototype.throwIfRequested = function throwIfRequested() {
|
||||
if (this.reason) {
|
||||
throw this.reason;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns an object that contains a new `CancelToken` and a function that, when called,
|
||||
* cancels the `CancelToken`.
|
||||
*/
|
||||
CancelToken.source = function source() {
|
||||
var cancel;
|
||||
var token = new CancelToken(function executor(c) {
|
||||
cancel = c;
|
||||
});
|
||||
return {
|
||||
token: token,
|
||||
cancel: cancel
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = CancelToken;
|
|
@ -0,0 +1,5 @@
|
|||
'use strict';
|
||||
|
||||
module.exports = function isCancel(value) {
|
||||
return !!(value && value.__CANCEL__);
|
||||
};
|
|
@ -0,0 +1,95 @@
|
|||
'use strict';
|
||||
|
||||
var utils = require('./../utils');
|
||||
var buildURL = require('../helpers/buildURL');
|
||||
var InterceptorManager = require('./InterceptorManager');
|
||||
var dispatchRequest = require('./dispatchRequest');
|
||||
var mergeConfig = require('./mergeConfig');
|
||||
|
||||
/**
|
||||
* Create a new instance of Axios
|
||||
*
|
||||
* @param {Object} instanceConfig The default config for the instance
|
||||
*/
|
||||
function Axios(instanceConfig) {
|
||||
this.defaults = instanceConfig;
|
||||
this.interceptors = {
|
||||
request: new InterceptorManager(),
|
||||
response: new InterceptorManager()
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatch a request
|
||||
*
|
||||
* @param {Object} config The config specific for this request (merged with this.defaults)
|
||||
*/
|
||||
Axios.prototype.request = function request(config) {
|
||||
/*eslint no-param-reassign:0*/
|
||||
// Allow for axios('example/url'[, config]) a la fetch API
|
||||
if (typeof config === 'string') {
|
||||
config = arguments[1] || {};
|
||||
config.url = arguments[0];
|
||||
} else {
|
||||
config = config || {};
|
||||
}
|
||||
|
||||
config = mergeConfig(this.defaults, config);
|
||||
|
||||
// Set config.method
|
||||
if (config.method) {
|
||||
config.method = config.method.toLowerCase();
|
||||
} else if (this.defaults.method) {
|
||||
config.method = this.defaults.method.toLowerCase();
|
||||
} else {
|
||||
config.method = 'get';
|
||||
}
|
||||
|
||||
// Hook up interceptors middleware
|
||||
var chain = [dispatchRequest, undefined];
|
||||
var promise = Promise.resolve(config);
|
||||
|
||||
this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) {
|
||||
chain.unshift(interceptor.fulfilled, interceptor.rejected);
|
||||
});
|
||||
|
||||
this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) {
|
||||
chain.push(interceptor.fulfilled, interceptor.rejected);
|
||||
});
|
||||
|
||||
while (chain.length) {
|
||||
promise = promise.then(chain.shift(), chain.shift());
|
||||
}
|
||||
|
||||
return promise;
|
||||
};
|
||||
|
||||
Axios.prototype.getUri = function getUri(config) {
|
||||
config = mergeConfig(this.defaults, config);
|
||||
return buildURL(config.url, config.params, config.paramsSerializer).replace(/^\?/, '');
|
||||
};
|
||||
|
||||
// Provide aliases for supported request methods
|
||||
utils.forEach(['delete', 'get', 'head', 'options'], function forEachMethodNoData(method) {
|
||||
/*eslint func-names:0*/
|
||||
Axios.prototype[method] = function(url, config) {
|
||||
return this.request(mergeConfig(config || {}, {
|
||||
method: method,
|
||||
url: url,
|
||||
data: (config || {}).data
|
||||
}));
|
||||
};
|
||||
});
|
||||
|
||||
utils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) {
|
||||
/*eslint func-names:0*/
|
||||
Axios.prototype[method] = function(url, data, config) {
|
||||
return this.request(mergeConfig(config || {}, {
|
||||
method: method,
|
||||
url: url,
|
||||
data: data
|
||||
}));
|
||||
};
|
||||
});
|
||||
|
||||
module.exports = Axios;
|
|
@ -0,0 +1,52 @@
|
|||
'use strict';
|
||||
|
||||
var utils = require('./../utils');
|
||||
|
||||
function InterceptorManager() {
|
||||
this.handlers = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new interceptor to the stack
|
||||
*
|
||||
* @param {Function} fulfilled The function to handle `then` for a `Promise`
|
||||
* @param {Function} rejected The function to handle `reject` for a `Promise`
|
||||
*
|
||||
* @return {Number} An ID used to remove interceptor later
|
||||
*/
|
||||
InterceptorManager.prototype.use = function use(fulfilled, rejected) {
|
||||
this.handlers.push({
|
||||
fulfilled: fulfilled,
|
||||
rejected: rejected
|
||||
});
|
||||
return this.handlers.length - 1;
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove an interceptor from the stack
|
||||
*
|
||||
* @param {Number} id The ID that was returned by `use`
|
||||
*/
|
||||
InterceptorManager.prototype.eject = function eject(id) {
|
||||
if (this.handlers[id]) {
|
||||
this.handlers[id] = null;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Iterate over all the registered interceptors
|
||||
*
|
||||
* This method is particularly useful for skipping over any
|
||||
* interceptors that may have become `null` calling `eject`.
|
||||
*
|
||||
* @param {Function} fn The function to call for each interceptor
|
||||
*/
|
||||
InterceptorManager.prototype.forEach = function forEach(fn) {
|
||||
utils.forEach(this.handlers, function forEachHandler(h) {
|
||||
if (h !== null) {
|
||||
fn(h);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = InterceptorManager;
|
|
@ -0,0 +1,7 @@
|
|||
# axios // core
|
||||
|
||||
The modules found in `core/` should be modules that are specific to the domain logic of axios. These modules would most likely not make sense to be consumed outside of the axios module, as their logic is too specific. Some examples of core modules are:
|
||||
|
||||
- Dispatching requests
|
||||
- Managing interceptors
|
||||
- Handling config
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue