From d786217d067b6cb9fb3c356497ae12252ac2c473 Mon Sep 17 00:00:00 2001 From: Alexandre Bodin Date: Fri, 9 Aug 2019 09:42:27 +0200 Subject: [PATCH 1/5] Single non required group test --- .../test/groups.test.e2e.js | 145 -------- .../groups/single-not-required.test.e2e.js | 318 ++++++++++++++++++ 2 files changed, 318 insertions(+), 145 deletions(-) delete mode 100644 packages/strapi-plugin-content-manager/test/groups.test.e2e.js create mode 100644 packages/strapi-plugin-content-manager/test/groups/single-not-required.test.e2e.js diff --git a/packages/strapi-plugin-content-manager/test/groups.test.e2e.js b/packages/strapi-plugin-content-manager/test/groups.test.e2e.js deleted file mode 100644 index bb9dd49d38..0000000000 --- a/packages/strapi-plugin-content-manager/test/groups.test.e2e.js +++ /dev/null @@ -1,145 +0,0 @@ -const { registerAndLogin } = require('../../../test/helpers/auth'); -const createModelsUtils = require('../../../test/helpers/models'); -const { createAuthRequest } = require('../../../test/helpers/request'); - -let modelsUtils; -let rq; - -describe('Test type groups', () => { - beforeAll(async () => { - const token = await registerAndLogin(); - rq = createAuthRequest(token); - - modelsUtils = createModelsUtils({ rq }); - - await modelsUtils.createGroup({ - name: 'somegroup', - attributes: { - name: { - type: 'string', - }, - }, - }); - }, 60000); - - afterAll(async () => { - await modelsUtils.deleteGroup('somegroup'); - }); - - describe('Non repeatable and Non required group', () => { - beforeAll(async () => { - await modelsUtils.createModelWithType('withgroup', 'group', { - group: 'somegroup', - repeatable: false, - required: false, - }); - }, 60000); - - afterAll(async () => { - await modelsUtils.deleteModel('withgroup'); - }, 60000); - - describe('POST new entry', () => { - test('Creating entry with JSON works', async () => { - const res = await rq.post('/content-manager/explorer/withgroup', { - body: { - field: { - name: 'someString', - }, - }, - }); - - expect(res.statusCode).toBe(200); - expect(res.body.field).toEqual( - expect.objectContaining({ - id: expect.anything(), - name: 'someString', - }) - ); - }); - - test('Creating entry with formdata works', async () => { - const res = await rq.post('/content-manager/explorer/withgroup', { - formData: { - data: JSON.stringify({ - field: { - name: 'someValue', - }, - }), - }, - }); - - expect(res.statusCode).toBe(200); - expect(res.body.field).toEqual( - expect.objectContaining({ - id: expect.anything(), - name: 'someValue', - }) - ); - }); - - test.each([[], 'someString', 128219, false])( - 'Throws if the field is not an object %p', - async value => { - const res = await rq.post('/content-manager/explorer/withgroup', { - body: { - field: value, - }, - }); - - expect(res.statusCode).toBe(400); - } - ); - - test('Can send a null value', async () => { - const res = await rq.post('/content-manager/explorer/withgroup', { - body: { - field: null, - }, - }); - - expect(res.statusCode).toBe(200); - expect(res.body.field).toBe(null); - }); - - test('Can send input without the group field', async () => { - const res = await rq.post('/content-manager/explorer/withgroup', { - body: {}, - }); - - expect(res.statusCode).toBe(200); - expect(res.body.field).toBe(null); - }); - }); - - describe('GET entries', () => { - test('Should return entries with their nested groups', async () => { - const res = await rq.get('/content-manager/explorer/withgroup'); - - expect(res.statusCode).toBe(200); - expect(Array.isArray(res.body)).toBe(true); - res.body.forEach(entry => { - if (entry.field === null) return; - - expect(entry.field).toMatchObject({ - name: expect.any(String), - }); - }); - }); - }); - - describe('PUT entry', () => { - test.todo('Keeps the previous value if group not sent'); - test.todo('Removes previous group if null sent'); - test.todo('Replaces the previous group if sent without id'); - test.todo('Throws on invalid id in sent group'); - test.todo('Updates group if previsous group id is sent'); - }); - }); - - describe('Non repeatable required group', () => {}); - describe('Repeatable non required group', () => {}); - describe('Repeatable non required group with min and max', () => {}); - describe('Repeatable required group', () => {}); - describe('Repeatable required group with min and max', () => {}); -}); diff --git a/packages/strapi-plugin-content-manager/test/groups/single-not-required.test.e2e.js b/packages/strapi-plugin-content-manager/test/groups/single-not-required.test.e2e.js new file mode 100644 index 0000000000..a86e276624 --- /dev/null +++ b/packages/strapi-plugin-content-manager/test/groups/single-not-required.test.e2e.js @@ -0,0 +1,318 @@ +const { registerAndLogin } = require('../../../../test/helpers/auth'); +const createModelsUtils = require('../../../../test/helpers/models'); +const { createAuthRequest } = require('../../../../test/helpers/request'); + +let modelsUtils; +let rq; + +describe('Non repeatable and Non required group', () => { + beforeAll(async () => { + const token = await registerAndLogin(); + rq = createAuthRequest(token); + + modelsUtils = createModelsUtils({ rq }); + + await modelsUtils.createGroup({ + name: 'somegroup', + attributes: { + name: { + type: 'string', + }, + }, + }); + + await modelsUtils.createModelWithType('withgroup', 'group', { + group: 'somegroup', + repeatable: false, + required: false, + }); + }, 60000); + + afterAll(async () => { + await modelsUtils.deleteGroup('somegroup'); + await modelsUtils.deleteModel('withgroup'); + }); + + describe('POST new entry', () => { + test('Creating entry with JSON works', async () => { + const res = await rq.post('/content-manager/explorer/withgroup', { + body: { + field: { + name: 'someString', + }, + }, + }); + + expect(res.statusCode).toBe(200); + expect(res.body.field).toEqual( + expect.objectContaining({ + id: expect.anything(), + name: 'someString', + }) + ); + }); + + test('Creating entry with formdata works', async () => { + const res = await rq.post('/content-manager/explorer/withgroup', { + formData: { + data: JSON.stringify({ + field: { + name: 'someValue', + }, + }), + }, + }); + + expect(res.statusCode).toBe(200); + expect(res.body.field).toEqual( + expect.objectContaining({ + id: expect.anything(), + name: 'someValue', + }) + ); + }); + + test.each([[], 'someString', 128219, false])( + 'Throws if the field is not an object %p', + async value => { + const res = await rq.post('/content-manager/explorer/withgroup', { + body: { + field: value, + }, + }); + + expect(res.statusCode).toBe(400); + } + ); + + test('Can send a null value', async () => { + const res = await rq.post('/content-manager/explorer/withgroup', { + body: { + field: null, + }, + }); + + expect(res.statusCode).toBe(200); + expect(res.body.field).toBe(null); + }); + + test('Can send input without the group field', async () => { + const res = await rq.post('/content-manager/explorer/withgroup', { + body: {}, + }); + + expect(res.statusCode).toBe(200); + expect(res.body.field).toBe(null); + }); + }); + + describe('GET entries', () => { + test('Should return entries with their nested groups', async () => { + const res = await rq.get('/content-manager/explorer/withgroup'); + + expect(res.statusCode).toBe(200); + expect(Array.isArray(res.body)).toBe(true); + res.body.forEach(entry => { + if (entry.field === null) return; + + expect(entry.field).toMatchObject({ + name: expect.any(String), + }); + }); + }); + }); + + describe('PUT entry', () => { + test('Keeps the previous value if group not sent', async () => { + const res = await rq.post('/content-manager/explorer/withgroup', { + body: { + field: { + name: 'someString', + }, + }, + }); + + const updateRes = await rq.put( + `/content-manager/explorer/withgroup/${res.body.id}`, + { + body: {}, + } + ); + + expect(updateRes.statusCode).toBe(200); + expect(updateRes.body).toEqual(res.body); + + const getRes = await rq.get( + `/content-manager/explorer/withgroup/${res.body.id}` + ); + + expect(getRes.statusCode).toBe(200); + expect(getRes.body).toEqual(res.body); + }); + + test('Removes previous group if null sent', async () => { + const res = await rq.post('/content-manager/explorer/withgroup', { + body: { + field: { + name: 'someString', + }, + }, + }); + + const updateRes = await rq.put( + `/content-manager/explorer/withgroup/${res.body.id}`, + { + body: { + field: null, + }, + } + ); + + const expectResult = { + id: res.body.id, + field: null, + }; + + expect(updateRes.statusCode).toBe(200); + expect(updateRes.body).toMatchObject(expectResult); + + const getRes = await rq.get( + `/content-manager/explorer/withgroup/${res.body.id}` + ); + + expect(getRes.statusCode).toBe(200); + expect(getRes.body).toMatchObject(expectResult); + }); + + test('Replaces the previous group if sent without id', async () => { + const res = await rq.post('/content-manager/explorer/withgroup', { + body: { + field: { + name: 'someString', + }, + }, + }); + + const updateRes = await rq.put( + `/content-manager/explorer/withgroup/${res.body.id}`, + { + body: { + field: { + name: 'new String', + }, + }, + } + ); + + expect(updateRes.statusCode).toBe(200); + expect(updateRes.body.field.id).not.toBe(res.body.field.id); + expect(updateRes.body).toMatchObject({ + id: res.body.id, + field: { + name: 'new String', + }, + }); + + const getRes = await rq.get( + `/content-manager/explorer/withgroup/${res.body.id}` + ); + + expect(getRes.statusCode).toBe(200); + expect(getRes.body).toMatchObject({ + id: res.body.id, + field: { + name: 'new String', + }, + }); + }); + + test('Throws on invalid id in group', async () => { + const res = await rq.post('/content-manager/explorer/withgroup', { + body: { + field: { + name: 'someString', + }, + }, + }); + + const updateRes = await rq.put( + `/content-manager/explorer/withgroup/${res.body.id}`, + { + body: { + field: { + id: 'invalid_id', + name: 'new String', + }, + }, + } + ); + + expect(updateRes.statusCode).toBe(400); + }); + + test('Updates group if previsous group id is sent', async () => { + const res = await rq.post('/content-manager/explorer/withgroup', { + body: { + field: { + name: 'someString', + }, + }, + }); + + const updateRes = await rq.put( + `/content-manager/explorer/withgroup/${res.body.id}`, + { + body: { + field: { + id: res.body.field.id, // send old id to update the previous group + name: 'new String', + }, + }, + } + ); + + const expectedResult = { + id: res.body.id, + field: { + id: res.body.field.id, + name: 'new String', + }, + }; + + expect(updateRes.statusCode).toBe(200); + expect(updateRes.body).toMatchObject(expectedResult); + + const getRes = await rq.get( + `/content-manager/explorer/withgroup/${res.body.id}` + ); + + expect(getRes.statusCode).toBe(200); + expect(getRes.body).toMatchObject(expectedResult); + }); + }); + + describe('DELETE entry', () => { + test('Returns entry with groups', async () => { + const res = await rq.post('/content-manager/explorer/withgroup', { + body: { + field: { + name: 'someString', + }, + }, + }); + + const deleteRes = await rq.delete( + `/content-manager/explorer/withgroup/${res.body.id}` + ); + + expect(deleteRes.statusCode).toBe(200); + expect(deleteRes.body).toMatchObject(res.body); + + const getRes = await rq.get( + `/content-manager/explorer/withgroup/${res.body.id}` + ); + + expect(getRes.statusCode).toBe(404); + }); + }); +}); From 8f6ca1c0acb7b38dcb8c31572a10c248134ba8c6 Mon Sep 17 00:00:00 2001 From: Alexandre Bodin Date: Fri, 9 Aug 2019 09:53:08 +0200 Subject: [PATCH 2/5] Run test on both content manager and generated apis in the same test suite --- .../groups/single-not-required.test.e2e.js | 132 ++++++++---------- 1 file changed, 56 insertions(+), 76 deletions(-) diff --git a/packages/strapi-plugin-content-manager/test/groups/single-not-required.test.e2e.js b/packages/strapi-plugin-content-manager/test/groups/single-not-required.test.e2e.js index a86e276624..54793e8bfd 100644 --- a/packages/strapi-plugin-content-manager/test/groups/single-not-required.test.e2e.js +++ b/packages/strapi-plugin-content-manager/test/groups/single-not-required.test.e2e.js @@ -5,12 +5,15 @@ const { createAuthRequest } = require('../../../../test/helpers/request'); let modelsUtils; let rq; -describe('Non repeatable and Non required group', () => { +describe.each([ + ['CONTENT MANAGER', '/content-manager/explorer/withgroup'], + ['GENERATED API', '/withgroups'], +])('[%s] => Non repeatable and Not required group for', (_, path) => { beforeAll(async () => { const token = await registerAndLogin(); - rq = createAuthRequest(token); + const authRq = createAuthRequest(token); - modelsUtils = createModelsUtils({ rq }); + modelsUtils = createModelsUtils({ rq: authRq }); await modelsUtils.createGroup({ name: 'somegroup', @@ -26,6 +29,10 @@ describe('Non repeatable and Non required group', () => { repeatable: false, required: false, }); + + rq = authRq.defaults({ + baseUrl: `http://localhost:1337${path}`, + }); }, 60000); afterAll(async () => { @@ -35,7 +42,7 @@ describe('Non repeatable and Non required group', () => { describe('POST new entry', () => { test('Creating entry with JSON works', async () => { - const res = await rq.post('/content-manager/explorer/withgroup', { + const res = await rq.post('/', { body: { field: { name: 'someString', @@ -53,7 +60,7 @@ describe('Non repeatable and Non required group', () => { }); test('Creating entry with formdata works', async () => { - const res = await rq.post('/content-manager/explorer/withgroup', { + const res = await rq.post('/', { formData: { data: JSON.stringify({ field: { @@ -75,7 +82,7 @@ describe('Non repeatable and Non required group', () => { test.each([[], 'someString', 128219, false])( 'Throws if the field is not an object %p', async value => { - const res = await rq.post('/content-manager/explorer/withgroup', { + const res = await rq.post('/', { body: { field: value, }, @@ -86,7 +93,7 @@ describe('Non repeatable and Non required group', () => { ); test('Can send a null value', async () => { - const res = await rq.post('/content-manager/explorer/withgroup', { + const res = await rq.post('/', { body: { field: null, }, @@ -97,7 +104,7 @@ describe('Non repeatable and Non required group', () => { }); test('Can send input without the group field', async () => { - const res = await rq.post('/content-manager/explorer/withgroup', { + const res = await rq.post('/', { body: {}, }); @@ -108,7 +115,7 @@ describe('Non repeatable and Non required group', () => { describe('GET entries', () => { test('Should return entries with their nested groups', async () => { - const res = await rq.get('/content-manager/explorer/withgroup'); + const res = await rq.get('/'); expect(res.statusCode).toBe(200); expect(Array.isArray(res.body)).toBe(true); @@ -124,7 +131,7 @@ describe('Non repeatable and Non required group', () => { describe('PUT entry', () => { test('Keeps the previous value if group not sent', async () => { - const res = await rq.post('/content-manager/explorer/withgroup', { + const res = await rq.post('/', { body: { field: { name: 'someString', @@ -132,26 +139,21 @@ describe('Non repeatable and Non required group', () => { }, }); - const updateRes = await rq.put( - `/content-manager/explorer/withgroup/${res.body.id}`, - { - body: {}, - } - ); + const updateRes = await rq.put(`/${res.body.id}`, { + body: {}, + }); expect(updateRes.statusCode).toBe(200); expect(updateRes.body).toEqual(res.body); - const getRes = await rq.get( - `/content-manager/explorer/withgroup/${res.body.id}` - ); + const getRes = await rq.get(`/${res.body.id}`); expect(getRes.statusCode).toBe(200); expect(getRes.body).toEqual(res.body); }); test('Removes previous group if null sent', async () => { - const res = await rq.post('/content-manager/explorer/withgroup', { + const res = await rq.post('/', { body: { field: { name: 'someString', @@ -159,14 +161,11 @@ describe('Non repeatable and Non required group', () => { }, }); - const updateRes = await rq.put( - `/content-manager/explorer/withgroup/${res.body.id}`, - { - body: { - field: null, - }, - } - ); + const updateRes = await rq.put(`/${res.body.id}`, { + body: { + field: null, + }, + }); const expectResult = { id: res.body.id, @@ -176,16 +175,14 @@ describe('Non repeatable and Non required group', () => { expect(updateRes.statusCode).toBe(200); expect(updateRes.body).toMatchObject(expectResult); - const getRes = await rq.get( - `/content-manager/explorer/withgroup/${res.body.id}` - ); + const getRes = await rq.get(`/${res.body.id}`); expect(getRes.statusCode).toBe(200); expect(getRes.body).toMatchObject(expectResult); }); test('Replaces the previous group if sent without id', async () => { - const res = await rq.post('/content-manager/explorer/withgroup', { + const res = await rq.post('/', { body: { field: { name: 'someString', @@ -193,16 +190,13 @@ describe('Non repeatable and Non required group', () => { }, }); - const updateRes = await rq.put( - `/content-manager/explorer/withgroup/${res.body.id}`, - { - body: { - field: { - name: 'new String', - }, + const updateRes = await rq.put(`/${res.body.id}`, { + body: { + field: { + name: 'new String', }, - } - ); + }, + }); expect(updateRes.statusCode).toBe(200); expect(updateRes.body.field.id).not.toBe(res.body.field.id); @@ -213,9 +207,7 @@ describe('Non repeatable and Non required group', () => { }, }); - const getRes = await rq.get( - `/content-manager/explorer/withgroup/${res.body.id}` - ); + const getRes = await rq.get(`/${res.body.id}`); expect(getRes.statusCode).toBe(200); expect(getRes.body).toMatchObject({ @@ -227,7 +219,7 @@ describe('Non repeatable and Non required group', () => { }); test('Throws on invalid id in group', async () => { - const res = await rq.post('/content-manager/explorer/withgroup', { + const res = await rq.post('/', { body: { field: { name: 'someString', @@ -235,23 +227,20 @@ describe('Non repeatable and Non required group', () => { }, }); - const updateRes = await rq.put( - `/content-manager/explorer/withgroup/${res.body.id}`, - { - body: { - field: { - id: 'invalid_id', - name: 'new String', - }, + const updateRes = await rq.put(`/${res.body.id}`, { + body: { + field: { + id: 'invalid_id', + name: 'new String', }, - } - ); + }, + }); expect(updateRes.statusCode).toBe(400); }); test('Updates group if previsous group id is sent', async () => { - const res = await rq.post('/content-manager/explorer/withgroup', { + const res = await rq.post('/', { body: { field: { name: 'someString', @@ -259,17 +248,14 @@ describe('Non repeatable and Non required group', () => { }, }); - const updateRes = await rq.put( - `/content-manager/explorer/withgroup/${res.body.id}`, - { - body: { - field: { - id: res.body.field.id, // send old id to update the previous group - name: 'new String', - }, + const updateRes = await rq.put(`/${res.body.id}`, { + body: { + field: { + id: res.body.field.id, // send old id to update the previous group + name: 'new String', }, - } - ); + }, + }); const expectedResult = { id: res.body.id, @@ -282,9 +268,7 @@ describe('Non repeatable and Non required group', () => { expect(updateRes.statusCode).toBe(200); expect(updateRes.body).toMatchObject(expectedResult); - const getRes = await rq.get( - `/content-manager/explorer/withgroup/${res.body.id}` - ); + const getRes = await rq.get(`/${res.body.id}`); expect(getRes.statusCode).toBe(200); expect(getRes.body).toMatchObject(expectedResult); @@ -293,7 +277,7 @@ describe('Non repeatable and Non required group', () => { describe('DELETE entry', () => { test('Returns entry with groups', async () => { - const res = await rq.post('/content-manager/explorer/withgroup', { + const res = await rq.post('/', { body: { field: { name: 'someString', @@ -301,16 +285,12 @@ describe('Non repeatable and Non required group', () => { }, }); - const deleteRes = await rq.delete( - `/content-manager/explorer/withgroup/${res.body.id}` - ); + const deleteRes = await rq.delete(`/${res.body.id}`); expect(deleteRes.statusCode).toBe(200); expect(deleteRes.body).toMatchObject(res.body); - const getRes = await rq.get( - `/content-manager/explorer/withgroup/${res.body.id}` - ); + const getRes = await rq.get(`/${res.body.id}`); expect(getRes.statusCode).toBe(404); }); From 87ddcaf6c42ba1bcabebf6d92016ada5d7ba9ebc Mon Sep 17 00:00:00 2001 From: Alexandre Bodin Date: Fri, 9 Aug 2019 10:44:24 +0200 Subject: [PATCH 3/5] Add all group tests --- .../groups/repeatable-min-max.test.e2e.js | 558 ++++++++++++++++++ .../repeatable-not-required.test.e2e.js | 490 +++++++++++++++ .../groups/repeatable-required.test.e2e.js | 489 +++++++++++++++ .../groups/single-not-required.test.e2e.js | 29 +- .../test/groups/single-required.test.e2e.js | 317 ++++++++++ 5 files changed, 1882 insertions(+), 1 deletion(-) create mode 100644 packages/strapi-plugin-content-manager/test/groups/repeatable-min-max.test.e2e.js create mode 100644 packages/strapi-plugin-content-manager/test/groups/repeatable-not-required.test.e2e.js create mode 100644 packages/strapi-plugin-content-manager/test/groups/repeatable-required.test.e2e.js create mode 100644 packages/strapi-plugin-content-manager/test/groups/single-required.test.e2e.js diff --git a/packages/strapi-plugin-content-manager/test/groups/repeatable-min-max.test.e2e.js b/packages/strapi-plugin-content-manager/test/groups/repeatable-min-max.test.e2e.js new file mode 100644 index 0000000000..3342911d4a --- /dev/null +++ b/packages/strapi-plugin-content-manager/test/groups/repeatable-min-max.test.e2e.js @@ -0,0 +1,558 @@ +const { registerAndLogin } = require('../../../../test/helpers/auth'); +const createModelsUtils = require('../../../../test/helpers/models'); +const { createAuthRequest } = require('../../../../test/helpers/request'); + +let modelsUtils; +let rq; + +describe.each([ + ['CONTENT MANAGER', '/content-manager/explorer/withgroup'], + ['GENERATED API', '/withgroups'], +])('[%s] => Non repeatable and Not required group', (_, path) => { + beforeAll(async () => { + const token = await registerAndLogin(); + const authRq = createAuthRequest(token); + + modelsUtils = createModelsUtils({ rq: authRq }); + + await modelsUtils.createGroup({ + name: 'somegroup', + attributes: { + name: { + type: 'string', + }, + }, + }); + + await modelsUtils.createModelWithType('withgroup', 'group', { + group: 'somegroup', + repeatable: true, + required: false, + min: 1, + max: 5, + }); + + rq = authRq.defaults({ + baseUrl: `http://localhost:1337${path}`, + }); + }, 60000); + + afterAll(async () => { + await modelsUtils.deleteGroup('somegroup'); + await modelsUtils.deleteModel('withgroup'); + }); + + describe('POST new entry', () => { + test('Creating entry with JSON works', async () => { + const res = await rq.post('/', { + body: { + field: [ + { + name: 'someString', + }, + ], + }, + }); + + expect(res.statusCode).toBe(200); + expect(Array.isArray(res.body.field)).toBe(true); + expect(res.body.field).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + id: expect.anything(), + name: 'someString', + }), + ]) + ); + }); + + test('Creating entry with formdata works', async () => { + const res = await rq.post('/', { + formData: { + data: JSON.stringify({ + field: [ + { + name: 'someValue', + }, + ], + }), + }, + }); + + expect(res.statusCode).toBe(200); + expect(Array.isArray(res.body.field)).toBe(true); + expect(res.body.field).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + id: expect.anything(), + name: 'someValue', + }), + ]) + ); + }); + + test.each(['someString', 128219, false, {}, null])( + 'Throws if the field is not an object %p', + async value => { + const res = await rq.post('/', { + body: { + field: value, + }, + }); + + expect(res.statusCode).toBe(400); + } + ); + + test('Throws when sending an empty array or an array with less then the min', async () => { + const res = await rq.post('/', { + body: { + field: [], + }, + }); + + expect(res.statusCode).toBe(400); + }); + + test('Throws when sending too many items', async () => { + const res = await rq.post('/', { + body: { + field: [ + { + name: 'one', + }, + { + name: 'one', + }, + { + name: 'one', + }, + { + name: 'one', + }, + { + name: 'one', + }, + { + name: 'one', + }, + ], + }, + }); + + expect(res.statusCode).toBe(400); + }); + + test('Can send input without the group field', async () => { + const res = await rq.post('/', { + body: {}, + }); + + expect(res.statusCode).toBe(200); + expect(res.body.field).toEqual([]); + }); + }); + + describe('GET entries', () => { + test('Data is orderd in the order sent', async () => { + const res = await rq.post('/', { + body: { + field: [ + { + name: 'firstString', + }, + { + name: 'someString', + }, + ], + }, + }); + + const getRes = await rq.get(`/${res.body.id}`); + expect(getRes.statusCode).toBe(200); + expect(Array.isArray(getRes.body.field)).toBe(true); + + expect(getRes.body.field[0]).toMatchObject({ + name: 'firstString', + }); + expect(getRes.body.field[1]).toMatchObject({ + name: 'someString', + }); + }); + + test('Should return entries with their nested groups', async () => { + const res = await rq.get('/'); + + expect(res.statusCode).toBe(200); + expect(Array.isArray(res.body)).toBe(true); + res.body.forEach(entry => { + expect(Array.isArray(entry.field)).toBe(true); + + if (entry.field.length === 0) return; + + expect(entry.field).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + name: expect.any(String), + }), + ]) + ); + }); + }); + }); + + describe('PUT entry', () => { + test.each(['someString', 128219, false, {}, null])( + 'Throws when sending invalid updated field %p', + async value => { + const res = await rq.post('/', { + body: { + field: [ + { + name: 'someString', + }, + ], + }, + }); + + const updateRes = await rq.put(`/${res.body.id}`, { + body: { + field: value, + }, + }); + + expect(updateRes.statusCode).toBe(400); + + // shouldn't have been updated + const getRes = await rq.get(`/${res.body.id}`); + + expect(getRes.statusCode).toBe(200); + expect(getRes.body).toEqual(res.body); + } + ); + + test('Updates order at each request', async () => { + const res = await rq.post('/', { + body: { + field: [ + { + name: 'someString', + }, + { + name: 'otherString', + }, + ], + }, + }); + + expect(res.body.field[0]).toMatchObject({ + name: 'someString', + }); + expect(res.body.field[1]).toMatchObject({ + name: 'otherString', + }); + + const updateRes = await rq.put(`/${res.body.id}`, { + body: { + field: [ + { + name: 'otherString', + }, + { + name: 'someString', + }, + ], + }, + }); + + expect(updateRes.statusCode).toBe(200); + expect(Array.isArray(updateRes.body.field)).toBe(true); + + expect(updateRes.body.field[0]).toMatchObject({ + name: 'otherString', + }); + expect(updateRes.body.field[1]).toMatchObject({ + name: 'someString', + }); + + const getRes = await rq.get(`/${res.body.id}`); + + expect(getRes.statusCode).toBe(200); + expect(Array.isArray(getRes.body.field)).toBe(true); + + expect(getRes.body.field[0]).toMatchObject({ + name: 'otherString', + }); + expect(getRes.body.field[1]).toMatchObject({ + name: 'someString', + }); + }); + + test('Keeps the previous value if group not sent', async () => { + const res = await rq.post('/', { + body: { + field: [ + { + name: 'someString', + }, + { + name: 'otherString', + }, + ], + }, + }); + + const updateRes = await rq.put(`/${res.body.id}`, { + body: {}, + }); + + expect(updateRes.statusCode).toBe(200); + expect(updateRes.body).toEqual(res.body); + + const getRes = await rq.get(`/${res.body.id}`); + + expect(getRes.statusCode).toBe(200); + expect(getRes.body).toEqual(res.body); + }); + + test('Throws when not enough items', async () => { + const res = await rq.post('/', { + body: { + field: [ + { + name: 'someString', + }, + ], + }, + }); + + const updateRes = await rq.put(`/${res.body.id}`, { + body: { + field: [], + }, + }); + + expect(updateRes.statusCode).toBe(400); + + const getRes = await rq.get(`/${res.body.id}`); + + expect(getRes.statusCode).toBe(200); + expect(getRes.body).toMatchObject(res.body); + }); + + test('Throws when too many items', async () => { + const res = await rq.post('/', { + body: { + field: [ + { + name: 'someString', + }, + ], + }, + }); + + const updateRes = await rq.put(`/${res.body.id}`, { + body: { + field: [ + { + name: 'someString', + }, + { + name: 'someString', + }, + { + name: 'someString', + }, + { + name: 'someString', + }, + { + name: 'someString', + }, + { + name: 'someString', + }, + ], + }, + }); + + expect(updateRes.statusCode).toBe(400); + + const getRes = await rq.get(`/${res.body.id}`); + + expect(getRes.statusCode).toBe(200); + expect(getRes.body).toMatchObject(res.body); + }); + + test('Replaces the previous groups if sent without id', async () => { + const res = await rq.post('/', { + body: { + field: [ + { + name: 'someString', + }, + ], + }, + }); + + const updateRes = await rq.put(`/${res.body.id}`, { + body: { + field: [ + { + name: 'new String', + }, + ], + }, + }); + + expect(updateRes.statusCode).toBe(200); + + const oldIds = res.body.field.map(val => val.id); + updateRes.body.field.forEach(val => { + expect(oldIds.includes(val.id)).toBe(false); + }); + + expect(updateRes.body).toMatchObject({ + id: res.body.id, + field: [ + { + name: 'new String', + }, + ], + }); + + const getRes = await rq.get(`/${res.body.id}`); + + expect(getRes.statusCode).toBe(200); + expect(getRes.body).toMatchObject({ + id: res.body.id, + field: [ + { + name: 'new String', + }, + ], + }); + }); + + test('Throws on invalid id in group', async () => { + const res = await rq.post('/', { + body: { + field: [ + { + name: 'someString', + }, + ], + }, + }); + + const updateRes = await rq.put(`/${res.body.id}`, { + body: { + field: [ + { + id: 'invalid_id', + name: 'new String', + }, + ], + }, + }); + + expect(updateRes.statusCode).toBe(400); + }); + + test('Updates group with ids, create new ones and removes old ones', async () => { + const res = await rq.post('/', { + body: { + field: [ + { + name: 'one', + }, + { + name: 'two', + }, + { + name: 'three', + }, + ], + }, + }); + + const updateRes = await rq.put(`/${res.body.id}`, { + body: { + field: [ + { + id: res.body.field[0].id, // send old id to update the previous group + name: 'newOne', + }, + { + name: 'newTwo', + }, + { + id: res.body.field[2].id, + name: 'three', + }, + { + name: 'four', + }, + ], + }, + }); + + const expectedResult = { + id: res.body.id, + field: [ + { + id: res.body.field[0].id, + name: 'newOne', + }, + { + name: 'newTwo', + }, + { + id: res.body.field[2].id, + name: 'three', + }, + { + name: 'four', + }, + ], + }; + + expect(updateRes.statusCode).toBe(200); + expect(updateRes.body).toMatchObject(expectedResult); + + const getRes = await rq.get(`/${res.body.id}`); + + expect(getRes.statusCode).toBe(200); + expect(getRes.body).toMatchObject(expectedResult); + }); + }); + + describe('DELETE entry', () => { + test('Returns entry with groups', async () => { + const res = await rq.post('/', { + body: { + field: [ + { + name: 'someString', + }, + { + name: 'someOtherString', + }, + { + name: 'otherSomeString', + }, + ], + }, + }); + + const deleteRes = await rq.delete(`/${res.body.id}`); + + expect(deleteRes.statusCode).toBe(200); + expect(deleteRes.body).toMatchObject(res.body); + + const getRes = await rq.get(`/${res.body.id}`); + + expect(getRes.statusCode).toBe(404); + }); + }); +}); diff --git a/packages/strapi-plugin-content-manager/test/groups/repeatable-not-required.test.e2e.js b/packages/strapi-plugin-content-manager/test/groups/repeatable-not-required.test.e2e.js new file mode 100644 index 0000000000..a7af863872 --- /dev/null +++ b/packages/strapi-plugin-content-manager/test/groups/repeatable-not-required.test.e2e.js @@ -0,0 +1,490 @@ +const { registerAndLogin } = require('../../../../test/helpers/auth'); +const createModelsUtils = require('../../../../test/helpers/models'); +const { createAuthRequest } = require('../../../../test/helpers/request'); + +let modelsUtils; +let rq; + +describe.each([ + ['CONTENT MANAGER', '/content-manager/explorer/withgroup'], + ['GENERATED API', '/withgroups'], +])('[%s] => Non repeatable and Not required group', (_, path) => { + beforeAll(async () => { + const token = await registerAndLogin(); + const authRq = createAuthRequest(token); + + modelsUtils = createModelsUtils({ rq: authRq }); + + await modelsUtils.createGroup({ + name: 'somegroup', + attributes: { + name: { + type: 'string', + }, + }, + }); + + await modelsUtils.createModelWithType('withgroup', 'group', { + group: 'somegroup', + repeatable: true, + required: false, + }); + + rq = authRq.defaults({ + baseUrl: `http://localhost:1337${path}`, + }); + }, 60000); + + afterAll(async () => { + await modelsUtils.deleteGroup('somegroup'); + await modelsUtils.deleteModel('withgroup'); + }); + + describe('POST new entry', () => { + test('Creating entry with JSON works', async () => { + const res = await rq.post('/', { + body: { + field: [ + { + name: 'someString', + }, + ], + }, + }); + + expect(res.statusCode).toBe(200); + expect(Array.isArray(res.body.field)).toBe(true); + expect(res.body.field).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + id: expect.anything(), + name: 'someString', + }), + ]) + ); + }); + + test('Creating entry with formdata works', async () => { + const res = await rq.post('/', { + formData: { + data: JSON.stringify({ + field: [ + { + name: 'someValue', + }, + ], + }), + }, + }); + + expect(res.statusCode).toBe(200); + expect(Array.isArray(res.body.field)).toBe(true); + expect(res.body.field).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + id: expect.anything(), + name: 'someValue', + }), + ]) + ); + }); + + test.each(['someString', 128219, false, {}, null])( + 'Throws if the field is not an object %p', + async value => { + const res = await rq.post('/', { + body: { + field: value, + }, + }); + + expect(res.statusCode).toBe(400); + } + ); + + test('Can send an empty array', async () => { + const res = await rq.post('/', { + body: { + field: [], + }, + }); + + expect(res.statusCode).toBe(200); + expect(res.body.field).toEqual([]); + }); + + test('Can send input without the group field', async () => { + const res = await rq.post('/', { + body: {}, + }); + + expect(res.statusCode).toBe(200); + expect(res.body.field).toEqual([]); + }); + }); + + describe('GET entries', () => { + test('Data is orderd in the order sent', async () => { + const res = await rq.post('/', { + body: { + field: [ + { + name: 'firstString', + }, + { + name: 'someString', + }, + ], + }, + }); + + const getRes = await rq.get(`/${res.body.id}`); + expect(getRes.statusCode).toBe(200); + expect(Array.isArray(getRes.body.field)).toBe(true); + + expect(getRes.body.field[0]).toMatchObject({ + name: 'firstString', + }); + expect(getRes.body.field[1]).toMatchObject({ + name: 'someString', + }); + }); + + test('Should return entries with their nested groups', async () => { + const res = await rq.get('/'); + + expect(res.statusCode).toBe(200); + expect(Array.isArray(res.body)).toBe(true); + res.body.forEach(entry => { + expect(Array.isArray(entry.field)).toBe(true); + + if (entry.field.length === 0) return; + + expect(entry.field).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + name: expect.any(String), + }), + ]) + ); + }); + }); + }); + + describe('PUT entry', () => { + test.each(['someString', 128219, false, {}, null])( + 'Throws when sending invalid updated field %p', + async value => { + const res = await rq.post('/', { + body: { + field: [ + { + name: 'someString', + }, + ], + }, + }); + + const updateRes = await rq.put(`/${res.body.id}`, { + body: { + field: value, + }, + }); + + expect(updateRes.statusCode).toBe(400); + + // shouldn't have been updated + const getRes = await rq.get(`/${res.body.id}`); + + expect(getRes.statusCode).toBe(200); + expect(getRes.body).toEqual(res.body); + } + ); + + test('Updates order at each request', async () => { + const res = await rq.post('/', { + body: { + field: [ + { + name: 'someString', + }, + { + name: 'otherString', + }, + ], + }, + }); + + expect(res.body.field[0]).toMatchObject({ + name: 'someString', + }); + expect(res.body.field[1]).toMatchObject({ + name: 'otherString', + }); + + const updateRes = await rq.put(`/${res.body.id}`, { + body: { + field: [ + { + name: 'otherString', + }, + { + name: 'someString', + }, + ], + }, + }); + + expect(updateRes.statusCode).toBe(200); + expect(Array.isArray(updateRes.body.field)).toBe(true); + + expect(updateRes.body.field[0]).toMatchObject({ + name: 'otherString', + }); + expect(updateRes.body.field[1]).toMatchObject({ + name: 'someString', + }); + + const getRes = await rq.get(`/${res.body.id}`); + + expect(getRes.statusCode).toBe(200); + expect(Array.isArray(getRes.body.field)).toBe(true); + + expect(getRes.body.field[0]).toMatchObject({ + name: 'otherString', + }); + expect(getRes.body.field[1]).toMatchObject({ + name: 'someString', + }); + }); + + test('Keeps the previous value if group not sent', async () => { + const res = await rq.post('/', { + body: { + field: [ + { + name: 'someString', + }, + { + name: 'otherString', + }, + ], + }, + }); + + const updateRes = await rq.put(`/${res.body.id}`, { + body: {}, + }); + + expect(updateRes.statusCode).toBe(200); + expect(updateRes.body).toEqual(res.body); + + const getRes = await rq.get(`/${res.body.id}`); + + expect(getRes.statusCode).toBe(200); + expect(getRes.body).toEqual(res.body); + }); + + test('Removes previous groups if empty array sent', async () => { + const res = await rq.post('/', { + body: { + field: [ + { + name: 'someString', + }, + ], + }, + }); + + const updateRes = await rq.put(`/${res.body.id}`, { + body: { + field: [], + }, + }); + + const expectResult = { + id: res.body.id, + field: [], + }; + + expect(updateRes.statusCode).toBe(200); + expect(updateRes.body).toMatchObject(expectResult); + + const getRes = await rq.get(`/${res.body.id}`); + + expect(getRes.statusCode).toBe(200); + expect(getRes.body).toMatchObject(expectResult); + }); + + test('Replaces the previous groups if sent without id', async () => { + const res = await rq.post('/', { + body: { + field: [ + { + name: 'someString', + }, + ], + }, + }); + + const updateRes = await rq.put(`/${res.body.id}`, { + body: { + field: [ + { + name: 'new String', + }, + ], + }, + }); + + expect(updateRes.statusCode).toBe(200); + + const oldIds = res.body.field.map(val => val.id); + updateRes.body.field.forEach(val => { + expect(oldIds.includes(val.id)).toBe(false); + }); + + expect(updateRes.body).toMatchObject({ + id: res.body.id, + field: [ + { + name: 'new String', + }, + ], + }); + + const getRes = await rq.get(`/${res.body.id}`); + + expect(getRes.statusCode).toBe(200); + expect(getRes.body).toMatchObject({ + id: res.body.id, + field: [ + { + name: 'new String', + }, + ], + }); + }); + + test('Throws on invalid id in group', async () => { + const res = await rq.post('/', { + body: { + field: [ + { + name: 'someString', + }, + ], + }, + }); + + const updateRes = await rq.put(`/${res.body.id}`, { + body: { + field: [ + { + id: 'invalid_id', + name: 'new String', + }, + ], + }, + }); + + expect(updateRes.statusCode).toBe(400); + }); + + test('Updates group with ids, create new ones and removes old ones', async () => { + const res = await rq.post('/', { + body: { + field: [ + { + name: 'one', + }, + { + name: 'two', + }, + { + name: 'three', + }, + ], + }, + }); + + const updateRes = await rq.put(`/${res.body.id}`, { + body: { + field: [ + { + id: res.body.field[0].id, // send old id to update the previous group + name: 'newOne', + }, + { + name: 'newTwo', + }, + { + id: res.body.field[2].id, + name: 'three', + }, + { + name: 'four', + }, + ], + }, + }); + + const expectedResult = { + id: res.body.id, + field: [ + { + id: res.body.field[0].id, + name: 'newOne', + }, + { + name: 'newTwo', + }, + { + id: res.body.field[2].id, + name: 'three', + }, + { + name: 'four', + }, + ], + }; + + expect(updateRes.statusCode).toBe(200); + expect(updateRes.body).toMatchObject(expectedResult); + + const getRes = await rq.get(`/${res.body.id}`); + + expect(getRes.statusCode).toBe(200); + expect(getRes.body).toMatchObject(expectedResult); + }); + }); + + describe('DELETE entry', () => { + test('Returns entry with groups', async () => { + const res = await rq.post('/', { + body: { + field: [ + { + name: 'someString', + }, + { + name: 'someOtherString', + }, + { + name: 'otherSomeString', + }, + ], + }, + }); + + const deleteRes = await rq.delete(`/${res.body.id}`); + + expect(deleteRes.statusCode).toBe(200); + expect(deleteRes.body).toMatchObject(res.body); + + const getRes = await rq.get(`/${res.body.id}`); + + expect(getRes.statusCode).toBe(404); + }); + }); +}); diff --git a/packages/strapi-plugin-content-manager/test/groups/repeatable-required.test.e2e.js b/packages/strapi-plugin-content-manager/test/groups/repeatable-required.test.e2e.js new file mode 100644 index 0000000000..e18cc1671b --- /dev/null +++ b/packages/strapi-plugin-content-manager/test/groups/repeatable-required.test.e2e.js @@ -0,0 +1,489 @@ +const { registerAndLogin } = require('../../../../test/helpers/auth'); +const createModelsUtils = require('../../../../test/helpers/models'); +const { createAuthRequest } = require('../../../../test/helpers/request'); + +let modelsUtils; +let rq; + +describe.each([ + ['CONTENT MANAGER', '/content-manager/explorer/withgroup'], + ['GENERATED API', '/withgroups'], +])('[%s] => Non repeatable and Not required group', (_, path) => { + beforeAll(async () => { + const token = await registerAndLogin(); + const authRq = createAuthRequest(token); + + modelsUtils = createModelsUtils({ rq: authRq }); + + await modelsUtils.createGroup({ + name: 'somegroup', + attributes: { + name: { + type: 'string', + }, + }, + }); + + await modelsUtils.createModelWithType('withgroup', 'group', { + group: 'somegroup', + repeatable: true, + required: true, + }); + + rq = authRq.defaults({ + baseUrl: `http://localhost:1337${path}`, + }); + }, 60000); + + afterAll(async () => { + await modelsUtils.deleteGroup('somegroup'); + await modelsUtils.deleteModel('withgroup'); + }); + + describe('POST new entry', () => { + test('Creating entry with JSON works', async () => { + const res = await rq.post('/', { + body: { + field: [ + { + name: 'someString', + }, + ], + }, + }); + + expect(res.statusCode).toBe(200); + expect(Array.isArray(res.body.field)).toBe(true); + expect(res.body.field).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + id: expect.anything(), + name: 'someString', + }), + ]) + ); + }); + + test('Creating entry with formdata works', async () => { + const res = await rq.post('/', { + formData: { + data: JSON.stringify({ + field: [ + { + name: 'someValue', + }, + ], + }), + }, + }); + + expect(res.statusCode).toBe(200); + expect(Array.isArray(res.body.field)).toBe(true); + expect(res.body.field).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + id: expect.anything(), + name: 'someValue', + }), + ]) + ); + }); + + test.each(['someString', 128219, false, {}, null])( + 'Throws if the field is not an object %p', + async value => { + const res = await rq.post('/', { + body: { + field: value, + }, + }); + + expect(res.statusCode).toBe(400); + } + ); + + test('Can send an empty array', async () => { + const res = await rq.post('/', { + body: { + field: [], + }, + }); + + expect(res.statusCode).toBe(200); + expect(res.body.field).toEqual([]); + }); + + test('Throws when group is not provided', async () => { + const res = await rq.post('/', { + body: {}, + }); + + expect(res.statusCode).toBe(400); + }); + }); + + describe('GET entries', () => { + test('Data is orderd in the order sent', async () => { + const res = await rq.post('/', { + body: { + field: [ + { + name: 'firstString', + }, + { + name: 'someString', + }, + ], + }, + }); + + const getRes = await rq.get(`/${res.body.id}`); + expect(getRes.statusCode).toBe(200); + expect(Array.isArray(getRes.body.field)).toBe(true); + + expect(getRes.body.field[0]).toMatchObject({ + name: 'firstString', + }); + expect(getRes.body.field[1]).toMatchObject({ + name: 'someString', + }); + }); + + test('Should return entries with their nested groups', async () => { + const res = await rq.get('/'); + + expect(res.statusCode).toBe(200); + expect(Array.isArray(res.body)).toBe(true); + res.body.forEach(entry => { + expect(Array.isArray(entry.field)).toBe(true); + + if (entry.field.length === 0) return; + + expect(entry.field).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + name: expect.any(String), + }), + ]) + ); + }); + }); + }); + + describe('PUT entry', () => { + test.each(['someString', 128219, false, {}, null])( + 'Throws when sending invalid updated field %p', + async value => { + const res = await rq.post('/', { + body: { + field: [ + { + name: 'someString', + }, + ], + }, + }); + + const updateRes = await rq.put(`/${res.body.id}`, { + body: { + field: value, + }, + }); + + expect(updateRes.statusCode).toBe(400); + + // shouldn't have been updated + const getRes = await rq.get(`/${res.body.id}`); + + expect(getRes.statusCode).toBe(200); + expect(getRes.body).toEqual(res.body); + } + ); + + test('Updates order at each request', async () => { + const res = await rq.post('/', { + body: { + field: [ + { + name: 'someString', + }, + { + name: 'otherString', + }, + ], + }, + }); + + expect(res.body.field[0]).toMatchObject({ + name: 'someString', + }); + expect(res.body.field[1]).toMatchObject({ + name: 'otherString', + }); + + const updateRes = await rq.put(`/${res.body.id}`, { + body: { + field: [ + { + name: 'otherString', + }, + { + name: 'someString', + }, + ], + }, + }); + + expect(updateRes.statusCode).toBe(200); + expect(Array.isArray(updateRes.body.field)).toBe(true); + + expect(updateRes.body.field[0]).toMatchObject({ + name: 'otherString', + }); + expect(updateRes.body.field[1]).toMatchObject({ + name: 'someString', + }); + + const getRes = await rq.get(`/${res.body.id}`); + + expect(getRes.statusCode).toBe(200); + expect(Array.isArray(getRes.body.field)).toBe(true); + + expect(getRes.body.field[0]).toMatchObject({ + name: 'otherString', + }); + expect(getRes.body.field[1]).toMatchObject({ + name: 'someString', + }); + }); + + test('Keeps the previous value if group not sent', async () => { + const res = await rq.post('/', { + body: { + field: [ + { + name: 'someString', + }, + { + name: 'otherString', + }, + ], + }, + }); + + const updateRes = await rq.put(`/${res.body.id}`, { + body: {}, + }); + + expect(updateRes.statusCode).toBe(200); + expect(updateRes.body).toEqual(res.body); + + const getRes = await rq.get(`/${res.body.id}`); + + expect(getRes.statusCode).toBe(200); + expect(getRes.body).toEqual(res.body); + }); + + test('Removes previous groups if empty array sent', async () => { + const res = await rq.post('/', { + body: { + field: [ + { + name: 'someString', + }, + ], + }, + }); + + const updateRes = await rq.put(`/${res.body.id}`, { + body: { + field: [], + }, + }); + + const expectResult = { + id: res.body.id, + field: [], + }; + + expect(updateRes.statusCode).toBe(200); + expect(updateRes.body).toMatchObject(expectResult); + + const getRes = await rq.get(`/${res.body.id}`); + + expect(getRes.statusCode).toBe(200); + expect(getRes.body).toMatchObject(expectResult); + }); + + test('Replaces the previous groups if sent without id', async () => { + const res = await rq.post('/', { + body: { + field: [ + { + name: 'someString', + }, + ], + }, + }); + + const updateRes = await rq.put(`/${res.body.id}`, { + body: { + field: [ + { + name: 'new String', + }, + ], + }, + }); + + expect(updateRes.statusCode).toBe(200); + + const oldIds = res.body.field.map(val => val.id); + updateRes.body.field.forEach(val => { + expect(oldIds.includes(val.id)).toBe(false); + }); + + expect(updateRes.body).toMatchObject({ + id: res.body.id, + field: [ + { + name: 'new String', + }, + ], + }); + + const getRes = await rq.get(`/${res.body.id}`); + + expect(getRes.statusCode).toBe(200); + expect(getRes.body).toMatchObject({ + id: res.body.id, + field: [ + { + name: 'new String', + }, + ], + }); + }); + + test('Throws on invalid id in group', async () => { + const res = await rq.post('/', { + body: { + field: [ + { + name: 'someString', + }, + ], + }, + }); + + const updateRes = await rq.put(`/${res.body.id}`, { + body: { + field: [ + { + id: 'invalid_id', + name: 'new String', + }, + ], + }, + }); + + expect(updateRes.statusCode).toBe(400); + }); + + test('Updates group with ids, create new ones and removes old ones', async () => { + const res = await rq.post('/', { + body: { + field: [ + { + name: 'one', + }, + { + name: 'two', + }, + { + name: 'three', + }, + ], + }, + }); + + const updateRes = await rq.put(`/${res.body.id}`, { + body: { + field: [ + { + id: res.body.field[0].id, // send old id to update the previous group + name: 'newOne', + }, + { + name: 'newTwo', + }, + { + id: res.body.field[2].id, + name: 'three', + }, + { + name: 'four', + }, + ], + }, + }); + + const expectedResult = { + id: res.body.id, + field: [ + { + id: res.body.field[0].id, + name: 'newOne', + }, + { + name: 'newTwo', + }, + { + id: res.body.field[2].id, + name: 'three', + }, + { + name: 'four', + }, + ], + }; + + expect(updateRes.statusCode).toBe(200); + expect(updateRes.body).toMatchObject(expectedResult); + + const getRes = await rq.get(`/${res.body.id}`); + + expect(getRes.statusCode).toBe(200); + expect(getRes.body).toMatchObject(expectedResult); + }); + }); + + describe('DELETE entry', () => { + test('Returns entry with groups', async () => { + const res = await rq.post('/', { + body: { + field: [ + { + name: 'someString', + }, + { + name: 'someOtherString', + }, + { + name: 'otherSomeString', + }, + ], + }, + }); + + const deleteRes = await rq.delete(`/${res.body.id}`); + + expect(deleteRes.statusCode).toBe(200); + expect(deleteRes.body).toMatchObject(res.body); + + const getRes = await rq.get(`/${res.body.id}`); + + expect(getRes.statusCode).toBe(404); + }); + }); +}); diff --git a/packages/strapi-plugin-content-manager/test/groups/single-not-required.test.e2e.js b/packages/strapi-plugin-content-manager/test/groups/single-not-required.test.e2e.js index 54793e8bfd..73e30885a3 100644 --- a/packages/strapi-plugin-content-manager/test/groups/single-not-required.test.e2e.js +++ b/packages/strapi-plugin-content-manager/test/groups/single-not-required.test.e2e.js @@ -8,7 +8,7 @@ let rq; describe.each([ ['CONTENT MANAGER', '/content-manager/explorer/withgroup'], ['GENERATED API', '/withgroups'], -])('[%s] => Non repeatable and Not required group for', (_, path) => { +])('[%s] => Non repeatable and Not required group', (_, path) => { beforeAll(async () => { const token = await registerAndLogin(); const authRq = createAuthRequest(token); @@ -130,6 +130,33 @@ describe.each([ }); describe('PUT entry', () => { + test.each([[], 'someString', 128219, false])( + 'Throws when sending invalid updated field %p', + async value => { + const res = await rq.post('/', { + body: { + field: { + name: 'someString', + }, + }, + }); + + const updateRes = await rq.put(`/${res.body.id}`, { + body: { + field: value, + }, + }); + + expect(updateRes.statusCode).toBe(400); + + // shouldn't have been updated + const getRes = await rq.get(`/${res.body.id}`); + + expect(getRes.statusCode).toBe(200); + expect(getRes.body).toEqual(res.body); + } + ); + test('Keeps the previous value if group not sent', async () => { const res = await rq.post('/', { body: { diff --git a/packages/strapi-plugin-content-manager/test/groups/single-required.test.e2e.js b/packages/strapi-plugin-content-manager/test/groups/single-required.test.e2e.js new file mode 100644 index 0000000000..7e44fe9e5c --- /dev/null +++ b/packages/strapi-plugin-content-manager/test/groups/single-required.test.e2e.js @@ -0,0 +1,317 @@ +const { registerAndLogin } = require('../../../../test/helpers/auth'); +const createModelsUtils = require('../../../../test/helpers/models'); +const { createAuthRequest } = require('../../../../test/helpers/request'); + +let modelsUtils; +let rq; + +describe.each([ + ['CONTENT MANAGER', '/content-manager/explorer/withgroup'], + ['GENERATED API', '/withgroups'], +])('[%s] => Non repeatable and required group', (_, path) => { + beforeAll(async () => { + const token = await registerAndLogin(); + const authRq = createAuthRequest(token); + + modelsUtils = createModelsUtils({ rq: authRq }); + + await modelsUtils.createGroup({ + name: 'somegroup', + attributes: { + name: { + type: 'string', + }, + }, + }); + + await modelsUtils.createModelWithType('withgroup', 'group', { + group: 'somegroup', + repeatable: false, + required: true, + }); + + rq = authRq.defaults({ + baseUrl: `http://localhost:1337${path}`, + }); + }, 60000); + + afterAll(async () => { + await modelsUtils.deleteGroup('somegroup'); + await modelsUtils.deleteModel('withgroup'); + }); + + describe('POST new entry', () => { + test('Creating entry with JSON works', async () => { + const res = await rq.post('/', { + body: { + field: { + name: 'someString', + }, + }, + }); + + expect(res.statusCode).toBe(200); + expect(res.body.field).toEqual( + expect.objectContaining({ + id: expect.anything(), + name: 'someString', + }) + ); + }); + + test('Creating entry with formdata works', async () => { + const res = await rq.post('/', { + formData: { + data: JSON.stringify({ + field: { + name: 'someValue', + }, + }), + }, + }); + + expect(res.statusCode).toBe(200); + expect(res.body.field).toEqual( + expect.objectContaining({ + id: expect.anything(), + name: 'someValue', + }) + ); + }); + + test.each([[], 'someString', 128219, false])( + 'Throws if the field is not an object %p', + async value => { + const res = await rq.post('/', { + body: { + field: value, + }, + }); + + expect(res.statusCode).toBe(400); + } + ); + + test('Throws when sending a null value', async () => { + const res = await rq.post('/', { + body: { + field: null, + }, + }); + + expect(res.statusCode).toBe(400); + }); + + test('Throws when the group is not provided', async () => { + const res = await rq.post('/', { + body: {}, + }); + + expect(res.statusCode).toBe(400); + }); + }); + + describe('GET entries', () => { + test('Should return entries with their nested groups', async () => { + const res = await rq.get('/'); + + expect(res.statusCode).toBe(200); + expect(Array.isArray(res.body)).toBe(true); + res.body.forEach(entry => { + if (entry.field === null) return; + + expect(entry.field).toMatchObject({ + name: expect.any(String), + }); + }); + }); + }); + + describe('PUT entry', () => { + test.each([[], 'someString', 128219, false])( + 'Throws when sending invalid updated field %p', + async value => { + const res = await rq.post('/', { + body: { + field: { + name: 'someString', + }, + }, + }); + + const updateRes = await rq.put(`/${res.body.id}`, { + body: { + field: value, + }, + }); + + expect(updateRes.statusCode).toBe(400); + + // shouldn't have been updated + const getRes = await rq.get(`/${res.body.id}`); + + expect(getRes.statusCode).toBe(200); + expect(getRes.body).toEqual(res.body); + } + ); + + test('Keeps the previous value if group not sent', async () => { + const res = await rq.post('/', { + body: { + field: { + name: 'someString', + }, + }, + }); + + const updateRes = await rq.put(`/${res.body.id}`, { + body: {}, + }); + + expect(updateRes.statusCode).toBe(200); + expect(updateRes.body).toEqual(res.body); + + const getRes = await rq.get(`/${res.body.id}`); + + expect(getRes.statusCode).toBe(200); + expect(getRes.body).toEqual(res.body); + }); + + test('Throws if group is null', async () => { + const res = await rq.post('/', { + body: { + field: { + name: 'someString', + }, + }, + }); + + const updateRes = await rq.put(`/${res.body.id}`, { + body: { + field: null, + }, + }); + + expect(updateRes.statusCode).toBe(400); + + const getRes = await rq.get(`/${res.body.id}`); + + expect(getRes.statusCode).toBe(200); + expect(getRes.body).toMatchObject(res.body); + }); + + test('Replaces the previous group if sent without id', async () => { + const res = await rq.post('/', { + body: { + field: { + name: 'someString', + }, + }, + }); + + const updateRes = await rq.put(`/${res.body.id}`, { + body: { + field: { + name: 'new String', + }, + }, + }); + + expect(updateRes.statusCode).toBe(200); + expect(updateRes.body.field.id).not.toBe(res.body.field.id); + expect(updateRes.body).toMatchObject({ + id: res.body.id, + field: { + name: 'new String', + }, + }); + + const getRes = await rq.get(`/${res.body.id}`); + + expect(getRes.statusCode).toBe(200); + expect(getRes.body).toMatchObject({ + id: res.body.id, + field: { + name: 'new String', + }, + }); + }); + + test('Throws on invalid id in group', async () => { + const res = await rq.post('/', { + body: { + field: { + name: 'someString', + }, + }, + }); + + const updateRes = await rq.put(`/${res.body.id}`, { + body: { + field: { + id: 'invalid_id', + name: 'new String', + }, + }, + }); + + expect(updateRes.statusCode).toBe(400); + }); + + test('Updates group if previsous group id is sent', async () => { + const res = await rq.post('/', { + body: { + field: { + name: 'someString', + }, + }, + }); + + const updateRes = await rq.put(`/${res.body.id}`, { + body: { + field: { + id: res.body.field.id, // send old id to update the previous group + name: 'new String', + }, + }, + }); + + const expectedResult = { + id: res.body.id, + field: { + id: res.body.field.id, + name: 'new String', + }, + }; + + expect(updateRes.statusCode).toBe(200); + expect(updateRes.body).toMatchObject(expectedResult); + + const getRes = await rq.get(`/${res.body.id}`); + + expect(getRes.statusCode).toBe(200); + expect(getRes.body).toMatchObject(expectedResult); + }); + }); + + describe('DELETE entry', () => { + test('Returns entry with groups', async () => { + const res = await rq.post('/', { + body: { + field: { + name: 'someString', + }, + }, + }); + + const deleteRes = await rq.delete(`/${res.body.id}`); + + expect(deleteRes.statusCode).toBe(200); + expect(deleteRes.body).toMatchObject(res.body); + + const getRes = await rq.get(`/${res.body.id}`); + + expect(getRes.statusCode).toBe(404); + }); + }); +}); From 1792338cbde53c070a1d111ab2404264e90da76c Mon Sep 17 00:00:00 2001 From: Alexandre Bodin Date: Fri, 9 Aug 2019 11:04:48 +0200 Subject: [PATCH 4/5] Add timeouts in afterAll tests --- .../test/groups/repeatable-min-max.test.e2e.js | 2 +- .../test/groups/repeatable-not-required.test.e2e.js | 2 +- .../test/groups/repeatable-required.test.e2e.js | 2 +- .../test/groups/single-not-required.test.e2e.js | 2 +- .../test/groups/single-required.test.e2e.js | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/strapi-plugin-content-manager/test/groups/repeatable-min-max.test.e2e.js b/packages/strapi-plugin-content-manager/test/groups/repeatable-min-max.test.e2e.js index 3342911d4a..f5a83ad94a 100644 --- a/packages/strapi-plugin-content-manager/test/groups/repeatable-min-max.test.e2e.js +++ b/packages/strapi-plugin-content-manager/test/groups/repeatable-min-max.test.e2e.js @@ -40,7 +40,7 @@ describe.each([ afterAll(async () => { await modelsUtils.deleteGroup('somegroup'); await modelsUtils.deleteModel('withgroup'); - }); + }, 60000); describe('POST new entry', () => { test('Creating entry with JSON works', async () => { diff --git a/packages/strapi-plugin-content-manager/test/groups/repeatable-not-required.test.e2e.js b/packages/strapi-plugin-content-manager/test/groups/repeatable-not-required.test.e2e.js index a7af863872..6e6fba03b0 100644 --- a/packages/strapi-plugin-content-manager/test/groups/repeatable-not-required.test.e2e.js +++ b/packages/strapi-plugin-content-manager/test/groups/repeatable-not-required.test.e2e.js @@ -38,7 +38,7 @@ describe.each([ afterAll(async () => { await modelsUtils.deleteGroup('somegroup'); await modelsUtils.deleteModel('withgroup'); - }); + }, 60000); describe('POST new entry', () => { test('Creating entry with JSON works', async () => { diff --git a/packages/strapi-plugin-content-manager/test/groups/repeatable-required.test.e2e.js b/packages/strapi-plugin-content-manager/test/groups/repeatable-required.test.e2e.js index e18cc1671b..6391589213 100644 --- a/packages/strapi-plugin-content-manager/test/groups/repeatable-required.test.e2e.js +++ b/packages/strapi-plugin-content-manager/test/groups/repeatable-required.test.e2e.js @@ -38,7 +38,7 @@ describe.each([ afterAll(async () => { await modelsUtils.deleteGroup('somegroup'); await modelsUtils.deleteModel('withgroup'); - }); + }, 60000); describe('POST new entry', () => { test('Creating entry with JSON works', async () => { diff --git a/packages/strapi-plugin-content-manager/test/groups/single-not-required.test.e2e.js b/packages/strapi-plugin-content-manager/test/groups/single-not-required.test.e2e.js index 73e30885a3..4f89dd96cb 100644 --- a/packages/strapi-plugin-content-manager/test/groups/single-not-required.test.e2e.js +++ b/packages/strapi-plugin-content-manager/test/groups/single-not-required.test.e2e.js @@ -38,7 +38,7 @@ describe.each([ afterAll(async () => { await modelsUtils.deleteGroup('somegroup'); await modelsUtils.deleteModel('withgroup'); - }); + }, 60000); describe('POST new entry', () => { test('Creating entry with JSON works', async () => { diff --git a/packages/strapi-plugin-content-manager/test/groups/single-required.test.e2e.js b/packages/strapi-plugin-content-manager/test/groups/single-required.test.e2e.js index 7e44fe9e5c..f05c9180c1 100644 --- a/packages/strapi-plugin-content-manager/test/groups/single-required.test.e2e.js +++ b/packages/strapi-plugin-content-manager/test/groups/single-required.test.e2e.js @@ -38,7 +38,7 @@ describe.each([ afterAll(async () => { await modelsUtils.deleteGroup('somegroup'); await modelsUtils.deleteModel('withgroup'); - }); + }, 60000); describe('POST new entry', () => { test('Creating entry with JSON works', async () => { From 34a5424a8a04b574b3e16fbd564d82cae4036718 Mon Sep 17 00:00:00 2001 From: Alexandre Bodin Date: Fri, 9 Aug 2019 11:45:31 +0200 Subject: [PATCH 5/5] Fixes to mongoose groups --- packages/strapi-hook-mongoose/lib/mount-models.js | 4 ++-- packages/strapi-hook-mongoose/lib/queries.js | 3 ++- .../test/groups/repeatable-min-max.test.e2e.js | 15 ++++++++++++--- .../groups/repeatable-not-required.test.e2e.js | 15 ++++++++++++--- .../test/groups/repeatable-required.test.e2e.js | 15 ++++++++++++--- .../test/groups/single-not-required.test.e2e.js | 15 ++++++++++++--- .../test/groups/single-required.test.e2e.js | 15 ++++++++++++--- 7 files changed, 64 insertions(+), 18 deletions(-) diff --git a/packages/strapi-hook-mongoose/lib/mount-models.js b/packages/strapi-hook-mongoose/lib/mount-models.js index 1f47e8515c..9b1c7f4425 100644 --- a/packages/strapi-hook-mongoose/lib/mount-models.js +++ b/packages/strapi-hook-mongoose/lib/mount-models.js @@ -347,10 +347,10 @@ const createOnFetchPopulateFn = ({ ) { this._mongooseOptions.populate[name].path = `${name}.ref`; } else { - this._mongooseOptions.populate[name] = { + _.set(this._mongooseOptions, ['populate', name], { path: `${name}.ref`, _docs: {}, - }; + }); } }); diff --git a/packages/strapi-hook-mongoose/lib/queries.js b/packages/strapi-hook-mongoose/lib/queries.js index d07a828d2d..9279fac51b 100644 --- a/packages/strapi-hook-mongoose/lib/queries.js +++ b/packages/strapi-hook-mongoose/lib/queries.js @@ -267,9 +267,10 @@ module.exports = ({ model, modelKey, strapi }) => { const relations = pickRelations(values); const data = omitExernalValues(values); + // update groups first in case it fails don't update the entity + await updateGroups(entry, values); // Update entry with no-relational data. await entry.updateOne(data); - await updateGroups(entry, values); // Update relational data and return the entry. return model.updateRelations(Object.assign(params, { values: relations })); diff --git a/packages/strapi-plugin-content-manager/test/groups/repeatable-min-max.test.e2e.js b/packages/strapi-plugin-content-manager/test/groups/repeatable-min-max.test.e2e.js index f5a83ad94a..5483e6bc3c 100644 --- a/packages/strapi-plugin-content-manager/test/groups/repeatable-min-max.test.e2e.js +++ b/packages/strapi-plugin-content-manager/test/groups/repeatable-min-max.test.e2e.js @@ -227,7 +227,10 @@ describe.each([ const getRes = await rq.get(`/${res.body.id}`); expect(getRes.statusCode).toBe(200); - expect(getRes.body).toEqual(res.body); + expect(getRes.body).toMatchObject({ + id: res.body.id, + field: res.body.field, + }); } ); @@ -307,12 +310,18 @@ describe.each([ }); expect(updateRes.statusCode).toBe(200); - expect(updateRes.body).toEqual(res.body); + expect(updateRes.body).toMatchObject({ + id: res.body.id, + field: res.body.field, + }); const getRes = await rq.get(`/${res.body.id}`); expect(getRes.statusCode).toBe(200); - expect(getRes.body).toEqual(res.body); + expect(getRes.body).toMatchObject({ + id: res.body.id, + field: res.body.field, + }); }); test('Throws when not enough items', async () => { diff --git a/packages/strapi-plugin-content-manager/test/groups/repeatable-not-required.test.e2e.js b/packages/strapi-plugin-content-manager/test/groups/repeatable-not-required.test.e2e.js index 6e6fba03b0..32a8b94a51 100644 --- a/packages/strapi-plugin-content-manager/test/groups/repeatable-not-required.test.e2e.js +++ b/packages/strapi-plugin-content-manager/test/groups/repeatable-not-required.test.e2e.js @@ -197,7 +197,10 @@ describe.each([ const getRes = await rq.get(`/${res.body.id}`); expect(getRes.statusCode).toBe(200); - expect(getRes.body).toEqual(res.body); + expect(getRes.body).toMatchObject({ + id: res.body.id, + field: res.body.field, + }); } ); @@ -277,12 +280,18 @@ describe.each([ }); expect(updateRes.statusCode).toBe(200); - expect(updateRes.body).toEqual(res.body); + expect(updateRes.body).toMatchObject({ + id: res.body.id, + field: res.body.field, + }); const getRes = await rq.get(`/${res.body.id}`); expect(getRes.statusCode).toBe(200); - expect(getRes.body).toEqual(res.body); + expect(getRes.body).toMatchObject({ + id: res.body.id, + field: res.body.field, + }); }); test('Removes previous groups if empty array sent', async () => { diff --git a/packages/strapi-plugin-content-manager/test/groups/repeatable-required.test.e2e.js b/packages/strapi-plugin-content-manager/test/groups/repeatable-required.test.e2e.js index 6391589213..c9922f9759 100644 --- a/packages/strapi-plugin-content-manager/test/groups/repeatable-required.test.e2e.js +++ b/packages/strapi-plugin-content-manager/test/groups/repeatable-required.test.e2e.js @@ -196,7 +196,10 @@ describe.each([ const getRes = await rq.get(`/${res.body.id}`); expect(getRes.statusCode).toBe(200); - expect(getRes.body).toEqual(res.body); + expect(getRes.body).toMatchObject({ + id: res.body.id, + field: res.body.field, + }); } ); @@ -276,12 +279,18 @@ describe.each([ }); expect(updateRes.statusCode).toBe(200); - expect(updateRes.body).toEqual(res.body); + expect(updateRes.body).toMatchObject({ + id: res.body.id, + field: res.body.field, + }); const getRes = await rq.get(`/${res.body.id}`); expect(getRes.statusCode).toBe(200); - expect(getRes.body).toEqual(res.body); + expect(getRes.body).toMatchObject({ + id: res.body.id, + field: res.body.field, + }); }); test('Removes previous groups if empty array sent', async () => { diff --git a/packages/strapi-plugin-content-manager/test/groups/single-not-required.test.e2e.js b/packages/strapi-plugin-content-manager/test/groups/single-not-required.test.e2e.js index 4f89dd96cb..b9465bb799 100644 --- a/packages/strapi-plugin-content-manager/test/groups/single-not-required.test.e2e.js +++ b/packages/strapi-plugin-content-manager/test/groups/single-not-required.test.e2e.js @@ -153,7 +153,10 @@ describe.each([ const getRes = await rq.get(`/${res.body.id}`); expect(getRes.statusCode).toBe(200); - expect(getRes.body).toEqual(res.body); + expect(getRes.body).toMatchObject({ + id: res.body.id, + field: res.body.field, + }); } ); @@ -171,12 +174,18 @@ describe.each([ }); expect(updateRes.statusCode).toBe(200); - expect(updateRes.body).toEqual(res.body); + expect(updateRes.body).toMatchObject({ + id: res.body.id, + field: res.body.field, + }); const getRes = await rq.get(`/${res.body.id}`); expect(getRes.statusCode).toBe(200); - expect(getRes.body).toEqual(res.body); + expect(getRes.body).toMatchObject({ + id: res.body.id, + field: res.body.field, + }); }); test('Removes previous group if null sent', async () => { diff --git a/packages/strapi-plugin-content-manager/test/groups/single-required.test.e2e.js b/packages/strapi-plugin-content-manager/test/groups/single-required.test.e2e.js index f05c9180c1..27003624dd 100644 --- a/packages/strapi-plugin-content-manager/test/groups/single-required.test.e2e.js +++ b/packages/strapi-plugin-content-manager/test/groups/single-required.test.e2e.js @@ -151,7 +151,10 @@ describe.each([ const getRes = await rq.get(`/${res.body.id}`); expect(getRes.statusCode).toBe(200); - expect(getRes.body).toEqual(res.body); + expect(getRes.body).toMatchObject({ + id: res.body.id, + field: res.body.field, + }); } ); @@ -169,12 +172,18 @@ describe.each([ }); expect(updateRes.statusCode).toBe(200); - expect(updateRes.body).toEqual(res.body); + expect(updateRes.body).toMatchObject({ + id: res.body.id, + field: res.body.field, + }); const getRes = await rq.get(`/${res.body.id}`); expect(getRes.statusCode).toBe(200); - expect(getRes.body).toEqual(res.body); + expect(getRes.body).toMatchObject({ + id: res.body.id, + field: res.body.field, + }); }); test('Throws if group is null', async () => {