2024-04-02 10:08:10 +01:00
|
|
|
'use strict';
|
|
|
|
|
|
|
|
const { createStrapiInstance } = require('api-tests/strapi');
|
|
|
|
const { createAuthRequest } = require('api-tests/request');
|
|
|
|
const { createTestBuilder } = require('api-tests/builder');
|
|
|
|
const { set } = require('lodash/fp');
|
|
|
|
|
|
|
|
const modelsUtils = require('api-tests/models');
|
|
|
|
const { cloneDeep } = require('lodash');
|
|
|
|
|
|
|
|
let strapi;
|
|
|
|
let rq;
|
|
|
|
|
|
|
|
const categoryModel = {
|
|
|
|
kind: 'collectionType',
|
|
|
|
collectionName: 'categories',
|
|
|
|
displayName: 'Category',
|
|
|
|
singularName: 'category',
|
|
|
|
pluralName: 'categories',
|
|
|
|
description: '',
|
|
|
|
name: 'Category',
|
|
|
|
draftAndPublish: true,
|
|
|
|
pluginOptions: {
|
|
|
|
i18n: {
|
|
|
|
localized: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
attributes: {
|
|
|
|
name: {
|
|
|
|
type: 'string',
|
|
|
|
unique: true,
|
|
|
|
pluginOptions: {
|
|
|
|
i18n: {
|
|
|
|
localized: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
nonLocalized: {
|
|
|
|
type: 'string',
|
|
|
|
pluginOptions: {
|
|
|
|
i18n: {
|
|
|
|
localized: false,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
nonLocalizedCompo: {
|
|
|
|
component: 'default.compo',
|
|
|
|
type: 'component',
|
|
|
|
repeatable: false,
|
|
|
|
pluginOptions: {
|
|
|
|
i18n: {
|
|
|
|
localized: false,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
nonLocalizedRepeatableCompo: {
|
|
|
|
component: 'default.compo',
|
|
|
|
type: 'component',
|
|
|
|
repeatable: true,
|
|
|
|
pluginOptions: {
|
|
|
|
i18n: {
|
|
|
|
localized: false,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
const tagModel = {
|
|
|
|
kind: 'collectionType',
|
|
|
|
collectionName: 'tags',
|
|
|
|
displayName: 'Tag',
|
|
|
|
singularName: 'tag',
|
|
|
|
pluralName: 'tags',
|
|
|
|
description: '',
|
|
|
|
options: {
|
|
|
|
reviewWorkflows: false,
|
|
|
|
draftAndPublish: true,
|
|
|
|
},
|
|
|
|
pluginOptions: {
|
|
|
|
i18n: {
|
|
|
|
localized: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
attributes: {
|
|
|
|
name: {
|
|
|
|
type: 'string',
|
|
|
|
pluginOptions: {
|
|
|
|
i18n: {
|
|
|
|
localized: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
nonLocalized: {
|
|
|
|
type: 'string',
|
|
|
|
pluginOptions: {
|
|
|
|
i18n: {
|
|
|
|
localized: false,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
const compo = (withRelations = false) => ({
|
|
|
|
displayName: 'compo',
|
|
|
|
category: 'default',
|
|
|
|
attributes: {
|
|
|
|
name: {
|
|
|
|
type: 'string',
|
|
|
|
},
|
|
|
|
...(!withRelations
|
|
|
|
? {}
|
|
|
|
: {
|
|
|
|
tag: {
|
|
|
|
type: 'relation',
|
|
|
|
relation: 'oneToOne',
|
|
|
|
target: 'api::tag.tag',
|
|
|
|
},
|
|
|
|
}),
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
|
|
const data = {
|
|
|
|
tags: [],
|
|
|
|
};
|
|
|
|
|
|
|
|
const allLocales = [
|
|
|
|
{ code: 'ko', name: 'Korean' },
|
|
|
|
{ code: 'it', name: 'Italian' },
|
|
|
|
{ code: 'fr', name: 'French' },
|
|
|
|
{ code: 'es-AR', name: 'Spanish (Argentina)' },
|
|
|
|
];
|
|
|
|
|
|
|
|
const allLocaleCodes = allLocales.map((locale) => locale.code);
|
|
|
|
|
|
|
|
// Make the tags available in all locales except one so we can test relation cases
|
|
|
|
// when the locale relation does not exist
|
|
|
|
const tagsAvailableIn = allLocaleCodes.slice(1);
|
|
|
|
|
|
|
|
const transformConnectToDisconnect = (data) => {
|
|
|
|
const transformObject = (obj) => {
|
|
|
|
if (obj.tag && obj.tag.connect) {
|
|
|
|
obj.tag.disconnect = obj.tag.connect;
|
|
|
|
delete obj.tag.connect;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
if (Array.isArray(data)) {
|
|
|
|
data.forEach((item) => transformObject(item));
|
|
|
|
} else if (typeof data === 'object' && data !== null) {
|
|
|
|
transformObject(data);
|
|
|
|
}
|
|
|
|
|
|
|
|
return data;
|
|
|
|
};
|
|
|
|
|
2024-09-04 10:59:07 +02:00
|
|
|
const create = async (uid, payload) => {
|
|
|
|
return rq({
|
|
|
|
method: 'POST',
|
|
|
|
url: `/content-manager/collection-types/${uid}`,
|
|
|
|
body: payload,
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
const update = async (uid, documentId, payload) => {
|
|
|
|
return rq({
|
|
|
|
method: 'PUT',
|
|
|
|
url: `/content-manager/collection-types/${uid}/${documentId}`,
|
|
|
|
body: payload,
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
const publish = async (uid, documentId, payload) => {
|
|
|
|
return rq({
|
|
|
|
method: 'POST',
|
|
|
|
url: `/content-manager/collection-types/${uid}/${documentId}/actions/publish`,
|
|
|
|
body: payload,
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
const unpublish = async (uid, documentId, payload) => {
|
|
|
|
return rq({
|
|
|
|
method: 'POST',
|
|
|
|
url: `/content-manager/collection-types/${uid}/${documentId}/actions/unpublish`,
|
|
|
|
body: payload,
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2024-04-02 10:08:10 +01:00
|
|
|
describe('i18n', () => {
|
|
|
|
const builder = createTestBuilder();
|
|
|
|
|
|
|
|
beforeAll(async () => {
|
|
|
|
await builder
|
|
|
|
.addComponent(compo(false))
|
|
|
|
.addContentTypes([tagModel, categoryModel])
|
|
|
|
.addFixtures('plugin::i18n.locale', [
|
|
|
|
{ name: 'Korean', code: 'ko' },
|
|
|
|
{ name: 'Italian', code: 'it' },
|
|
|
|
{ name: 'French', code: 'fr' },
|
|
|
|
{ name: 'Spanish (Argentina)', code: 'es-AR' },
|
|
|
|
])
|
|
|
|
.build();
|
|
|
|
|
|
|
|
await modelsUtils.modifyComponent(compo(true));
|
|
|
|
|
|
|
|
strapi = await createStrapiInstance();
|
|
|
|
rq = await createAuthRequest({ strapi });
|
|
|
|
});
|
|
|
|
|
|
|
|
afterAll(async () => {
|
|
|
|
// Delete all locales that have been created
|
|
|
|
await strapi.db.query('plugin::i18n.locale').deleteMany({ code: { $ne: 'en' } });
|
|
|
|
|
|
|
|
await strapi.destroy();
|
|
|
|
await builder.cleanup();
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('Non localized fields', () => {
|
|
|
|
let documentId = '';
|
|
|
|
|
|
|
|
beforeAll(async () => {
|
|
|
|
// Create a document with an entry in every locale with the localized
|
|
|
|
// field filled in. This field can be different across locales
|
2024-09-04 10:59:07 +02:00
|
|
|
const res = await create('api::category.category', { name: `Test` });
|
2024-04-02 10:08:10 +01:00
|
|
|
documentId = res.body.data.documentId;
|
|
|
|
|
|
|
|
for (const locale of allLocaleCodes) {
|
2024-09-04 10:59:07 +02:00
|
|
|
await update('api::category.category', documentId, {
|
|
|
|
locale,
|
|
|
|
name: `Test ${locale}`,
|
2024-04-02 10:08:10 +01:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create 2 tags in the default locale
|
|
|
|
const [tag1, tag2] = await Promise.all([
|
2024-09-04 10:59:07 +02:00
|
|
|
create('api::tag.tag', { name: `Test tag` }),
|
|
|
|
create('api::tag.tag', { name: `Test tag 2` }),
|
2024-04-02 10:08:10 +01:00
|
|
|
]);
|
|
|
|
data.tags.push(tag1.body.data);
|
|
|
|
data.tags.push(tag2.body.data);
|
|
|
|
|
|
|
|
for (const locale of tagsAvailableIn) {
|
|
|
|
// Create 2 tags for every other locale that supports tags
|
|
|
|
const [localeTag1, localeTag2] = await Promise.all([
|
2024-09-04 10:59:07 +02:00
|
|
|
update('api::tag.tag', tag1.body.data.documentId, {
|
|
|
|
locale,
|
|
|
|
name: `Test tag ${locale}`,
|
2024-04-02 10:08:10 +01:00
|
|
|
}),
|
2024-09-04 10:59:07 +02:00
|
|
|
update('api::tag.tag', tag2.body.data.documentId, {
|
|
|
|
locale,
|
|
|
|
name: `Test tag ${locale} 2`,
|
2024-04-02 10:08:10 +01:00
|
|
|
}),
|
|
|
|
]);
|
|
|
|
|
|
|
|
data.tags.push(localeTag1.body.data);
|
|
|
|
data.tags.push(localeTag2.body.data);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
// Test non localized behaviour across these actions
|
|
|
|
const actionsToTest = [['publish'], ['unpublish + discard'], ['update']];
|
|
|
|
|
|
|
|
describe('Scalar non localized fields', () => {
|
2024-09-04 10:59:07 +02:00
|
|
|
const attribute = 'nonLocalized';
|
2024-04-02 10:08:10 +01:00
|
|
|
|
2024-09-04 10:59:07 +02:00
|
|
|
test('Modify a scalar non localized field - Publish', async () => {
|
|
|
|
const res = await publish('api::category.category', documentId, { [attribute]: 'publish' });
|
2024-04-02 10:08:10 +01:00
|
|
|
|
2024-09-04 10:59:07 +02:00
|
|
|
expect(res.statusCode).toBe(200);
|
2024-04-02 10:08:10 +01:00
|
|
|
|
2024-09-04 10:59:07 +02:00
|
|
|
// Expect all locales to be updates, both draft and published versions
|
|
|
|
for (const locale of allLocaleCodes) {
|
|
|
|
const localeRes = await strapi.db.query('api::category.category').findOne({
|
|
|
|
where: {
|
|
|
|
documentId,
|
|
|
|
publishedAt: null,
|
|
|
|
locale: { $eq: locale },
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
|
|
// The locale should now have the same value as the default locale.
|
|
|
|
expect(localeRes[attribute]).toEqual('publish');
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
test('Modify a scalar non localized field - Unpublish + Discard', async () => {
|
|
|
|
// Publish the default locale entry
|
|
|
|
let res = await publish('api::category.category', documentId, { [attribute]: 'unpublish' });
|
|
|
|
expect(res.statusCode).toBe(200);
|
|
|
|
|
|
|
|
// Update the default locale draft entry with random data
|
|
|
|
const randomData = 'random';
|
|
|
|
res = await update('api::category.category', documentId, { [attribute]: randomData });
|
|
|
|
expect(res.statusCode).toBe(200);
|
|
|
|
|
|
|
|
// Unpublish the default locale entry
|
|
|
|
res = await unpublish('api::category.category', documentId, { discardDraft: true });
|
|
|
|
|
|
|
|
expect(res.statusCode).toBe(200);
|
|
|
|
|
|
|
|
// Expect all locales to be updates, both draft and published versions
|
|
|
|
for (const locale of allLocaleCodes) {
|
|
|
|
const localeRes = await strapi.db.query('api::category.category').findOne({
|
|
|
|
where: {
|
|
|
|
documentId,
|
|
|
|
publishedAt: null,
|
|
|
|
locale: { $eq: locale },
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
|
|
// The locale should now have the same value as the default locale.
|
|
|
|
expect(localeRes[attribute]).toEqual('unpublish');
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
test('Modify a scalar non localized field - Update', async () => {
|
|
|
|
const updatedValue = 'update';
|
|
|
|
|
|
|
|
const res = await update('api::category.category', documentId, {
|
|
|
|
[attribute]: updatedValue,
|
2024-04-02 10:08:10 +01:00
|
|
|
});
|
2024-09-04 10:59:07 +02:00
|
|
|
|
|
|
|
expect(res.statusCode).toBe(200);
|
|
|
|
|
|
|
|
// Expect all locales to be updates, both draft and published versions
|
|
|
|
for (const locale of allLocaleCodes) {
|
|
|
|
const localeRes = await strapi.db.query('api::category.category').findOne({
|
|
|
|
where: {
|
|
|
|
documentId,
|
|
|
|
publishedAt: null,
|
|
|
|
locale: { $eq: locale },
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
|
|
// The locale should now have the same value as the default locale.
|
|
|
|
expect(localeRes[attribute]).toEqual(updatedValue);
|
|
|
|
}
|
2024-04-02 10:08:10 +01:00
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('Scalar field within a non localized component', () => {
|
|
|
|
describe.each(actionsToTest)('', (method) => {
|
|
|
|
test(`Modify a scalar field within a non localized component - Method ${method}`, async () => {
|
|
|
|
const isPublish = method === 'publish';
|
|
|
|
const isUnpublish = method.includes('unpublish');
|
|
|
|
|
|
|
|
const key = 'nonLocalizedCompo';
|
|
|
|
const updateAt = [{ key: 'name', value: 'Compo Name' }];
|
|
|
|
|
|
|
|
const updatedValue = updateAt.reduce((acc, { key, value }) => {
|
|
|
|
return set(key, `${key}::${value}::${method}`, acc);
|
|
|
|
}, {});
|
|
|
|
|
|
|
|
if (isPublish) {
|
|
|
|
// Publish the default locale entry
|
|
|
|
await rq({
|
|
|
|
method: 'POST',
|
|
|
|
url: `/content-manager/collection-types/api::category.category/${documentId}/actions/publish`,
|
|
|
|
body: {
|
|
|
|
[key]: updatedValue,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
} else if (isUnpublish) {
|
|
|
|
// Publish the default locale entry
|
|
|
|
await rq({
|
|
|
|
method: 'POST',
|
|
|
|
url: `/content-manager/collection-types/api::category.category/${documentId}/actions/publish`,
|
|
|
|
body: {
|
|
|
|
[key]: updatedValue,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
|
|
let randomData = {};
|
|
|
|
Object.entries(updatedValue).forEach(([key, value]) => {
|
|
|
|
if (typeof value === 'string') {
|
|
|
|
randomData[key] = 'random';
|
|
|
|
} else {
|
|
|
|
randomData[key] = value;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
// Update the default locale draft entry with random data
|
|
|
|
await rq({
|
|
|
|
method: 'PUT',
|
|
|
|
url: `/content-manager/collection-types/api::category.category/${documentId}`,
|
|
|
|
body: {
|
|
|
|
[key]: randomData,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
|
|
// Unpublish the default locale entry
|
|
|
|
await rq({
|
|
|
|
method: 'POST',
|
|
|
|
url: `/content-manager/collection-types/api::category.category/${documentId}/actions/unpublish`,
|
|
|
|
body: {
|
|
|
|
discardDraft: true,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
await rq({
|
|
|
|
method: 'PUT',
|
|
|
|
url: `/content-manager/collection-types/api::category.category/${documentId}`,
|
|
|
|
body: {
|
|
|
|
[key]: updatedValue,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
for (const locale of allLocaleCodes) {
|
|
|
|
const localeRes = await strapi.db.query('api::category.category').findOne({
|
|
|
|
where: {
|
|
|
|
documentId,
|
|
|
|
publishedAt: null,
|
|
|
|
locale: { $eq: locale },
|
|
|
|
},
|
|
|
|
populate: [key],
|
|
|
|
});
|
|
|
|
|
|
|
|
// Make sure non localized component fields in other locales have been updated in the same way.
|
|
|
|
expect(localeRes[key]).toEqual(expect.objectContaining(updatedValue));
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe.each([false, true])('', (isRepeatable) => {
|
|
|
|
describe('Relation within a non localized component', () => {
|
|
|
|
describe.each(actionsToTest)('', (method) => {
|
|
|
|
test(`Modify a relation within a non localized component - Method ${method} - Repeatable ${isRepeatable}`, async () => {
|
|
|
|
const isPublish = method === 'publish';
|
|
|
|
const isUnpublish = method.includes('unpublish');
|
|
|
|
|
|
|
|
const key = isRepeatable ? 'nonLocalizedRepeatableCompo' : 'nonLocalizedCompo';
|
|
|
|
const connectRelationAt = 'tag';
|
|
|
|
|
|
|
|
let updatedValue;
|
|
|
|
if (isRepeatable) {
|
|
|
|
const localeTags = data.tags.filter((tag) => tag.locale === 'en');
|
|
|
|
|
|
|
|
updatedValue = [
|
|
|
|
{
|
|
|
|
[connectRelationAt]: {
|
|
|
|
connect: [localeTags[0]],
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
[connectRelationAt]: {
|
|
|
|
connect: [localeTags[1]],
|
|
|
|
},
|
|
|
|
},
|
|
|
|
];
|
|
|
|
} else {
|
|
|
|
updatedValue = {
|
|
|
|
[connectRelationAt]: {
|
|
|
|
connect: [data.tags.find((tag) => tag.locale === 'en')],
|
|
|
|
},
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
let res;
|
|
|
|
if (isPublish) {
|
|
|
|
// Publish the default locale entry
|
|
|
|
res = await rq({
|
|
|
|
method: 'POST',
|
|
|
|
url: `/content-manager/collection-types/api::category.category/${documentId}/actions/publish`,
|
|
|
|
body: {
|
|
|
|
[key]: updatedValue,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
} else if (isUnpublish) {
|
|
|
|
// Publish the default locale entry
|
|
|
|
await rq({
|
|
|
|
method: 'POST',
|
|
|
|
url: `/content-manager/collection-types/api::category.category/${documentId}/actions/publish`,
|
|
|
|
body: {
|
|
|
|
[key]: updatedValue,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
|
|
// Update the default locale draft entry to remove any connected tags
|
|
|
|
await rq({
|
|
|
|
method: 'PUT',
|
|
|
|
url: `/content-manager/collection-types/api::category.category/${documentId}`,
|
|
|
|
body: {
|
|
|
|
[key]: transformConnectToDisconnect(cloneDeep(updatedValue)),
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
|
|
// Unpublish the default locale entry
|
|
|
|
res = await rq({
|
|
|
|
method: 'POST',
|
|
|
|
url: `/content-manager/collection-types/api::category.category/${documentId}/actions/unpublish`,
|
|
|
|
body: {
|
|
|
|
discardDraft: true,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
res = await rq({
|
|
|
|
method: 'PUT',
|
|
|
|
url: `/content-manager/collection-types/api::category.category/${documentId}`,
|
|
|
|
body: {
|
|
|
|
[key]: updatedValue,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
// If we have connected a relation, we should expect the count to
|
|
|
|
// equal the number of relations we have connected
|
2024-05-03 11:41:42 +02:00
|
|
|
const fieldData = res.body.data[key];
|
|
|
|
if (Array.isArray(fieldData)) {
|
|
|
|
fieldData.forEach((item, index) => {
|
|
|
|
expect(item[connectRelationAt].count).toEqual(
|
|
|
|
Array.isArray(updatedValue)
|
|
|
|
? updatedValue[index][connectRelationAt].connect.length
|
|
|
|
: updatedValue[connectRelationAt].connect.length
|
|
|
|
);
|
|
|
|
});
|
|
|
|
}
|
2024-04-02 10:08:10 +01:00
|
|
|
|
|
|
|
for (const locale of allLocaleCodes) {
|
|
|
|
const localeRes = await strapi.db.query('api::category.category').findOne({
|
|
|
|
where: {
|
|
|
|
documentId,
|
|
|
|
publishedAt: null,
|
|
|
|
locale: { $eq: locale },
|
|
|
|
},
|
|
|
|
populate: [`${key}.${connectRelationAt}`],
|
|
|
|
});
|
|
|
|
|
|
|
|
// Connecting a relation to the default locale should add the
|
|
|
|
// equivalent locale relation if it exists to the other locales
|
|
|
|
(Array.isArray(localeRes[key]) ? localeRes[key] : [localeRes[key]]).forEach(
|
|
|
|
(item, index) => {
|
|
|
|
if (!tagsAvailableIn.includes(locale)) {
|
|
|
|
expect(item[connectRelationAt]).toBeNull();
|
|
|
|
} else {
|
|
|
|
expect(item[connectRelationAt]).toEqual(
|
|
|
|
expect.objectContaining({
|
|
|
|
locale,
|
|
|
|
documentId: (Array.isArray(updatedValue) ? updatedValue : [updatedValue])[
|
|
|
|
index
|
|
|
|
][connectRelationAt].connect[0].documentId,
|
|
|
|
})
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|