money-strategy.e2e-spec.ts 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. import { SortOrder } from '@vendure/common/lib/generated-types';
  2. import { Logger, mergeConfig, MoneyStrategy, VendurePlugin } from '@vendure/core';
  3. import { createErrorResultGuard, createTestEnvironment, ErrorResultGuard } from '@vendure/testing';
  4. import path from 'path';
  5. import { ColumnOptions } from 'typeorm';
  6. import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest';
  7. import { initialData } from '../../../e2e-common/e2e-initial-data';
  8. import { TEST_SETUP_TIMEOUT_MS, testConfig } from '../../../e2e-common/test-config';
  9. import { FragmentOf } from './graphql/graphql-shop';
  10. import { getProductVariantListDocument } from './graphql/shared-definitions';
  11. import { localAddItemToOrderDocument, localUpdatedOrderFragment } from './graphql/shop-definitions';
  12. /* eslint-disable @typescript-eslint/no-non-null-assertion */
  13. type UpdatedOrder = FragmentOf<typeof localUpdatedOrderFragment>;
  14. const orderGuard: ErrorResultGuard<UpdatedOrder> = createErrorResultGuard(input => !!input.total);
  15. class CustomMoneyStrategy implements MoneyStrategy {
  16. static transformerFromSpy = vi.fn();
  17. readonly moneyColumnOptions: ColumnOptions = {
  18. type: 'bigint',
  19. transformer: {
  20. to: (entityValue: number) => {
  21. return entityValue;
  22. },
  23. from: (databaseValue: string): number => {
  24. CustomMoneyStrategy.transformerFromSpy(databaseValue);
  25. if (databaseValue == null) {
  26. return databaseValue;
  27. }
  28. const intVal = Number.parseInt(databaseValue, 10);
  29. if (!Number.isSafeInteger(intVal)) {
  30. Logger.warn(`Monetary value ${databaseValue} is not a safe integer!`);
  31. }
  32. if (Number.isNaN(intVal)) {
  33. Logger.warn(`Monetary value ${databaseValue} is not a number!`);
  34. }
  35. return intVal;
  36. },
  37. },
  38. };
  39. round(value: number, quantity = 1): number {
  40. return Math.round(value * quantity);
  41. }
  42. }
  43. @VendurePlugin({
  44. configuration: config => {
  45. config.entityOptions.moneyStrategy = new CustomMoneyStrategy();
  46. return config;
  47. },
  48. })
  49. class MyPlugin {}
  50. describe('Custom MoneyStrategy', () => {
  51. const { server, adminClient, shopClient } = createTestEnvironment(
  52. mergeConfig(testConfig(), {
  53. plugins: [MyPlugin],
  54. }),
  55. );
  56. let cheapVariantId: string;
  57. let expensiveVariantId: string;
  58. beforeAll(async () => {
  59. await server.init({
  60. initialData,
  61. productsCsvPath: path.join(__dirname, 'fixtures/e2e-products-money-handling.csv'),
  62. customerCount: 1,
  63. });
  64. await adminClient.asSuperAdmin();
  65. }, TEST_SETUP_TIMEOUT_MS);
  66. afterAll(async () => {
  67. await server.destroy();
  68. });
  69. it('check initial prices', async () => {
  70. expect(CustomMoneyStrategy.transformerFromSpy).toHaveBeenCalledTimes(0);
  71. const { productVariants } = await adminClient.query(getProductVariantListDocument, {
  72. options: {
  73. sort: {
  74. price: SortOrder.ASC,
  75. },
  76. },
  77. });
  78. expect(productVariants.items[0].price).toBe(31);
  79. expect(productVariants.items[0].priceWithTax).toBe(37);
  80. expect(productVariants.items[1].price).toBe(9_999_999_00);
  81. expect(productVariants.items[1].priceWithTax).toBe(11_999_998_80);
  82. cheapVariantId = productVariants.items[0].id;
  83. expensiveVariantId = productVariants.items[1].id;
  84. expect(CustomMoneyStrategy.transformerFromSpy).toHaveBeenCalledTimes(2);
  85. });
  86. // https://github.com/vendure-ecommerce/vendure/issues/838
  87. it('can handle totals over 21 million', async () => {
  88. await shopClient.asAnonymousUser();
  89. const { addItemToOrder } = await shopClient.query(localAddItemToOrderDocument, {
  90. productVariantId: expensiveVariantId,
  91. quantity: 2,
  92. });
  93. orderGuard.assertSuccess(addItemToOrder);
  94. expect(addItemToOrder.lines[0].linePrice).toBe(1_999_999_800);
  95. expect(addItemToOrder.lines[0].linePriceWithTax).toBe(2_399_999_760);
  96. });
  97. // https://github.com/vendure-ecommerce/vendure/issues/1835
  98. // 31 * 1.2 = 37.2
  99. // Math.round(37.2 * 10) =372
  100. it('tax calculation rounds at the unit level', async () => {
  101. await shopClient.asAnonymousUser();
  102. const { addItemToOrder } = await shopClient.query(localAddItemToOrderDocument, {
  103. productVariantId: cheapVariantId,
  104. quantity: 10,
  105. });
  106. orderGuard.assertSuccess(addItemToOrder);
  107. expect(addItemToOrder.lines[0].linePrice).toBe(310);
  108. expect(addItemToOrder.lines[0].linePriceWithTax).toBe(372);
  109. });
  110. });