custom-field-default-values.e2e-spec.ts 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278
  1. import { createTestEnvironment } from '@vendure/testing';
  2. import gql from 'graphql-tag';
  3. import path from 'path';
  4. import { afterAll, beforeAll, describe, expect, it } from 'vitest';
  5. import { initialData } from '../../../e2e-common/e2e-initial-data';
  6. import { TEST_SETUP_TIMEOUT_MS, testConfig } from '../../../e2e-common/test-config';
  7. /**
  8. * Tests for GitHub issue #3266: Custom field default values not applied when explicitly set to null
  9. * https://github.com/vendurehq/vendure/issues/3266
  10. */
  11. const customConfig = {
  12. ...testConfig(),
  13. customFields: {
  14. Product: [
  15. { name: 'stringWithDefault', type: 'string', defaultValue: 'hello' },
  16. { name: 'intWithDefault', type: 'int', defaultValue: 5 },
  17. { name: 'booleanWithDefault', type: 'boolean', defaultValue: true },
  18. ],
  19. Customer: [
  20. { name: 'stringWithDefault', type: 'string', defaultValue: 'customer-default' },
  21. { name: 'intWithDefault', type: 'int', defaultValue: 100 },
  22. { name: 'booleanWithDefault', type: 'boolean', defaultValue: false },
  23. ],
  24. },
  25. };
  26. describe('Custom field default values', () => {
  27. const { server, adminClient, shopClient } = createTestEnvironment(customConfig);
  28. const CREATE_CUSTOMER = gql`
  29. mutation CreateCustomer($input: CreateCustomerInput!) {
  30. createCustomer(input: $input) {
  31. ... on Customer {
  32. id
  33. firstName
  34. lastName
  35. emailAddress
  36. customFields {
  37. stringWithDefault
  38. intWithDefault
  39. booleanWithDefault
  40. }
  41. }
  42. ... on ErrorResult {
  43. errorCode
  44. message
  45. }
  46. }
  47. }
  48. `;
  49. beforeAll(async () => {
  50. await server.init({
  51. initialData,
  52. productsCsvPath: path.join(__dirname, 'fixtures/e2e-products-minimal.csv'),
  53. customerCount: 1,
  54. });
  55. await adminClient.asSuperAdmin();
  56. }, TEST_SETUP_TIMEOUT_MS);
  57. afterAll(async () => {
  58. await server.destroy();
  59. });
  60. describe('translatable entity (Product)', () => {
  61. it('should apply default values when creating product without custom fields', async () => {
  62. const { createProduct } = await adminClient.query(gql`
  63. mutation {
  64. createProduct(
  65. input: {
  66. translations: [
  67. {
  68. languageCode: en
  69. name: "Test Product 1"
  70. slug: "test-product-1"
  71. description: "Test"
  72. }
  73. ]
  74. }
  75. ) {
  76. id
  77. name
  78. customFields {
  79. stringWithDefault
  80. intWithDefault
  81. booleanWithDefault
  82. }
  83. }
  84. }
  85. `);
  86. expect(createProduct.customFields.stringWithDefault).toBe('hello');
  87. expect(createProduct.customFields.intWithDefault).toBe(5);
  88. expect(createProduct.customFields.booleanWithDefault).toBe(true);
  89. });
  90. it('should apply default values when creating product with empty custom fields', async () => {
  91. const { createProduct } = await adminClient.query(gql`
  92. mutation {
  93. createProduct(
  94. input: {
  95. translations: [
  96. {
  97. languageCode: en
  98. name: "Test Product 2"
  99. slug: "test-product-2"
  100. description: "Test"
  101. }
  102. ]
  103. customFields: {}
  104. }
  105. ) {
  106. id
  107. name
  108. customFields {
  109. stringWithDefault
  110. intWithDefault
  111. booleanWithDefault
  112. }
  113. }
  114. }
  115. `);
  116. expect(createProduct.customFields.stringWithDefault).toBe('hello');
  117. expect(createProduct.customFields.intWithDefault).toBe(5);
  118. expect(createProduct.customFields.booleanWithDefault).toBe(true);
  119. });
  120. it('should apply default values when custom fields are explicitly set to null', async () => {
  121. const { createProduct } = await adminClient.query(gql`
  122. mutation {
  123. createProduct(
  124. input: {
  125. translations: [
  126. {
  127. languageCode: en
  128. name: "Test Product Null"
  129. slug: "test-product-null"
  130. description: "Test"
  131. }
  132. ]
  133. customFields: {
  134. stringWithDefault: null
  135. intWithDefault: null
  136. booleanWithDefault: null
  137. }
  138. }
  139. ) {
  140. id
  141. name
  142. customFields {
  143. stringWithDefault
  144. intWithDefault
  145. booleanWithDefault
  146. }
  147. }
  148. }
  149. `);
  150. // This is the core issue: when custom fields are explicitly set to null,
  151. // they should still get their default values
  152. expect(createProduct.customFields.stringWithDefault).toBe('hello');
  153. expect(createProduct.customFields.intWithDefault).toBe(5);
  154. expect(createProduct.customFields.booleanWithDefault).toBe(true);
  155. });
  156. it('should not override explicitly provided values', async () => {
  157. const { createProduct } = await adminClient.query(gql`
  158. mutation {
  159. createProduct(
  160. input: {
  161. translations: [
  162. {
  163. languageCode: en
  164. name: "Test Product Custom"
  165. slug: "test-product-custom"
  166. description: "Test"
  167. }
  168. ]
  169. customFields: {
  170. stringWithDefault: "custom value"
  171. intWithDefault: 999
  172. booleanWithDefault: false
  173. }
  174. }
  175. ) {
  176. id
  177. name
  178. customFields {
  179. stringWithDefault
  180. intWithDefault
  181. booleanWithDefault
  182. }
  183. }
  184. }
  185. `);
  186. // When explicit values are provided, they should be used instead of defaults
  187. expect(createProduct.customFields.stringWithDefault).toBe('custom value');
  188. expect(createProduct.customFields.intWithDefault).toBe(999);
  189. expect(createProduct.customFields.booleanWithDefault).toBe(false);
  190. });
  191. });
  192. describe('non-translatable entity (Customer)', () => {
  193. it('should apply default values when creating customer without custom fields', async () => {
  194. const { createCustomer } = await adminClient.query(CREATE_CUSTOMER, {
  195. input: {
  196. firstName: 'John',
  197. lastName: 'Doe',
  198. emailAddress: 'john.doe@example.com',
  199. },
  200. });
  201. expect(createCustomer.customFields.stringWithDefault).toBe('customer-default');
  202. expect(createCustomer.customFields.intWithDefault).toBe(100);
  203. expect(createCustomer.customFields.booleanWithDefault).toBe(false);
  204. });
  205. it('should apply default values when creating customer with empty custom fields', async () => {
  206. const { createCustomer } = await adminClient.query(CREATE_CUSTOMER, {
  207. input: {
  208. firstName: 'Jane',
  209. lastName: 'Smith',
  210. emailAddress: 'jane.smith@example.com',
  211. customFields: {},
  212. },
  213. });
  214. expect(createCustomer.customFields.stringWithDefault).toBe('customer-default');
  215. expect(createCustomer.customFields.intWithDefault).toBe(100);
  216. expect(createCustomer.customFields.booleanWithDefault).toBe(false);
  217. });
  218. it('should apply default values when custom fields are explicitly set to null', async () => {
  219. const { createCustomer } = await adminClient.query(CREATE_CUSTOMER, {
  220. input: {
  221. firstName: 'Bob',
  222. lastName: 'Johnson',
  223. emailAddress: 'bob.johnson@example.com',
  224. customFields: {
  225. stringWithDefault: null,
  226. intWithDefault: null,
  227. booleanWithDefault: null,
  228. },
  229. },
  230. });
  231. // This should reproduce the issue for non-translatable entities
  232. expect(createCustomer.customFields.stringWithDefault).toBe('customer-default');
  233. expect(createCustomer.customFields.intWithDefault).toBe(100);
  234. expect(createCustomer.customFields.booleanWithDefault).toBe(false);
  235. });
  236. it('should not override explicitly provided values', async () => {
  237. const { createCustomer } = await adminClient.query(CREATE_CUSTOMER, {
  238. input: {
  239. firstName: 'Alice',
  240. lastName: 'Wilson',
  241. emailAddress: 'alice.wilson@example.com',
  242. customFields: {
  243. stringWithDefault: 'custom customer value',
  244. intWithDefault: 777,
  245. booleanWithDefault: true,
  246. },
  247. },
  248. });
  249. // When explicit values are provided, they should be used instead of defaults
  250. expect(createCustomer.customFields.stringWithDefault).toBe('custom customer value');
  251. expect(createCustomer.customFields.intWithDefault).toBe(777);
  252. expect(createCustomer.customFields.booleanWithDefault).toBe(true);
  253. });
  254. });
  255. });