@shopware-ag/acceptance-test-suite 2.0.1 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1,8 +1,8 @@
1
1
  import { test as test$c, expect, request, mergeTests } from '@playwright/test';
2
2
  export * from '@playwright/test';
3
- import crypto, { createHash } from 'crypto';
4
- import { stringify } from 'uuid';
5
3
  import { Image } from 'image-js';
4
+ import crypto from 'crypto';
5
+ import { stringify } from 'uuid';
6
6
  import fs from 'fs';
7
7
  import { AxeBuilder } from '@axe-core/playwright';
8
8
  import { createHtmlReport } from 'axe-html-reporter';
@@ -200,6 +200,11 @@ const setOrderStatus = async (orderId, orderStatus, adminApiContext) => {
200
200
  return await adminApiContext.post(`./_action/order/${orderId}/state/${orderStatus}`);
201
201
  };
202
202
 
203
+ const isSaaSInstance = async (adminApiContext) => {
204
+ const instanceFeatures = await adminApiContext.get("./instance/features");
205
+ return instanceFeatures.ok();
206
+ };
207
+
203
208
  const test$a = test$c.extend({
204
209
  SalesChannelBaseConfig: [
205
210
  async ({ AdminApiContext }, use) => {
@@ -242,8 +247,26 @@ const test$a = test$c.extend({
242
247
  const { uuid: domainUuid } = IdProvider.getWorkerDerivedStableId("domain");
243
248
  const { uuid: customerUuid } = IdProvider.getWorkerDerivedStableId("customer");
244
249
  const baseUrl = `${SalesChannelBaseConfig.appUrl}test-${uuid}/`;
245
- const currentConfigResponse = await AdminApiContext.get(`./_action/system-config?domain=storefront&salesChannelId=${uuid}`);
246
- const currentConfig = await currentConfigResponse.json();
250
+ const response = await AdminApiContext.post(`./search/system-config`, {
251
+ data: {
252
+ page: 1,
253
+ limit: 1,
254
+ filter: [
255
+ {
256
+ type: "equals",
257
+ field: "salesChannelId",
258
+ value: uuid
259
+ },
260
+ {
261
+ type: "equals",
262
+ field: "configurationKey",
263
+ value: "storefront.themeSeed"
264
+ }
265
+ ]
266
+ }
267
+ });
268
+ const currentConfig = await response.json();
269
+ const themeSeed = currentConfig.total > 0 ? currentConfig.data[0].configurationValue : null;
247
270
  await AdminApiContext.delete(`./customer/${customerUuid}`);
248
271
  const ordersResp = await AdminApiContext.post(`./search/order`, {
249
272
  data: {
@@ -333,37 +356,21 @@ const test$a = test$c.extend({
333
356
  currencies: [{ id: SalesChannelBaseConfig.eurCurrencyId }]
334
357
  }
335
358
  ]
336
- },
337
- "theme-assignment": {
338
- entity: "theme_sales_channel",
339
- action: "upsert",
340
- payload: [{
341
- salesChannelId: uuid,
342
- themeId: SalesChannelBaseConfig.defaultThemeId
343
- }]
344
359
  }
345
360
  }
346
361
  });
347
362
  expect(syncResp.ok()).toBeTruthy();
348
- const salesChannelPromise = AdminApiContext.get(`./sales-channel/${uuid}`);
349
- let themeAssignPromise;
350
- if (currentConfig && currentConfig["storefront.themeSeed"]) {
351
- const md5 = (data) => createHash("md5").update(data).digest("hex");
352
- const md5Str = md5(`${SalesChannelBaseConfig.defaultThemeId}${uuid}${currentConfig["storefront.themeSeed"]}`);
353
- const themeCssResp = await AdminApiContext.head(`${SalesChannelBaseConfig.appUrl}theme/${md5Str}/css/all.css`);
354
- if (themeCssResp.status() === 200) {
355
- themeAssignPromise = AdminApiContext.post(`./_action/system-config?salesChannelId=${uuid}`, {
356
- data: {
357
- "storefront.themeSeed": currentConfig["storefront.themeSeed"]
358
- }
359
- });
360
- }
361
- }
362
- if (!themeAssignPromise) {
363
- themeAssignPromise = AdminApiContext.post(
364
- `./_action/theme/${SalesChannelBaseConfig.defaultThemeId}/assign/${uuid}`
365
- );
363
+ if (themeSeed) {
364
+ await AdminApiContext.post("./system-config", {
365
+ data: {
366
+ id: uuid,
367
+ salesChannelId: uuid,
368
+ configurationKey: "storefront.themeSeed",
369
+ configurationValue: themeSeed
370
+ }
371
+ });
366
372
  }
373
+ const salesChannelPromise = AdminApiContext.get(`./sales-channel/${uuid}`);
367
374
  const salutationResponse = await AdminApiContext.get(`./salutation`);
368
375
  const salutations = await salutationResponse.json();
369
376
  const customerData = {
@@ -400,36 +407,67 @@ const test$a = test$c.extend({
400
407
  const customerRespPromise = AdminApiContext.post("./customer?_response", {
401
408
  data: customerData
402
409
  });
403
- const [customerResp, themeAssignResp, salesChannelResp] = await Promise.all([
410
+ const [customerResp, salesChannelResp] = await Promise.all([
404
411
  customerRespPromise,
405
- themeAssignPromise,
406
412
  salesChannelPromise
407
413
  ]);
408
414
  expect(customerResp.ok()).toBeTruthy();
409
- expect(themeAssignResp.ok()).toBeTruthy();
410
415
  expect(salesChannelResp.ok()).toBeTruthy();
411
416
  const customer = await customerResp.json();
412
417
  const salesChannel = await salesChannelResp.json();
413
418
  await use({
414
419
  salesChannel: salesChannel.data,
415
420
  customer: { ...customer.data, password: customerData.password },
416
- url: baseUrl
421
+ url: baseUrl,
422
+ themeSeed
423
+ });
424
+ },
425
+ { scope: "worker" }
426
+ ],
427
+ DefaultStorefront: [
428
+ async ({ browser, AdminApiContext, DefaultSalesChannel, SalesChannelBaseConfig }, use) => {
429
+ const { id: uuid } = DefaultSalesChannel.salesChannel;
430
+ const isSaasInstance = await isSaaSInstance(AdminApiContext);
431
+ await AdminApiContext.post(
432
+ `./_action/theme/${SalesChannelBaseConfig.defaultThemeId}/assign/${uuid}`
433
+ );
434
+ test$c.slow();
435
+ if (isSaasInstance) {
436
+ const tmpContext = await browser.newContext();
437
+ const tmpPage = await tmpContext.newPage();
438
+ for (let i = 0; i < 100; ++i) {
439
+ let latestTimestamp = (/* @__PURE__ */ new Date()).toISOString();
440
+ const response = await AdminApiContext.get(`./notification/message?limit=10&latestTimestamp=${latestTimestamp}`);
441
+ const json = await response.json();
442
+ if (json.timestamp) {
443
+ latestTimestamp = json.timestamp;
444
+ }
445
+ if (json.notifications.find((n) => n.message.includes(`Compilation for sales channel ${DefaultSalesChannel.salesChannel.name} completed`))) {
446
+ tmpPage.close();
447
+ tmpContext.close();
448
+ break;
449
+ }
450
+ await tmpPage.waitForTimeout(1e3);
451
+ }
452
+ }
453
+ await use({
454
+ ...DefaultSalesChannel
417
455
  });
418
456
  },
419
457
  { scope: "worker" }
420
458
  ]
421
459
  });
422
460
 
423
- var __defProp$r = Object.defineProperty;
424
- var __defNormalProp$r = (obj, key, value) => key in obj ? __defProp$r(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
425
- var __publicField$r = (obj, key, value) => {
426
- __defNormalProp$r(obj, typeof key !== "symbol" ? key + "" : key, value);
461
+ var __defProp$s = Object.defineProperty;
462
+ var __defNormalProp$s = (obj, key, value) => key in obj ? __defProp$s(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
463
+ var __publicField$s = (obj, key, value) => {
464
+ __defNormalProp$s(obj, typeof key !== "symbol" ? key + "" : key, value);
427
465
  return value;
428
466
  };
429
467
  const _AdminApiContext = class _AdminApiContext {
430
468
  constructor(context, options) {
431
- __publicField$r(this, "context");
432
- __publicField$r(this, "options");
469
+ __publicField$s(this, "context");
470
+ __publicField$s(this, "options");
433
471
  this.context = context;
434
472
  this.options = options;
435
473
  }
@@ -521,7 +559,7 @@ const _AdminApiContext = class _AdminApiContext {
521
559
  return this.context.head(url, options);
522
560
  }
523
561
  };
524
- __publicField$r(_AdminApiContext, "defaultOptions", {
562
+ __publicField$s(_AdminApiContext, "defaultOptions", {
525
563
  app_url: process.env["APP_URL"],
526
564
  client_id: process.env["SHOPWARE_ACCESS_KEY_ID"],
527
565
  client_secret: process.env["SHOPWARE_SECRET_ACCESS_KEY"],
@@ -531,16 +569,16 @@ __publicField$r(_AdminApiContext, "defaultOptions", {
531
569
  });
532
570
  let AdminApiContext = _AdminApiContext;
533
571
 
534
- var __defProp$q = Object.defineProperty;
535
- var __defNormalProp$q = (obj, key, value) => key in obj ? __defProp$q(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
536
- var __publicField$q = (obj, key, value) => {
537
- __defNormalProp$q(obj, typeof key !== "symbol" ? key + "" : key, value);
572
+ var __defProp$r = Object.defineProperty;
573
+ var __defNormalProp$r = (obj, key, value) => key in obj ? __defProp$r(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
574
+ var __publicField$r = (obj, key, value) => {
575
+ __defNormalProp$r(obj, typeof key !== "symbol" ? key + "" : key, value);
538
576
  return value;
539
577
  };
540
578
  const _StoreApiContext = class _StoreApiContext {
541
579
  constructor(context, options) {
542
- __publicField$q(this, "context");
543
- __publicField$q(this, "options");
580
+ __publicField$r(this, "context");
581
+ __publicField$r(this, "options");
544
582
  this.context = context;
545
583
  this.options = options;
546
584
  }
@@ -599,12 +637,977 @@ const _StoreApiContext = class _StoreApiContext {
599
637
  return this.context.head(url, options);
600
638
  }
601
639
  };
602
- __publicField$q(_StoreApiContext, "defaultOptions", {
640
+ __publicField$r(_StoreApiContext, "defaultOptions", {
603
641
  app_url: process.env["APP_URL"],
604
642
  ignoreHTTPSErrors: true
605
643
  });
606
644
  let StoreApiContext = _StoreApiContext;
607
645
 
646
+ function createRandomImage(width = 800, height = 600) {
647
+ const buffer = Buffer.alloc(width * height * 4);
648
+ let i = 0;
649
+ while (i < buffer.length) {
650
+ buffer[i++] = Math.floor(Math.random() * 256);
651
+ }
652
+ return new Image(width, height, buffer);
653
+ }
654
+
655
+ var __defProp$q = Object.defineProperty;
656
+ var __defNormalProp$q = (obj, key, value) => key in obj ? __defProp$q(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
657
+ var __publicField$q = (obj, key, value) => {
658
+ __defNormalProp$q(obj, typeof key !== "symbol" ? key + "" : key, value);
659
+ return value;
660
+ };
661
+ class TestDataService {
662
+ constructor(AdminApiClient, IdProvider, options) {
663
+ __publicField$q(this, "AdminApiClient");
664
+ __publicField$q(this, "IdProvider");
665
+ __publicField$q(this, "namePrefix", "Test-");
666
+ __publicField$q(this, "nameSuffix", "");
667
+ __publicField$q(this, "defaultSalesChannel");
668
+ __publicField$q(this, "defaultTaxId");
669
+ __publicField$q(this, "defaultCurrencyId");
670
+ __publicField$q(this, "defaultCategoryId");
671
+ __publicField$q(this, "defaultLanguageId");
672
+ __publicField$q(this, "defaultCountryId");
673
+ __publicField$q(this, "defaultCustomerGroupId");
674
+ __publicField$q(this, "shouldCleanUp", true);
675
+ __publicField$q(this, "createdRecords", []);
676
+ this.AdminApiClient = AdminApiClient;
677
+ this.IdProvider = IdProvider;
678
+ this.defaultSalesChannel = options.defaultSalesChannel;
679
+ this.defaultTaxId = options.defaultTaxId;
680
+ this.defaultCurrencyId = options.defaultCurrencyId;
681
+ this.defaultCategoryId = options.defaultCategoryId;
682
+ this.defaultLanguageId = options.defaultLanguageId;
683
+ this.defaultCountryId = options.defaultCountryId;
684
+ this.defaultCustomerGroupId = options.defaultCustomerGroupId;
685
+ if (options.namePrefix) {
686
+ this.namePrefix = options.namePrefix;
687
+ }
688
+ if (options.nameSuffix) {
689
+ this.nameSuffix = options.nameSuffix;
690
+ }
691
+ }
692
+ /**
693
+ * Creates a basic product without images or other special configuration.
694
+ * The product will be added to the default sales channel category if configured.
695
+ *
696
+ * @param overrides - Specific data overrides that will be applied to the product data struct.
697
+ * @param taxId - The uuid of the tax rule to use for the product pricing.
698
+ * @param currencyId - The uuid of the currency to use for the product pricing.
699
+ */
700
+ async createBasicProduct(overrides = {}, taxId = this.defaultTaxId, currencyId = this.defaultCurrencyId) {
701
+ if (!taxId) {
702
+ return Promise.reject("Missing tax ID for creating product.");
703
+ }
704
+ if (!currencyId) {
705
+ return Promise.reject("Missing currency ID for creating product.");
706
+ }
707
+ const basicProduct = this.getBasicProductStruct(taxId, currencyId, overrides);
708
+ const productResponse = await this.AdminApiClient.post("./product?_response=detail", {
709
+ data: basicProduct
710
+ });
711
+ const { data: product } = await productResponse.json();
712
+ this.addCreatedRecord("product", product.id);
713
+ return product;
714
+ }
715
+ /**
716
+ * Creates a basic product with one randomly generated image.
717
+ * The product will be added to the default sales channel category if configured.
718
+ *
719
+ * @param overrides - Specific data overrides that will be applied to the product data struct.
720
+ * @param taxId - The uuid of the tax rule to use for the product pricing.
721
+ * @param currencyId - The uuid of the currency to use for the product pricing.
722
+ */
723
+ async createProductWithImage(overrides = {}, taxId = this.defaultTaxId, currencyId = this.defaultCurrencyId) {
724
+ const product = await this.createBasicProduct(overrides, taxId, currencyId);
725
+ const media = await this.createMediaPNG();
726
+ await this.assignProductMedia(product.id, media.id);
727
+ return product;
728
+ }
729
+ /**
730
+ * Creates a digital product with a text file as its download.
731
+ * The product will be added to the default sales channel category if configured.
732
+ *
733
+ * @param content - The content of the text file for the product download.
734
+ * @param overrides - Specific data overrides that will be applied to the product data struct.
735
+ * @param taxId - The uuid of the tax rule to use for the product pricing.
736
+ * @param currencyId - The uuid of the currency to use for the product pricing.
737
+ */
738
+ async createDigitalProduct(content = "Lorem ipsum dolor", overrides = {}, taxId = this.defaultTaxId, currencyId = this.defaultCurrencyId) {
739
+ const product = await this.createBasicProduct(overrides, taxId, currencyId);
740
+ const media = await this.createMediaTXT(content);
741
+ const productDownload = await this.assignProductDownload(product.id, media.id);
742
+ this.addCreatedRecord("product-download", productDownload.id);
743
+ return product;
744
+ }
745
+ /**
746
+ * Creates a basic product with a price range matrix.
747
+ * The product will be added to the default sales channel category if configured.
748
+ *
749
+ * @param overrides - Specific data overrides that will be applied to the product data struct.
750
+ * @param taxId - The uuid of the tax rule to use for the product pricing.
751
+ * @param currencyId - The uuid of the currency to use for the product pricing.
752
+ */
753
+ async createProductWithPriceRange(overrides = {}, taxId = this.defaultTaxId, currencyId = this.defaultCurrencyId) {
754
+ if (!currencyId) {
755
+ return Promise.reject("Missing currency ID for creating product.");
756
+ }
757
+ const rule = await this.getRule("Always valid (Default)");
758
+ const priceRange = this.getProductPriceRangeStruct(currencyId, rule.id);
759
+ const productOverrides = Object.assign({}, priceRange, overrides);
760
+ return this.createBasicProduct(productOverrides, taxId, currencyId);
761
+ }
762
+ /**
763
+ * Creates a basic product category to assign products to.
764
+ *
765
+ * @param parentId - The uuid of the parent category.
766
+ * @param overrides - Specific data overrides that will be applied to the category data struct.
767
+ */
768
+ async createCategory(overrides = {}, parentId = this.defaultCategoryId) {
769
+ const basicCategory = this.getBasicCategoryStruct(overrides, parentId);
770
+ const response = await this.AdminApiClient.post("category?_response=detail", {
771
+ data: basicCategory
772
+ });
773
+ const { data: category } = await response.json();
774
+ this.addCreatedRecord("category", category.id);
775
+ return category;
776
+ }
777
+ /**
778
+ * Creates a new media resource containing a random generated PNG image.
779
+ *
780
+ * @param width - The width of the image in pixel. Default is 800.
781
+ * @param height - The height of the image in pixel. Default is 600.
782
+ */
783
+ async createMediaPNG(width = 800, height = 600) {
784
+ const image = createRandomImage(width, height);
785
+ const media = await this.createMediaResource();
786
+ const filename = `${this.namePrefix}Media-${media.id}${this.nameSuffix}`;
787
+ await this.AdminApiClient.post(`_action/media/${media.id}/upload?extension=png&fileName=${filename}`, {
788
+ data: Buffer.from(image.toBuffer()),
789
+ headers: { "content-type": "image/png" }
790
+ });
791
+ this.addCreatedRecord("media", media.id);
792
+ return media;
793
+ }
794
+ /**
795
+ * Creates a new media resource containing a text file.
796
+ *
797
+ * @param content - The content of the text file.
798
+ */
799
+ async createMediaTXT(content = "Lorem ipsum dolor") {
800
+ const media = await this.createMediaResource();
801
+ const filename = `${this.namePrefix}Media-${media.id}${this.nameSuffix}`;
802
+ await this.AdminApiClient.post(`_action/media/${media.id}/upload?extension=txt&fileName=${filename}`, {
803
+ data: content,
804
+ headers: { "content-type": "application/octet-stream" }
805
+ });
806
+ this.addCreatedRecord("media", media.id);
807
+ return media;
808
+ }
809
+ /**
810
+ * Creates a new empty media resource.
811
+ * This method is mostly used to combine it with a certain file upload.
812
+ */
813
+ async createMediaResource() {
814
+ const id = this.IdProvider.getIdPair().id;
815
+ const mediaResponse = await this.AdminApiClient.post("media?_response=detail", {
816
+ data: {
817
+ private: false,
818
+ alt: `Alt-${id}`,
819
+ title: `Title-${id}`
820
+ }
821
+ });
822
+ const { data: media } = await mediaResponse.json();
823
+ return media;
824
+ }
825
+ /**
826
+ * Creates a new property group with color type options.
827
+ *
828
+ * @param overrides - Specific data overrides that will be applied to the property group data struct.
829
+ */
830
+ async createColorPropertyGroup(overrides = {}) {
831
+ const id = this.IdProvider.getIdPair().id;
832
+ const colorPropertyGroup = {
833
+ name: `${this.namePrefix}Color-${id}${this.nameSuffix}`,
834
+ description: "Color",
835
+ displayType: "color",
836
+ sortingType: "name",
837
+ options: [{
838
+ name: "Blue",
839
+ colorHexCode: "#2148d6"
840
+ }, {
841
+ name: "Red",
842
+ colorHexCode: "#bf0f2a"
843
+ }, {
844
+ name: "Green",
845
+ colorHexCode: "#12bf0f"
846
+ }]
847
+ };
848
+ const propertyGroupResponse = await this.AdminApiClient.post("property-group?_response=detail", {
849
+ data: Object.assign({}, colorPropertyGroup, overrides)
850
+ });
851
+ const { data: propertyGroup } = await propertyGroupResponse.json();
852
+ this.addCreatedRecord("property-group", propertyGroup.id);
853
+ return propertyGroup;
854
+ }
855
+ /**
856
+ * Creates a new property group with text type options.
857
+ *
858
+ * @param overrides - Specific data overrides that will be applied to the property group data struct.
859
+ */
860
+ async createTextPropertyGroup(overrides = {}) {
861
+ const id = this.IdProvider.getIdPair().id;
862
+ const textPropertyGroup = {
863
+ name: `${this.namePrefix}Size-${id}${this.nameSuffix}`,
864
+ description: "Size",
865
+ displayType: "text",
866
+ sortingType: "name",
867
+ options: [{
868
+ name: "Small"
869
+ }, {
870
+ name: "Medium"
871
+ }, {
872
+ name: "Large"
873
+ }]
874
+ };
875
+ const propertyGroupResponse = await this.AdminApiClient.post("property-group?_response=detail", {
876
+ data: Object.assign({}, textPropertyGroup, overrides)
877
+ });
878
+ const { data: propertyGroup } = await propertyGroupResponse.json();
879
+ this.addCreatedRecord("property-group", propertyGroup.id);
880
+ return propertyGroup;
881
+ }
882
+ /**
883
+ * Creates a new tag which can be assigned to other entities.
884
+ *
885
+ * @param tagName - The name of the tag.
886
+ */
887
+ async createTag(tagName) {
888
+ const response = await this.AdminApiClient.post("tag?_response=detail", {
889
+ data: {
890
+ id: this.IdProvider.getIdPair().uuid,
891
+ name: tagName
892
+ }
893
+ });
894
+ const { data: tag } = await response.json();
895
+ this.addCreatedRecord("tag", tag.id);
896
+ return tag;
897
+ }
898
+ /**
899
+ * Creates a new shop customer.
900
+ *
901
+ * @param overrides - Specific data overrides that will be applied to the customer data struct.
902
+ * @param salutationKey - The key of the salutation that should be used for the customer. Default is "mr".
903
+ * @param salesChannel - The sales channel for which the customer should be registered.
904
+ */
905
+ async createCustomer(overrides = {}, salutationKey = "mr", salesChannel = this.defaultSalesChannel) {
906
+ const salutation = await this.getSalutation(salutationKey);
907
+ const basicCustomerStruct = this.getBasicCustomerStruct(
908
+ salesChannel.id,
909
+ salesChannel.customerGroupId,
910
+ salesChannel.languageId,
911
+ salesChannel.countryId,
912
+ salesChannel.paymentMethodId,
913
+ salutation.id,
914
+ overrides
915
+ );
916
+ const response = await this.AdminApiClient.post("customer?_response=detail", {
917
+ data: basicCustomerStruct
918
+ });
919
+ const { data: customer } = await response.json();
920
+ this.addCreatedRecord("customer", customer.id);
921
+ return customer;
922
+ }
923
+ /**
924
+ * Creates a new order. This order is created on pure data and prices are not guaranteed to be calculated correctly.
925
+ *
926
+ * @param lineItems - Products that should be added to the order.
927
+ * @param customer - The customer to which the order should be assigned.
928
+ * @param salesChannel - The sales channel in which the order should be created.
929
+ * @param overrides - Specific data overrides that will be applied to the order data struct.
930
+ */
931
+ async createOrder(lineItems, customer, overrides = {}, salesChannel = this.defaultSalesChannel) {
932
+ const orderStateMachine = await this.getOrderStateMachine();
933
+ const deliveryStateMachine = await this.getDeliveryStateMachine();
934
+ const transactionStateMachine = await this.getTransactionStateMachine();
935
+ const orderState = await this.getStateMachineState(orderStateMachine.id);
936
+ const deliveryState = await this.getStateMachineState(deliveryStateMachine.id);
937
+ const transactionState = await this.getStateMachineState(transactionStateMachine.id);
938
+ const currencyResponse = await this.AdminApiClient.get(`currency/${salesChannel.currencyId}`);
939
+ const { data: currency } = await currencyResponse.json();
940
+ const shippingMethod = await this.getShippingMethod();
941
+ const paymentMethod = await this.getPaymentMethod();
942
+ const customerAddress = await this.getCustomerAddress(customer.defaultBillingAddressId);
943
+ const basicOrder = this.getBasicOrderStruct(
944
+ lineItems,
945
+ salesChannel.languageId,
946
+ currency,
947
+ paymentMethod,
948
+ shippingMethod,
949
+ orderState,
950
+ deliveryState,
951
+ transactionState,
952
+ customer,
953
+ customerAddress,
954
+ salesChannel.id,
955
+ overrides
956
+ );
957
+ const orderResponse = await this.AdminApiClient.post("order?_response=detail", {
958
+ data: basicOrder
959
+ });
960
+ const { data: order } = await orderResponse.json();
961
+ this.addCreatedRecord("order", order.id);
962
+ return order;
963
+ }
964
+ /**
965
+ * Creates a new promotion with a promotion code.
966
+ *
967
+ * @param overrides - Specific data overrides that will be applied to the promotion data struct.
968
+ * @param salesChannelId - The uuid of the sales channel in which the promotion should be active.
969
+ */
970
+ async createPromotionWithCode(overrides = {}, salesChannelId = this.defaultSalesChannel.id) {
971
+ const basicPromotion = this.getBasicPromotionStruct(salesChannelId, overrides);
972
+ const promotionResponse = await this.AdminApiClient.post("promotion?_response=detail", {
973
+ data: basicPromotion
974
+ });
975
+ const { data: promotion } = await promotionResponse.json();
976
+ this.addCreatedRecord("promotion", promotion.id);
977
+ return promotion;
978
+ }
979
+ /**
980
+ * Assigns a media resource as the download of a digital product.
981
+ *
982
+ * @param productId - The uuid of the product.
983
+ * @param mediaId - The uuid of the media resource.
984
+ */
985
+ async assignProductDownload(productId, mediaId) {
986
+ const downloadResponse = await this.AdminApiClient.post(`product-download?_response=basic`, {
987
+ data: {
988
+ productId,
989
+ mediaId
990
+ }
991
+ });
992
+ const { data: productDownload } = await downloadResponse.json();
993
+ return productDownload;
994
+ }
995
+ /**
996
+ * Assigns a media resource to a product as the product image.
997
+ *
998
+ * @param productId - The uuid of the product.
999
+ * @param mediaId - The uuid of the media resource.
1000
+ */
1001
+ async assignProductMedia(productId, mediaId) {
1002
+ const productMediaId = this.IdProvider.getIdPair().uuid;
1003
+ const mediaResponse = await this.AdminApiClient.patch(`product/${productId}?_response=basic`, {
1004
+ data: {
1005
+ coverId: productMediaId,
1006
+ media: [
1007
+ {
1008
+ id: productMediaId,
1009
+ media: {
1010
+ id: mediaId
1011
+ }
1012
+ }
1013
+ ]
1014
+ }
1015
+ });
1016
+ const { data: productMedia } = await mediaResponse.json();
1017
+ return productMedia;
1018
+ }
1019
+ /**
1020
+ * Assigns a product to a category.
1021
+ *
1022
+ * @param productId - The uuid of the product.
1023
+ * @param categoryId - The uuid of the category.
1024
+ */
1025
+ async assignProductCategory(productId, categoryId) {
1026
+ return await this.AdminApiClient.patch(`product/${productId}?_response=basic`, {
1027
+ data: {
1028
+ categories: [{
1029
+ id: categoryId
1030
+ }]
1031
+ }
1032
+ });
1033
+ }
1034
+ /**
1035
+ * Assigns a tag to a product.
1036
+ *
1037
+ * @param productId - The uuid of the product.
1038
+ * @param tagId - The uuid of the tag.
1039
+ */
1040
+ async assignProductTag(productId, tagId) {
1041
+ return await this.AdminApiClient.patch(`product/${productId}?_response=basic`, {
1042
+ data: {
1043
+ tags: [{
1044
+ id: tagId
1045
+ }]
1046
+ }
1047
+ });
1048
+ }
1049
+ /**
1050
+ * Retrieves a currency based on its ISO code.
1051
+ *
1052
+ * @param isoCode - The ISO code of the currency, for example "EUR".
1053
+ */
1054
+ async getCurrency(isoCode) {
1055
+ const currencyResponse = await this.AdminApiClient.post("search/currency", {
1056
+ data: {
1057
+ limit: 1,
1058
+ filter: [{
1059
+ type: "equals",
1060
+ field: "isoCode",
1061
+ value: isoCode
1062
+ }]
1063
+ }
1064
+ });
1065
+ const { data: result } = await currencyResponse.json();
1066
+ return result[0];
1067
+ }
1068
+ /**
1069
+ * Retrieves a rule based on its name.
1070
+ *
1071
+ * @param name - The name of the rule.
1072
+ */
1073
+ async getRule(name) {
1074
+ const response = await this.AdminApiClient.post("search/rule", {
1075
+ data: {
1076
+ limit: 1,
1077
+ filter: [{
1078
+ type: "equals",
1079
+ field: "name",
1080
+ value: name
1081
+ }]
1082
+ }
1083
+ });
1084
+ const { data: result } = await response.json();
1085
+ return result[0];
1086
+ }
1087
+ /**
1088
+ * Retrieves a shipping method by its name.
1089
+ *
1090
+ * @param name - The name of the shipping method. Default is "Standard".
1091
+ */
1092
+ async getShippingMethod(name = "Standard") {
1093
+ const response = await this.AdminApiClient.post("search/shipping-method", {
1094
+ data: {
1095
+ limit: 1,
1096
+ filter: [{
1097
+ type: "equals",
1098
+ field: "name",
1099
+ value: name
1100
+ }]
1101
+ }
1102
+ });
1103
+ const { data: result } = await response.json();
1104
+ return result[0];
1105
+ }
1106
+ /**
1107
+ * Retrieves a payment method by its name.
1108
+ *
1109
+ * @param name - The name of the payment method. Default is "Invoice".
1110
+ */
1111
+ async getPaymentMethod(name = "Invoice") {
1112
+ const response = await this.AdminApiClient.post("search/payment-method", {
1113
+ data: {
1114
+ limit: 1,
1115
+ filter: [{
1116
+ type: "equals",
1117
+ field: "name",
1118
+ value: name
1119
+ }]
1120
+ }
1121
+ });
1122
+ const { data: result } = await response.json();
1123
+ return result[0];
1124
+ }
1125
+ /**
1126
+ * Retrieves the address of a customer by its uuid.
1127
+ *
1128
+ * @param addressId - The uuid of the customer address.
1129
+ */
1130
+ async getCustomerAddress(addressId) {
1131
+ const response = await this.AdminApiClient.get(`customer-address/${addressId}`);
1132
+ const { data: address } = await response.json();
1133
+ return address;
1134
+ }
1135
+ /**
1136
+ * Retrieves a customer salutations by its key.
1137
+ *
1138
+ * @param key - The key of the salutation. Default is "mr".
1139
+ */
1140
+ async getSalutation(key = "mr") {
1141
+ const response = await this.AdminApiClient.post("search/salutation", {
1142
+ data: {
1143
+ limit: 1,
1144
+ filter: [{
1145
+ type: "equals",
1146
+ field: "salutationKey",
1147
+ value: key
1148
+ }]
1149
+ }
1150
+ });
1151
+ const { data: result } = await response.json();
1152
+ return result[0];
1153
+ }
1154
+ /**
1155
+ * Retrieves the state machine for order states.
1156
+ */
1157
+ async getOrderStateMachine() {
1158
+ return await this.getStateMachine("order.state");
1159
+ }
1160
+ /**
1161
+ * Retrieves the state machine for delivery states.
1162
+ */
1163
+ async getDeliveryStateMachine() {
1164
+ return await this.getStateMachine("order_delivery.state");
1165
+ }
1166
+ /**
1167
+ * Retrieves the state machine for transaction states.
1168
+ */
1169
+ async getTransactionStateMachine() {
1170
+ return await this.getStateMachine("order_transaction.state");
1171
+ }
1172
+ /**
1173
+ * Retrieves a state machine by its name.
1174
+ *
1175
+ * @param name - The name of the state machine.
1176
+ */
1177
+ async getStateMachine(name) {
1178
+ const response = await this.AdminApiClient.post("search/state-machine", {
1179
+ data: {
1180
+ limit: 1,
1181
+ filter: [{
1182
+ type: "equals",
1183
+ field: "technicalName",
1184
+ value: name
1185
+ }]
1186
+ }
1187
+ });
1188
+ const { data: result } = await response.json();
1189
+ return result[0];
1190
+ }
1191
+ /**
1192
+ * Retrieves the state of a state machine.
1193
+ *
1194
+ * @param stateMachineId - The uuid of the state machine.
1195
+ * @param stateName - The name of the state. Default is "open".
1196
+ */
1197
+ async getStateMachineState(stateMachineId, stateName = "open") {
1198
+ const response = await this.AdminApiClient.post("search/state-machine-state", {
1199
+ data: {
1200
+ limit: 1,
1201
+ filter: [{
1202
+ type: "equals",
1203
+ field: "stateMachineId",
1204
+ value: stateMachineId
1205
+ }, {
1206
+ type: "equals",
1207
+ field: "technicalName",
1208
+ value: stateName
1209
+ }]
1210
+ }
1211
+ });
1212
+ const { data: result } = await response.json();
1213
+ return result[0];
1214
+ }
1215
+ /**
1216
+ * Adds an entity reference to the registry of created records.
1217
+ * All entities added to the registry will be deleted by the cleanup call.
1218
+ *
1219
+ * @param resource - The resource name of the entity.
1220
+ * @param id - The uuid of the entity.
1221
+ */
1222
+ addCreatedRecord(resource, id) {
1223
+ this.createdRecords.push({ resource, id });
1224
+ }
1225
+ /**
1226
+ * Set the configuration of automated data clean up.
1227
+ * If set to "true" the data service will delete all entities created by it.
1228
+ *
1229
+ * @param shouldCleanUp - The config setting for the automated data clean up. Default is "true".
1230
+ */
1231
+ setCleanUp(shouldCleanUp = true) {
1232
+ this.shouldCleanUp = shouldCleanUp;
1233
+ }
1234
+ /**
1235
+ * Will delete all entities created by the data service via sync API.
1236
+ */
1237
+ async cleanUp() {
1238
+ if (!this.shouldCleanUp) {
1239
+ return Promise.reject();
1240
+ }
1241
+ const deleteOperations = {};
1242
+ this.createdRecords.forEach((record) => {
1243
+ if (!deleteOperations[`delete-${record.resource}`]) {
1244
+ deleteOperations[`delete-${record.resource}`] = {
1245
+ entity: record.resource,
1246
+ action: "delete",
1247
+ payload: []
1248
+ };
1249
+ }
1250
+ deleteOperations[`delete-${record.resource}`].payload.push({ id: record.id });
1251
+ });
1252
+ return await this.AdminApiClient.post("_action/sync", {
1253
+ data: deleteOperations
1254
+ });
1255
+ }
1256
+ /**
1257
+ * Convert a JS date object into a date-time compatible string.
1258
+ *
1259
+ * @param date - The JS date object from which the date-time should be retrieved.
1260
+ */
1261
+ convertDateTime(date) {
1262
+ return date.toISOString().slice(0, 19).replace("T", " ");
1263
+ }
1264
+ getBasicProductStruct(taxId = this.defaultTaxId, currencyId = this.defaultCurrencyId, overrides = {}) {
1265
+ const { id: productId, uuid: productUuid } = this.IdProvider.getIdPair();
1266
+ const productName = `${this.namePrefix}Product-${productId}${this.nameSuffix}`;
1267
+ const productNumber = "Product-" + productId;
1268
+ const description = `
1269
+ Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.
1270
+ At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.
1271
+ Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.
1272
+ At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.`.trim();
1273
+ let basicProduct = {
1274
+ id: productUuid,
1275
+ name: productName,
1276
+ description,
1277
+ productNumber,
1278
+ taxId,
1279
+ active: true,
1280
+ stock: 10,
1281
+ price: [{
1282
+ currencyId,
1283
+ gross: 10,
1284
+ linked: false,
1285
+ net: 8.4
1286
+ }],
1287
+ purchasePrices: [{
1288
+ currencyId,
1289
+ gross: 8,
1290
+ linked: false,
1291
+ net: 6.7
1292
+ }]
1293
+ };
1294
+ if (this.defaultCategoryId) {
1295
+ basicProduct = Object.assign({}, basicProduct, {
1296
+ categories: [{
1297
+ id: this.defaultCategoryId
1298
+ }]
1299
+ });
1300
+ }
1301
+ if (this.defaultSalesChannel) {
1302
+ basicProduct = Object.assign({}, basicProduct, {
1303
+ visibilities: [{
1304
+ salesChannelId: this.defaultSalesChannel.id,
1305
+ visibility: 30
1306
+ }]
1307
+ });
1308
+ }
1309
+ return Object.assign({}, basicProduct, overrides);
1310
+ }
1311
+ getProductPriceRangeStruct(currencyId, ruleId) {
1312
+ return {
1313
+ price: [{
1314
+ currencyId,
1315
+ gross: 100,
1316
+ net: 84.03,
1317
+ linked: false
1318
+ }],
1319
+ prices: [{
1320
+ ruleId,
1321
+ price: [{
1322
+ currencyId,
1323
+ gross: 100,
1324
+ net: 84.03,
1325
+ linked: false
1326
+ }],
1327
+ quantityStart: 1,
1328
+ quantityEnd: 10
1329
+ }, {
1330
+ ruleId,
1331
+ price: [{
1332
+ currencyId,
1333
+ gross: 90,
1334
+ net: 75.63,
1335
+ linked: false
1336
+ }],
1337
+ quantityStart: 11,
1338
+ quantityEnd: 20
1339
+ }, {
1340
+ ruleId,
1341
+ price: [{
1342
+ currencyId,
1343
+ gross: 80,
1344
+ net: 67.23,
1345
+ linked: false
1346
+ }],
1347
+ quantityStart: 21,
1348
+ quantityEnd: 50
1349
+ }]
1350
+ };
1351
+ }
1352
+ getBasicCategoryStruct(overrides = {}, parentId = this.defaultCategoryId) {
1353
+ const { id: categoryId, uuid: categoryUuid } = this.IdProvider.getIdPair();
1354
+ const categoryName = `${this.namePrefix}Category-${categoryId}${this.nameSuffix}`;
1355
+ const basicCategory = {
1356
+ id: categoryUuid,
1357
+ name: categoryName,
1358
+ parentId: parentId || null,
1359
+ displayNestedProducts: true,
1360
+ type: "page",
1361
+ productAssignmentType: "product",
1362
+ visible: true,
1363
+ active: true
1364
+ };
1365
+ return Object.assign({}, basicCategory, overrides);
1366
+ }
1367
+ getBasicCustomerStruct(salesChannelId, customerGroupId, languageId, countryId, defaultPaymentMethodId, salutationId, overrides = {}) {
1368
+ const { id, uuid: customerUuid } = this.IdProvider.getIdPair();
1369
+ const firstName = "John";
1370
+ const lastName = "Goldblum";
1371
+ const basicCustomer = {
1372
+ id: customerUuid,
1373
+ email: `customer_${id}@example.com`,
1374
+ password: "shopware",
1375
+ salutationId,
1376
+ languageId,
1377
+ defaultShippingAddress: {
1378
+ firstName,
1379
+ lastName,
1380
+ city: "Sch\xF6ppingen",
1381
+ street: "Ebbinghoff 10",
1382
+ zipcode: "48624",
1383
+ countryId,
1384
+ salutationId
1385
+ },
1386
+ defaultBillingAddress: {
1387
+ firstName,
1388
+ lastName,
1389
+ city: "Sch\xF6ppingen",
1390
+ street: "Ebbinghoff 10",
1391
+ zipcode: "48624",
1392
+ countryId,
1393
+ salutationId
1394
+ },
1395
+ firstName,
1396
+ lastName,
1397
+ salesChannelId,
1398
+ groupId: customerGroupId,
1399
+ customerNumber: `${customerUuid}`,
1400
+ defaultPaymentMethodId
1401
+ };
1402
+ return Object.assign({}, basicCustomer, overrides);
1403
+ }
1404
+ getBasicOrderStruct(lineItems, languageId, currency, paymentMethod, shippingMethod, orderState, deliveryState, transactionState, customer, customerAddress, salesChannelId = this.defaultSalesChannel.id, overrides = {}) {
1405
+ const date = /* @__PURE__ */ new Date();
1406
+ const orderDateTime = this.convertDateTime(date);
1407
+ const shippingDate = new Date(date.getDate() + 3);
1408
+ const shippingDateTime = this.convertDateTime(shippingDate);
1409
+ let totalPrice = 0;
1410
+ const lineItemProducts = [];
1411
+ lineItems.forEach((lineItem) => {
1412
+ lineItemProducts.push(this.getBasicProductLineItemStruct(lineItem));
1413
+ totalPrice += lineItem.product.price[0].gross * (lineItem.quantity || 1);
1414
+ });
1415
+ const shippingCosts = 8.99;
1416
+ totalPrice += shippingCosts;
1417
+ const basicOrder = {
1418
+ orderNumber: this.IdProvider.getIdPair().id,
1419
+ stateId: orderState.id,
1420
+ orderDateTime,
1421
+ currencyId: currency.id,
1422
+ currencyFactor: currency.factor,
1423
+ languageId,
1424
+ salesChannelId,
1425
+ billingAddressId: customerAddress.id,
1426
+ itemRounding: {
1427
+ decimals: 2,
1428
+ interval: 0.01,
1429
+ roundForNet: true
1430
+ },
1431
+ totalRounding: {
1432
+ decimals: 2,
1433
+ interval: 0.01,
1434
+ roundForNet: true
1435
+ },
1436
+ price: {
1437
+ totalPrice,
1438
+ positionPrice: totalPrice,
1439
+ rawTotal: totalPrice,
1440
+ netPrice: totalPrice,
1441
+ taxStatus: "gross",
1442
+ calculatedTaxes: [{
1443
+ tax: 0,
1444
+ taxRate: 0,
1445
+ price: totalPrice
1446
+ }],
1447
+ taxRules: [{
1448
+ taxRate: 0,
1449
+ percentage: 100
1450
+ }]
1451
+ },
1452
+ orderCustomer: {
1453
+ customerId: customer.id,
1454
+ email: customer.email,
1455
+ firstName: customer.firstName,
1456
+ lastName: customer.lastName,
1457
+ salutationId: customer.salutationId
1458
+ },
1459
+ shippingCosts: {
1460
+ unitPrice: shippingCosts,
1461
+ totalPrice: shippingCosts,
1462
+ quantity: 1,
1463
+ calculatedTaxes: [{
1464
+ tax: 0,
1465
+ taxRate: 0,
1466
+ price: shippingCosts
1467
+ }],
1468
+ taxRules: [{
1469
+ taxRate: 0,
1470
+ percentage: 100
1471
+ }]
1472
+ },
1473
+ lineItems: lineItemProducts,
1474
+ deliveries: [
1475
+ {
1476
+ stateId: deliveryState.id,
1477
+ shippingMethodId: shippingMethod.id,
1478
+ shippingOrderAddress: {
1479
+ id: customerAddress.id,
1480
+ salutationId: customerAddress.salutationId,
1481
+ firstName: customerAddress.firstName,
1482
+ lastName: customerAddress.lastName,
1483
+ street: customerAddress.street,
1484
+ zipcode: customerAddress.zipcode,
1485
+ city: customerAddress.city,
1486
+ countryId: customerAddress.countryId,
1487
+ phoneNumber: customerAddress.phoneNumber
1488
+ },
1489
+ shippingDateEarliest: shippingDateTime,
1490
+ shippingDateLatest: shippingDateTime,
1491
+ shippingCosts: {
1492
+ unitPrice: shippingCosts,
1493
+ totalPrice: shippingCosts,
1494
+ quantity: 1,
1495
+ calculatedTaxes: [
1496
+ {
1497
+ tax: 0,
1498
+ taxRate: 0,
1499
+ price: shippingCosts
1500
+ }
1501
+ ],
1502
+ taxRules: [
1503
+ {
1504
+ taxRate: 0,
1505
+ percentage: 100
1506
+ }
1507
+ ]
1508
+ }
1509
+ }
1510
+ ],
1511
+ transactions: [
1512
+ {
1513
+ paymentMethodId: paymentMethod.id,
1514
+ stateId: transactionState.id,
1515
+ amount: {
1516
+ unitPrice: totalPrice,
1517
+ totalPrice,
1518
+ quantity: 1,
1519
+ calculatedTaxes: [
1520
+ {
1521
+ tax: 0,
1522
+ taxRate: 0,
1523
+ price: 0
1524
+ }
1525
+ ],
1526
+ taxRules: [
1527
+ {
1528
+ taxRate: 0,
1529
+ percentage: 100
1530
+ }
1531
+ ]
1532
+ }
1533
+ }
1534
+ ]
1535
+ };
1536
+ return Object.assign({}, basicOrder, overrides);
1537
+ }
1538
+ getBasicProductLineItemStruct(lineItem) {
1539
+ const unitPrice = lineItem.product.price[0].gross || 10;
1540
+ const totalPrice = unitPrice * (lineItem.quantity || 1);
1541
+ return {
1542
+ productId: lineItem.product.id,
1543
+ referencedId: lineItem.product.id,
1544
+ payload: {
1545
+ productNumber: lineItem.product.productNumber
1546
+ },
1547
+ identifier: lineItem.product.id,
1548
+ type: "product",
1549
+ label: lineItem.product.name,
1550
+ quantity: lineItem.quantity || 1,
1551
+ position: lineItem.position || 1,
1552
+ price: {
1553
+ unitPrice,
1554
+ totalPrice,
1555
+ quantity: lineItem.quantity,
1556
+ calculatedTaxes: [{
1557
+ tax: 0,
1558
+ taxRate: 0,
1559
+ price: totalPrice
1560
+ }],
1561
+ taxRules: [{
1562
+ taxRate: 0,
1563
+ percentage: 100
1564
+ }]
1565
+ },
1566
+ priceDefinition: {
1567
+ type: "quantity",
1568
+ price: totalPrice,
1569
+ quantity: lineItem.quantity || 1,
1570
+ taxRules: [{
1571
+ taxRate: 0,
1572
+ percentage: 100
1573
+ }],
1574
+ listPrice: 8,
1575
+ isCalculated: true,
1576
+ referencePriceDefinition: null
1577
+ }
1578
+ };
1579
+ }
1580
+ getBasicPromotionStruct(salesChannelId = this.defaultSalesChannel.id, overrides = {}) {
1581
+ const promotionCode = `${this.IdProvider.getIdPair().id}`;
1582
+ const promotionName = `${this.namePrefix}Promotion-${promotionCode}${this.nameSuffix}`;
1583
+ const basicPromotion = {
1584
+ name: promotionName,
1585
+ active: true,
1586
+ maxRedemptionsGlobal: 100,
1587
+ maxRedemptionsPerCustomer: 10,
1588
+ priority: 1,
1589
+ exclusive: false,
1590
+ useCodes: true,
1591
+ useIndividualCodes: false,
1592
+ useSetGroups: false,
1593
+ preventCombination: true,
1594
+ customerRestriction: false,
1595
+ code: promotionCode,
1596
+ discounts: [{
1597
+ scope: "cart",
1598
+ type: "percentage",
1599
+ value: 10,
1600
+ considerAdvancedRules: false
1601
+ }],
1602
+ salesChannels: [{
1603
+ salesChannelId,
1604
+ priority: 1
1605
+ }]
1606
+ };
1607
+ return Object.assign({}, basicPromotion, overrides);
1608
+ }
1609
+ }
1610
+
608
1611
  const test$9 = test$c.extend({
609
1612
  AdminApiContext: [
610
1613
  async ({}, use) => {
@@ -624,15 +1627,66 @@ const test$9 = test$c.extend({
624
1627
  await use(storeApiContext);
625
1628
  },
626
1629
  { scope: "worker" }
1630
+ ],
1631
+ TestDataService: [
1632
+ async ({ AdminApiContext: AdminApiContext2, IdProvider, DefaultSalesChannel, SalesChannelBaseConfig }, use) => {
1633
+ const DataService = new TestDataService(AdminApiContext2, IdProvider, {
1634
+ defaultSalesChannel: DefaultSalesChannel.salesChannel,
1635
+ defaultTaxId: SalesChannelBaseConfig.taxId,
1636
+ defaultCurrencyId: SalesChannelBaseConfig.defaultCurrencyId,
1637
+ defaultCategoryId: DefaultSalesChannel.salesChannel.navigationCategoryId,
1638
+ defaultLanguageId: DefaultSalesChannel.salesChannel.languageId,
1639
+ defaultCountryId: DefaultSalesChannel.salesChannel.countryId,
1640
+ defaultCustomerGroupId: DefaultSalesChannel.salesChannel.customerGroupId
1641
+ });
1642
+ await use(DataService);
1643
+ await DataService.cleanUp();
1644
+ },
1645
+ { scope: "worker" }
627
1646
  ]
628
1647
  });
629
1648
 
1649
+ async function mockApiCalls(page) {
1650
+ await page.route("**/api/notification/message*", (route) => route.fulfill({
1651
+ status: 200,
1652
+ contentType: "application/json",
1653
+ body: JSON.stringify({ notifications: [], timestamp: "2024-06-19 06:23:25.040" })
1654
+ }));
1655
+ await page.route("**/api/_action/store/plugin/search*", (route) => route.fulfill({
1656
+ status: 200,
1657
+ contentType: "application/json",
1658
+ body: JSON.stringify({ items: [], total: 0 })
1659
+ }));
1660
+ await page.route("**/api/_action/store/updates*", (route) => route.fulfill({
1661
+ status: 200,
1662
+ contentType: "application/json",
1663
+ body: JSON.stringify({ items: [], total: 0 })
1664
+ }));
1665
+ await page.route("**/api/sbp/shop-info*", (route) => route.fulfill({
1666
+ status: 200,
1667
+ contentType: "application/json",
1668
+ body: JSON.stringify({ items: [], total: 0 })
1669
+ }));
1670
+ await page.route("**/api/sbp/shop-info*", (route) => route.fulfill({
1671
+ status: 200,
1672
+ contentType: "application/json",
1673
+ body: process.env.SBP_SHOP_INFO_JSON ?? "{}"
1674
+ }));
1675
+ await page.route("**/api/sbp/bookableplans*", (route) => route.fulfill({
1676
+ status: 200,
1677
+ contentType: "application/json",
1678
+ body: process.env.SBP_BOOKABLE_PLANS_JSON ?? "{}"
1679
+ }));
1680
+ }
1681
+
630
1682
  const test$8 = test$c.extend({
631
1683
  AdminPage: async ({ IdProvider, AdminApiContext, SalesChannelBaseConfig, browser }, use) => {
632
1684
  const context = await browser.newContext({
633
- baseURL: SalesChannelBaseConfig.adminUrl
1685
+ baseURL: SalesChannelBaseConfig.adminUrl,
1686
+ serviceWorkers: "block"
634
1687
  });
635
1688
  const page = await context.newPage();
1689
+ await mockApiCalls(page);
636
1690
  const { id, uuid } = IdProvider.getIdPair();
637
1691
  const adminUser = {
638
1692
  id: uuid,
@@ -650,6 +1704,16 @@ const test$8 = test$c.extend({
650
1704
  });
651
1705
  expect(response.ok()).toBeTruthy();
652
1706
  await page.goto("#/login");
1707
+ await page.addStyleTag({
1708
+ content: `
1709
+ .sf-toolbar {
1710
+ width: 0 !important;
1711
+ height: 0 !important;
1712
+ display: none !important;
1713
+ pointer-events: none !important;
1714
+ }
1715
+ `.trim()
1716
+ });
653
1717
  await page.getByLabel(/Username|Email address/).fill(adminUser.username);
654
1718
  await page.getByLabel("Password").fill(adminUser.password);
655
1719
  await page.getByRole("button", { name: "Log in" }).click();
@@ -662,29 +1726,19 @@ const test$8 = test$c.extend({
662
1726
  await expect(page.locator(".sw-loader")).toHaveCount(0, {
663
1727
  timeout: 1e4
664
1728
  });
665
- await page.addStyleTag({
666
- content: `
667
- .sf-toolbar {
668
- width: 0 !important;
669
- height: 0 !important;
670
- display: none !important;
671
- pointer-events: none !important;
672
- }
673
- `.trim()
674
- });
675
1729
  await use(page);
676
1730
  await page.close();
677
1731
  await context.close();
678
1732
  const cleanupResponse = await AdminApiContext.delete(`user/${uuid}`);
679
1733
  expect(cleanupResponse.ok()).toBeTruthy();
680
1734
  },
681
- StorefrontPage: async ({ DefaultSalesChannel, browser }, use) => {
682
- const { url } = DefaultSalesChannel;
1735
+ StorefrontPage: async ({ DefaultStorefront, browser }, use) => {
1736
+ const { url } = DefaultStorefront;
683
1737
  const context = await browser.newContext({
684
1738
  baseURL: url
685
1739
  });
686
1740
  const page = await context.newPage();
687
- await page.goto("./");
1741
+ await page.goto("./", { waitUntil: "load" });
688
1742
  await use(page);
689
1743
  await page.close();
690
1744
  await context.close();
@@ -783,10 +1837,33 @@ const test$6 = test$c.extend({
783
1837
  IdProvider: [
784
1838
  async ({}, use, workerInfo) => {
785
1839
  const seed = process.env.SHOPWARE_ACCESS_KEY_ID || process.env.SHOPWARE_ADMIN_PASSWORD || "test-suite";
786
- const idProvider = new IdProvider(workerInfo.workerIndex, seed);
1840
+ const idProvider = new IdProvider(workerInfo.parallelIndex, seed);
787
1841
  await use(idProvider);
788
1842
  },
789
1843
  { scope: "worker" }
1844
+ ],
1845
+ SaaSInstanceSetup: [
1846
+ async ({ AdminApiContext, browser }, use) => {
1847
+ const SetupInstance = async function SetupInstance2() {
1848
+ await test$6.skip(!await isSaaSInstance(AdminApiContext), "Skipping SaaS setup, could not detect SaaS instance");
1849
+ const instanceStatusResponse = await AdminApiContext.get("./instance/status");
1850
+ const instanceStatus = await instanceStatusResponse.json();
1851
+ await expect(instanceStatus.tags, 'expect instance to have "ci" tag').toContain("ci");
1852
+ await test$6.skip((await AdminApiContext.get(`${process.env.APP_URL}constructionmode`, { maxRedirects: 0 })).status() > 204, "Instance already setup");
1853
+ const page = await browser.newPage({ baseURL: process.env.ADMIN_URL });
1854
+ await page.goto("./set-up-shop");
1855
+ await page.getByRole("button", { name: "Next" }).click();
1856
+ await expect(page.getByRole("heading", { name: "Everything finished!" })).toBeVisible();
1857
+ await page.getByRole("button", { name: "Open your shop" }).click();
1858
+ await page.goto(`${process.env.ADMIN_URL}`);
1859
+ await page.getByLabel(/Username|Email address/).fill(AdminApiContext.options.admin_username ?? "admin");
1860
+ await page.getByLabel("Password").fill(AdminApiContext.options.admin_password ?? "shopware");
1861
+ await page.getByRole("button", { name: "Log in" }).click();
1862
+ await page.getByRole("button", { name: "Launch your business" }).click();
1863
+ };
1864
+ await use(SetupInstance);
1865
+ },
1866
+ { scope: "worker" }
790
1867
  ]
791
1868
  });
792
1869
 
@@ -1925,15 +3002,6 @@ const PromotionWithCodeData = test$c.extend({
1925
3002
  }
1926
3003
  });
1927
3004
 
1928
- function createRandomImage(width = 800, height = 600) {
1929
- const buffer = Buffer.alloc(width * height * 4);
1930
- let i = 0;
1931
- while (i < buffer.length) {
1932
- buffer[i++] = Math.floor(Math.random() * 256);
1933
- }
1934
- return new Image(width, height, buffer);
1935
- }
1936
-
1937
3005
  const MediaData = test$c.extend({
1938
3006
  MediaData: async ({ AdminApiContext, IdProvider }, use) => {
1939
3007
  const imageId = IdProvider.getIdPair().id;
@@ -2518,11 +3586,6 @@ const test$1 = mergeTests(
2518
3586
  ValidateAccessibility
2519
3587
  );
2520
3588
 
2521
- const isSaaSInstance = async (adminApiContext) => {
2522
- const instanceFeatures = await adminApiContext.get("./instance/features");
2523
- return instanceFeatures.ok();
2524
- };
2525
-
2526
3589
  const test = mergeTests(
2527
3590
  test$6,
2528
3591
  test$a,
@@ -2536,4 +3599,4 @@ const test = mergeTests(
2536
3599
  test$1
2537
3600
  );
2538
3601
 
2539
- export { AdminPageObjects, StorefrontPageObjects, createRandomImage, extractIdFromUrl, getCountryId, getCurrency, getDefaultShippingMethodId, getFlowId, getLanguageData, getMediaId, getOrderTransactionId, getPaymentMethodId, getSalutationId, getSnippetSetId, getStateMachineId, getStateMachineStateId, getTaxId, getThemeId, isSaaSInstance, setOrderStatus, test };
3602
+ export { AdminPageObjects, StorefrontPageObjects, TestDataService, createRandomImage, extractIdFromUrl, getCountryId, getCurrency, getDefaultShippingMethodId, getFlowId, getLanguageData, getMediaId, getOrderTransactionId, getPaymentMethodId, getSalutationId, getSnippetSetId, getStateMachineId, getStateMachineStateId, getTaxId, getThemeId, isSaaSInstance, setOrderStatus, test };