string.go 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
  1. package httpexpect
  2. import (
  3. "net/http"
  4. "regexp"
  5. "strings"
  6. "time"
  7. )
  8. // String provides methods to inspect attached string value
  9. // (Go representation of JSON string).
  10. type String struct {
  11. chain chain
  12. value string
  13. }
  14. // NewString returns a new String given a reporter used to report failures
  15. // and value to be inspected.
  16. //
  17. // reporter should not be nil.
  18. //
  19. // Example:
  20. // str := NewString(t, "Hello")
  21. func NewString(reporter Reporter, value string) *String {
  22. return &String{makeChain(reporter), value}
  23. }
  24. // Raw returns underlying value attached to String.
  25. // This is the value originally passed to NewString.
  26. //
  27. // Example:
  28. // str := NewString(t, "Hello")
  29. // assert.Equal(t, "Hello", str.Raw())
  30. func (s *String) Raw() string {
  31. return s.value
  32. }
  33. // Path is similar to Value.Path.
  34. func (s *String) Path(path string) *Value {
  35. return getPath(&s.chain, s.value, path)
  36. }
  37. // Schema is similar to Value.Schema.
  38. func (s *String) Schema(schema interface{}) *String {
  39. checkSchema(&s.chain, s.value, schema)
  40. return s
  41. }
  42. // Length returns a new Number object that may be used to inspect string length.
  43. //
  44. // Example:
  45. // str := NewString(t, "Hello")
  46. // str.Length().Equal(5)
  47. func (s *String) Length() *Number {
  48. return &Number{s.chain, float64(len(s.value))}
  49. }
  50. // DateTime parses date/time from string an returns a new DateTime object.
  51. //
  52. // If layout is given, DateTime() uses time.Parse() with given layout.
  53. // Otherwise, it uses http.ParseTime(). If pasing error occurred,
  54. // DateTime reports failure and returns empty (but non-nil) object.
  55. //
  56. // Example:
  57. // str := NewString(t, "Tue, 15 Nov 1994 08:12:31 GMT")
  58. // str.DateTime().Lt(time.Now())
  59. //
  60. // str := NewString(t, "15 Nov 94 08:12 GMT")
  61. // str.DateTime(time.RFC822).Lt(time.Now())
  62. func (s *String) DateTime(layout ...string) *DateTime {
  63. if s.chain.failed() {
  64. return &DateTime{s.chain, time.Unix(0, 0)}
  65. }
  66. var (
  67. t time.Time
  68. err error
  69. )
  70. if len(layout) != 0 {
  71. t, err = time.Parse(layout[0], s.value)
  72. } else {
  73. t, err = http.ParseTime(s.value)
  74. }
  75. if err != nil {
  76. s.chain.fail(err.Error())
  77. return &DateTime{s.chain, time.Unix(0, 0)}
  78. }
  79. return &DateTime{s.chain, t}
  80. }
  81. // Empty succeeds if string is empty.
  82. //
  83. // Example:
  84. // str := NewString(t, "")
  85. // str.Empty()
  86. func (s *String) Empty() *String {
  87. return s.Equal("")
  88. }
  89. // NotEmpty succeeds if string is non-empty.
  90. //
  91. // Example:
  92. // str := NewString(t, "Hello")
  93. // str.NotEmpty()
  94. func (s *String) NotEmpty() *String {
  95. return s.NotEqual("")
  96. }
  97. // Equal succeeds if string is equal to another str.
  98. //
  99. // Example:
  100. // str := NewString(t, "Hello")
  101. // str.Equal("Hello")
  102. func (s *String) Equal(value string) *String {
  103. if !(s.value == value) {
  104. s.chain.fail("\nexpected string equal to:\n %q\n\nbut got:\n %q",
  105. value, s.value)
  106. }
  107. return s
  108. }
  109. // NotEqual succeeds if string is not equal to another str.
  110. //
  111. // Example:
  112. // str := NewString(t, "Hello")
  113. // str.NotEqual("Goodbye")
  114. func (s *String) NotEqual(value string) *String {
  115. if !(s.value != value) {
  116. s.chain.fail("\nexpected string not equal to:\n %q", value)
  117. }
  118. return s
  119. }
  120. // EqualFold succeeds if string is equal to another string under Unicode case-folding
  121. // (case-insensitive match).
  122. //
  123. // Example:
  124. // str := NewString(t, "Hello")
  125. // str.EqualFold("hELLo")
  126. func (s *String) EqualFold(value string) *String {
  127. if !strings.EqualFold(s.value, value) {
  128. s.chain.fail(
  129. "\nexpected string equal to (case-insensitive):\n %q\n\nbut got:\n %q",
  130. value, s.value)
  131. }
  132. return s
  133. }
  134. // NotEqualFold succeeds if string is not equal to another string under Unicode
  135. // case-folding (case-insensitive match).
  136. //
  137. // Example:
  138. // str := NewString(t, "Hello")
  139. // str.NotEqualFold("gOODBYe")
  140. func (s *String) NotEqualFold(value string) *String {
  141. if strings.EqualFold(s.value, value) {
  142. s.chain.fail(
  143. "\nexpected string not equal to (case-insensitive):\n %q\n\nbut got:\n %q",
  144. value, s.value)
  145. }
  146. return s
  147. }
  148. // Contains succeeds if string contains given substr.
  149. //
  150. // Example:
  151. // str := NewString(t, "Hello")
  152. // str.Contains("ell")
  153. func (s *String) Contains(value string) *String {
  154. if !strings.Contains(s.value, value) {
  155. s.chain.fail(
  156. "\nexpected string containing substring:\n %q\n\nbut got:\n %q",
  157. value, s.value)
  158. }
  159. return s
  160. }
  161. // NotContains succeeds if string doesn't contain given substr.
  162. //
  163. // Example:
  164. // str := NewString(t, "Hello")
  165. // str.NotContains("bye")
  166. func (s *String) NotContains(value string) *String {
  167. if strings.Contains(s.value, value) {
  168. s.chain.fail(
  169. "\nexpected string not containing substring:\n %q\n\nbut got:\n %q",
  170. value, s.value)
  171. }
  172. return s
  173. }
  174. // ContainsFold succeeds if string contains given substring under Unicode case-folding
  175. // (case-insensitive match).
  176. //
  177. // Example:
  178. // str := NewString(t, "Hello")
  179. // str.ContainsFold("ELL")
  180. func (s *String) ContainsFold(value string) *String {
  181. if !strings.Contains(strings.ToLower(s.value), strings.ToLower(value)) {
  182. s.chain.fail(
  183. "\nexpected string containing substring (case-insensitive):\n %q"+
  184. "\n\nbut got:\n %q", value, s.value)
  185. }
  186. return s
  187. }
  188. // NotContainsFold succeeds if string doesn't contain given substring under Unicode
  189. // case-folding (case-insensitive match).
  190. //
  191. // Example:
  192. // str := NewString(t, "Hello")
  193. // str.NotContainsFold("BYE")
  194. func (s *String) NotContainsFold(value string) *String {
  195. if strings.Contains(strings.ToLower(s.value), strings.ToLower(value)) {
  196. s.chain.fail(
  197. "\nexpected string not containing substring (case-insensitive):\n %q"+
  198. "\n\nbut got:\n %q", value, s.value)
  199. }
  200. return s
  201. }
  202. // Match matches the string with given regexp and returns a new Match object
  203. // with found submatches.
  204. //
  205. // If regexp is invalid or string doesn't match regexp, Match fails and returns
  206. // empty (but non-nil) object. regexp.Compile is used to construct regexp, and
  207. // Regexp.FindStringSubmatch is used to construct matches.
  208. //
  209. // Example:
  210. // s := NewString(t, "http://example.com/users/john")
  211. // m := s.Match(`http://(?P<host>.+)/users/(?P<user>.+)`)
  212. //
  213. // m.NotEmpty()
  214. // m.Length().Equal(3)
  215. //
  216. // m.Index(0).Equal("http://example.com/users/john")
  217. // m.Index(1).Equal("example.com")
  218. // m.Index(2).Equal("john")
  219. //
  220. // m.Name("host").Equal("example.com")
  221. // m.Name("user").Equal("john")
  222. func (s *String) Match(re string) *Match {
  223. r, err := regexp.Compile(re)
  224. if err != nil {
  225. s.chain.fail(err.Error())
  226. return makeMatch(s.chain, nil, nil)
  227. }
  228. m := r.FindStringSubmatch(s.value)
  229. if m == nil {
  230. s.chain.fail("\nexpected string matching regexp:\n `%s`\n\nbut got:\n %q",
  231. re, s.value)
  232. return makeMatch(s.chain, nil, nil)
  233. }
  234. return makeMatch(s.chain, m, r.SubexpNames())
  235. }
  236. // MatchAll find all matches in string for given regexp and returns a list
  237. // of found matches.
  238. //
  239. // If regexp is invalid or string doesn't match regexp, MatchAll fails and
  240. // returns empty (but non-nil) slice. regexp.Compile is used to construct
  241. // regexp, and Regexp.FindAllStringSubmatch is used to find matches.
  242. //
  243. // Example:
  244. // s := NewString(t,
  245. // "http://example.com/users/john http://example.com/users/bob")
  246. //
  247. // m := s.MatchAll(`http://(?P<host>\S+)/users/(?P<user>\S+)`)
  248. //
  249. // m[0].Name("user").Equal("john")
  250. // m[1].Name("user").Equal("bob")
  251. func (s *String) MatchAll(re string) []Match {
  252. r, err := regexp.Compile(re)
  253. if err != nil {
  254. s.chain.fail(err.Error())
  255. return []Match{}
  256. }
  257. matches := r.FindAllStringSubmatch(s.value, -1)
  258. if matches == nil {
  259. s.chain.fail("\nexpected string matching regexp:\n `%s`\n\nbut got:\n %q",
  260. re, s.value)
  261. return []Match{}
  262. }
  263. ret := []Match{}
  264. for _, m := range matches {
  265. ret = append(ret, *makeMatch(
  266. s.chain,
  267. m,
  268. r.SubexpNames()))
  269. }
  270. return ret
  271. }
  272. // NotMatch succeeds if the string doesn't match to given regexp.
  273. //
  274. // regexp.Compile is used to construct regexp, and Regexp.MatchString
  275. // is used to perform match.
  276. //
  277. // Example:
  278. // s := NewString(t, "a")
  279. // s.NotMatch(`[^a]`)
  280. func (s *String) NotMatch(re string) *String {
  281. r, err := regexp.Compile(re)
  282. if err != nil {
  283. s.chain.fail(err.Error())
  284. return s
  285. }
  286. if r.MatchString(s.value) {
  287. s.chain.fail("\nexpected string not matching regexp:\n `%s`\n\nbut got:\n %q",
  288. re, s.value)
  289. return s
  290. }
  291. return s
  292. }