mirror of
https://github.com/kingomarnajjar/flex-template-web.git
synced 2026-07-26 06:47:17 +10:00
331 lines
13 KiB
JavaScript
331 lines
13 KiB
JavaScript
import { types as sdkTypes } from './sdkLoader';
|
|
import {
|
|
combinedRelationships,
|
|
combinedResourceObjects,
|
|
updatedEntities,
|
|
denormalisedEntities,
|
|
arrayToFormValues,
|
|
formValuesToArray,
|
|
} from './data';
|
|
|
|
const { UUID } = sdkTypes;
|
|
|
|
describe('data utils', () => {
|
|
describe('combinedRelationships()', () => {
|
|
it('handles no rels', () => {
|
|
expect(combinedRelationships(undefined, undefined)).toEqual(null);
|
|
});
|
|
|
|
it('takes existings rels if no new rels are given', () => {
|
|
const listingRels = {
|
|
author: { data: { id: new UUID('author-id'), type: 'author' } },
|
|
images: {
|
|
data: [
|
|
{ id: new UUID('image1'), type: 'image' },
|
|
{ id: new UUID('image2'), type: 'image' },
|
|
],
|
|
},
|
|
};
|
|
expect(combinedRelationships(listingRels, undefined)).toEqual(listingRels);
|
|
});
|
|
|
|
it('takes new rels if no existing rels are given', () => {
|
|
const listingRels = {
|
|
author: { data: { id: new UUID('author-id'), type: 'author' } },
|
|
images: { data: { id: new UUID('image1'), type: 'image' } },
|
|
};
|
|
expect(combinedRelationships(undefined, listingRels)).toEqual(listingRels);
|
|
});
|
|
|
|
it('merges two nonempty rels', () => {
|
|
const authorRel = { data: { id: new UUID('author-id'), type: 'author' } };
|
|
const imagesRel = {
|
|
data: [
|
|
{ id: new UUID('image1'), type: 'image' },
|
|
{ id: new UUID('image2'), type: 'image' },
|
|
],
|
|
};
|
|
const newImagesRel = { data: { id: new UUID('image1'), type: 'image' } };
|
|
const reviewsRel = {
|
|
data: [
|
|
{ id: new UUID('review1'), type: 'review' },
|
|
{ id: new UUID('review2'), type: 'review' },
|
|
],
|
|
};
|
|
|
|
const oldListingRels = { author: authorRel, images: imagesRel };
|
|
const newListingRels = { images: newImagesRel, reviews: reviewsRel };
|
|
expect(combinedRelationships(oldListingRels, newListingRels)).toEqual({
|
|
author: authorRel,
|
|
images: newImagesRel,
|
|
reviews: reviewsRel,
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('combinedResourceObjects()', () => {
|
|
it("throws if ids don't match", () => {
|
|
const listing1 = { id: new UUID('listing1'), type: 'listing' };
|
|
const listing2 = { id: new UUID('listing2'), type: 'listing' };
|
|
expect(() => combinedResourceObjects(listing1, listing2)).toThrow(
|
|
'Cannot merge resource objects with different ids or types'
|
|
);
|
|
});
|
|
|
|
it("throws if types don't match", () => {
|
|
const listing1 = { id: new UUID('listing1'), type: 'listing' };
|
|
const author1 = { id: new UUID('author1'), type: 'author' };
|
|
expect(() => combinedResourceObjects(listing1, author1)).toThrow(
|
|
'Cannot merge resource objects with different ids or types'
|
|
);
|
|
});
|
|
|
|
it("doesn't add attributes or relationships keys unnecessarily", () => {
|
|
const res1 = { id: new UUID('listing1'), type: 'listing' };
|
|
const res2 = { id: new UUID('listing1'), type: 'listing' };
|
|
const merged = combinedResourceObjects(res1, res2);
|
|
expect(merged).toEqual(res1);
|
|
const keys = Object.keys(merged).sort();
|
|
expect(keys).toEqual(['id', 'type']);
|
|
});
|
|
|
|
it('merges new attributes', () => {
|
|
const res1 = {
|
|
id: new UUID('listing1'),
|
|
type: 'listing',
|
|
attributes: { title: 'Listing 1 title', description: 'Listing 1 description' },
|
|
};
|
|
const res2 = {
|
|
id: new UUID('listing1'),
|
|
type: 'listing',
|
|
attributes: { title: 'Changed title', description: 'Some description' },
|
|
};
|
|
expect(combinedResourceObjects(res1, res2)).toEqual(res2);
|
|
});
|
|
|
|
it("keeps old attributes if new does't have any", () => {
|
|
const res1Attributes = { title: 'Listing 1 title', description: 'Listing 1 description' };
|
|
const res2Relationships = { author: { data: { id: new UUID('author1'), type: 'author' } } };
|
|
|
|
const res1 = { id: new UUID('listing1'), type: 'listing', attributes: res1Attributes };
|
|
const res2 = { id: new UUID('listing1'), type: 'listing', relationships: res2Relationships };
|
|
expect(combinedResourceObjects(res1, res2)).toEqual({
|
|
id: new UUID('listing1'),
|
|
type: 'listing',
|
|
attributes: res1Attributes,
|
|
relationships: res2Relationships,
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('updatedEntities()', () => {
|
|
it('adds a single entity', () => {
|
|
const listing1 = { id: new UUID('listing1'), type: 'listing' };
|
|
const response = { data: listing1 };
|
|
const entities = updatedEntities({}, response);
|
|
expect(entities).toEqual({ listing: { listing1 } });
|
|
});
|
|
|
|
it('add multiple entities', () => {
|
|
const listing1 = { id: new UUID('listing1'), type: 'listing' };
|
|
const listing2 = { id: new UUID('listing2'), type: 'listing' };
|
|
const response = { data: [listing1, listing2] };
|
|
const entities = updatedEntities({}, response);
|
|
expect(entities).toEqual({ listing: { listing1, listing2 } });
|
|
});
|
|
|
|
it('handles a more complex merge', () => {
|
|
const listing1 = {
|
|
id: new UUID('listing1'),
|
|
type: 'listing',
|
|
attributes: { title: 'Listing 1 title', description: 'Listing 1 description' },
|
|
};
|
|
const listing2 = {
|
|
id: new UUID('listing2'),
|
|
type: 'listing',
|
|
attributes: { title: 'Listing 2 title', description: 'Listing 2 description' },
|
|
};
|
|
const initialResponse = { data: [listing1, listing2] };
|
|
const initialEntities = updatedEntities({}, initialResponse);
|
|
expect(initialEntities).toEqual({ listing: { listing1, listing2 } });
|
|
const author1 = { id: new UUID('author1'), type: 'author' };
|
|
const author1WithAttributes = {
|
|
id: new UUID('author1'),
|
|
type: 'author',
|
|
attributes: { name: 'Author 1 name' },
|
|
};
|
|
const listing2Updated = {
|
|
id: new UUID('listing2'),
|
|
type: 'listing',
|
|
attributes: { title: 'New listing 2 title', description: 'new listing 2 description' },
|
|
relationships: { author: { data: author1 } },
|
|
};
|
|
const included = [author1WithAttributes];
|
|
const response = { data: [listing2Updated], included };
|
|
const entities = updatedEntities(initialEntities, response, true);
|
|
expect(entities).toEqual({
|
|
listing: { listing1, listing2: listing2Updated },
|
|
author: { author1: author1WithAttributes },
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('denormalisedEntities()', () => {
|
|
const createListing = id => ({
|
|
id: new UUID(id),
|
|
type: 'listing',
|
|
attributes: { title: `Listing ${id} title`, description: `Listing ${id} description` },
|
|
});
|
|
|
|
const createUser = id => ({
|
|
id: new UUID(id),
|
|
type: 'user',
|
|
attributes: { name: `User ${id} name` },
|
|
});
|
|
|
|
const createImage = id => ({
|
|
id: new UUID(id),
|
|
type: 'image',
|
|
attributes: { url: `https://example.com/${id}.jpg`, width: 300, height: 200 },
|
|
});
|
|
|
|
it('returns no results with empty resources', () => {
|
|
const entities = { listing: { listing1: createListing('listing1') } };
|
|
expect(denormalisedEntities(entities, [])).toEqual([]);
|
|
});
|
|
|
|
it('returns no results with empty entities and empty ids', () => {
|
|
const entities = {};
|
|
expect(denormalisedEntities(entities, [])).toEqual([]);
|
|
});
|
|
|
|
it('handles selecting a nonexistent resource of different type', () => {
|
|
const entities = { listing: { listing1: createListing('listing1') } };
|
|
const user = createUser('user1');
|
|
expect(() => denormalisedEntities(entities, [user])).toThrow(
|
|
'Entity with type "user" and id "user1" not found'
|
|
);
|
|
// Empty results when error throwIfNotFound=false
|
|
expect(denormalisedEntities(entities, [user], false)).toEqual([]);
|
|
});
|
|
|
|
it('handles selecting a nonexistent resource of same type', () => {
|
|
const entities = { listing: { listing1: createListing('listing1') } };
|
|
const listing2 = createListing('listing2');
|
|
expect(() => denormalisedEntities(entities, [listing2])).toThrow(
|
|
'Entity with type "listing" and id "listing2" not found'
|
|
);
|
|
// Empty results when error throwIfNotFound=false
|
|
expect(denormalisedEntities(entities, [listing2], false)).toEqual([]);
|
|
});
|
|
|
|
it('throws if a related entity is not found', () => {
|
|
const listing1 = createListing('listing1');
|
|
listing1.relationships = { author: { data: createUser('user1') } };
|
|
const entities = { listing: { listing1 } };
|
|
const resources = [listing1];
|
|
expect(() => denormalisedEntities(entities, resources)).toThrow(
|
|
'Entity with type "user" and id "user1" not found'
|
|
);
|
|
});
|
|
|
|
it('returns the selected entities', () => {
|
|
const listing1 = createListing('listing1');
|
|
const listing2 = createListing('listing2');
|
|
const listing3 = createListing('listing3');
|
|
|
|
const entities = { listing: { listing1, listing2, listing3 } };
|
|
const resources = [listing1, listing3];
|
|
expect(denormalisedEntities(entities, resources)).toEqual([listing1, listing3]);
|
|
});
|
|
|
|
it('returns entities of different types', () => {
|
|
const user1 = createUser('user1');
|
|
const listing1 = createListing('listing1');
|
|
const entities = {
|
|
listing: { listing1 },
|
|
user: { user1 },
|
|
};
|
|
expect(denormalisedEntities(entities, [listing1, user1])).toEqual([listing1, user1]);
|
|
});
|
|
|
|
it('throws with circular relationships', () => {
|
|
// Currently the data handling code cannot handle circular
|
|
// relationships. These are possible in practice with certain
|
|
// include params in different API queries, but fixing this is
|
|
// not trivial. The denormalisation cannot skip entities that
|
|
// are already added since that would remove the fundamental
|
|
// feature of updating entities with new data.
|
|
|
|
const user1 = createUser('user1');
|
|
const listing1 = createListing('listing1');
|
|
const listing1Relationships = { author: { data: user1 } };
|
|
const listing1WithRelationships = { ...listing1, relationships: listing1Relationships };
|
|
const user1Relationships = { topListing: { data: listing1 } };
|
|
const user1WithRelationships = { ...user1, relationships: user1Relationships };
|
|
const entities = {
|
|
listing: { listing1: listing1WithRelationships },
|
|
user: { user1: user1WithRelationships },
|
|
};
|
|
expect(() => denormalisedEntities(entities, [listing1])).toThrow();
|
|
});
|
|
|
|
it('denormalises simple relationships', () => {
|
|
const user1 = createUser('user1');
|
|
const listing1 = createListing('listing1');
|
|
const listing1Relationships = { author: { data: user1 } };
|
|
const listing1WithRelationships = { ...listing1, relationships: listing1Relationships };
|
|
const listing2 = createListing('listing2');
|
|
const entities = {
|
|
listing: { listing1: listing1WithRelationships, listing2 },
|
|
user: { user1 },
|
|
};
|
|
expect(denormalisedEntities(entities, [listing1])).toEqual([{ ...listing1, author: user1 }]);
|
|
});
|
|
|
|
it('denormalises multiple relationships', () => {
|
|
const user1 = createUser('user1');
|
|
const image1 = createImage('image1');
|
|
const image2 = createImage('image2');
|
|
const listing1 = createListing('listing1');
|
|
const listing1Relationships = {
|
|
author: { data: user1 },
|
|
images: { data: [image1, image2] },
|
|
};
|
|
const listing1WithRelationships = { ...listing1, relationships: listing1Relationships };
|
|
const listing2 = createListing('listing2');
|
|
const listing3 = createListing('listing3');
|
|
const entities = {
|
|
listing: { listing1: listing1WithRelationships, listing2, listing3 },
|
|
user: { user1 },
|
|
image: { image1, image2 },
|
|
};
|
|
|
|
expect(denormalisedEntities(entities, [listing1, listing2])).toEqual([
|
|
{ ...listing1, author: user1, images: [image1, image2] },
|
|
listing2,
|
|
]);
|
|
});
|
|
|
|
it('denormalises multiple relationships when relationship data is empty', () => {
|
|
const user1 = createUser('user1');
|
|
const listing1 = createListing('listing1');
|
|
const listing1Relationships = {
|
|
author: { data: null },
|
|
images: { data: [] },
|
|
};
|
|
const listing1WithRelationships = { ...listing1, relationships: listing1Relationships };
|
|
const listing2 = createListing('listing2');
|
|
const listing3 = createListing('listing3');
|
|
const entities = {
|
|
listing: { listing1: listing1WithRelationships, listing2, listing3 },
|
|
user: { user1 },
|
|
};
|
|
|
|
expect(denormalisedEntities(entities, [listing1, listing2])).toEqual([
|
|
{ ...listing1, author: null, images: [] },
|
|
listing2,
|
|
]);
|
|
});
|
|
});
|
|
});
|