mirror of
https://github.com/strapi/strapi.git
synced 2025-11-05 04:13:36 +00:00
add e2e tests for CTB singularNames + better handle names containing numbers
This commit is contained in:
parent
64940edbae
commit
7f0877b73f
@ -64,11 +64,13 @@ describe('Content type validator', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('Prevents use of names without plural form', () => {
|
||||
test('Throws when using name without plural form', async () => {
|
||||
describe('Prevents use of same singularName and pluralName', () => {
|
||||
test('Throws when using same singularName and pluralName', async () => {
|
||||
const data = {
|
||||
contentType: {
|
||||
name: 'news',
|
||||
displayName: 'news',
|
||||
singularName: 'news',
|
||||
pluralName: 'news',
|
||||
attributes: {
|
||||
title: {
|
||||
type: 'string',
|
||||
@ -79,7 +81,7 @@ describe('Content type validator', () => {
|
||||
|
||||
await validateContentTypeInput(data).catch(err => {
|
||||
expect(err).toMatchObject({
|
||||
'contentType.name': [expect.stringMatching('cannot be pluralized')],
|
||||
contentType: [expect.stringMatching('singularName and pluralName should be different')],
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -89,7 +91,9 @@ describe('Content type validator', () => {
|
||||
test('Can use custom keys', async () => {
|
||||
const input = {
|
||||
contentType: {
|
||||
name: 'test',
|
||||
displayName: 'test',
|
||||
singularName: 'test',
|
||||
pluralName: 'tests',
|
||||
attributes: {
|
||||
views: {
|
||||
type: 'integer',
|
||||
@ -115,7 +119,9 @@ describe('Content type validator', () => {
|
||||
test('Deletes empty defaults', async () => {
|
||||
const data = {
|
||||
contentType: {
|
||||
name: 'test',
|
||||
displayName: 'test',
|
||||
singularName: 'test',
|
||||
pluralName: 'tests',
|
||||
attributes: {
|
||||
slug: {
|
||||
type: 'string',
|
||||
@ -161,7 +167,9 @@ describe('Content type validator', () => {
|
||||
test('Deleted UID target fields are removed from input data', async () => {
|
||||
const data = {
|
||||
contentType: {
|
||||
name: 'test',
|
||||
displayName: 'test',
|
||||
singularName: 'test',
|
||||
pluralName: 'tests',
|
||||
attributes: {
|
||||
slug: {
|
||||
type: 'uid',
|
||||
@ -181,7 +189,9 @@ describe('Content type validator', () => {
|
||||
test('Can use custom keys', async () => {
|
||||
const input = {
|
||||
contentType: {
|
||||
name: 'test',
|
||||
displayName: 'test',
|
||||
singularName: 'test',
|
||||
pluralName: 'tests',
|
||||
attributes: {
|
||||
views: {
|
||||
type: 'integer',
|
||||
|
||||
@ -46,7 +46,8 @@ const createContentTypeSchema = (data, { isEdition = false } = {}) => {
|
||||
const kind = _.get(data, 'contentType.kind', typeKinds.COLLECTION_TYPE);
|
||||
const contentTypeSchema = createSchema(VALID_TYPES, VALID_RELATIONS[kind] || [], {
|
||||
modelType: modelTypes.CONTENT_TYPE,
|
||||
}).shape({
|
||||
})
|
||||
.shape({
|
||||
displayName: yup
|
||||
.string()
|
||||
.min(1)
|
||||
@ -65,7 +66,12 @@ const createContentTypeSchema = (data, { isEdition = false } = {}) => {
|
||||
.test(forbiddenContentTypeNameValidator())
|
||||
.isKebabCase()
|
||||
.required(),
|
||||
});
|
||||
})
|
||||
.test(
|
||||
'singularName-not-equal-pluralName',
|
||||
'${path}: singularName and pluralName should be different',
|
||||
value => value.singularName !== value.pluralName
|
||||
);
|
||||
|
||||
return yup
|
||||
.object({
|
||||
|
||||
@ -12,15 +12,17 @@ Object {
|
||||
},
|
||||
"collectionName": "tests",
|
||||
"description": "My description",
|
||||
"displayName": "My name",
|
||||
"draftAndPublish": false,
|
||||
"kind": "singleType",
|
||||
"name": "My name",
|
||||
"pluginOptions": Object {
|
||||
"content-manager": Object {
|
||||
"visible": true,
|
||||
},
|
||||
},
|
||||
"pluralName": "my-names",
|
||||
"restrictRelationsTo": null,
|
||||
"singularName": "my-name",
|
||||
"visible": true,
|
||||
},
|
||||
"uid": "test-uid",
|
||||
|
||||
@ -12,6 +12,8 @@ describe('Content types service', () => {
|
||||
collectionName: 'tests',
|
||||
info: {
|
||||
displayName: 'My name',
|
||||
singularName: 'my-name',
|
||||
pluralName: 'my-names',
|
||||
description: 'My description',
|
||||
},
|
||||
options: {
|
||||
|
||||
@ -59,38 +59,3 @@ Object {
|
||||
},
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`Content Type Builder - Content types Single Types Get single type returns full schema and information 1`] = `
|
||||
Object {
|
||||
"data": Object {
|
||||
"apiID": "test-single-type",
|
||||
"schema": Object {
|
||||
"attributes": Object {
|
||||
"title": Object {
|
||||
"pluginOptions": Object {
|
||||
"i18n": Object {
|
||||
"localized": true,
|
||||
},
|
||||
},
|
||||
"type": "string",
|
||||
},
|
||||
},
|
||||
"collectionName": "test_single_types",
|
||||
"description": "",
|
||||
"displayName": "Test Single Type",
|
||||
"draftAndPublish": false,
|
||||
"kind": "singleType",
|
||||
"pluginOptions": Object {
|
||||
"i18n": Object {
|
||||
"localized": true,
|
||||
},
|
||||
},
|
||||
"pluralName": "test-single-types",
|
||||
"restrictRelationsTo": null,
|
||||
"singularName": "test-single-type",
|
||||
"visible": true,
|
||||
},
|
||||
"uid": "api::test-single-type.test-single-type",
|
||||
},
|
||||
}
|
||||
`;
|
||||
@ -0,0 +1,36 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Content Type Builder - Content types Single Types Get single type returns full schema and information 1`] = `
|
||||
Object {
|
||||
"data": Object {
|
||||
"apiID": "test-single-type",
|
||||
"schema": Object {
|
||||
"attributes": Object {
|
||||
"title": Object {
|
||||
"pluginOptions": Object {
|
||||
"i18n": Object {
|
||||
"localized": true,
|
||||
},
|
||||
},
|
||||
"type": "string",
|
||||
},
|
||||
},
|
||||
"collectionName": "test_single_types",
|
||||
"description": "",
|
||||
"displayName": "Test Single Type",
|
||||
"draftAndPublish": false,
|
||||
"kind": "singleType",
|
||||
"pluginOptions": Object {
|
||||
"i18n": Object {
|
||||
"localized": true,
|
||||
},
|
||||
},
|
||||
"pluralName": "test-single-types",
|
||||
"restrictRelationsTo": null,
|
||||
"singularName": "test-single-type",
|
||||
"visible": true,
|
||||
},
|
||||
"uid": "api::test-single-type.test-single-type",
|
||||
},
|
||||
}
|
||||
`;
|
||||
@ -0,0 +1,277 @@
|
||||
/**
|
||||
* Integration test for the content-type-builder content types management apis
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
const { createStrapiInstance } = require('../../../../test/helpers/strapi');
|
||||
const { createAuthRequest } = require('../../../../test/helpers/request');
|
||||
const modelsUtils = require('../../../../test/helpers/models');
|
||||
|
||||
let strapi;
|
||||
let rq;
|
||||
|
||||
const restart = async () => {
|
||||
await strapi.destroy();
|
||||
strapi = await createStrapiInstance();
|
||||
rq = await createAuthRequest({ strapi });
|
||||
};
|
||||
|
||||
describe('Content Type Builder - Content types', () => {
|
||||
beforeAll(async () => {
|
||||
strapi = await createStrapiInstance();
|
||||
rq = await createAuthRequest({ strapi });
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await restart();
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
const modelsUIDs = [
|
||||
'api::test-collection-type.test-collection-type',
|
||||
'api::ct-with-dp.ct-with-dp',
|
||||
'api::kebab-case.kebab-case',
|
||||
'api::my2space.my2space',
|
||||
'api::my-3-space.my-3-space',
|
||||
];
|
||||
|
||||
await modelsUtils.cleanupModels(modelsUIDs, { strapi });
|
||||
await modelsUtils.deleteContentTypes(modelsUIDs, { strapi });
|
||||
|
||||
await strapi.destroy();
|
||||
});
|
||||
|
||||
describe('Collection Types', () => {
|
||||
const testCollectionTypeUID = 'api::test-collection-type.test-collection-type';
|
||||
const ctWithDpUID = 'api::ct-with-dp.ct-with-dp';
|
||||
|
||||
test('Successful creation of a collection type', async () => {
|
||||
const res = await rq({
|
||||
method: 'POST',
|
||||
url: '/content-type-builder/content-types',
|
||||
body: {
|
||||
contentType: {
|
||||
displayName: 'Test Collection Type',
|
||||
singularName: 'test-collection-type',
|
||||
pluralName: 'test-collection-types',
|
||||
pluginOptions: {
|
||||
i18n: {
|
||||
localized: true,
|
||||
},
|
||||
},
|
||||
attributes: {
|
||||
title: {
|
||||
type: 'string',
|
||||
pluginOptions: {
|
||||
i18n: {
|
||||
localized: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(res.statusCode).toBe(201);
|
||||
expect(res.body).toEqual({
|
||||
data: {
|
||||
uid: testCollectionTypeUID,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test('Get collection type returns full schema and information', async () => {
|
||||
const res = await rq({
|
||||
method: 'GET',
|
||||
url: `/content-type-builder/content-types/${testCollectionTypeUID}`,
|
||||
});
|
||||
|
||||
expect(res.statusCode).toBe(200);
|
||||
expect(res.body).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('Successfull creation of a collection type with draftAndPublish enabled', async () => {
|
||||
const res = await rq({
|
||||
method: 'POST',
|
||||
url: '/content-type-builder/content-types',
|
||||
body: {
|
||||
contentType: {
|
||||
displayName: 'CT with DP',
|
||||
singularName: 'ct-with-dp',
|
||||
pluralName: 'ct-with-dps',
|
||||
draftAndPublish: true,
|
||||
attributes: {
|
||||
title: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(res.statusCode).toBe(201);
|
||||
expect(res.body).toEqual({
|
||||
data: {
|
||||
uid: ctWithDpUID,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test('Get collection type returns full schema and informations with draftAndPublish', async () => {
|
||||
const res = await rq({
|
||||
method: 'GET',
|
||||
url: `/content-type-builder/content-types/${ctWithDpUID}`,
|
||||
});
|
||||
|
||||
expect(res.statusCode).toBe(200);
|
||||
expect(res.body).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('Cannot use same string for singularName and pluralName', async () => {
|
||||
const res = await rq({
|
||||
method: 'POST',
|
||||
url: '/content-type-builder/content-types',
|
||||
body: {
|
||||
contentType: {
|
||||
displayName: 'same string',
|
||||
singularName: 'same-string',
|
||||
pluralName: 'same-string',
|
||||
draftAndPublish: true,
|
||||
attributes: {
|
||||
title: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(res.statusCode).toBe(400);
|
||||
expect(res.body).toEqual({
|
||||
error: {
|
||||
contentType: ['contentType: singularName and pluralName should be different'],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test('displayNamen singularName and pluralName are required', async () => {
|
||||
const res = await rq({
|
||||
method: 'POST',
|
||||
url: '/content-type-builder/content-types',
|
||||
body: {
|
||||
contentType: {
|
||||
draftAndPublish: true,
|
||||
attributes: {
|
||||
title: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(res.statusCode).toBe(400);
|
||||
expect(res.body).toEqual({
|
||||
error: {
|
||||
contentType: ['contentType: singularName and pluralName should be different'],
|
||||
'contentType.displayName': ['contentType.displayName is a required field'],
|
||||
'contentType.singularName': ['contentType.singularName is a required field'],
|
||||
'contentType.pluralName': ['contentType.pluralName is a required field'],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test('Can edit displayName but singularName and pluralName are ignored', async () => {
|
||||
const uid = 'api::ct-with-dp.ct-with-dp';
|
||||
let res = await rq({
|
||||
method: 'PUT',
|
||||
url: `/content-type-builder/content-types/${uid}`,
|
||||
body: {
|
||||
contentType: {
|
||||
displayName: 'new displayName',
|
||||
singularName: 'ct-with-dp-new',
|
||||
pluralName: 'ct-with-dps-new',
|
||||
draftAndPublish: true,
|
||||
attributes: {
|
||||
title: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
expect(res.statusCode).toBe(201);
|
||||
|
||||
await restart();
|
||||
|
||||
res = await rq({
|
||||
method: 'GET',
|
||||
url: `/content-type-builder/content-types/${uid}`,
|
||||
});
|
||||
|
||||
expect(res.statusCode).toBe(200);
|
||||
expect(res.body).toMatchObject({
|
||||
data: {
|
||||
uid,
|
||||
schema: {
|
||||
displayName: 'new displayName',
|
||||
singularName: 'ct-with-dp', // no change
|
||||
pluralName: 'ct-with-dps',
|
||||
draftAndPublish: true,
|
||||
attributes: {
|
||||
title: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test.each([
|
||||
['kebab-case', 'kebab-cases', true],
|
||||
['Kebab-case', 'Kebab-cases', false],
|
||||
['kebab case', 'kebab cases', false],
|
||||
['kebabCase', 'kebabCases', false],
|
||||
['kebab@case', 'kebab@cases', false],
|
||||
['my2space', 'my2spaces', true],
|
||||
['2myspace', '2myspaces', false],
|
||||
['my-3-space', 'my-3-spaces', true],
|
||||
])('Are "%s" and "%s" valid: %s', async (singularName, pluralName, isExpectedValid) => {
|
||||
const res = await rq({
|
||||
method: 'POST',
|
||||
url: '/content-type-builder/content-types',
|
||||
body: {
|
||||
contentType: {
|
||||
displayName: 'same string',
|
||||
singularName,
|
||||
pluralName,
|
||||
draftAndPublish: true,
|
||||
attributes: {
|
||||
title: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (isExpectedValid) {
|
||||
expect(res.statusCode).toBe(201);
|
||||
} else {
|
||||
expect(res.statusCode).toBe(400);
|
||||
expect(res.body).toEqual({
|
||||
error: {
|
||||
'contentType.pluralName': [
|
||||
'contentType.pluralName is not in kebab case (an-example-of-kebab-case)',
|
||||
],
|
||||
'contentType.singularName': [
|
||||
'contentType.singularName is not in kebab case (an-example-of-kebab-case)',
|
||||
],
|
||||
},
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -28,10 +28,8 @@ describe('Content Type Builder - Content types', () => {
|
||||
|
||||
afterAll(async () => {
|
||||
const modelsUIDs = [
|
||||
'api::test-collection-type.test-collection-type',
|
||||
'api::test-collection.test-collection',
|
||||
'api::test-single-type.test-single-type',
|
||||
'api::ct-with-dp.ct-with-dp',
|
||||
];
|
||||
|
||||
await modelsUtils.cleanupModels(modelsUIDs, { strapi });
|
||||
@ -40,94 +38,6 @@ describe('Content Type Builder - Content types', () => {
|
||||
await strapi.destroy();
|
||||
});
|
||||
|
||||
describe('Collection Types', () => {
|
||||
const testCollectionTypeUID = 'api::test-collection-type.test-collection-type';
|
||||
const ctWithDpUID = 'api::ct-with-dp.ct-with-dp';
|
||||
|
||||
test('Successful creation of a collection type', async () => {
|
||||
const res = await rq({
|
||||
method: 'POST',
|
||||
url: '/content-type-builder/content-types',
|
||||
body: {
|
||||
contentType: {
|
||||
displayName: 'Test Collection Type',
|
||||
singularName: 'test-collection-type',
|
||||
pluralName: 'test-collection-types',
|
||||
pluginOptions: {
|
||||
i18n: {
|
||||
localized: true,
|
||||
},
|
||||
},
|
||||
attributes: {
|
||||
title: {
|
||||
type: 'string',
|
||||
pluginOptions: {
|
||||
i18n: {
|
||||
localized: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(res.statusCode).toBe(201);
|
||||
expect(res.body).toEqual({
|
||||
data: {
|
||||
uid: testCollectionTypeUID,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test('Get collection type returns full schema and information', async () => {
|
||||
const res = await rq({
|
||||
method: 'GET',
|
||||
url: `/content-type-builder/content-types/${testCollectionTypeUID}`,
|
||||
});
|
||||
|
||||
expect(res.statusCode).toBe(200);
|
||||
expect(res.body).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('Successfull creation of a collection type with draftAndPublish enabled', async () => {
|
||||
const res = await rq({
|
||||
method: 'POST',
|
||||
url: '/content-type-builder/content-types',
|
||||
body: {
|
||||
contentType: {
|
||||
displayName: 'CT with DP',
|
||||
singularName: 'ct-with-dp',
|
||||
pluralName: 'ct-with-dps',
|
||||
draftAndPublish: true,
|
||||
attributes: {
|
||||
title: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(res.statusCode).toBe(201);
|
||||
expect(res.body).toEqual({
|
||||
data: {
|
||||
uid: ctWithDpUID,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test('Get collection type returns full schema and informations with draftAndPublish', async () => {
|
||||
const res = await rq({
|
||||
method: 'GET',
|
||||
url: `/content-type-builder/content-types/${ctWithDpUID}`,
|
||||
});
|
||||
|
||||
expect(res.statusCode).toBe(200);
|
||||
expect(res.body).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Single Types', () => {
|
||||
const singleTypeUID = 'api::test-single-type.test-single-type';
|
||||
|
||||
@ -259,12 +169,8 @@ describe('Content Type Builder - Content types', () => {
|
||||
expect(updateRes.statusCode).toBe(400);
|
||||
expect(updateRes.body.error).toMatch('multiple entries in DB');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Private relation field', () => {
|
||||
const singleTypeUID = 'api::test-single-type.test-single-type';
|
||||
|
||||
test('should add a relation field', async () => {
|
||||
test('Should add a relation field', async () => {
|
||||
const res = await rq({
|
||||
method: 'PUT',
|
||||
url: `/content-type-builder/content-types/${singleTypeUID}`,
|
||||
@ -294,7 +200,7 @@ describe('Content Type Builder - Content types', () => {
|
||||
});
|
||||
});
|
||||
|
||||
test('should contain a private relation field', async () => {
|
||||
test('Should contain a private relation field', async () => {
|
||||
const res = await rq({
|
||||
method: 'GET',
|
||||
url: `/content-type-builder/content-types/${singleTypeUID}`,
|
||||
@ -4,8 +4,10 @@ const { join, extname, basename } = require('path');
|
||||
const { existsSync } = require('fs-extra');
|
||||
const _ = require('lodash');
|
||||
const fse = require('fs-extra');
|
||||
const { isKebabCase } = require('@strapi/utils');
|
||||
|
||||
const normalizeName = _.kebabCase;
|
||||
// to handle names with numbers in it we first check is it is already in kebabCase
|
||||
const normalizeName = name => (isKebabCase(name) ? name : _.kebabCase(name));
|
||||
|
||||
const DEFAULT_CONTENT_TYPE = {
|
||||
schema: {},
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user