validate-custom-field-value.spec.ts 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289
  1. import { LanguageCode } from '@vendure/common/lib/generated-types';
  2. import { fail } from 'assert';
  3. import { describe, expect, it } from 'vitest';
  4. import { Injector } from '../../common/injector';
  5. import { RequestContext } from './request-context';
  6. import { validateCustomFieldValue } from './validate-custom-field-value';
  7. describe('validateCustomFieldValue()', () => {
  8. const injector = new Injector({} as any);
  9. async function assertThrowsError(validateFn: (() => Promise<void>) | (() => void), message: string) {
  10. try {
  11. await validateFn();
  12. fail('Should have thrown');
  13. } catch (e: any) {
  14. expect(e.message).toBe(message);
  15. }
  16. }
  17. const ctx = RequestContext.empty();
  18. describe('string & localeString', () => {
  19. const validate = (value: string | null) => () =>
  20. validateCustomFieldValue(
  21. {
  22. name: 'test',
  23. type: 'string',
  24. pattern: '^[0-9]+',
  25. },
  26. value,
  27. injector,
  28. ctx,
  29. );
  30. it('passes valid pattern', async () => {
  31. expect(validate('1')).not.toThrow();
  32. expect(validate('123')).not.toThrow();
  33. expect(validate('1foo')).not.toThrow();
  34. });
  35. it('throws on invalid pattern', async () => {
  36. await assertThrowsError(validate(''), 'error.field-invalid-string-pattern');
  37. await assertThrowsError(validate('foo'), 'error.field-invalid-string-pattern');
  38. await assertThrowsError(validate(' 1foo'), 'error.field-invalid-string-pattern');
  39. });
  40. it('allows null for nullable field with pattern', async () => {
  41. expect(validate(null)).not.toThrow();
  42. });
  43. });
  44. describe('string options', () => {
  45. const validate = (value: string | null) => () =>
  46. validateCustomFieldValue(
  47. {
  48. name: 'test',
  49. type: 'string',
  50. options: [{ value: 'small' }, { value: 'large' }],
  51. },
  52. value,
  53. injector,
  54. ctx,
  55. );
  56. it('passes valid option', async () => {
  57. expect(validate('small')).not.toThrow();
  58. expect(validate('large')).not.toThrow();
  59. });
  60. it('throws on invalid option', async () => {
  61. await assertThrowsError(validate('SMALL'), 'error.field-invalid-string-option');
  62. await assertThrowsError(validate(''), 'error.field-invalid-string-option');
  63. await assertThrowsError(validate('bad'), 'error.field-invalid-string-option');
  64. });
  65. it('allows null for a nullable field', () => {
  66. expect(validate(null)).not.toThrow();
  67. });
  68. it('throws on null for non-nullable field', async () => {
  69. await assertThrowsError(
  70. () =>
  71. validateCustomFieldValue(
  72. {
  73. name: 'test',
  74. type: 'string',
  75. nullable: false,
  76. options: [{ value: 'small' }, { value: 'large' }],
  77. },
  78. null,
  79. injector,
  80. ctx,
  81. ),
  82. 'error.field-invalid-non-nullable',
  83. );
  84. });
  85. });
  86. describe('int & float', () => {
  87. const validate = (value: number | null) => () =>
  88. validateCustomFieldValue(
  89. {
  90. name: 'test',
  91. type: 'int',
  92. min: 5,
  93. max: 10,
  94. },
  95. value,
  96. injector,
  97. ctx,
  98. );
  99. it('passes valid range', async () => {
  100. expect(validate(5)).not.toThrow();
  101. expect(validate(7)).not.toThrow();
  102. expect(validate(10)).not.toThrow();
  103. });
  104. it('throws on invalid range', async () => {
  105. await assertThrowsError(validate(4), 'error.field-invalid-number-range-min');
  106. await assertThrowsError(validate(11), 'error.field-invalid-number-range-max');
  107. await assertThrowsError(validate(-7), 'error.field-invalid-number-range-min');
  108. });
  109. it('allows null for nullable field', async () => {
  110. expect(validate(null)).not.toThrow();
  111. });
  112. });
  113. describe('datetime', () => {
  114. const validate = (value: string | null) => () =>
  115. validateCustomFieldValue(
  116. {
  117. name: 'test',
  118. type: 'datetime',
  119. min: '2019-01-01T08:30',
  120. max: '2019-06-01T08:30',
  121. },
  122. value,
  123. injector,
  124. ctx,
  125. );
  126. it('passes valid range', async () => {
  127. expect(validate('2019-01-01T08:30:00.000')).not.toThrow();
  128. expect(validate('2019-06-01T08:30:00.000')).not.toThrow();
  129. expect(validate('2019-04-12T14:15:51.200')).not.toThrow();
  130. });
  131. it('throws on invalid range', async () => {
  132. await assertThrowsError(
  133. validate('2019-01-01T08:29:00.000'),
  134. 'error.field-invalid-datetime-range-min',
  135. );
  136. await assertThrowsError(
  137. validate('2019-06-01T08:30:00.100'),
  138. 'error.field-invalid-datetime-range-max',
  139. );
  140. });
  141. it('allows null for nullable field', async () => {
  142. expect(validate(null)).not.toThrow();
  143. });
  144. });
  145. describe('validate function', () => {
  146. const validate1 = (value: string) => () =>
  147. validateCustomFieldValue(
  148. {
  149. name: 'test',
  150. type: 'string',
  151. validate: (v: string) => {
  152. if (v !== 'valid') {
  153. return 'invalid';
  154. }
  155. },
  156. },
  157. value,
  158. injector,
  159. ctx,
  160. );
  161. const validate2 = (value: string, languageCode: LanguageCode) => () => {
  162. const ctxWithLanguage = new RequestContext({
  163. languageCode,
  164. apiType: 'admin',
  165. } as any);
  166. return validateCustomFieldValue(
  167. {
  168. name: 'test',
  169. type: 'string',
  170. validate: (v: string) => {
  171. if (v !== 'valid') {
  172. return [
  173. { languageCode: LanguageCode.en, value: 'invalid' },
  174. { languageCode: LanguageCode.de, value: 'ungültig' },
  175. ];
  176. }
  177. },
  178. },
  179. value,
  180. injector,
  181. ctxWithLanguage,
  182. );
  183. };
  184. it('passes validate fn string', async () => {
  185. expect(validate1('valid')).not.toThrow();
  186. });
  187. it('passes validate fn localized string', async () => {
  188. expect(validate2('valid', LanguageCode.de)).not.toThrow();
  189. });
  190. it('fails validate fn string', async () => {
  191. await assertThrowsError(validate1('bad'), 'invalid');
  192. });
  193. it('fails validate fn localized string en', async () => {
  194. await assertThrowsError(validate2('bad', LanguageCode.en), 'invalid');
  195. });
  196. it('fails validate fn localized string de', async () => {
  197. await assertThrowsError(validate2('bad', LanguageCode.de), 'ungültig');
  198. });
  199. });
  200. describe('list types', () => {
  201. it('number list', async () => {
  202. const validate = (value: number[]) => () =>
  203. validateCustomFieldValue(
  204. {
  205. name: 'test',
  206. type: 'int',
  207. list: true,
  208. min: 0,
  209. max: 10,
  210. },
  211. value,
  212. injector,
  213. ctx,
  214. );
  215. expect(validate([1, 2, 6])).not.toThrow();
  216. await assertThrowsError(validate([1, 15, 3]), 'error.field-invalid-number-range-max');
  217. });
  218. it('string list with options', async () => {
  219. const validate = (value: string[]) => () =>
  220. validateCustomFieldValue(
  221. {
  222. name: 'test',
  223. list: true,
  224. type: 'string',
  225. options: [{ value: 'small' }, { value: 'large' }],
  226. },
  227. value,
  228. injector,
  229. ctx,
  230. );
  231. expect(validate(['small', 'large'])).not.toThrow();
  232. await assertThrowsError(validate(['small', 'huge']), 'error.field-invalid-string-option');
  233. });
  234. it('list with validate function', async () => {
  235. const validate = (value: string[]) => () =>
  236. validateCustomFieldValue(
  237. {
  238. name: 'test',
  239. type: 'string',
  240. list: true,
  241. validate: (v: string[]) => {
  242. if (!v.every(val => val === 'valid')) {
  243. return 'invalid';
  244. }
  245. },
  246. },
  247. value,
  248. injector,
  249. ctx,
  250. );
  251. expect(validate(['valid', 'valid'])).not.toThrow();
  252. await assertThrowsError(validate(['bad input', 'valid']), 'invalid');
  253. });
  254. });
  255. });