소스 검색

test(core): Add e2e tests for Facet channel assignment

Relates to #1725
Michael Bromley 3 년 전
부모
커밋
10b1bf7e4e

+ 11 - 11
packages/core/e2e/collection.e2e-spec.ts

@@ -10,11 +10,11 @@ import gql from 'graphql-tag';
 import path from 'path';
 import path from 'path';
 
 
 import { initialData } from '../../../e2e-common/e2e-initial-data';
 import { initialData } from '../../../e2e-common/e2e-initial-data';
-import { testConfig, TEST_SETUP_TIMEOUT_MS } from '../../../e2e-common/test-config';
+import { TEST_SETUP_TIMEOUT_MS, testConfig } from '../../../e2e-common/test-config';
 import { pick } from '../../common/lib/pick';
 import { pick } from '../../common/lib/pick';
 import { productIdCollectionFilter, variantIdCollectionFilter } from '../src/index';
 import { productIdCollectionFilter, variantIdCollectionFilter } from '../src/index';
 
 
-import { CHANNEL_FRAGMENT, COLLECTION_FRAGMENT, FACET_VALUE_FRAGMENT } from './graphql/fragments';
+import { COLLECTION_FRAGMENT, FACET_VALUE_FRAGMENT } from './graphql/fragments';
 import {
 import {
     AssignCollectionsToChannelMutation,
     AssignCollectionsToChannelMutation,
     AssignCollectionsToChannelMutationVariables,
     AssignCollectionsToChannelMutationVariables,
@@ -36,8 +36,8 @@ import {
     GetAssetList,
     GetAssetList,
     GetCollection,
     GetCollection,
     GetCollectionBreadcrumbs,
     GetCollectionBreadcrumbs,
-    GetCollectionListQuery,
-    GetCollectionListQueryVariables,
+    GetCollectionListAdminQuery,
+    GetCollectionListAdminQueryVariables,
     GetCollectionNestedParents,
     GetCollectionNestedParents,
     GetCollectionProducts,
     GetCollectionProducts,
     GetCollections,
     GetCollections,
@@ -2126,8 +2126,8 @@ describe('Collection resolver', () => {
         it('assign to channel', async () => {
         it('assign to channel', async () => {
             adminClient.setChannelToken(SECOND_CHANNEL_TOKEN);
             adminClient.setChannelToken(SECOND_CHANNEL_TOKEN);
             const { collections: before } = await adminClient.query<
             const { collections: before } = await adminClient.query<
-                GetCollectionListQuery,
-                GetCollectionListQueryVariables
+                GetCollectionListAdminQuery,
+                GetCollectionListAdminQueryVariables
             >(GET_COLLECTION_LIST);
             >(GET_COLLECTION_LIST);
             expect(before.items.length).toBe(1);
             expect(before.items.length).toBe(1);
             expect(before.items.map(i => i.id).includes(testCollection.id)).toBe(false);
             expect(before.items.map(i => i.id).includes(testCollection.id)).toBe(false);
@@ -2147,8 +2147,8 @@ describe('Collection resolver', () => {
 
 
             adminClient.setChannelToken(SECOND_CHANNEL_TOKEN);
             adminClient.setChannelToken(SECOND_CHANNEL_TOKEN);
             const { collections: after } = await adminClient.query<
             const { collections: after } = await adminClient.query<
-                GetCollectionListQuery,
-                GetCollectionListQueryVariables
+                GetCollectionListAdminQuery,
+                GetCollectionListAdminQueryVariables
             >(GET_COLLECTION_LIST);
             >(GET_COLLECTION_LIST);
             expect(after.items.length).toBe(2);
             expect(after.items.length).toBe(2);
             expect(after.items.map(i => i.id).includes(testCollection.id)).toBe(true);
             expect(after.items.map(i => i.id).includes(testCollection.id)).toBe(true);
@@ -2170,8 +2170,8 @@ describe('Collection resolver', () => {
 
 
             adminClient.setChannelToken(SECOND_CHANNEL_TOKEN);
             adminClient.setChannelToken(SECOND_CHANNEL_TOKEN);
             const { collections: after } = await adminClient.query<
             const { collections: after } = await adminClient.query<
-                GetCollectionListQuery,
-                GetCollectionListQueryVariables
+                GetCollectionListAdminQuery,
+                GetCollectionListAdminQueryVariables
             >(GET_COLLECTION_LIST);
             >(GET_COLLECTION_LIST);
             expect(after.items.length).toBe(1);
             expect(after.items.length).toBe(1);
             expect(after.items.map(i => i.id).includes(testCollection.id)).toBe(false);
             expect(after.items.map(i => i.id).includes(testCollection.id)).toBe(false);
@@ -2204,7 +2204,7 @@ export const GET_COLLECTION = gql`
 `;
 `;
 
 
 export const GET_COLLECTION_LIST = gql`
 export const GET_COLLECTION_LIST = gql`
-    query GetCollectionList($options: CollectionListOptions) {
+    query GetCollectionListAdmin($options: CollectionListOptions) {
         collections(options: $options) {
         collections(options: $options) {
             items {
             items {
                 ...Collection
                 ...Collection

+ 122 - 1
packages/core/e2e/facet.e2e-spec.ts

@@ -8,6 +8,8 @@ import { testConfig, TEST_SETUP_TIMEOUT_MS } from '../../../e2e-common/test-conf
 
 
 import { FACET_VALUE_FRAGMENT, FACET_WITH_VALUES_FRAGMENT } from './graphql/fragments';
 import { FACET_VALUE_FRAGMENT, FACET_WITH_VALUES_FRAGMENT } from './graphql/fragments';
 import {
 import {
+    AssignFacetsToChannelMutation,
+    AssignFacetsToChannelMutationVariables,
     AssignProductsToChannel,
     AssignProductsToChannel,
     ChannelFragment,
     ChannelFragment,
     CreateChannel,
     CreateChannel,
@@ -19,11 +21,16 @@ import {
     DeletionResult,
     DeletionResult,
     FacetWithValues,
     FacetWithValues,
     GetFacetList,
     GetFacetList,
+    GetFacetListSimple,
+    GetFacetListSimpleQuery,
     GetFacetWithValues,
     GetFacetWithValues,
     GetProductListWithVariants,
     GetProductListWithVariants,
     GetProductWithFacetValues,
     GetProductWithFacetValues,
     GetProductWithVariants,
     GetProductWithVariants,
     LanguageCode,
     LanguageCode,
+    RemoveCollectionsFromChannelMutationVariables,
+    RemoveFacetsFromChannelMutation,
+    RemoveFacetsFromChannelMutationVariables,
     UpdateFacet,
     UpdateFacet,
     UpdateFacetValues,
     UpdateFacetValues,
     UpdateProduct,
     UpdateProduct,
@@ -430,6 +437,7 @@ describe('Facet resolver', () => {
 
 
     describe('channels', () => {
     describe('channels', () => {
         const SECOND_CHANNEL_TOKEN = 'second_channel_token';
         const SECOND_CHANNEL_TOKEN = 'second_channel_token';
+        let secondChannel: ChannelFragment;
         let createdFacet: CreateFacet.CreateFacet;
         let createdFacet: CreateFacet.CreateFacet;
 
 
         beforeAll(async () => {
         beforeAll(async () => {
@@ -448,12 +456,14 @@ describe('Facet resolver', () => {
                 },
                 },
             });
             });
 
 
+            secondChannel = createChannel as ChannelFragment;
+
             const { assignProductsToChannel } = await adminClient.query<
             const { assignProductsToChannel } = await adminClient.query<
                 AssignProductsToChannel.Mutation,
                 AssignProductsToChannel.Mutation,
                 AssignProductsToChannel.Variables
                 AssignProductsToChannel.Variables
             >(ASSIGN_PRODUCT_TO_CHANNEL, {
             >(ASSIGN_PRODUCT_TO_CHANNEL, {
                 input: {
                 input: {
-                    channelId: (createChannel as ChannelFragment).id,
+                    channelId: secondChannel.id,
                     productIds: ['T_1'],
                     productIds: ['T_1'],
                     priceFactor: 0.5,
                     priceFactor: 0.5,
                 },
                 },
@@ -634,6 +644,91 @@ describe('Facet resolver', () => {
                 );
                 );
             }, `No Facet with the id '1' could be found`),
             }, `No Facet with the id '1' could be found`),
         );
         );
+
+        it('removing from channel with error', async () => {
+            adminClient.setChannelToken(SECOND_CHANNEL_TOKEN);
+            const { facets: before } = await adminClient.query<GetFacetListSimpleQuery>(
+                GET_FACET_LIST_SIMPLE,
+            );
+            expect(before.items).toEqual([{ id: 'T_4', name: 'Channel Facet' }]);
+
+            adminClient.setChannelToken(E2E_DEFAULT_CHANNEL_TOKEN);
+            const { removeFacetsFromChannel } = await adminClient.query<
+                RemoveFacetsFromChannelMutation,
+                RemoveFacetsFromChannelMutationVariables
+            >(REMOVE_FACETS_FROM_CHANNEL, {
+                input: {
+                    channelId: secondChannel.id,
+                    facetIds: [createdFacet.id],
+                    force: false,
+                },
+            });
+
+            expect(removeFacetsFromChannel).toEqual([
+                {
+                    errorCode: 'FACET_IN_USE_ERROR',
+                    message:
+                        'The selected Facet includes FacetValues which are assigned to 1 Product 1 ProductVariant',
+                    productCount: 1,
+                    variantCount: 1,
+                },
+            ]);
+
+            adminClient.setChannelToken(SECOND_CHANNEL_TOKEN);
+            const { facets: after } = await adminClient.query<GetFacetListSimpleQuery>(GET_FACET_LIST_SIMPLE);
+            expect(after.items).toEqual([{ id: 'T_4', name: 'Channel Facet' }]);
+        });
+
+        it('force removing from channel', async () => {
+            adminClient.setChannelToken(SECOND_CHANNEL_TOKEN);
+            const { facets: before } = await adminClient.query<GetFacetListSimpleQuery>(
+                GET_FACET_LIST_SIMPLE,
+            );
+            expect(before.items).toEqual([{ id: 'T_4', name: 'Channel Facet' }]);
+
+            adminClient.setChannelToken(E2E_DEFAULT_CHANNEL_TOKEN);
+            const { removeFacetsFromChannel } = await adminClient.query<
+                RemoveFacetsFromChannelMutation,
+                RemoveFacetsFromChannelMutationVariables
+            >(REMOVE_FACETS_FROM_CHANNEL, {
+                input: {
+                    channelId: secondChannel.id,
+                    facetIds: [createdFacet.id],
+                    force: true,
+                },
+            });
+
+            expect(removeFacetsFromChannel).toEqual([{ id: 'T_4', name: 'Channel Facet' }]);
+
+            adminClient.setChannelToken(SECOND_CHANNEL_TOKEN);
+            const { facets: after } = await adminClient.query<GetFacetListSimpleQuery>(GET_FACET_LIST_SIMPLE);
+            expect(after.items).toEqual([]);
+        });
+
+        it('assigning to channel', async () => {
+            adminClient.setChannelToken(SECOND_CHANNEL_TOKEN);
+            const { facets: before } = await adminClient.query<GetFacetListSimpleQuery>(
+                GET_FACET_LIST_SIMPLE,
+            );
+            expect(before.items).toEqual([]);
+
+            adminClient.setChannelToken(E2E_DEFAULT_CHANNEL_TOKEN);
+            const { assignFacetsToChannel } = await adminClient.query<
+                AssignFacetsToChannelMutation,
+                AssignFacetsToChannelMutationVariables
+            >(ASSIGN_FACETS_TO_CHANNEL, {
+                input: {
+                    channelId: secondChannel.id,
+                    facetIds: [createdFacet.id],
+                },
+            });
+
+            expect(assignFacetsToChannel).toEqual([{ id: 'T_4', name: 'Channel Facet' }]);
+
+            adminClient.setChannelToken(SECOND_CHANNEL_TOKEN);
+            const { facets: after } = await adminClient.query<GetFacetListSimpleQuery>(GET_FACET_LIST_SIMPLE);
+            expect(after.items).toEqual([{ id: 'T_4', name: 'Channel Facet' }]);
+        });
     });
     });
 
 
     // https://github.com/vendure-ecommerce/vendure/issues/715
     // https://github.com/vendure-ecommerce/vendure/issues/715
@@ -773,3 +868,29 @@ export const UPDATE_FACET_VALUES = gql`
     }
     }
     ${FACET_VALUE_FRAGMENT}
     ${FACET_VALUE_FRAGMENT}
 `;
 `;
+
+export const ASSIGN_FACETS_TO_CHANNEL = gql`
+    mutation AssignFacetsToChannel($input: AssignFacetsToChannelInput!) {
+        assignFacetsToChannel(input: $input) {
+            id
+            name
+        }
+    }
+`;
+
+export const REMOVE_FACETS_FROM_CHANNEL = gql`
+    mutation RemoveFacetsFromChannel($input: RemoveFacetsFromChannelInput!) {
+        removeFacetsFromChannel(input: $input) {
+            ... on Facet {
+                id
+                name
+            }
+            ... on FacetInUseError {
+                errorCode
+                message
+                productCount
+                variantCount
+            }
+        }
+    }
+`;

+ 45 - 0
packages/core/e2e/graphql/generated-e2e-admin-types.ts

@@ -5378,6 +5378,16 @@ export type GetCollectionQuery = { collection?: Maybe<(
     & CollectionFragment
     & CollectionFragment
   )> };
   )> };
 
 
+export type GetCollectionListAdminQueryVariables = Exact<{
+  options?: Maybe<CollectionListOptions>;
+}>;
+
+
+export type GetCollectionListAdminQuery = { collections: (
+    Pick<CollectionList, 'totalItems'>
+    & { items: Array<CollectionFragment> }
+  ) };
+
 export type MoveCollectionMutationVariables = Exact<{
 export type MoveCollectionMutationVariables = Exact<{
   input: MoveCollectionInput;
   input: MoveCollectionInput;
 }>;
 }>;
@@ -5784,6 +5794,20 @@ export type UpdateFacetValuesMutationVariables = Exact<{
 
 
 export type UpdateFacetValuesMutation = { updateFacetValues: Array<FacetValueFragment> };
 export type UpdateFacetValuesMutation = { updateFacetValues: Array<FacetValueFragment> };
 
 
+export type AssignFacetsToChannelMutationVariables = Exact<{
+  input: AssignFacetsToChannelInput;
+}>;
+
+
+export type AssignFacetsToChannelMutation = { assignFacetsToChannel: Array<Pick<Facet, 'id' | 'name'>> };
+
+export type RemoveFacetsFromChannelMutationVariables = Exact<{
+  input: RemoveFacetsFromChannelInput;
+}>;
+
+
+export type RemoveFacetsFromChannelMutation = { removeFacetsFromChannel: Array<Pick<Facet, 'id' | 'name'> | Pick<FacetInUseError, 'errorCode' | 'message' | 'productCount' | 'variantCount'>> };
+
 export type GetGlobalSettingsQueryVariables = Exact<{ [key: string]: never; }>;
 export type GetGlobalSettingsQueryVariables = Exact<{ [key: string]: never; }>;
 
 
 
 
@@ -7497,6 +7521,13 @@ export namespace GetCollection {
   export type Items = NonNullable<(NonNullable<(NonNullable<(NonNullable<GetCollectionQuery['collection']>)['productVariants']>)['items']>)[number]>;
   export type Items = NonNullable<(NonNullable<(NonNullable<(NonNullable<GetCollectionQuery['collection']>)['productVariants']>)['items']>)[number]>;
 }
 }
 
 
+export namespace GetCollectionListAdmin {
+  export type Variables = GetCollectionListAdminQueryVariables;
+  export type Query = GetCollectionListAdminQuery;
+  export type Collections = (NonNullable<GetCollectionListAdminQuery['collections']>);
+  export type Items = NonNullable<(NonNullable<(NonNullable<GetCollectionListAdminQuery['collections']>)['items']>)[number]>;
+}
+
 export namespace MoveCollection {
 export namespace MoveCollection {
   export type Variables = MoveCollectionMutationVariables;
   export type Variables = MoveCollectionMutationVariables;
   export type Mutation = MoveCollectionMutation;
   export type Mutation = MoveCollectionMutation;
@@ -7847,6 +7878,20 @@ export namespace UpdateFacetValues {
   export type UpdateFacetValues = NonNullable<(NonNullable<UpdateFacetValuesMutation['updateFacetValues']>)[number]>;
   export type UpdateFacetValues = NonNullable<(NonNullable<UpdateFacetValuesMutation['updateFacetValues']>)[number]>;
 }
 }
 
 
+export namespace AssignFacetsToChannel {
+  export type Variables = AssignFacetsToChannelMutationVariables;
+  export type Mutation = AssignFacetsToChannelMutation;
+  export type AssignFacetsToChannel = NonNullable<(NonNullable<AssignFacetsToChannelMutation['assignFacetsToChannel']>)[number]>;
+}
+
+export namespace RemoveFacetsFromChannel {
+  export type Variables = RemoveFacetsFromChannelMutationVariables;
+  export type Mutation = RemoveFacetsFromChannelMutation;
+  export type RemoveFacetsFromChannel = NonNullable<(NonNullable<RemoveFacetsFromChannelMutation['removeFacetsFromChannel']>)[number]>;
+  export type FacetInlineFragment = (DiscriminateUnion<NonNullable<(NonNullable<RemoveFacetsFromChannelMutation['removeFacetsFromChannel']>)[number]>, { __typename?: 'Facet' }>);
+  export type FacetInUseErrorInlineFragment = (DiscriminateUnion<NonNullable<(NonNullable<RemoveFacetsFromChannelMutation['removeFacetsFromChannel']>)[number]>, { __typename?: 'FacetInUseError' }>);
+}
+
 export namespace GetGlobalSettings {
 export namespace GetGlobalSettings {
   export type Variables = GetGlobalSettingsQueryVariables;
   export type Variables = GetGlobalSettingsQueryVariables;
   export type Query = GetGlobalSettingsQuery;
   export type Query = GetGlobalSettingsQuery;

+ 1 - 0
packages/core/src/i18n/messages/en.json

@@ -60,6 +60,7 @@
     "CREATE_FULFILLMENT_ERROR": "An error occurred when attempting to create the Fulfillment",
     "CREATE_FULFILLMENT_ERROR": "An error occurred when attempting to create the Fulfillment",
     "EMAIL_ADDRESS_CONFLICT_ERROR": "The email address is not available.",
     "EMAIL_ADDRESS_CONFLICT_ERROR": "The email address is not available.",
     "EMPTY_ORDER_LINE_SELECTION_ERROR": "At least one OrderLine must be specified",
     "EMPTY_ORDER_LINE_SELECTION_ERROR": "At least one OrderLine must be specified",
+    "FACET_IN_USE_ERROR": "The selected Facet includes FacetValues which are assigned to {productCount, plural, =0 {} one {1 Product} other {# Products}} {variantCount, plural, =0 {} one {1 ProductVariant} other {# ProductVariants}}",
     "IDENTIFIER_CHANGE_TOKEN_INVALID_ERROR": "Identifier change token not recognized",
     "IDENTIFIER_CHANGE_TOKEN_INVALID_ERROR": "Identifier change token not recognized",
     "INELIGIBLE_SHIPPING_METHOD_ERROR": "This Order is not eligible for the selected ShippingMethod",
     "INELIGIBLE_SHIPPING_METHOD_ERROR": "This Order is not eligible for the selected ShippingMethod",
     "INSUFFICIENT_STOCK_ERROR": "{quantityAvailable, plural, =0 {No items were} one {Only 1 item was} other {Only # items were}} added to the order due to insufficient stock",
     "INSUFFICIENT_STOCK_ERROR": "{quantityAvailable, plural, =0 {No items were} one {Only 1 item was} other {Only # items were}} added to the order due to insufficient stock",

+ 3 - 1
packages/core/src/service/services/facet.service.ts

@@ -301,7 +301,9 @@ export class FacetService {
         if (idsAreEqual(input.channelId, defaultChannel.id)) {
         if (idsAreEqual(input.channelId, defaultChannel.id)) {
             throw new UserInputError('error.facets-cannot-be-removed-from-default-channel');
             throw new UserInputError('error.facets-cannot-be-removed-from-default-channel');
         }
         }
-        const facets = await this.connection.getRepository(ctx, Facet).findByIds(input.facetIds);
+        const facets = await this.connection
+            .getRepository(ctx, Facet)
+            .findByIds(input.facetIds, { relations: ['values'] });
 
 
         const results: Array<ErrorResultUnion<RemoveFacetFromChannelResult, Facet>> = [];
         const results: Array<ErrorResultUnion<RemoveFacetFromChannelResult, Facet>> = [];