strapi/tests/api/plugins/i18n/locales.test.api.js
Ben Irvin 0954e210a1
test(api): cleanup test data (#22760)
* test(api): cleanup test data

* test(api): do not delete products in afterAll
2025-01-31 12:31:36 +01:00

480 lines
12 KiB
JavaScript

'use strict';
const { omit } = require('lodash/fp');
const { createTestBuilder } = require('api-tests/builder');
const { createStrapiInstance } = require('api-tests/strapi');
const { createAuthRequest } = require('api-tests/request');
const data = {
locales: [],
deletedLocales: [],
};
const omitTimestamps = omit(['updatedAt', 'createdAt']);
const compareLocales = (a, b) => (a.code < b.code ? -1 : 1);
const productModel = {
pluginOptions: {
i18n: {
localized: true,
},
},
attributes: {
name: {
type: 'string',
},
},
displayName: 'Product',
singularName: 'product',
pluralName: 'products',
description: '',
collectionName: '',
};
describe('CRUD locales', () => {
let rq;
let strapi;
const builder = createTestBuilder();
let localeService;
beforeAll(async () => {
await builder.addContentType(productModel).build();
strapi = await createStrapiInstance();
rq = await createAuthRequest({ strapi });
localeService = strapi.plugin('i18n').service('locales');
// ensure we don't have any data in the database left over from previous tests that didn't clean up
await strapi.db.query('api::product.product').deleteMany();
});
afterAll(async () => {
await localeService.setDefaultLocale({ code: 'en' });
// Delete all data that has been created
await strapi.db.query('plugin::i18n.locale').deleteMany({ code: { $ne: 'en' } });
await strapi.destroy();
await builder.cleanup();
});
describe('Default locale', () => {
test('Default locale is already created', async () => {
const res = await rq({
url: '/i18n/locales',
method: 'GET',
});
expect(res.statusCode).toBe(200);
expect(res.body).toHaveLength(1);
expect(res.body[0].isDefault).toBe(true);
data.locales.push(res.body[0]);
});
});
describe('Creation', () => {
test('Can create a locale', async () => {
const locale = {
name: 'French',
code: 'fr',
isDefault: false,
};
const res = await rq({
url: '/i18n/locales',
method: 'POST',
body: locale,
});
expect(res.statusCode).toBe(200);
expect(res.body).toMatchObject({
id: expect.anything(),
...locale,
});
data.locales.push(res.body);
});
test('Cannot create a locale if code or isDefault is missing', async () => {
const locale = {
name: 'Italian',
};
const res = await rq({
url: '/i18n/locales',
method: 'POST',
body: locale,
});
expect(res.statusCode).toBe(400);
expect(res.body).toMatchObject({
data: null,
error: {
details: {
errors: [
{
message: 'code is a required field',
name: 'ValidationError',
path: ['code'],
},
{
message: 'isDefault is a required field',
name: 'ValidationError',
path: ['isDefault'],
},
],
},
message: '2 errors occurred',
name: 'ValidationError',
status: 400,
},
});
});
test('Cannot create a locale if code already exists', async () => {
const locale = {
code: 'fr',
name: 'random name',
isDefault: false,
};
const res = await rq({
url: '/i18n/locales',
method: 'POST',
body: locale,
});
expect(res.statusCode).toBe(400);
expect(res.body).toMatchObject({
data: null,
error: {
status: 400,
name: 'ApplicationError',
message: 'This locale already exists',
details: {},
},
});
});
test('Can create a locale even if name already exists', async () => {
const locale = {
name: 'French',
code: 'fr-FR',
isDefault: false,
};
const res = await rq({
url: '/i18n/locales',
method: 'POST',
body: locale,
});
expect(res.statusCode).toBe(200);
expect(res.body).toMatchObject({
id: expect.anything(),
...locale,
});
data.locales.push(res.body);
});
test('Only one locale can be default (POST)', async () => {
let res = await rq({
url: '/i18n/locales',
method: 'POST',
body: { code: 'bas', name: 'random', isDefault: true },
});
expect(res.statusCode).toBe(200);
expect(res.body.isDefault).toBe(true);
data.locales[0].isDefault = false;
res = await rq({
url: '/i18n/locales',
method: 'POST',
body: { code: 'en-US', name: 'random', isDefault: true },
});
expect(res.statusCode).toBe(200);
expect(res.body.isDefault).toBe(true);
res = await rq({
url: '/i18n/locales',
method: 'GET',
});
expect(res.statusCode).toBe(200);
const enLocale = res.body.find((locale) => locale.code === 'bas');
const enUsLocale = res.body.find((locale) => locale.code === 'en-US');
expect(enLocale.isDefault).toBe(false);
expect(enUsLocale.isDefault).toBe(true);
data.locales.push(enLocale);
data.locales.push(enUsLocale);
});
});
describe('Read', () => {
test('Can list the locales', async () => {
const res = await rq({
url: '/i18n/locales',
method: 'GET',
});
expect(res.statusCode).toBe(200);
expect(res.body).toHaveLength(data.locales.length);
expect(res.body.sort(compareLocales)).toMatchObject(
data.locales.slice().sort(compareLocales)
);
});
});
describe('Update', () => {
test('Can update the name of a locale', async () => {
const localeUpdate = {
name: 'French update',
isDefault: false,
};
const res = await rq({
url: `/i18n/locales/${data.locales[1].id}`,
method: 'PUT',
body: localeUpdate,
});
expect(res.statusCode).toBe(200);
expect(res.body).toMatchObject({
...omitTimestamps(data.locales[1]),
...localeUpdate,
});
data.locales[1] = res.body;
});
test('Cannot update the code of a locale (without name)', async () => {
const localeUpdate = {
code: 'ak',
};
const res = await rq({
url: `/i18n/locales/${data.locales[0].id}`,
method: 'PUT',
body: localeUpdate,
});
expect(res.statusCode).toBe(400);
expect(res.body).toMatchObject({
data: null,
error: {
details: {
errors: [
{
path: [],
name: 'ValidationError',
message: 'this field has unspecified keys: code',
},
],
},
message: 'this field has unspecified keys: code',
name: 'ValidationError',
status: 400,
},
});
});
test('Cannot update the code of a locale (with name)', async () => {
const localeUpdate = {
name: 'French',
code: 'ak',
};
const res = await rq({
url: `/i18n/locales/${data.locales[0].id}`,
method: 'PUT',
body: localeUpdate,
});
expect(res.statusCode).toBe(400);
expect(res.body).toMatchObject({
data: null,
error: {
details: {
errors: [
{
path: [],
name: 'ValidationError',
message: 'this field has unspecified keys: code',
},
],
},
message: 'this field has unspecified keys: code',
name: 'ValidationError',
status: 400,
},
});
});
test('Only one locale can be default (PUT)', async () => {
let res = await rq({
url: `/i18n/locales/${data.locales[0].id}`,
method: 'PUT',
body: { isDefault: true },
});
expect(res.statusCode).toBe(200);
expect(res.body.isDefault).toBe(true);
res = await rq({
url: `/i18n/locales/${data.locales[1].id}`,
method: 'PUT',
body: { isDefault: true },
});
expect(res.statusCode).toBe(200);
expect(res.body.isDefault).toBe(true);
res = await rq({
url: '/i18n/locales',
method: 'GET',
});
expect(res.statusCode).toBe(200);
expect(res.body.find((locale) => locale.code === data.locales[0].code).isDefault).toBe(false);
expect(res.body.find((locale) => locale.code === data.locales[1].code).isDefault).toBe(true);
});
test('Cannot unselect isDefault', async () => {
let res = await rq({
url: `/i18n/locales/${data.locales[0].id}`,
method: 'PUT',
body: { isDefault: true },
});
expect(res.statusCode).toBe(200);
expect(res.body.isDefault).toBe(true);
res = await rq({
url: `/i18n/locales/${data.locales[0].id}`,
method: 'PUT',
body: { isDefault: false },
});
expect(res.statusCode).toBe(200);
expect(res.body.isDefault).toBe(true);
});
});
describe('Delete', () => {
test('Cannot delete default locale', async () => {
let res = await rq({
url: `/i18n/locales/${data.locales[0].id}`,
method: 'PUT',
body: { isDefault: true },
});
expect(res.statusCode).toBe(200);
expect(res.body.isDefault).toBe(true);
data.locales[1].isDefault = false;
res = await rq({
url: `/i18n/locales/${data.locales[0].id}`,
method: 'DELETE',
});
expect(res.statusCode).toBe(400);
expect(res.body).toMatchObject({
data: null,
error: {
status: 400,
name: 'ApplicationError',
message: 'Cannot delete the default locale',
details: {},
},
});
});
test('Simply delete a locale', async () => {
const res = await rq({
url: `/i18n/locales/${data.locales[1].id}`,
method: 'DELETE',
});
expect(res.statusCode).toBe(200);
expect(res.body).toMatchObject(omitTimestamps(data.locales[1]));
data.deletedLocales.push(res.body);
data.locales.splice(1, 1);
});
test('Delete a locale and entities in this locale', async () => {
const {
body: { data: frenchProduct },
} = await rq({
url: '/content-manager/collection-types/api::product.product',
method: 'POST',
qs: { locale: 'fr-FR' },
body: { name: 'product name' },
});
await rq({
url: `/content-manager/collection-types/api::product.product/${frenchProduct.documentId}`,
method: 'PUT',
body: { name: 'product name' },
qs: {
locale: 'en',
},
});
const {
body: { results: createdProducts },
} = await rq({
url: '/content-manager/collection-types/api::product.product',
method: 'GET',
qs: { locale: 'fr-FR' },
});
expect(createdProducts).toHaveLength(1);
// expect(createdProducts[0].localizations[0].locale).toBe('en');
const res = await rq({
url: `/i18n/locales/${data.locales[1].id}`,
method: 'DELETE',
});
const {
body: { results: frenchProducts },
} = await rq({
url: '/content-manager/collection-types/api::product.product',
method: 'GET',
qs: { locale: 'fr-FR' },
});
expect(frenchProducts).toHaveLength(0);
const {
body: { results: englishProducts },
} = await rq({
url: '/content-manager/collection-types/api::product.product',
method: 'GET',
qs: { locale: 'en' },
});
expect(englishProducts).toHaveLength(1);
expect(res.statusCode).toBe(200);
expect(res.body).toMatchObject(omitTimestamps(data.locales[1]));
data.deletedLocales.push(res.body);
data.locales.splice(1, 1);
});
test('Cannot delete not found locale', async () => {
const res = await rq({
url: `/i18n/locales/${data.deletedLocales[0].id}`,
method: 'DELETE',
});
expect(res.statusCode).toBe(404);
expect(res.body).toMatchObject({
data: null,
error: {
status: 404,
name: 'NotFoundError',
message: 'locale.notFound',
details: {},
},
});
});
});
});