Guest User

Untitled

a guest
May 27th, 2018
84
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 5.14 KB | None | 0 0
  1. import {IsString, MaxLength, IsNumber, Max, getFromContainer, MetadataStorage, IsEmail, IsOptional, validate} from 'class-validator';
  2. import {ValidationMetadata} from 'class-validator/metadata/ValidationMetadata';
  3. import * as _ from 'lodash';
  4.  
  5. import { InheritValidation } from './inherit-validation.decorator';
  6.  
  7. /**
  8. * Used as a base for validation, in order for partial classes
  9. * to pick validation metadatas, property by property.
  10. */
  11. class Dto {
  12. @IsNumber()
  13. @Max(999)
  14. readonly id: number;
  15.  
  16. @IsString()
  17. @MaxLength(10)
  18. readonly name: number;
  19.  
  20. @IsEmail()
  21. @IsOptional()
  22. readonly email: string;
  23. }
  24. const validationsCount = 6;
  25.  
  26. describe('@InheritValidation', () => {
  27. let dtoMetaDatas: ValidationMetadata[];
  28.  
  29. beforeEach(() => {
  30. dtoMetaDatas = getMetadatasFrom(Dto);
  31. expect(dtoMetaDatas).toHaveLength(validationsCount);
  32. });
  33.  
  34. it('does not modify metadatas of the source class', () => {
  35. const dtoMetadatasNow = getMetadatasFrom(Dto);
  36. expect(dtoMetaDatas).toEqual(dtoMetadatasNow);
  37. });
  38.  
  39. it('does a deep copy of validation metadatas', () => {
  40. class SubDto {
  41. @InheritValidation(Dto, 'name')
  42. readonly name: string;
  43. }
  44.  
  45. const dtoMetadatas = getMetadatasFrom(Dto, 'name');
  46. const subMetadatas = getMetadatasFrom(SubDto, 'name');
  47. const areEqual = areMetadatasEqual(
  48. [dtoMetadatas, subMetadatas],
  49. // `propertyName` did not change ("name" => "name"), but `target` did: remove it
  50. ['target'],
  51. );
  52. // with `target` removed, this is a perfect copy
  53. expect(areEqual).toBe(true);
  54. });
  55.  
  56. it('uses destination property name as a source property name if none is given', () => {
  57. class SubDto {
  58. @InheritValidation(Dto)
  59. readonly name: string;
  60. }
  61.  
  62. const dtoMetadatas = getMetadatasFrom(Dto, 'name');
  63. const subMetadatas = getMetadatasFrom(SubDto, 'name');
  64. const areEqual = areMetadatasEqual(
  65. [dtoMetadatas, subMetadatas],
  66. ['target'],
  67. );
  68. expect(areEqual).toBe(true);
  69. });
  70.  
  71. it('allows inheriting validation metadatas with a different property name', () => {
  72. class SubDto {
  73. @InheritValidation(Dto, 'name')
  74. readonly nickname: string;
  75. }
  76.  
  77. const dtoMetadatas = getMetadatasFrom(Dto, 'name');
  78. const subMetadatas = getMetadatasFrom(SubDto, 'nickname');
  79. const areEqual = areMetadatasEqual(
  80. [dtoMetadatas, subMetadatas],
  81. // `propertyName` and `target` changed: remove them before checking equality
  82. ['propertyName', 'target'],
  83. );
  84. expect(areEqual).toBe(true);
  85. });
  86.  
  87. it('can be used on multiple properties', () => {
  88. class SubDto {
  89. @InheritValidation(Dto)
  90. readonly id: number;
  91.  
  92. @InheritValidation(Dto)
  93. readonly name: string;
  94. }
  95.  
  96. const dtoMetadatas = _.concat(
  97. // only get metadatas from fields used by SubDto
  98. getMetadatasFrom(Dto, 'id'),
  99. getMetadatasFrom(Dto, 'name'),
  100. );
  101. const subMetadatas = getMetadatasFrom(SubDto);
  102. const areEqual = areMetadatasEqual(
  103. [dtoMetadatas, subMetadatas],
  104. ['target'],
  105. );
  106. expect(areEqual).toBe(true);
  107. });
  108.  
  109. it('uses the inherited metadatas for objects validation', async () => {
  110. class SubDto {
  111. constructor(name: string) {
  112. this.name = name;
  113. }
  114.  
  115. @InheritValidation(Dto)
  116. readonly name: string;
  117. }
  118.  
  119. const validSubDto = new SubDto('Mike');
  120. expect(await validate(validSubDto)).toHaveLength(0);
  121.  
  122. const invalidSubDto = new SubDto('way_too_long_name');
  123. const errors = await validate(invalidSubDto);
  124. expect(errors).toHaveLength(1);
  125. expect(errors[0].constraints).toHaveProperty('maxLength');
  126. });
  127. });
  128.  
  129. /**
  130. * Use `class-validator`'s `MetadataStorage` to get the `ValidationMetadata`s
  131. * of a given class, or (more specific) one of its property.
  132. *
  133. * @param fromClass Class to get `ValidationMetadata`s from.
  134. * @param property Source property (if none is given, get metadatas from all properties).
  135. *
  136. * @return {ValidationMetadata[]} Target metadatas.
  137. */
  138. function getMetadatasFrom(
  139. fromClass: new () => object,
  140. property?: string,
  141. ): ValidationMetadata[] {
  142. const metadataStorage = getFromContainer(MetadataStorage);
  143. const metadatas = _.cloneDeep(metadataStorage.getTargetValidationMetadatas(
  144. fromClass,
  145. undefined,
  146. ));
  147.  
  148. if (!property) {
  149. return metadatas;
  150. }
  151.  
  152. return metadatas.filter((vm) => vm.propertyName === property);
  153. }
  154.  
  155. /**
  156. * Determine whether two collections of `ValidationMetadata`s are
  157. * the same, eventually after having removed a few fields (which are
  158. * known to have been changed by design).
  159. *
  160. * @param metaDataCollections Array of 2 `ValidationMetadata[]` to be compared.
  161. * @param withoutFields Fields to be removed from the metadatas before comparing them.
  162. *
  163. * @return {boolean} `true` if both collections are equal.
  164. */
  165. function areMetadatasEqual(
  166. metaDataCollections: ValidationMetadata[][],
  167. withoutFields: string[],
  168. ): boolean {
  169. if (metaDataCollections.length !== 2) {
  170. throw new TypeError('Misuse of metadatasAreEqual');
  171. }
  172.  
  173. _.each(withoutFields, (field) => {
  174. _.each(metaDataCollections, (metadatas) => {
  175. _.each(metadatas, (md) => _.unset(md, field));
  176. });
  177. });
  178.  
  179. return _.isEqual(metaDataCollections[0], metaDataCollections[1]);
  180. }
Add Comment
Please, Sign In to add comment