import gql from 'graphql-tag'; import { TEST_NOT_EXISTING_PAGE_LAYOUT_ID } from 'test/integration/constants/test-page-layout-ids.constants'; import { createPageLayoutOperationFactory } from 'test/integration/graphql/utils/create-page-layout-operation-factory.util'; import { deletePageLayoutOperationFactory } from 'test/integration/graphql/utils/delete-page-layout-operation-factory.util'; import { destroyPageLayoutOperationFactory } from 'test/integration/graphql/utils/destroy-page-layout-operation-factory.util'; import { findPageLayoutOperationFactory } from 'test/integration/graphql/utils/find-page-layout-operation-factory.util'; import { findPageLayoutsOperationFactory } from 'test/integration/graphql/utils/find-page-layouts-operation-factory.util'; import { assertGraphQLErrorResponse, assertGraphQLSuccessfulResponse, } from 'test/integration/graphql/utils/graphql-test-assertions.util'; import { makeGraphqlAPIRequest } from 'test/integration/graphql/utils/make-graphql-api-request.util'; import { createTestPageLayoutWithGraphQL } from 'test/integration/graphql/utils/page-layout-graphql.util'; import { restorePageLayoutOperationFactory } from 'test/integration/graphql/utils/restore-page-layout-operation-factory.util'; import { updatePageLayoutOperationFactory } from 'test/integration/graphql/utils/update-page-layout-operation-factory.util'; import { createOneObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/create-one-object-metadata.util'; import { deleteOneObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/delete-one-object-metadata.util'; import { updateOneObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/update-one-object-metadata.util'; import { assertPageLayoutStructure, cleanupPageLayoutRecords, } from 'test/integration/utils/page-layout-test.util'; import { ErrorCode } from 'src/engine/core-modules/graphql/utils/graphql-errors.util'; import { PageLayoutType } from 'src/engine/core-modules/page-layout/enums/page-layout-type.enum'; import { PageLayoutExceptionMessageKey, generatePageLayoutExceptionMessage, } from 'src/engine/core-modules/page-layout/exceptions/page-layout.exception'; describe('Page Layout Resolver', () => { let testObjectMetadataId: string; beforeAll(async () => { const { data: { createOneObject: { id: objectMetadataId }, }, } = await createOneObjectMetadata({ input: { nameSingular: 'myTestPageLayoutObject', namePlural: 'myTestPageLayoutObjects', labelSingular: 'My Test Page Layout Object', labelPlural: 'My Test Page Layout Objects', icon: 'IconLayout', }, }); testObjectMetadataId = objectMetadataId; }); afterAll(async () => { await updateOneObjectMetadata({ expectToFail: false, input: { idToUpdate: testObjectMetadataId, updatePayload: { isActive: false, }, }, }); await deleteOneObjectMetadata({ input: { idToDelete: testObjectMetadataId }, }); await cleanupPageLayoutRecords(); }); beforeEach(async () => { await cleanupPageLayoutRecords(); }); describe('getPageLayouts', () => { it('should return all page layouts for workspace when no objectMetadataId provided', async () => { const pageLayoutName = 'Test Page Layout for Workspace'; await createTestPageLayoutWithGraphQL({ name: pageLayoutName, objectMetadataId: testObjectMetadataId, }); const operation = findPageLayoutsOperationFactory(); const response = await makeGraphqlAPIRequest(operation); assertGraphQLSuccessfulResponse(response); expect(response.body.data.getPageLayouts).toHaveLength(1); assertPageLayoutStructure(response.body.data.getPageLayouts[0], { name: pageLayoutName, objectMetadataId: testObjectMetadataId, type: PageLayoutType.RECORD_PAGE, }); }); it('should filter page layouts by objectMetadataId when provided', async () => { const object1PageLayoutName = 'Page Layout for Object 1'; const object2PageLayoutName = 'Page Layout for Object 2'; const { data: { createOneObject: { id: objectMetadata2Id }, }, } = await createOneObjectMetadata({ input: { nameSingular: 'myTestPageLayoutObject2', namePlural: 'myTestPageLayoutObjects2', labelSingular: 'My Test Page Layout Object 2', labelPlural: 'My Test Page Layout Objects 2', icon: 'IconLayout2', isLabelSyncedWithName: false, }, }); await Promise.all([ createTestPageLayoutWithGraphQL({ name: object1PageLayoutName, objectMetadataId: testObjectMetadataId, }), createTestPageLayoutWithGraphQL({ name: object2PageLayoutName, objectMetadataId: objectMetadata2Id, }), ]); const operation = findPageLayoutsOperationFactory({ objectMetadataId: testObjectMetadataId, }); const response = await makeGraphqlAPIRequest(operation); assertGraphQLSuccessfulResponse(response); expect(response.body.data.getPageLayouts).toHaveLength(1); assertPageLayoutStructure(response.body.data.getPageLayouts[0], { name: object1PageLayoutName, objectMetadataId: testObjectMetadataId, }); await updateOneObjectMetadata({ expectToFail: false, input: { idToUpdate: objectMetadata2Id, updatePayload: { isActive: false, }, }, }); await deleteOneObjectMetadata({ input: { idToDelete: objectMetadata2Id }, }); }); }); describe('getPageLayout', () => { it('should throw when page layout does not exist', async () => { const operation = findPageLayoutOperationFactory({ pageLayoutId: TEST_NOT_EXISTING_PAGE_LAYOUT_ID, }); const response = await makeGraphqlAPIRequest(operation); assertGraphQLErrorResponse( response, ErrorCode.NOT_FOUND, generatePageLayoutExceptionMessage( PageLayoutExceptionMessageKey.PAGE_LAYOUT_NOT_FOUND, TEST_NOT_EXISTING_PAGE_LAYOUT_ID, ), ); }); it('should return page layout when it exists', async () => { const pageLayoutName = 'Test Page Layout for Get'; const pageLayout = await createTestPageLayoutWithGraphQL({ name: pageLayoutName, objectMetadataId: testObjectMetadataId, }); const operation = findPageLayoutOperationFactory({ pageLayoutId: pageLayout.id, }); const response = await makeGraphqlAPIRequest(operation); assertGraphQLSuccessfulResponse(response); assertPageLayoutStructure(response.body.data.getPageLayout, { id: pageLayout.id, name: pageLayoutName, objectMetadataId: testObjectMetadataId, type: PageLayoutType.RECORD_PAGE, }); }); }); describe('createPageLayout', () => { it('should create a new page layout with all properties', async () => { const input = { name: 'Dashboard Page Layout', type: PageLayoutType.DASHBOARD, objectMetadataId: testObjectMetadataId, }; const operation = createPageLayoutOperationFactory({ data: input }); const response = await makeGraphqlAPIRequest(operation); assertGraphQLSuccessfulResponse(response); const createdPageLayout = response.body.data.createPageLayout; assertPageLayoutStructure(createdPageLayout, { name: input.name, type: input.type, objectMetadataId: input.objectMetadataId, deletedAt: null, }); }); it('should create a page layout with minimum required fields', async () => { const input = { name: 'Minimal Page Layout', }; const operation = createPageLayoutOperationFactory({ data: input }); const response = await makeGraphqlAPIRequest(operation); assertGraphQLSuccessfulResponse(response); const createdPageLayout = response.body.data.createPageLayout; assertPageLayoutStructure(createdPageLayout, { name: input.name, type: PageLayoutType.RECORD_PAGE, objectMetadataId: null, deletedAt: null, }); }); }); describe('updatePageLayout', () => { it('should update an existing page layout', async () => { const pageLayout = await createTestPageLayoutWithGraphQL({ name: 'Original Page Layout', type: PageLayoutType.RECORD_PAGE, }); const updateInput = { name: 'Updated Page Layout', type: PageLayoutType.DASHBOARD, }; const operation = updatePageLayoutOperationFactory({ pageLayoutId: pageLayout.id, data: updateInput, }); const response = await makeGraphqlAPIRequest(operation); assertGraphQLSuccessfulResponse(response); assertPageLayoutStructure(response.body.data.updatePageLayout, { id: pageLayout.id, name: updateInput.name, type: updateInput.type, deletedAt: null, }); }); it('should update only provided fields', async () => { const pageLayout = await createTestPageLayoutWithGraphQL({ name: 'Original Page Layout', type: PageLayoutType.RECORD_PAGE, }); const updateInput = { name: 'Updated Name Only', }; const operation = updatePageLayoutOperationFactory({ pageLayoutId: pageLayout.id, data: updateInput, }); const response = await makeGraphqlAPIRequest(operation); assertGraphQLSuccessfulResponse(response); assertPageLayoutStructure(response.body.data.updatePageLayout, { id: pageLayout.id, name: updateInput.name, type: PageLayoutType.RECORD_PAGE, deletedAt: null, }); }); it('should throw error when updating non-existent page layout', async () => { const operation = updatePageLayoutOperationFactory({ pageLayoutId: TEST_NOT_EXISTING_PAGE_LAYOUT_ID, data: { name: 'Non-existent Page Layout' }, }); const response = await makeGraphqlAPIRequest(operation); assertGraphQLErrorResponse( response, ErrorCode.NOT_FOUND, generatePageLayoutExceptionMessage( PageLayoutExceptionMessageKey.PAGE_LAYOUT_NOT_FOUND, TEST_NOT_EXISTING_PAGE_LAYOUT_ID, ), ); }); }); describe('deletePageLayout', () => { it('should delete an existing page layout (soft delete)', async () => { const pageLayout = await createTestPageLayoutWithGraphQL({ name: 'Page Layout to Delete', }); const deleteOperation = deletePageLayoutOperationFactory({ pageLayoutId: pageLayout.id, }); const deleteResponse = await makeGraphqlAPIRequest(deleteOperation); assertGraphQLSuccessfulResponse(deleteResponse); assertPageLayoutStructure( deleteResponse.body.data.deletePageLayout, pageLayout, ); const getOperation = findPageLayoutOperationFactory({ pageLayoutId: pageLayout.id, }); const getResponse = await makeGraphqlAPIRequest(getOperation); assertGraphQLErrorResponse( getResponse, ErrorCode.NOT_FOUND, generatePageLayoutExceptionMessage( PageLayoutExceptionMessageKey.PAGE_LAYOUT_NOT_FOUND, pageLayout.id, ), ); }); it('should throw an error when deleting non-existent page layout', async () => { const operation = deletePageLayoutOperationFactory({ pageLayoutId: TEST_NOT_EXISTING_PAGE_LAYOUT_ID, }); const response = await makeGraphqlAPIRequest(operation); assertGraphQLErrorResponse( response, ErrorCode.NOT_FOUND, generatePageLayoutExceptionMessage( PageLayoutExceptionMessageKey.PAGE_LAYOUT_NOT_FOUND, TEST_NOT_EXISTING_PAGE_LAYOUT_ID, ), ); }); }); describe('destroyPageLayout', () => { it('should destroy an existing page layout (hard delete)', async () => { const pageLayout = await createTestPageLayoutWithGraphQL({ name: 'Page Layout to Destroy', }); const destroyOperation = destroyPageLayoutOperationFactory({ pageLayoutId: pageLayout.id, }); const destroyResponse = await makeGraphqlAPIRequest(destroyOperation); assertGraphQLSuccessfulResponse(destroyResponse); expect(destroyResponse.body.data.destroyPageLayout).toBe(true); }); it('should throw an error when destroying non-existent page layout', async () => { const operation = destroyPageLayoutOperationFactory({ pageLayoutId: TEST_NOT_EXISTING_PAGE_LAYOUT_ID, }); const response = await makeGraphqlAPIRequest(operation); assertGraphQLErrorResponse( response, ErrorCode.NOT_FOUND, generatePageLayoutExceptionMessage( PageLayoutExceptionMessageKey.PAGE_LAYOUT_NOT_FOUND, TEST_NOT_EXISTING_PAGE_LAYOUT_ID, ), ); }); it('should destroy all associated dashboards when page layout is of type dashboard', async () => { const dashboardId = '20202020-304c-44f2-ba7b-070762ff0e8a'; const pageLayout = await createTestPageLayoutWithGraphQL({ name: 'Page Layout to Destroy', type: PageLayoutType.DASHBOARD, }); const findOneDashboardOperation = { query: gql` query Dashboard($filter: DashboardFilterInput!) { dashboard(filter: $filter) { id } } `, variables: { filter: { id: { eq: dashboardId } }, }, }; await makeGraphqlAPIRequest({ query: gql` mutation CreateDashboard($input: CreateDashboardInput!) { createDashboard(input: $input) { pageLayoutId id title } } `, variables: { input: { id: dashboardId, name: 'Dashboard to Destroy', pageLayoutId: pageLayout.id, }, }, }); const findOneDashboardResponseBeforeDestroy = await makeGraphqlAPIRequest( findOneDashboardOperation, ); expect( findOneDashboardResponseBeforeDestroy.body.data.dashboard, ).toBeDefined(); const destroyOperation = destroyPageLayoutOperationFactory({ pageLayoutId: pageLayout.id, }); const destroyResponse = await makeGraphqlAPIRequest(destroyOperation); const findOneDashboardResponseAfterDestroy = await makeGraphqlAPIRequest( findOneDashboardOperation, ); assertGraphQLSuccessfulResponse(destroyResponse); expect(destroyResponse.body.data.destroyPageLayout).toBe(true); assertGraphQLErrorResponse( findOneDashboardResponseAfterDestroy, ErrorCode.NOT_FOUND, ); }); }); describe('restorePageLayout', () => { it('should restore a soft deleted page layout', async () => { const pageLayout = await createTestPageLayoutWithGraphQL({ name: 'Page Layout to Restore', type: PageLayoutType.RECORD_INDEX, }); const deleteOperation = deletePageLayoutOperationFactory({ pageLayoutId: pageLayout.id, }); const deleteResponse = await makeGraphqlAPIRequest(deleteOperation); assertGraphQLSuccessfulResponse(deleteResponse); const restoreOperation = restorePageLayoutOperationFactory({ pageLayoutId: pageLayout.id, }); const restoreResponse = await makeGraphqlAPIRequest(restoreOperation); assertGraphQLSuccessfulResponse(restoreResponse); assertPageLayoutStructure(restoreResponse.body.data.restorePageLayout, { id: pageLayout.id, name: 'Page Layout to Restore', type: PageLayoutType.RECORD_INDEX, deletedAt: null, }); const getOperation = findPageLayoutOperationFactory({ pageLayoutId: pageLayout.id, }); const getResponse = await makeGraphqlAPIRequest(getOperation); assertGraphQLSuccessfulResponse(getResponse); assertPageLayoutStructure(getResponse.body.data.getPageLayout, { id: pageLayout.id, name: 'Page Layout to Restore', type: PageLayoutType.RECORD_INDEX, deletedAt: null, }); }); it('should throw an error when restoring non-existent page layout', async () => { const operation = restorePageLayoutOperationFactory({ pageLayoutId: TEST_NOT_EXISTING_PAGE_LAYOUT_ID, }); const response = await makeGraphqlAPIRequest(operation); assertGraphQLErrorResponse( response, ErrorCode.NOT_FOUND, generatePageLayoutExceptionMessage( PageLayoutExceptionMessageKey.PAGE_LAYOUT_NOT_FOUND, TEST_NOT_EXISTING_PAGE_LAYOUT_ID, ), ); }); }); describe('tabs resolver field', () => { it('should resolve tabs field for page layout', async () => { const pageLayout = await createTestPageLayoutWithGraphQL({ name: 'Page Layout with Tabs', }); const operation = findPageLayoutOperationFactory({ pageLayoutId: pageLayout.id, gqlFields: ` id name type createdAt updatedAt deletedAt tabs { id title position pageLayoutId } `, }); const response = await makeGraphqlAPIRequest(operation); assertGraphQLSuccessfulResponse(response); assertPageLayoutStructure(response.body.data.getPageLayout, { id: pageLayout.id, name: 'Page Layout with Tabs', tabs: expect.any(Array), }); }); }); });