strapi/api-tests/core/content-manager/components/repeatable-not-required.test.api.js
Josh 4f4e3c3acd
feat(cm): D&P pt2 (#19380)
* feat: return metadata on content manager endpoints (#19361)

* feat: return metadata on content manager endpoints

* feat: return meta

* fix: cm tests

* feat: fix cm metadata api tests (#19375)

* feat: return metadata on content manager endpoints

* feat: return meta

* fix: cm tests

* fix: admin test

* chore(cm): refactor CM (#19341)

* chore(cm): refactor EditView

comes with a host of new re-usable APIs!

* feat(cm): add useDocument hook

* feat(cm): add useDocumentOperations hook

* feat(cm): initialise EditView header

* feat(cm): add useDocumentLayout hook

* fix: listView from layout refactor

* chore(cm): refactor editview form input renderer

* chore: fix lint & ts

* fix: re-add custom fields

* test: fix admin/CM unit tests

* docs(cm): add docs at a high level of how the CM works

* test: add useDocumentRBAC tests

* chore: pr amends

* feat: add addEditViewSidePanel API to strapi (#19398)

* feat: add addEditViewSidePanel API to strapi

fix: don't pass all the query params to the create route

* chore: pr amends

* fix: return available status when content type doesnt have i18n enabled (#19419)

* fix: return available status when content type doesnt have i18n enabled

* chore: remove comment

* fix(cm): list view & build process

* feat: save and publish

* feat: update contract

* feat: dp tests

* chore: use document from create and update

* feat: save and publish single types

* feat: return metadata on content manager endpoints (#19361)

* feat: return metadata on content manager endpoints

* feat: return meta

* fix: cm tests

* feat: fix cm metadata api tests (#19375)

* feat: return metadata on content manager endpoints

* feat: return meta

* fix: cm tests

* fix: admin test

* chore(cm): refactor CM (#19341)

* chore(cm): refactor EditView

comes with a host of new re-usable APIs!

* feat(cm): add useDocument hook

* feat(cm): add useDocumentOperations hook

* feat(cm): initialise EditView header

* feat(cm): add useDocumentLayout hook

* fix: listView from layout refactor

* chore(cm): refactor editview form input renderer

* chore: fix lint & ts

* fix: re-add custom fields

* test: fix admin/CM unit tests

* docs(cm): add docs at a high level of how the CM works

* test: add useDocumentRBAC tests

* chore: pr amends

* feat: add addEditViewSidePanel API to strapi (#19398)

* feat: add addEditViewSidePanel API to strapi

fix: don't pass all the query params to the create route

* chore: pr amends

* fix: return available status when content type doesnt have i18n enabled (#19419)

* fix: return available status when content type doesnt have i18n enabled

* chore: remove comment

* fix(cm): list view & build process

* feat: add publish & update action (#19423)

* feat: return metadata on content manager endpoints (#19361)

* feat: return metadata on content manager endpoints

* feat: return meta

* fix: cm tests

* feat: fix cm metadata api tests (#19375)

* feat: return metadata on content manager endpoints

* feat: return meta

* fix: cm tests

* fix: admin test

* chore(cm): refactor CM (#19341)

* chore(cm): refactor EditView

comes with a host of new re-usable APIs!

* feat(cm): add useDocument hook

* feat(cm): add useDocumentOperations hook

* feat(cm): initialise EditView header

* feat(cm): add useDocumentLayout hook

* fix: listView from layout refactor

* chore(cm): refactor editview form input renderer

* chore: fix lint & ts

* fix: re-add custom fields

* test: fix admin/CM unit tests

* docs(cm): add docs at a high level of how the CM works

* test: add useDocumentRBAC tests

* chore: pr amends

* feat: add addEditViewSidePanel API to strapi (#19398)

* feat: add addEditViewSidePanel API to strapi

fix: don't pass all the query params to the create route

* chore: pr amends

* fix: return available status when content type doesnt have i18n enabled (#19419)

* fix: return available status when content type doesnt have i18n enabled

* chore: remove comment

* feat: add publish & update action

* feat: add published disabled state

* test: fix suite

* test: add unit for Panels

* fix(cm): status not state for redirect

* fix(cm): list view status & component main field property

* chore: pr feedback

* chore: apply suggestions

Co-authored-by: markkaylor <mark.kaylor@strapi.io>

---------

Co-authored-by: Marc Roig <marc12info@gmail.com>
Co-authored-by: markkaylor <mark.kaylor@strapi.io>

* feat: test single types

* feat: wrap single type publish into a transaction

* feat(cm): add unpublish & delete actions, also re-enable single-types (#19459)

* fix: delete url was wrong way round

* feat: compute modified status

* fix(cm): reimplement ListSettingsView (#19432)

* chore: PR feedback

* feat: discard endpoint

* feat: discard draft api tests

* feat: unpublish and discard

* chore: pr comments

* chore: update sanitizer

* feat(cm): add publish and save (#19500)

* feat(cm): add publish and save

* test(cm): fix unit for useDocumentActions

* Update packages/core/content-manager/server/src/controllers/collection-types.ts

Co-authored-by: Jamie Howard <48524071+jhoward1994@users.noreply.github.com>

* Update packages/core/content-manager/shared/contracts/single-types-v5.ts

Co-authored-by: Jamie Howard <48524071+jhoward1994@users.noreply.github.com>

* feat(document-service): map document ID to entry ID (#19248)

* feat: use document service in content manager

* feat: update contracts with meta information

* chore: group metadata types into a single type

* feat: metadata information in single types

* chore: change meta contract to return documents instead of strings

* fix: remove unused type

* fix: ignore doc id if entry is null

* fix: update contract metadata

* feat: document metadata service

* feat: locale and status filtering

* chore: add comment

* chore: refactor metadata service

* chore: refactor entity manager exists to handle single types

* feat: refactor single type controllers to use documents

* feat: get locale param from in cm endpoints

* Revert "feat: get locale param from in cm endpoints"

This reverts commit 856c38588b8f8521cadd85c8c933f42a36a2178a.

* feat: get locale param from cm endpoints

* Update packages/plugins/i18n/server/src/controllers/validate-locale-creation.ts

Co-authored-by: Ben Irvin <ben@innerdvations.com>

* fix: entity manager unit tests

* chore: unit test document metadata

* feat: prevent empty string locale filtering

* fix: cm contract import

* chore: test new d&p cm features

* fix: search content manager api test

* fix: cm tests

* fix: cm tests

* fix: cm tests

* fix(content-manager): access to non default locale documents (#19190)

* fix(content-manager): access to non default locale documents

* chore(content-manager): revert route construction

* fix(content-manager): api tests for number of draft relations

* test(content-manager): counting number of draft relations for non default locales

* chore(content-manager): remove default locale from entity manager countDraftRelations

* chore: basic relations testing for document service

* chore(e2e): disable edit view tests (#19235)

* feat: get relation ids

* chore: clean functions into other folders

* chore: rename files

* fix: group document ids by its uid

* feat: id mapper

* chore: improve typings

* chore: rename transform functions

* fix: id-transform tests

* chore: simplify function return value

* chore: improve comments

* fix: api tests

* fix: single types unit test

* fix: skip relations test

* fix: exclude fields

* fix: short hand ifs

* fix: merge conflict

* fix: transform output of find one

* Update packages/core/core/src/services/document-service/transform/utils.ts

Co-authored-by: Ben Irvin <ben.irvin@strapi.io>

* feat[Document Service]: Param transformation (#19373)

* fix(core): wip param transformation

* feat(core): wip param transformation based on relational status

* feat(core): wip populate and filter transformation based on relational status

* chore(core): simplify fields and sort

* chore(core): clean up

* feat(core): wip filter transformation with traverseQueryFilters

* feat(core): reorganise and PR feedback

* fix(core): filters traversal logic

* feat(core): populate relational transformations

* chore(core): reintroduce populate transformation

* fix(core): enforce that fields must include id

* fix(core): enforce that fields must include id

* fix(core): filter and sort transformation

* chore(core): typos

* chore(core): further filters test cases

* feat(core): support object based sorts

* chore(core): fields test naming

* feat(core): handle logical operators in filters

* fix: skip conditions test

* fix(core): switchIdForDocumentId (#19497)

* fix: uniqueness test

* fix: available status should be an array

* fix: available statuses

* fix: skip uniqueness folder

* fix: skip uniqueness test errors

* fix: skip failing test

---------

Co-authored-by: Ben Irvin <ben@innerdvations.com>
Co-authored-by: Jamie Howard <48524071+jhoward1994@users.noreply.github.com>
Co-authored-by: Josh <37798644+joshuaellis@users.noreply.github.com>
Co-authored-by: Jamie Howard <jamie.howard@strapi.io>
Co-authored-by: Ben Irvin <ben.irvin@strapi.io>

* chore: re-implement edit configuration (#19488)

* chore: re-implement edit configuration

* chore: cleanups

* test: fix unit tests

* feat(cm): add discard changes action (#19509)

* feat(cm): add discard changes action

* feat(cm): add discard when unpublishing

* test(e2e): fix editview e2e

* test(e2e): fix uniqueness partially

* test(unit): fix unit tests for actions & add for discard

* chore: bump playwright

* fix(webkit): add shim for requestIdCallback

* chore: pr amends

* feat: return status on available locales

* feat: add document-actions to list-view (#19523)

* fix(cm): add gap and alignment for edit-view heading with super long names

* fix(cm): list view status'

* feat: add list-view actions

* fix: conditions for actions to be enabled

* fix: stay on draft tab when published

* fix: stop propogation on list-view row click

* test(e2e): fix editview tests

* chore: update spelling error

Co-authored-by: Jamie Howard <48524071+jhoward1994@users.noreply.github.com>

---------

Co-authored-by: Jamie Howard <48524071+jhoward1994@users.noreply.github.com>

* feat: test document metadata

* chore: remove unnecessary unit test

* fix: return modified on published documents

* chore: init split single-type collection-type in document service

* Fix/fields test case (#19481)

* Update packages/core/content-manager/server/src/services/document-metadata.ts

Co-authored-by: Jamie Howard <48524071+jhoward1994@users.noreply.github.com>

* fix: pretty

* chore: refactor middlewares

* feat: send empty object if locale does not exist on document

* feat: single types

* chore: add tests to middlewares

* feat: update locale using query params (#19546)

* feat: return available locales when not finding locale

* chore: update typings

* feat: add clone action (#19526)

* feat: add clone action

* test(e2e): add auto clone tests

* chore: pr amends

* feat: add information to header actions menu (#19548)

* feat: add information to header actions menu

* fix: dont use non-null-assertion

* feat: manage relations on publish (#19427)


---------

Co-authored-by: Ben Irvin <ben@innerdvations.com>
Co-authored-by: Jamie Howard <48524071+jhoward1994@users.noreply.github.com>
Co-authored-by: Josh <37798644+joshuaellis@users.noreply.github.com>
Co-authored-by: Jamie Howard <jamie.howard@strapi.io>

* feat: v5 i18n relations (#19504)


Co-authored-by: Ben Irvin <ben@innerdvations.com>
Co-authored-by: Jamie Howard <48524071+jhoward1994@users.noreply.github.com>
Co-authored-by: Josh <37798644+joshuaellis@users.noreply.github.com>
Co-authored-by: Jamie Howard <jamie.howard@strapi.io>

* fix: locale test api

* chore: refactor i18n (#19555)

* chore: remove types package, should be using strapi

* chore: refactor i18n settings page

* feat: add i18n to CM

* feat(i18n): add delete locale action (#19562)

* chore: pr amends

Co-authored-by: Simone <startae14@gmail.com>

---------

Co-authored-by: Simone <startae14@gmail.com>

* feat(cm): re-implement validation (#19578)

* feat: re-implement validation in the CM

feat: add blocker

feat: handle validation errors from the API

chore: reimplement useFieldHint

test(unit): fix fe tests

chore: fix bad logical operator

* chore: await notifications to leave before trying to go to other pages

* fix: validation issues & blocker showing up incorrectly

* fix: broken publish behaviour

* fix(content-manager): uid availability and generation (#19518)

* fix(content-manager): uid availability and generation

* fix(content-manager): pass locale as is from UID service

* fix(content-manager): match UIDs based on startsWith

---------

Co-authored-by: Marc Roig <marc12info@gmail.com>
Co-authored-by: markkaylor <mark.kaylor@strapi.io>
Co-authored-by: Jamie Howard <48524071+jhoward1994@users.noreply.github.com>
Co-authored-by: Ben Irvin <ben@innerdvations.com>
Co-authored-by: Jamie Howard <jamie.howard@strapi.io>
Co-authored-by: Ben Irvin <ben.irvin@strapi.io>
Co-authored-by: Alexandre Bodin <bodin.alex@gmail.com>
Co-authored-by: Alexandre BODIN <alexandrebodin@users.noreply.github.com>
Co-authored-by: Simone <startae14@gmail.com>
2024-02-22 17:18:32 +00:00

574 lines
13 KiB
JavaScript

'use strict';
const { createTestBuilder } = require('api-tests/builder');
const { createStrapiInstance } = require('api-tests/strapi');
const { createAuthRequest } = require('api-tests/request');
let strapi;
let rq;
const component = {
displayName: 'somecomponent',
attributes: {
name: {
type: 'string',
},
},
};
const ct = {
displayName: 'withcomponent',
singularName: 'withcomponent',
pluralName: 'withcomponents',
attributes: {
field: {
type: 'component',
component: 'default.somecomponent',
repeatable: true,
required: false,
},
},
};
describe('Non repeatable and Not required component', () => {
const builder = createTestBuilder();
beforeAll(async () => {
await builder.addComponent(component).addContentType(ct).build();
strapi = await createStrapiInstance();
rq = await createAuthRequest({ strapi });
rq.setURLPrefix('/content-manager/collection-types/api::withcomponent.withcomponent');
});
afterAll(async () => {
await strapi.destroy();
await builder.cleanup();
});
describe('POST new entry', () => {
test('Creating entry with JSON works', async () => {
const res = await rq.post('/', {
body: {
field: [
{
name: 'someString',
},
],
},
qs: {
populate: ['field'],
},
});
expect(res.statusCode).toBe(200);
expect(Array.isArray(res.body.data.field)).toBe(true);
expect(res.body.data.field).toEqual(
expect.arrayContaining([
expect.objectContaining({
id: expect.anything(),
name: 'someString',
}),
])
);
});
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: [],
},
qs: {
populate: ['field'],
},
});
expect(res.statusCode).toBe(200);
expect(res.body.data.field).toEqual([]);
});
test('Can send input without the component field', async () => {
const res = await rq.post('/', {
body: {},
qs: {
populate: ['field'],
},
});
expect(res.statusCode).toBe(200);
expect(res.body.data.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',
},
],
},
qs: {
populate: ['field'],
},
});
const getRes = await rq.get(`/${res.body.data.id}`, {
qs: {
populate: ['field'],
},
});
expect(getRes.statusCode).toBe(200);
expect(Array.isArray(getRes.body.data.field)).toBe(true);
expect(getRes.body.data.field[0]).toMatchObject({
name: 'firstString',
});
expect(getRes.body.data.field[1]).toMatchObject({
name: 'someString',
});
});
test('Should return entries with their nested components', async () => {
const res = await rq.get('/', {
qs: {
populate: ['field'],
},
});
expect(res.statusCode).toBe(200);
expect(res.body.pagination).toBeDefined();
expect(Array.isArray(res.body.results)).toBe(true);
res.body.results.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',
},
],
},
qs: {
populate: ['field'],
},
});
const updateRes = await rq.put(`/${res.body.data.id}`, {
body: {
field: value,
},
qs: {
populate: ['field'],
},
});
expect(updateRes.statusCode).toBe(400);
// shouldn't have been updated
const getRes = await rq.get(`/${res.body.data.id}`, {
qs: {
populate: ['field'],
},
});
expect(getRes.statusCode).toBe(200);
expect(getRes.body.data).toMatchObject({
id: res.body.data.id,
field: res.body.data.field,
});
}
);
test('Updates order at each request', async () => {
const res = await rq.post('/', {
body: {
field: [
{
name: 'someString',
},
{
name: 'otherString',
},
],
},
qs: {
populate: ['field'],
},
});
expect(res.body.data.field[0]).toMatchObject({
name: 'someString',
});
expect(res.body.data.field[1]).toMatchObject({
name: 'otherString',
});
const updateRes = await rq.put(`/${res.body.data.id}`, {
body: {
field: [
{
name: 'otherString',
},
{
name: 'someString',
},
],
},
qs: {
populate: ['field'],
},
});
expect(updateRes.statusCode).toBe(200);
expect(Array.isArray(updateRes.body.data.field)).toBe(true);
expect(updateRes.body.data.field[0]).toMatchObject({
name: 'otherString',
});
expect(updateRes.body.data.field[1]).toMatchObject({
name: 'someString',
});
const getRes = await rq.get(`/${res.body.data.id}`, {
qs: {
populate: ['field'],
},
});
expect(getRes.statusCode).toBe(200);
expect(Array.isArray(getRes.body.data.field)).toBe(true);
expect(getRes.body.data.field[0]).toMatchObject({
name: 'otherString',
});
expect(getRes.body.data.field[1]).toMatchObject({
name: 'someString',
});
});
test('Keeps the previous value if component not sent', async () => {
const res = await rq.post('/', {
body: {
field: [
{
name: 'someString',
},
{
name: 'otherString',
},
],
},
qs: {
populate: ['field'],
},
});
const updateRes = await rq.put(`/${res.body.data.id}`, {
body: {},
qs: {
populate: ['field'],
},
});
expect(updateRes.statusCode).toBe(200);
expect(updateRes.body.data).toMatchObject({
id: res.body.data.id,
field: res.body.data.field,
});
const getRes = await rq.get(`/${res.body.data.id}`, {
qs: {
populate: ['field'],
},
});
expect(getRes.statusCode).toBe(200);
expect(getRes.body.data).toMatchObject({
id: res.body.data.id,
field: res.body.data.field,
});
});
test('Removes previous components if empty array sent', async () => {
const res = await rq.post('/', {
body: {
field: [
{
name: 'someString',
},
],
},
qs: {
populate: ['field'],
},
});
const updateRes = await rq.put(`/${res.body.data.id}`, {
body: {
field: [],
},
qs: {
populate: ['field'],
},
});
const expectResult = {
id: res.body.data.id,
field: [],
};
expect(updateRes.statusCode).toBe(200);
expect(updateRes.body.data).toMatchObject(expectResult);
const getRes = await rq.get(`/${res.body.data.id}`, {
qs: {
populate: ['field'],
},
});
expect(getRes.statusCode).toBe(200);
expect(getRes.body.data).toMatchObject(expectResult);
});
test('Replaces the previous components if sent without id', async () => {
const res = await rq.post('/', {
body: {
field: [
{
name: 'someString',
},
],
},
qs: {
populate: ['field'],
},
});
const updateRes = await rq.put(`/${res.body.data.id}`, {
body: {
field: [
{
name: 'new String',
},
],
},
qs: {
populate: ['field'],
},
});
expect(updateRes.statusCode).toBe(200);
const oldIds = res.body.data.field.map((val) => val.id);
updateRes.body.data.field.forEach((val) => {
expect(oldIds.includes(val.id)).toBe(false);
});
expect(updateRes.body.data).toMatchObject({
id: res.body.data.id,
field: [
{
name: 'new String',
},
],
});
const getRes = await rq.get(`/${res.body.data.id}`, {
qs: {
populate: ['field'],
},
});
expect(getRes.statusCode).toBe(200);
expect(getRes.body.data).toMatchObject({
id: res.body.data.id,
field: [
{
name: 'new String',
},
],
});
});
test('Throws on invalid id in component', async () => {
const res = await rq.post('/', {
body: {
field: [
{
name: 'someString',
},
],
},
});
const updateRes = await rq.put(`/${res.body.data.id}`, {
body: {
field: [
{
id: 'invalid_id',
name: 'new String',
},
],
},
});
expect(updateRes.statusCode).toBe(400);
});
test('Updates component with ids, create new ones and removes old ones', async () => {
const res = await rq.post('/', {
body: {
field: [
{
name: 'one',
},
{
name: 'two',
},
{
name: 'three',
},
],
},
qs: {
populate: ['field'],
},
});
const updateRes = await rq.put(`/${res.body.data.id}`, {
body: {
field: [
{
id: res.body.data.field[0].id, // send old id to update the previous component
name: 'newOne',
},
{
name: 'newTwo',
},
{
id: res.body.data.field[2].id,
name: 'three',
},
{
name: 'four',
},
],
},
qs: {
populate: ['field'],
},
});
const expectedResult = {
id: res.body.data.id,
field: [
{
id: res.body.data.field[0].id,
name: 'newOne',
},
{
name: 'newTwo',
},
{
id: res.body.data.field[2].id,
name: 'three',
},
{
name: 'four',
},
],
};
expect(updateRes.statusCode).toBe(200);
expect(updateRes.body.data).toMatchObject(expectedResult);
const getRes = await rq.get(`/${res.body.data.id}`, {
qs: {
populate: ['field'],
},
});
expect(getRes.statusCode).toBe(200);
expect(getRes.body.data).toMatchObject(expectedResult);
});
});
describe('DELETE entry', () => {
test('Returns entry with components', async () => {
const res = await rq.post('/', {
body: {
field: [
{
name: 'someString',
},
{
name: 'someOtherString',
},
{
name: 'otherSomeString',
},
],
},
qs: {
populate: ['field'],
},
});
const deleteRes = await rq.delete(`/${res.body.data.id}`, {
qs: {
populate: ['field'],
},
});
expect(deleteRes.statusCode).toBe(200);
const getRes = await rq.get(`/${res.body.data.id}`, {
qs: {
populate: ['field'],
},
});
expect(getRes.statusCode).toBe(404);
});
});
});