import { randomUUID } from 'crypto'; import gql from 'graphql-tag'; import { execute as executeNoAuth, signUpAnonymously, startSessionAnonymously, } from 'test/graphql'; import { ProductList, User, Page } from '@/entities'; const createPage = async (values = {}) => Page.create({ firebaseId: randomUUID(), price: 100, retailer: 'Lowes', url: 'test://test', ...values, }).save(); const createList = async ({ createdBy, collaborators, pages, ...other }: Partial = {}) => { const { data } = createdBy ? { data: {} } : await signUpAnonymously(); const list = ProductList.create({ createdBy: createdBy || data?.signUpAnonymously?.user, ...other, }); collaborators && (list.collaborators = collaborators); pages && (list.pages = pages); return list.save(); }; describe('Query:productList', () => { afterEach(() => { jest.restoreAllMocks(); }); const query = gql` query ($input: ProductListInput!) { productList(input: $input) { id viewerCanEdit createdBy { id } collaborators { id } } } `; test('should return product list', async () => { const list = await createList({ collaborators: Promise.resolve([ (await signUpAnonymously())?.data?.signUpAnonymously?.user as User, ]), }); const response = await executeNoAuth({ query: query, variables: { input: { id: list.id }, }, }); expect(response.errors?.length).toBeFalsy(); expect(response.data?.productList.id).toBe(list.id); expect(response.data?.productList.viewerCanEdit).toBe(false); expect(response.data?.productList.createdBy?.id).toBe(list.createdBy.id); expect(response.data?.productList.collaborators).toHaveLength(1); }); test('should return can edit true if user is creator', async () => { const { execute, user } = await startSessionAnonymously(); const list = await createList({ createdBy: user, }); const response = await execute({ query: query, variables: { input: { id: list.id }, }, }); expect(response.data?.productList.id).toBe(list.id); expect(response.data?.productList.viewerCanEdit).toBe(true); }); test('should return can edit true if user is collaborator', async () => { const { execute, user } = await startSessionAnonymously(); const list = await createList({ collaborators: Promise.resolve([user]), }); const response = await execute({ query: query, variables: { input: { id: list.id }, }, }); expect(response.data?.productList.id).toBe(list.id); expect(response.data?.productList.viewerCanEdit).toBe(true); }); test('should fail if product list does not exist', async () => { const response = await executeNoAuth({ query: query, variables: { input: { id: randomUUID() }, }, }); expect(response.errors).toHaveLength(1); expect( response.errors?.[0].message.startsWith( 'Could not find any entity of type "ProductList" matching:' ) ).toBeTruthy(); expect(response.data?.productList).toBeNull(); }); }); describe('Query:productLists', () => { afterEach(() => { jest.restoreAllMocks(); }); const query = gql` query { productLists { edges { node { id collaborators { id } createdBy { id } } } } } `; test('should return product lists of given user', async () => { const { execute, user } = await startSessionAnonymously(); const list1 = await createList({ createdBy: user }); const list2 = await createList({ collaborators: Promise.resolve([user]), }); await createList(); const response = await execute({ query: query, }); expect(response.errors?.length).toBeFalsy(); expect(response.data?.productLists?.edges).toHaveLength(2); expect( response.data?.productLists?.edges?.map((e: any) => e.node.id).sort() ).toEqual([list1.id, list2.id].sort()); expect( response.data?.productLists?.edges?.find( (e: any) => e.node.id === list2.id )?.node?.collaborators?.[0]?.id ).toEqual(user.id); }); test('should return empty if user has no lists', async () => { const { execute } = await startSessionAnonymously(); await createList(); const response = await execute({ query: query, }); expect(response.errors?.length).toBeFalsy(); expect(response.data?.productLists?.edges).toHaveLength(0); }); }); describe('Mutation:createProductList', () => { afterEach(() => { jest.restoreAllMocks(); }); const query = gql` mutation ($input: CreateProductListInput!) { createProductList(input: $input) { productList { id createdBy { id } pages { id } collaborators { id } } } } `; test('should fail if user not signed in', async () => { const response = await executeNoAuth({ query, variables: { input: {}, }, }); expect(response.errors).toHaveLength(1); expect(response.errors?.[0].message).toMatchSnapshot('not authorized'); expect(response.data).toBeNull(); }); test('should create if user signed in', async () => { const { execute, user } = await startSessionAnonymously(); const page = await createPage(); const user2 = (await signUpAnonymously()).data?.signUpAnonymously?.user; const response = await execute({ query: query, variables: { input: { pageIds: [page.id], collaboratorIds: [user2.id] }, }, }); expect(response.errors || []).toHaveLength(0); expect(response.data?.createProductList?.productList?.id).toBeTruthy(); expect( response.data?.createProductList?.productList?.createdBy?.id ).toEqual(user.id); expect( response.data?.createProductList?.productList?.pages?.[0]?.id ).toEqual(page.id); expect( response.data?.createProductList?.productList?.collaborators?.[0]?.id ).toEqual(user2.id); }); }); describe('Mutation:updateProductList', () => { afterEach(() => { jest.restoreAllMocks(); }); const query = gql` mutation ($input: UpdateProductListInput!) { updateProductList(input: $input) { productList { id createdBy { id } pages { id } collaborators { id } } } } `; test('should fail if user not signed in', async () => { const list = await createList(); const response = await executeNoAuth({ query, variables: { input: { id: list.id }, }, }); expect(response.errors).toHaveLength(1); expect(response.errors?.[0].message).toMatchSnapshot('not authorized'); }); test('should fail if no id given', async () => { const { execute } = await startSessionAnonymously(); const response = await execute({ query, variables: { input: {}, }, }); expect(response.errors).toHaveLength(1); expect(response.errors?.[0].message).toMatchSnapshot('no id given'); }); test('should fail if signed user is not creator/collaborator', async () => { const { execute } = await startSessionAnonymously(); const user2 = (await signUpAnonymously()).data?.signUpAnonymously?.user; const list = await createList({ createdBy: user2 }); const page = await createPage(); const response = await execute({ query: query, variables: { input: { id: list.id, pageIds: [page.id] }, }, }); expect(response.errors).toHaveLength(1); expect(response.errors?.[0].message).toMatchSnapshot( 'user not collaborator' ); expect(response.data).toBeNull(); }); test('should update if signed user is creator', async () => { const { execute, user } = await startSessionAnonymously(); const user2 = (await signUpAnonymously()).data?.signUpAnonymously?.user; const list = await createList({ createdBy: user }); const page = await createPage(); const response = await execute({ query: query, variables: { input: { id: list.id, pageIds: [page.id], collaboratorIds: [user2.id], }, }, }); expect(response.errors || []).toHaveLength(0); expect(response.data?.updateProductList?.productList?.id).toEqual(list.id); expect( response.data?.updateProductList?.productList?.pages?.[0]?.id ).toEqual(page.id); expect( response.data?.updateProductList?.productList?.collaborators?.[0]?.id ).toEqual(user2.id); }); test('should update if signed user is collaborator, but should not update collaborator list', async () => { const { execute, user } = await startSessionAnonymously(); const user2 = (await signUpAnonymously()).data?.signUpAnonymously?.user; const list = await createList({ collaborators: Promise.resolve([user]) }); const page = await createPage(); const response = await execute({ query: query, variables: { input: { id: list.id, pageIds: [page.id], collaboratorIds: [user2.id], }, }, }); expect(response.errors || []).toHaveLength(0); expect(response.data?.updateProductList?.productList?.id).toEqual(list.id); expect( response.data?.updateProductList?.productList?.pages?.[0]?.id ).toEqual(page.id); expect( response.data?.updateProductList?.productList?.collaborators ).toHaveLength(1); expect( response.data?.updateProductList?.productList?.collaborators?.[0]?.id ).toEqual(user.id); }); }); describe('Mutation:updatePagesInList', () => { afterEach(() => { jest.restoreAllMocks(); }); const query = gql` mutation ($input: UpdatePagesInListInput!) { updatePagesInList(input: $input) { productList { id createdBy { id } pages { id } collaborators { id } } } } `; test('should fail if user not signed in', async () => { const list = await createList(); const response = await executeNoAuth({ query, variables: { input: { listId: list.id }, }, }); expect(response.errors).toHaveLength(1); expect(response.errors?.[0].message).toMatchSnapshot('not authorized'); }); test('should create list if no id given', async () => { const { execute } = await startSessionAnonymously(); const response = await execute({ query, variables: { input: {}, }, }); expect(response.errors).toBeUndefined(); expect(response.data?.updatePagesInList?.productList?.id).toBeTruthy(); }); test('should fail if signed user is not creator/collaborator', async () => { const { execute } = await startSessionAnonymously(); const user2 = (await signUpAnonymously()).data?.signUpAnonymously?.user; const list = await createList({ createdBy: user2 }); const page = await createPage(); const response = await execute({ query: query, variables: { input: { listId: list.id, pageIdsToAdd: [page.id] }, }, }); expect(response.errors).toHaveLength(1); expect(response.errors?.[0].message).toMatchSnapshot( 'user not collaborator' ); expect(response.data).toBeNull(); }); test('should add/remove given product ids', async () => { const { execute, user } = await startSessionAnonymously(); const page = await createPage(); const list = await createList({ createdBy: user, pages: Promise.resolve([page]), }); const page2 = await createPage(); const response = await execute({ query: query, variables: { input: { listId: list.id, pageIdsToAdd: [page2.id], pageIdsToRemove: [page.id], }, }, }); expect(response.errors).toBeUndefined(); expect(response.data?.updatePagesInList?.productList?.id).toBeTruthy(); expect(response.data?.updatePagesInList?.productList?.pages).toHaveLength( 1 ); expect( response.data?.updatePagesInList?.productList?.pages?.[0]?.id ).toEqual(page2.id); }); test('should add/remove given product firebase ids', async () => { const { execute, user } = await startSessionAnonymously(); const page = await createPage(); const list = await createList({ createdBy: user, pages: Promise.resolve([page]), }); const page2 = await createPage(); const response = await execute({ query: query, variables: { input: { listId: list.id, pageIdsToAdd: [page2.firebaseId], pageIdsToRemove: [page.firebaseId], }, }, }); expect(response.errors).toBeUndefined(); expect(response.data?.updatePagesInList?.productList?.id).toBeTruthy(); expect(response.data?.updatePagesInList?.productList?.pages).toHaveLength( 1 ); expect( response.data?.updatePagesInList?.productList?.pages?.[0]?.id ).toEqual(page2.id); }); test('should not fail on adding/removing wrong product ids', async () => { const { execute, user } = await startSessionAnonymously(); const page = await createPage(); const list = await createList({ createdBy: user, pages: Promise.resolve([page]), }); const response = await execute({ query: query, variables: { input: { listId: list.id, pageIdsToAdd: [randomUUID()], pageIdsToRemove: [randomUUID()], }, }, }); expect(response.errors).toBeUndefined(); expect(response.data?.updatePagesInList?.productList?.id).toBeTruthy(); expect(response.data?.updatePagesInList?.productList?.pages).toHaveLength( 1 ); expect( response.data?.updatePagesInList?.productList?.pages?.[0]?.id ).toEqual(page.id); }); });